Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9b718bc

Browse files
geoHeilclaude
andauthored
feat: add Gitea forge implementation (3/3) (cesarferreira#134)
* feat: add Gitea forge implementation Add GiteaClient implementing the ForgeClient interface for Gitea pull request operations. Supports creating, updating, merging PRs, stack comment management via issue comments, and CI status checks. Key additions: - src/forge/gitea.rs with full Gitea API v1 client - AuthorizationToken auth style and patch_json helper in forge/mod.rs - Integration tests for submit (comment/body/both/off modes) and merge/merge-when-ready with PR retargeting Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: is_pr_merged returns true only for actually merged PRs, add pagination limits - Fix is_pr_merged() to check only the `merged` field instead of also treating closed PRs as merged (a closed PR may have been rejected) - Add limit=50 to all Gitea list API calls (pulls, comments, statuses) to avoid silent truncation at Gitea's default page size of 30 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: normalize Gitea check conclusion values for consistency with GitLab Map raw Gitea status strings to normalized conclusions: success->success, failure/error->failure. This matches the GitLab normalization pattern so downstream consumers get consistent values. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: remove spurious ?limit=50 from create_stack_comment POST URL Pagination parameters only apply to GET requests. The limit=50 was accidentally added to the POST endpoint by a bulk replace. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: CI status priority, forge-generic messages, and silent no-op warnings - Fix aggregate CI overall status: failure now takes priority over pending. Previously find_map short-circuited on the first non-success status, so [pending, failed] would report "pending" instead of "failure". Both GitLab and Gitea clients affected. - Replace GitHub-specific auth check in ci.rs with forge_token() so `stax ci` works on GitLab/Gitea repos. - Replace hardcoded "GitHub" in user-facing error messages (submit.rs, ci.rs) with forge-generic wording. - Rename API stats labels from "github.api.*" to "forge.api.*". - Add eprintln warnings when reviewers/labels/assignees are requested on forges that don't support them (were silent no-ops). - Add Display impl for ForgeType. - Add unit tests for aggregate_ci_overall priority logic. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: Gitea MergeTitleField serde rename, detect Forgejo as Gitea - Fix MergePullRequest serialization: Gitea API expects PascalCase "MergeTitleField" but we were sending snake_case "merge_title_field", causing squash merge commit titles to be silently ignored. - Recognize Forgejo (popular Gitea fork) in forge detection so self-hosted Forgejo instances use the Gitea client automatically. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: add missing #[tokio::test] and normalize Gitea merged-PR state Two bugs: 1. test_submit_gitea_comment_mode_creates_pull_and_issue_comment was missing the #[tokio::test] attribute, so the test runner never executed it (same class of bug as the GitLab one). 2. Gitea's API returns state="closed" for merged PRs (with a separate merged=true field), so pr.state.to_uppercase() produced "CLOSED" instead of "MERGED". This broke sync's metadata-based merge detection (sync.rs:1133 checks for state "MERGED"). Added normalize_gitea_state() that checks the merged field first, mirroring GitLab's normalize_gitlab_state() approach. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
1 parent fd43ee5 commit 9b718bc

6 files changed

Lines changed: 1432 additions & 29 deletions

File tree

src/commands/ci.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -254,17 +254,21 @@ pub fn run(
254254
return Ok(());
255255
}
256256

257-
// Check for GitHub token
258-
if Config::github_token().is_none() {
257+
let Some(remote) = remote_info else {
258+
anyhow::bail!("Could not determine remote info. Check that a git remote is configured.");
259+
};
260+
261+
if crate::forge::forge_token(remote.forge).is_none() {
259262
anyhow::bail!(
260-
"GitHub auth not configured. Use one of: `stax auth`, `stax auth --from-gh`, `gh auth login`, or set `STAX_GITHUB_TOKEN`."
263+
"{} auth not configured.\n\
264+
Set the appropriate token for your forge:\n \
265+
- GitHub: `stax auth`, `stax auth --from-gh`, or set `STAX_GITHUB_TOKEN`\n \
266+
- GitLab: set `STAX_GITLAB_TOKEN` or `GITLAB_TOKEN`\n \
267+
- Gitea: set `STAX_GITEA_TOKEN` or `GITEA_TOKEN`",
268+
remote.forge
261269
);
262270
}
263271

264-
let Some(remote) = remote_info else {
265-
anyhow::bail!("Could not determine GitHub remote info.");
266-
};
267-
268272
let rt = tokio::runtime::Runtime::new()?;
269273
let _enter = rt.enter();
270274

src/commands/submit.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ pub fn run(
226226
"Base branch '{}' does not exist on the remote.\n\n\
227227
This can happen if:\n \
228228
- This is a new repository that hasn't been pushed yet\n \
229-
- The default branch has a different name on GitHub\n\n\
229+
- The default branch has a different name on the remote\n\n\
230230
To fix this, push your base branch first:\n \
231231
git push -u {} {}",
232232
stack.trunk,
@@ -956,9 +956,9 @@ pub fn run(
956956
.context(format!(
957957
"Failed to create PR for '{}' with base '{}'\n\
958958
This may happen if:\n \
959-
- The base branch '{}' doesn't exist on GitHub\n \
959+
- The base branch '{}' doesn't exist on the remote\n \
960960
- The branch has no commits different from base\n \
961-
- GitHub API request timed out (check network/VPN and retry)\n \
961+
- API request timed out (check network/VPN and retry)\n \
962962
Try: git log {}..{} to see the commits",
963963
plan.branch, plan.parent, plan.parent, plan.parent, plan.branch
964964
))?;
@@ -1442,19 +1442,19 @@ fn print_verbose_network_summary(
14421442
if let Some(stats) = client.and_then(|client| client.api_call_stats()) {
14431443
println!(
14441444
" {:<28} {}",
1445-
"github.api.total",
1445+
"forge.api.total",
14461446
stats.total_requests.to_string().cyan()
14471447
);
14481448
if stats.by_operation.is_empty() {
1449-
println!(" {}", "No GitHub API requests recorded".dimmed());
1449+
println!(" {}", "No forge API requests recorded".dimmed());
14501450
} else {
14511451
for (operation, count) in stats.by_operation {
14521452
println!(" {:<28} {}", operation, count);
14531453
}
14541454
}
14551455
} else {
1456-
println!(" {:<28} {}", "github.api.total", "0".cyan());
1457-
println!(" {}", "No GitHub client available".dimmed());
1456+
println!(" {:<28} {}", "forge.api.total", "0".cyan());
1457+
println!(" {}", "No API stats available".dimmed());
14581458
}
14591459

14601460
println!();

0 commit comments

Comments
 (0)