-
Notifications
You must be signed in to change notification settings - Fork 6
Add automated release workflows for the jsdoc and tracking-jsdoc packages
#232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1d6e00b
fe53ff5
38d9626
d857de8
d78b63c
f7ebe97
6168dce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| REPO_URL=$1 | ||
| TAG_NAME=$2 | ||
| PACKAGE_DIR=$3 | ||
| PACKAGE_NAME=$(basename "$PACKAGE_DIR") | ||
| SOURCE_SHA=$(git rev-parse HEAD) | ||
|
|
||
| TMP_BRANCH="tmp-js-pkg-release-build-${PACKAGE_NAME}" | ||
| TMP_BRANCH_PUSHED=false | ||
|
|
||
| cleanup() { | ||
| if [ "$TMP_BRANCH_PUSHED" = true ]; then | ||
| git push -d origin "$TMP_BRANCH" 2>/dev/null || true | ||
| fi | ||
| } | ||
| trap cleanup EXIT | ||
|
|
||
| # Use the github-actions bot account to commit. | ||
| # https://api.github.com/users/github-actions%5Bbot%5D | ||
| git config user.name github-actions[bot] | ||
| git config user.email 41898282+github-actions[bot]@users.noreply.github.com | ||
|
|
||
| # To move the package to the top directory: | ||
| ## 1. Delete all files from version control system. | ||
| git rm -r . | ||
| git commit -q -m "Create the ${TAG_NAME} release build for the \`${PACKAGE_NAME}\` package." | ||
|
|
||
| ## 2. Get the package files back. | ||
| git checkout HEAD^ -- "./${PACKAGE_DIR}" | ||
| git restore --staged . | ||
|
|
||
| ## 3. Remove files not needed in the release build. | ||
| ## This includes release-notes-config.yml and all dotfiles (e.g., .jsdocrc.dev.json, .npmrc). | ||
| rm -f "./${PACKAGE_DIR}/release-notes-config.yml" | ||
| find "./${PACKAGE_DIR}" -maxdepth 1 -name ".*" -not -name "." -exec rm -rf {} + | ||
|
|
||
| ## 4. Move the package contents to the top directory. | ||
| ## The glob * does not match dotfiles, which is fine since step 3 already removed them. | ||
| git add "./${PACKAGE_DIR}" | ||
| git mv "./${PACKAGE_DIR}"/* ./ | ||
|
|
||
| ## 5. Create the README to point to the source revision of this build. | ||
| tee README.md << END | ||
| # ${PACKAGE_NAME} | ||
| ### This is the release build of version \`${TAG_NAME}\`. | ||
| ### Please visit [here to view the source code of this version](${REPO_URL}/tree/${SOURCE_SHA}/${PACKAGE_DIR}). | ||
| END | ||
| git add README.md | ||
|
|
||
| ## 6. Complete the build for release. | ||
| git commit -q --amend -C HEAD | ||
|
|
||
| # The temporary branch is only for pushing to the remote repo. | ||
| # Tagging it with a version tag will be proceeded with a separate step. | ||
| git push origin "HEAD:refs/heads/$TMP_BRANCH" | ||
| TMP_BRANCH_PUSHED=true | ||
|
|
||
| # Deleting the temporary branch is cleanup, so a failure here should not fail an | ||
| # otherwise successful release. Leave it best-effort and let the EXIT trap retry. | ||
| if git push -d origin "$TMP_BRANCH"; then | ||
| TMP_BRANCH_PUSHED=false | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| name: JS Packages - Create Release | ||
|
|
||
| on: | ||
| pull_request_review: | ||
| types: | ||
| - submitted | ||
|
|
||
| jobs: | ||
| CreateRelease: | ||
| name: Create release for JS package | ||
| runs-on: ubuntu-latest | ||
| if: ${{ (github.event.pull_request.head.ref == 'release/jsdoc' || github.event.pull_request.head.ref == 'release/tracking-jsdoc') && github.event.review.state == 'approved' }} | ||
| steps: | ||
| - name: Derive package config | ||
| id: config | ||
| run: | | ||
| PACKAGE_NAME="${{ github.event.pull_request.head.ref }}" | ||
| PACKAGE_NAME="${PACKAGE_NAME#release/}" | ||
| PACKAGE_DIR="packages/js/${PACKAGE_NAME}" | ||
| TAG_PREFIX="${PACKAGE_NAME}-v" | ||
| echo "package-name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT | ||
| echo "package-dir=${PACKAGE_DIR}" >> $GITHUB_OUTPUT | ||
| echo "tag-prefix=${TAG_PREFIX}" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Create release | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const workspace = '${{ github.workspace }}'; | ||
| const { default: script } = await import( `${ workspace }/.github/scripts/create-release.mjs` ); | ||
| await script( { | ||
| github, | ||
| context, | ||
| outputJsonPath: '/tmp/release.json', | ||
| packageDir: '${{ steps.config.outputs.package-dir }}', | ||
| packageName: '${{ steps.config.outputs.package-name }}', | ||
| tagPrefix: '${{ steps.config.outputs.tag-prefix }}', | ||
| } ); | ||
|
|
||
| - name: Upload release artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: release | ||
| path: /tmp/release.json |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| name: JS Packages - Prepare New Release | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - release/jsdoc | ||
| - release/tracking-jsdoc | ||
|
|
||
| jobs: | ||
| CheckCreatedBranch: | ||
| name: Check Created Branch | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Check created release branch | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| if ( ! context.payload.created ) { | ||
| await github.rest.actions.cancelWorkflowRun( { | ||
| ...context.repo, | ||
| run_id: context.runId, | ||
| } ); | ||
| } | ||
|
|
||
| PrepareRelease: | ||
| name: Prepare Release | ||
| runs-on: ubuntu-latest | ||
| needs: CheckCreatedBranch | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Derive package config | ||
| id: config | ||
| run: | | ||
| PACKAGE_NAME="${{ github.ref_name }}" | ||
| PACKAGE_NAME="${PACKAGE_NAME#release/}" | ||
| PACKAGE_DIR="packages/js/${PACKAGE_NAME}" | ||
| TAG_TEMPLATE="${PACKAGE_NAME}-v{version}" | ||
| CONFIG_PATH="${PACKAGE_DIR}/release-notes-config.yml" | ||
| HAS_LOCK_FILE="false" | ||
| if [ -f "${PACKAGE_DIR}/package-lock.json" ]; then | ||
| HAS_LOCK_FILE="true" | ||
| fi | ||
| echo "package-name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT | ||
| echo "package-dir=${PACKAGE_DIR}" >> $GITHUB_OUTPUT | ||
| echo "tag-template=${TAG_TEMPLATE}" >> $GITHUB_OUTPUT | ||
| echo "config-path=${CONFIG_PATH}" >> $GITHUB_OUTPUT | ||
| echo "has-lock-file=${HAS_LOCK_FILE}" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Get release notes | ||
| id: get-notes | ||
| uses: woocommerce/grow/get-release-notes@actions-v2 | ||
| with: | ||
| repo-token: ${{ secrets.GITHUB_TOKEN }} | ||
| package-dir: ${{ steps.config.outputs.package-dir }} | ||
| config-path: ${{ steps.config.outputs.config-path }} | ||
| tag-template: ${{ steps.config.outputs.tag-template }} | ||
| minor-keywords: feature, update, enhancement | ||
|
|
||
| - name: Prepare release commits | ||
| run: | | ||
| PACKAGE_DIR="${{ steps.config.outputs.package-dir }}" | ||
| cd "./${PACKAGE_DIR}" | ||
|
|
||
| TODAY=$(date '+%Y-%m-%d') | ||
| NEXT_VER="${{ steps.get-notes.outputs.next-version }}" | ||
| CHANGELOG='${{ steps.get-notes.outputs.release-changelog-shell }}' | ||
| CHANGELOG=$(echo "$CHANGELOG" | sed -E 's/\.? by @[^ ]+ in (https:\/\/github\.com\/.+)/. (\1)/') | ||
|
|
||
| sed -i "/# Changelog/r"<( | ||
| printf "\n## ${TODAY} (${NEXT_VER})\n${CHANGELOG}\n" | ||
| ) CHANGELOG.md | ||
|
|
||
| jq ".version=\"${NEXT_VER}\"" package.json > package.json.tmp | ||
| mv package.json.tmp package.json | ||
|
|
||
| if [ "${{ steps.config.outputs.has-lock-file }}" = "true" ]; then | ||
| jq ".version=\"${NEXT_VER}\" | .packages.\"\".version=\"${NEXT_VER}\"" package-lock.json > package-lock.json.tmp | ||
| mv package-lock.json.tmp package-lock.json | ||
| fi | ||
|
|
||
| git config user.name github-actions[bot] | ||
| git config user.email 41898282+github-actions[bot]@users.noreply.github.com | ||
| git add CHANGELOG.md | ||
| git add package.json | ||
| if [ "${{ steps.config.outputs.has-lock-file }}" = "true" ]; then | ||
| git add package-lock.json | ||
| fi | ||
| cd - | ||
| git commit -q -m "Update changelog and package version for the ${{ steps.get-notes.outputs.next-tag }} release of ${{ steps.config.outputs.package-name }}." | ||
| git push | ||
|
|
||
| - name: Create a pull request for release | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const workspace = '${{ github.workspace }}'; | ||
| const { default: script } = await import( `${ workspace }/.github/scripts/create-pr-for-release.mjs` ); | ||
| await script( { | ||
| github, | ||
| context, | ||
| refName: '${{ github.ref_name }}', | ||
| version: '${{ steps.get-notes.outputs.next-version }}', | ||
| packageDir: '${{ steps.config.outputs.package-dir }}', | ||
| packageName: '${{ steps.config.outputs.package-name }}', | ||
| createReleaseWorkflow: 'js-packages-create-release.yml', | ||
| releaseWorkflow: 'js-packages-release.yml', | ||
| } ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| name: JS Packages - Release | ||
|
|
||
| on: | ||
| release: | ||
| types: | ||
| - published | ||
|
|
||
| workflow_run: | ||
| workflows: | ||
| - JS Packages - Create Release | ||
| types: | ||
| - completed | ||
| branches: | ||
| - release/jsdoc | ||
| - release/tracking-jsdoc | ||
|
|
||
| jobs: | ||
| Setup: | ||
| name: Setup and Checks | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| release: ${{ steps.set-result.outputs.release }} | ||
| steps: | ||
| - name: Check tag name or workflow_run conclusion | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const { payload, eventName } = context; | ||
| const tagReg = /^(jsdoc|tracking-jsdoc)-v(0|[1-9]\d*)(\.(0|[1-9]\d*)){2}(-pre)?$/; | ||
| const failedWorkflowRun = eventName === 'workflow_run' && payload.workflow_run.conclusion !== 'success'; | ||
| const mismatchedTagName = eventName === 'release' && ! tagReg.test( payload.release.tag_name ); | ||
|
|
||
| if ( failedWorkflowRun || mismatchedTagName ) { | ||
| await github.rest.actions.cancelWorkflowRun( { | ||
| ...context.repo, | ||
| run_id: context.runId, | ||
| } ); | ||
| } | ||
|
Comment on lines
+28
to
+38
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The automated release is created with the default |
||
|
|
||
| - name: Get release artifact | ||
| id: set-result | ||
| if: ${{ github.event.workflow_run.conclusion == 'success' }} | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const fs = require( 'fs' ); | ||
| const { data: { artifacts } } = await github.rest.actions.listWorkflowRunArtifacts( { | ||
| ...context.repo, | ||
| run_id: context.payload.workflow_run.id, | ||
| } ); | ||
|
|
||
| const artifact = artifacts.find( ( el ) => el.name === 'release' ); | ||
| const download = await github.rest.actions.downloadArtifact( { | ||
| ...context.repo, | ||
| artifact_id: artifact.id, | ||
| archive_format: 'zip', | ||
| } ); | ||
|
Comment on lines
+52
to
+57
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This step is gated by |
||
|
|
||
| fs.writeFileSync( `/tmp/release.zip`, Buffer.from( download.data ) ); | ||
| await exec.exec( 'unzip', [ '/tmp/release.zip', '-d', '/tmp' ] ); | ||
|
|
||
| const release = fs.readFileSync( `/tmp/release.json`, 'utf8' ); | ||
| core.setOutput( 'release', release ); | ||
|
|
||
| UpdateTags: | ||
| name: Create Release Build and Update Version Tags | ||
| runs-on: ubuntu-latest | ||
| needs: Setup | ||
| steps: | ||
| - name: Resolve tag name | ||
| id: resolve-tag | ||
| run: | | ||
| TAG_NAME="${{ github.event.release.tag_name }}" | ||
| if [ "$TAG_NAME" = '' ]; then | ||
| TAG_NAME="${{ fromJSON(needs.Setup.outputs.release || '{}').tag_name }}" | ||
| fi | ||
| echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Derive package config | ||
| id: config | ||
| run: | | ||
| TAG_NAME="${{ steps.resolve-tag.outputs.tag_name }}" | ||
| PACKAGE_NAME=$(echo "$TAG_NAME" | sed 's/-v[0-9].*//') | ||
| PACKAGE_DIR="packages/js/${PACKAGE_NAME}" | ||
| echo "package-name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT | ||
| echo "package-dir=${PACKAGE_DIR}" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ steps.resolve-tag.outputs.tag_name }} | ||
|
|
||
| - name: Create and commit release build | ||
| id: commit-build | ||
| run: | | ||
| REPO_URL="${{ github.server_url }}/${{ github.repository }}" | ||
| TAG_NAME="${{ steps.resolve-tag.outputs.tag_name }}" | ||
| PACKAGE_DIR="${{ steps.config.outputs.package-dir }}" | ||
|
|
||
| .github/scripts/js-packages-create-and-commit-build.sh "$REPO_URL" "$TAG_NAME" "$PACKAGE_DIR" | ||
|
|
||
| echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Update version tags | ||
| uses: woocommerce/grow/update-version-tags@actions-v2 | ||
| with: | ||
| repo-token: ${{ secrets.GITHUB_TOKEN }} | ||
| sha: ${{ steps.commit-build.outputs.sha }} | ||
| release: ${{ needs.Setup.outputs.release }} | ||
|
|
||
| MergeReleasePR: | ||
| name: Merge Release PR | ||
| if: ${{ github.event_name == 'workflow_run' }} | ||
| needs: UpdateTags | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| steps: | ||
| - name: Merge the release PR | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| gh pr merge "${{ github.event.workflow_run.head_branch }}" \ | ||
| --repo "${{ github.repository }}" \ | ||
| --merge \ | ||
| --delete-branch | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
major-keywordsinput is intentionally omitted here so it falls back to the action default, which is"breaking", as defined inget-release-notes/action.yml. With that default, only the "Breaking Changes" heading matches the major level, so regular releases are not bumped to major.