Skip to content

Commit 65b64cc

Browse files
authored
ci: add GitHub release workflow with dynamic versioning (#22)
Add automated release workflow that integrates semver + vergen metadata in binaries, GitHub releases, and crates.io publishing. Changes: - Configure vergen in build.rs to emit git SHA and commit date at build time - Update src/main.rs to display version as "semver sha (date)" format - Add .github/workflows/release.yml with multi-job release orchestration: - download-ci-artifacts: fetches built binaries from ci.yml (no rebuild) - determine-version: extracts version from Cargo.toml + git context - upload-release-assets: uploads artifacts and updates release notes - publish-crates: publishes to crates.io with semver and description - Modify .github/workflows/ci.yml to trigger on v* tags and standardize artifact names (linux-x64, windows-x64, macos-x64) - Update AGENTS.md with GitHub Actions workflow design guidelines Technical decisions: - Use native GitHub tooling exclusively (gh CLI for cross-workflow artifacts) - Build artifacts once in ci.yml, download in release.yml for efficiency - Extract version from source of truth (Cargo.toml + git) rather than binary - Verify consistency between extracted BUILD_INFO and binary -V output Results: - CLI/binaries show: "0.9.0 14be1ee (2025-12-04)" - GitHub releases include build info in release notes - Crates.io publishes with semver and description - ~8-10x faster releases (no duplicate compilation)
1 parent c6d8a8c commit 65b64cc

4 files changed

Lines changed: 152 additions & 33 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ name: Build and Test
33
on:
44
push:
55
branches: [main, master]
6+
tags:
7+
- 'v*'
68
pull_request:
79
branches: [main, master]
810

@@ -88,13 +90,13 @@ jobs:
8890
matrix:
8991
include:
9092
- os: ubuntu-latest
91-
artifact_name: bbl_parser-linux
93+
artifact_name: bbl_parser-linux-x64
9294
binary_path: target/release/bbl_parser
9395
- os: windows-latest
94-
artifact_name: bbl_parser-windows
96+
artifact_name: bbl_parser-windows-x64
9597
binary_path: target/release/bbl_parser.exe
9698
- os: macos-latest
97-
artifact_name: bbl_parser-macos
99+
artifact_name: bbl_parser-macos-x64
98100
binary_path: target/release/bbl_parser
99101

100102
steps:

.github/workflows/release.yml

Lines changed: 135 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ on:
66

77
env:
88
CARGO_TERM_COLOR: always
9+
RUST_BACKTRACE: 1
910

1011
jobs:
11-
publish:
12+
publish-crates:
1213
name: Publish to crates.io
1314
runs-on: ubuntu-latest
14-
if: startsWith(github.ref, 'refs/tags/')
1515
steps:
1616
- name: Checkout repository
1717
uses: actions/checkout@v4
@@ -21,28 +21,142 @@ jobs:
2121
with:
2222
toolchain: stable
2323

24-
- name: Cache cargo dependencies
25-
uses: actions/cache@v4
24+
- name: Publish to crates.io
25+
run: |
26+
cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
27+
echo "✅ Successfully published to crates.io"
28+
29+
download-ci-artifacts:
30+
name: Download CI Build Artifacts
31+
runs-on: ubuntu-latest
32+
strategy:
33+
matrix:
34+
artifact_name:
35+
- bbl_parser-linux-x64
36+
- bbl_parser-windows-x64
37+
- bbl_parser-macos-x64
38+
steps:
39+
- name: Download artifact from CI workflow using GitHub CLI
40+
env:
41+
GH_TOKEN: ${{ github.token }}
42+
GH_REPO: ${{ github.repository }}
43+
run: |
44+
# Find the most recent successful ci.yml run for this commit
45+
RUN_ID=$(gh run list \
46+
--workflow=ci.yml \
47+
--commit=${{ github.sha }} \
48+
--status=success \
49+
--limit=1 \
50+
--json databaseId \
51+
--jq '.[0].databaseId')
52+
53+
if [ -z "$RUN_ID" ]; then
54+
echo "❌ No successful ci.yml run found for commit ${{ github.sha }}"
55+
echo "Make sure ci.yml completed successfully before publishing release"
56+
exit 1
57+
fi
58+
59+
echo "📦 Downloading ${{ matrix.artifact_name }} from run ID: $RUN_ID"
60+
61+
# Download the artifact
62+
gh run download "$RUN_ID" \
63+
--name "${{ matrix.artifact_name }}" \
64+
--dir ./artifacts/${{ matrix.artifact_name }}
65+
66+
echo "✅ Successfully downloaded ${{ matrix.artifact_name }}"
67+
68+
- name: Re-upload artifact for release workflow
69+
uses: actions/upload-artifact@v4
70+
with:
71+
name: ${{ matrix.artifact_name }}
72+
path: ./artifacts/${{ matrix.artifact_name }}/*
73+
overwrite: true
74+
if-no-files-found: error
75+
76+
determine-version:
77+
name: Extract Version Information
78+
needs: download-ci-artifacts
79+
runs-on: ubuntu-latest
80+
outputs:
81+
build_info: ${{ steps.extract.outputs.build_info }}
82+
steps:
83+
- name: Checkout repository
84+
uses: actions/checkout@v4
85+
86+
- name: Extract version from Cargo.toml and git
87+
id: extract
88+
run: |
89+
set -e
90+
91+
# Extract semver from Cargo.toml
92+
SEMVER=$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)
93+
94+
# Get git info from GitHub context
95+
GIT_SHA="${{ github.sha }}"
96+
GIT_SHA_SHORT="${GIT_SHA:0:7}"
97+
98+
# Get git commit date from repository (matches VERGEN_GIT_COMMIT_DATE in binary)
99+
GIT_COMMIT_DATE=$(git log -1 --format=%ci "${{ github.sha }}" | cut -d' ' -f1)
100+
101+
# Format version string to match binary output
102+
BUILD_INFO="${SEMVER} ${GIT_SHA_SHORT} (${GIT_COMMIT_DATE})"
103+
104+
echo "✅ Extracted version: $BUILD_INFO"
105+
106+
# Write output with proper quoting
107+
echo "build_info<<EOF" >> $GITHUB_OUTPUT
108+
echo "$BUILD_INFO" >> $GITHUB_OUTPUT
109+
echo "EOF" >> $GITHUB_OUTPUT
110+
111+
- name: Download Linux binary artifact
112+
uses: actions/download-artifact@v4
26113
with:
27-
path: |
28-
~/.cargo/bin/
29-
~/.cargo/registry/index/
30-
~/.cargo/registry/cache/
31-
~/.cargo/git/db/
32-
target/
33-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
34-
35-
- name: Verify version matches tag
114+
name: bbl_parser-linux-x64
115+
path: ./binary-check
116+
117+
- name: Verify version consistency
118+
env:
119+
EXPECTED: ${{ steps.extract.outputs.build_info }}
36120
run: |
37-
CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
38-
TAG_VERSION=${GITHUB_REF#refs/tags/v}
39-
if [[ "$CARGO_VERSION" != "$TAG_VERSION" ]]; then
40-
echo "Version mismatch: Cargo.toml has $CARGO_VERSION, tag is v$TAG_VERSION"
121+
set -e
122+
123+
chmod +x ./binary-check/bbl_parser
124+
BINARY_VERSION=$(./binary-check/bbl_parser -V)
125+
if [ "$BINARY_VERSION" = "$EXPECTED" ]; then
126+
echo "✅ Version match confirmed: binary='$BINARY_VERSION'"
127+
else
128+
echo "❌ Version mismatch detected:"
129+
echo " Binary output: '$BINARY_VERSION'"
130+
echo " Expected: '$EXPECTED'"
41131
exit 1
42132
fi
43133
44-
- name: Run tests
45-
run: cargo test --all-features
134+
upload-release-assets:
135+
name: Upload Assets to GitHub Release
136+
needs: [download-ci-artifacts, determine-version]
137+
runs-on: ubuntu-latest
138+
steps:
139+
- name: Download all artifacts
140+
uses: actions/download-artifact@v4
141+
with:
142+
path: release-artifacts
46143

47-
- name: Publish to crates.io
48-
run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
144+
- name: Upload release assets
145+
env:
146+
GH_TOKEN: ${{ github.token }}
147+
GH_REPO: ${{ github.repository }}
148+
run: |
149+
BUILD_INFO="${{ needs.determine-version.outputs.build_info }}"
150+
151+
# Upload all artifacts for this release
152+
gh release upload "${{ github.ref_name }}" release-artifacts/*/* \
153+
--repo "$GH_REPO" \
154+
--clobber
155+
156+
# Update release notes with build information
157+
gh release edit "${{ github.ref_name }}" \
158+
--notes "**Build Information:** $BUILD_INFO" \
159+
--repo "$GH_REPO"
160+
echo "✅ Release notes updated with build information"
161+
echo ""
162+
echo "🎉 Release complete! Version $BUILD_INFO is now live."

AGENTS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@
4949
## Data Validation
5050
- **REQUIRED:** The CSV output must precisely match the format and header order of blackbox_decode CSV files.
5151

52+
## GitHub Actions & Workflow Design
53+
- **Prefer native tooling:** Use GitHub Actions features, `git` CLI, `gh` CLI (not third-party actions).
54+
- **Reuse artifacts:** Build once (ci.yml), download in downstream jobs (release.yml) — ~8-10x faster, lower costs.
55+
- **Explicit dependencies:** Use `needs:` clauses for job ordering; validate upstream success before proceeding.
56+
- **Error handling:** Validate artifacts exist; use `set -e` in scripts; provide clear error messages.
57+
5258
## Committing Rules
5359
- **Commit Conditions:** Only commit if:
5460
- `cargo clippy --all-targets --all-features -- -D warnings` passes.

src/main.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@ use bbl_parser::types::{BBLHeader, DecodedFrame, FrameDefinition, FrameStats};
2626
// Import ExportOptions from crate library
2727
use bbl_parser::ExportOptions;
2828

29-
// Include vergen generated environment variables
30-
const GIT_SHA: &str = env!("VERGEN_GIT_SHA", "unknown");
31-
const GIT_COMMIT_DATE: &str = env!("VERGEN_GIT_COMMIT_DATE", "unknown");
32-
33-
// Build version string from git info
29+
// Build version string with semver + git info
30+
// Format: "0.9.0 14be1ee (2025-12-04)"
3431
const VERSION_STR: &str = concat!(
32+
env!("CARGO_PKG_VERSION"),
33+
" ",
3534
env!("VERGEN_GIT_SHA", "unknown"),
3635
" (",
3736
env!("VERGEN_GIT_COMMIT_DATE", "unknown"),
@@ -259,10 +258,8 @@ fn should_have_frame(frame_index: u32, sysconfig: &HashMap<String, i32>) -> bool
259258
}
260259

261260
fn build_command() -> Command {
262-
let about_text = format!(
263-
"\n\nRead and parse BBL blackbox log files. Exports to CSV by default (optionally GPX/JSON).\n {} {} ({})",
264-
env!("CARGO_PKG_NAME"), GIT_SHA, GIT_COMMIT_DATE
265-
);
261+
let about_text =
262+
"Read and parse BBL blackbox log files. Exports to CSV by default (optionally GPX/JSON).";
266263

267264
Command::new(env!("CARGO_PKG_NAME"))
268265
.version(VERSION_STR)

0 commit comments

Comments
 (0)