Skip to content
Open
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
6 changes: 6 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 @@ -110,6 +115,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
8 changes: 4 additions & 4 deletions actions/assemble-docs/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21269,11 +21269,11 @@ var require_dist = __commonJS({
exec2(`git config user.email "${authorEmail}"`);
exec2(`git commit -m "${message}"`);
}
function gitCheckoutBranch(branch) {
exec2(`git checkout -b ${branch}`);
function gitCheckoutBranch(branch, { reset = false } = {}) {
exec2(`git checkout ${reset ? "-B" : "-b"} ${branch}`);
}
function gitPushBranch(branch) {
exec2(`git push -u origin ${branch}`);
function gitPushBranch(branch, { force = false } = {}) {
exec2(`git push ${force ? "--force " : ""}-u origin ${branch}`);
}
function gitHasChanges() {
const output = exec2("git status --porcelain");
Expand Down
6 changes: 6 additions & 0 deletions 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 and update the pull request already open for it, instead of creating a new randomly suffixed branch on every run. When there is nothing left to propose, the open pull request is closed.'
required: false
default: 'false'

pull_request_title:
description: 'The title of the pull request.'
Expand Down Expand Up @@ -45,6 +49,8 @@ outputs:
description: 'The number of the created pull request.'
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
85 changes: 74 additions & 11 deletions actions/create-pr/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22084,7 +22084,7 @@ var require_dist = __commonJS({
generateRandomSuffix: () => generateRandomSuffix2,
getInput: () => getInput22,
getNumberInput: () => getNumberInput,
getOptInput: () => getOptInput,
getOptInput: () => getOptInput2,
gitAdd: () => gitAdd2,
gitCheckoutBranch: () => gitCheckoutBranch2,
gitCommit: () => gitCommit2,
Expand Down Expand Up @@ -22137,11 +22137,11 @@ var require_dist = __commonJS({
exec2(`git config user.email "${authorEmail}"`);
exec2(`git commit -m "${message}"`);
}
function gitCheckoutBranch2(branch) {
exec2(`git checkout -b ${branch}`);
function gitCheckoutBranch2(branch, { reset = false } = {}) {
exec2(`git checkout ${reset ? "-B" : "-b"} ${branch}`);
}
function gitPushBranch2(branch) {
exec2(`git push -u origin ${branch}`);
function gitPushBranch2(branch, { force = false } = {}) {
exec2(`git push ${force ? "--force " : ""}-u origin ${branch}`);
}
function gitHasChanges2() {
const output = exec2("git status --porcelain");
Expand All @@ -22151,7 +22151,7 @@ var require_dist = __commonJS({
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 +25933,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 +25972,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,7 +25987,9 @@ 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");
Expand All @@ -25968,14 +26002,42 @@ async function run() {
"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 +26049,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
10 changes: 8 additions & 2 deletions actions/create-pr/src/create-commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,24 @@ export interface CreateCommitOptions {
head: string;
authorName: string;
authorEmail: string;
/**
* Reuse a long-lived branch rather than a fresh one, resetting it to the
* current commit and force pushing it.
*/
reuseBranch?: boolean;
}

export function createCommit({
message,
head,
authorName,
authorEmail,
reuseBranch = false,
}: CreateCommitOptions): void {
gitCheckoutBranch(head);
gitCheckoutBranch(head, { reset: reuseBranch });
gitAdd();
gitCommit(message, authorName, authorEmail);
gitPushBranch(head);
gitPushBranch(head, { force: reuseBranch });

core.info(`Created git commit on branch ${head}`);
}
54 changes: 54 additions & 0 deletions actions/create-pr/src/create-pull-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,57 @@ export async function createPullRequest({
number: res.data.number,
};
}

export interface FindOpenPullRequestOptions {
octokit: Octokit;
owner: string;
repo: string;
head: string;
base: string;
}

/**
* Finds the open pull request for the given head and base branches, if any.
*/
export async function findOpenPullRequest({
octokit,
owner,
repo,
head,
base,
}: FindOpenPullRequestOptions): Promise<CreatePullRequestResult | undefined> {
const res = await octokit.rest.pulls.list({
owner,
repo,
base,
head: `${owner}:${head}`,
state: 'open',
});

const [pullRequest] = res.data;

return pullRequest ? { number: pullRequest.number } : undefined;
}

export interface ClosePullRequestOptions {
octokit: Octokit;
owner: string;
repo: string;
number: number;
}

export async function closePullRequest({
octokit,
owner,
repo,
number,
}: ClosePullRequestOptions): Promise<void> {
await octokit.rest.pulls.update({
owner,
repo,
pull_number: number,
state: 'closed',
});

core.info(`Closed pull request #${number}`);
}
57 changes: 55 additions & 2 deletions actions/create-pr/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,28 @@ import * as github from '@actions/github';
import {
generateRandomSuffix,
getInput,
getOptInput,
gitHasChanges,
} from '@dfinity/action-utils';
import { createPullRequest } from './create-pull-request';
import {
closePullRequest,
createPullRequest,
findOpenPullRequest,
} from './create-pull-request';
import { createCommit } from './create-commit';

export async function run(): Promise<void> {
try {
const authorName = getInput('author_name');
const authorEmail = getInput('author_email');
const head = `${getInput('branch_name')}-${generateRandomSuffix(6)}`;
const reuseBranch = getOptInput('reuse_branch', 'false') === 'true';
const branchName = getInput('branch_name');
// A fresh branch per run leaves a pull request behind on every run where an
// earlier one has not been merged yet. Reusing a single branch keeps one
// self-updating pull request instead.
const head = reuseBranch
? branchName
: `${branchName}-${generateRandomSuffix(6)}`;
const base = getInput('base_branch_name');
const message = getInput('commit_message');
const title = getInput('pull_request_title');
Expand All @@ -27,6 +39,30 @@ export async function run(): Promise<void> {
'No changes detected, skipping commit and pull request creation',
);
core.setOutput('pull_request_created', false);
core.setOutput('pull_request_updated', false);

// There is nothing left to propose, so a pull request opened by an earlier
// run is obsolete. Left open it goes stale and eventually conflicts.
if (reuseBranch) {
const obsolete = await findOpenPullRequest({
octokit,
owner,
repo,
head,
base,
});

if (obsolete) {
await closePullRequest({
octokit,
owner,
repo,
number: obsolete.number,
});
core.setOutput('pull_request_number', obsolete.number);
}
}

return;
}

Expand All @@ -35,8 +71,24 @@ export async function run(): Promise<void> {
authorName,
head,
message,
reuseBranch,
});

// The force push above has already updated any open pull request for this
// branch, so creating another one would fail.
const existing = reuseBranch
? await findOpenPullRequest({ octokit, owner, repo, head, base })
: undefined;

if (existing) {
core.info(`Updated pull request #${existing.number}`);
core.setOutput('pull_request_number', existing.number);
core.setOutput('pull_request_created', false);
core.setOutput('pull_request_updated', true);

return;
}

const res = await createPullRequest({
octokit,
owner,
Expand All @@ -49,6 +101,7 @@ export async function run(): Promise<void> {

core.setOutput('pull_request_number', res.number);
core.setOutput('pull_request_created', true);
core.setOutput('pull_request_updated', false);
} catch (error) {
if (error instanceof Error) {
core.setFailed(error.message);
Expand Down
Loading
Loading