From 2f683def3d9664bbfdbe652adc52d876bf0fe7f1 Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Sun, 10 Nov 2024 20:08:14 -0600 Subject: [PATCH 1/4] ci: use actions' token GHA provides a token itself, we don't need to generate one. This is more-fine grained, easier to change if needed, and more secure. I think the existing `contents: read` is sufficient, but it might not be. We'll see. --- .github/workflows/test.yml | 2 ++ turbo.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9b07841f..d0614679 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,3 +94,5 @@ jobs: - name: Run tests run: npm test + env: + AUTH_GITHUB: ${{ secrets.GITHUB_TOKEN }} diff --git a/turbo.json b/turbo.json index 794625a8..0e59f799 100644 --- a/turbo.json +++ b/turbo.json @@ -1,7 +1,7 @@ { "$schema": "https://turbo.build/schema.json", "globalEnv": ["NO_COLOR", "LOCAL_ELM_REVIEW_SRC", "ELM_HOME"], - "globalPassThroughEnv": ["GITHUB_TOKEN", "AUTH_GITHUB"], + "globalPassThroughEnv": ["AUTH_GITHUB"], "tasks": { "elm-format": { "inputs": [ From ef02f3c12b3626a4cd6dc889ded82c106d2065a9 Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:51:30 -0600 Subject: [PATCH 2/4] fix: update github auth to not require a username --- .github/workflows/test.yml | 2 +- lib/flags.js | 2 +- lib/options.js | 23 ++++++++----------- lib/remote-template.js | 13 +++++------ lib/types/options.ts | 3 +-- test/flags.test.js | 2 +- test/run.mjs | 8 +++++++ .../flags/github-auth-bad-argument.txt | 2 +- turbo.json | 4 ++-- 9 files changed, 30 insertions(+), 29 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d0614679..859ff434 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -95,4 +95,4 @@ jobs: - name: Run tests run: npm test env: - AUTH_GITHUB: ${{ secrets.GITHUB_TOKEN }} + AUTH_GITHUB: ${{ github.token }} diff --git a/lib/flags.js b/lib/flags.js index 29266db3..419674d3 100644 --- a/lib/flags.js +++ b/lib/flags.js @@ -23,7 +23,7 @@ const gitHubAuthFlag = { 'Follow https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token to create an API token. The API token needs access to public repositories.', '', 'Then use the flag like this:', - chalk.greenBright(' --github-auth=my-user-name:abcdef01234567890') + chalk.greenBright(' --github-auth=github_pat_abcdef01234567890') ] }; diff --git a/lib/options.js b/lib/options.js index 2fb4534a..e9a0e59f 100644 --- a/lib/options.js +++ b/lib/options.js @@ -181,12 +181,7 @@ try re-running it with ${chalk.cyan('--elmjson ')}.`, const forTests = args['FOR-TESTS']; - // eslint-disable-next-line unicorn/no-useless-undefined -- Bad TS type. - const {gitHubUser = undefined, gitHubPassword = undefined} = args[ - 'github-auth' - ] - ? parseGitHubAuth(subcommand, args['github-auth']) - : {}; + const gitHubPat = parseGitHubAuth(subcommand, args['github-auth']); const localElmReviewSrc = process.env.LOCAL_ELM_REVIEW_SRC; @@ -326,9 +321,8 @@ try re-running it with ${chalk.cyan('--elmjson ')}.`, resultCachePath: (appHash) => path.join(elmStuffFolder(), 'result-cache', appHash), - // GitHub tokens - gitHubUser, - gitHubPassword + // GitHub token + gitHubPat }; } @@ -655,12 +649,14 @@ I recommend you try to gain network access and try again.`, /** * @param {Subcommand | null} subcommand - * @param {string} gitHubAuth - * @returns {{gitHubUser: string | undefined, gitHubPassword: string | undefined} | never} + * @param {string | undefined} gitHubAuth + * @returns {string | undefined | never} */ function parseGitHubAuth(subcommand, gitHubAuth) { + if (gitHubAuth === undefined) return; + const split = gitHubAuth.split(':'); - if (split.length !== 2) { + if (split.length !== 2 && split.length !== 1) { reportErrorAndExit( new ErrorMessage.CustomError( 'INVALID FLAG ARGUMENT', @@ -675,8 +671,7 @@ ${Flags.buildFlag(subcommand, Flags.gitHubAuthFlag)}` ); } - const [gitHubUser, gitHubPassword] = split; - return {gitHubUser, gitHubPassword}; + return split.length === 2 ? split[1] : split[0]; } /** diff --git a/lib/remote-template.js b/lib/remote-template.js index d9285ab6..08eeaa73 100644 --- a/lib/remote-template.js +++ b/lib/remote-template.js @@ -203,7 +203,7 @@ const rateLimitErrorMessage = { message: `It looks like you exceeded the GitHub rate limit by using "--template" too many times, this will likely last for 30 minutes. -In the meantime, you can use \`--github-auth your-github-username:your-api-token\`. +In the meantime, you can use \`--github-auth=your-api-token\`. Follow this guide: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token to create an API token, and give it access to public repositories. To avoid this problem and to make the review process faster, consider setting up @@ -358,12 +358,11 @@ async function downloadFile(url, dest) { * @returns {Promise} */ async function makeGitHubApiRequest(options, url, handleNotFound) { - /** @type {OptionsOfJSONResponseBody}} */ - const parameters = {responseType: 'json'}; - if (options.gitHubUser && options.gitHubPassword) { - parameters.username = options.gitHubUser; - parameters.password = options.gitHubPassword; - } + /** @type {OptionsOfJSONResponseBody} */ + const parameters = { + responseType: 'json', + headers: {Authorization: `BEARER: ${options.gitHubPat}`} + }; Debug.log(`Making API request to GitHub: ${url}`); try { diff --git a/lib/types/options.ts b/lib/types/options.ts index c3e748d0..c24d5ff6 100644 --- a/lib/types/options.ts +++ b/lib/types/options.ts @@ -63,8 +63,7 @@ export type Options = OptionsBase & { fileCachePath: () => Path; resultCachePath: (appHash: AppHash) => Path; - gitHubUser: string | undefined; - gitHubPassword: string | undefined; + gitHubPat?: string | undefined; }; export type ReviewOptions = Options & { diff --git a/test/flags.test.js b/test/flags.test.js index becd0851..54522a26 100644 --- a/test/flags.test.js +++ b/test/flags.test.js @@ -96,7 +96,7 @@ test('Running new-package --compiler without an argument', async () => { }); test('Running --github-auth with a bad value', async () => { - const output = await TestCli.runAndExpectError(['--github-auth=bad']); + const output = await TestCli.runAndExpectError(['--github-auth=::']); expect(output).toMatchFile(testName('github-auth-bad-argument')); }); diff --git a/test/run.mjs b/test/run.mjs index d25c1eed..5bbc19a1 100755 --- a/test/run.mjs +++ b/test/run.mjs @@ -1,4 +1,12 @@ #!/bin/node + +/* + * If you get errors like "rate limit exceeded", + * you can run these tests with `AUTH_GITHUB=token`. + * Follow this guide: to create an API token, + * and give it access to public repositories. + */ + /* eslint n/no-process-exit: "off" -- WIP */ import * as fsp from 'node:fs/promises'; import * as path from 'node:path'; diff --git a/test/snapshots/flags/github-auth-bad-argument.txt b/test/snapshots/flags/github-auth-bad-argument.txt index 153b9c1a..c9e3882d 100644 --- a/test/snapshots/flags/github-auth-bad-argument.txt +++ b/test/snapshots/flags/github-auth-bad-argument.txt @@ -9,5 +9,5 @@ Here is the documentation for this flag: Follow https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token to create an API token. The API token needs access to public repositories. Then use the flag like this: - --github-auth=my-user-name:abcdef01234567890 + --github-auth=github_pat_abcdef01234567890 diff --git a/turbo.json b/turbo.json index 0e59f799..e0d276e5 100644 --- a/turbo.json +++ b/turbo.json @@ -1,7 +1,7 @@ { "$schema": "https://turbo.build/schema.json", - "globalEnv": ["NO_COLOR", "LOCAL_ELM_REVIEW_SRC", "ELM_HOME"], - "globalPassThroughEnv": ["AUTH_GITHUB"], + "globalEnv": ["NO_COLOR", "LOCAL_ELM_REVIEW_SRC"], + "globalPassThroughEnv": ["AUTH_GITHUB", "ELM_HOME"], "tasks": { "elm-format": { "inputs": [ From 9442d309f27cce938f32ef79d5fcbc28eebb1a57 Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:17:32 -0600 Subject: [PATCH 3/4] feat: enhance rate limit error * Don't tell authenticated users to log in. * Tell users when the rate limit expires. --- lib/remote-template.js | 37 +++++++++++++------ .../flags/github-auth-bad-argument.txt | 2 +- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/remote-template.js b/lib/remote-template.js index 08eeaa73..2c56efbd 100644 --- a/lib/remote-template.js +++ b/lib/remote-template.js @@ -197,21 +197,31 @@ ${error.message}` } } -/** @type {ErrorMessageInfo} */ -const rateLimitErrorMessage = { - title: 'GITHUB RATE LIMIT EXCEEDED', - message: `It looks like you exceeded the GitHub rate limit by using "--template" too many -times, this will likely last for 30 minutes. - +/** + * @param {boolean} hasPat + * @param {string} resetTime + * @returns {ErrorMessageInfo} + */ +function rateLimitErrorMessage(hasPat, resetTime) { + return { + title: 'GITHUB RATE LIMIT EXCEEDED', + message: `It looks like you exceeded the GitHub rate limit by using "--template" too many +times, this will likely last until ${resetTime}. +${ + hasPat + ? '' + : ` In the meantime, you can use \`--github-auth=your-api-token\`. -Follow this guide: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token to create an API token, and give it access to public repositories. +Follow this guide: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token to create an API token, and give it access to public repositories.` +} To avoid this problem and to make the review process faster, consider setting up elm-review in your project: elm-review init elm-review init --template ` -}; + }; +} /** * @param {string} repoName @@ -380,11 +390,14 @@ async function makeGitHubApiRequest(options, url, handleNotFound) { Debug.log(`# body:\n${JSON.stringify(error.response.body, null, 4)}`); if (error.name === 'HTTPError') { switch (error.response.statusCode) { + case 429: case 403: { - throw new ErrorMessage.CustomError( - rateLimitErrorMessage.title, - rateLimitErrorMessage.message - ); + const hasPat = options.gitHubPat !== undefined; + const rateLimitReset = error.response.headers['x-ratelimit-reset']; + const resetTime = new Date(rateLimitReset * 1000).toLocaleString(); + const {title, message} = rateLimitErrorMessage(hasPat, resetTime); + + throw new ErrorMessage.CustomError(title, message); } case 404: diff --git a/test/snapshots/flags/github-auth-bad-argument.txt b/test/snapshots/flags/github-auth-bad-argument.txt index c9e3882d..16d09dd6 100644 --- a/test/snapshots/flags/github-auth-bad-argument.txt +++ b/test/snapshots/flags/github-auth-bad-argument.txt @@ -1,6 +1,6 @@ -- INVALID FLAG ARGUMENT ------------------------------------------------------- -The value bad passed to --github-auth is not a valid one. +The value :: passed to --github-auth is not a valid one. Here is the documentation for this flag: From 3093815ba7ce6c01ddaf56b78fefe3d60157edc6 Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:34:01 -0600 Subject: [PATCH 4/4] feat: set gh rest api version --- lib/remote-template.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/remote-template.js b/lib/remote-template.js index 2c56efbd..8f23744d 100644 --- a/lib/remote-template.js +++ b/lib/remote-template.js @@ -371,7 +371,10 @@ async function makeGitHubApiRequest(options, url, handleNotFound) { /** @type {OptionsOfJSONResponseBody} */ const parameters = { responseType: 'json', - headers: {Authorization: `BEARER: ${options.gitHubPat}`} + headers: { + Authorization: `BEARER: ${options.gitHubPat}`, + 'X-GitHub-Api-Version': '2022-11-28' + } }; Debug.log(`Making API request to GitHub: ${url}`);