NPM: Publish all packages in Github Actions (#111034)

* Initial npm-publish workflow

* Validate version type and version match, typecheck packages before building, set npm tag

* quote GITHUB_OUTPUT

* codeowners

* fix syntax in release-build

* only tag the latest version with latest

* don't require NPM_TOKEN env var to be set if using OIDC

* put comment back

* tighten script

* Codeowners

* Use workflow_call for canaries, Rename workflow to match release-build

* codeowners
This commit is contained in:
Josh Hunt 2025-09-18 18:24:59 +01:00 committed by GitHub
parent cb11bc15fa
commit d96d7e680c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 233 additions and 37 deletions

3
.github/CODEOWNERS vendored
View File

@ -1268,6 +1268,9 @@ embed.go @grafana/grafana-as-code
/.github/workflows/pr-e2e-tests.yml @grafana/grafana-developer-enablement-squad /.github/workflows/pr-e2e-tests.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/skye-add-to-project.yml @grafana/grafana-frontend-platform /.github/workflows/skye-add-to-project.yml @grafana/grafana-frontend-platform
/.github/workflows/frontend-perf-tests.yaml @grafana/grafana-frontend-platform /.github/workflows/frontend-perf-tests.yaml @grafana/grafana-frontend-platform
/.github/workflows/release-npm.yml @grafana/grafana-frontend-platform
/.github/workflows/scripts/determine-npm-tag.sh @grafana/grafana-frontend-platform
/.github/workflows/scripts/validate-commit-in-head.sh @grafana/grafana-frontend-platform
/.github/zizmor.yml @grafana/grafana-developer-enablement-squad /.github/zizmor.yml @grafana/grafana-developer-enablement-squad
/.github/license_finder.yaml @bergquist /.github/license_finder.yaml @bergquist
/.github/actionlint.yaml @grafana/grafana-developer-enablement-squad /.github/actionlint.yaml @grafana/grafana-developer-enablement-squad

View File

@ -271,43 +271,18 @@ jobs:
docker manifest push "grafana/grafana-dev:${VERSION}" docker manifest push "grafana/grafana-dev:${VERSION}"
docker manifest push "grafana/grafana-dev:${VERSION}-ubuntu" docker manifest push "grafana/grafana-dev:${VERSION}-ubuntu"
publish-npm: publish-npm-canaries:
if: github.ref_name == 'main' if: github.ref_name == 'main'
name: Publish NPM canaries
uses: ./.github/workflows/release-npm.yml
permissions: permissions:
contents: read contents: read
id-token: write id-token: write
# NPM Trusted Publishing does not yet support self-hosted runners
runs-on: github-hosted-ubuntu-x64-small
needs: needs:
- setup - setup
- build - build
env: with:
PACKAGES_VERSION: ${{ needs.setup.outputs.version }} grafana_commit: ${{ needs.setup.outputs.grafana-commit }}
steps: version: ${{ needs.setup.outputs.version }}
- name: Checkout code build_id: ${{ github.run_id }}
uses: actions/checkout@v4 version_type: "canary"
with:
persist-credentials: false
# Setting nodejs up just for npm publish. Not restoring the cache for all our dependencies
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
# Trusted Publishing is only available in npm v11.5.1 and later
- name: Update npm
run: npm install -g npm@^11.5.1
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: artifacts-linux-amd64
path: dist
- name: Copy packages
run: mv dist/"${PACKAGES_VERSION}"/npm-packages npm-artifacts
- name: Publish to NPM
env:
NPM_TOKEN: "oidc"
run: ./scripts/publish-npm-packages.sh --dist-tag 'canary' --registry 'https://registry.npmjs.org/'

135
.github/workflows/release-npm.yml vendored Normal file
View File

@ -0,0 +1,135 @@
name: Publish NPM packages
on:
workflow_call:
inputs:
grafana_commit:
description: 'Grafana commit SHA to build against'
required: true
type: string
version:
description: 'Version to publish as'
required: true
type: string
build_id:
description: 'Run ID from the original release-build workflow'
required: true
type: string
version_type:
description: 'Version type (canary, nightly, stable)'
required: true
type: string
workflow_dispatch:
inputs:
grafana_commit:
description: 'Grafana commit SHA to build against'
required: true
version:
description: 'Version to publish as'
required: true
build_id:
description: 'Run ID from the original release-build workflow'
required: true
version_type:
description: 'Version type (canary, nightly, stable)'
required: true
permissions: {}
jobs:
# If called with version_type 'canary' or 'stable', build + publish to NPM
# If called with version_type 'nightly', just tag the given version with nightly tag. It was already published by the canary build.
publish:
name: Publish NPM packages
runs-on: github-hosted-ubuntu-x64-small
if: inputs.version_type == 'canary' || inputs.version_type == 'stable'
permissions:
contents: read
id-token: write
steps:
- name: Info
env:
GITHUB_REF: ${{ github.ref }}
run: |
echo "GRAFANA_COMMIT: $GRAFANA_COMMIT"
echo "github.ref: $GITHUB_REF"
- name: Checkout workflow ref
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Verify commit is in workflow HEAD
env:
GIT_COMMIT: ${{ github.event.inputs.grafana_commit }}
run: ./github/workflows/scripts/validate-commit-in-head.sh
shell: bash
- name: Map version type to NPM tag
id: npm-tag
env:
VERSION_TYPE: ${{ github.event.inputs.version_type }}
run: |
TAG=$(./github/workflows/scripts/determine-npm-tag.sh)
echo "NPM_TAG=$TAG" >> "$GITHUB_OUTPUT"
shell: bash
- name: Checkout build commit
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ github.event.inputs.grafana_commit }}
- name: Setup Node
uses: ./github/actions/setup-node
- name: Install dependencies
run: yarn install --immutable
- name: Typecheck packages
run: yarn run packages:typecheck
- name: Version, build, and pack packages
run: |
yarn run packages:build
yarn lerna version "$PACKAGES_VERSION" \
--exact \
--no-git-tag-version \
--no-push \
--force-publish \
--yes
yarn run packages:pack
- name: Debug packed files
run: tree -a ./npm-artifacts
- name: Validate packages
run: ./scripts/validate-npm-packages.sh
- name: Debug OIDC Claims
uses: github/actions-oidc-debugger@018a1dc4f8e47adca924d55e4bb0ddce917af32d
with:
audience: '${{ github.server_url }}/${{ github.repository_owner }}'
- name: Publish packages
env:
NPM_TAG: ${{ steps.npm-tag.outputs.NPM_TAG }}
run: ./scripts/publish-npm-packages.sh --dist-tag "$NPM_TAG" --registry 'https://registry.npmjs.org/'
# TODO: finish this step
tag-nightly:
name: Tag nightly release
runs-on: github-hosted-ubuntu-x64-small
needs: publish
if: inputs.version_type == 'nightly'
steps:
- name: Checkout workflow ref
uses: actions/checkout@v4
with:
persist-credentials: false
# TODO: tag the given release with nightly

View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -euo pipefail
fail() { echo "Error: $*" >&2; exit 1; }
# Ensure required variables are set
if [[ -z "${REFERENCE_PKG}" || -z "${VERSION_TYPE}" || -z "${VERSION}" ]]; then
fail "Missing required environment variables: REFERENCE_PKG, VERSION_TYPE, VERSION"
fi
semver_cmp () {
IFS='.' read -r -a arr_a <<< "$1"
IFS='.' read -r -a arr_b <<< "$2"
for i in 0 1 2; do
local aa=${arr_a[i]:-0}
local bb=${arr_b[i]:-0}
# shellcheck disable=SC2004
if (( 10#$aa > 10#$bb )); then echo gt; return 0; fi
if (( 10#$aa < 10#$bb )); then echo lt; return 0; fi
done
echo "eq"
}
STABLE_REGEX='^([0-9]+)\.([0-9]+)\.([0-9]+)$' # x.y.z
PRE_REGEX='^([0-9]+)\.([0-9]+)\.([0-9]+)-([0-9]+)$' # x.y.z-123456
# Validate that the VERSION matches VERSION_TYPE
# - stable must be x.y.z
# - nightly/canary must be x.y.z-123456
case "$VERSION_TYPE" in
stable)
[[ $VERSION =~ $STABLE_REGEX ]] || fail "For 'stable', version must match x.y.z" ;;
nightly|canary)
[[ $VERSION =~ $PRE_REGEX ]] || fail "For '$VERSION_TYPE', version must match x.y.z-123456" ;;
*)
fail "Unknown version_type '$VERSION_TYPE'" ;;
esac
# Extract major, minor from VERSION
IFS=.- read -r major minor patch _ <<< "$VERSION"
# Determine NPM tag
case "$VERSION_TYPE" in
canary) TAG="canary" ;;
nightly) TAG="nightly" ;;
stable)
# Use npm dist-tag "latest" as the reference
LATEST="$(npm view --silent "$REFERENCE_PKG" dist-tags.latest 2>/dev/null || true)"
echo "Latest for $REFERENCE_PKG is ${LATEST:-<none>}" >&2
if [[ -z ${LATEST:-} ]]; then
TAG="latest" # first ever publish
else
case "$(semver_cmp "$VERSION" "$LATEST")" in
gt) TAG="latest" ;; # newer than reference -> latest
lt|eq) TAG="v${major}.${minor}-latest" ;; # older or equal -> vX.Y-latest
esac
fi
;;
esac
echo "Resolved NPM_TAG=$TAG (VERSION=$VERSION, current latest=${LATEST:-none})" 1>&2 # stderr
printf '%s' "$TAG"

View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -z "${GIT_COMMIT:-}" ]]; then
echo "Error: Environment variable GIT_COMMIT is required"
exit 1
fi
if git merge-base --is-ancestor "$GIT_COMMIT" HEAD; then
echo "Commit $GIT_COMMIT is contained in HEAD"
else
echo "Error: Commit $GIT_COMMIT is not contained in HEAD"
exit 1
fi

View File

@ -5,9 +5,11 @@
dist_tag="canary" dist_tag="canary"
registry="http://localhost:4873" registry="http://localhost:4873"
if [ -z "$NPM_TOKEN" ]; then # Require either ACTIONS_ID_TOKEN_REQUEST_URL or NPM_TOKEN to be set
echo "The NPM_TOKEN environment variable does not exist." if [ -z "$ACTIONS_ID_TOKEN_REQUEST_URL" ] && [ -z "$NPM_TOKEN" ]; then
exit 1 echo "ERROR: Either ACTIONS_ID_TOKEN_REQUEST_URL or NPM_TOKEN environment variable must be set."
echo "If running in Github Actions, ensure that 'id-token: write' permission is granted."
exit 1
fi fi
# Parse command line arguments # Parse command line arguments
@ -33,7 +35,8 @@ done
echo "Starting to release $dist_tag version with NPM version $(npm --version) to registry $registry" echo "Starting to release $dist_tag version with NPM version $(npm --version) to registry $registry"
if [[ "$NPM_TOKEN" != "oidc" ]]; then if [ -n "$NPM_TOKEN" ]; then
echo "Configured NPM_TOKEN in ~/.npmrc"
registry_without_protocol=${registry#*:} registry_without_protocol=${registry#*:}
echo "$registry_without_protocol/:_authToken=${NPM_TOKEN}" >> ~/.npmrc echo "$registry_without_protocol/:_authToken=${NPM_TOKEN}" >> ~/.npmrc
fi fi