Skip to content

Commit

Permalink
CI: Merge to main: Use separate tokens for creating a branch
Browse files Browse the repository at this point in the history
The `RUN_WORKFLOW_FROM_WORKFLOW` secret does not allow for writing to
branches; use a separate GitHub-provided token for that.

Signed-off-by: Mark Yen <mark.yen@suse.com>
  • Loading branch information
mook-as committed May 31, 2024
1 parent 97105e9 commit 3cd7d7d
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 15 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/release-merge-to-main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
if: needs.check-for-token.outputs.has-token == 'true'
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
Expand All @@ -49,4 +49,5 @@ jobs:

- run: node scripts/ts-wrapper.js scripts/release-merge-to-main.ts
env:
GITHUB_TOKEN: ${{ secrets.RUN_WORKFLOW_FROM_WORKFLOW }}
GITHUB_WRITE_TOKEN: ${{ github.token }}
GITHUB_PR_TOKEN: ${{ secrets.RUN_WORKFLOW_FROM_WORKFLOW }}
22 changes: 16 additions & 6 deletions scripts/lib/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,24 @@ export type HasUnreleasedChangesResult = {latestReleaseTag: string, hasUnrelease
export type GitHubRelease = Awaited<ReturnType<Octokit['rest']['repos']['listReleases']>>['data'][0];

let _octokit: Octokit | undefined;
let _octokitAuthToken: string | undefined;

export function getOctokit() {
if (_octokit) {
return _octokit;
}
const personalAccessToken = process.env.GITHUB_TOKEN;
/**
* Get a cached instance of Octokit, or create a new one as needed. If the given token does not
* match the one used to create the cached instance, a new one is created (and cached).
* @param personalAccessToken Optional GitHub personal access token; defaults to GITHUB_TOKEN.
*/
export function getOctokit(personalAccessToken?: string): Octokit {
personalAccessToken ||= process.env.GITHUB_TOKEN;

if (!personalAccessToken) {
throw new Error('Please set GITHUB_TOKEN to a PAT to check versions of github-based dependencies.');
}

if (_octokit && _octokitAuthToken === personalAccessToken) {
return _octokit;
}

function makeLimitHandler(type: string, maxRetries: number): NonNullable<ThrottlingOptions['onSecondaryRateLimit']> {
return (retryAfter, options, octokit, retryCount) => {
function getOpt(prop: string) {
Expand All @@ -132,13 +139,16 @@ export function getOctokit() {
};
}

return new Octokit({
_octokit = new Octokit({
auth: personalAccessToken,
throttle: {
onRateLimit: makeLimitHandler('primary', 3),
onSecondaryRateLimit: makeLimitHandler('secondary', 3),
},
});
_octokitAuthToken = personalAccessToken;

return _octokit;
}

export type IssueOrPullRequest = Awaited<ReturnType<Octokit['rest']['search']['issuesAndPullRequests']>>['data']['items'][0];
Expand Down
21 changes: 14 additions & 7 deletions scripts/release-merge-to-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
// Environment:
// GITHUB_REPOSITORY, GITHUB_EVENT_PATH, and others
// See https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
// GITHUB_TOKEN: GitHub authorization token.
// Must have write permissions for `actions`, `contents`, `pull_requests`.
// GITHUB_WRITE_TOKEN: GitHub authorization token for creating a branch.
// Must have `contents:write` permissions.
// GITHUB_PR_TOKEN: GitHub authorization token.
// Must have write permissions for `actions` and `pull_requests`.

import fs from 'fs';

Expand All @@ -19,7 +21,8 @@ import { getOctokit } from './lib/dependencies';
type EnvironmentVariableName =
'GITHUB_REPOSITORY' |
'GITHUB_EVENT_PATH' |
'GITHUB_TOKEN';
'GITHUB_WRITE_TOKEN' |
'GITHUB_PR_TOKEN';

/**
* Partial contents of the event payload, for a release event.
Expand Down Expand Up @@ -59,7 +62,7 @@ function getEnv(variable: EnvironmentVariableName): string {
*/
async function ensureBranch(owner: string, repo: string, branchName: string, tagName: string): Promise<void> {
const ref = `heads/${ branchName }`;
const { git } = getOctokit().rest;
const { git } = getOctokit(getEnv('GITHUB_WRITE_TOKEN')).rest;
const { data: tagRef } = await git.getRef({
owner, repo, ref: `tags/${ tagName }`,
});
Expand All @@ -77,11 +80,15 @@ async function ensureBranch(owner: string, repo: string, branchName: string, tag
await git.updateRef({
owner, repo, ref, sha,
});
} else {
console.log(`Branch ${ owner }/${ repo }/${ ref } is already up-to-date.`);
}
} catch (ex) {
if (!(ex instanceof RequestError) || ex.status !== 404) {
throw ex;
}
console.log(`Creating new branch ${ owner }/${ repo }/${ ref }` +
`at ${ sha }`);
// Branch does not exist; create it.
await git.createRef({
// Only this API takes a `refs/` prefix; get & update omit it.
Expand All @@ -100,14 +107,14 @@ async function ensureBranch(owner: string, repo: string, branchName: string, tag
async function findExisting(owner: string, repo: string, branch: string) {
const fullRepo = `${ owner }/${ repo }`;
const query = `type:pr is:open repo:${ fullRepo } base:${ base } head:${ branch } sort:updated`;
const result = await getOctokit().rest.search.issuesAndPullRequests({ q: query });
const result = await getOctokit(getEnv('GITHUB_WRITE_TOKEN')).rest.search.issuesAndPullRequests({ q: query });

for (const item of result.data.items) {
// Must be an open item, and that item must be a pull request.
if (item.state !== 'open' || !item.pull_request) {
continue;
}
const { data: pr } = await getOctokit().rest.pulls.get({
const { data: pr } = await getOctokit(getEnv('GITHUB_PR_TOKEN')).rest.pulls.get({
owner, repo, pull_number: item.number,
});

Expand Down Expand Up @@ -169,7 +176,7 @@ async function findExisting(owner: string, repo: string, branch: string) {
console.log(`Creating new PR on ${ owner }/${ repo }: ${ base } <- ${ branchName }`);
await ensureBranch(owner, repo, branchName, tagName);
const title = `Merge release ${ tagName } back into ${ base }`;
const { data: item } = await getOctokit().rest.pulls.create({
const { data: item } = await getOctokit(getEnv('GITHUB_PR_TOKEN')).rest.pulls.create({
owner, repo, title, head: branchName, base, maintainer_can_modify: true,
});

Expand Down

0 comments on commit 3cd7d7d

Please sign in to comment.