diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e26c7b..428c1e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to the Toolpath workspace are documented here. -## path-cli 0.5.0 + toolpath-cli 0.5.1 +## path-cli 0.5.0 + toolpath-cli 0.5.1 + workspace re-alignment ### path-cli 0.5.0 (new crate name) @@ -14,6 +14,10 @@ All notable changes to the Toolpath workspace are documented here. - `toolpath-cli` is now a tiny shim crate whose only job is to make `cargo install toolpath-cli` keep working — it depends on `path-cli` and ships the same `path` binary. Existing users see no behavioral change on upgrade. The shim will be retired in a future release; pin to `path-cli` directly to avoid the eventual removal. - The dev-only `gen_synthetic_path` helper is no longer shipped from this crate; it lives in `path-cli` only. +### toolpath-dot 0.1.3, toolpath-md 0.2.1, toolpath-git 0.1.4, toolpath-github 0.2.1 (publish re-alignment) + +Patch bumps with no source changes. These four satellite crates were last released when `toolpath` was at 0.1.5, so their on-registry manifests still pin `toolpath = "0.1.5"`. Without these bumps, publishing any new crate (like `path-cli`) that depends on both `toolpath = "0.2.0"` and one of these four would drag two majors of `toolpath` into cargo's publish-time resolution and fail with E0308 type mismatches between `toolpath::types::Document` and `toolpath::v1::Document`. Each crate still uses `toolpath = { workspace = true }`, so the new published versions automatically pick up the workspace's current `toolpath = "0.2.0"` and the skew is closed. + ## toolpath-claude 0.8.0 + toolpath-gemini 0.2.0 + toolpath-pi 0.2.0 ### toolpath-claude 0.8.0 diff --git a/Cargo.lock b/Cargo.lock index dc3d20e..8835030 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2002,7 +2002,7 @@ dependencies = [ [[package]] name = "toolpath-dot" -version = "0.1.2" +version = "0.1.3" dependencies = [ "toolpath", ] @@ -2024,7 +2024,7 @@ dependencies = [ [[package]] name = "toolpath-git" -version = "0.1.3" +version = "0.1.4" dependencies = [ "anyhow", "chrono", @@ -2035,7 +2035,7 @@ dependencies = [ [[package]] name = "toolpath-github" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "chrono", @@ -2047,7 +2047,7 @@ dependencies = [ [[package]] name = "toolpath-md" -version = "0.2.0" +version = "0.2.1" dependencies = [ "serde_json", "toolpath", diff --git a/Cargo.toml b/Cargo.toml index dd7d247..49a9722 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,14 +23,14 @@ license = "Apache-2.0" [workspace.dependencies] toolpath = { version = "0.2.0", path = "crates/toolpath" } toolpath-convo = { version = "0.7.0", path = "crates/toolpath-convo" } -toolpath-git = { version = "0.1.3", path = "crates/toolpath-git" } +toolpath-git = { version = "0.1.4", path = "crates/toolpath-git" } toolpath-claude = { version = "0.8.0", path = "crates/toolpath-claude", default-features = false } toolpath-gemini = { version = "0.2.0", path = "crates/toolpath-gemini", default-features = false } toolpath-codex = { version = "0.1.0", path = "crates/toolpath-codex" } toolpath-opencode = { version = "0.1.0", path = "crates/toolpath-opencode" } -toolpath-github = { version = "0.2.0", path = "crates/toolpath-github" } -toolpath-dot = { version = "0.1.2", path = "crates/toolpath-dot" } -toolpath-md = { version = "0.2.0", path = "crates/toolpath-md" } +toolpath-github = { version = "0.2.1", path = "crates/toolpath-github" } +toolpath-dot = { version = "0.1.3", path = "crates/toolpath-dot" } +toolpath-md = { version = "0.2.1", path = "crates/toolpath-md" } toolpath-pi = { version = "0.2.0", path = "crates/toolpath-pi" } path-cli = { version = "0.5.0", path = "crates/path-cli" } diff --git a/crates/toolpath-dot/Cargo.toml b/crates/toolpath-dot/Cargo.toml index 1ef0177..6715607 100644 --- a/crates/toolpath-dot/Cargo.toml +++ b/crates/toolpath-dot/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "toolpath-dot" -version = "0.1.2" +version = "0.1.3" edition.workspace = true license.workspace = true repository = "https://github.com/empathic/toolpath" diff --git a/crates/toolpath-git/Cargo.toml b/crates/toolpath-git/Cargo.toml index c23aefc..c4c4aa7 100644 --- a/crates/toolpath-git/Cargo.toml +++ b/crates/toolpath-git/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "toolpath-git" -version = "0.1.3" +version = "0.1.4" edition.workspace = true license.workspace = true repository = "https://github.com/empathic/toolpath" diff --git a/crates/toolpath-github/Cargo.toml b/crates/toolpath-github/Cargo.toml index 47c0da9..e60b036 100644 --- a/crates/toolpath-github/Cargo.toml +++ b/crates/toolpath-github/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "toolpath-github" -version = "0.2.0" +version = "0.2.1" edition.workspace = true license.workspace = true repository = "https://github.com/empathic/toolpath" diff --git a/crates/toolpath-md/Cargo.toml b/crates/toolpath-md/Cargo.toml index e7457e1..30fd2a6 100644 --- a/crates/toolpath-md/Cargo.toml +++ b/crates/toolpath-md/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "toolpath-md" -version = "0.2.0" +version = "0.2.1" edition.workspace = true license.workspace = true repository = "https://github.com/empathic/toolpath" diff --git a/scripts/release.sh b/scripts/release.sh index 0676a17..1b80a1f 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -1,12 +1,23 @@ #!/usr/bin/env bash set -euo pipefail -# Publish all workspace crates to crates.io in dependency order. +# Verify (default) or publish (--execute) all workspace crates to crates.io +# in dependency order. +# +# Default mode is a dry run — it packages each crate, runs `cargo publish +# --dry-run` against the real crates.io view, and reports what *would* +# happen. Nothing is uploaded. This catches publish-time resolution issues +# (e.g. deps pinning incompatible major versions of a shared crate) that +# `cargo build/test --workspace` cannot see, because workspace builds use +# local path-deps while `cargo publish` resolves against the registry. +# +# Pass --execute to actually publish. # # Usage: -# scripts/release.sh # publish for real (prompts for confirmation) -# scripts/release.sh --dry-run # verify packaging without uploading -# scripts/release.sh --yes # skip confirmation prompt +# scripts/release.sh # dry-run (default; safe) +# scripts/release.sh --execute # publish for real (prompts) +# scripts/release.sh --execute --yes # publish for real, skip prompt +# scripts/release.sh --dry-run # alias for default (back-compat) # # Dependency order: # 1. toolpath (no workspace deps) @@ -25,29 +36,38 @@ set -euo pipefail ALL_CRATES=(toolpath toolpath-convo toolpath-git toolpath-github toolpath-dot toolpath-md toolpath-claude toolpath-gemini toolpath-codex toolpath-opencode toolpath-pi path-cli toolpath-cli) -DRY_RUN="" +EXECUTE=0 AUTO_YES="" for arg in "$@"; do case "$arg" in - --dry-run) DRY_RUN="--dry-run" ;; - --yes|-y) AUTO_YES=1 ;; - *) echo "unknown argument: $arg"; exit 1 ;; + --execute) EXECUTE=1 ;; + --dry-run) ;; # back-compat: dry-run is the default + --yes|-y) AUTO_YES=1 ;; + -h|--help) + sed -n '4,28p' "$0" | sed 's/^# \{0,1\}//' + exit 0 + ;; + *) echo "unknown argument: $arg"; echo "see --help"; exit 1 ;; esac done -if [[ -n "$DRY_RUN" ]]; then - echo "=== DRY RUN ===" - echo +if (( EXECUTE )); then + DRY_RUN="" + echo "=== mode: EXECUTE — will publish to crates.io ===" +else + DRY_RUN="--dry-run" + echo "=== mode: dry-run (pass --execute to publish for real) ===" fi +echo ALLOW_DIRTY="" if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then - if [[ -n "$DRY_RUN" ]]; then - ALLOW_DIRTY="--allow-dirty" - else + if (( EXECUTE )); then echo "error: working directory has uncommitted changes" echo "commit or stash before publishing" exit 1 + else + ALLOW_DIRTY="--allow-dirty" fi fi @@ -82,7 +102,7 @@ already_published() { wait_for_index() { local crate="$1" local version="$2" - if [[ -n "$DRY_RUN" ]]; then + if ! (( EXECUTE )); then return fi echo " waiting for $crate $version to appear on crates.io index..." @@ -97,7 +117,7 @@ wait_for_index() { } # --- Survey: check what needs publishing --- -# Uses parallel indexed arrays instead of associative arrays (bash 3.2 compat) +# Uses parallel indexed arrays instead of associative arrays (bash 3.2 compat). echo "=== surveying crates ===" @@ -109,10 +129,7 @@ for i in "${!ALL_CRATES[@]}"; do crate="${ALL_CRATES[$i]}" version=$(get_version "$crate") VERSIONS+=("$version") - if [[ -n "$DRY_RUN" ]]; then - STATUSES+=("publish") - TO_PUBLISH+=("$crate") - elif already_published "$crate" "$version"; then + if already_published "$crate" "$version"; then STATUSES+=("skip") else STATUSES+=("publish") @@ -141,9 +158,9 @@ for i in "${!ALL_CRATES[@]}"; do done echo -# --- Confirmation --- +# --- Confirmation (only when actually publishing) --- -if [[ -z "$DRY_RUN" && -z "$AUTO_YES" ]]; then +if (( EXECUTE )) && [[ -z "$AUTO_YES" ]]; then read -rp "proceed? [y/N] " answer if [[ "$answer" != "y" && "$answer" != "Y" ]]; then echo "aborted." @@ -152,15 +169,73 @@ if [[ -z "$DRY_RUN" && -z "$AUTO_YES" ]]; then echo fi -# --- Pre-flight: run tests and clippy --- +# --- Pre-flight: workspace tests and clippy --- echo "=== pre-flight checks ===" cargo test --workspace --quiet cargo clippy --workspace --quiet -- -D warnings cargo doc --workspace --no-deps --quiet -echo "all checks passed" +echo "workspace checks ok" echo +# --- Pre-flight: per-crate publish dry-run --- +# +# Workspace `cargo build/test` resolve every dep through local path entries, +# bypassing crates.io entirely. `cargo publish --dry-run` resolves against +# the registry — exactly the view that matters at publish time. This loop +# catches issues like "satellite-crate-A on crates.io still pins old toolpath, +# while we depend directly on a newer toolpath" before any real upload. +# +# Failures classed as chicken-and-egg (the failing dep is itself in this +# release's TO_PUBLISH set, so it'll land on the registry mid-publish) are +# tolerated; everything else aborts. + +echo "=== publish dry-runs ===" +PREFLIGHT_FAILED=() +for crate in "${TO_PUBLISH[@]}"; do + logfile=$(mktemp -t release-dryrun.XXXXXX) + rc=0 + if [[ "$crate" == "toolpath-cli" ]]; then + (cd crates/toolpath-cli && cargo publish --dry-run $ALLOW_DIRTY) > "$logfile" 2>&1 || rc=$? + else + cargo publish --dry-run $ALLOW_DIRTY -p "$crate" > "$logfile" 2>&1 || rc=$? + fi + if (( rc == 0 )); then + echo " $crate: ok" + else + # Cargo phrases "dep not on registry yet" several ways depending on + # context. Try each known shape; whichever produces a match wins. + missing=$(sed -nE \ + -e 's/.*no matching package named `([^`]+)`.*/\1/p' \ + -e 's/.*failed to select a version for the requirement `([^ `]+).*/\1/p' \ + -e 's/.*could not find `([^`]+)` in registry.*/\1/p' \ + "$logfile" | head -1) + if [[ -n "$missing" ]] && printf '%s\n' "${TO_PUBLISH[@]}" | grep -qFx "$missing"; then + echo " $crate: deferred (depends on $missing being published in this run)" + else + echo " $crate: FAILED" + tail -40 "$logfile" | sed 's/^/ /' + PREFLIGHT_FAILED+=("$crate") + fi + fi + rm -f "$logfile" +done +if (( ${#PREFLIGHT_FAILED[@]} > 0 )); then + echo + echo "publish dry-run failed for: ${PREFLIGHT_FAILED[*]}" + echo "aborting before any real publishing happens." + exit 1 +fi +echo "publish dry-runs ok" +echo + +# In dry-run mode (the default), the dry-run pre-flight above is the whole +# point of the script. Stop here. +if ! (( EXECUTE )); then + echo "=== dry-run done — pass --execute to publish ===" + exit 0 +fi + # --- Helpers to look up survey results --- crate_index() { @@ -201,9 +276,9 @@ publish() { echo "--- publishing $crate $version ---" if [[ "$crate" == "toolpath-cli" ]]; then # Excluded from the workspace; publish from its own manifest. - (cd crates/toolpath-cli && cargo publish $DRY_RUN $ALLOW_DIRTY) + (cd crates/toolpath-cli && cargo publish $ALLOW_DIRTY) else - cargo publish -p "$crate" $DRY_RUN $ALLOW_DIRTY + cargo publish -p "$crate" $ALLOW_DIRTY fi echo } diff --git a/site/_data/crates.json b/site/_data/crates.json index 9f8b336..b09e4ee 100644 --- a/site/_data/crates.json +++ b/site/_data/crates.json @@ -17,7 +17,7 @@ }, { "name": "toolpath-git", - "version": "0.1.3", + "version": "0.1.4", "description": "Derive from git repository history", "docs": "https://docs.rs/toolpath-git", "crate": "https://crates.io/crates/toolpath-git", @@ -25,7 +25,7 @@ }, { "name": "toolpath-github", - "version": "0.2.0", + "version": "0.2.1", "description": "Derive from GitHub pull requests", "docs": "https://docs.rs/toolpath-github", "crate": "https://crates.io/crates/toolpath-github", @@ -73,7 +73,7 @@ }, { "name": "toolpath-dot", - "version": "0.1.2", + "version": "0.1.3", "description": "Graphviz DOT visualization", "docs": "https://docs.rs/toolpath-dot", "crate": "https://crates.io/crates/toolpath-dot", @@ -81,7 +81,7 @@ }, { "name": "toolpath-md", - "version": "0.2.0", + "version": "0.2.1", "description": "Markdown rendering for LLM consumption", "docs": "https://docs.rs/toolpath-md", "crate": "https://crates.io/crates/toolpath-md",