Skip to content

Commit 9f13b24

Browse files
authored
Merge pull request #150 from zantarix/gitlab
GitLab support
2 parents 8e268bd + 77924a0 commit 9f13b24

240 files changed

Lines changed: 23023 additions & 13721 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cargo/deny.toml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ allow = [
1313
"ISC",
1414
"Unicode-3.0",
1515
"Zlib",
16+
# CDLA-Permissive-2.0 is a Linux Foundation data license used by `webpki-root-certs`
17+
# (Mozilla's CA bundle) reached transitively via the `gitlab` crate → reqwest → rustls.
18+
# Applies to certificate data, not code.
19+
"CDLA-Permissive-2.0",
1620
]
1721

1822
# Warn on confidence level
@@ -30,8 +34,19 @@ skip = [
3034
"rand",
3135
"rand_core",
3236
"r-efi",
33-
"windows-sys",
3437
"wit-bindgen",
38+
# Duplicates pulled in transitively by the `gitlab` crate (cron 0.15, derive_builder,
39+
# graphql_client, etc.) until upstream catches up. These crates also appear via toml /
40+
# clap / proc-macro frameworks in newer versions; neither version is directly used by
41+
# cursus.
42+
"darling",
43+
"darling_core",
44+
"darling_macro",
45+
"heck",
46+
"syn",
47+
"thiserror",
48+
"thiserror-impl",
49+
"winnow",
3550
]
3651

3752
# Deny specific crates (empty by default)

.claude/CLAUDE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ Cursus is a Rust CLI tool for release management. The library is fully async (to
5656
- `changeset/` - Changeset file I/O: Hugo-style `+++` TOML frontmatter format, parsing, writing to `.cursus/`, and editor integration
5757
- `changelog/` - Changelog generation and formatting for releases
5858
- `package_manager/` - Adapter pattern (`PackageManagerAdapter` trait: `enumerate_projects`, `write_version`, `update_dependency_version`, `update_lock_file`, `publish`, `registry_name`) for Cargo and npm/yarn/pnpm workspace enumeration. Versions are returned via `ProjectInfo` from `enumerate_projects()`. Both `write_version` and `update_dependency_version` return `Result<Vec<PathBuf>>` — the files actually written to disk (or that would be written in dry-run mode); callers must extend their `modified_files` list with these paths so they are staged for git. For Cargo crates using `version.workspace = true`, `write_version` returns the workspace-root `Cargo.toml` (not the member manifest). `Project` exposes two release-readiness query methods: `is_releasable_under(&Config)` (true if the project is publishable or listed in `[git].publish_private_packages`) and `is_prepared_for_release(&dyn Filesystem)` (true if `CHANGELOG.md` exists at the project root). The `matching` submodule exposes two public functions for file-to-project attribution: `match_files_to_projects` (primitive, pass the complete project list) and `match_files_to_projects_in_scope` (preferred when `projects` is a filtered subset — takes a wider `attribution_scope` so files inside ignored sub-projects are not mis-attributed to their releasable parents; used by `change`). The `name_validation` submodule (`pub(crate)`) provides `validate_cargo_package_name` and `validate_npm_package_name`, called during `enumerate_projects` for both adapters. Any new adapter must call the appropriate validator on each manifest-sourced package name before populating `ProjectInfo.name`.
59-
- `git/` - `Git` trait abstracting all git operations; includes `head_sha()` returning the full SHA of the current HEAD commit. `GitWorkdir` is the production impl that delegates to the `git` binary via `CommandRunner`. `SignedCommitGit` is a decorator that wraps `GitWorkdir` and overrides `commit()`, `push()`, and `force_push_branch()` to route the prepare commit through the GitHub Git Data API, producing Verified commits via GitHub's web-flow GPG key (ADR-050). All alternative `Git` impls must implement `head_sha()`. The `ref_format` submodule (`pub(crate)`) provides `validate_branch_name`, `validate_tag_name`, and `validate_revision`; every `GitWorkdir` method that accepts a caller-supplied branch, tag, or revision string calls the appropriate validator before invoking the `CommandRunner`. Any new git operation that takes such a string must do the same. `diff_names` does not validate its `extra_args` slice — that is a caller contract documented on the method.
60-
- `github/` - `CodeForgeClient` trait; `OctocrabGitHubClient` is the production impl. Handles release creation, PRs, asset uploads, and pre-creation idempotency checks (`find_release_by_tag` — returns `Ok(None)` on 404, `Ok(Some(ExistingRelease { is_draft, .. }))` on hit, error otherwise; every new impl must provide this method).
59+
- `git/` - `Git` trait abstracting all git operations; includes `head_sha()` returning the full SHA of the current HEAD commit, and `path_exists_at_head(path)` returning whether a path is tracked at HEAD (used by the signed-commit decorators to classify staged paths as `create`/`update`/`delete`). `GitWorkdir` is the production impl that delegates to the `git` binary via `CommandRunner`. Two `Git` decorators route the prepare commit through a forge's web-commit API to produce Verified commits with no key custody: `GitHubSignedCommit` via the GitHub Git Data API (ADR-050) and `GitLabSignedCommit` via the GitLab commits API (ADR-058). Both override `commit()`, `push()`, and `force_push_branch()` and delegate all other methods to the inner `Git`. Selection happens at the binary boundary based on `[github].enabled` vs `[gitlab].enabled`. All alternative `Git` impls must implement every trait method including `head_sha()` and `path_exists_at_head()`. The `ref_format` submodule (`pub(crate)`) provides `validate_branch_name`, `validate_tag_name`, and `validate_revision`; every `GitWorkdir` method that accepts a caller-supplied branch, tag, or revision string calls the appropriate validator before invoking the `CommandRunner`. Any new git operation that takes such a string must do the same. `diff_names` does not validate its `extra_args` slice — that is a caller contract documented on the method.
60+
- `forge/` - `CodeForgeClient` trait (in `forge/client.rs`, re-exported from `forge`) and per-forge implementations as sibling submodules: `forge::github::OctocrabGitHubClient` (octocrab) and `forge::gitlab::ReqwestGitLabClient` (Kitware `gitlab` crate's `AsyncGitlab`). Every impl provides: `forge_name() -> &'static str` (returns the forge's user-facing label, e.g. `"GitHub"`/`"GitLab"`, used by orchestration code to compose forge-aware log lines); `find_release_by_tag` (returns `Ok(None)` on 404, `Ok(Some(ExistingRelease { is_draft, .. }))` on hit, error otherwise — `is_draft` is always `false` on GitLab); `create_release`, `upload_asset`, `publish_release` (no-op on GitLab since releases are created in final state), `create_pull_request`, `find_open_pull_request`, `update_pull_request`. The trait, parameter names, and shared types stay forge-neutral; per-forge log lines and error messages use the active forge's native vocabulary at the API boundary (e.g. the GitLab impl translates trait `head`/`base` → `source_branch`/`target_branch`).
6161
- `filesystem.rs` - `Filesystem` trait abstracting all file I/O; `LocalFilesystem` is the production impl using `tokio::fs`.
62-
- `command/` - `CommandRunner` trait with `run` (read-only) and mutating variants (`run_mut`, `run_interactive`, `run_shell_interactive`, `run_streaming`). `run_streaming` executes user-configurable shell commands (`github.build_command`, `npm.lock_command`) with inherited stdout/stderr so output appears live; stdin is null. `DryRunCommandRunner` decorator implements the ADR-017 late-guard dry-run pattern (skips mutating ops, forwards read-only); `VerboseCommandRunner` logs invocations.
63-
- `env.rs` - `Env` struct: dependency injection container holding `Arc<dyn CommandRunner>`, `Arc<dyn Filesystem>`, `Arc<dyn Git>`, `Option<Arc<dyn CodeForgeClient>>`, editor, locale, and environment flags. Builder methods compose runners (e.g. `with_dry_run_runner()`). All command execution and file I/O goes through `Env`.
62+
- `command/` - `CommandRunner` trait with `run` (read-only) and mutating variants (`run_mut`, `run_interactive`, `run_shell_interactive`, `run_streaming`). `run_streaming` executes user-configurable shell commands (`github.build_command`, `gitlab.build_command`, `npm.lock_command`) with inherited stdout/stderr so output appears live; stdin is null. `DryRunCommandRunner` decorator implements the ADR-017 late-guard dry-run pattern (skips mutating ops, forwards read-only); `VerboseCommandRunner` logs invocations.
63+
- `env.rs` - `Env` struct: dependency injection container holding `Arc<dyn CommandRunner>`, `Arc<dyn Filesystem>`, `Arc<dyn Git>`, `Result<Arc<dyn CodeForgeClient>, String>` (`Err` carries a human-readable reason why the client is unavailable, set by the binary boundary), editor, locale, and environment flags. Builder methods compose runners (e.g. `with_dry_run_runner()`). All command execution and file I/O goes through `Env`.
6464
- `conventional_commit.rs` - Parser for Conventional Commits; maps `feat`→Minor, `fix`→Patch, breaking→Major via `ConventionalCommit::change_type()`
6565
- `locale.rs` - i18n via `fluent-templates`; messages are embedded at compile time (ADR-034).
6666
- `path.rs` - `AbsolutePath` newtype wrapping validated absolute `PathBuf`

.claude/agent-memory/base-adr-architect/inventory.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,7 @@
5858
| 053 | 053-npm-package-node-spawner.md | Use a Node.js Spawner Script for the npm Package Binary Entry Point | Accepted (2026-05-02) |
5959
| 054 | 054-cargo-binstall-support.md | Add cargo-binstall Support for Prebuilt Binary Installation | Accepted (2026-05-02) |
6060
| 055 | 055-end-to-end-idempotent-publish-recovery.md | End-to-End Idempotent Publish Recovery | Accepted (2026-05-03) |
61+
| 056 | 056-gitlab-support-client-config-and-ci.md | GitLab Support — Client, Config, and CI Integration | Accepted (2026-05-13) |
62+
| 057 | 057-cursus-init-gitlab-support.md | `cursus init` GitLab Support | Accepted (2026-05-21) |
63+
| 058 | 058-verified-release-commits-on-gitlab-via-web-commits-api.md | Produce Verified Release Commits on GitLab via the Web Commits API | Accepted (2026-05-22) |
64+
| 059 | 059-forge-selection-runtime-rules.md | Forge Selection Runtime Rules | Accepted (2026-05-13) |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
+++
2+
cursus = "minor"
3+
cursus-bin = "minor"
4+
+++
5+
6+
Adds GitLab verified release commits. When running on GitLab CI ≥18.10 with a token, the prepare commit is routed through the GitLab commits API and appears as **Verified** in the GitLab UI — no signing key custody required. Reuses the existing `[git].signed_commits` config knob (`"auto"`, `"force"`, `"off"`); `"auto"` engages whenever `GITLAB_CI=true` and a token is present. See the [GitLab CI guide](https://zantarix.github.io/cursus/guides/ci-integration/gitlab/#verified-commits) for setup.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
+++
2+
cursus = "patch"
3+
cursus-bin = "patch"
4+
+++
5+
6+
update rust crate octocrab to v0.51.0
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
+++
2+
cursus = "minor"
3+
cursus-bin = "minor"
4+
+++
5+
6+
Harden GitLab forge support. Self-managed instances served over plain HTTP are now reachable end-to-end: the API client and the asset URLs surfaced in release notes both honour the scheme from `CI_API_V4_URL` or `[gitlab].host`. The release-asset host is also pinned to the same endpoint the API client used, so a stale or mirrored git remote can no longer cause asset links to point at the wrong instance. GitLab API errors run through credential redaction before being logged, matching the protection already in place for the signed-commit decorator.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
+++
2+
cursus = "minor"
3+
cursus-bin = "minor"
4+
+++
5+
6+
Cursus now rejects configurations with more than one forge section enabled at load time. Setting both `[github].enabled = true` and `[gitlab].enabled = true` in `.cursus/config.toml` produces a hard error that names the offending flags and explains the fix. Configs with a single enabled forge — or no enabled forge — continue to work as before.

.cursus/piteously-one-glider.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
+++
2+
"@zantarix/cursus" = "minor"
3+
cursus = "minor"
4+
cursus-bin = "minor"
5+
+++
6+
7+
Add GitLab support to `cursus init`. The wizard now prompts you to pick GitHub, GitLab, or Neither as your forge, with a dedicated GitLab editor screen that auto-detects `group/project` from your git origin and surfaces a self-managed host field for non-gitlab.com instances. The generated `.cursus/config.toml` writes the chosen forge as `enabled = true` and emits the other forge as a commented-out template, so switching forges later is a hand-edit away. The config also reorders active sections to the top of the file so your live configuration is visible without scrolling as the schema grows.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
+++
2+
cursus = "minor"
3+
cursus-bin = "minor"
4+
+++
5+
6+
Add first-class GitLab support: [gitlab] config section, ReqwestGitLabClient implementing CodeForgeClient via the Kitware gitlab crate, GitLab CI token detection at the binary boundary, and a forge-neutral crate::forge module layout (relocates crate::github to crate::forge::github)

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ packages/npm/*.tgz
1919

2020
# Just Claude things
2121
/.claude/worktrees
22+
/.reviews

0 commit comments

Comments
 (0)