Skip to content

Commit 9e322e2

Browse files
fix: patrick's 3 issues (latest tag, ccusage fallback, versioning)
1. CRITICAL: Fix 'latest' tag creation after releases - Move update-latest-tag job from release.yml to release-please.yml - release-please creates tags via API (no push event) → must run in same workflow - Job now conditional on release_created output 2. IMPORTANT: Add npx fallback for ccusage + improve message - Check binary in PATH first, fallback to 'npx ccusage' - Updated message: "npm i -g ccusage (or use npx ccusage)" - Consistent with other JS tooling (next_cmd, tsc_cmd, prettier_cmd) 3. PROCESS: Slow down version bumps with release-please config - Add release-please-config.json with bump-patch-for-minor-pre-major - In 0.x versions: feat: → patch bump instead of minor - Prevents rapid version inflation (0.3.1 → 0.5.0 in 21h) Fixes issues raised by Patrick after PR #21 merge. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 62f9055 commit 9e322e2

5 files changed

Lines changed: 72 additions & 33 deletions

File tree

.github/workflows/release-please.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,29 @@ permissions:
1212
jobs:
1313
release-please:
1414
runs-on: ubuntu-latest
15+
outputs:
16+
release_created: ${{ steps.release.outputs.release_created }}
17+
tag_name: ${{ steps.release.outputs.tag_name }}
1518
steps:
1619
- uses: googleapis/release-please-action@v4
20+
id: release
1721
with:
1822
release-type: rust
1923
package-name: rtk
24+
25+
update-latest-tag:
26+
name: Update 'latest' tag
27+
needs: release-please
28+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
29+
runs-on: ubuntu-latest
30+
steps:
31+
- uses: actions/checkout@v4
32+
with:
33+
fetch-depth: 0
34+
35+
- name: Update latest tag
36+
run: |
37+
git config user.name "github-actions[bot]"
38+
git config user.email "github-actions[bot]@users.noreply.github.com"
39+
git tag -fa latest -m "Latest stable release (${{ needs.release-please.outputs.tag_name }})"
40+
git push origin latest --force

.github/workflows/release.yml

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -180,32 +180,6 @@ jobs:
180180
env:
181181
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
182182

183-
update-latest-tag:
184-
name: Update 'latest' tag
185-
needs: release
186-
runs-on: ubuntu-latest
187-
steps:
188-
- name: Checkout
189-
uses: actions/checkout@v4
190-
with:
191-
fetch-depth: 0
192-
193-
- name: Get version
194-
id: version
195-
run: |
196-
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
197-
echo "version=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
198-
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
199-
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
200-
fi
201-
202-
- name: Update latest tag
203-
run: |
204-
git config user.name "github-actions[bot]"
205-
git config user.email "github-actions[bot]@users.noreply.github.com"
206-
git tag -fa latest -m "Latest stable release (${{ steps.version.outputs.version }})"
207-
git push origin latest --force
208-
209183
# TODO: Enable when HOMEBREW_TAP_TOKEN is configured
210184
# homebrew:
211185
# name: Update Homebrew Tap

.release-please-manifest.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
".": "0.5.0"
3+
}

release-please-config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"packages": {
3+
".": {
4+
"release-type": "rust",
5+
"package-name": "rtk",
6+
"bump-minor-pre-major": true,
7+
"bump-patch-for-minor-pre-major": true
8+
}
9+
}
10+
}

src/ccusage.rs

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,33 +82,64 @@ struct MonthlyEntry {
8282

8383
// ── Public API ──
8484

85-
/// Check if ccusage CLI is available in PATH
86-
pub fn is_available() -> bool {
85+
/// Check if ccusage binary exists in PATH
86+
fn binary_exists() -> bool {
8787
Command::new("which")
8888
.arg("ccusage")
8989
.output()
9090
.map(|o| o.status.success())
9191
.unwrap_or(false)
9292
}
9393

94+
/// Build the ccusage command, falling back to npx if binary not in PATH
95+
fn build_command() -> Option<Command> {
96+
if binary_exists() {
97+
return Some(Command::new("ccusage"));
98+
}
99+
100+
// Fallback: try npx
101+
let npx_check = Command::new("npx")
102+
.arg("ccusage")
103+
.arg("--help")
104+
.stdout(std::process::Stdio::null())
105+
.stderr(std::process::Stdio::null())
106+
.status();
107+
108+
if npx_check.map(|s| s.success()).unwrap_or(false) {
109+
let mut cmd = Command::new("npx");
110+
cmd.arg("ccusage");
111+
return Some(cmd);
112+
}
113+
114+
None
115+
}
116+
117+
/// Check if ccusage CLI is available (binary or via npx)
118+
pub fn is_available() -> bool {
119+
build_command().is_some()
120+
}
121+
94122
/// Fetch usage data from ccusage for the last 90 days
95123
///
96124
/// Returns `Ok(None)` if ccusage is unavailable (graceful degradation)
97125
/// Returns `Ok(Some(vec))` with parsed data on success
98126
/// Returns `Err` only on unexpected failures (JSON parse, etc.)
99127
pub fn fetch(granularity: Granularity) -> Result<Option<Vec<CcusagePeriod>>> {
100-
if !is_available() {
101-
eprintln!("⚠️ ccusage not found. Install: npm i -g ccusage");
102-
return Ok(None);
103-
}
128+
let mut cmd = match build_command() {
129+
Some(cmd) => cmd,
130+
None => {
131+
eprintln!("⚠️ ccusage not found. Install: npm i -g ccusage (or use npx ccusage)");
132+
return Ok(None);
133+
}
134+
};
104135

105136
let subcommand = match granularity {
106137
Granularity::Daily => "daily",
107138
Granularity::Weekly => "weekly",
108139
Granularity::Monthly => "monthly",
109140
};
110141

111-
let output = Command::new("ccusage")
142+
let output = cmd
112143
.arg(subcommand)
113144
.arg("--json")
114145
.arg("--since")

0 commit comments

Comments
 (0)