Skip to content

fix(ci): make forge workflows run outside this repo and on GitLab - #56

Merged
mrwogu merged 5 commits into
mainfrom
fix/gitlab-forge-job
Sep 24, 2026
Merged

mrwogu merged 5 commits into
mainfrom
fix/gitlab-forge-job

Conversation

@mrwogu

@mrwogu mrwogu commented Sep 24, 2026

Copy link
Copy Markdown
Member

Summary

I ran the forge job end to end, the way a consumer project uses it, and it failed in three places, two of them GitLab only. Each fix is its own commit, so it's easier to review one by one.

#50. Both templates ran go run ./cmd/git-byline, which works only inside this repo. In any other project the job failed on this step. Now the workflow downloads the release pinned on the GIT_BYLINE_VERSION line, checks the archive against checksums.txt and the binary version, same like action.yml and install.sh, and runs that binary. Release Please bumps the pin in both templates, and validate fails when the pin and the manifest drift. The GitLab job runs in buildpack-deps:trixie-scm now, it does not need Go.

#51. The GitLab job put GITLAB_TOKEN into http.extraHeader as PRIVATE-TOKEN. This header is for REST calls, Git over HTTPS takes basic auth, so the first authenticated fetch or the notes push got 401. Now it's basic auth with user oauth2 and the token as password, same idea like #43 for GitHub.

#52. After a squash merge the MR commits, and their notes, stay only on refs/merge-requests/<iid>/head. The job took the source from commit parents, so the lines stayed untracked. Now it reads the <project path>!<iid> reference from the merged commit message, fetches the MR head and uses it as source, but only when its diff has the same stable patch ID as the diff the target commit brings. Otherwise it keeps the parents like before and prints a warning. The default GitLab squash template is only the title, so with the fast-forward merge method the project has to add %{reference} there. README says it now.

We squash merge, so the block below keeps three changelog entries instead of one:

BEGIN_COMMIT_OVERRIDE
fix(ci): install pinned release binary in forge workflows (#50)

fix(ci): use HTTP basic auth in GitLab forge job (#51)
fix(ci): reconstruct GitLab squash merges from merge request head (#52)
END_COMMIT_OVERRIDE

Scope

  • User-visible behavior: the forge workflow runs in any repo, not only here. On GitLab it can fetch and push notes, and squash merges keep agent attribution when the merged commit message has the MR reference.
  • Interfaces or compatibility: no breaking change. A workflow file that differs from the template only in the pinned tag still counts as managed, so after an upgrade install-hooks does not warn and uninstall still removes it. GIT_BYLINE_VERSION and GIT_BYLINE_RELEASES_URL can be set as CI/CD variables, for example for an internal mirror. The GitHub workflow triggers on the default branch instead of hardcoded main.
  • Documentation: README forge section (pin, GitLab token, %{reference} in the squash template), docs/SECURITY_MODEL.md, docs/SUPPLY_CHAIN.md, docs/ARCHITECTURE.md.
  • Generated artifacts: .github/workflows/git-byline.yml and .gitlab/ci/git-byline.yml are byte for byte the embedded templates. release-please-config.json bumps the pin in both templates.
  • Security and privacy: the token is only in the fetch and push steps, as basic auth header now. The downloaded binary is checked against checksums.txt and its version before it runs. The MR head is used only when the patch ID matches, so a wrong or crafted reference in a commit message cannot bring attribution from another MR.

Related issue

Fixes #50
Fixes #51
Fixes #52

Validation

go run ./tools/validate
  validate: all 10 stages passed

GitLab job script in buildpack-deps:trixie-scm (podman), bare remote with
refs/merge-requests/<iid>/head, run with sh and with bash (same output):
  B   merge commit, no squash                          ai lines kept
  C   squash + merge commit, two MR commits            ai lines kept
  C2  squash + fast-forward, template with reference   ai lines kept
  C3  squash + fast-forward, default template          untracked (no reference, expected)
  D   fast-forward, no squash                          ai lines kept
  E   squash + merge commit after main moved           ai lines kept
  F   merge message names the wrong MR                 untracked + warning (patch ID differs)

Auth header in the runner image: Git sends it on the first request,
it decodes to oauth2:<token>.

Not run against live GitLab yet, I will verify that in the pilot.

Checklist

  • Focused change with unrelated refactors excluded
  • Tests added or updated
  • Documentation updated when behavior changed
  • Generated artifacts regenerated and reviewed
  • PromptScript sources updated instead of generated files
  • No secrets, private data, or unsafe fixtures included
  • Security impact considered
  • Breaking changes documented
  • Release impact understood

Both templates ran go run ./cmd/git-byline, which works only in this
repo. In any other project the reconstruct job failed on this step.

Now the workflow downloads the release pinned on the GIT_BYLINE_VERSION
line, checks the archive against checksums.txt and the binary version,
same like action.yml and install.sh, and runs that binary. Release
Please bumps the pin in both templates, and validate fails when the pin
and the manifest drift.

A workflow that differs from the template only in the pinned tag still
counts as managed, so an upgrade does not make install-hooks warn or
uninstall skip the file.

The GitLab job runs in buildpack-deps:trixie-scm now, it does not need
Go anymore. GIT_BYLINE_VERSION and GIT_BYLINE_RELEASES_URL can be set
as CI/CD variables, for example for an internal mirror. The GitHub
workflow triggers on the default branch instead of hardcoded main.

Fixes #50
The job put GITLAB_TOKEN into http.extraHeader as PRIVATE-TOKEN. This
header is for REST calls, Git over HTTPS takes HTTP basic auth. With
credential.helper empty Git had no other way to log in, so the first
authenticated fetch or the notes push got 401.

Now the header is basic auth with user oauth2 and the token as
password, same idea like #43 for GitHub. I checked in the runner image
that Git sends it on the first request and that it decodes to
oauth2:<token>. A run against live GitLab is still to verify in the
pilot.

A missing GITLAB_TOKEN also fails with a message now, not a bare test.

Fixes #51
After a squash merge the MR commits, and their notes, stay only on
refs/merge-requests/<iid>/head. The job took the source from commit
parents, so a squash with fast-forward had an empty source range and a
squash with merge commit pointed at the squash commit, which has no
notes. In both cases the lines stayed untracked.

Now the job reads the <project path>!<iid> reference from the merged
commit message, fetches the MR head and uses it as the source, but only
when its diff has the same stable patch ID as the diff the target
commit brings. Without the reference it keeps the parents like before.
When the diff differs it keeps them too and prints a warning.

The default merge commit template has the reference. The default
squash template is only the title, so with the fast-forward merge
method the project has to add %{reference} there, README says it now.

I checked seven merge setups in the runner image with sh and bash.
Live GitLab is still to verify in the pilot.

Fixes #52
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Warning

Review limit reached

Next included review available in 20 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: comarch/git-byline/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: cf9ec631-fc49-490b-b752-7d3b6962c046

📥 Commits

Reviewing files that changed from the base of the PR and between 02cd686 and 009cd99.

📒 Files selected for processing (7)
  • .gitlab/ci/git-byline.yml
  • docs/SECURITY_MODEL.md
  • internal/ci/ci_test.go
  • internal/ci/templates/gitlab.yml
  • tools/validate/checks.go
  • tools/validate/checks_test.go
  • tools/validate/scan_more_test.go

Summary by CodeRabbit

  • New Features

    • GitHub and GitLab attribution workflows now use verified, version-pinned releases and run only for eligible merges to the default branch.
    • GitLab workflows can identify squash-merged changes when the merge request’s patch matches the target commit.
    • Uninstall recognizes generated workflows even when their pinned release version differs from the template.
  • Documentation

    • Added guidance on release pinning, GitLab token setup and squash-merge behavior, and workflow security.

Walkthrough

GitHub and GitLab forge workflows now install checksum-verified, version-pinned release binaries. The GitLab workflow also checks referenced merge-request heads against merged diffs. Workflow installation and validation now account for release pins.

Changes

Forge workflow updates

Layer / File(s) Summary
Managed workflow release pins
internal/ci/ci.go, internal/ci/ci_test.go, internal/ci/forge_detect_test.go, release-please-config.json, tools/validate/*, docs/ARCHITECTURE.md, docs/SECURITY_MODEL.md, docs/SUPPLY_CHAIN.md
Install and uninstall recognize generated workflows when only the release tag differs. Validation checks both template pins against the release manifest.
Verified release binary workflows
.github/workflows/git-byline.yml, .gitlab/ci/git-byline.yml, internal/ci/templates/*, internal/ci/ci_test.go, README.md, docs/SECURITY_MODEL.md
Both providers download and verify pinned release binaries instead of building from source. GitHub reconstruction is limited to merged pull requests targeting the default branch. GitLab uses Basic authentication and checks for a configured token.
GitLab merge-request source selection
.gitlab/ci/git-byline.yml, internal/ci/templates/gitlab.yml, docs/SECURITY_MODEL.md
The GitLab workflow uses a referenced merge-request head only when its stable patch ID matches the merged diff. Otherwise, it retains the parent-derived source and warns.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: 🔵 Low · up to 02cd6

Failed GitLab jobs can leave downloaded files in their build directory. Add failure-path cleanup; the remaining risk is bounded and does not otherwise block merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 6 files. (9 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main CI changes: forge workflows run in consumer repositories and support GitLab.
Description check ✅ Passed The description directly explains the workflow fixes, authentication change, squash-merge handling, validation, documentation, and test coverage.
Linked Issues check ✅ Passed #50: Both templates replace go run with a pinned release download. They validate the version, runner architecture, SHA-256 checksum, and binary version before execution. Tests cover the template con…
Out of Scope Changes check ✅ Passed The workflow trigger restriction, release-pin management, validation checks, supply-chain documentation, security documentation, and tests directly support #50, #51, or #52. No unrelated change is est…
Full details: Docstring Coverage

Explanation

Docstring coverage is 48.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 6 files. (9 skipped: 9 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: comarch/git-byline/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: cb5b7b60-c3ab-4967-aadf-0b7fa828ee10

📥 Commits

Reviewing files that changed from the base of the PR and between 8b69bbe and 02cd686.

📒 Files selected for processing (15)
  • .github/workflows/git-byline.yml
  • .gitlab/ci/git-byline.yml
  • README.md
  • docs/ARCHITECTURE.md
  • docs/SECURITY_MODEL.md
  • docs/SUPPLY_CHAIN.md
  • internal/ci/ci.go
  • internal/ci/ci_test.go
  • internal/ci/forge_detect_test.go
  • internal/ci/templates/github.yml
  • internal/ci/templates/gitlab.yml
  • release-please-config.json
  • tools/validate/checks.go
  • tools/validate/checks_test.go
  • tools/validate/scan.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread internal/ci/templates/gitlab.yml
checkCITemplateVersions had a provider lookup that can not fail for the
two fixed templates, so its error return was never hit. Read the two
template files directly. Also test the checkScans branch when the pin
does not match the release manifest.
The job runs under set -eu, so a failed checksum, version check, tar, or
ci run exits before the rm and the archive stays in CI_BUILDS_DIR. An
EXIT trap removes it on every path.
@sonarqubecloud

Copy link
Copy Markdown

@mrwogu
mrwogu merged commit 88324a5 into main Sep 24, 2026
21 checks passed
@mrwogu
mrwogu deleted the fix/gitlab-forge-job branch September 24, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant