diff --git a/.github/workflows/create_miner_build.yml b/.github/workflows/create_miner_build.yml index f7cb415ab..6e5f88725 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 @@ -202,6 +204,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 @@ -240,7 +244,7 @@ jobs: - name: Package Linux App run: | cd miner-app/build/linux/x64/release/bundle/ - tar -czvf quantus_miner_linux.tar.gz * + tar -czvf quantus_miner_linux.tar.gz -- * - name: Upload Linux Artifact uses: actions/upload-artifact@v4 @@ -256,6 +260,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 @@ -288,103 +294,3 @@ jobs: 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 diff --git a/.github/workflows/create_miner_release_proposal.yml b/.github/workflows/create_miner_release_proposal.yml new file mode 100644 index 000000000..f0d40c36d --- /dev/null +++ b/.github/workflows/create_miner_release_proposal.yml @@ -0,0 +1,210 @@ +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 + +permissions: + contents: read + +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 current versions + id: current_versions + run: | + set -euo pipefail + 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 + + pubspec_version=$(grep -E '^version:' miner-app/pubspec.yaml | head -n 1 | sed -E 's/^version:[[:space:]]*//' | cut -d+ -f1) + if [[ ! "$pubspec_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: could not parse version from miner-app/pubspec.yaml (got: '$pubspec_version')" + exit 1 + fi + + echo "latest_tag_found=$latest_miner_tag" >> "$GITHUB_OUTPUT" + echo "pubspec_version=$pubspec_version" >> "$GITHUB_OUTPUT" + echo "Latest miner tag found: $latest_miner_tag (pubspec: $pubspec_version)" + + - name: Calculate new version + id: versioner + env: + LATEST_TAG: ${{ steps.current_versions.outputs.latest_tag_found }} + PUBSPEC_VERSION: ${{ steps.current_versions.outputs.pubspec_version }} + VERSION_TYPE: ${{ github.event.inputs.version_type }} + CUSTOM_VERSION: ${{ github.event.inputs.custom_version }} + run: | + set -euo pipefail + tag_version=${LATEST_TAG#miner-v} + # Bump from whichever is higher: the last released tag or a manually pre-bumped pubspec + current_version=$(printf '%s\n%s\n' "$tag_version" "$PUBSPEC_VERSION" | sort -V | tail -n 1) + echo "Base version: $current_version (tag: $tag_version, pubspec: $PUBSPEC_VERSION)" + + 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 + lower=$(printf '%s\n%s\n' "$CUSTOM_VERSION" "$current_version" | sort -V | head -n 1) + if [[ "$lower" == "$CUSTOM_VERSION" && "$CUSTOM_VERSION" != "$current_version" ]]; then + echo "Error: custom version $CUSTOM_VERSION is lower than current version $current_version (latest tag / pubspec)." + 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 }} + VERSION_TYPE: ${{ github.event.inputs.version_type }} + IS_DRAFT: ${{ github.event.inputs.is_draft }} + run: | + set -ex + new_pubspec_version=${NEW_VERSION#miner-v} + branch_name="release/${NEW_VERSION}" + + # Check the remote directly so the guard doesn't depend on local tag state + if git ls-remote --exit-code --tags origin "refs/tags/$NEW_VERSION" >/dev/null; then + echo "Error: Tag $NEW_VERSION already exists" + exit 1 + fi + + git checkout "$SOURCE_BRANCH" + git checkout -b "$branch_name" + + current_pubspec=$(grep -E '^version:' miner-app/pubspec.yaml | head -n 1 | sed -E 's/^version:[[:space:]]*//' | cut -d+ -f1) + lower=$(printf '%s\n%s\n' "$new_pubspec_version" "$current_pubspec" | sort -V | head -n 1) + if [[ "$lower" == "$new_pubspec_version" && "$new_pubspec_version" != "$current_pubspec" ]]; then + echo "Error: refusing to downgrade miner-app/pubspec.yaml from $current_pubspec to $new_pubspec_version" + exit 1 + fi + + 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 [[ "$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: ${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 000000000..d55418957 --- /dev/null +++ b/.github/workflows/publish_miner_release.yml @@ -0,0 +1,165 @@ +name: Miner - Publish Release + +on: + pull_request: + types: [closed] + branches: + - main + +permissions: + contents: read + +concurrency: + group: miner-publish-release + cancel-in-progress: false + +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 merge commit + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.merge_commit_sha }} + + - 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]\+' | head -n 1) + 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: + VERSION: ${{ steps.extract_version.outputs.version }} + run: | + PUBSPEC_VERSION=$(grep '^version:' miner-app/pubspec.yaml | head -n 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: | + set -euo pipefail + # Idempotent: a re-run after a failed build/release must not die here + if git fetch origin "refs/tags/$TAG:refs/tags/$TAG" 2>/dev/null; then + EXISTING=$(git rev-parse "$TAG^{commit}") + if [ "$EXISTING" = "$MERGE_COMMIT" ]; then + echo "Tag $TAG already points at $MERGE_COMMIT; skipping tag creation" + exit 0 + fi + echo "Error: tag $TAG already exists at $EXISTING, not at merge commit $MERGE_COMMIT" + exit 1 + fi + 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 + + ASSETS=( + ./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 + ) + for ASSET in "${ASSETS[@]}"; do + if [ ! -f "$ASSET" ]; then + echo "Error: expected build artifact missing: $ASSET" + exit 1 + fi + done + + # Idempotent: a re-run after a partial failure re-uploads assets + if gh release view "$TAG" >/dev/null 2>&1; then + echo "Release $TAG already exists; re-uploading assets" + gh release upload "$TAG" --clobber "${ASSETS[@]}" + exit 0 + fi + + 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 -Fx -B1 "$TAG" | head -n 1 || true) + 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[@]}" \ + "${ASSETS[@]}" diff --git a/miner-app/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/miner-app/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..9ab534d83 --- /dev/null +++ b/miner-app/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swiftsdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/TelemetryDeck/SwiftSDK", + "state" : { + "revision" : "ad4a03ec7ea7416a4081370f21c86e55b02a5b88", + "version" : "2.14.1" + } + } + ], + "version" : 2 +}