Skip to content
Open
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
11 changes: 6 additions & 5 deletions .github/workflows/CICD.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,19 @@ Trigger: pull_request to develop or master

## Merge to develop — pre-release (cd.yml)

Trigger: push to develop | Concurrency: cancel-in-progress
Trigger: push to develop | workflow_dispatch (not master) | Concurrency: cancel-in-progress

```
┌──────────────────┐
│ push to develop │
│ OR dispatch │
└────────┬─────────┘
┌────────▼──────────────────┐
│ pre-release │
read Cargo.toml version │
tag = v{ver}-rc.{run}
safety: fail if exists
compute next version
from conventional commits
tag = v{next}-rc.{run}
└────────┬──────────────────┘
┌────────▼──────────────────┐
Expand All @@ -74,7 +75,7 @@ Trigger: push to develop | Concurrency: cancel-in-progress

## Merge to master — stable release (cd.yml)

Trigger: push to master | Concurrency: never cancelled
Trigger: push to master (only) | Concurrency: never cancelled

```
┌──────────────────┐
Expand Down
57 changes: 48 additions & 9 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: CD

on:
workflow_dispatch:
push:
branches: [develop, master]

Expand All @@ -18,7 +19,9 @@ jobs:
# ═══════════════════════════════════════════════

pre-release:
if: github.ref == 'refs/heads/develop'
if: >-
github.ref == 'refs/heads/develop'
|| (github.event_name == 'workflow_dispatch' && github.ref != 'refs/heads/master')
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.tag.outputs.tag }}
Expand All @@ -27,16 +30,53 @@ jobs:
with:
fetch-depth: 0

- name: Compute pre-release tag
- name: Compute version from commits like release please
id: tag
run: |
VERSION=$(grep '^version = ' Cargo.toml | head -1 | cut -d'"' -f2)
TAG="v${VERSION}-rc.${{ github.run_number }}"
# ── Find latest stable tag reachable from HEAD ──
LATEST_TAG=""
for t in $(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | grep -v '-'); do
if git merge-base --is-ancestor "$t" HEAD 2>/dev/null; then
LATEST_TAG="$t"; break
fi
done
if [ -z "$LATEST_TAG" ]; then
echo "::error::No stable release tag found in branch history"
exit 1
fi
LATEST_VERSION="${LATEST_TAG#v}"
echo "Latest ancestor release: $LATEST_TAG"

# ── Analyse conventional commits since that tag ──
COMMITS=$(git log "${LATEST_TAG}..HEAD" --format="%s")
HAS_BREAKING=$(echo "$COMMITS" | grep -cE '^[a-z]+(\(.+\))?!:' || true)
HAS_FEAT=$(echo "$COMMITS" | grep -cE '^feat(\(.+\))?:' || true)
HAS_FIX=$(echo "$COMMITS" | grep -cE '^fix(\(.+\))?:' || true)
echo "Commits since ${LATEST_TAG} — breaking=$HAS_BREAKING feat=$HAS_FEAT fix=$HAS_FIX"

# Safety: warn if this base version is already released
if git ls-remote --tags origin "refs/tags/v${VERSION}" | grep -q .; then
echo "::warning::v${VERSION} already released. Consider bumping Cargo.toml on develop."
# ── Compute next version (matches release-please observed behaviour) ──
# Pre-1.0 with bump-minor-pre-major: breaking → minor, feat → minor, fix → patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$LATEST_VERSION"
if [ "$MAJOR" -eq 0 ]; then
if [ "$HAS_BREAKING" -gt 0 ] || [ "$HAS_FEAT" -gt 0 ]; then
MINOR=$((MINOR + 1)); PATCH=0 # breaking or feat → minor
else
PATCH=$((PATCH + 1)) # fix only → patch
fi
else
if [ "$HAS_BREAKING" -gt 0 ]; then
MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 # breaking → major
elif [ "$HAS_FEAT" -gt 0 ]; then
MINOR=$((MINOR + 1)); PATCH=0 # feat → minor
else
PATCH=$((PATCH + 1)) # fix → patch
fi
fi
VERSION="${MAJOR}.${MINOR}.${PATCH}"
TAG="v${VERSION}-rc.${{ github.run_number }}"

echo "Next version: $VERSION (from $LATEST_VERSION)"
echo "Pre-release tag: $TAG"

# Safety: fail if this exact tag already exists
if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then
Expand All @@ -45,7 +85,6 @@ jobs:
fi

echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "Pre-release tag: $TAG"

build-prerelease:
name: Build pre-release
Expand All @@ -64,7 +103,7 @@ jobs:
# ═══════════════════════════════════════════════

release-please:
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Bug Fixes

* **ruby:** use `rails test` instead of `rake test` when positional file args are passed — `rake test` ignores positional files and only supports `TEST=path`

### Features

* **ruby:** add RSpec test runner filter with JSON parsing and text fallback (60%+ reduction)
Expand All @@ -16,6 +20,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* **ruby:** add `ruby_exec()` shared utility for auto-detecting `bundle exec` when Gemfile exists
* **ruby:** add discover/rewrite rules for rake, rails, rspec, rubocop, and bundle commands

### Bug Fixes

* **cargo:** preserve compile diagnostics when `cargo test` fails before any test suites run
* **npx:** auto-approve installable `npx` fallback commands to avoid first-run approval hangs

## [0.30.1](https://github.com/rtk-ai/rtk/compare/v0.30.0...v0.30.1) (2026-03-18)


Expand Down
35 changes: 35 additions & 0 deletions src/cargo_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,18 @@ fn filter_cargo_test(output: &str) -> String {
}

if result.trim().is_empty() {
let has_compile_errors = output.lines().any(|line| {
let trimmed = line.trim_start();
trimmed.starts_with("error[") || trimmed.starts_with("error:")
});

if has_compile_errors {
let build_filtered = filter_cargo_build(output);
if build_filtered.starts_with("cargo build:") {
return build_filtered.replacen("cargo build:", "cargo test:", 1);
}
}

// Fallback: show last meaningful lines
let meaningful: Vec<&str> = output
.lines()
Expand Down Expand Up @@ -1314,6 +1326,29 @@ test result: MALFORMED LINE WITHOUT PROPER FORMAT
);
}

#[test]
fn test_filter_cargo_test_compile_error_preserves_error_header() {
let output = r#" Compiling rtk v0.31.0 (/workspace/projects/rtk)
error[E0425]: cannot find value `missing_symbol` in this scope
--> tests/repro_compile_fail.rs:3:13
|
3 | let _ = missing_symbol;
| ^^^^^^^^^^^^^^ not found in this scope

For more information about this error, try `rustc --explain E0425`.
error: could not compile `rtk` (test "repro_compile_fail") due to 1 previous error
"#;
let result = filter_cargo_test(output);
assert!(result.contains("cargo test: 1 errors, 0 warnings (1 crates)"));
assert!(result.contains("error[E0425]"), "got: {}", result);
assert!(
result.contains("--> tests/repro_compile_fail.rs:3:13"),
"got: {}",
result
);
assert!(!result.starts_with('|'), "got: {}", result);
}

#[test]
fn test_filter_cargo_clippy_clean() {
let output = r#" Checking rtk v0.5.0
Expand Down
6 changes: 3 additions & 3 deletions src/ccusage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! Claude Code API usage metrics. Handles subprocess execution, JSON parsing,
//! and graceful degradation when ccusage is unavailable.

use crate::utils::{resolved_command, tool_exists};
use crate::utils::{npx_command, resolved_command, tool_exists};
use anyhow::{Context, Result};
use serde::Deserialize;
use std::process::Command;
Expand Down Expand Up @@ -95,15 +95,15 @@ fn build_command() -> Option<Command> {
}

// Fallback: try npx
let npx_check = resolved_command("npx")
let npx_check = npx_command()
.arg("ccusage")
.arg("--help")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status();

if npx_check.map(|s| s.success()).unwrap_or(false) {
let mut cmd = resolved_command("npx");
let mut cmd = npx_command();
cmd.arg("ccusage");
return Some(cmd);
}
Expand Down
Loading
Loading