From 8aa155185d41f8de84639d6a0be55b8076fc374e Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Fri, 12 Jun 2026 21:41:34 +0800 Subject: [PATCH] Automate miner release versioning Release proposal workflow computes the next miner-v version (patch/minor/major/custom) from tags and opens a version-bump PR; merging it tags, builds all platforms, and publishes the release. --- .github/workflows/create_miner_build.yml | 120 ++---------- .../create_miner_release_proposal.yml | 179 ++++++++++++++++++ .github/workflows/publish_miner_release.yml | 134 +++++++++++++ 3 files changed, 326 insertions(+), 107 deletions(-) create mode 100644 .github/workflows/create_miner_release_proposal.yml create mode 100644 .github/workflows/publish_miner_release.yml diff --git a/.github/workflows/create_miner_build.yml b/.github/workflows/create_miner_build.yml index a3939011..f12b26b0 100644 --- a/.github/workflows/create_miner_build.yml +++ b/.github/workflows/create_miner_build.yml @@ -1,15 +1,15 @@ name: Create Miner Build on: - workflow_dispatch: # Manual trigger only + workflow_dispatch: # Manual test builds (no release) + workflow_call: # Reused by publish_miner_release.yml inputs: - create_release: - description: 'Create a new release' + ref: + description: 'Git ref (tag/branch/SHA) to build' required: false - default: false - type: boolean + type: string permissions: - contents: write + contents: read jobs: build-macos: @@ -20,6 +20,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref || github.ref }} - name: Set up Flutter uses: subosito/flutter-action@v2 @@ -193,6 +195,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref || github.ref }} - name: Set up Flutter uses: subosito/flutter-action@v2 @@ -247,6 +251,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref || github.ref }} - name: Set up Flutter uses: subosito/flutter-action@v2 @@ -278,104 +284,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: quantus_miner-windows - path: miner-app/build/windows/x64/runner/Release/quantus_miner_windows.zip - - create-release: - name: Create Release - if: ${{ inputs.create_release }} - needs: [build-macos, build-linux, build-windows] - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Read version from pubspec.yaml - id: get_version - run: | - VERSION=$(grep '^version:' miner-app/pubspec.yaml | head -1 | sed 's/version:[[:space:]]*//') - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - echo "tag=miner-v$VERSION" >> "$GITHUB_OUTPUT" - - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - path: ./artifacts - - - name: List downloaded artifacts - run: | - # For debugging only; artifact already contains the final ZIP we want - find ./artifacts -type f \( -name "*.zip" -o -name "*.tar.gz" \) -o -type d -name "*.app" | sort - - - name: Create and push git tag - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - TAG="${{ steps.get_version.outputs.tag }}" - echo "Tag to create: $TAG" - - if git ls-remote --tags origin "$TAG" | grep -q "$TAG"; then - echo "Error: Tag $TAG already exists on remote." - echo "Please update the version in pubspec.yaml before creating a new release." - exit 1 - else - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git tag "$TAG" "$GITHUB_SHA" - git push origin "$TAG" - fi - - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ steps.get_version.outputs.tag }} - release_name: Quantus Miner ${{ steps.get_version.outputs.version }} - body: | - ## Quantus Miner Release - This release includes binaries for: - - 🍎 macOS (Intel/Apple Silicon) - - 🐧 Linux (x64) - - 🪟 Windows (x64) - ### Installation - **macOS**: Download the `.zip` file, extract it, and run the `Quantus Miner.app` - **Linux**: Download the `.tar.gz` file, extract it, and run the `quantus_miner` executable - **Windows**: Download the `.zip` file, extract it, and run the `quantus_miner.exe` - ### What's Changed - - Cross-platform mining support - - External miner integration - - Improved UI and stability - Built from commit: ${{ github.sha }} - draft: false - prerelease: false - - - name: Upload macOS Asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./artifacts/quantus_miner-macos/quantus_miner_macos.zip - asset_name: quantus_miner_macos.zip - asset_content_type: application/zip - - - name: Upload Linux Asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./artifacts/quantus_miner-linux/quantus_miner_linux.tar.gz - asset_name: quantus_miner_linux.tar.gz - asset_content_type: application/gzip - - - name: Upload Windows Asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./artifacts/quantus_miner-windows/quantus_miner_windows.zip - asset_name: quantus_miner_windows.zip - asset_content_type: application/zip \ No newline at end of file + path: miner-app/build/windows/x64/runner/Release/quantus_miner_windows.zip \ No newline at end of file diff --git a/.github/workflows/create_miner_release_proposal.yml b/.github/workflows/create_miner_release_proposal.yml new file mode 100644 index 00000000..2b8e1e8f --- /dev/null +++ b/.github/workflows/create_miner_release_proposal.yml @@ -0,0 +1,179 @@ +name: Miner - Create Release Proposal + +on: + workflow_dispatch: + inputs: + target_branch: + description: 'Target branch for the PR (default: main)' + required: false + type: string + default: 'main' + version_type: + description: 'Type of version bump (major, minor, patch) or specify custom version' + required: true + default: 'patch' + type: choice + options: + - patch + - minor + - major + - custom + custom_version: + description: 'Custom version string (e.g., 0.5.0). Only used if version_type is "custom". Do NOT include "miner-v" prefix' + required: false + is_draft: + description: 'Is this a draft release?' + required: true + type: boolean + default: false + +jobs: + calculate-next-version: + name: 🧮 Calculate Next Miner Version + runs-on: ubuntu-latest + outputs: + new_version: ${{ steps.versioner.outputs.new_version }} + source_branch: ${{ steps.vars.outputs.source_branch }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Get current branch + id: vars + run: | + echo "source_branch=$(git rev-parse --abbrev-ref HEAD)" >> "$GITHUB_OUTPUT" + + - name: Get latest miner tag + id: latest_tag + run: | + latest_miner_tag=$(git tag -l "miner-v[0-9]*.[0-9]*.[0-9]*" | sort -V | tail -n 1) + + if [ -z "$latest_miner_tag" ]; then + latest_miner_tag="miner-v0.0.0" + fi + + echo "latest_tag_found=$latest_miner_tag" >> "$GITHUB_OUTPUT" + echo "Latest miner tag found: $latest_miner_tag" + + - name: Calculate new version + id: versioner + env: + LATEST_TAG: ${{ steps.latest_tag.outputs.latest_tag_found }} + VERSION_TYPE: ${{ github.event.inputs.version_type }} + CUSTOM_VERSION: ${{ github.event.inputs.custom_version }} + run: | + current_version=${LATEST_TAG#miner-v} + + if [[ "$VERSION_TYPE" == "custom" ]]; then + if [[ -z "$CUSTOM_VERSION" ]]; then + echo "Error: Custom version is selected but no custom_version string provided." + exit 1 + fi + if [[ ! "$CUSTOM_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Custom version string must be in format X.Y.Z (e.g., 0.5.0)." + exit 1 + fi + new_version="miner-v$CUSTOM_VERSION" + else + IFS='.' read -r major minor patch <<< "$current_version" + + if [[ "$VERSION_TYPE" == "major" ]]; then + major=$((major + 1)) + minor=0 + patch=0 + elif [[ "$VERSION_TYPE" == "minor" ]]; then + minor=$((minor + 1)) + patch=0 + elif [[ "$VERSION_TYPE" == "patch" ]]; then + patch=$((patch + 1)) + else + echo "Error: Invalid version_type: $VERSION_TYPE" + exit 1 + fi + new_version="miner-v$major.$minor.$patch" + fi + + echo "New version: $new_version" + echo "new_version=$new_version" >> "$GITHUB_OUTPUT" + + update-version: + name: 📝 Update Version & Open PR + needs: calculate-next-version + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Create version bump branch and PR + env: + NEW_VERSION: ${{ needs.calculate-next-version.outputs.new_version }} + GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} + SOURCE_BRANCH: ${{ needs.calculate-next-version.outputs.source_branch }} + TARGET_BRANCH: ${{ github.event.inputs.target_branch }} + run: | + set -ex + new_pubspec_version=${NEW_VERSION#miner-v} + branch_name="release/${NEW_VERSION}" + + if git tag -l | grep -q "^${NEW_VERSION}$"; then + echo "Error: Tag $NEW_VERSION already exists" + exit 1 + fi + + git checkout "$SOURCE_BRANCH" + git checkout -b "$branch_name" + + echo "Updating miner-app/pubspec.yaml to version: $new_pubspec_version" + sed -i -E "s/^version:[[:space:]]*.+$/version: $new_pubspec_version/" miner-app/pubspec.yaml + + if ! grep -q "^version: $new_pubspec_version$" miner-app/pubspec.yaml; then + echo "Error: Failed to update version in miner-app/pubspec.yaml" + exit 1 + fi + + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor }}@users.noreply.github.com" + + git add miner-app/pubspec.yaml + # --allow-empty: pubspec may already be at the target version (manual pre-bump) + git commit --allow-empty -m "ci: Miner version bump to $NEW_VERSION" + git push origin "$branch_name" + + gh label create "automated" --force --color "ededed" --description "Created by CI" + gh label create "miner-release-proposal" --force --color "0e8a16" --description "Miner release proposal PR" + gh label create "draft-release" --force --color "d4c5f9" --description "Release will be created as draft" + + PR_TITLE="ci: Miner version bump to $NEW_VERSION" + PR_LABELS="automated,miner-release-proposal" + if [[ "${{ github.event.inputs.is_draft }}" == "true" ]]; then + PR_LABELS="$PR_LABELS,draft-release" + fi + + cat > pr_body.md << EOF + ## Miner Release Proposal + + This PR proposes releasing **Quantus Miner** version \`${new_pubspec_version}\` (tag \`${NEW_VERSION}\`). + + Merging this PR will automatically tag the merge commit, build macOS/Linux/Windows binaries, and publish a GitHub release. + + ### Changes + - Updated \`miner-app/pubspec.yaml\` version to \`${new_pubspec_version}\` + + Triggered by workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + Bump type: ${{ github.event.inputs.version_type }} + EOF + + gh pr create \ + --title "$PR_TITLE" \ + --body-file pr_body.md \ + --base "$TARGET_BRANCH" \ + --head "$branch_name" \ + --label "$PR_LABELS" diff --git a/.github/workflows/publish_miner_release.yml b/.github/workflows/publish_miner_release.yml new file mode 100644 index 00000000..d7916887 --- /dev/null +++ b/.github/workflows/publish_miner_release.yml @@ -0,0 +1,134 @@ +name: Miner - Publish Release + +on: + pull_request: + types: [closed] + branches: + - main + +permissions: + contents: read + +jobs: + create-tag: + name: Create Miner Tag + if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'miner-release-proposal') + runs-on: ubuntu-latest + permissions: + contents: write + outputs: + tag: ${{ steps.extract_version.outputs.tag }} + version: ${{ steps.extract_version.outputs.version }} + is_draft: ${{ steps.extract_version.outputs.is_draft }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Extract version from PR title + id: extract_version + env: + PR_TITLE: ${{ github.event.pull_request.title }} + HAS_DRAFT_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'draft-release') }} + run: | + # PR title format: "ci: Miner version bump to miner-vX.Y.Z" + TAG=$(echo "$PR_TITLE" | grep -o 'miner-v[0-9]\+\.[0-9]\+\.[0-9]\+') + if [ -z "$TAG" ]; then + echo "Error: Could not extract miner version from PR title: $PR_TITLE" + exit 1 + fi + { + echo "tag=$TAG" + echo "version=${TAG#miner-v}" + echo "is_draft=$HAS_DRAFT_LABEL" + } >> "$GITHUB_OUTPUT" + echo "Extracted tag: $TAG" + + - name: Verify pubspec version matches tag + env: + MERGE_COMMIT: ${{ github.event.pull_request.merge_commit_sha }} + VERSION: ${{ steps.extract_version.outputs.version }} + run: | + PUBSPEC_VERSION=$(git show "$MERGE_COMMIT:miner-app/pubspec.yaml" | grep '^version:' | head -1 | sed 's/version:[[:space:]]*//') + if [ "$PUBSPEC_VERSION" != "$VERSION" ]; then + echo "Error: pubspec version ($PUBSPEC_VERSION) does not match release version ($VERSION)" + exit 1 + fi + + - name: Create and push tag + env: + TAG: ${{ steps.extract_version.outputs.tag }} + MERGE_COMMIT: ${{ github.event.pull_request.merge_commit_sha }} + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor }}@users.noreply.github.com" + git tag -a "$TAG" "$MERGE_COMMIT" -m "Quantus Miner release $TAG" + git push origin "$TAG" + + build: + name: Build Miner + needs: create-tag + uses: ./.github/workflows/create_miner_build.yml + with: + ref: ${{ needs.create-tag.outputs.tag }} + secrets: inherit + + create-release: + name: 🚀 Create GitHub Release + needs: [create-tag, build] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout code at tag + uses: actions/checkout@v4 + with: + ref: ${{ needs.create-tag.outputs.tag }} + fetch-depth: 0 + fetch-tags: true + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: ./artifacts + + - name: Create GitHub Release with assets + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ needs.create-tag.outputs.tag }} + VERSION: ${{ needs.create-tag.outputs.version }} + IS_DRAFT: ${{ needs.create-tag.outputs.is_draft }} + run: | + set -eo pipefail + + NOTES="## Quantus Miner Release + This release includes binaries for: + - 🍎 macOS (Intel/Apple Silicon) + - 🐧 Linux (x64) + - 🪟 Windows (x64) + ### Installation + **macOS**: Download the \`.zip\` file, extract it, and run the \`Quantus Miner.app\` + **Linux**: Download the \`.tar.gz\` file, extract it, and run the \`quantus_miner\` executable + **Windows**: Download the \`.zip\` file, extract it, and run the \`quantus_miner.exe\`" + + EXTRA_ARGS=() + + # Generated notes must diff against the previous miner tag, not a wallet-v* tag + PREV_TAG=$(git tag -l "miner-v[0-9]*.[0-9]*.[0-9]*" | sort -V | grep -x -B1 "$TAG" | head -n 1) + if [ -n "$PREV_TAG" ] && [ "$PREV_TAG" != "$TAG" ]; then + EXTRA_ARGS+=(--notes-start-tag "$PREV_TAG") + fi + + if [[ "$IS_DRAFT" == "true" ]]; then + EXTRA_ARGS+=(--draft) + fi + + gh release create "$TAG" \ + --title "Quantus Miner $VERSION" \ + --notes "$NOTES" \ + --generate-notes \ + "${EXTRA_ARGS[@]}" \ + ./artifacts/quantus_miner-macos/quantus_miner_macos.zip \ + ./artifacts/quantus_miner-linux/quantus_miner_linux.tar.gz \ + ./artifacts/quantus_miner-windows/quantus_miner_windows.zip