Merge pull request 'ci: gitea workflow, drop inherited github workflows, embed commit in --version' (#10) from ci/gitea-workflows-and-build-sha into main
All checks were successful
ci / fmt (push) Successful in 51s
ci / clippy (push) Successful in 1m46s
ci / doc (push) Successful in 2m50s
ci / test (push) Successful in 10m57s

This commit was merged in pull request #10.
This commit is contained in:
2026-09-03 10:02:39 +00:00
9 changed files with 142 additions and 495 deletions

81
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,81 @@
---
# Lint, build and test on the fleet's `rust` runner (architecture/gitea-runners.md).
#
# Replaces origin's .github/workflows/ci.yml, which targeted GitHub-hosted
# runners that do not exist here. Gitea Actions also reads .github/workflows,
# so origin's files are deleted rather than left inert; on every origin merge
# `git rm -r .github/workflows` is the standing resolution (quantus/miner#1).
#
# Deliberately not here: origin's CPU benchmark job (meaningless on a shared
# 4-CPU runner; the real gate is the GPU harness, quantus/miner#2) and taplo
# (not on the runner image; add it to runner-rust per gitea-runners.md §5
# rather than `cargo install` on every run).
name: ci
on:
pull_request:
push:
branches: [main, origin-main]
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
CARGO_INCREMENTAL: "0"
CARGO_TERM_COLOR: always
# rust-toolchain pins 1.93.0. rustup on the runner auto-installs the toolchain
# on first use but NOT the components the file lists (observed: "cargo-fmt is
# not installed for the toolchain"), so each job that needs one adds it
# explicitly; the call is a no-op once present.
jobs:
fmt:
runs-on: rust
steps:
- uses: actions/checkout@v4
- name: toolchain
run: rustup component add rustfmt
- name: rustfmt
run: cargo fmt --all -- --check
clippy:
runs-on: rust
needs: fmt
steps:
- uses: actions/checkout@v4
- name: toolchain
run: rustup component add clippy
- name: clippy (all targets, all features, warnings denied)
# Same invocation as clippy.sh so local and CI agree.
run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
test:
runs-on: rust
needs: fmt
steps:
- uses: actions/checkout@v4
- name: build
run: cargo build --locked --workspace
- name: test
# GPU tests skip themselves when no adapter is present; the runner has none.
run: cargo test --locked --workspace
- name: version carries the commit
# build.rs embeds the SHA; a binary that says "unknown" cannot be
# matched to a deploy (quantus/miner#8).
run: |
set -euo pipefail
v=$(./target/debug/quantus-miner --version)
echo "$v"
case "$v" in
*unknown*) echo "build SHA not embedded" >&2; exit 1 ;;
esac
doc:
runs-on: rust
needs: fmt
steps:
- uses: actions/checkout@v4
- name: doc
run: cargo doc --locked --workspace --no-deps --all-features

View File

@@ -1,15 +0,0 @@
---
name: free disk space
description: when rust compiling, free up some disk space
runs:
using: composite
steps:
- name: free disk space
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # 1.3.1
with:
android: true
dotnet: false
haskell: false
large-packages: false
swap-storage: false

View File

@@ -1,96 +0,0 @@
---
name: CI
on:
pull_request:
paths-ignore:
- "docs/**"
- "*.md"
- "LICENSE"
push:
branches:
- main
paths-ignore:
- "docs/**"
- "*.md"
- "LICENSE"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
CARGO_INCREMENTAL: 0
CARGO_TERM_COLOR: always
jobs:
fast-checks:
name: 🏁 Fast Checks (Format)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: setup rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: rustfmt
- name: install taplo
run: cargo install taplo-cli --locked
- name: Run format checks
run: |
taplo format --check --config taplo.toml
cargo fmt --all -- --check
timeout-minutes: 5
build-and-test:
name: 🛠️ Build & Test
needs: fast-checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: setup rust
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: compile (gpu)
run: |
cargo build --locked --workspace
timeout-minutes: 90
- name: test (gpu)
run: |
cargo test --locked --workspace
timeout-minutes: 15
analysis:
name: 🤖 Analysis (Clippy & Doc)
needs: fast-checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: setup rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: clippy
- name: clippy (all features)
run: cargo clippy --locked --workspace --all-features
timeout-minutes: 30
- name: doc
run: cargo doc --locked --workspace --no-deps --all-features
timeout-minutes: 15
benchmark:
name: 🏃 Benchmark
needs: fast-checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: setup rust
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: build benchmark binary
run: cargo build -p miner-cli --release
timeout-minutes: 60
- name: upload linux release binary
uses: actions/upload-artifact@v4
with:
name: quantus-miner-linux-x86_64
path: target/release/quantus-miner
- name: run cpu benchmark
run: ./target/release/quantus-miner benchmark --cpu-workers 2 --duration 5
timeout-minutes: 10

View File

@@ -1,13 +0,0 @@
name: Dependency cooldown audit
on:
schedule:
- cron: '0 6 * * 1'
workflow_dispatch:
permissions:
contents: read
jobs:
audit:
uses: Quantus-Network/shared-workflows/.github/workflows/dependency-cooldown-audit.yml@v1

View File

@@ -1,14 +0,0 @@
name: Dependency cooldown
on:
pull_request:
# labeled/unlabeled/edited are required so the gate re-runs when an
# emergency bypass label or reason is added.
types: [opened, synchronize, reopened, labeled, unlabeled, edited]
permissions:
contents: read
jobs:
dependency-cooldown:
uses: Quantus-Network/shared-workflows/.github/workflows/dependency-cooldown.yml@v1

View File

@@ -1,199 +0,0 @@
---
name: Release Proposal
env:
CARGO_TERM_COLOR: always
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"
required: true
default: "patch"
type: choice
options:
- patch
- minor
- major
- custom
custom_version:
description: 'Custom version string (e.g., 1.2.3). Only used if version_type is "custom".'
required: false
is_draft:
description: "Is this a draft release?"
required: true
type: boolean
default: false
jobs:
calculate-next-version:
name: 🧮 Calculate Next Version
runs-on: ubuntu-latest
outputs:
new_version: ${{ steps.versioner.outputs.new_version }}
new_tag: ${{ steps.versioner.outputs.new_tag }}
commit_sha_short: ${{ steps.vars.outputs.commit_sha_short }}
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 and commit SHA
id: vars
run: |
echo "commit_sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "source_branch=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_OUTPUT
- name: Get latest tag
id: latest_tag
run: |
# Get all version tags and sort them by version
latest_semver_tag=$(git tag -l "v[0-9]*.[0-9]*.[0-9]*" | sort -V | tail -n 1)
# If no tags found, use default
if [ -z "$latest_semver_tag" ]; then
latest_semver_tag="v0.0.0"
fi
echo "latest_tag_found=$latest_semver_tag" >> $GITHUB_OUTPUT
echo "Latest semantic version tag found: $latest_semver_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: |
# Remove 'v' prefix and any suffix for processing
current_version=${LATEST_TAG#v}
# Remove any suffix after the version number
current_version=$(echo "$current_version" | sed -E 's/-[^-]+$//')
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" =~ ^v ]]; then
echo "Error: Custom version string MUST start with 'v' (e.g., v1.2.3)."
exit 1
fi
new_version="$CUSTOM_VERSION"
else
# Split version and pre-release part
IFS='-' read -r version_core prerelease_part <<< "$current_version"
IFS='.' read -r major minor patch <<< "$version_core"
# Increment based on type
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="v$major.$minor.$patch"
fi
echo "New version: $new_version"
echo "new_version=$new_version" >> $GITHUB_OUTPUT
echo "new_tag=$new_version" >> $GITHUB_OUTPUT
update-cargo-toml:
name: 📝 Update version files
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: Setup rust
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Create version bump branch and PR
env:
NEW_VERSION: ${{ needs.calculate-next-version.outputs.new_version }}
NEW_TAG: ${{ needs.calculate-next-version.outputs.new_tag }}
GITHUB_TOKEN: ${{ secrets.ADMIN_PAT }}
SOURCE_BRANCH: ${{ needs.calculate-next-version.outputs.source_branch }}
TARGET_BRANCH: ${{ github.event.inputs.target_branch }}
run: |
set -ex
new_cargo_version=${NEW_VERSION#v}
branch_name="release/${NEW_VERSION}"
# Create new branch from source branch
git checkout "$SOURCE_BRANCH"
git checkout -b "$branch_name"
# Update version in workspace Cargo.toml (safer than cargo set-version)
echo "Updating workspace Cargo.toml to version: $new_cargo_version"
sed -i -E "s/^version\s*=\s*\"[0-9a-zA-Z.-]+\"/version = \"$new_cargo_version\"/" Cargo.toml
# Regenerate Cargo.lock with precise updates for our packages only
cargo update -p miner-cli --precise "$new_cargo_version"
cargo update -p miner-service --precise "$new_cargo_version"
cargo update -p pow-core --precise "$new_cargo_version"
cargo update -p metrics --precise "$new_cargo_version"
cargo update -p miner-telemetry --precise "$new_cargo_version"
cargo update -p engine-cpu --precise "$new_cargo_version"
cargo update -p engine-gpu --precise "$new_cargo_version"
# Verify everything compiles correctly
cargo check --workspace
# Commit changes
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor }}@users.noreply.github.com"
git add Cargo.toml Cargo.lock
git commit -m "bump version to $NEW_VERSION"
git push origin "$branch_name"
# Prepare PR title and body
PR_TITLE="Release $NEW_VERSION"
PR_BODY="Automated version bump for release $NEW_VERSION.
## Overview
- Version bump: ${{ github.event.inputs.version_type }}
- Type: ${{ github.event.inputs.version_type }}
- Draft: ${{ github.event.inputs.is_draft }}
## What changed
- Updated version in Cargo.toml and Cargo.lock
Triggered by workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
# Prepare labels
PR_LABELS="release-proposal"
if [[ "${{ github.event.inputs.is_draft }}" == "true" ]]; then
PR_LABELS="$PR_LABELS,draft-release"
fi
gh pr create \
--title "$PR_TITLE" \
--body "$PR_BODY" \
--base "$TARGET_BRANCH" \
--head "$branch_name" \
--label "$PR_LABELS"

View File

@@ -1,157 +0,0 @@
---
name: Release Publish
on:
pull_request:
types: [closed]
branches:
- main
permissions:
contents: write
packages: write
env:
CARGO_TERM_COLOR: always
jobs:
create-tag:
name: Create Tag
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release-proposal')
runs-on: ubuntu-latest
outputs:
version: ${{ steps.extract_version.outputs.version }}
tag: ${{ steps.extract_version.outputs.tag }}
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
run: |
# Extract version from PR title (format: "Release vX.Y.Z")
VERSION_TAG=$(echo "${{ github.event.pull_request.title }}" | grep -o 'v[0-9]\+\.[0-9]\+\.[0-9]\+')
if [ -z "$VERSION_TAG" ]; then
echo "Error: Could not extract version from PR title: ${{ github.event.pull_request.title }}"
exit 1
fi
VERSION=${VERSION_TAG#v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=$VERSION_TAG" >> $GITHUB_OUTPUT
# Check if this is a draft release
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'draft-release') }}" == "true" ]]; then
echo "is_draft=true" >> $GITHUB_OUTPUT
else
echo "is_draft=false" >> $GITHUB_OUTPUT
fi
echo "Extracted version: $VERSION"
echo "Extracted tag: $VERSION_TAG"
- name: Create and push tag
run: |
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor }}@users.noreply.github.com"
git tag -a "${{ steps.extract_version.outputs.tag }}" -m "Release ${{ steps.extract_version.outputs.tag }}"
git push origin "${{ steps.extract_version.outputs.tag }}"
build:
needs: create-tag
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
# Linux builds
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
binary_name: quantus-miner
asset_name: quantus-miner-linux-x86_64
# Windows builds
- os: windows-latest
target: x86_64-pc-windows-msvc
binary_name: quantus-miner.exe
asset_name: quantus-miner-windows-x86_64.exe
# macOS builds (Intel)
- os: macos-15-intel
target: x86_64-apple-darwin
binary_name: quantus-miner
asset_name: quantus-miner-macos-x86_64
# macOS builds (Apple Silicon)
- os: macos-latest
target: aarch64-apple-darwin
binary_name: quantus-miner
asset_name: quantus-miner-macos-aarch64
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.create-tag.outputs.tag }}
- name: setup rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
target: ${{ matrix.target }}
- name: build binary
shell: bash
run: |
if [[ -n "${{ matrix.features }}" ]]; then
cargo build --release --locked --target ${{ matrix.target }} --features ${{ matrix.features }}
else
cargo build --release --locked --target ${{ matrix.target }}
fi
- name: prepare binary
shell: bash
run: |
cd target/${{ matrix.target }}/release
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
cp ${{ matrix.binary_name }} ${{ matrix.asset_name }}
else
cp ${{ matrix.binary_name }} ${{ matrix.asset_name }}
strip ${{ matrix.asset_name }}
fi
- name: upload binary artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.asset_name }}
path: target/${{ matrix.target }}/release/${{ matrix.asset_name }}
release:
needs: [create-tag, build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.create-tag.outputs.tag }}
- name: download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: create release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ needs.create-tag.outputs.tag }}
IS_DRAFT: ${{ needs.create-tag.outputs.is_draft }}
run: |
# Prepare draft flag
if [[ "$IS_DRAFT" == "true" ]]; then
DRAFT_FLAG="--draft"
else
DRAFT_FLAG=""
fi
# Create release with all artifacts
gh release create "$TAG" \
--title "Release $TAG" \
--generate-notes \
$DRAFT_FLAG \
artifacts/*/quantus-miner-*

51
crates/miner-cli/build.rs Normal file
View File

@@ -0,0 +1,51 @@
// lair: embed the git commit in the binary so `--version` and the build-info
// metric identify a deployed build by commit, not by the workspace semver
// (which does not change between commits on a branch that deploys on push).
//
// Resolution order:
// 1. MINER_BUILD_SHA in the environment (CI sets it from the checked-out ref)
// 2. `git rev-parse HEAD` of the workspace, with "-dirty" if the tree differs
// 3. "unknown"
use std::process::Command;
fn git(args: &[&str]) -> Option<String> {
let out = Command::new("git").args(args).output().ok()?;
if !out.status.success() {
return None;
}
let s = String::from_utf8(out.stdout).ok()?;
let s = s.trim();
if s.is_empty() {
None
} else {
Some(s.to_string())
}
}
fn main() {
println!("cargo:rerun-if-env-changed=MINER_BUILD_SHA");
let sha = match std::env::var("MINER_BUILD_SHA") {
Ok(s) if !s.trim().is_empty() => s.trim().to_string(),
_ => match git(&["rev-parse", "--short=12", "HEAD"]) {
Some(head) => {
// Re-run when HEAD moves so a rebuild after a commit picks it up.
if let Some(dir) = git(&["rev-parse", "--git-dir"]) {
println!("cargo:rerun-if-changed={dir}/HEAD");
println!("cargo:rerun-if-changed={dir}/refs/heads");
}
let dirty = git(&["status", "--porcelain", "--untracked-files=no"])
.map(|s| !s.is_empty())
.unwrap_or(false);
if dirty {
format!("{head}-dirty")
} else {
head
}
}
None => "unknown".to_string(),
},
};
println!("cargo:rustc-env=MINER_BUILD_SHA={sha}");
}

View File

@@ -124,9 +124,18 @@ enum Command {
},
}
// lair: semver plus the commit the binary was built from (see build.rs), so a
// deploy can assert the running binary is the commit it shipped.
const VERSION: &str = concat!(
env!("CARGO_PKG_VERSION"),
" (",
env!("MINER_BUILD_SHA"),
")"
);
/// Quantus External Miner CLI
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
#[command(author, version = VERSION, about, long_about = None)]
struct Args {
#[command(subcommand)]
command: Option<Command>,