diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml new file mode 100644 index 0000000..17712c8 --- /dev/null +++ b/.github/workflows/auto-tag.yml @@ -0,0 +1,12 @@ +name: Auto-tag +on: + push: + tags: + - '*.*.*' +jobs: + auto-tag: + name: Auto-tag + runs-on: ubuntu-latest + steps: + - name: Auto-tag + uses: silverstripe/gha-auto-tag@v1 diff --git a/.github/workflows/keepalive.yml b/.github/workflows/keepalive.yml new file mode 100644 index 0000000..6795933 --- /dev/null +++ b/.github/workflows/keepalive.yml @@ -0,0 +1,17 @@ +name: Keepalive + +on: + # The 7th of every month at 10:40am UTC + schedule: + - cron: '40 10 7 * *' + workflow_dispatch: + +jobs: + keepalive: + name: Keepalive + # Only run cron on the silverstripe account + if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule') + runs-on: ubuntu-latest + steps: + - name: Keepalive + uses: silverstripe/gha-keepalive@v1 diff --git a/.github/workflows/merge-up.yml b/.github/workflows/merge-up.yml new file mode 100644 index 0000000..7d4d6db --- /dev/null +++ b/.github/workflows/merge-up.yml @@ -0,0 +1,17 @@ +name: Merge-up + +on: + # At 2:40 PM UTC, only on Thursday + schedule: + - cron: '40 14 * * 4' + workflow_dispatch: + +jobs: + merge-up: + name: Merge-up + # Only run cron on the silverstripe account + if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule') + runs-on: ubuntu-latest + steps: + - name: Merge-up + uses: silverstripe/gha-merge-up@v1 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f86cb0e --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2023, Silverstripe Limited - www.silverstripe.com +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index ce67f3c..7ccd91e 100644 --- a/README.md +++ b/README.md @@ -1 +1,11 @@ -# gha-gauge-release \ No newline at end of file +# GitHub Actions - Gauage release + +GitHub action to gauge whether to do a patch release + +Action is designed to only be used as part of [gha-ci](https://github.com/silverstripe/gha-ci) and [gha-action-ci](https://github.com/silverstripe/gha-action-ci) + +## Inputs + +### Latest local sha +Required - The result of $(git rev-parse HEAD) +`latest_local_sha: `f22dbc6ec6118096c8ccccee1ca0074bfb2f2291` diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..c0d1c00 --- /dev/null +++ b/action.yml @@ -0,0 +1,143 @@ +name: Gauge release +description: Gauge if should do a patch release + +inputs: + latest_local_sha: + description: The latest local sha + required: true + type: string + +outputs: + do_release: + description: Whether to do a release + value: ${{ steps.gauge-release.outputs.do_release }} + next_tag: + description: The patch tag to release + value: ${{ steps.gauge-release.outputs.next_tag }} + +runs: + using: composite + steps: + - name: Gauge release + id: gauge-release + shell: bash + env: + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SHA: ${{ github.sha }} + LATEST_LOCAL_SHA: ${{ inputs.latest_local_sha }} + run: | + DO_RELEASE=1 + + # Double check that LATEST_LOCAL_SHA matches GITHUB_SHA + echo "LATEST_LOCAL_SHA is $LATEST_LOCAL_SHA" + echo "GITHUB_SHA is $GITHUB_SHA" + if [[ $LATEST_LOCAL_SHA != $GITHUB_SHA ]]; then + echo "Not patch releasing because GITHUB_SHA is not equal to latest local sha" + DO_RELEASE=0 + fi + + # Must be on a minor branch to do a patch release + if [[ $DO_RELEASE == "1" ]]; then + if ! [[ $GITHUB_REF_NAME =~ ^[0-9]+\.[0-9]+$ ]]; then + echo "Not patch releasing because not on a minor branch" + DO_RELEASE=0 + fi + fi + + # Validate that this commit is that latest commit for the branch using GitHub API + # We need to check this in case re-rerunning an old job and there have been new commits since + if [[ $DO_RELEASE == "1" ]]; then + # https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28 + RESP_CODE=$(curl -w %{http_code} -s -o __response.json \ + -X GET "https://api.github.com/repos/${GITHUB_REPOSITORY}/commits?sha=${GITHUB_REF_NAME}&per_page=1" \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ github.token }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + ) + if [[ $RESP_CODE != "200" ]]; then + echo "Unable to read list of commits - HTTP response code was $RESP_CODE" + exit 1 + fi + LATEST_REMOTE_SHA=$(jq -r '.[0].sha' __response.json) + echo "LATEST_REMOTE_SHA is $LATEST_REMOTE_SHA" + echo "LATEST_LOCAL_SHA is $LATEST_LOCAL_SHA" + if [[ $LATEST_REMOTE_SHA != $LATEST_LOCAL_SHA ]]; then + echo "Not patch releasing because latest remote sha is not equal to latest local sha" + DO_RELEASE=0 + fi + # Also validate the sha matches GITHUB_SHA, which is what gha-tag-release will use + if [[ $GITHUB_SHA != $LATEST_LOCAL_SHA ]]; then + echo "Not patch releasing because GITHUB_SHA is not equal to latest local sha" + DO_RELEASE=0 + fi + fi + + # Check is there is an existing tag on the branch using GitHub API + # Note cannot use local `git tag` because actions/checkout by default will not checkout tags + # and you need to checkout full history in order to get them + LATEST_TAG="" + NEXT_TAG="" + if [[ $DO_RELEASE == "1" ]]; then + # https://docs.github.com/en/rest/git/refs?apiVersion=2022-11-28#list-matching-references + RESP_CODE=$(curl -w %{http_code} -s -o __response.json \ + -X GET https://api.github.com/repos/${GITHUB_REPOSITORY}/git/matching-refs/tags/${GITHUB_REF_NAME} \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ github.token }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + ) + if [[ $RESP_CODE != "200" ]]; then + echo "Unable to read list of tags - HTTP response code was $RESP_CODE" + exit 1 + fi + # Get the latest tag + LATEST_TAG=$(jq -r '.[].ref' __response.json | grep -Po '(?<=^refs\/tags\/)[0-9]+\.[0-9]+\.[0-9]+$' | sort -V -r | head -n 1) || true + echo "LATEST_TAG is $LATEST_TAG" + echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT + if ! [[ $LATEST_TAG =~ ([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then + echo "Not patch releasing because cannot find a matching semver tag on the branch" + DO_RELEASE=0 + else + MAJOR=${BASH_REMATCH[1]} + MINOR=${BASH_REMATCH[2]} + PATCH=${BASH_REMATCH[3]} + NEXT_TAG="$MAJOR.$MINOR.$((PATCH+1))" + echo "NEXT_TAG is $NEXT_TAG" + echo "next_tag=$NEXT_TAG" >> $GITHUB_OUTPUT + fi + fi + + # Check if there is anything relevant to release using GitHub API + # The API results include all merged pull-requests, not commits + # Pull-requests prefixed with MNT or DOC will not be considered relevant for releasing + if [[ $DO_RELEASE == "1" ]]; then + # Check on github release notes api if there's anything worth releasing + # Compare commits between current sha with latest tag to see if there is anything worth releasing + # https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits + RESP_CODE=$(curl -w %{http_code} -s -o __response.json \ + -X GET https://api.github.com/repos/$GITHUB_REPOSITORY/compare/$LATEST_TAG...$GITHUB_SHA?per_page=100 \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ github.token }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + ) + if [[ $RESP_CODE != "200" ]]; then + echo "Unable to fetch compare two commits - HTTP response code was $RESP_CODE" + exit 1 + fi + # Get commits for text parsing + jq -r '.commits[].commit.message' __response.json > __commits.json + # Parse comits one line at a time + HAS_THINGS_TO_RELEASE=0 + while IFS="" read -r line || [[ -n $line ]]; do + if ! [[ "$line" =~ ^(Merge|MNT|DOC) ]] && ! [[ $line =~ ^[[:space:]]*$ ]]; then + HAS_THINGS_TO_RELEASE=1 + break + fi + done < __commits.json + if [[ $HAS_THINGS_TO_RELEASE == "0" ]]; then + echo "Not patch releasing because there is nothing relevant to release" + DO_RELEASE=0 + fi + fi + echo "do_release output is $DO_RELEASE" + echo "do_release=$DO_RELEASE" >> $GITHUB_OUTPUT