Desktop Release #6
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: Desktop Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: Release version to publish (for example 2026.4.7) | |
| required: true | |
| type: string | |
| prerelease: | |
| description: Publish this release as a prerelease | |
| required: true | |
| default: false | |
| type: boolean | |
| push: | |
| tags: | |
| - "v*.*.*" | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: desktop-release-${{ github.event_name }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| prepare: | |
| name: Prepare release tag | |
| if: github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag: ${{ steps.release_meta.outputs.tag }} | |
| version: ${{ steps.release_meta.outputs.version }} | |
| tag_exists: ${{ steps.check_tag.outputs.exists }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| - id: release_meta | |
| env: | |
| INPUT_VERSION: ${{ github.event.inputs.version }} | |
| run: | | |
| version="${INPUT_VERSION#v}" | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "tag=v$version" >> "$GITHUB_OUTPUT" | |
| - id: check_tag | |
| name: Check if tag already exists | |
| env: | |
| RELEASE_TAG: ${{ steps.release_meta.outputs.tag }} | |
| run: | | |
| if git ls-remote --exit-code --tags origin "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Release tag $RELEASE_TAG already exists on origin. Reusing the existing tag and release entry." | |
| else | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Sync repository versions | |
| if: steps.check_tag.outputs.exists != 'true' | |
| env: | |
| RELEASE_VERSION: ${{ steps.release_meta.outputs.version }} | |
| run: node scripts/sync-version.mjs --set "$RELEASE_VERSION" | |
| - name: Commit release metadata | |
| if: steps.check_tag.outputs.exists != 'true' | |
| env: | |
| RELEASE_VERSION: ${{ steps.release_meta.outputs.version }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add VERSION Cargo.lock Cargo.toml package.json apps/desktop/package.json apps/desktop/src-tauri/tauri.conf.json packages/contracts/package.json packages/core/package.json packages/sidecar/package.json packages/cli/package.json | |
| if ! git diff --cached --quiet; then | |
| git commit -m "chore(release): cut v$RELEASE_VERSION" | |
| fi | |
| - name: Create release tag | |
| if: steps.check_tag.outputs.exists != 'true' | |
| env: | |
| RELEASE_TAG: ${{ steps.release_meta.outputs.tag }} | |
| run: git tag "$RELEASE_TAG" | |
| - name: Push release commit and tag | |
| if: steps.check_tag.outputs.exists != 'true' | |
| env: | |
| RELEASE_TAG: ${{ steps.release_meta.outputs.tag }} | |
| RELEASE_BRANCH: ${{ github.ref_name }} | |
| run: | | |
| git push origin "HEAD:$RELEASE_BRANCH" | |
| git push origin "$RELEASE_TAG" | |
| release-info: | |
| name: Resolve release metadata | |
| needs: [prepare] | |
| if: always() && (github.event_name == 'push' || needs.prepare.result == 'success') | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag: ${{ steps.meta.outputs.tag }} | |
| version: ${{ steps.meta.outputs.version }} | |
| prerelease: ${{ steps.meta.outputs.prerelease }} | |
| ref: ${{ steps.meta.outputs.ref }} | |
| steps: | |
| - id: meta | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| PUSH_TAG: ${{ github.ref_name }} | |
| PUSH_REF: ${{ github.ref }} | |
| INPUT_PRERELEASE: ${{ github.event.inputs.prerelease }} | |
| PREPARED_TAG: ${{ needs.prepare.outputs.tag }} | |
| PREPARED_VERSION: ${{ needs.prepare.outputs.version }} | |
| run: | | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | |
| tag="$PREPARED_TAG" | |
| version="$PREPARED_VERSION" | |
| prerelease="$INPUT_PRERELEASE" | |
| case "$version" in | |
| *-*) prerelease="true" ;; | |
| esac | |
| ref="refs/tags/$tag" | |
| else | |
| tag="$PUSH_TAG" | |
| version="${PUSH_TAG#v}" | |
| case "$version" in | |
| *-*) prerelease="true" ;; | |
| *) prerelease="false" ;; | |
| esac | |
| ref="$PUSH_REF" | |
| fi | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT" | |
| echo "ref=$ref" >> "$GITHUB_OUTPUT" | |
| create-release: | |
| name: Create GitHub release | |
| needs: [release-info] | |
| if: always() && needs.release-info.result == 'success' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release_id: ${{ steps.release.outputs.release_id }} | |
| steps: | |
| - id: release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_TAG: ${{ needs.release-info.outputs.tag }} | |
| RELEASE_VERSION: ${{ needs.release-info.outputs.version }} | |
| RELEASE_PRERELEASE: ${{ needs.release-info.outputs.prerelease }} | |
| run: | | |
| if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then | |
| release_id="$(gh release view "$RELEASE_TAG" --json databaseId --jq '.databaseId')" | |
| else | |
| args=( | |
| release create "$RELEASE_TAG" | |
| --title "OpenGoat v$RELEASE_VERSION" | |
| --generate-notes | |
| --notes "Download the installer for your platform from the assets below." | |
| ) | |
| if [ "$RELEASE_PRERELEASE" = "true" ]; then | |
| args+=(--prerelease) | |
| fi | |
| gh "${args[@]}" | |
| release_id="$(gh release view "$RELEASE_TAG" --json databaseId --jq '.databaseId')" | |
| fi | |
| echo "release_id=$release_id" >> "$GITHUB_OUTPUT" | |
| publish: | |
| name: Build and publish desktop bundles | |
| needs: [release-info, create-release] | |
| if: always() && needs.release-info.result == 'success' && needs.create-release.result == 'success' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: macos-latest | |
| target: aarch64-apple-darwin | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ needs.release-info.outputs.ref }} | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install dependencies | |
| shell: bash | |
| run: pnpm install --frozen-lockfile | |
| - name: Verify release version sync | |
| shell: bash | |
| run: pnpm release:check-version | |
| - name: Build desktop bundles | |
| env: | |
| CI: true | |
| shell: bash | |
| run: | | |
| args=(--target "${{ matrix.target }}" --ci) | |
| if [ "${{ runner.os }}" = "macOS" ]; then | |
| args+=(--no-sign) | |
| fi | |
| pnpm --dir apps/desktop tauri build "${args[@]}" | |
| - name: Upload desktop bundles to GitHub release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| RELEASE_TAG: ${{ needs.release-info.outputs.tag }} | |
| RELEASE_VERSION: ${{ needs.release-info.outputs.version }} | |
| TARGET_TRIPLE: ${{ matrix.target }} | |
| shell: bash | |
| run: | | |
| bundle_root="target/$TARGET_TRIPLE/release/bundle" | |
| upload_dir="$RUNNER_TEMP/release-assets" | |
| rm -rf "$upload_dir" | |
| mkdir -p "$upload_dir" | |
| if [ "${{ runner.os }}" = "macOS" ]; then | |
| dmg_path="$(find "$bundle_root/dmg" -type f -name '*.dmg' | head -n1)" | |
| app_path="$(find "$bundle_root/macos" -type d -name '*.app' | head -n1)" | |
| if [ -n "$dmg_path" ]; then | |
| cp "$dmg_path" "$upload_dir/OpenGoat_${RELEASE_VERSION}_darwin_aarch64.dmg" | |
| fi | |
| if [ -n "$app_path" ]; then | |
| app_name="$(basename "$app_path")" | |
| tar -C "$(dirname "$app_path")" -czf "$upload_dir/OpenGoat_${RELEASE_VERSION}_darwin_aarch64.app.tar.gz" "$app_name" | |
| fi | |
| else | |
| msi_path="$(find "$bundle_root" -type f -name '*.msi' | head -n1)" | |
| exe_path="$(find "$bundle_root" -type f -name '*setup*.exe' | head -n1)" | |
| if [ -n "$msi_path" ]; then | |
| cp "$msi_path" "$upload_dir/OpenGoat_${RELEASE_VERSION}_windows_x64.msi" | |
| fi | |
| if [ -n "$exe_path" ]; then | |
| cp "$exe_path" "$upload_dir/OpenGoat_${RELEASE_VERSION}_windows_x64_setup.exe" | |
| fi | |
| fi | |
| mapfile -d '' assets < <(find "$upload_dir" -type f -print0) | |
| if [ "${#assets[@]}" -eq 0 ]; then | |
| echo "::error::No release assets found under $bundle_root" | |
| exit 1 | |
| fi | |
| gh release upload "$RELEASE_TAG" "${assets[@]}" --clobber |