-
Notifications
You must be signed in to change notification settings - Fork 37
[FSSDK-12882] release pipeline update #343
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
Merged
junaed-optimizely
merged 6 commits into
master
from
junaed/fssdk-12882-ghr-publish-adjustment
Jul 17, 2026
+238
−20
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
a73db2d
[FSSDK-12882] release pipeline update
junaed-optimizely 3a6bbab
[FSSDK-12882] review feedback
junaed-optimizely 9b437e5
[FSSDK-12882] review feedback
junaed-optimizely 87d94e7
[FSSDK-12882] optimization
junaed-optimizely 1d177a6
[FSSDK-12882] optimization
junaed-optimizely cc18520
[FSSDK-12881] feedback addressed
junaed-optimizely 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,68 @@ | ||
| name: Backfill GitHub Package Registry | ||
|
|
||
| # Manually triggered. Copies versions already published on npm into the GitHub | ||
| # Package Registry so GHR mirrors npm. Re-publishing the npm tarball guarantees | ||
| # the GHR copy is byte-identical to what npm consumers received (no rebuild). | ||
| # | ||
| # 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. 4.0.0). 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 GHR auth | ||
| run: echo "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> ~/.npmrc | ||
|
|
||
| - name: Backfill | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| DRY_RUN: ${{ inputs.dry_run }} | ||
| INPUT_VERSION: ${{ inputs.version }} | ||
| run: | | ||
| set -euo pipefail | ||
| pkg="@optimizely/react-sdk" | ||
|
|
||
| if [[ -n "$INPUT_VERSION" ]]; then | ||
| versions="$INPUT_VERSION" | ||
| else | ||
| versions=$(npm view "$pkg" versions --json --registry https://registry.npmjs.org | 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 https://registry.npmjs.org | tail -n1) | ||
| scripts/publish.sh "https://npm.pkg.github.com" "$tarball" | ||
| rm -f "$tarball" | ||
| echo "::endgroup::" | ||
| done | ||
|
arnica-github-connector[bot] marked this conversation as resolved.
|
||
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,143 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # Registry-agnostic, idempotent publish for @optimizely/react-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`. Otherwise it is tagged | ||
| # `v<major>.<minor>-latest` (its own release line), so `latest` never | ||
| # moves backwards onto an older release — this covers both an older | ||
| # major (5.x shipped after 6.x) and an older minor of the current major | ||
| # (6.4.1 shipped while latest is 6.5.0). | ||
| 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%%.*} | ||
| rest=${version#*.} | ||
| minor=${rest%%.*} | ||
| tag="v${major}.${minor}-latest" | ||
| echo "Current latest is ${current_latest}; ${version} is not newer, tagging as ${tag} to preserve latest." | ||
| 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.