Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/ghr_backfill.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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 backfill all missing versions."
required: false
default: ""
Comment thread
junaed-optimizely marked this conversation as resolved.
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
Comment thread
arnica-github-connector[bot] marked this conversation as resolved.
with:
persist-credentials: false

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 18
registry-url: "https://registry.npmjs.org/"
always-auth: "true"

# Add GHR auth for publishing. npm expands ${NODE_AUTH_TOKEN} at runtime, and
# scripts/publish.sh passes --registry explicitly, so the default registry
# stays on npm (backfill reads tarballs from npm, writes them to GHR).
- 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: ${{ github.event.inputs.dry_run }}
INPUT_VERSION: ${{ github.event.inputs.version }}
Comment thread
junaed-optimizely marked this conversation as resolved.
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
Comment thread
arnica-github-connector[bot] marked this conversation as resolved.
62 changes: 43 additions & 19 deletions .github/workflows/react_release.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
name: Publish React SDK to NPM
name: Publish React SDK

on:
release:
types: [ published ]

jobs:
publish:
publish-npm:
name: Publish to NPM
runs-on: ubuntu-latest
steps:
- name: Checkout branch
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Set up Node
uses: actions/setup-node@v4
Expand All @@ -19,27 +21,49 @@ jobs:
registry-url: "https://registry.npmjs.org/"
always-auth: "true"
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Publish to NPM
env:
NODE_AUTH_TOKEN: ${{ secrets.PUBLISH_REACT_TO_NPM_FROM_GITHUB }}
run: scripts/publish.sh "https://registry.npmjs.org"

publish-ghr:
name: Publish to GitHub Package Registry
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
Comment thread
junaed-optimizely marked this conversation as resolved.
- name: Checkout branch
uses: actions/checkout@v4
with:
persist-credentials: false

# Install from the public npm registry so scoped dependencies
# (e.g. @optimizely/optimizely-sdk) resolve normally. We do NOT route the
# @optimizely scope to GHR here, otherwise `npm ci` would look for
# dependencies on GHR instead of npm.
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 18
registry-url: "https://registry.npmjs.org/"
always-auth: "true"
cache: 'npm'

- name: Install dependencies
run: npm ci

- id: npm-tag
name: Determine NPM tag
run: |
version=$(jq -r '.version' package.json)
if [[ "$version" == *"-beta"* ]]; then
echo "npm-tag=beta" >> "$GITHUB_OUTPUT"
elif [[ "$version" == *"-alpha"* ]]; then
echo "npm-tag=alpha" >> "$GITHUB_OUTPUT"
elif [[ "$version" == *"-rc"* ]]; then
echo "npm-tag=rc" >> "$GITHUB_OUTPUT"
else
echo "npm-tag=latest" >> "$GITHUB_OUTPUT"
fi

- name: Test, build, then publish
# Add GHR auth for the publish only. npm expands ${NODE_AUTH_TOKEN} at
# runtime, and scripts/publish.sh passes --registry explicitly, so the
# default registry stays on npm.
- name: Configure GHR auth
run: echo "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> ~/.npmrc

- name: Publish to GHR
env:
NODE_AUTH_TOKEN: ${{ secrets.PUBLISH_REACT_TO_NPM_FROM_GITHUB }}
run: npm publish --tag ${{ steps.npm-tag.outputs['npm-tag'] }}
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: scripts/publish.sh "https://npm.pkg.github.com"
72 changes: 72 additions & 0 deletions scripts/publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/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. The caller must have an
# .npmrc entry supplying auth for <registry-url>'s host, e.g.
# `//<host>/:_authToken=${NODE_AUTH_TOKEN}` (setup-node writes
# this). 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:
# - Skips (exit 0) if the version already exists on the target registry, so
# re-running a release or the backfill is always a safe no-op.
# - Computes the dist-tag from the version string (beta/alpha/rc/latest) so a
# pre-release never moves the `latest` pointer.
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

case "$version" in
*-beta*) tag=beta ;;
*-alpha*) tag=alpha ;;
*-rc*) tag=rc ;;
*) tag=latest ;;
esac

if npm view "${pkg}@${version}" version --registry "$registry" >/dev/null 2>&1; then
echo "Version ${pkg}@${version} already on ${registry}, skipping."
exit 0
fi

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
Loading