From 02ade5d400ff291405e3aa22ada1a372ec60a58e Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 14 Feb 2022 23:18:53 +0000 Subject: [PATCH 1/6] Don't overwrite annotated tags with commit object When checking out a repository with full history, a full clone is done and then the ref is finally updated to point to the commit that caused the workflow to be run. Normally, this is a good protection against someone pushing to the repository twice in short succession, but it causes problems with annotated tags. Specifically, because the entry in refs/tags is set to the commit hash, if an annotated tag was used, the tag is turned merely into a lightweight one, which breaks `git describe`. Every other tag in the repository will continue to remain a valid annotated tag except the one for which the workflow was invoked, which is not what the user expected. Let's work around this by not performing a fetch if what we're fetching is a tag. Technically, annotated tags can be anywhere in the hierarchy at any ref, but this should work as a suitable heuristic for now. Note that the proper solution would be to expose the revision of the actual object and check against that instead of the commit, but it doesn't presently appear that that information is exposed. Also, we explicitly do not case-fold since Git refs are case sensitive. --- src/git-source-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts index 42a12e0..2b35cd1 100644 --- a/src/git-source-provider.ts +++ b/src/git-source-provider.ts @@ -135,7 +135,7 @@ export async function getSource(settings: IGitSourceSettings): Promise { // When all history is fetched, the ref we're interested in may have moved to a different // commit (push or force push). If so, fetch again with a targeted refspec. - if (!(await refHelper.testRef(git, settings.ref, settings.commit))) { + if (!settings.refs.startsWith("refs/tags") && !(await refHelper.testRef(git, settings.ref, settings.commit))) { refSpec = refHelper.getRefSpec(settings.ref, settings.commit) await git.fetch(refSpec) } From 163217dfcd28294438ea1c1c149cfaf66eec283e Mon Sep 17 00:00:00 2001 From: John Wesley Walker III <81404201+jww3@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:07:17 +0200 Subject: [PATCH 2/6] `url-helper.ts` now leverages well-known environment variables. (#1941) * `utl-helper.ts` now leverages well-known environment variables. --------- Co-authored-by: Erez Testiler --- __test__/url-helper.test.ts | 55 ++++++++++++++++++++++++++++++++++ dist/index.js | 50 ++++++++++++++++++++++++------- src/url-helper.ts | 59 +++++++++++++++++++++++++++++-------- 3 files changed, 141 insertions(+), 23 deletions(-) create mode 100644 __test__/url-helper.test.ts diff --git a/__test__/url-helper.test.ts b/__test__/url-helper.test.ts new file mode 100644 index 0000000..27f6606 --- /dev/null +++ b/__test__/url-helper.test.ts @@ -0,0 +1,55 @@ +import * as urlHelper from '../src/url-helper' + +describe('getServerUrl tests', () => { + it('basics', async () => { + // Note that URL::toString will append a trailing / when passed just a domain name ... + expect(urlHelper.getServerUrl().toString()).toBe('https://github.com/') + expect(urlHelper.getServerUrl(' ').toString()).toBe('https://github.com/') + expect(urlHelper.getServerUrl(' ').toString()).toBe('https://github.com/') + expect(urlHelper.getServerUrl('http://contoso.com').toString()).toBe( + 'http://contoso.com/' + ) + expect(urlHelper.getServerUrl('https://contoso.com').toString()).toBe( + 'https://contoso.com/' + ) + expect(urlHelper.getServerUrl('https://contoso.com/').toString()).toBe( + 'https://contoso.com/' + ) + + // ... but can't make that same assumption when passed an URL that includes some deeper path. + expect(urlHelper.getServerUrl('https://contoso.com/a/b').toString()).toBe( + 'https://contoso.com/a/b' + ) + }) +}) + +describe('isGhes tests', () => { + it('basics', async () => { + expect(urlHelper.isGhes()).toBeFalsy() + expect(urlHelper.isGhes('https://github.com')).toBeFalsy() + expect(urlHelper.isGhes('https://contoso.ghe.com')).toBeFalsy() + expect(urlHelper.isGhes('https://test.github.localhost')).toBeFalsy() + expect(urlHelper.isGhes('https://src.onpremise.fabrikam.com')).toBeTruthy() + }) +}) + +describe('getServerApiUrl tests', () => { + it('basics', async () => { + expect(urlHelper.getServerApiUrl()).toBe('https://api.github.com') + expect(urlHelper.getServerApiUrl('https://github.com')).toBe( + 'https://api.github.com' + ) + expect(urlHelper.getServerApiUrl('https://GitHub.com')).toBe( + 'https://api.github.com' + ) + expect(urlHelper.getServerApiUrl('https://contoso.ghe.com')).toBe( + 'https://api.contoso.ghe.com' + ) + expect(urlHelper.getServerApiUrl('https://fabrikam.GHE.COM')).toBe( + 'https://api.fabrikam.ghe.com' + ) + expect( + urlHelper.getServerApiUrl('https://src.onpremise.fabrikam.com') + ).toBe('https://src.onpremise.fabrikam.com/api/v3') + }) +}) diff --git a/dist/index.js b/dist/index.js index d86415e..b0db713 100644 --- a/dist/index.js +++ b/dist/index.js @@ -2454,22 +2454,50 @@ function getFetchUrl(settings) { return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`; } function getServerUrl(url) { - let urlValue = url && url.trim().length > 0 - ? url - : process.env['GITHUB_SERVER_URL'] || 'https://github.com'; - return new url_1.URL(urlValue); + let resolvedUrl = process.env['GITHUB_SERVER_URL'] || 'https://github.com'; + if (hasContent(url, WhitespaceMode.Trim)) { + resolvedUrl = url; + } + return new url_1.URL(resolvedUrl); } function getServerApiUrl(url) { - let apiUrl = 'https://api.github.com'; - if (isGhes(url)) { - const serverUrl = getServerUrl(url); - apiUrl = new url_1.URL(`${serverUrl.origin}/api/v3`).toString(); + if (hasContent(url, WhitespaceMode.Trim)) { + let serverUrl = getServerUrl(url); + if (isGhes(url)) { + serverUrl.pathname = 'api/v3'; + } + else { + serverUrl.hostname = 'api.' + serverUrl.hostname; + } + return pruneSuffix(serverUrl.toString(), '/'); } - return apiUrl; + return process.env['GITHUB_API_URL'] || 'https://api.github.com'; } function isGhes(url) { - const ghUrl = getServerUrl(url); - return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; + const ghUrl = new url_1.URL(url || process.env['GITHUB_SERVER_URL'] || 'https://github.com'); + const hostname = ghUrl.hostname.trimEnd().toUpperCase(); + const isGitHubHost = hostname === 'GITHUB.COM'; + const isGitHubEnterpriseCloudHost = hostname.endsWith('.GHE.COM'); + const isLocalHost = hostname.endsWith('.LOCALHOST'); + return !isGitHubHost && !isGitHubEnterpriseCloudHost && !isLocalHost; +} +function pruneSuffix(text, suffix) { + if (hasContent(suffix, WhitespaceMode.Preserve) && (text === null || text === void 0 ? void 0 : text.endsWith(suffix))) { + return text.substring(0, text.length - suffix.length); + } + return text; +} +var WhitespaceMode; +(function (WhitespaceMode) { + WhitespaceMode[WhitespaceMode["Trim"] = 0] = "Trim"; + WhitespaceMode[WhitespaceMode["Preserve"] = 1] = "Preserve"; +})(WhitespaceMode || (WhitespaceMode = {})); +function hasContent(text, whitespaceMode) { + let refinedText = text !== null && text !== void 0 ? text : ''; + if (whitespaceMode == WhitespaceMode.Trim) { + refinedText = refinedText.trim(); + } + return refinedText.length > 0; } diff --git a/src/url-helper.ts b/src/url-helper.ts index 64ecbf3..17a0842 100644 --- a/src/url-helper.ts +++ b/src/url-helper.ts @@ -21,26 +21,61 @@ export function getFetchUrl(settings: IGitSourceSettings): string { } export function getServerUrl(url?: string): URL { - let urlValue = - url && url.trim().length > 0 - ? url - : process.env['GITHUB_SERVER_URL'] || 'https://github.com' - return new URL(urlValue) + let resolvedUrl = process.env['GITHUB_SERVER_URL'] || 'https://github.com' + if (hasContent(url, WhitespaceMode.Trim)) { + resolvedUrl = url! + } + + return new URL(resolvedUrl) } export function getServerApiUrl(url?: string): string { - let apiUrl = 'https://api.github.com' + if (hasContent(url, WhitespaceMode.Trim)) { + let serverUrl = getServerUrl(url) + if (isGhes(url)) { + serverUrl.pathname = 'api/v3' + } else { + serverUrl.hostname = 'api.' + serverUrl.hostname + } - if (isGhes(url)) { - const serverUrl = getServerUrl(url) - apiUrl = new URL(`${serverUrl.origin}/api/v3`).toString() + return pruneSuffix(serverUrl.toString(), '/') } - return apiUrl + return process.env['GITHUB_API_URL'] || 'https://api.github.com' } export function isGhes(url?: string): boolean { - const ghUrl = getServerUrl(url) + const ghUrl = new URL( + url || process.env['GITHUB_SERVER_URL'] || 'https://github.com' + ) - return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM' + const hostname = ghUrl.hostname.trimEnd().toUpperCase() + const isGitHubHost = hostname === 'GITHUB.COM' + const isGitHubEnterpriseCloudHost = hostname.endsWith('.GHE.COM') + const isLocalHost = hostname.endsWith('.LOCALHOST') + + return !isGitHubHost && !isGitHubEnterpriseCloudHost && !isLocalHost +} + +function pruneSuffix(text: string, suffix: string) { + if (hasContent(suffix, WhitespaceMode.Preserve) && text?.endsWith(suffix)) { + return text.substring(0, text.length - suffix.length) + } + return text +} + +enum WhitespaceMode { + Trim, + Preserve +} + +function hasContent( + text: string | undefined, + whitespaceMode: WhitespaceMode +): boolean { + let refinedText = text ?? '' + if (whitespaceMode == WhitespaceMode.Trim) { + refinedText = refinedText.trim() + } + return refinedText.length > 0 } From e3d2460bbb42d7710191569f88069044cfb9d8cf Mon Sep 17 00:00:00 2001 From: John Wesley Walker III <81404201+jww3@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:59:08 +0200 Subject: [PATCH 3/6] Expand unit test coverage (#1946) --- __test__/url-helper.test.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/__test__/url-helper.test.ts b/__test__/url-helper.test.ts index 27f6606..57cb28f 100644 --- a/__test__/url-helper.test.ts +++ b/__test__/url-helper.test.ts @@ -24,13 +24,50 @@ describe('getServerUrl tests', () => { }) describe('isGhes tests', () => { + const pristineEnv = process.env + + beforeEach(() => { + jest.resetModules() + process.env = {...pristineEnv} + }) + + afterAll(() => { + process.env = pristineEnv + }) + it('basics', async () => { + delete process.env['GITHUB_SERVER_URL'] expect(urlHelper.isGhes()).toBeFalsy() expect(urlHelper.isGhes('https://github.com')).toBeFalsy() expect(urlHelper.isGhes('https://contoso.ghe.com')).toBeFalsy() expect(urlHelper.isGhes('https://test.github.localhost')).toBeFalsy() expect(urlHelper.isGhes('https://src.onpremise.fabrikam.com')).toBeTruthy() }) + + it('returns false when the GITHUB_SERVER_URL environment variable is not defined', async () => { + delete process.env['GITHUB_SERVER_URL'] + expect(urlHelper.isGhes()).toBeFalsy() + }) + + it('returns false when the GITHUB_SERVER_URL environment variable is set to github.com', async () => { + process.env['GITHUB_SERVER_URL'] = 'https://github.com' + expect(urlHelper.isGhes()).toBeFalsy() + }) + + it('returns false when the GITHUB_SERVER_URL environment variable is set to a GitHub Enterprise Cloud-style URL', async () => { + process.env['GITHUB_SERVER_URL'] = 'https://contoso.ghe.com' + expect(urlHelper.isGhes()).toBeFalsy() + }) + + it('returns false when the GITHUB_SERVER_URL environment variable has a .localhost suffix', async () => { + process.env['GITHUB_SERVER_URL'] = 'https://mock-github.localhost' + expect(urlHelper.isGhes()).toBeFalsy() + }) + + it('returns true when the GITHUB_SERVER_URL environment variable is set to some other URL', async () => { + process.env['GITHUB_SERVER_URL'] = 'https://src.onpremise.fabrikam.com' + expect(urlHelper.isGhes()).toBeTruthy() + }) }) describe('getServerApiUrl tests', () => { From 11bd71901bbe5b1630ceea73d27597364c9af683 Mon Sep 17 00:00:00 2001 From: John Wesley Walker III <81404201+jww3@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:24:28 +0200 Subject: [PATCH 4/6] Prepare 4.2.2 Release (#1953) * Prepare 4.2.2 Release --------- Co-authored-by: Josh Gross --- CHANGELOG.md | 4 ++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b127df3..a96c76e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v4.2.2 +* `url-helper.ts` now leverages well-known environment variables by @jww3 in https://github.com/actions/checkout/pull/1941 +* Expand unit test coverage for `isGhes` by @jww3 in https://github.com/actions/checkout/pull/1946 + ## v4.2.1 * Check out other refs/* by commit if provided, fall back to ref by @orhantoy in https://github.com/actions/checkout/pull/1924 diff --git a/package-lock.json b/package-lock.json index 281b2ea..25753a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "checkout", - "version": "4.2.1", + "version": "4.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "checkout", - "version": "4.2.1", + "version": "4.2.2", "license": "MIT", "dependencies": { "@actions/core": "^1.10.1", diff --git a/package.json b/package.json index e145004..5661d70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "checkout", - "version": "4.2.1", + "version": "4.2.2", "description": "checkout action", "main": "lib/main.js", "scripts": { From 3b9b8c884f6b4bb4d5be2779c26374abadae0871 Mon Sep 17 00:00:00 2001 From: The web walker Date: Fri, 8 Nov 2024 23:32:54 +0800 Subject: [PATCH 5/6] docs: update README.md (#1971) Add a scenario where it is necessary to push a commit to a pull request. --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index e1ea032..a43e887 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ - [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit) - [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event) - [Push a commit using the built-in token](#Push-a-commit-using-the-built-in-token) +- [Push a commit to a PR using the built-in token](#Push-a-commit-to-a-PR-using-the-built-in-token) ## Fetch only the root files @@ -288,6 +289,31 @@ jobs: ``` *NOTE:* The user email is `{user.id}+{user.login}@users.noreply.github.com`. See users API: https://api.github.com/users/github-actions%5Bbot%5D +## Push a commit to a PR using the built-in token + +In a pull request trigger, `ref` is required as GitHub Actions checks out in detached HEAD mode, meaning it doesn’t check out your branch by default. + +```yaml +on: pull_request +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + - run: | + date > generated.txt + # Note: the following account information will not work on GHES + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add . + git commit -m "generated" + git push +``` +*NOTE:* The user email is `{user.id}+{user.login}@users.noreply.github.com`. See users API: https://api.github.com/users/github-actions%5Bbot%5D + + # License The scripts and documentation in this project are released under the [MIT License](LICENSE) From cbb722410c2e876e24abbe8de2cc27693e501dcb Mon Sep 17 00:00:00 2001 From: Mohammad Ismail <96207520+mouismail@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:41:00 +0100 Subject: [PATCH 6/6] Update README.md (#1977) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a43e887..b0f6224 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ repository: my-org/my-tools path: my-tools ``` -> - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private) +> - If your secondary repository is private or internal you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private) ## Checkout multiple repos (nested) @@ -226,7 +226,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ repository: my-org/my-tools path: my-tools ``` -> - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private) +> - If your secondary repository is private or internal you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private) ## Checkout multiple repos (private)