Auto Release #308
This file contains hidden or 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: Auto Release | |
| on: | |
| workflow_run: | |
| workflows: ["Build"] | |
| types: [completed] | |
| concurrency: | |
| group: auto-release-master | |
| cancel-in-progress: false | |
| jobs: | |
| auto-release: | |
| runs-on: ubuntu-latest | |
| if: | | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_branch == 'master' && | |
| !contains(github.event.workflow_run.head_commit.message, '[skip-release]') | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| fetch-depth: 0 | |
| token: ${{ secrets.GR_TOKEN }} | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| - name: Get current version | |
| id: current_version | |
| run: | | |
| CURRENT_VERSION=$(node -p "require('./package.json').version") | |
| echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT | |
| echo "Current version: $CURRENT_VERSION" | |
| - name: Determine version increment | |
| id: version_increment | |
| env: | |
| COMMIT_MSG: ${{ github.event.workflow_run.head_commit.message }} | |
| run: | | |
| # Check commit message for version increment hints | |
| if [[ "$COMMIT_MSG" == *"[major]"* ]]; then | |
| INCREMENT_TYPE="major" | |
| elif [[ "$COMMIT_MSG" == *"[minor]"* ]] || [[ "$COMMIT_MSG" == *"feat:"* ]] || [[ "$COMMIT_MSG" == *"feature:"* ]]; then | |
| INCREMENT_TYPE="minor" | |
| else | |
| INCREMENT_TYPE="patch" | |
| fi | |
| echo "increment_type=$INCREMENT_TYPE" >> $GITHUB_OUTPUT | |
| echo "Version increment type: $INCREMENT_TYPE" | |
| - name: Calculate new version | |
| id: new_version | |
| run: | | |
| CURRENT="${{ steps.current_version.outputs.current }}" | |
| INCREMENT_TYPE="${{ steps.version_increment.outputs.increment_type }}" | |
| # Split version into parts | |
| IFS='.' read -ra VERSION_PARTS <<< "$CURRENT" | |
| MAJOR=${VERSION_PARTS[0]} | |
| MINOR=${VERSION_PARTS[1]} | |
| PATCH=${VERSION_PARTS[2]} | |
| # Increment based on type | |
| case $INCREMENT_TYPE in | |
| "major") | |
| MAJOR=$((MAJOR + 1)) | |
| MINOR=0 | |
| PATCH=0 | |
| ;; | |
| "minor") | |
| MINOR=$((MINOR + 1)) | |
| PATCH=0 | |
| ;; | |
| "patch") | |
| PATCH=$((PATCH + 1)) | |
| ;; | |
| esac | |
| NEW_VERSION="$MAJOR.$MINOR.$PATCH" | |
| echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT | |
| echo "New version: $NEW_VERSION" | |
| - name: Prepare release target | |
| id: release_target | |
| run: | | |
| set -euo pipefail | |
| NEW_VERSION="${{ steps.new_version.outputs.new_version }}" | |
| WORKFLOW_SHA="${{ github.event.workflow_run.head_sha }}" | |
| git fetch origin master:refs/remotes/origin/master --tags | |
| REMOTE_SHA=$(git rev-parse origin/master) | |
| if [ "$REMOTE_SHA" = "$WORKFLOW_SHA" ]; then | |
| echo "should_release=true" >> "$GITHUB_OUTPUT" | |
| echo "mode=create" >> "$GITHUB_OUTPUT" | |
| echo "Release target is current master" | |
| exit 0 | |
| fi | |
| REMOTE_VERSION=$(git show origin/master:package.json | node -p "JSON.parse(require('fs').readFileSync(0, 'utf8')).version") | |
| REMOTE_SUBJECT=$(git log -1 --pretty=%s origin/master) | |
| EXPECTED_SUBJECT="chore: bump version to v$NEW_VERSION [skip-release] [skip ci]" | |
| if [ "$REMOTE_VERSION" = "$NEW_VERSION" ] && [ "$REMOTE_SUBJECT" = "$EXPECTED_SUBJECT" ]; then | |
| git checkout -B release-target origin/master | |
| echo "should_release=true" >> "$GITHUB_OUTPUT" | |
| echo "mode=reuse" >> "$GITHUB_OUTPUT" | |
| echo "Reusing release commit already on master" | |
| exit 0 | |
| fi | |
| echo "should_release=false" >> "$GITHUB_OUTPUT" | |
| echo "mode=skip" >> "$GITHUB_OUTPUT" | |
| echo "::notice ::Skipping release because master advanced from $WORKFLOW_SHA to $REMOTE_SHA" | |
| echo "::notice ::Remote master version is $REMOTE_VERSION; planned version is $NEW_VERSION" | |
| - name: Update package.json version | |
| if: steps.release_target.outputs.mode == 'create' | |
| run: | | |
| NEW_VERSION="${{ steps.new_version.outputs.new_version }}" | |
| npm version $NEW_VERSION --no-git-tag-version | |
| echo "Updated package.json to version $NEW_VERSION" | |
| - name: Set up Go | |
| if: steps.release_target.outputs.should_release == 'true' | |
| uses: actions/setup-go@v4 | |
| with: | |
| go-version: "1.23.1" | |
| - name: Cache Go modules | |
| if: steps.release_target.outputs.should_release == 'true' | |
| uses: actions/cache@v3 | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go- | |
| - name: Update Go module versions | |
| if: steps.release_target.outputs.mode == 'create' | |
| run: | | |
| NEW_VERSION="${{ steps.new_version.outputs.new_version }}" | |
| chmod +x ./release.sh | |
| ./release.sh $NEW_VERSION | |
| echo "Updated Go modules to version $NEW_VERSION" | |
| - name: Configure Git | |
| if: steps.release_target.outputs.mode == 'create' | |
| run: | | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| - name: Commit version updates | |
| if: steps.release_target.outputs.mode == 'create' | |
| run: | | |
| NEW_VERSION="${{ steps.new_version.outputs.new_version }}" | |
| git add . | |
| git commit -m "chore: bump version to v$NEW_VERSION [skip-release] [skip ci]" | |
| - name: Create and push tags | |
| id: push_release | |
| if: steps.release_target.outputs.should_release == 'true' | |
| run: | | |
| set -euo pipefail | |
| NEW_VERSION="${{ steps.new_version.outputs.new_version }}" | |
| RELEASE_MODE="${{ steps.release_target.outputs.mode }}" | |
| WORKFLOW_SHA="${{ github.event.workflow_run.head_sha }}" | |
| tags=( | |
| "auth/v$NEW_VERSION" | |
| "cmd/v$NEW_VERSION" | |
| "conf/v$NEW_VERSION" | |
| "core/v$NEW_VERSION" | |
| "plugin/otel/v$NEW_VERSION" | |
| "serv/v$NEW_VERSION" | |
| "tests/v$NEW_VERSION" | |
| "v$NEW_VERSION" | |
| ) | |
| tag_refs=() | |
| # Delete existing local tags if they exist (handles re-runs gracefully). | |
| for tag in "${tags[@]}"; do | |
| git tag -d "$tag" 2>/dev/null || true | |
| done | |
| # Create all module tags. | |
| for tag in "${tags[@]}"; do | |
| git tag "$tag" | |
| tag_refs+=("refs/tags/$tag") | |
| done | |
| if [ "$RELEASE_MODE" = "create" ]; then | |
| git fetch origin master:refs/remotes/origin/master | |
| REMOTE_SHA=$(git rev-parse origin/master) | |
| if [ "$REMOTE_SHA" != "$WORKFLOW_SHA" ]; then | |
| echo "pushed=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice ::Skipping release push because master advanced from $WORKFLOW_SHA to $REMOTE_SHA" | |
| exit 0 | |
| fi | |
| git push origin HEAD:master | |
| fi | |
| # Replace remote release tags only after the branch push is complete. | |
| for tag in "${tags[@]}"; do | |
| git push origin ":refs/tags/$tag" 2>/dev/null || true | |
| done | |
| git push origin "${tag_refs[@]}" | |
| echo "pushed=true" >> "$GITHUB_OUTPUT" | |
| echo "Created and pushed tags for version $NEW_VERSION" | |
| - name: Delete existing release if any | |
| if: steps.push_release.outputs.pushed == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| NEW_VERSION="${{ steps.new_version.outputs.new_version }}" | |
| gh release delete "v$NEW_VERSION" --yes 2>/dev/null || true | |
| - name: Import GPG key | |
| if: steps.push_release.outputs.pushed == 'true' | |
| id: import_gpg | |
| uses: crazy-max/ghaction-import-gpg@v4 | |
| with: | |
| gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} | |
| passphrase: ${{ secrets.GPG_PASSPHRASE }} | |
| - name: Login to Docker Hub | |
| if: steps.push_release.outputs.pushed == 'true' | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Run GoReleaser | |
| if: steps.push_release.outputs.pushed == 'true' | |
| uses: goreleaser/goreleaser-action@v4 | |
| with: | |
| distribution: goreleaser | |
| version: latest | |
| args: release --clean | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GR_TOKEN }} | |
| GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} | |
| KO_DOCKER_REPO: dosco/graphjin | |
| - name: Publish to npm | |
| if: steps.push_release.outputs.pushed == 'true' | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" > ~/.npmrc | |
| npm publish --access public |