diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 00000000..9dfee96a --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,22 @@ +name: Security Audit + +on: + push: + branches: [main, master] + pull_request: + schedule: + - cron: "0 0 * * *" # Daily at midnight + +permissions: + contents: read + +jobs: + audit: + name: Cargo Audit + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Run cargo-audit + uses: actions-rust-lang/audit@v1 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..36847092 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,30 @@ +name: CodeQL + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + schedule: + - cron: "0 6 * * 1" # Weekly on Monday + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: rust + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 506c5216..b221aed4 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -15,17 +15,10 @@ jobs: uses: actions/checkout@v6 - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true + uses: dtolnay/rust-toolchain@stable - name: Build Documentation - uses: actions-rs/cargo@v1 - with: - command: doc - args: --all --no-deps + run: cargo doc --all --no-deps - name: Create index run: echo '' > target/doc/index.html diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..47ada723 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,30 @@ +name: Publish to crates.io + +on: + release: + types: + - published + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + environment: release + permissions: + id-token: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Authenticate with crates.io + uses: rust-lang/crates-io-auth-action@v1 + id: auth + + - name: Publish to crates.io + run: cargo publish + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d22f94e8..9fcce7b3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,17 +20,14 @@ jobs: steps: - uses: actions/checkout@v6 + - name: Install ${{ matrix.toolchain }} toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.toolchain }} - override: true - name: Run cargo check - uses: actions-rs/cargo@v1 - with: - command: check + run: cargo check test: name: Test Suite @@ -46,16 +43,12 @@ jobs: submodules: true - name: Install ${{ matrix.toolchain }} toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.toolchain }} - override: true - name: Run cargo test - uses: actions-rs/cargo@v1 - with: - command: test + run: cargo test lints: name: Lints @@ -65,21 +58,12 @@ jobs: uses: actions/checkout@v6 - name: Install stable toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - profile: minimal - toolchain: stable - override: true components: rustfmt, clippy - name: Run cargo fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + run: cargo fmt --all -- --check - name: Run cargo clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: -- -D warnings + run: cargo clippy -- -D warnings diff --git a/dev-bin/release.sh b/dev-bin/release.sh new file mode 100755 index 00000000..518f83f1 --- /dev/null +++ b/dev-bin/release.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +set -eu -o pipefail + +# Check that we're not on the main branch +current_branch=$(git branch --show-current) +if [ "$current_branch" = "main" ]; then + echo "Error: Releases should not be done directly on the main branch." + echo "Please create a release branch and run this script from there." + exit 1 +fi + +# Fetch latest changes and check that we're not behind origin/main +echo "Fetching from origin..." +git fetch origin + +if ! git merge-base --is-ancestor origin/main HEAD; then + echo "Error: Current branch is behind origin/main." + echo "Please merge or rebase with origin/main before releasing." + exit 1 +fi + +changelog=$(cat CHANGELOG.md) + +# Match: ## X.Y.Z - YYYY-MM-DD +regex='## ([0-9]+\.[0-9]+\.[0-9]+) - ([0-9]{4}-[0-9]{2}-[0-9]{2})' + +if [[ ! $changelog =~ $regex ]]; then + echo "Could not find version/date line in CHANGELOG.md!" + echo "Expected format: ## X.Y.Z - YYYY-MM-DD" + exit 1 +fi + +version="${BASH_REMATCH[1]}" +date="${BASH_REMATCH[2]}" + +# Extract release notes (everything between first ## version and next ## version) +notes=$(sed -n '/^## '"$version"'/,/^## [0-9]/p' CHANGELOG.md | sed '1d;$d') + +if [[ "$date" != $(date +"%Y-%m-%d") ]]; then + echo "Release date $date is not today ($(date +"%Y-%m-%d"))!" + exit 1 +fi + +tag="v$version" + +if [ -n "$(git status --porcelain)" ]; then + echo "Working directory is not clean." >&2 + exit 1 +fi + +# Update version in Cargo.toml +current_cargo_version=$(grep -E '^version = "[0-9]+\.[0-9]+\.[0-9]+"' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/') +if [ "$current_cargo_version" != "$version" ]; then + echo "Updating Cargo.toml version from $current_cargo_version to $version" + sed -i "s/^version = \"$current_cargo_version\"/version = \"$version\"/" Cargo.toml +fi + +echo "Running tests..." +cargo test + +echo $'\nDiff:' +git diff + +echo $'\nRelease notes:' +echo "$notes" + +read -r -p "Commit changes and push to origin? [y/N] " should_push + +if [ "$should_push" != "y" ]; then + echo "Aborting" + git checkout -- Cargo.toml + exit 1 +fi + +if [ -n "$(git status --porcelain)" ]; then + git commit -m "Prepare $tag release" -a +fi + +git push + +gh release create --target "$(git branch --show-current)" -t "$version" -n "$notes" "$tag" + +git push --tags