Skip to content

feat(daycount): add ACT and 30/360 day-count conventions (Phase 0) (#2) #6

feat(daycount): add ACT and 30/360 day-count conventions (Phase 0) (#2)

feat(daycount): add ACT and 30/360 day-count conventions (Phase 0) (#2) #6

Workflow file for this run

name: Release to crates.io + SLSA
# Auto-publish flow:
# 1. Push to main → detect job compares Cargo.toml `version` between HEAD and HEAD~1.
# 2. If version bumped → build+package job runs full tests, packages the .crate, hashes it.
# 3. SLSA generic generator (Level 3) creates an in-toto attestation from the hash.
# 4. Publish job uploads to crates.io, tags v<version>, and attaches the .crate
# file plus the SLSA provenance to a GitHub Release.
#
# Required repo secret: CARGO_REGISTRY_TOKEN (scope: publish-update on crate `yield-curves`).
on:
push:
branches: [main]
permissions:
contents: read
jobs:
detect:
name: detect version bump
runs-on: ubuntu-latest
outputs:
should_release: ${{ steps.diff.outputs.should_release }}
version: ${{ steps.diff.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Compare Cargo.toml version vs latest on crates.io
id: diff
shell: bash
run: |
set -euo pipefail
NEW=$(grep -m1 '^version =' Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/')
if [ -z "$NEW" ]; then
echo "::error::cannot read version from Cargo.toml"
exit 1
fi
# Query crates.io for the latest published version. Fallback to ""
# if crate is brand new (404) or if jq misses the field.
PUBLISHED=$(curl -fsSL "https://crates.io/api/v1/crates/yield-curves" \
-H "User-Agent: yield-curves-release-workflow" \
| jq -r '.crate.max_stable_version // ""' || echo "")
echo "cargo_toml=$NEW"
echo "crates_io=$PUBLISHED"
# Publish whenever Cargo.toml differs from what's on crates.io.
# cargo publish enforces monotonic version on the registry side, so
# downgrades fail loudly if a non-bump slips through.
if [ "$PUBLISHED" != "$NEW" ]; then
echo "should_release=true" >> "$GITHUB_OUTPUT"
echo "version=$NEW" >> "$GITHUB_OUTPUT"
else
echo "should_release=false" >> "$GITHUB_OUTPUT"
fi
build:
name: build + package
needs: detect
if: needs.detect.outputs.should_release == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
hashes: ${{ steps.hash.outputs.hashes }}
filename: ${{ steps.hash.outputs.filename }}
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Verify tests pass before publishing
run: cargo test --all
- name: Dry-run publish
run: cargo publish --dry-run
- name: Package
run: cargo package --no-verify
- name: Hash artifact for SLSA
id: hash
shell: bash
run: |
set -euo pipefail
cd target/package
FILE=$(ls *.crate)
HASH_LINE=$(sha256sum "$FILE")
echo "filename=$FILE" >> "$GITHUB_OUTPUT"
echo "hashes=$(echo -n "$HASH_LINE" | base64 -w0)" >> "$GITHUB_OUTPUT"
- name: Upload package
uses: actions/upload-artifact@v4
with:
name: crate-package
path: target/package/*.crate
retention-days: 7
if-no-files-found: error
provenance:
name: SLSA Level 3 provenance
needs: [detect, build]
if: needs.detect.outputs.should_release == 'true'
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
with:
base64-subjects: ${{ needs.build.outputs.hashes }}
provenance-name: yield-curves-provenance.intoto.jsonl
upload-assets: false
publish:
name: publish to crates.io + GitHub Release
needs: [detect, build, provenance]
if: needs.detect.outputs.should_release == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish
# Download artefatos depois do publish — cargo publish recusa se houver
# arquivos não commitados na working tree, então só baixamos pra
# anexar no GitHub Release.
- name: Download package
uses: actions/download-artifact@v4
with:
name: crate-package
path: target/package
- name: Download SLSA provenance
uses: actions/download-artifact@v4
with:
name: ${{ needs.provenance.outputs.provenance-name }}
path: provenance
- name: Create git tag
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag "v${{ needs.detect.outputs.version }}"
git push origin "v${{ needs.detect.outputs.version }}"
- name: Build changelog from commits since previous tag
id: changelog
shell: bash
run: |
set -euo pipefail
# 2nd entry by creation date is the previous tag (1st is the one we just pushed).
PREV_TAG=$(git tag --sort=-creatordate | sed -n '2p' || true)
if [ -n "$PREV_TAG" ]; then
RANGE="$PREV_TAG..HEAD"
HEADER="### Changes since $PREV_TAG"
else
# First-ever release tag — list everything up to HEAD (capped).
RANGE="HEAD"
HEADER="### Changes"
fi
{
echo "header=$HEADER"
echo 'log<<EOF'
git log --pretty=format:"- %s (%h)" "$RANGE" | head -n 50
echo
echo EOF
} >> "$GITHUB_OUTPUT"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.detect.outputs.version }}
name: v${{ needs.detect.outputs.version }}
files: |
target/package/*.crate
provenance/*.intoto.jsonl
body: |
## yield-curves v${{ needs.detect.outputs.version }}
Published to crates.io: https://crates.io/crates/yield-curves/${{ needs.detect.outputs.version }}
${{ steps.changelog.outputs.header }}
${{ steps.changelog.outputs.log }}
### Verifying SLSA provenance
This release ships a SLSA Level 3 provenance attestation generated by
[`slsa-github-generator`](https://github.com/slsa-framework/slsa-github-generator).
Verify with:
```bash
slsa-verifier verify-artifact \
--provenance-path yield-curves-provenance.intoto.jsonl \
--source-uri github.com/${{ github.repository }} \
yield-curves-${{ needs.detect.outputs.version }}.crate
```