Skip to content

fix(gitlab): handle branch creation without commits - #2875

Merged
chmouel merged 1 commit into
tektoncd:mainfrom
l-qing:fix/gitlab-branch-creation-without-commits
Jul 31, 2026
Merged

fix(gitlab): handle branch creation without commits#2875
chmouel merged 1 commit into
tektoncd:mainfrom
l-qing:fix/gitlab-branch-creation-without-commits

Conversation

@l-qing

@l-qing l-qing commented Jul 22, 2026

Copy link
Copy Markdown
Member

📝 Description of the Change

GitLab can send a Push Hook with an empty commits array when a new branch is
created at an existing commit. Pipelines as Code currently rejects every such
payload with no commits attached to this push event, so the new branch never
triggers a PipelineRun.

This change:

  • Accepts only valid branch-creation payloads: a non-empty refs/heads/<branch>
    ref, an all-zero before SHA, and a valid non-zero 40-character hexadecimal
    after SHA.
  • Uses after as the authoritative immutable revision and fetches commit
    metadata for that exact SHA instead of a mutable branch head.
  • Keeps PipelineRun definitions and repository-local Task/Pipeline references
    on the same selected source revision, including default_branch provenance.
  • Passes the selected repository revision explicitly through the resolver,
    removing the ordering dependency between GetTektonDir and
    GetFileInsideRepo.
  • Resolves the full commit message before the early skip-CI decision when the
    webhook omits commit metadata.
  • Continues to reject branch deletion, tag pushes, invalid SHAs, empty branch
    names, and unrelated empty-commit push events.

🔍 Root Cause and Code Location

The links below use the fixed pre-change main revision
3bbaf67f5042b852f9acebc59758eeb38e0ee870.

  1. ParsePayload rejected every push with an empty commits array.
    A valid GitLab branch-creation payload has an all-zero before SHA, a
    non-zero after SHA containing the created branch tip, and a
    refs/heads/... ref, but no entry in commits. The unconditional rejection
    therefore discarded a valid event before matching could begin.
  2. PipelineRun definitions were loaded through GetTektonDir,
    while the GitLab provider selected the mutable branch ref.
    If another push moved the branch after webhook delivery, matching could read
    definitions from a different commit than the event's after SHA.
  3. Repository-local Task and Pipeline references followed a separate call
    chain: getPipelineRunsFromRepo called resolve.Resolve,
    Resolve created RemoteTasks without a selected revision,
    and RemoteTasks passed an empty target to the provider.
    The GitLab implementation then always fetched from HeadBranch,
    so repository-local references could drift independently of the PipelineRun
    definitions.
  4. The early push skip-CI check only inspected SHATitle.
    Branch-creation payloads do not provide that commit metadata, so the exact
    after commit must be queried before checking its full message.

The fix classifies only genuine branch-creation payloads, records after as an
immutable event-scoped source revision, and queries commit metadata by that SHA.
For source provenance, .tekton definitions use the same SHA. For
default_branch provenance, the default branch is selected explicitly. The
chosen revision is then passed through resolve.Opts and RemoteTasks into
GetFileInsideRepo, so repository-local references use the same source without
requiring GetTektonDir to mutate shared event state first. If a caller does not
provide an explicit target, GitLab branch creation safely falls back to the
immutable event SHA.

📦 Release Notes

GitLab branch creation events can now trigger Pipelines as Code when GitLab
omits the commits array from the Push Hook payload.

🔗 Linked GitHub Issue

Fixes #2874

🧪 Testing Strategy

  • Unit tests
  • Integration tests
  • End-to-end tests
  • Manual testing
  • Not Applicable

The tests cover payload classification, exact-SHA metadata lookup, invalid and
deletion payload rejection, source/default-branch revision selection, explicit
repository-revision propagation, repository-local references, skip-CI
handling, and the adapter event flow.

make test passes all 3162 tests. Go, gofumpt, and YAML lint checks pass. The
complete local make lint could not run its Markdown/grammar stage because the
local environment does not have markdownlint or vale; this PR does not
change Markdown files, and CI remains the authoritative complete lint run.

🤖 AI Assistance

  • I have not used any AI assistance for this PR.
  • I have used AI assistance for this PR.

OpenAI Codex (GPT-5) assisted with implementation and test development. The
submitter reviewed and understood the changes, validated the behavior, and ran
the test and available lint suites. The commits include an Assisted-by
trailer.

✅ Submitter Checklist

  • 📝 My commit messages are clear, informative, and follow the project's How to write a git commit message guide. The Gitlint linter ensures in CI it's properly validated
  • ✨ I have ensured my commit message prefix (e.g., fix:, feat:) matches the "Type of Change" I selected above.
  • ♽ I have run make test and make lint locally to check for and fix any issues. make test and all available lint stages pass; the missing local Markdown tools are documented above.
  • 📖 I have added or updated documentation for any user-facing changes. No documentation update is needed for this GitLab webhook bug fix; release notes are included above.
  • 🧪 I have added sufficient unit tests for my code changes.
  • 🎁 I have added end-to-end tests where feasible. The GitLab webhook edge case is covered by parser, provider, matcher, and adapter integration tests.
  • 🔎 I have addressed any CI test flakiness or provided a clear reason to bypass it.
  • If adding a provider feature, I have filled in the following and updated the provider documentation. This is a GitLab bug fix, not a new provider feature.
    • GitHub App
    • GitHub Webhook
    • Gitea/Forgejo
    • GitLab
    • Bitbucket Cloud
    • Bitbucket Data Center

@pipelines-as-code

Copy link
Copy Markdown

Paco Review 🔍

This PR adds support for GitLab "branch creation" push webhooks that arrive with an empty commits array (only before/after SHAs), by detecting the pattern, deferring skip-CI evaluation until commit metadata is fetched via API and SHA-verified, and pinning PipelineRun/file resolution to the immutable webhook SHA (or default branch) via a new shared PipelineRunSourceRevision field. It touches pkg/adapter/sinker.go (new shouldSkipPushEvent helper), pkg/provider/gitlab/{gitlab.go,parse_payload.go} (new detection/validation helpers and revised GetCommitInfo/GetTektonDir/GetFileInsideRepo), and pkg/params/info/events.go (two new Event fields), with substantial accompanying unit tests.

Review difficulty: 4/5 (Hard) — The change spans webhook parsing, skip-CI decisioning, and revision resolution across several interdependent files with subtle edge cases (SHA casing, branch-creation detection, call-order coupling) that require careful tracing to validate.

1 new inline comment(s) found.

Reviewed commit: dae8edb

@pipelines-as-code pipelines-as-code Bot added paco/review-hard Paco review difficulty security-review Flagged as security-sensitive by Paco labels Jul 22, 2026

@pipelines-as-code pipelines-as-code 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.

Paco inline comments -- see the Paco Review summary comment for the overview.

Comment thread pkg/provider/gitlab/gitlab.go Outdated
@l-qing
l-qing force-pushed the fix/gitlab-branch-creation-without-commits branch 2 times, most recently from a28ee30 to 7abd479 Compare July 22, 2026 12:38
Comment thread pkg/provider/gitlab/parse_payload.go
Comment thread pkg/provider/gitlab/parse_payload.go
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.72727% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.44%. Comparing base (5106a0e) to head (85a3913).

Files with missing lines Patch % Lines
pkg/provider/gitlab/test/test.go 0.00% 19 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2875      +/-   ##
==========================================
+ Coverage   68.23%   68.44%   +0.20%     
==========================================
  Files         197      197              
  Lines       16680    16761      +81     
==========================================
+ Hits        11382    11472      +90     
+ Misses       4432     4417      -15     
- Partials      866      872       +6     
Flag Coverage Δ
unit-tests 68.44% <82.72%> (+0.20%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@l-qing
l-qing force-pushed the fix/gitlab-branch-creation-without-commits branch from d2b86ad to 04d8cc9 Compare July 28, 2026 03:12
Comment on lines +585 to +588
if branchCreate {
// Pin branch creation to the webhook SHA so a later push cannot change the matched definitions.
revision = event.SHA
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think not only on branchCreate we can use event.SHA for every event as it is available all the time and good for security...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@l-qing PTAL at this and let me know...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sorry, I missed this comment earlier.

I think you are right: for source provenance, we should use event.SHA across all supported GitLab event types.

I originally added the branchCreate guard to keep this fix narrowly scoped and avoid changing the existing behavior for other events. At that point, I had not verified the meaning of event.SHA for every GitLab event type, so I did not want to assume that it was always a valid and reachable commit SHA. In particular, I was concerned that some event types might carry a zero SHA such as 0000000000000000000000000000000000000000. For branch creation, we explicitly validate gitEvent.After, so overriding revision in that case was the conservative choice.

I reviewed how processedEvent.SHA is populated in pkg/provider/gitlab/parse_payload.go (code), together with how runevent.SHA is resolved by GetCommitInfo (code). I do not see such an exception in the supported event paths: they either provide an actual commit SHA or resolve one before the PipelineRun definitions are loaded.

I’ll open a follow-up PR to use event.SHA consistently for GitLab source provenance. I’ll also make sure that both the .tekton definitions and repository-local Task/Pipeline references are pinned to the same revision.

Thanks for pointing this out.

@zakisk

zakisk commented Jul 29, 2026

Copy link
Copy Markdown
Member

overall PR looks good to me! just address the comment and squash your commits

@chmouel

chmouel commented Jul 30, 2026

Copy link
Copy Markdown
Member

fine by me as well just need to have the squashed commit please

Accept branch-creation push payloads that omit the commits array and
resolve the created branch from the immutable after SHA.

Keep PipelineRun definitions, repository-local references, and commit
metadata pinned to the same event revision.

Add regression coverage for payload validation, skip-CI handling, and
source revision selection.

Signed-off-by: qingliu <qingliu@alauda.io>
Assisted-by: OpenAI Codex (GPT-5)
@l-qing
l-qing force-pushed the fix/gitlab-branch-creation-without-commits branch from 04d8cc9 to 85a3913 Compare July 31, 2026 05:22
@l-qing

l-qing commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

fine by me as well just need to have the squashed commit please

Thanks — done.
I’ve squashed the commits into one. I kept them separate earlier to make the individual changes easier for reviewers to follow.

@chmouel chmouel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, Thanks you!

@chmouel
chmouel merged commit b328e04 into tektoncd:main Jul 31, 2026
15 checks passed
l-qing added a commit to l-qing/pipelines-as-code that referenced this pull request Aug 1, 2026
Use the immutable event SHA for PipelineRun definitions and
repository-local Task and Pipeline references across GitLab events.
Fall back to the source branch only when the event SHA is empty or
all zeros.

This follows up on tektoncd#2875 after its source-provenance review comment
was noticed after merge. It prevents later branch updates from
changing the repository revision selected for an existing event.

Signed-off-by: qingliu <qingliu@alauda.io>
zakisk pushed a commit to l-qing/pipelines-as-code that referenced this pull request Aug 3, 2026
Use the immutable event SHA for PipelineRun definitions and
repository-local Task and Pipeline references across GitLab events.
Fall back to the source branch only when the event SHA is empty or
all zeros.

This follows up on tektoncd#2875 after its source-provenance review comment
was noticed after merge. It prevents later branch updates from
changing the repository revision selected for an existing event.

Signed-off-by: qingliu <qingliu@alauda.io>
zakisk pushed a commit that referenced this pull request Aug 3, 2026
Use the immutable event SHA for PipelineRun definitions and
repository-local Task and Pipeline references across GitLab events.
Fall back to the source branch only when the event SHA is empty or
all zeros.

This follows up on #2875 after its source-provenance review comment
was noticed after merge. It prevents later branch updates from
changing the repository revision selected for an existing event.

Signed-off-by: qingliu <qingliu@alauda.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

paco/review-hard Paco review difficulty security-review Flagged as security-sensitive by Paco

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GitLab branch creation pushes with no commits are rejected

3 participants