Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/actions/bitbox-audit/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ inputs:
testkit-ref:
description: >
Git ref of DFXswiss/bitbox-testkit to install. Pin to a
tag (v0.2.0) for reproducibility; use 'main' during testkit
tag (v0.5.0) for reproducibility; use 'main' during testkit
development to track the bleeding edge. The sentinel value
'local' is reserved for the testkit's own self-test workflow
and builds the CLI from the checked-out source — consumers
should never set it.
required: false
default: v0.2.0
default: v0.5.0
firmware:
description: >
Optional firmware version (e.g. 9.23.0) to narrow the quirk set to
Expand Down
13 changes: 12 additions & 1 deletion .github/actions/bitbox-simulator/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ inputs:
controls the action.yml itself. Mismatched refs leave the CLI
running with stale embedded SHA pins — keep these in sync.
required: false
default: v0.4.2
default: v0.5.0
comment-on-pr:
description: >
'true' to post the markdown report as a sticky PR comment.
Expand All @@ -37,6 +37,15 @@ inputs:
red check just because the simulator can't run there.
required: false
default: 'false'
firmware:
description: >
Specific embedded firmware name (e.g.
bitbox02-multi-v9.21.0-simulator1.0.0-linux-amd64) or 'all' to
run the matrix over every embedded firmware. Default '' = newest.
Matrix mode catches regressions that only surface on older
firmwares still in the production tail.
required: false
default: ''

outputs:
report-path:
Expand Down Expand Up @@ -74,11 +83,13 @@ runs:
env:
FAIL_ON_FINDINGS: ${{ inputs.fail-on-findings }}
FAIL_ON_SKIP: ${{ inputs.fail-on-skip }}
FIRMWARE: ${{ inputs.firmware }}
run: |
set -o pipefail
mkdir -p .bitbox-simulator
flags=()
if [ "$FAIL_ON_SKIP" = "true" ]; then flags+=(--fail-on-skip); fi
if [ -n "$FIRMWARE" ]; then flags+=(--firmware "$FIRMWARE"); fi

# JSON first so the markdown render can reuse the SAME run.
rc=0
Expand Down
4 changes: 2 additions & 2 deletions .github/workflow-templates/bitbox-audit-slash.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
const args = body.replace(/^\/bitbox-audit\s*/, '').split(/\s+/).filter(Boolean);
let firmware = '';
let failOnFindings = 'false';
let testkitRef = 'v0.2.0';
let testkitRef = 'v0.5.0';
for (const tok of args) {
if (tok === 'fail') { failOnFindings = 'true'; continue; }
const [k, v] = tok.split('=');
Expand Down Expand Up @@ -115,7 +115,7 @@ jobs:
TESTKIT_REF: ${{ needs.guard.outputs.testkit-ref }}
FIRMWARE: ${{ needs.guard.outputs.firmware }}

- uses: DFXswiss/bitbox-testkit/.github/actions/bitbox-audit@v0.2.0
- uses: DFXswiss/bitbox-testkit/.github/actions/bitbox-audit@v0.5.0
with:
testkit-ref: ${{ needs.guard.outputs.testkit-ref }}
firmware: ${{ needs.guard.outputs.firmware }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflow-templates/bitbox-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ jobs:
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: DFXswiss/bitbox-testkit/.github/actions/bitbox-audit@v0.2.0
- uses: DFXswiss/bitbox-testkit/.github/actions/bitbox-audit@v0.5.0
14 changes: 12 additions & 2 deletions .github/workflow-templates/bitbox-simulator-slash.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
authorized: ${{ steps.authz.outputs.authorized }}
sha: ${{ steps.parse.outputs.sha }}
testkit-ref: ${{ steps.parse.outputs.testkit_ref }}
firmware: ${{ steps.parse.outputs.firmware }}
fail-on-findings: ${{ steps.parse.outputs.fail_on_findings }}
steps:
- name: Authorization
id: authz
Expand Down Expand Up @@ -60,17 +62,23 @@ jobs:
script: |
const body = (context.payload.comment.body || '').trim();
const args = body.replace(/^\/bitbox-simulator\s*/, '').split(/\s+/).filter(Boolean);
let testkitRef = 'v0.3.0';
let testkitRef = 'v0.5.0';
let firmware = '';
let failOnFindings = 'true';
for (const tok of args) {
if (tok === 'fail') { failOnFindings = 'true'; continue; }
const [k, v] = tok.split('=');
if (k === 'ref' && v) testkitRef = v;
if (k === 'firmware' && v) firmware = v;
}
const { data: pr } = await github.rest.pulls.get({
...context.repo,
pull_number: context.issue.number,
});
core.setOutput('sha', pr.head.sha);
core.setOutput('testkit_ref', testkitRef);
core.setOutput('firmware', firmware);
core.setOutput('fail_on_findings', failOnFindings);
await github.rest.reactions.createForIssueComment({
...context.repo,
comment_id: context.payload.comment.id,
Expand All @@ -86,6 +94,8 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ needs.guard.outputs.sha }}
- uses: DFXswiss/bitbox-testkit/.github/actions/bitbox-simulator@v0.3.0
- uses: DFXswiss/bitbox-testkit/.github/actions/bitbox-simulator@v0.5.0
with:
testkit-ref: ${{ needs.guard.outputs.testkit-ref }}
firmware: ${{ needs.guard.outputs.firmware }}
fail-on-findings: ${{ needs.guard.outputs.fail-on-findings }}
2 changes: 1 addition & 1 deletion .github/workflow-templates/bitbox-simulator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ jobs:
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: DFXswiss/bitbox-testkit/.github/actions/bitbox-simulator@v0.3.0
- uses: DFXswiss/bitbox-testkit/.github/actions/bitbox-simulator@v0.5.0
109 changes: 84 additions & 25 deletions .github/workflows/auto-tag.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
name: Auto Tag on Merge

# Conventional-Commits-aware release tagger. On every push to main:
# 1. Find the latest vX.Y.Z tag.
# 2. Run go/cmd/release-version against the latest-tag..HEAD range —
# it parses each commit subject + body as Conventional Commits and
# picks the highest bump (feat! / BREAKING CHANGE → major,
# feat: → minor, fix/perf/refactor/chore/ci/... → patch).
# 3. Create the dual tags vX.Y.Z + go/vX.Y.Z (Go submodule resolver
# needs both at the same commit, see CONTRIBUTING.md Releases).
# 4. Create the matching GitHub Release with auto-generated notes.
#
# Exit code 4 from the version tool means "no commits in the range"
# and short-circuits the rest of the job — that prevents an empty
# re-tag if a maintainer pushes the same commit twice.
on:
push:
branches: [main]
Expand All @@ -22,48 +35,87 @@ jobs:
fetch-depth: 0
fetch-tags: true

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
cache: true
cache-dependency-path: go/go.sum

- name: Get latest tag
id: get-tag
run: |
LATEST_TAG=$(git tag -l --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)

if [ -z "$LATEST_TAG" ]; then
echo "No existing tags found, starting with v0.0.0"
LATEST_TAG="v0.0.0"
echo "::notice::No existing release tag found, this run will emit the initial release"
LATEST_TAG=""
else
echo "::notice::Latest tag: $LATEST_TAG"
fi

echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT
echo "::notice::Latest tag: $LATEST_TAG"
echo "latest_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"

- name: Calculate next version
- name: Decide next version (Conventional Commits)
id: next-version
working-directory: go
env:
LATEST_TAG: ${{ steps.get-tag.outputs.latest_tag }}
run: |
LATEST_TAG="${{ steps.get-tag.outputs.latest_tag }}"
set +e
NEW_TAG=$(go run ./cmd/release-version --base "$LATEST_TAG" --report 2>release-version.stderr)
rc=$?
set -e

# Surface the per-commit report into the job log regardless
# of whether the tool succeeded — useful when a review needs
# to see which commits drove the bump.
if [ -s release-version.stderr ]; then
echo "::group::release-version warnings"
cat release-version.stderr
echo "::endgroup::"
fi

VERSION="${LATEST_TAG#v}"
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
# The tool prints the next version on the first stdout line
# followed by a blank line and the report. Extract just the
# tag for downstream steps; emit the rest as a group.
FIRST_LINE=$(printf '%s' "$NEW_TAG" | head -n 1)
REST=$(printf '%s' "$NEW_TAG" | tail -n +2)
if [ -n "$REST" ]; then
echo "::group::release-version per-commit breakdown"
printf '%s\n' "$REST"
echo "::endgroup::"
fi

NEW_PATCH=$((PATCH + 1))
NEW_TAG="v${MAJOR}.${MINOR}.${NEW_PATCH}"
if [ "$rc" -eq 4 ]; then
echo "::notice::No commits since $LATEST_TAG — skipping release"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "$rc" -ne 0 ]; then
echo "::error::release-version exited $rc"
exit "$rc"
fi

echo "new_tag=$NEW_TAG" >> $GITHUB_OUTPUT
echo "::notice::New tag: $NEW_TAG"
echo "::notice::Next tag: $FIRST_LINE (was: ${LATEST_TAG:-<none>})"
echo "new_tag=$FIRST_LINE" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT"

- name: Check if tag exists
id: check-tag
if: steps.next-version.outputs.skip != 'true'
run: |
NEW_TAG="${{ steps.next-version.outputs.new_tag }}"
if git rev-parse "$NEW_TAG" >/dev/null 2>&1; then
echo "::error::Tag $NEW_TAG already exists!"
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "::error::Tag $NEW_TAG already exists — refusing to overwrite. \
Push an empty fixup commit to bump again, or delete the existing tag if it was misapplied."
echo "exists=true" >> "$GITHUB_OUTPUT"
exit 1
fi
echo "exists=false" >> "$GITHUB_OUTPUT"

- name: Create tag and release
if: steps.check-tag.outputs.exists == 'false'
if: steps.next-version.outputs.skip != 'true' && steps.check-tag.outputs.exists == 'false'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_TAG: ${{ steps.next-version.outputs.new_tag }}
Expand All @@ -72,15 +124,22 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

# Go submodule convention: the module at /go/ needs a `go/vX.Y.Z`
# tag on the same commit, otherwise `go install` cannot resolve
# the package — see CONTRIBUTING.md "Releases".
# Dual-tag for the Go submodule pattern. Without `go/vX.Y.Z`,
# `go install github.com/DFXswiss/bitbox-testkit/go/cmd/...@vX.Y.Z`
# fails because Go's module resolver looks for the path-prefixed
# tag when the module lives in a subdirectory.
GO_TAG="go/${NEW_TAG}"
git tag -a "$NEW_TAG" -m "Release $NEW_TAG"
git tag -a "$GO_TAG" -m "$GO_TAG: submodule tag matching $NEW_TAG"
git push origin "$NEW_TAG" "$GO_TAG"

if [ "$PREV_TAG" = "v0.0.0" ]; then
# --atomic so the server applies both ref updates as a single
# transaction. Without it a partial push could leave the repo
# with vX.Y.Z but no go/vX.Y.Z (or vice versa), and the next
# auto-tag run would fail the "tag exists" check while the
# consumer-facing `go install` would still 404 on the missing
# submodule tag.
git push --atomic origin "$NEW_TAG" "$GO_TAG"

if [ -z "$PREV_TAG" ]; then
gh release create "$NEW_TAG" --title "$NEW_TAG" --generate-notes
else
gh release create "$NEW_TAG" --title "$NEW_TAG" --generate-notes --notes-start-tag "$PREV_TAG"
Expand Down
48 changes: 48 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,54 @@ jobs:
mkdir -p "$WALLET_TESTKIT_SIMCACHE"
go test -tags simulator -timeout 5m ./bitbox/simulator/...

go-simulator-matrix:
# Runs the full baseline against EVERY embedded firmware version
# (v9.19.0 → v9.26.1). Catches regressions where a new scenario
# works against the newest firmware but breaks against an older
# one a real user may still be running — important because the
# BitBox02 only auto-updates when the user opens the BitBoxApp,
# so production has a long tail of older firmwares in the wild.
name: simulator matrix · ${{ matrix.firmware }}
runs-on: ubuntu-latest
defaults:
run:
working-directory: go
strategy:
fail-fast: false
matrix:
firmware:
- bitbox02-multi-v9.26.1-simulator1.0.0-linux-amd64
- bitbox02-multi-v9.25.0-simulator1.0.0-linux-amd64
- bitbox02-multi-v9.24.0-simulator1.0.0-linux-amd64
- bitbox02-multi-v9.23.0-simulator1.0.0-linux-amd64
- bitbox02-multi-v9.22.0-simulator1.0.0-linux-amd64
- bitbox02-multi-v9.21.0-simulator1.0.0-linux-amd64
- bitbox02-multi-v9.20.0-simulator1.0.0-linux-amd64
- bitbox02-multi-v9.19.0-simulator1.0.0-linux-amd64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.24'
cache: true
cache-dependency-path: go/go.sum
- name: cache simulator binaries
uses: actions/cache@v4
with:
path: ~/.cache/bitbox-testkit-simulators
key: bitbox-simulators-${{ hashFiles('go/bitbox/simulator/embedded.go') }}
restore-keys: |
bitbox-simulators-
- name: run scenarios against ${{ matrix.firmware }}
env:
WALLET_TESTKIT_SIMCACHE: ${{ github.workspace }}/.simcache
run: |
mkdir -p "$WALLET_TESTKIT_SIMCACHE"
go run -tags simulator ./cmd/bitbox-simulator-check \
--firmware "${{ matrix.firmware }}" \
--cache "$WALLET_TESTKIT_SIMCACHE" \
--format markdown

ts-unit:
name: TypeScript unit tests
runs-on: ubuntu-latest
Expand Down
Loading
Loading