NPM Operations #203
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
| name: NPM Operations | |
| permissions: | |
| id-token: write | |
| contents: write | |
| on: | |
| # 1. Pull request merge - triggers prerelease operation | |
| push: | |
| branches: | |
| - main | |
| # 2. Release - triggers release operations | |
| release: | |
| types: [published] | |
| # 3. Scheduled deprecation (keeping existing nightly schedule) | |
| schedule: | |
| # Runs every day at 4AM | |
| - cron: '0 4 * * *' | |
| # 4. Manual trigger for testing/manual/emergency operations | |
| workflow_dispatch: | |
| inputs: | |
| operation: | |
| description: 'NPM operation to perform' | |
| required: true | |
| type: choice | |
| options: | |
| - publish-release | |
| - publish-prerelease | |
| - deprecate-old-versions | |
| preid: | |
| description: '(publish-prerelease) identifier (e.g., rc, alpha, beta)' | |
| required: false | |
| type: string | |
| default: 'rc' | |
| version-override: | |
| description: '(publish-prerelease) Override version' | |
| required: false | |
| type: string | |
| dry_run: | |
| description: '(publish-prerelease) Dry run mode for testing' | |
| required: false | |
| type: boolean | |
| default: false | |
| jobs: | |
| # Determine which operations to run based on trigger | |
| determine-operations: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| operations: ${{ steps.determine.outputs.operations }} | |
| steps: | |
| - name: Determine operations based on trigger | |
| id: determine | |
| run: | | |
| case "${{ github.event_name }}" in | |
| "push") | |
| echo "operations=[\"publish-prerelease\"]" >> $GITHUB_OUTPUT | |
| ;; | |
| "release") | |
| echo "operations=[\"publish-release\"]" >> $GITHUB_OUTPUT | |
| ;; | |
| "schedule") | |
| echo "operations=[\"deprecate-old-versions\"]" >> $GITHUB_OUTPUT | |
| ;; | |
| "workflow_dispatch") | |
| echo "operations=[\"${{ inputs.operation }}\"]" >> $GITHUB_OUTPUT | |
| ;; | |
| *) | |
| echo "operations=[]" >> $GITHUB_OUTPUT | |
| ;; | |
| esac | |
| # Execute NPM operations based on determined operations | |
| npm-operations: | |
| needs: determine-operations | |
| if: needs.determine-operations.outputs.operations != '[]' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| defaults: | |
| run: | |
| shell: bash | |
| env: | |
| GITHUB_LOGIN: ${{ secrets.GITHUB_LOGIN || secrets.BROWSER_GITHUB_BOT_NAME }} | |
| GITHUB_EMAIL: ${{ secrets.GITHUB_EMAIL || secrets.BROWSER_GITHUB_BOT_EMAIL }} | |
| OPERATION: ${{ fromJson(needs.determine-operations.outputs.operations)[0] }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: actions/setup-node@v5 | |
| with: | |
| # Trusted Publishing requires `npm@11.5.1` or later. Compatible | |
| # versions of `npm` started shipping with Node.js v24.5.0. | |
| # If, for some odd reason, you need to use an earlier Node.js release, | |
| # you _must_ add another step subsequent to this one that performs: | |
| # `npm install -g npm@latest`. | |
| node-version: 24.x | |
| # This is important. The OIDC exchange requires that the registry | |
| # URL matches on both ends. | |
| registry-url: https://registry.npmjs.org | |
| # Setup Git for prerelease operations | |
| - name: Set up git | |
| run: | | |
| git config --global user.name "${GITHUB_LOGIN}" | |
| git config --global user.email "${GITHUB_EMAIL}" | |
| # Install dependencies for publish operations | |
| - name: Install project dependencies | |
| if: env.OPERATION == 'publish-release' || env.OPERATION == 'publish-prerelease' | |
| run: npm ci | |
| # Build NPM package for release | |
| - name: Build npm package | |
| if: env.OPERATION == 'publish-release' | |
| run: npm run build:npm | |
| # Publish release version | |
| - name: Publish npm package (release) | |
| if: env.OPERATION == 'publish-release' | |
| run: npm publish | |
| # Publish prerelease version | |
| - name: Publish prerelease version | |
| if: env.OPERATION == 'publish-prerelease' | |
| uses: ./.github/actions/prerelease-npm-version | |
| with: | |
| version-override: ${{ inputs.version-override }} | |
| preid: ${{ inputs.preid || 'rc' }} | |
| dry_run: ${{ inputs.dry_run || false }} | |
| # Deprecate old versions | |
| - name: Deprecate old versions | |
| if: env.OPERATION == 'deprecate-old-versions' | |
| env: | |
| # Ensure npm uses the registry URL for authentication | |
| NPM_CONFIG_REGISTRY: https://registry.npmjs.org/ | |
| run: | | |
| # Get agent EoL table from NRQL | |
| response=$(curl -X POST https://api.newrelic.com/graphql \ | |
| -H 'Content-Type: application/json' \ | |
| -H 'API-Key: ${{ secrets.NR_API_KEY_PRODUCTION }}' \ | |
| -d '{ "query": "{\n docs {\n agentReleases(agentName: BROWSER) {\n eolDate\n version\n }\n }\n}" }') | |
| eol_table=$(echo "$response" | jq -r '.data.docs.agentReleases | map({(.version): .eolDate}) | add') | |
| echo "Fetched EoL table from NRDB." | |
| # Fetch package metadata from npm registry | |
| package_name="@newrelic/browser-agent" | |
| metadata=$(curl -s "https://registry.npmjs.org/$package_name") | |
| echo "Fetched agent releases metadata from NPM." | |
| # Parse versions and their publication dates | |
| today=$(date +%Y-%m-%d) | |
| versions=$(echo "$metadata" | jq -r '.versions | keys[]') | |
| for version in $versions; do | |
| eol_date=$(echo "$eol_table" | jq -r --arg version "$version" '.[$version]') | |
| if [ "$eol_date" = "null" ]; then | |
| echo "No EoL date found for version $version. Skipping..." | |
| continue | |
| fi | |
| deprecation_message=$(echo "$metadata" | jq -r ".versions[\"$version\"].deprecated") | |
| if [[ "$eol_date" < "$today" || "$eol_date" == "$today" ]]; then | |
| if [ "$deprecation_message" = "null" ]; then | |
| echo "Deprecating version $version no longer supported as of $eol_date..." | |
| npm deprecate "$package_name@$version" "This version is no longer supported." | |
| else | |
| echo "Version $version is already deprecated." | |
| fi | |
| else | |
| break | |
| fi | |
| done | |
| outputs: | |
| operation: ${{ env.OPERATION }} | |
| success: ${{ job.status == 'success' }} |