diff --git a/.github/workflows/check-dist.yml b/.github/workflows/check-dist.yml index 5c4bd8b0c..63503a72d 100644 --- a/.github/workflows/check-dist.yml +++ b/.github/workflows/check-dist.yml @@ -9,15 +9,33 @@ # expected from the build. name: Check Transpiled JavaScript +# This workflow will only run on PRs targeting `main` and direct pushes to +# `main`. It will only run if the listed files/paths are modified (e.g. there is +# no need to run this workflow when documentation files are modified). on: pull_request: branches: - main + paths: + - src/** + - .node-version + - .prettierrc.json + - package.json + - package-lock.json + - tsconfig.json push: branches: - main + paths: + - src/** + - .node-version + - .prettierrc.json + - package.json + - package-lock.json + - tsconfig.json permissions: + checks: write contents: read jobs: @@ -47,7 +65,7 @@ jobs: # This will fail the workflow if the `dist/` directory is different than # expected. - - name: Compare Directories + - name: Compare Expected and Actual Directories id: diff run: | if [ ! -d dist/ ]; then diff --git a/.github/workflows/continuous-delivery.yml b/.github/workflows/continuous-delivery.yml new file mode 100644 index 000000000..683c4c75c --- /dev/null +++ b/.github/workflows/continuous-delivery.yml @@ -0,0 +1,60 @@ +# This workflow creates a new release any time a PR is merged that includes an +# update to the version listed in `package.json`. This ensures that the releases +# are always in sync with the version listed in the package manifest. +name: Continuous Delivery + +on: + pull_request: + types: + - closed + branches: + - main + workflow_dispatch: + +permissions: + contents: write + +jobs: + release: + name: Release Version + runs-on: ubuntu-latest + + # Only run this job if the workflow was triggered manually or a + # non-Dependabot PR was merged. + if: | + github.event_name == 'workflow_dispatch' || + (github.event.pull_request.merged == true && + startsWith(github.head_ref, 'dependabot/') == false) + + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + # Parse the version from package.json and, if not already present, + # publish a new action version. + - name: Tag + id: tag + uses: issue-ops/semver@v2 + with: + manifest-path: package.json + workspace: ${{ github.workspace }} + ref: main + + # Always overwrite if the workflow was triggered manually. + overwrite: ${{ github.event_name == 'workflow_dispatch' }} + + # Create a release using the tag from the previous step. The release will + # always be created if the workflow was triggered manually, but will only + # be created on PR merge if the tag step ran successfully. + - if: | + github.event_name == 'workflow_dispatch' || + steps.tag.outcome == 'success' + name: Create Release + id: release + uses: issue-ops/releaser@v2 + with: + tag: v${{ steps.tag.outputs.version }} diff --git a/.github/workflows/ci.yml b/.github/workflows/continuous-integration.yml similarity index 90% rename from .github/workflows/ci.yml rename to .github/workflows/continuous-integration.yml index c6d2861c7..d27af80d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/continuous-integration.yml @@ -1,3 +1,5 @@ +# This workflow performs CI tests to ensure that the code reaching `main` +# has been tested and validated. name: Continuous Integration on: @@ -9,6 +11,7 @@ on: - main permissions: + checks: write contents: read jobs: diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml new file mode 100644 index 000000000..ebdcd61a1 --- /dev/null +++ b/.github/workflows/version-check.yml @@ -0,0 +1,41 @@ +# This workflow checks the version of the action that will be published by the +# current pull request. If the version has already been published, the workflow +# fails in order to prevent PRs from being merged until the version has been +# incremented in the package.json manifest file. +name: Version Check + +on: + pull_request: + branches: + - main + +env: + MANIFEST_PATH: package.json + +permissions: + checks: write + contents: read + pull-requests: write + +jobs: + check-version: + name: Version Check + runs-on: ubuntu-latest + + # Skips Dependabot PRs + if: ${{ startsWith(github.head_ref, 'dependabot/') == false }} + + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Check Version + id: check-version + uses: issue-ops/semver@v2 + with: + check-only: true + manifest-path: ${{ env.MANIFEST_PATH }} diff --git a/.gitignore b/.gitignore index 80a899159..98b5f4493 100644 --- a/.gitignore +++ b/.gitignore @@ -100,3 +100,6 @@ __tests__/runner/* # IDE files .idea *.code-workspace + +# Jest JUnit files +reports/ diff --git a/README.md b/README.md index 43b8c21a2..ff70fbf96 100644 --- a/README.md +++ b/README.md @@ -237,27 +237,25 @@ steps: ## Publishing a New Release -This project includes a helper script, [`script/release`](./script/release) -designed to streamline the process of tagging and pushing new releases for -GitHub Actions. - -GitHub Actions allows users to select a specific version of the action to use, -based on release tags. This script simplifies this process by performing the -following steps: - -1. **Retrieving the latest release tag:** The script starts by fetching the most - recent SemVer release tag of the current branch, by looking at the local data - available in your repository. -1. **Prompting for a new release tag:** The user is then prompted to enter a new - release tag. To assist with this, the script displays the tag retrieved in - the previous step, and validates the format of the inputted tag (vX.X.X). The - user is also reminded to update the version field in package.json. -1. **Tagging the new release:** The script then tags a new release and syncs the - separate major tag (e.g. v1, v2) with the new release tag (e.g. v1.0.0, - v2.1.2). When the user is creating a new major release, the script - auto-detects this and creates a `releases/v#` branch for the previous major - version. -1. **Pushing changes to remote:** Finally, the script pushes the necessary - commits, tags and branches to the remote repository. From here, you will need - to create a new release in GitHub so users can easily reference the new tags - in their workflows. +This project includes two workflow files, `continuous-integration.yml` and +`continuous-delivery.yml` that are used to build, test, and publish new releases +of the action. + +The `continuous-integration.yml` workflow is triggered on every push to a pull +request branch. It will run unit tests and add a comment to the pull request +with the test results. + +The `continuous-delivery.yml` workflow is triggered when a pull request is +merged to the `main` branch. It will create a new release of the action based on +the version specified in the `version` property of the `package.json` file. + +The steps to publish a new version are as follows: + +1. Create a feature branch +1. Make changes to the action code +1. Add tests for the changes +1. Update the `version` property in the `package.json` file +1. Commit and push the changes to the feature branch +1. Open a pull request to merge the changes to the `main` branch + +After the pull request is merged, a new release will be created automatically. diff --git a/script/release b/script/release deleted file mode 100755 index 9e23fd2d9..000000000 --- a/script/release +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/bash - -# Exit early -# See: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#The-Set-Builtin -set -e - -# About: -# -# This is a helper script to tag and push a new release. GitHub Actions use -# release tags to allow users to select a specific version of the action to use. -# -# See: https://github.com/actions/typescript-action#publishing-a-new-release -# See: https://github.com/actions/toolkit/blob/master/docs/action-versioning.md#recommendations -# -# This script will do the following: -# -# 1. Retrieve the latest release tag -# 2. Display the latest release tag -# 3. Prompt the user for a new release tag -# 4. Validate the new release tag -# 5. Remind user to update the version field in package.json -# 6. Tag a new release -# 7. Set 'is_major_release' variable -# 8. Point separate major release tag (e.g. v1, v2) to the new release -# 9. Push the new tags (with commits, if any) to remote -# 10. If this is a major release, create a 'releases/v#' branch and push -# -# Usage: -# -# script/release - -# Variables -semver_tag_regex='v[0-9]+\.[0-9]+\.[0-9]+$' -semver_tag_glob='v[0-9].[0-9].[0-9]*' -git_remote='origin' -major_semver_tag_regex='\(v[0-9]*\)' - -# Terminal colors -OFF='\033[0m' -BOLD_RED='\033[1;31m' -BOLD_GREEN='\033[1;32m' -BOLD_BLUE='\033[1;34m' -BOLD_PURPLE='\033[1;35m' -BOLD_UNDERLINED='\033[1;4m' -BOLD='\033[1m' - -# 1. Retrieve the latest release tag -if ! latest_tag=$(git describe --abbrev=0 --match="$semver_tag_glob"); then - # There are no existing release tags - echo -e "No tags found (yet) - Continue to create and push your first tag" - latest_tag="[unknown]" -fi - -# 2. Display the latest release tag -echo -e "The latest release tag is: ${BOLD_BLUE}${latest_tag}${OFF}" - -# 3. Prompt the user for a new release tag -read -r -p 'Enter a new release tag (vX.X.X format): ' new_tag - -# 4. Validate the new release tag -if echo "$new_tag" | grep -q -E "$semver_tag_regex"; then - # Release tag is valid - echo -e "Tag: ${BOLD_BLUE}$new_tag${OFF} is valid syntax" -else - # Release tag is not in `vX.X.X` format - echo -e "Tag: ${BOLD_BLUE}$new_tag${OFF} is ${BOLD_RED}not valid${OFF} (must be in ${BOLD}vX.X.X${OFF} format)" - exit 1 -fi - -# 5. Remind user to update the version field in package.json -echo -e -n "Make sure the version field in package.json is ${BOLD_BLUE}$new_tag${OFF}. Yes? [Y/${BOLD_UNDERLINED}n${OFF}] " -read -r YN - -if [[ ! ($YN == "y" || $YN == "Y") ]]; then - # Package.json version field is not up to date - echo -e "Please update the package.json version to ${BOLD_PURPLE}$new_tag${OFF} and commit your changes" - exit 1 -fi - -# 6. Tag a new release -git tag "$new_tag" --annotate --message "$new_tag Release" -echo -e "Tagged: ${BOLD_GREEN}$new_tag${OFF}" - -# 7. Set 'is_major_release' variable -latest_major_release_tag=$(expr "$latest_tag" : "$major_semver_tag_regex") -new_major_release_tag=$(expr "$new_tag" : "$major_semver_tag_regex") - -if ! [[ "$new_major_release_tag" = "$latest_major_release_tag" ]]; then - is_major_release='yes' -else - is_major_release='no' -fi - -# 8. Point separate major release tag (e.g. v1, v2) to the new release -if [ $is_major_release = 'yes' ]; then - # Create a new major verison tag and point it to this release - git tag "$new_major_release_tag" --annotate --message "$new_major_release_tag Release" - echo -e "New major version tag: ${BOLD_GREEN}$new_major_release_tag${OFF}" -else - # Update the major verison tag to point it to this release - git tag "$latest_major_release_tag" --force --annotate --message "Sync $latest_major_release_tag tag with $new_tag" - echo -e "Synced ${BOLD_GREEN}$latest_major_release_tag${OFF} with ${BOLD_GREEN}$new_tag${OFF}" -fi - -# 9. Push the new tags (with commits, if any) to remote -git push --follow-tags - -if [ $is_major_release = 'yes' ]; then - # New major version tag is pushed with the '--follow-tags' flags - echo -e "Tags: ${BOLD_GREEN}$new_major_release_tag${OFF} and ${BOLD_GREEN}$new_tag${OFF} pushed to remote" -else - # Force push the updated major version tag - git push $git_remote "$latest_major_release_tag" --force - echo -e "Tags: ${BOLD_GREEN}$latest_major_release_tag${OFF} and ${BOLD_GREEN}$new_tag${OFF} pushed to remote" -fi - -# 10. If this is a major release, create a 'releases/v#' branch and push -if [ $is_major_release = 'yes' ]; then - git branch "releases/$latest_major_release_tag" "$latest_major_release_tag" - echo -e "Branch: ${BOLD_BLUE}releases/$latest_major_release_tag${OFF} created from ${BOLD_BLUE}$latest_major_release_tag${OFF} tag" - git push --set-upstream $git_remote "releases/$latest_major_release_tag" - echo -e "Branch: ${BOLD_GREEN}releases/$latest_major_release_tag${OFF} pushed to remote" -fi - -# Completed -echo -e "${BOLD_GREEN}Done!${OFF}"