Skip to content

backport: internal: streamline release process (#4615) (#4626) #22

backport: internal: streamline release process (#4615) (#4626)

backport: internal: streamline release process (#4615) (#4626) #22

Workflow file for this run

name: Release
on:
push:
branches:
- 16.x.x
permissions: {}
jobs:
check-publish:
name: Check for publish need and prepare artifacts
# Keep this guard on every job for defense-in-depth in case job dependencies are refactored.
if: ${{ !github.event.repository.fork && github.repository == 'graphql/graphql-js' && github.ref_name == '16.x.x' }}
runs-on: ubuntu-latest
outputs:
should_publish: ${{ steps.release_metadata.outputs.should_publish }}
tag: ${{ steps.release_metadata.outputs.tag }}
dist_tag: ${{ steps.release_metadata.outputs.dist_tag }}
prerelease: ${{ steps.release_metadata.outputs.prerelease }}
tarball_name: ${{ steps.release_metadata.outputs.tarball_name }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
permissions:
contents: read # for actions/checkout
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
# Keep checkout fast: we should only need to scroll back a few
# commits for release notes. If the release commit is older than
# this depth, release:metadata will emit empty release notes.
fetch-depth: 10
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: npm
node-version-file: '.node-version'
- name: Install Dependencies
run: npm ci --ignore-scripts
- name: Read release metadata
id: release_metadata
run: |
release_metadata_json="$(npm run --silent release:metadata)"
jq -r '
"version=\(.version)",
"tag=\(.tag)",
"dist_tag=\(.distTag)",
"prerelease=\(.prerelease)",
"package_spec=\(.packageSpec)",
"tarball_name=\(.tarballName)",
"should_publish=\(.shouldPublish)"
' <<< "${release_metadata_json}" >> "${GITHUB_OUTPUT}"
jq -r '.releaseNotes' <<< "${release_metadata_json}" > ./release-notes.md
- name: Log publish decision
run: |
if [ "${{ steps.release_metadata.outputs.should_publish }}" = "true" ]; then
echo "${{ steps.release_metadata.outputs.package_spec }} is not published yet."
else
echo "${{ steps.release_metadata.outputs.package_spec }} is already published."
fi
- name: Build NPM package
if: steps.release_metadata.outputs.should_publish == 'true'
run: npm run build:npm
- name: Pack npmDist package
if: steps.release_metadata.outputs.should_publish == 'true'
run: npm pack ./npmDist --pack-destination . > /dev/null
- name: Upload npm package tarball
if: steps.release_metadata.outputs.should_publish == 'true'
uses: actions/upload-artifact@v4
with:
name: npmDist-tarball
path: ./${{ steps.release_metadata.outputs.tarball_name }}
- name: Upload release notes
if: steps.release_metadata.outputs.should_publish == 'true'
uses: actions/upload-artifact@v4
with:
name: release-notes
path: ./release-notes.md
publish-npm:
name: Publish npm package
needs: check-publish
# Keep this guard on every job for defense-in-depth in case job dependencies are refactored.
if: ${{ !github.event.repository.fork && github.repository == 'graphql/graphql-js' && github.ref_name == '16.x.x' && needs.check-publish.outputs.should_publish == 'true' && needs.check-publish.result == 'success' }}
runs-on: ubuntu-latest
environment: release
permissions:
contents: read # keep token scopes minimal
id-token: write # for npm trusted publishing via OIDC
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
# npm trusted publishing requires a newer Node/npm line than 16.x.x
# branch runtime constraints, so pin only this job to Node 24.
node-version: 24
- name: Download npmDist package
uses: actions/download-artifact@v4
with:
name: npmDist-tarball
path: ./artifacts
- name: Publish npm package
run: |
if [ -n "${{ needs.check-publish.outputs.dist_tag }}" ]; then
npm publish --provenance --tag "${{ needs.check-publish.outputs.dist_tag }}" "./artifacts/${{ needs.check-publish.outputs.tarball_name }}"
else
npm publish --provenance "./artifacts/${{ needs.check-publish.outputs.tarball_name }}"
fi
create-release:
name: Create release
needs: check-publish
# Keep this guard on every job for defense-in-depth in case job dependencies are refactored.
if: ${{ !github.event.repository.fork && github.repository == 'graphql/graphql-js' && github.ref_name == '16.x.x' && needs.check-publish.outputs.should_publish == 'true' && needs.check-publish.result == 'success' }}
runs-on: ubuntu-latest
environment: release
permissions:
contents: write # for creating GitHub release
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Download release notes
uses: actions/download-artifact@v4
with:
name: release-notes
path: ./artifacts
- name: Create release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
tag="${{ needs.check-publish.outputs.tag }}"
if gh release view "${tag}" > /dev/null 2>&1; then
echo "GitHub release ${tag} already exists. Skipping release creation."
exit 0
fi
if git ls-remote --exit-code --tags origin "refs/tags/${tag}" > /dev/null; then
echo "Tag ${tag} already exists on origin."
else
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "${tag}" "${GITHUB_SHA}" -m "${tag}"
gh auth setup-git
git push origin "refs/tags/${tag}"
echo "Created annotated tag ${tag} at ${GITHUB_SHA}."
fi
release_notes_file="./artifacts/release-notes.md"
if [ "${{ needs.check-publish.outputs.prerelease }}" = "true" ]; then
gh release create "${tag}" \
--verify-tag \
--title "${tag}" \
--notes-file "${release_notes_file}" \
--prerelease
else
gh release create "${tag}" \
--verify-tag \
--title "${tag}" \
--notes-file "${release_notes_file}"
fi