Skip to content

feat(gitlab-issue-to-mr): add a label-triggered issue-to-MR automation - #533

Open
VascoSch92 wants to merge 2 commits into
mainfrom
vasco/gitlab-issue-to-mr-automation
Open

feat(gitlab-issue-to-mr): add a label-triggered issue-to-MR automation#533
VascoSch92 wants to merge 2 commits into
mainfrom
vasco/gitlab-issue-to-mr-automation

Conversation

@VascoSch92

@VascoSch92 VascoSch92 commented Sep 3, 2026

Copy link
Copy Markdown
Member
  • A human has tested these changes.

Why

The catalog has a first-class GitHub issue-to-pr automation, and ticket-to-code
entries for Jira, Linear, and Bitbucket, but nothing that starts from a GitLab
issue and ends in a GitLab merge request. GitLab-hosted teams could only get
there by keeping their tickets somewhere else. This ports the proven
github-issue-to-pr automation to GitLab.

Note: #525 opened against the same issue on Aug 31. This branch is an independent
port written against current main (post-#531 template rename), with a full test
suite and self-managed instance support. Whichever lands, the other should close.

Summary

  • Add skills/gitlab-issue-to-mr/SKILL.md setup workflow, README.md,
    references/state-schema.md, and scripts/main.py, the deterministic poller.
  • Add automations/catalog/gitlab-issue-to-mr/manifest.json — a gitlab
    repo-picker, a merge-request-mode select, a configurable API root for
    self-managed instances, and a bundle pointing at the shipped script.
  • Add tests/test_gitlab_issue_to_mr.py — 70 unit tests over what the script
    owns: config validation, project-path normalization, URL encoding, access
    level, credential handling, label events, branch naming, MR creation, clone
    release, and state.
  • Register the skill in marketplaces/openhands-extensions.json; regenerate
    skills/index.js, automations/catalog-index.js, automations/bundle-index.js,
    and the README catalog section.

Every structural guarantee of the GitHub original is kept: one poll per project
with its own state document, dedup on the label event id, a claim persisted
before the slow work, a clone that carries no credential, an allow-list of
forwarded secrets, and a finalization pass that opens the merge request itself
when the agent did not.

One deliberate divergence from github-issue-to-pr: the deployment's MCP
servers are forwarded to the spawned conversation, as github-pr-reviewer
already does, so a connected GitLab server gives the agent typed tools instead
of curl. The curl calls stay in the prompt as the fallback, and pushing the
branch is a git operation either way, so GITLAB_TOKEN is still required. This
does widen what an issue-authored prompt can reach to everything those servers
expose. github-issue-to-pr forwards nothing for exactly that reason, so this
is worth an explicit yes or no in review. Reverting it is a three-line change.

What GitLab makes different:

GitHub GitLab
labeled issue events resource_label_events with action: "add"
repo-global issue numbers project-scoped iid
owner/repo in the path URL-encoded project path, so subgroups survive
labels are objects labels are plain strings
draft: true on the API a Draft: title prefix
x-access-token basic auth oauth2 basic auth
permissions.push Developer role, reading the group role when the project states none
fixed api.github.com configurable API root, and the clone URL comes from the project

Issue Number

Closes #443

How to Test

uv run --group test pytest tests/ -q          # 862 passed, 14 skipped
uv run --group test python scripts/sync_extensions.py --check
npm run build                                  # no diff after regeneration
python3 -m py_compile skills/gitlab-issue-to-mr/scripts/main.py

End to end, on a GitLab project you can push to:

  1. Add GITLAB_TOKEN in OpenHands Settings → Secrets, with the api scope and
    at least the Developer role.
  2. Create the automation from the catalog entry, or run /issue-to-mr:setup.
  3. Apply the openhands label to an open issue.
  4. The next poll comments on the issue with the branch and conversation link, and
    a draft merge request follows once the agent stops.

Video/Screenshots

Not applicable — no UI surface. The catalog entry renders through the existing
automation template form.

Notes

  • Leave the configured branch prefix out of any protected-branch rule, or the
    push is rejected.
  • Self-managed instances are configured by their API root, e.g.
    https://gitlab.example.com/api/v4.
  • automations/interface.json's featuredAutomationIds is unchanged; this entry
    is not featured.
  • GitLab's catalog entry offers MCP only on its OAuth connection option, which is
    beta and gated behind Duo availability on a top-level group. The API option
    stores GITLAB_TOKEN and carries no MCP server. So the MCP path here is a
    bonus when a deployment has one connected, never a requirement.

HUMAN: I ran the full test suite and the sync/build checks locally on this branch; the end-to-end GitLab run against a live project has not been done yet.

@github-actions github-actions Bot added the type: feat A new feature label Sep 3, 2026
@VascoSch92
VascoSch92 marked this pull request as ready for review September 4, 2026 07:53
Ports the github-issue-to-pr automation to GitLab, closing the ticket-to-code
gap for the third of the big-three git providers.

The script keeps every structural guarantee of the GitHub original: one poll
per project with its own state document, dedup on the label event id, a claim
persisted before the slow work, a clone that carries no credential, an
allow-list of forwarded secrets, and a finalization pass that opens the merge
request itself when the agent did not.

What GitLab makes different:

- resource label events with action "add" replace GitHub labeled events
- issue IIDs are project-scoped, and project paths are URL-encoded wherever an
  ID is expected, so subgroups survive
- issue labels come back as plain strings
- a draft is a "Draft: " title prefix, not an API flag
- git authenticates the token as the oauth2 user
- the API root is configurable, so self-managed instances work, and the clone
  URL comes from the project rather than being built from parts
- the Developer role check replaces GitHub's push permission check, reading
  the group role when the project states none

Closes #443
… agent

Matches github-pr-reviewer: the conversation gets agent_settings.mcp_config
whole, so a connected GitLab server gives the agent typed tools rather than the
curl calls the prompt spells out. Those stay as the fallback, and pushing the
branch is a git operation either way, so GITLAB_TOKEN is still required.

An unreadable settings endpoint is a warning, not a dropped task.

This widens what an issue-authored prompt can reach to everything the connected
servers expose. The skill and README now say so, and the setup workflow asks the
operator to confirm it.
@VascoSch92
VascoSch92 force-pushed the vasco/gitlab-issue-to-mr-automation branch from 4dfcb02 to bd6af7e Compare September 4, 2026 07:55
@all-hands-bot

Copy link
Copy Markdown
Contributor

🤖 OpenHands is reviewing this PR.

Head commit: bd6af7e166eb37ada78f4003b9c3f11fc7e69d9b
View the conversation: https://oss-agent-canvas.ngrok.dev/conversations/1cc2807d-7515-47cd-b989-b30e783c2037

This comment was posted by an AI agent (OpenHands).

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This review was created by an AI agent (OpenHands) on behalf of the repository maintainers.

Summary

This PR ports the proven github-issue-to-pr automation to GitLab, adding a new gitlab-issue-to-mr skill, catalog manifest, and 75 unit tests. The code is well-structured, closely follows the established patterns from the GitHub original, and correctly adapts for GitLab's API differences (resource label events, project-scoped IIDs, URL-encoded paths, oauth2 basic auth, Developer role checks, configurable API root).

Taste Rating: Good taste

The structural guarantees from the GitHub original are all preserved: per-project state, label-event dedup, claim-before-clone, credential-free origin, secret allow-list, and a finalization fallback that opens the MR when the agent did not.

Security Consideration: MCP Server Forwarding

The one deliberate divergence from github-issue-to-pr is forwarding the deployment's MCP servers to the spawned conversation. The PR description explicitly calls this out and asks for review feedback.

The concern: github-issue-to-pr deliberately withholds MCP servers ("a connected GitHub MCP server would hand the conversation the same write access the empty secrets payload just withheld"), while this automation forwards them wholesale, matching github-pr-reviewer. The trust boundary difference matters: github-pr-reviewer is triggered by a PR label (requires write access to the repo), while this automation is triggered by an issue label. On GitLab, a Reporter (access level 20, below Developer) can be granted permission to label issues, which is a lower bar than PR write access. An issue-authored prompt can therefore reach everything those MCP servers expose from a lower-trust trigger.

This is a real widening of the attack surface. The prompt does include an untrusted-input boundary instruction, but that is a prompt-level guardrail, not an architectural one. The README and SKILL.md document this trade-off clearly, and reverting is a three-line change. Recommendation: consider whether the MCP forwarding should be opt-in (e.g., a config flag defaulting to off) rather than always-on, given the lower trust boundary of issue-triggered automations. If the maintainers decide the always-on behavior is acceptable given the documentation, the implementation itself is correct and consistent with github-pr-reviewer.

Minor Observation: Agent Push URL vs Script Clone URL

The prompt instructs the agent to push using a URL built from _instance_url() (derived from GITLAB_API_URL), while the script's clone uses _clone_url() which prefers http_url_to_repo from the project data. For self-managed instances where git is served from a different host than the API, the agent's push would target the wrong host and fail. This is not a bug - the script's fallback _push_branch uses origin (set correctly during clone), and the prompt tells the agent to stop if the push fails. But it does mean the agent's self-service path (opening the MR immediately) would not work on split-host self-managed instances, falling back to the script's next-poll finalization. Worth noting in the troubleshooting table if not already.

Verification

  • 75 new tests pass (867 total, 14 skipped)
  • sync_extensions.py --check passes
  • npm run build produces no diff
  • Manifest validates against catalog.schema.json
  • py_compile passes

[RISK ASSESSMENT]

  • Overall PR: MEDIUM
    The code is solid and well-tested, but the MCP forwarding on an issue-triggered automation lowers the trust boundary compared to the GitHub original. The implementation is correct; the risk is in the security posture choice, which the PR author has explicitly flagged for review.

VERDICT: Worth merging, pending maintainer decision on the MCP forwarding security posture.

KEY INSIGHT: The MCP forwarding is the single security decision that separates this automation from its GitHub counterpart, and it deserves an explicit maintainer sign-off given the lower trust boundary of issue-triggered workflows.


Improve this review? If any feedback above seems incorrect or irrelevant to this repository, you can teach the reviewer to do better:

  1. Add a .agents/skills/custom-codereview-guide.md file to your branch (or edit it if one already exists) with the /codereview trigger and the context the reviewer is missing (e.g., "Security concerns about X do not apply here because Y"). See the customization docs for the required frontmatter format.
  2. Re-request a review - the reviewer reads guidelines from the PR branch, so your changes take effect immediately.
  3. When your PR is merged, the guideline file goes through normal code review by repository maintainers.

Resolve with AI? Install the iterate skill in your agent and run /iterate to automatically drive this PR through CI, review, and QA until it's merge-ready.

Was this review helpful? React with thumbs up or thumbs down to give feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feat A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add GitLab issue → MR automation (ticket→PR parity with Jira)

2 participants