1
0
mirror of https://github.com/actions/checkout.git synced 2026-07-02 18:33:48 +08:00

Compare commits

...

9 Commits

Author SHA1 Message Date
Michi Mutsuzaki
5da1eb48b5 Merge e832aee124 into 85e6279cec 2025-01-22 15:44:57 +03:00
Josh Gross
85e6279cec Adjust positioning of user email note and permissions heading (#2044) 2025-01-16 15:56:18 -05:00
Ben Wells
009b9ae9e4 Documentation update - add recommended permissions to Readme (#2043)
* Update README.md

* Update README.md

Co-authored-by: Josh Gross <joshmgross@github.com>

---------

Co-authored-by: Josh Gross <joshmgross@github.com>
2025-01-16 14:14:48 -05:00
Mohammad Ismail
cbb722410c Update README.md (#1977) 2024-11-14 10:41:00 -05:00
The web walker
3b9b8c884f docs: update README.md (#1971)
Add a scenario where it is necessary to push a commit to a pull request.
2024-11-08 10:32:54 -05:00
John Wesley Walker III
11bd71901b Prepare 4.2.2 Release (#1953)
* Prepare 4.2.2 Release

---------

Co-authored-by: Josh Gross <joshmgross@github.com>
2024-10-23 16:24:28 +02:00
John Wesley Walker III
e3d2460bbb Expand unit test coverage (#1946) 2024-10-23 15:59:08 +02:00
John Wesley Walker III
163217dfcd url-helper.ts now leverages well-known environment variables. (#1941)
* `utl-helper.ts` now leverages well-known environment variables.

---------
Co-authored-by: Erez Testiler <easyt@github.com>
2024-10-18 10:07:17 +02:00
Michi Mutsuzaki
e832aee124 Change the default value of persist-credentials to false
Change the default value of persist-credentials setting from true to
false to reduce the risk of unintentionally exposing the GITHUB_TOKEN
secret.

Fixes: #485

Signed-off-by: Michi Mutsuzaki <michi@isovalent.com>
2024-04-20 23:37:24 +00:00
8 changed files with 225 additions and 31 deletions

View File

@@ -1,5 +1,9 @@
# Changelog # 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 ## 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 * Check out other refs/* by commit if provided, fall back to ref by @orhantoy in https://github.com/actions/checkout/pull/1924

View File

@@ -6,7 +6,7 @@ This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workfl
Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth: 0` to fetch all history for all branches and tags. Refer [here](https://docs.github.com/actions/using-workflows/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events. Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth: 0` to fetch all history for all branches and tags. Refer [here](https://docs.github.com/actions/using-workflows/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events.
The auth token is persisted in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup. Set `persist-credentials: false` to opt-out. Set `persist-credentials: true` to opt-in to persist the auth token in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup.
When Git 2.18 or higher is not in your PATH, falls back to the REST API to download the files. When Git 2.18 or higher is not in your PATH, falls back to the REST API to download the files.
@@ -68,7 +68,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
ssh-user: '' ssh-user: ''
# Whether to configure the token or SSH key with the local git config # Whether to configure the token or SSH key with the local git config
# Default: true # Default: false
persist-credentials: '' persist-credentials: ''
# Relative path under $GITHUB_WORKSPACE to place the repository # Relative path under $GITHUB_WORKSPACE to place the repository
@@ -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 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) - [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 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 ## Fetch only the root files
@@ -211,7 +212,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
repository: my-org/my-tools repository: my-org/my-tools
path: 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) ## Checkout multiple repos (nested)
@@ -225,7 +226,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
repository: my-org/my-tools repository: my-org/my-tools
path: 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) ## Checkout multiple repos (private)
@@ -288,6 +289,40 @@ 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 *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 doesnt 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
# Recommended permissions
When using the `checkout` action in your GitHub Actions workflow, it is recommended to set the following `GITHUB_TOKEN` permissions to ensure proper functionality, unless alternative auth is provided via the `token` or `ssh-key` inputs:
```yaml
permissions:
contents: read
```
# License # License
The scripts and documentation in this project are released under the [MIT License](LICENSE) The scripts and documentation in this project are released under the [MIT License](LICENSE)

View File

@@ -0,0 +1,92 @@
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', () => {
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', () => {
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')
})
})

View File

@@ -51,7 +51,7 @@ inputs:
default: git default: git
persist-credentials: persist-credentials:
description: 'Whether to configure the token or SSH key with the local git config' description: 'Whether to configure the token or SSH key with the local git config'
default: true default: false
path: path:
description: 'Relative path under $GITHUB_WORKSPACE to place the repository' description: 'Relative path under $GITHUB_WORKSPACE to place the repository'
clean: clean:

50
dist/index.js vendored
View File

@@ -2454,22 +2454,50 @@ function getFetchUrl(settings) {
return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`; return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`;
} }
function getServerUrl(url) { function getServerUrl(url) {
let urlValue = url && url.trim().length > 0 let resolvedUrl = process.env['GITHUB_SERVER_URL'] || 'https://github.com';
? url if (hasContent(url, WhitespaceMode.Trim)) {
: process.env['GITHUB_SERVER_URL'] || 'https://github.com'; resolvedUrl = url;
return new url_1.URL(urlValue); }
return new url_1.URL(resolvedUrl);
} }
function getServerApiUrl(url) { function getServerApiUrl(url) {
let apiUrl = 'https://api.github.com'; if (hasContent(url, WhitespaceMode.Trim)) {
if (isGhes(url)) { let serverUrl = getServerUrl(url);
const serverUrl = getServerUrl(url); if (isGhes(url)) {
apiUrl = new url_1.URL(`${serverUrl.origin}/api/v3`).toString(); 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) { function isGhes(url) {
const ghUrl = getServerUrl(url); const ghUrl = new url_1.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, 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;
} }

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "checkout", "name": "checkout",
"version": "4.2.1", "version": "4.2.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "checkout", "name": "checkout",
"version": "4.2.1", "version": "4.2.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "checkout", "name": "checkout",
"version": "4.2.1", "version": "4.2.2",
"description": "checkout action", "description": "checkout action",
"main": "lib/main.js", "main": "lib/main.js",
"scripts": { "scripts": {

View File

@@ -21,26 +21,61 @@ export function getFetchUrl(settings: IGitSourceSettings): string {
} }
export function getServerUrl(url?: string): URL { export function getServerUrl(url?: string): URL {
let urlValue = let resolvedUrl = process.env['GITHUB_SERVER_URL'] || 'https://github.com'
url && url.trim().length > 0 if (hasContent(url, WhitespaceMode.Trim)) {
? url resolvedUrl = url!
: process.env['GITHUB_SERVER_URL'] || 'https://github.com' }
return new URL(urlValue)
return new URL(resolvedUrl)
} }
export function getServerApiUrl(url?: string): string { 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)) { return pruneSuffix(serverUrl.toString(), '/')
const serverUrl = getServerUrl(url)
apiUrl = new URL(`${serverUrl.origin}/api/v3`).toString()
} }
return apiUrl return process.env['GITHUB_API_URL'] || 'https://api.github.com'
} }
export function isGhes(url?: string): boolean { 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
} }