Bug
`src/git/repo.rs:20-47` (`resolve_current_branch`) falls back to `GITHUB_REF_NAME` / `CI_COMMIT_BRANCH` etc. when `head.referent_name()` is `None`.
On a detached HEAD in CI — typical pattern when `actions/checkout` is used with a ref pointing at a tag, or after `ferrflow release` itself moves HEAD to the tagged commit — `GITHUB_REF_NAME` contains the tag name (`v1.2.3`), not a branch name. That value then flows through:
- `monorepo/run/mod.rs:103-107` → `target_branch` used by `push_branch` / `reset_branch_to_remote`
- `git/push.rs::verify_remote_branch` looking up `refs/heads/v1.2.3` (which doesn't exist) → spurious GIT_REMOTE_BRANCH_NOT_FOUND
- `git/push.rs::fetch_and_rebase` trying to fetch `refs/heads/v1.2.3:refs/remotes/origin/v1.2.3` → no-op or weird state
Fix
Only use `GITHUB_REF_NAME` when `GITHUB_REF` starts with `refs/heads/`. Same idea for the other CI vars: prefer a positive signal of branch context. If detached and no branch env is reliable, fall through to `fallback` (the configured workspace branch from `.ferrflow`).
```rust
if let Ok(full) = std::env::var("GITHUB_REF") {
if let Some(branch) = full.strip_prefix("refs/heads/") {
return branch.to_string();
}
}
```
Repro
```
git checkout --detach HEAD
GITHUB_REF=refs/tags/v1.2.3 GITHUB_REF_NAME=v1.2.3 ferrflow release --dry-run
observes: target_branch = "v1.2.3" instead of "main"
```
Test
Add a unit case in `src/git/tests.rs` (currently `resolve_branch_detached_returns_non_empty` only asserts non-empty — tighten to assert it's the configured fallback when `GITHUB_REF` doesn't point at a branch).
Bug
`src/git/repo.rs:20-47` (`resolve_current_branch`) falls back to `GITHUB_REF_NAME` / `CI_COMMIT_BRANCH` etc. when `head.referent_name()` is `None`.
On a detached HEAD in CI — typical pattern when `actions/checkout` is used with a ref pointing at a tag, or after `ferrflow release` itself moves HEAD to the tagged commit — `GITHUB_REF_NAME` contains the tag name (`v1.2.3`), not a branch name. That value then flows through:
Fix
Only use `GITHUB_REF_NAME` when `GITHUB_REF` starts with `refs/heads/`. Same idea for the other CI vars: prefer a positive signal of branch context. If detached and no branch env is reliable, fall through to `fallback` (the configured workspace branch from `.ferrflow`).
```rust
if let Ok(full) = std::env::var("GITHUB_REF") {
if let Some(branch) = full.strip_prefix("refs/heads/") {
return branch.to_string();
}
}
```
Repro
```
git checkout --detach HEAD
GITHUB_REF=refs/tags/v1.2.3 GITHUB_REF_NAME=v1.2.3 ferrflow release --dry-run
observes: target_branch = "v1.2.3" instead of "main"
```
Test
Add a unit case in `src/git/tests.rs` (currently `resolve_branch_detached_returns_non_empty` only asserts non-empty — tighten to assert it's the configured fallback when `GITHUB_REF` doesn't point at a branch).