From 1474a2ee7e53623e866ca54db07698785c34e3e2 Mon Sep 17 00:00:00 2001 From: Steve Morin Date: Sat, 30 May 2026 22:34:10 -0700 Subject: [PATCH] ci: enable crates.io publishing; reclaim `togl` crate for the CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The release pipeline only ships GitHub binaries, so crates.io has been stuck at `togl` 0.2.3 (published before the toggle-*→togl-* crate rename). This wires up crate publishing again. - Rename the CLI package `togl-cli` → `togl` (directory unchanged; binaries stay `toggle`/`togl`). Continues the existing `togl` line so `cargo install togl` works, as the README already advertises. - Add a `version` to the `togl-lib` dep/dev-dep (required for publishing). - Mark `togl-ffi` `publish = false` (it's the libtogl C library, not a Rust dependency). - Add a `publish-crates` job to release.yml: on a release tag it syncs the togl-lib dep version to the tag, then publishes `togl-lib` and `togl` (in order) with CARGO_REGISTRY_TOKEN. - Update the `-p togl-cli` references in nix/togl.nix and the shell test script. Verified: full test suite + `nix build .#togl` green after the rename; `cargo publish --dry-run` clean for togl-lib (togl manifest validated; its full dry-run is gated only on togl-lib not yet being on crates.io, resolved by the publish ordering). actionlint clean. Refs P11. First publish fires on the next release tag once CARGO_REGISTRY_TOKEN is added. --- .github/workflows/release.yml | 46 +++++++++++++++++++++++++++++++++++ Cargo.lock | 2 +- PROJECTS.md | 34 ++++++++++++++++++++++++++ crates/togl-cli/Cargo.toml | 8 +++--- crates/togl-ffi/Cargo.toml | 3 +++ nix/togl.nix | 4 +-- tests/test_simple_python.sh | 10 ++++---- 7 files changed, 96 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7c8f8f..a9ceea5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,3 +56,49 @@ jobs: zip: windows env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Publish the library and CLI crates to crates.io. `togl-lib` is published + # first because `togl` depends on it (cargo waits for it to be available + # before the dependent publish). `togl-ffi` is `publish = false`. + # + # Requires the `CARGO_REGISTRY_TOKEN` secret. Until it is set, this job fails + # loudly with a clear message but does not affect the binary-build job above. + publish-crates: + name: Publish to crates.io + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + steps: + - name: Validate tag ref + if: github.ref_type != 'tag' + run: | + echo "::error::Release workflow must be triggered by a tag push, not a branch." + exit 1 + + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable + + - name: Require crates.io token + run: | + if [ -z "${CARGO_REGISTRY_TOKEN}" ]; then + echo "::error::CARGO_REGISTRY_TOKEN secret is not set. Add it under repo Settings → Secrets to enable crates.io publishing." + exit 1 + fi + + # release-please bumps the workspace version but not the inter-crate dep + # requirement, so pin togl-lib's `version` to this release before publish. + - name: Sync togl-lib dependency version to the release + run: | + version="${GITHUB_REF_NAME#v}" + sed -i "s|\(togl-lib = { path = \"../togl-lib\", version = \"\)[^\"]*|\1${version}|" crates/togl-cli/Cargo.toml + git --no-pager diff crates/togl-cli/Cargo.toml + + # --allow-dirty: the version sync above is an intentional, uncommitted + # release-time edit. + - name: Publish togl-lib + run: cargo publish -p togl-lib --allow-dirty + + - name: Publish togl + run: cargo publish -p togl --allow-dirty diff --git a/Cargo.lock b/Cargo.lock index 9bf86ec..fda5c76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -921,7 +921,7 @@ dependencies = [ ] [[package]] -name = "togl-cli" +name = "togl" version = "0.5.0" dependencies = [ "anyhow", diff --git a/PROJECTS.md b/PROJECTS.md index ac7c2c1..43445d9 100644 --- a/PROJECTS.md +++ b/PROJECTS.md @@ -264,3 +264,37 @@ interface. Three additive layers, shipped in order: - `cargo test` green (all parity + existing suites) - `cargo clippy --all-targets -- -D warnings` clean - `togl --help` / `--man` / `--completions` render the subcommands under the aliased bin name + +--- + +## [~] Project P11: crates.io publishing (v0.6.0) +**Goal**: Resume publishing to crates.io (stuck at `togl` 0.2.3 since the +release pipeline only ships GitHub binaries). Reclaim the `togl` crate name for +the CLI and auto-publish the library + CLI on each release tag. + +**Decisions** +- Rename the CLI **package** `togl-cli` → `togl` (directory stays `crates/togl-cli`; + binaries stay `toggle`/`togl`) — continues the existing `togl` 0.2.3 line and + keeps `cargo install togl` working (already in the README). +- Publish `togl-lib` (library) + `togl` (CLI). `togl-ffi` is `publish = false` + (it's the C library, not a Rust dependency). +- Inter-crate `togl-lib` dep gets a `version` (required for publishing); the + release workflow syncs it to the release tag before publishing. + +**Out of Scope** +- Publishing `togl-ffi` to crates.io. +- Switching release tooling (release-please/`nix-update` stay as-is). + +### Tests & Tasks +- [x] [P11-T01] Rename CLI package → `togl`; add `version` to `togl-lib` dep/dev-dep; + `togl-ffi` `publish = false`; update `-p togl-cli` refs (flake, test script) +- [x] [P11-T02] `publish-crates` job in `release.yml` (sync dep version → tag, + publish `togl-lib` then `togl`, `CARGO_REGISTRY_TOKEN`) +- [x] [P11-TS01] `cargo publish --dry-run` (togl-lib full; togl manifest), + full test suite + `nix build .#togl` green after the rename + +### Manual Steps (maintainer) +- Add repo secret `CARGO_REGISTRY_TOKEN` (crates.io API token). Until then the + publish job fails loudly but does not block the binary build. +- First publish fires on the next release tag (e.g. v0.6.0); crates.io versions + jump 0.2.3 → that release (allowed — versions only need to increase). diff --git a/crates/togl-cli/Cargo.toml b/crates/togl-cli/Cargo.toml index ab1a89e..9c90859 100644 --- a/crates/togl-cli/Cargo.toml +++ b/crates/togl-cli/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "togl-cli" +name = "togl" description = "A CLI tool for toggling code comments across multiple languages" version.workspace = true edition.workspace = true @@ -15,8 +15,10 @@ path = "src/main.rs" name = "togl" path = "src/bin/togl.rs" +# The `version` is required for crates.io publishing and is kept in sync with +# the workspace version by the release workflow before each publish. [dependencies] -togl-lib = { path = "../togl-lib" } +togl-lib = { path = "../togl-lib", version = "0.5.0" } anyhow.workspace = true serde.workspace = true serde_json = "1" @@ -26,7 +28,7 @@ clap_mangen = "0.2" signal-hook = "0.3" [dev-dependencies] -togl-lib = { path = "../togl-lib" } +togl-lib = { path = "../togl-lib", version = "0.5.0" } assert_cmd = "2.0" predicates = "3.0" tempfile.workspace = true diff --git a/crates/togl-ffi/Cargo.toml b/crates/togl-ffi/Cargo.toml index 6f4c285..fab9c75 100644 --- a/crates/togl-ffi/Cargo.toml +++ b/crates/togl-ffi/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true +# Not published to crates.io: this crate produces the libtogl C library +# (static/cdylib) for C consumers, not a Rust crate others depend on. +publish = false [lib] name = "togl" diff --git a/nix/togl.nix b/nix/togl.nix index 03fa7ec..af03ca2 100644 --- a/nix/togl.nix +++ b/nix/togl.nix @@ -19,8 +19,8 @@ rustPlatform.buildRustPackage { cargoLock.lockFile = src + "/Cargo.lock"; # Virtual workspace: build and test only the CLI crate. - cargoBuildFlags = [ "-p" "togl-cli" ]; - cargoTestFlags = [ "-p" "togl-cli" ]; + cargoBuildFlags = [ "-p" "togl" ]; + cargoTestFlags = [ "-p" "togl" ]; meta = { description = "CLI tool for toggling code comments across multiple languages"; diff --git a/tests/test_simple_python.sh b/tests/test_simple_python.sh index 8cb858e..e83abe6 100755 --- a/tests/test_simple_python.sh +++ b/tests/test_simple_python.sh @@ -13,7 +13,7 @@ cp tests/fixtures/simple_python.py tests/fixtures/simple_python.py.orig # Test 1: Toggle the debug section ON (comment it out) echo "Test 1: Toggle 'debug' section ON (comment it out)" -cargo run -p togl-cli -- --section debug --force on tests/fixtures/simple_python.py +cargo run -p togl -- --section debug --force on tests/fixtures/simple_python.py # Check if it was properly commented if grep -q "#.*print(\"Debug information\")" tests/fixtures/simple_python.py; then @@ -25,7 +25,7 @@ fi # Test 2: Toggle the debug section OFF (uncomment it) echo "Test 2: Toggle 'debug' section OFF (uncomment it)" -cargo run -p togl-cli -- --section debug --force off tests/fixtures/simple_python.py +cargo run -p togl -- --section debug --force off tests/fixtures/simple_python.py # Check if it was properly uncommented if grep -q "print(\"Debug information\")" tests/fixtures/simple_python.py && ! grep -q "#.*print(\"Debug information\")" tests/fixtures/simple_python.py; then @@ -37,7 +37,7 @@ fi # Test 3: Toggle the feature section ON (comment it out) echo "Test 3: Toggle 'feature' section ON (comment it out)" -cargo run -p togl-cli -- --section feature --force on tests/fixtures/simple_python.py +cargo run -p togl -- --section feature --force on tests/fixtures/simple_python.py # Check if it was properly commented if grep -q "#.*print(\"This is an experimental feature\")" tests/fixtures/simple_python.py; then @@ -49,11 +49,11 @@ fi # Test 4: Toggle by line range echo "Test 4: Toggle line range (lines 3-6)" -cargo run -p togl-cli -- --line 3:6 tests/fixtures/simple_python.py +cargo run -p togl -- --line 3:6 tests/fixtures/simple_python.py # Test 5: Toggle all sections at once echo "Test 5: Toggle all sections at once" -cargo run -p togl-cli -- --section debug --section feature tests/fixtures/simple_python.py +cargo run -p togl -- --section debug --section feature tests/fixtures/simple_python.py # Restore original file mv tests/fixtures/simple_python.py.orig tests/fixtures/simple_python.py