pr

$npx mdskill add liferay/liferay-portal/pr

Creates a GitHub PR for the current branch and updates Jira tickets.

  • Solves the need to create a pull request from the current branch.
  • Depends on Git, GitHub CLI, and Jira API for ticket updates.
  • Decides based on branch name, commit messages, and pr-check results.
  • Delivers a PR URL and transitions linked Jira tickets to review.

SKILL.md

.github/skills/prView on GitHub ↗
---

allowed-tools: [Bash, Glob, Grep, Read, Skill]
argument-hint: "[optional target-org/repo, message hint, or --skip-pr-check]"
description: Create a GitHub PR for the current branch. Use when the user asks to create a PR, send a PR, or invokes /pr.
name: pr

---

# Create a Pull Request

Create a GitHub PR for the current branch, transition the linked Jira tickets to review, and record the PR URL on those tickets.

## Preconditions

- At least one commit adds tests. When none do, ask the user for a rationale and refuse to proceed without one. The only exceptions are PRs with no code changes (e.g., language key updates or markdown changes).

- The current branch is a development branch, not `master` or any other protected branch.

- The `pr-check` skill must pass. Skip only when `${ARGUMENTS}` contains `--skip-pr-check`. A skip requires a reason: take it from the text following the flag when present, otherwise prompt the user for one. The reason is recorded in the **PR Check** section, which is written for a skip rather than omitted.

## Input

### Branch

The current Git branch must contain the commits ready to ship.

### Jira Tickets

A branch may span more than one ticket. Resolve the **ticket set** — every ticket the PR touches.

The ticket key follows the pattern `LPD-12345`, `LCD-12345`, `LRCI-1234`, and similar forms (uppercase letters, hyphen, digits).

Collect every distinct ticket key from the subjects of the branch's commits relative to `master`. Each subject is prefixed with its ticket (`LPD-12345 <subject>`); extract every distinct key, in commit order (oldest first). When no commit carries a ticket, prompt the user for one.

### Target Repository

The target repository defaults to `<fork-owner>/liferay-portal`. When `${ARGUMENTS}` names a different `org/repo`, use that; when it matches an alias below, expand the alias; otherwise, ask the user to choose `<fork-owner>` from one of the team forks:

- `liferay-ac`
- `liferay-appsec`
- `liferay-bpm`
- `liferay-commerce`
- `liferay-content-management`
- `liferay-core-infra`
- `liferay-database-infra`
- `liferay-devtools`
- `liferay-frontend`
- `liferay-headless`
- `liferay-page-management`
- `liferay-platform-experience`
- `liferay-search`
- `liferay-site-management`

The following short aliases resolve to a target repository:

- `brian` → `brianchandotcom/liferay-portal`

The PR head is `<github-username>:<branch-name>` (the GitHub username is read from the user's `origin` remote URL — e.g., `[email protected]:brianchandotcom/liferay-portal.git` yields `brianchandotcom`), and the base is `master`.

## Expected Output

### Pushed Branch

Push the current branch to the user's remote when it has not been pushed yet or when new local commits exist.

### Pull Request

The title is concise (under 72 characters) and prefixed with the key of the first ticket in the set:

```
LPD-83847 Fix OutOfMemoryError during batch engine import
```

The body follows this format, with one `browse` link per ticket in the set:

```markdown
https://liferay.atlassian.net/browse/TICKET-ID-1
https://liferay.atlassian.net/browse/TICKET-ID-2

## What Is Being Fixed

Explain the problem or bug that motivated the change — what was going wrong or what was missing.

## How It Is Being Fixed

Explain the approach taken across all commits. Describe the key changes and the reasoning behind the approach. Write in plain prose rather than bullet points.

## Why Are There No Tests?

This optional section is only included when the commits do not add any tests. It should contain the rationale provided by the user.

## PR Check

<pr-check Results Summary>

<!-- pr-check {"result": "<state>", "sha": "<tested-SHA>"} -->
```

The **PR Check** section is the Results Summary block emitted by the `pr-check` skill that ran as the precondition — the overall state line, the tested SHA, and the per-validation table, pasted verbatim — followed by a hidden marker. The marker is an HTML comment, invisible in rendered Markdown, whose payload is a JSON object of the form `<!-- pr-check {"result": "<state>", "sha": "<tested-SHA>"} -->`, where `<state>` is `success` when the overall state is `PASS` and `failure` when it is `FAIL`, and `<tested-SHA>` is the full 40-character SHA from the Results Summary. The webhook reads the `result` and `sha` fields to apply the `pr-check` commit status to that SHA and the `pr-check - <state>` label to the PR, so this skill records no status or label itself.

When pr-check was skipped via `--skip-pr-check`, still write the section, but as a skip rather than a run. Under the same `## PR Check` heading, the body is a single `**pr-check: SKIPPED** — <reason>` line with no table, and the marker payload adds a `reason` field alongside `result` (set to `skipped`) and the PR head SHA. The JSON object keys are alphabetical (`reason`, `result`, `sha`).

```markdown
**pr-check: SKIPPED** — <reason>

<!-- pr-check {"reason": "<reason>", "result": "skipped", "sha": "<head-SHA>"} -->
```

The webhook applies the status and label when it processes the `pull_request` event for the newly opened PR, so no publish step is needed at creation. Use the `pr-check-publish` skill only to record a later pr-check run on an existing PR.

Use a direct, to-the-point style. Avoid being verbose. Present the proposed title and body to the user before submitting, and proceed once they approve.

Create the pull request with `--body-file`, or with `--body` from a quoted-heredoc variable; either keeps the marker's literal `!` off the command line, where it could otherwise trigger history expansion and corrupt the marker. Use `mktemp` for the file so it stays out of the working tree, and remove it afterward.

```bash
body_file=$(mktemp)

gh pr create \
	--base master \
	--body-file "${body_file}" \
	--head <github-username>:<branch-name> \
	--repo <target-org/repo> \
	--title "<title>"

rm "${body_file}"
```

### Transitioned Jira Tickets

Apply the steps below to **every ticket in the ticket set**, recording the outcome per ticket and continuing on failure.

For each ticket, fetch it (issue type, status, subtasks) and resolve the **target ticket** — the one whose status reflects active work and on which the PR URL is recorded:

| Ticket Type | Target |
| --- | --- |
| Bug (`10004`) | The bug itself |
| Task (`10002`) | Its Technical Task (`10153`) subtask |
| Technical Task (`10153`) | Itself |

When the target is not already in an in-progress status, transition it first:

| Target Type | Destination | Transition ID |
| --- | --- | --- |
| Bug | In Progress | `61` |
| Technical Task | In Progress | `41` |

Then transition it to review:

| Target Type | Destination | Transition ID |
| --- | --- | --- |
| Bug | In Review | `71` |
| Technical Task | In Peer Review | `31` |

When the review transition fails (for example, because the ticket is already in a later status), still proceed to record the PR URL.

Set the **Git Pull Request** field (`customfield_10201`) on the target ticket to the new PR URL.

### Existing Pull Request

This handling applies per target ticket, while recording the PR URL above.

When the **Git Pull Request** field already holds one or more PR URLs, ask the user whether the new PR **supersedes** the existing one or is **added** alongside it.

When the user chooses **supersede**:

1. Overwrite **Git Pull Request** with the new PR URL, dropping the previous value.

1. Add a comment on the previous PR, linking to the new one (for example, `Superseded by <new-pr-url>.`).

1. Close the previous PR when possible. When the user lacks permission to close it directly, add a `ci:close` comment instead, so the CI bot closes it.

When the user chooses **add**:

1. Append the new PR URL to the existing value, separating each URL with a comma and a space.

### Summary

Report back to the user with:

- Each ticket in the set, with its resulting status and link.
- The PR URL.

More from liferay/liferay-portal

SkillDescription
format-sourceFormat source files to align with Liferay's coding standards.
format-source-add-ruleAdd a new rule to the format-source skill, derived from a Git commit.
jira-bugCreate a Jira bug ticket in the LPD project through the REST API. Use when the user asks to create or file a Jira bug or LPD ticket.
jira-taskCreate a Jira task in the LPD project through the REST API. Use when the user asks to create a Jira task or LPD task ticket.
markdown-formatFormat a Markdown file to match Liferay conventions — frontmatter order, Title Case headings, braced shell variables, long-form CLI flags, and professional prose. Use when the user asks to format, clean up, polish, or copy-edit a Markdown file destined for the Liferay repository.
poshi-migrateMigrate a Liferay Poshi .testcase file to its modern test layer (Playwright, Jest, JUnit unit, Java integration). Routes each test to the right layer, consolidates compatible tests at write time, and follows existing Playwright patterns from a configurable reference folder. Use when the user asks to migrate, port, or convert a Poshi .testcase to Playwright, Jest, or JUnit.
poshi-shrinkShrink a Liferay component's Poshi test suite by merging overlapping tests. Use when the user asks to reduce, merge, or clean up Poshi tests for a @component-name.
pr-checkCheck that a PR is ready to be sent for review.
pr-check-publishComment a pr-check run on an existing GitHub PR with a marker the webhook parses to apply the commit status and label. Use after rerunning pr-check on an open PR.
test-fixResolve a single Liferay test failure end-to-end.