Update Node.js to v22.12.0 #299
Workflow file for this run
This file contains 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: 'Open Version Bump PR' | |
on: | |
workflow_dispatch: | |
inputs: | |
version-type: | |
description: 'How to tick the version (none, patch, minor, major)' | |
required: true | |
default: 'none' | |
dry-run: | |
description: 'If true, skips commit on npm version and passes the --dry-run flag to npm publish. Useful for testing.' | |
required: false | |
default: 'false' | |
pull_request: | |
types: | |
- closed | |
jobs: | |
preflight: # Check that labels include a version bump, and extract the version bump label | |
runs-on: ubuntu-latest | |
if: (github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' ) | |
outputs: | |
MESSAGE: ${{ steps.validate.outputs.MESSAGE }} | |
JOB_NAME: ${{ steps.set-job-name.outputs.JOB_NAME }} | |
VERSION_INSTRUCTION: ${{ steps.validate.outputs.VERSION_INSTRUCTION }} | |
steps: | |
- id: set-job-name | |
run: echo "JOB_NAME=${{ github.job }}" >> $GITHUB_OUTPUT | |
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
- id: validate | |
name: Validate that version bump labels are on the PR | |
uses: ./.github/actions/validate-version-labels | |
with: | |
LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') || inputs.version-type }} | |
createVersionBumpPR: | |
if: (github.event.pull_request.merged == true || inputs.version-type != 'none' || inputs.dry-run == 'true') && needs.preflight.outputs.VERSION_INSTRUCTION != 'version-bump' | |
runs-on: ubuntu-latest | |
needs: preflight # Will be skipped if preflight is skipped, which causes this to not run on closes w/o merge unless it's a manual run | |
outputs: | |
PR_NUMBER: ${{ steps.create-pr.outputs.pull-request-number }} | |
PR_URL: ${{ steps.create-pr.outputs.pull-request-url }} | |
MESSAGE: ${{ steps.validate-pr-change.outputs.MESSAGE || steps.tick-version.outputs.MESSAGE }} | |
JOB_NAME: ${{ steps.set-job-name.outputs.JOB_NAME }} | |
VERSION_TAG: ${{ steps.tick-version.outputs.VERSION_TAG }} | |
steps: | |
- id: set-job-name | |
run: echo "JOB_NAME=${{ github.job }}" >> $GITHUB_OUTPUT | |
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
with: | |
ref: ${{ github.base_ref || github.ref }} # Explicit ref required to push from PR because of github internals | |
- uses: ./.github/actions/set-git-credentials | |
- uses: ./.github/actions/setup | |
- id: tick-version | |
run: | | |
VERSION_INSTRUCTION=${{ needs.preflight.outputs.VERSION_INSTRUCTION }} | |
if [[ $VERSION_INSTRUCTION == "none" ]]; then | |
echo "No version bump required" | |
exit 0 | |
elif [[ -z $VERSION_INSTRUCTION ]]; then | |
MESSAGE="`createVersionBumpPR failed` because no `VERSION_INSTRUCTION` was provided. This is most likely because the `preflight` job or [validate_version_labels](./.github/actions/validate_version_labels) isn't producing output." | |
echo "MESSAGE=$MESSAGE" >> $GITHUB_OUTPUT | |
exit 1 | |
fi | |
if ! npm version $VERSION_INSTRUCTION ; then | |
MESSAGE="Failed to tick version with `npm version $VERSION_INSTRUCTION`" | |
echo "MESSAGE=$MESSAGE" >> $GITHUB_OUTPUT | |
exit 1 | |
fi | |
# Can't use `npm show . version` because that will show the version of the package in the registry, not the version in package.json, and we haven't published yet | |
PACKAGE_VERSION=$(cat package.json \ | |
| grep version \ | |
| head -1 \ | |
| awk -F: '{ print $2 }' \ | |
| sed 's/[", ]//g') | |
echo "VERSION_TAG=$PACKAGE_VERSION" >> $GITHUB_OUTPUT | |
- name: Create commit | |
id: create-commit | |
if: inputs.dry-run != 'true' && ${{ steps.tick-version.outputs.VERSION_TAG != '' }} | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
FILE_TO_COMMIT: package.json | |
VERSION_BRANCH: 'version-bump' | |
run: | | |
# start with creating new branch 'version-bump', also update the reference to the origin if it doesn't exist | |
git checkout -b $VERSION_BRANCH | |
if ! gh api -X GET /repos/:owner/:repo/git/ref/heads/$VERSION_BRANCH >/dev/null 2>&1; then | |
echo "creating new branch $VERSION_BRANCH" | |
gh api -X POST /repos/:owner/:repo/git/refs -f "ref=refs/heads/$VERSION_BRANCH" -f sha="$GITHUB_SHA" | |
fi | |
# get the content of package.json from $VERSION_BRANCH to get the current bump version | |
PACKAGE_JSON_CONTENT=$(gh api --method GET /repos/:owner/:repo/contents/$FILE_TO_COMMIT \ | |
-f "ref=refs/heads/$VERSION_BRANCH" \ | |
| jq -r '.content' \ | |
| base64 -d) | |
BUMP_VERSION=$(echo "$PACKAGE_JSON_CONTENT" | jq -r '.version') | |
NEW_VERSION="${{ steps.tick-version.outputs.VERSION_TAG }}" | |
echo "BUMP_VERSION $BUMP_VERSION" | |
echo "NEW_VERSION $NEW_VERSION" | |
#We need to compare and update the package version in version bump PR only if the new version is higher then current bump version. | |
#For example if PR labeled as 'minor' is merged, then only 'major' labeled PR will overwrite the version change, not the 'patch' or 'minor' labeled PRs. | |
# Split versions into major, minor, and patch components | |
IFS='.' read -r -a bump <<< "$BUMP_VERSION" | |
IFS='.' read -r -a new <<< "$NEW_VERSION" | |
HIGHEST_VERSION="equal" | |
# Compare major version number | |
if (( bump[0] > new[0] )); then | |
HIGHEST_VERSION="bump" | |
elif (( bump[0] < new[0] )); then | |
HIGHEST_VERSION="new" | |
else | |
# Compare minor version number | |
if (( bump[1] > new[1] )); then | |
HIGHEST_VERSION="bump" | |
elif (( bump[1] < new[1] )); then | |
HIGHEST_VERSION="new" | |
else | |
# Compare patch version number | |
if (( bump[2] > new[2] )); then | |
HIGHEST_VERSION="bump" | |
elif (( bump[2] < new[2] )); then | |
HIGHEST_VERSION="new" | |
fi | |
fi | |
fi | |
if [[ "$HIGHEST_VERSION" == "new" ]]; then | |
#new version is greater than current bump version, creating new commit | |
# move the branch pointer one commit backwards so that we can manually commit changes done by 'npm version ...' command | |
git reset HEAD~ | |
# create a commit with content of package.json. This will give us 'verified' commit label from github actions bot | |
MESSAGE="${{ steps.tick-version.outputs.VERSION_TAG }}" | |
SHA=$(gh api --method GET /repos/:owner/:repo/contents/$FILE_TO_COMMIT \ | |
-f "ref=refs/heads/$VERSION_BRANCH" \ | |
--jq '.sha') | |
CONTENT=$( base64 -i $FILE_TO_COMMIT ) | |
NEW_COMMIT_SHA=$(gh api --method PUT /repos/:owner/:repo/contents/$FILE_TO_COMMIT \ | |
--field message="$MESSAGE" \ | |
--field content="$CONTENT" \ | |
--field encoding="base64" \ | |
--field branch="$VERSION_BRANCH" \ | |
--field sha="$SHA" | jq -r '.commit.sha') | |
echo "UPDATED_VERSION_TAG=$NEW_VERSION" >> $GITHUB_OUTPUT | |
# create a tag from VERSION_TAG | |
TAG_RESPONSE=$(gh api --method POST /repos/:owner/:repo/git/tags \ | |
--field tag="v${{ steps.tick-version.outputs.VERSION_TAG }}" \ | |
--field message="${{ steps.tick-version.outputs.VERSION_TAG }}" \ | |
--field object="$NEW_COMMIT_SHA" \ | |
--field type="commit") | |
NEW_TAG_SHA=$(echo "$TAG_RESPONSE" | jq -r '.sha') | |
# update the reference so that the tag is visible in github | |
gh api --method POST /repos/:owner/:repo/git/refs \ | |
--field ref="refs/tags/v${{ steps.tick-version.outputs.VERSION_TAG }}" \ | |
--field sha="$NEW_TAG_SHA" | |
fi | |
echo "VERSION_BRANCH=$VERSION_BRANCH" >> $GITHUB_OUTPUT | |
- name: Update code coverage badge | |
if: ${{ steps.tick-version.outputs.VERSION_TAG != '' }} | |
continue-on-error: true | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
# get the current and new code coverage result and update the coverage badge if needed | |
VERSION_BRANCH="${{ steps.create-commit.outputs.VERSION_BRANCH }}" | |
COVERAGE_BRANCH="coverage-pr-${{ github.event.number }}" | |
COVERAGE_FILE="coverage/coverage-summary.json" | |
# read the content of readme from $VERSION_BRANCH | |
README_CONTENT=$(gh api --method GET /repos/:owner/:repo/contents/README.md \ | |
-f "ref=refs/heads/$VERSION_BRANCH" \ | |
| jq -r '.content' \ | |
| base64 -d) | |
CURRENT_COVERAGE=$(echo "$README_CONTENT" | grep -o 'coverage-[0-9]*\(\.[0-9]\{1,\}\)\?%' | sed 's/coverage-\(.*\)%/\1/') | |
echo "Current code coverage - $CURRENT_COVERAGE" | |
# read the content of coverage-report.json from $COVERAGE_BRANCH and get the coverage number for statements | |
COVERAGE_REPORT=$(gh api --method GET /repos/:owner/:repo/contents/$COVERAGE_FILE \ | |
-f "ref=refs/heads/$COVERAGE_BRANCH" \ | |
| jq -r '.content' \ | |
| base64 -d) | |
NEW_COVERAGE=$(echo "$COVERAGE_REPORT" | jq '.total.statements.pct') | |
echo "New code coverage - $NEW_COVERAGE" | |
if [ "$NEW_COVERAGE" != "$CURRENT_COVERAGE" ]; then | |
# change the README content by replacing coverage percentage | |
FILE_TO_COMMIT="README.md" | |
awk -v new_coverage="$NEW_COVERAGE" '{gsub(/!\[Coverage\]\(https:\/\/img\.shields\.io\/badge\/coverage-[0-9]{1,3}(\.[0-9]{1,4})?%25-green\)/,"![Coverage](https://img.shields.io/badge/coverage-" new_coverage "%25-green)")}1' "$FILE_TO_COMMIT" > tmp && mv tmp "$FILE_TO_COMMIT" | |
# commit changes to repo | |
SHA=$(gh api --method GET /repos/:owner/:repo/contents/$FILE_TO_COMMIT \ | |
-f "ref=refs/heads/$VERSION_BRANCH" \ | |
--jq '.sha') | |
CONTENT=$( base64 -i $FILE_TO_COMMIT ) | |
gh api --method PUT /repos/:owner/:repo/contents/$FILE_TO_COMMIT \ | |
--field message="update code coverage badge" \ | |
--field content="$CONTENT" \ | |
--field encoding="base64" \ | |
--field branch="$VERSION_BRANCH" \ | |
--field sha="$SHA" | |
fi | |
- name: Generate reference tables | |
if: ${{ steps.tick-version.outputs.VERSION_TAG != '' }} | |
continue-on-error: true | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
yarn generate-ref-tables | |
# Since package.json is modified locally from 'create-commit' step, we filter it out so we have only autogenerated doc files | |
# Same for README.md as it might be modified from 'code coverage badge' step. | |
FILES=$(git diff --name-only HEAD | grep -Ev 'package.json|README.md') | |
VERSION_BRANCH="${{ steps.create-commit.outputs.VERSION_BRANCH }}" | |
# Loop over each generated file and commit them one by one. It's not possible to commit multiple files using `gh api` | |
for FILENAME in $FILES; do | |
SHA=$(gh api --method GET /repos/:owner/:repo/contents/"$FILENAME" \ | |
-f "ref=refs/heads/$VERSION_BRANCH" \ | |
--jq '.sha') | |
CONTENT=$(base64 -i "$FILENAME") | |
gh api --method PUT /repos/:owner/:repo/contents/"$FILENAME" \ | |
--field message="update $FILENAME" \ | |
--field content="$CONTENT" \ | |
--field encoding="base64" \ | |
--field branch="$VERSION_BRANCH" \ | |
--field sha="$SHA" | |
done | |
- name: Create PR | |
id: create-pr | |
if: inputs.dry-run != 'true' && ${{ steps.tick-version.outputs.VERSION_TAG != '' }} | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
# at this point either new branch with new commit is created so we open PR, or we get the open PR and set the outputs | |
pullRequest=$(gh api --method GET "/repos/:owner/:repo/pulls" --jq ".[] | select(.head.ref == \"${{ steps.create-commit.outputs.VERSION_BRANCH }}\" and .state == \"open\") | {url: .html_url, number: .number}") | |
if [[ -z "$pullRequest" ]]; then | |
echo "No pull requests found for branch ${{ steps.create-commit.outputs.VERSION_BRANCH }}, creating new PR" | |
response=$(gh api --method POST /repos/:owner/:repo/pulls \ | |
--field title="Bump version to ${{ steps.tick-version.outputs.VERSION_TAG }}" \ | |
--field body="This PR bumps the version to ${{ steps.tick-version.outputs.VERSION_TAG }}" \ | |
--field head="${{ steps.create-commit.outputs.VERSION_BRANCH }}" \ | |
--field base="${{ github.base_ref || 'main' }}") | |
pr_url=$(echo $response | jq -r '.html_url') | |
pr_number=$(echo $response | jq -r '.number') | |
echo "pull-request-number=$pr_number" >> $GITHUB_OUTPUT | |
echo "pull-request-url=$pr_url" >> $GITHUB_OUTPUT | |
# as a last step we create a label for PR | |
gh api --method POST "/repos/:owner/:repo/issues/$pr_number/labels" -F "labels[]=version-bump" | |
else | |
echo "Pull requests found for branch ${{ steps.create-commit.outputs.VERSION_BRANCH }}, setting outputs" | |
pr_url=$(echo "$pullRequest" | jq -r '.url') | |
pr_number=$(echo "$pullRequest" | jq -r '.number') | |
#update the title and description of PR if there is new version bump commit | |
if [ "${{ steps.create-commit.outputs.UPDATED_VERSION_TAG }}" != "" ]; then | |
echo "Found new version tag ${{ steps.create-commit.outputs.UPDATED_VERSION_TAG }} for PR, updating title and description" | |
gh api --method PATCH /repos/:owner/:repo/pulls/$pr_number \ | |
--field title="Bump version to ${{ steps.create-commit.outputs.UPDATED_VERSION_TAG }}" \ | |
--field body="This PR bumps the version to ${{ steps.create-commit.outputs.UPDATED_VERSION_TAG }}" | |
fi | |
echo "pull-request-number=$pr_number" >> $GITHUB_OUTPUT | |
echo "pull-request-url=$pr_url" >> $GITHUB_OUTPUT | |
fi | |
post-result-to-pr: | |
runs-on: ubuntu-latest | |
if: ${{ always() }} # Always run, even if a previous step fails, since we always want to post a result message/comment | |
needs: | |
- preflight | |
- createVersionBumpPR | |
steps: | |
- name: Post success or failure comment to PR | |
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 | |
with: | |
script: | | |
// `needs` isn't loaded into `actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1`.context, so we have to read it from the outer context | |
// Using toJSON will dump a string representation of the object verbatim, which effective generates code for the `needs` javascript variable below | |
const needs = ${{ toJSON(needs) }} | |
// Note, it's possible to iterate over jobs to show multiple failures, but we instead consolidate since each job depends on the prior. | |
// needs["JOB"] will be undefined for skipped jobs, so spreading will show the latest failure in the chain | |
const { MESSAGE, JOB_NAME, VERSION_TAG, PR_URL } = { | |
...needs?.preflight?.outputs, | |
...needs?.createVersionBumpPR?.outputs, | |
} | |
// delete the coverage-pr branch if it exists | |
const ref = `heads/coverage-pr-${context.payload.pull_request.number}` | |
try { | |
const coverageRef = await github.rest.git.getRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref, | |
}); | |
if (coverageRef && coverageRef.data && coverageRef.data.ref) { | |
await github.rest.git.deleteRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref, | |
}); | |
} | |
}catch(error) {} | |
// This workflow always runs on PR close, and this job always runs because of always(). | |
// We have limited access to PR context, so check for a universally set param to determine if the PR was merged | |
// A skipped job is a successful job, so we can't check skipped. | |
if(!JOB_NAME) { | |
core.notice("No job name found, won't post comment to PR") | |
return | |
} | |
const failed = needs?.preflight?.result == 'failure' || needs?.createVersionBumpPR?.result == 'failure' | |
const cancelled = needs?.preflight?.result == 'cancelled' || needs?.createVersionBumpPR?.result == 'cancelled' | |
const cancelledMessage = `:x: ${JOB_NAME} was cancelled.` | |
let failureMessage = `:x: failed to run create-pr/publish workflow` | |
let successMessage = `:white_check_mark: successfully ran create-pr/publish workflow.` | |
if (JOB_NAME === 'createVersionBumpPR') { | |
successMessage = `:rocket: Successfully created version bump PR: ${PR_URL}` | |
failureMessage = `:x: failed to create version bump PR` | |
} else if (JOB_NAME === 'preflight') { | |
successMessage = `:rocket: Successfully ran preflight checks.` | |
failureMessage = `:x: failed to run preflight checks` | |
} | |
failureMessage = `${failureMessage} | |
Failure message: ${MESSAGE || "No message provided"} | |
Failed job: [${JOB_NAME || "Unknown"}](https://github.com/smartcontractkit/ea-framework-js/actions/runs/${{ github.run_id }})` | |
const body = failed ? failureMessage : cancelled ? cancelledMessage : successMessage | |
if(context.eventName == 'pull_request'){ | |
core.notice("This is a pr, posting comment") | |
github.rest.issues.createComment({ | |
issue_number: context.issue.number, | |
owner: context.repo.owner, // Required for Rest API route, not used for comment | |
repo: context.repo.repo, | |
body | |
}) | |
} | |
// Regardless of whether we create a PR comment, add an error message to the job so we can see it in the Actions tab | |
(failed || cancelled) ? core.error(body) : core.notice(body) | |
github-token: ${{secrets.GITHUB_TOKEN}} |