Release CLI #3
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: Release CLI | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_type: | |
| description: 'Release type' | |
| required: true | |
| default: 'auto' | |
| type: choice | |
| options: | |
| - auto | |
| - patch | |
| - minor | |
| - major | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: false | |
| env: | |
| OPENTAINT_DEPENDENCIES_VERSION: 2026.03.19.adeb616 | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| container: ghcr.io/catthehacker/ubuntu:act-latest | |
| defaults: | |
| run: | |
| shell: bash | |
| permissions: | |
| contents: write | |
| packages: write | |
| steps: | |
| - | |
| name: Install GitHub CLI | |
| run: | | |
| (type -p wget >/dev/null || apt-get install -yqq wget) \ | |
| && mkdir -p -m 755 /etc/apt/keyrings \ | |
| && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \ | |
| && cat $out | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \ | |
| && chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ | |
| && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ | |
| && apt-get update \ | |
| && apt-get install -yqq gh | |
| - | |
| name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - | |
| name: Manual release | |
| if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_type != 'auto' }} | |
| id: manual_release | |
| uses: ./.github/actions/manual-version-bump | |
| with: | |
| tag-prefix: 'v' | |
| bump-type: ${{ github.event.inputs.release_type }} | |
| - | |
| name: Write manual release version | |
| if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_type != 'auto' }} | |
| run: | | |
| echo "${{ steps.manual_release.outputs.new-version }}" > cli/release_version.txt | |
| - | |
| name: Create release tag (manual) | |
| if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_type != 'auto' }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git tag "v${{ steps.manual_release.outputs.new-version }}" | |
| git push origin "v${{ steps.manual_release.outputs.new-version }}" | |
| - | |
| name: Force-update tags | |
| run: git fetch --tags --force | |
| - | |
| name: 'Release (auto)' | |
| if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.release_type == 'auto' }} | |
| id: semantic_release | |
| uses: cycjimmy/semantic-release-action@v4 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| semantic_version: 23.0.0 | |
| working_directory: cli | |
| extra_plugins: | | |
| @semantic-release/commit-analyzer@11.1.0 | |
| @semantic-release/exec@6.0.3 | |
| conventional-changelog-conventionalcommits@7.0.2 | |
| - | |
| name: 'Get release_version and pass to next job' | |
| id: release_version | |
| working-directory: cli | |
| run: | | |
| if [ -f release_version.txt ]; then | |
| echo "status=succeeded" >>"$GITHUB_OUTPUT" | |
| echo "RELEASE_VERSION=$(cat release_version.txt)" >> $GITHUB_OUTPUT | |
| else | |
| echo "status=skipped" >>"$GITHUB_OUTPUT" | |
| fi | |
| rm -f release_version.txt | |
| - | |
| name: Login to GitHub Container Registry | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ secrets.SEQRA_GITHUB_ACTOR }} | |
| password: ${{ secrets.SEQRA_GITHUB_TOKEN }} | |
| - | |
| name: Set up QEMU | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| uses: docker/setup-qemu-action@v3 | |
| - | |
| name: Set up Docker Buildx | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| uses: docker/setup-buildx-action@v3 | |
| - | |
| name: Set up Go | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: '>=1.25.0' | |
| - | |
| name: Docker meta | |
| id: meta | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: | | |
| ghcr.io/${{ github.repository }} | |
| tags: | | |
| type=semver,pattern={{version}},value=${{ steps.release_version.outputs.RELEASE_VERSION }} | |
| - | |
| name: Download bundled artifacts | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| working-directory: cli | |
| env: | |
| GH_TOKEN: ${{ secrets.SEQRA_GITHUB_TOKEN }} | |
| run: | | |
| ANALYZER_TAG=$(grep '^analyzer:' internal/globals/versions.yaml | awk '{print $2}' | tr -d '"') | |
| AUTOBUILDER_TAG=$(grep '^autobuilder:' internal/globals/versions.yaml | awk '{print $2}' | tr -d '"') | |
| RULES_TAG=$(grep '^rules:' internal/globals/versions.yaml | awk '{print $2}' | tr -d '"') | |
| mkdir -p lib | |
| echo "Downloading analyzer ${ANALYZER_TAG}..." | |
| gh release download "$ANALYZER_TAG" \ | |
| --repo ${{ github.repository }} \ | |
| --pattern "opentaint-project-analyzer.jar" \ | |
| --dir lib | |
| echo "Downloading autobuilder ${AUTOBUILDER_TAG}..." | |
| gh release download "$AUTOBUILDER_TAG" \ | |
| --repo ${{ github.repository }} \ | |
| --pattern "opentaint-project-auto-builder.jar" \ | |
| --dir lib | |
| echo "Downloading rules ${RULES_TAG}..." | |
| gh release download "$RULES_TAG" \ | |
| --repo ${{ github.repository }} \ | |
| --pattern "opentaint-rules.tar.gz" \ | |
| --dir /tmp | |
| mkdir -p lib/rules | |
| tar -xzf /tmp/opentaint-rules.tar.gz -C lib/rules | |
| echo "Bundled artifacts:" | |
| ls -la lib/ | |
| - | |
| name: Run GoReleaser | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| uses: goreleaser/goreleaser-action@v6 | |
| with: | |
| distribution: goreleaser | |
| version: '~> v2' | |
| args: release --clean | |
| workdir: cli | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GORELEASER_CURRENT_TAG: v${{ steps.release_version.outputs.RELEASE_VERSION }} | |
| HOMEBREW_TAP_OWNER: ${{ github.repository_owner }} | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| - | |
| name: Bundle JREs into release archives | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| working-directory: cli | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| bash scripts/bundle-jre.sh dist | |
| RELEASE_TAG="v${{ steps.release_version.outputs.RELEASE_VERSION }}" | |
| for archive in dist/opentaint-full_*.tar.gz dist/opentaint-full_*.zip; do | |
| [ -f "$archive" ] || continue | |
| echo "Re-uploading $archive..." | |
| gh release upload "$RELEASE_TAG" "$archive" --clobber | |
| done | |
| # Regenerate checksums covering all three variants | |
| cd dist | |
| sha256sum opentaint-cli_*.{tar.gz,zip} opentaint_*.{tar.gz,zip} opentaint-full_*.{tar.gz,zip} 2>/dev/null > checksums.txt | |
| cd .. | |
| if [ -s dist/checksums.txt ]; then | |
| gh release upload "$RELEASE_TAG" dist/checksums.txt --clobber | |
| else | |
| echo "No checksums to upload" | |
| fi | |
| - | |
| name: Check Homebrew token availability | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| id: check_homebrew | |
| env: | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| run: | | |
| if [ -n "$HOMEBREW_TAP_TOKEN" ]; then | |
| echo "available=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "available=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - | |
| name: Update package manager manifests | |
| if: ${{ steps.check_homebrew.outputs.available == 'true' && steps.release_version.outputs.status == 'succeeded' && (steps.semantic_release.outputs.new_release_channel == '' || steps.manual_release.outputs.new-version != '') }} | |
| working-directory: cli | |
| env: | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| HOMEBREW_TAP_OWNER: ${{ github.repository_owner }} | |
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | |
| run: | | |
| RELEASE_TAG="v${{ steps.release_version.outputs.RELEASE_VERSION }}" | |
| # Import GPG key for signed commits | |
| echo "$GPG_PRIVATE_KEY" | gpg --batch --import | |
| # Fix SHA256 values in GoReleaser-generated cask file | |
| CASK_FILE="dist/homebrew/Casks/opentaint.rb" | |
| for f in dist/opentaint-full_*.tar.gz dist/opentaint-full_*.zip; do | |
| [ -f "$f" ] || continue | |
| name="$(basename "$f")" | |
| sha="$(sha256sum "$f" | awk '{print $1}')" | |
| escaped_name="${name//./\\.}" | |
| sed -i "/${escaped_name}/{ n; s/sha256 \"[a-f0-9]\{64\}\"/sha256 \"${sha}\"/; }" "$CASK_FILE" | |
| done | |
| # Push updated cask to Homebrew tap | |
| git clone "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/${{ github.repository_owner }}/homebrew-tap.git" /tmp/homebrew-tap | |
| mkdir -p /tmp/homebrew-tap/Casks | |
| cp "$CASK_FILE" /tmp/homebrew-tap/Casks/opentaint.rb | |
| cd /tmp/homebrew-tap | |
| git config user.name "seqradev" | |
| git config user.email "seqradev@gmail.com" | |
| git config user.signingkey "6000887370B8C08E" | |
| git config commit.gpgsign true | |
| git add -A | |
| if ! git diff --cached --quiet; then | |
| git commit -m "Update opentaint to ${RELEASE_TAG}" | |
| git push | |
| fi | |
| cd - | |
| - | |
| name: Build and push Docker image | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' && (steps.semantic_release.outputs.new_release_channel == '' || steps.manual_release.outputs.new-version != '') }} | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: cli | |
| build-contexts: | | |
| opentaint-dependencies:latest=docker-image://ghcr.io/${{ github.repository_owner }}/opentaint/infra:${{ env.OPENTAINT_DEPENDENCIES_VERSION }} | |
| platforms: linux/amd64, linux/arm64 | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| secrets: | | |
| github_token=${{ secrets.SEQRA_GITHUB_TOKEN }} | |
| build-args: | | |
| OPENTAINT_OWNER=${{ github.repository_owner }} | |
| OPENTAINT_BUILD_FLAGS=-s -w -X github.com/${{ github.repository_owner }}/opentaint/internal/version.Version=${{ steps.release_version.outputs.RELEASE_VERSION }} | |
| - | |
| name: Update floating version tags | |
| if: ${{ steps.release_version.outputs.status == 'succeeded' }} | |
| uses: ./.github/actions/update-floating-tags | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| release-version: ${{ steps.release_version.outputs.RELEASE_VERSION }} | |
| tag-prefix: '' | |
| component-name: 'CLI' | |
| copy-assets-from: v${{ steps.release_version.outputs.RELEASE_VERSION }} | |
| outputs: | |
| release_version: ${{ steps.release_version.outputs.RELEASE_VERSION }} |