-
Notifications
You must be signed in to change notification settings - Fork 85
[FSSDK-12881] NPM to GPR backfill + release workflow update #1168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
junaed-optimizely
wants to merge
4
commits into
master
Choose a base branch
from
junaed/fssdk-12881-ghr-workflow-adjustmet
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+265
−46
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| name: Backfill GitHub Package Registry | ||
|
|
||
| # Manually triggered. Copies versions already published on npm into the GitHub | ||
| # Package Registry (GHR) so GHR mirrors npm. Re-publishing the npm tarball | ||
| # guarantees the GHR copy is byte-identical to what npm consumers received | ||
| # (no rebuild). | ||
| # | ||
| # dist-tags: scripts/publish.sh computes the tag for each version from the | ||
| # version string against GHR's current latest (latest only when strictly newer, | ||
| # otherwise v<major>-last-published; beta/alpha/rc for pre-releases), so latest | ||
| # never moves backwards while backfilling historical versions. | ||
| # | ||
| # Idempotent: scripts/publish.sh skips any version already present on GHR, so | ||
| # this can be re-run safely. | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| version: | ||
| description: "Single version to backfill (e.g. 5.3.4). Leave blank to process all published npm versions (versions already on GHR are skipped)." | ||
| required: false | ||
| default: "" | ||
| dry_run: | ||
| description: "Dry run: report what would be published to GHR without publishing." | ||
| type: boolean | ||
| required: false | ||
| default: false | ||
|
|
||
| jobs: | ||
| backfill: | ||
| name: Backfill npm versions to GHR | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| steps: | ||
| - name: Checkout branch | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Node | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 18 | ||
|
|
||
| - name: Configure GitHub Package Registry auth | ||
| run: echo "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> ~/.npmrc | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Backfill | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| DRY_RUN: ${{ inputs.dry_run }} | ||
| INPUT_VERSION: ${{ inputs.version }} | ||
| run: | | ||
| set -euo pipefail | ||
| pkg="@optimizely/optimizely-sdk" | ||
| npm_registry="https://registry.npmjs.org" | ||
| gpr_registry="https://npm.pkg.github.com" | ||
|
|
||
| if [[ -n "$INPUT_VERSION" ]]; then | ||
| versions="$INPUT_VERSION" | ||
| else | ||
| versions=$(npm view "$pkg" versions --json --registry "$npm_registry" | jq -r '.[]') | ||
| fi | ||
|
|
||
| for v in $versions; do | ||
| echo "::group::${pkg}@${v}" | ||
| # npm pack writes the tarball filename to stdout and notices/errors to | ||
| # stderr; keep stderr visible so pack failures (auth/404/network) show | ||
| # in the logs. pipefail (set above) makes a failed pack abort the job. | ||
| tarball=$(npm pack "${pkg}@${v}" --registry "$npm_registry" | tail -n1) | ||
| scripts/publish.sh "$gpr_registry" "$tarball" | ||
| rm -f "$tarball" | ||
| echo "::endgroup::" | ||
| done | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # Registry-agnostic, idempotent publish for @optimizely/optimizely-sdk. | ||
| # | ||
| # Usage: | ||
| # scripts/publish.sh <registry-url> [tarball] | ||
| # | ||
| # Args: | ||
| # registry-url Target registry, e.g. https://registry.npmjs.org | ||
| # or https://npm.pkg.github.com | ||
| # tarball Optional. When given, publishes that packed tarball instead | ||
| # of the current working directory (used by the GHR backfill). | ||
| # | ||
| # Env: | ||
| # NODE_AUTH_TOKEN Auth token for the target registry. Used both to read the | ||
| # registry (existence/dist-tag lookup) and, via the caller's | ||
| # .npmrc entry (`//<host>/:_authToken=${NODE_AUTH_TOKEN}`, | ||
| # which setup-node writes), to publish. This script passes | ||
| # --registry explicitly, so no scope-to-registry routing is | ||
| # required (and the GHR job intentionally does NOT route | ||
| # @optimizely to GHR, so that `npm ci` still installs | ||
| # dependencies from npm). | ||
| # DRY_RUN When "true", report what would happen (publish vs. skip) | ||
| # without actually publishing. | ||
| # | ||
| # Behavior: | ||
| # - Reads the target registry's packument once (a single HTTP GET) and: | ||
| # * 200 -> package exists; skip (exit 0) if this exact version is already | ||
| # present, so re-running a release or the backfill is a safe no-op. | ||
| # * 404 -> package/version absent; proceed to publish. | ||
| # * any other status or a network failure -> abort (exit 1) rather than | ||
| # guess. A flaky lookup must never be misread as "not published" and | ||
| # trigger a publish. | ||
| # - Computes the dist-tag from the version: | ||
| # * pre-releases (beta/alpha/rc) get their own tag, never `latest`. | ||
| # * a stable release gets `latest` ONLY when it is strictly greater (by | ||
| # semver) than the registry's current `latest`, so `latest` never moves | ||
| # backwards onto an older release — e.g. 5.x shipped after 6.x, or a | ||
| # 6.4.1 patch shipped while latest is 6.5.0. Otherwise it is tagged | ||
| # `v<major>-last-published`: a per-major pointer to the most recently | ||
| # published version on that major line. It is named for recency (not | ||
| # "latest") on purpose — an out-of-order backport can leave it below the | ||
| # highest version in the major. Consumers install a line via | ||
| # @<pkg>@v<major>-last-published. | ||
| set -euo pipefail | ||
|
|
||
| dry_run="${DRY_RUN:-false}" | ||
|
|
||
| registry="${1:?usage: publish.sh <registry-url> [tarball]}" | ||
| tarball="${2:-}" | ||
|
|
||
| if [[ -n "$tarball" ]]; then | ||
| # Derive name/version from the tarball's own package.json so the guard matches | ||
| # exactly what we're about to publish (backfill of historical versions). | ||
| meta=$(tar -xzO -f "$tarball" package/package.json) | ||
| pkg=$(printf '%s' "$meta" | jq -r '.name') | ||
| version=$(printf '%s' "$meta" | jq -r '.version') | ||
| else | ||
| pkg=$(jq -r '.name' package.json) | ||
| version=$(jq -r '.version' package.json) | ||
| fi | ||
|
|
||
| # Returns 0 if $1 is strictly greater than $2 (numeric major.minor.patch). Only | ||
| # called for stable versions, so no pre-release precedence handling is needed. | ||
| version_gt() { | ||
| local -a a b | ||
| local i x y | ||
| IFS=. read -ra a <<< "$1" | ||
| IFS=. read -ra b <<< "$2" | ||
| for i in 0 1 2; do | ||
| x=${a[i]:-0} | ||
| y=${b[i]:-0} | ||
| (( 10#$x > 10#$y )) && return 0 | ||
| (( 10#$x < 10#$y )) && return 1 | ||
| done | ||
| return 1 | ||
| } | ||
|
|
||
| # --- Existence / current-latest lookup (single packument GET) ---------------- | ||
| # npm scoped names are URL-encoded with the slash as %2f: @scope/name. | ||
| pkg_encoded="${pkg//\//%2f}" | ||
| packument_url="${registry%/}/${pkg_encoded}" | ||
|
|
||
| body_file=$(mktemp) | ||
| trap 'rm -f "$body_file"' EXIT | ||
|
|
||
| # curl -sS (no --fail) returns 0 for any HTTP response, non-zero only on | ||
| # network/protocol errors — so we can cleanly separate "server answered" from | ||
| # "could not reach server". | ||
| if ! http_code=$(curl -sS -m 30 -o "$body_file" -w '%{http_code}' \ | ||
| -H "Authorization: Bearer ${NODE_AUTH_TOKEN:-}" "$packument_url"); then | ||
| echo "ERROR: request to ${packument_url} failed (network/timeout); refusing to publish on an ambiguous result." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| current_latest="" | ||
| case "$http_code" in | ||
| 200) | ||
| if jq -e --arg v "$version" '.versions[$v] != null' "$body_file" >/dev/null 2>&1; then | ||
| echo "Version ${pkg}@${version} already on ${registry}, skipping." | ||
| exit 0 | ||
| fi | ||
| current_latest=$(jq -r '.["dist-tags"].latest // empty' "$body_file") | ||
| ;; | ||
| 404) | ||
| # Package (or this version) not published yet; nothing to compare against. | ||
| current_latest="" | ||
| ;; | ||
| *) | ||
| echo "ERROR: unexpected HTTP ${http_code} querying ${packument_url}; refusing to publish on an ambiguous result." >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
|
|
||
| # --- dist-tag selection ------------------------------------------------------ | ||
| case "$version" in | ||
| *-beta*) tag=beta ;; | ||
| *-alpha*) tag=alpha ;; | ||
| *-rc*) tag=rc ;; | ||
| *) | ||
| # Stable release: `latest` only if strictly greater than the current latest. | ||
| if [[ -z "$current_latest" ]] || version_gt "$version" "$current_latest"; then | ||
| tag=latest | ||
| else | ||
| major=${version%%.*} | ||
| tag="v${major}-last-published" | ||
| echo "Current latest is ${current_latest}; ${version} is not newer, tagging as ${tag} (latest preserved)." | ||
| fi | ||
| ;; | ||
| esac | ||
|
|
||
| if [[ "$dry_run" == "true" ]]; then | ||
| echo "[dry-run] would publish ${pkg}@${version} (tag: ${tag}) to ${registry}" | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "Publishing ${pkg}@${version} (tag: ${tag}) to ${registry}" | ||
| if [[ -n "$tarball" ]]; then | ||
| # The tarball is a prebuilt artifact; skip lifecycle scripts (prepublishOnly | ||
| # = test + build) that would otherwise run from the current package.json. | ||
| npm publish "$tarball" --registry "$registry" --tag "$tag" --ignore-scripts | ||
| else | ||
| npm publish --registry "$registry" --tag "$tag" | ||
| fi |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.