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
20 changes: 20 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Normalize line endings: Git stores LF, checks out platform-native by default.
* text=auto eol=lf
Comment on lines +1 to +2

# Windows-only scripts must keep CRLF on disk.
*.ps1 text eol=crlf
*.psm1 text eol=crlf
*.psd1 text eol=crlf
*.cmd text eol=crlf
*.bat text eol=crlf

# Treat known binary formats as binary so Git never tries to munge them.
*.binlog binary
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.vsix binary
*.zip binary
*.gz binary
41 changes: 41 additions & 0 deletions .github/actions/workspace-version/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (c) 2026 Mike Grier
name: Read workspace version
description: >
Reads [workspace.package].version from the root Cargo.toml (the single
source of truth after workspace version inheritance) and exposes it as an
output so callers do not have to duplicate the Cargo.toml parsing logic.

outputs:
version:
description: Workspace version string (e.g. "0.1.2")
value: ${{ steps.read.outputs.version }}

runs:
using: composite
steps:
- id: read
shell: bash
run: |
set -euo pipefail
# Read the version only from the [workspace.package] section:
# 1. Set `in_section=1` when entering [workspace.package].
# 2. Reset `in_section=0` when any other section header is seen.
# 3. Capture the version string only while in_section is set.
# This avoids accidentally matching a `version = "..."` line that
# might appear in another TOML section before [workspace.package].
ver=$(awk '
/^\[workspace\.package\]/ { in_section=1; next }
/^\[/ { in_section=0 }
in_section && /^version[[:space:]]*=/ {
# POSIX split() on " is universally supported (mawk, gawk, awk).
# Field [2] is the text between the first and second double-quote.
split($0, a, "\"")
print a[2]
exit
}
' Cargo.toml)
if [ -z "$ver" ]; then
echo "::error::Could not parse [workspace.package].version from Cargo.toml"
exit 1
fi
echo "version=$ver" >> "$GITHUB_OUTPUT"
44 changes: 44 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copilot Instructions

Use LF line endings.

## Repository purpose

This repository is a **template** for a Rust crate that will be published to
crates.io. Keep the automation working while replacing the placeholder crate
with project-specific code.

## Working rules

- Prefer small, reviewable changes.
- Do not leave placeholder names like `your-crate-name`, `OWNER`, or
`REPOSITORY` behind when specializing the template for a real project.
- Keep documentation, release automation, and publish automation aligned with
the actual crate metadata.
- If you add or remove workspace members, update any workflow or release config
that assumes a single publishable crate.

## Validation

Run the standard workspace checks from the repository root:

```sh
cargo fmt --all --check
cargo build --workspace --all-targets --locked
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked
```

## Release automation

- `release-please` manages version bumps, tags, and changelog updates.
- The publish workflow expects a `v<version>` tag that matches the crate
version.
- Publishing requires the `RELEASE_PLEASE_TOKEN` and
`CARGO_REGISTRY_TOKEN` repository secrets.

## Planning docs

- Use `DESIGN-NOTES.md` for durable design decisions.
- Use `CHECKLIST.md` for outstanding work.
- Use `PLANS.md` for short-lived implementation plans.
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Keep GitHub Actions and Cargo deps current.
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 5
labels:
- dependencies
- github-actions

- package-ecosystem: cargo
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 5
labels:
- dependencies
- rust
87 changes: 87 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Copyright (c) 2026 Mike Grier. All rights reserved.
name: ci

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

concurrency:
group: ${{ github.repository }}-${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

jobs:
encoding:
name: encoding sanity check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Run check-encoding.ps1
shell: pwsh
run: ./tools/check-encoding.ps1

build-test:
name: build + test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: cargo build
run: cargo build --workspace --all-targets --locked
- name: cargo test
env:
RUST_BACKTRACE: 1
RUST_LIB_BACKTRACE: 1
run: cargo test --workspace --locked --no-fail-fast

fmt:
name: rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: cargo fmt --check
run: cargo fmt --all --check

clippy:
name: clippy (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- name: cargo clippy
run: cargo clippy --workspace --all-targets --locked -- -D warnings

msrv:
name: MSRV check 1.97 (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@1.97.0
- uses: Swatinem/rust-cache@v2
- name: cargo check (MSRV)
run: cargo check --workspace --all-targets --locked
43 changes: 43 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: "CodeQL Advanced"

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '27 3 * * 3'

jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
security-events: write
packages: read
actions: read
contents: read

strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
- language: rust
build-mode: none

steps:
- name: Checkout repository
uses: actions/checkout@v7

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
42 changes: 42 additions & 0 deletions .github/workflows/publish-crate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright (c) 2026 Mike Grier
name: publish-crate

on:
push:
tags:
- 'v*'

permissions:
contents: read

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

jobs:
publish:
name: cargo publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Verify tag matches crate version
run: |
metadata="$(cargo metadata --no-deps --format-version 1)"
member_count="$(printf '%s' "$metadata" | jq '.workspace_members | length')"
if [ "$member_count" -ne 1 ]; then
echo "::error::publish-crate.yml expects exactly one workspace member; found ${member_count}" >&2
exit 1
fi
pkg_id="$(printf '%s' "$metadata" | jq -r '.workspace_members[0]')"
crate_name="$(printf '%s' "$metadata" | jq -r --arg pkg_id "$pkg_id" '.packages[] | select(.id == $pkg_id) | .name')"
version="$(printf '%s' "$metadata" | jq -r --arg pkg_id "$pkg_id" '.packages[] | select(.id == $pkg_id) | .version')"
expected="v${version}"
if [ "${GITHUB_REF_NAME}" != "${expected}" ]; then
echo "::error::tag ${GITHUB_REF_NAME} does not match crate version ${expected}" >&2
exit 1
fi
echo "CRATE_NAME=${crate_name}" >> "$GITHUB_ENV"
- name: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish -p "$CRATE_NAME" --locked
57 changes: 57 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright (c) 2026 Mike Grier
name: release-please

on:
push:
branches: [main]

concurrency:
group: release-please
cancel-in-progress: false

permissions:
contents: write
pull-requests: write

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
pr: ${{ steps.release.outputs.pr }}
steps:
- uses: googleapis/release-please-action@v5
id: release
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

update-lockfile:
name: update Cargo.lock on release PR
runs-on: ubuntu-latest
needs: release-please
if: ${{ needs.release-please.outputs.pr != '' }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ fromJSON(needs.release-please.outputs.pr).headBranchName }}
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2

- name: Update Cargo.lock for version bump
run: cargo check --workspace

- name: Commit updated Cargo.lock
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Cargo.lock
git diff --staged --quiet || git commit -m "chore: update Cargo.lock for version bump"
git push
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Generated by Cargo
debug
target

# Backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these (debug info)
*.pdb

# cargo mutants
**/mutants.out*/

# Scratch / diagnostic output (git-ignored per repo instructions)
.scratch/
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.1.0"
}
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog

All notable changes to this project will be documented in this file.

## [0.1.0] - 2026-08-16

- Initial template contents.
9 changes: 9 additions & 0 deletions CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Checklist

Use this file for the open work needed to turn this template into your actual
crate project. Replace these items with project-specific milestones.

- [ ] Rename the placeholder crate and package metadata.
- [ ] Replace the example library code with the real implementation.
- [ ] Update the release configuration and repository URLs.
- [ ] Confirm CI and crates.io publishing secrets are configured.
Loading