Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/generate-changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ on:
required: false
default: 'main'
type: string
reuse_branch:
description: 'Keep a single self-updating changelog pull request instead of opening a new one on every run.'
required: false
default: true
type: boolean

pull_request_title:
description: 'The title of the pull request.'
Expand Down Expand Up @@ -80,6 +85,15 @@ on:
token_private_key:
description: 'A GitHub App private key used to generate an access token to create a pull request.'

# The changelog branch is reused and force pushed, so two runs racing on it would
# leave the pull request holding whichever finished last rather than the newest
# state of the base branch. The branch being pushed is what has to be serialised,
# so it is the key: a triggering ref would put runs that share one branch_name
# into separate groups and let them race anyway.
concurrency:
group: generate-changelog-${{ inputs.branch_name }}
cancel-in-progress: true

jobs:
generate_changelog:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -153,6 +167,7 @@ jobs:
with:
branch_name: ${{ inputs.branch_name }}
base_branch_name: ${{ inputs.base_branch_name }}
reuse_branch: ${{ inputs.reuse_branch }}
pull_request_title: ${{ inputs.pull_request_title }}
pull_request_body: ${{ inputs.pull_request_body }}
author_name: ${{ inputs.author_name }}
Expand Down
25 changes: 16 additions & 9 deletions actions/assemble-docs/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21213,6 +21213,7 @@ var require_dist = __commonJS({
absolutePath: () => absolutePath2,
deleteFile: () => deleteFile2,
exec: () => exec2,
execFile: () => execFile,
generateRandomSuffix: () => generateRandomSuffix,
getInput: () => getInput22,
getNumberInput: () => getNumberInput,
Expand All @@ -21233,6 +21234,9 @@ var require_dist = __commonJS({
function exec2(command) {
return (0, import_child_process.execSync)(command).toString();
}
function execFile(file, args) {
return (0, import_child_process.execFileSync)(file, args).toString();
}
var ALPHANUM = "abcdefghijklmnopqrstuvwxyz0123456789";
function generateRandomSuffix(length) {
let result = "";
Expand Down Expand Up @@ -21261,22 +21265,25 @@ var require_dist = __commonJS({
function absolutePath2(p) {
return p.startsWith("/") ? p : import_node_path2.default.join(process.cwd(), p);
}
function git(args) {
return execFile("git", args);
}
function gitAdd() {
exec2(`git add .`);
git(["add", "."]);
}
function gitCommit(message, authorName, authorEmail) {
exec2(`git config user.name "${authorName}"`);
exec2(`git config user.email "${authorEmail}"`);
exec2(`git commit -m "${message}"`);
git(["config", "user.name", authorName]);
git(["config", "user.email", authorEmail]);
git(["commit", "-m", message]);
}
function gitCheckoutBranch(branch) {
exec2(`git checkout -b ${branch}`);
function gitCheckoutBranch(branch, { reset = false } = {}) {
git(["checkout", reset ? "-B" : "-b", branch]);
}
function gitPushBranch(branch) {
exec2(`git push -u origin ${branch}`);
function gitPushBranch(branch, { force = false } = {}) {
git(["push", ...force ? ["--force"] : [], "-u", "origin", "--", branch]);
}
function gitHasChanges() {
const output = exec2("git status --porcelain");
const output = git(["status", "--porcelain"]);
return output.trim().length > 0;
}
var core = __toESM2((init_core(), __toCommonJS(core_exports)));
Expand Down
28 changes: 15 additions & 13 deletions actions/create-pr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This action creates a pull request from a branch to a target branch.

Any pull requests created as a result of actions that use the default token (`${{ GITHUB_TOKEN }}`) will not trigger any pipeline events. To ensure that any pipelines are triggered, a different token must be used.
Any pull requests created as a result of actions that use the default token (`${{ github.token }}`) will not trigger any pipeline events. To ensure that any pipelines are triggered, a different token must be used.

Any files that will be changed and committed to the pull request must be listed in the `.github/repo_policies/BOT_APPROVED_FILES` file of the repository. For example:

Expand All @@ -14,21 +14,23 @@ packages/some-package/package.json

## Action inputs

| Input | Description | Default |
| -------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------- |
| `branch_name` | The name of the branch to create the pull request from. | `'patch'` |
| `base_branch_name` | The name of the base branch to create a pull request against. | `'main'` |
| `pull_request_title` | The title of the pull request. | `'chore: automated by GitHub actions'` |
| `pull_request_body` | The body of the pull request. | `'This pull request was automatically created by a GitHub Action.'` |
| `author_name` | The name of the author of the pull request and commit. | `${{ github.actor }}` |
| `author_email` | The email of the author of the pull request and commit. | `${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com` |
| `commit_message` | The message of the commit. | `'chore: automated by GitHub actions'` |
| `token` | Access token to manage the pull request. | `${{ GITHUB_TOKEN }}` |
| Input | Description | Default |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| `branch_name` | The name of the branch to create the pull request from. | `'patch'` |
| `base_branch_name` | The name of the base branch to create a pull request against. | `'main'` |
| `reuse_branch` | Reuse branch_name as-is instead of creating a new randomly suffixed branch on every run. The branch is reset to the current commit and force pushed, and the pull request already open for it is updated rather than duplicated; when there is nothing left to propose that pull request is closed. Enable this only for a branch owned by automation, since whatever is already on it is overwritten. | `'false'` |
| `pull_request_title` | The title of the pull request. | `'chore: automated by GitHub actions'` |
| `pull_request_body` | The body of the pull request. | `'This pull request was automatically created by a GitHub Action.'` |
| `author_name` | The name of the author of the pull request and commit. | `${{ github.actor }}` |
| `author_email` | The email of the author of the pull request and commit. | `${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com` |
| `commit_message` | The message of the commit. | `'chore: automated by GitHub actions'` |
| `token` | Access token to manage the pull request. | `${{ github.token }}` |

## Action outputs

- `pull_request_number`: The number of the created pull request.
- `pull_request_created`: A boolean indicating whether the pull request was created. This will be `false` is there are no changes to commit.
- `pull_request_number`: The number of the pull request that was created, updated or closed. Empty when there was nothing to propose and no pull request was open.
- `pull_request_created`: A boolean indicating whether the pull request was created. This will be `false` if there are no changes to commit.
- `pull_request_updated`: A boolean indicating whether an already open pull request was updated instead of a new one being created. Only ever `true` when `reuse_branch` is enabled.

## Example usage

Expand Down
8 changes: 7 additions & 1 deletion actions/create-pr/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ inputs:
description: 'The name of the base branch to create a pull request against.'
required: false
default: 'main'
reuse_branch:
description: 'Reuse branch_name as-is instead of creating a new randomly suffixed branch on every run. The branch is reset to the current commit and force pushed, and the pull request already open for it is updated rather than duplicated; when there is nothing left to propose that pull request is closed. Enable this only for a branch owned by automation, since whatever is already on it is overwritten.'
required: false
default: 'false'

pull_request_title:
description: 'The title of the pull request.'
Expand Down Expand Up @@ -42,9 +46,11 @@ inputs:

outputs:
pull_request_number:
description: 'The number of the created pull request.'
description: 'The number of the pull request that was created, updated or closed. Empty when there was nothing to propose and no pull request was open.'
pull_request_created:
description: 'Whether the pull request was created.'
pull_request_updated:
description: 'Whether an already open pull request was updated instead of a new one being created. Only ever true when reuse_branch is enabled.'

runs:
using: node24
Expand Down
107 changes: 91 additions & 16 deletions actions/create-pr/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22081,10 +22081,11 @@ var require_dist = __commonJS({
absolutePath: () => absolutePath,
deleteFile: () => deleteFile,
exec: () => exec2,
execFile: () => execFile,
generateRandomSuffix: () => generateRandomSuffix2,
getInput: () => getInput22,
getNumberInput: () => getNumberInput,
getOptInput: () => getOptInput,
getOptInput: () => getOptInput2,
gitAdd: () => gitAdd2,
gitCheckoutBranch: () => gitCheckoutBranch2,
gitCommit: () => gitCommit2,
Expand All @@ -22101,6 +22102,9 @@ var require_dist = __commonJS({
function exec2(command) {
return (0, import_child_process.execSync)(command).toString();
}
function execFile(file, args) {
return (0, import_child_process.execFileSync)(file, args).toString();
}
var ALPHANUM = "abcdefghijklmnopqrstuvwxyz0123456789";
function generateRandomSuffix2(length) {
let result = "";
Expand Down Expand Up @@ -22129,29 +22133,32 @@ var require_dist = __commonJS({
function absolutePath(p) {
return p.startsWith("/") ? p : import_node_path.default.join(process.cwd(), p);
}
function git(args) {
return execFile("git", args);
}
function gitAdd2() {
exec2(`git add .`);
git(["add", "."]);
}
function gitCommit2(message, authorName, authorEmail) {
exec2(`git config user.name "${authorName}"`);
exec2(`git config user.email "${authorEmail}"`);
exec2(`git commit -m "${message}"`);
git(["config", "user.name", authorName]);
git(["config", "user.email", authorEmail]);
git(["commit", "-m", message]);
}
function gitCheckoutBranch2(branch) {
exec2(`git checkout -b ${branch}`);
function gitCheckoutBranch2(branch, { reset = false } = {}) {
git(["checkout", reset ? "-B" : "-b", branch]);
}
function gitPushBranch2(branch) {
exec2(`git push -u origin ${branch}`);
function gitPushBranch2(branch, { force = false } = {}) {
git(["push", ...force ? ["--force"] : [], "-u", "origin", "--", branch]);
}
function gitHasChanges2() {
const output = exec2("git status --porcelain");
const output = git(["status", "--porcelain"]);
return output.trim().length > 0;
}
var core = __toESM2((init_core(), __toCommonJS(core_exports)));
function getInput22(name) {
return core.getInput(name, { required: true, trimWhitespace: true });
}
function getOptInput(name, defaultValue) {
function getOptInput2(name, defaultValue) {
return core.getInput(name, { required: false, trimWhitespace: true }) || defaultValue;
}
function getNumberInput(name) {
Expand Down Expand Up @@ -25933,6 +25940,37 @@ async function createPullRequest({
number: res.data.number
};
}
async function findOpenPullRequest({
octokit,
owner,
repo,
head,
base
}) {
const res = await octokit.rest.pulls.list({
owner,
repo,
base,
head: `${owner}:${head}`,
state: "open"
});
const [pullRequest] = res.data;
return pullRequest ? { number: pullRequest.number } : void 0;
}
async function closePullRequest({
octokit,
owner,
repo,
number
}) {
await octokit.rest.pulls.update({
owner,
repo,
pull_number: number,
state: "closed"
});
info(`Closed pull request #${number}`);
}

// src/create-commit.ts
init_core();
Expand All @@ -25941,12 +25979,13 @@ function createCommit({
message,
head,
authorName,
authorEmail
authorEmail,
reuseBranch = false
}) {
(0, import_action_utils.gitCheckoutBranch)(head);
(0, import_action_utils.gitCheckoutBranch)(head, { reset: reuseBranch });
(0, import_action_utils.gitAdd)();
(0, import_action_utils.gitCommit)(message, authorName, authorEmail);
(0, import_action_utils.gitPushBranch)(head);
(0, import_action_utils.gitPushBranch)(head, { force: reuseBranch });
info(`Created git commit on branch ${head}`);
}

Expand All @@ -25955,27 +25994,62 @@ async function run() {
try {
const authorName = (0, import_action_utils2.getInput)("author_name");
const authorEmail = (0, import_action_utils2.getInput)("author_email");
const head = `${(0, import_action_utils2.getInput)("branch_name")}-${(0, import_action_utils2.generateRandomSuffix)(6)}`;
const reuseBranch = (0, import_action_utils2.getOptInput)("reuse_branch", "false") === "true";
const branchName = (0, import_action_utils2.getInput)("branch_name");
const head = reuseBranch ? branchName : `${branchName}-${(0, import_action_utils2.generateRandomSuffix)(6)}`;
const base = (0, import_action_utils2.getInput)("base_branch_name");
const message = (0, import_action_utils2.getInput)("commit_message");
const title = (0, import_action_utils2.getInput)("pull_request_title");
const body = (0, import_action_utils2.getInput)("pull_request_body");
const token = (0, import_action_utils2.getInput)("token");
if (head === base) {
throw new Error(
`branch_name resolves to the base branch '${base}'. Set branch_name to a different branch.`
);
}
const octokit = getOctokit(token);
const { owner, repo } = context2.repo;
if (!(0, import_action_utils2.gitHasChanges)()) {
info(
"No changes detected, skipping commit and pull request creation"
);
setOutput("pull_request_created", false);
setOutput("pull_request_updated", false);
if (reuseBranch) {
const obsolete = await findOpenPullRequest({
octokit,
owner,
repo,
head,
base
});
if (obsolete) {
await closePullRequest({
octokit,
owner,
repo,
number: obsolete.number
});
setOutput("pull_request_number", obsolete.number);
}
}
return;
}
createCommit({
authorEmail,
authorName,
head,
message
message,
reuseBranch
});
const existing = reuseBranch ? await findOpenPullRequest({ octokit, owner, repo, head, base }) : void 0;
if (existing) {
info(`Updated pull request #${existing.number}`);
setOutput("pull_request_number", existing.number);
setOutput("pull_request_created", false);
setOutput("pull_request_updated", true);
return;
}
const res = await createPullRequest({
octokit,
owner,
Expand All @@ -25987,6 +26061,7 @@ async function run() {
});
setOutput("pull_request_number", res.number);
setOutput("pull_request_created", true);
setOutput("pull_request_updated", false);
} catch (error2) {
if (error2 instanceof Error) {
setFailed(error2.message);
Expand Down
Loading
Loading