Compare commits

..

3 Commits

Author SHA1 Message Date
7884fcad6b Prepare changelog for v2.4.2. (#778) 2022-04-21 10:45:29 -04:00
f67ee5d622 Add set-safe-directory input to allow customers to take control. (#770) (#776)
* Add set-safe-directory input to allow customers to take control.
2022-04-21 10:12:11 -04:00
f25a3a9f25 Safe Directory v2 update (#764)
* set safe directory when running checkout
2022-04-14 12:12:00 -04:00
14 changed files with 395 additions and 193 deletions

View File

@ -24,10 +24,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set Node.js 16.x - name: Set Node.js 12.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 16.x node-version: 12.x
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci

View File

@ -13,7 +13,7 @@ jobs:
steps: steps:
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
with: with:
node-version: 16.x node-version: 12.x
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- run: npm ci - run: npm ci
- run: npm run build - run: npm run build
@ -205,3 +205,41 @@ jobs:
path: basic path: basic
- name: Verify basic - name: Verify basic
run: __test__/verify-basic.sh --archive run: __test__/verify-basic.sh --archive
test-git-container:
runs-on: ubuntu-latest
container: bitnami/git:latest
steps:
# Clone this repo
- name: Checkout
uses: actions/checkout@v3
with:
path: v3
# Basic checkout using git
- name: Checkout basic
uses: ./v3
with:
ref: test-data/v2/basic
- name: Verify basic
run: |
if [ ! -f "./basic-file.txt" ]; then
echo "Expected basic file does not exist"
exit 1
fi
# Verify .git folder
if [ ! -d "./.git" ]; then
echo "Expected ./.git folder to exist"
exit 1
fi
# Verify auth token
git config --global --add safe.directory "*"
git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main
# needed to make checkout post cleanup succeed
- name: Fix Checkout v3
uses: actions/checkout@v3
with:
path: v3

View File

@ -1,5 +1,11 @@
# Changelog # Changelog
## v2.4.2
- [Add input `set-safe-directory`](https://github.com/actions/checkout/pull/776)
## v2.4.1
- [Set the safe directory option on git to prevent git commands failing when running in containers](https://github.com/actions/checkout/pull/762)
## v2.3.1 ## v2.3.1
- [Fix default branch resolution for .wiki and when using SSH](https://github.com/actions/checkout/pull/284) - [Fix default branch resolution for .wiki and when using SSH](https://github.com/actions/checkout/pull/284)

View File

@ -2,7 +2,7 @@
<a href="https://github.com/actions/checkout"><img alt="GitHub Actions status" src="https://github.com/actions/checkout/workflows/test-local/badge.svg"></a> <a href="https://github.com/actions/checkout"><img alt="GitHub Actions status" src="https://github.com/actions/checkout/workflows/test-local/badge.svg"></a>
</p> </p>
# Checkout V3 # Checkout V2
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it. This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
@ -14,14 +14,27 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
# What's new # What's new
- Updated to the node16 runtime by default - Improved performance
- This requires a minimum [Actions Runner](https://github.com/actions/runner/releases/tag/v2.285.0) version of v2.285.0 to run, which is by default available in GHES 3.4 or later. - Fetches only a single commit by default
- Script authenticated git commands
- Auth token persisted in the local git config
- Supports SSH
- Creates a local branch
- No longer detached HEAD when checking out a branch
- Improved layout
- The input `path` is always relative to $GITHUB_WORKSPACE
- Aligns better with container actions, where $GITHUB_WORKSPACE gets mapped in
- Fallback to REST API download
- When Git 2.18 or higher is not in the PATH, the REST API will be used to download the files
- When using a job container, the container's PATH is used
Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous versions.
# Usage # Usage
<!-- start usage --> <!-- start usage -->
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
# Repository name with owner. For example, actions/checkout # Repository name with owner. For example, actions/checkout
# Default: ${{ github.repository }} # Default: ${{ github.repository }}
@ -92,6 +105,11 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
# #
# Default: false # Default: false
submodules: '' submodules: ''
# Add repository path as safe.directory for Git global config by running `git
# config --global --add safe.directory <path>`
# Default: true
set-safe-directory: ''
``` ```
<!-- end usage --> <!-- end usage -->
@ -110,7 +128,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
## Fetch all history for all tags and branches ## Fetch all history for all tags and branches
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
fetch-depth: 0 fetch-depth: 0
``` ```
@ -118,7 +136,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
## Checkout a different branch ## Checkout a different branch
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
ref: my-branch ref: my-branch
``` ```
@ -126,7 +144,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
## Checkout HEAD^ ## Checkout HEAD^
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
fetch-depth: 2 fetch-depth: 2
- run: git checkout HEAD^ - run: git checkout HEAD^
@ -136,12 +154,12 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
```yaml ```yaml
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
path: main path: main
- name: Checkout tools repo - name: Checkout tools repo
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
repository: my-org/my-tools repository: my-org/my-tools
path: my-tools path: my-tools
@ -151,10 +169,10 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
```yaml ```yaml
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Checkout tools repo - name: Checkout tools repo
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
repository: my-org/my-tools repository: my-org/my-tools
path: my-tools path: my-tools
@ -164,12 +182,12 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
```yaml ```yaml
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
path: main path: main
- name: Checkout private tools - name: Checkout private tools
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
repository: my-org/my-private-tools repository: my-org/my-private-tools
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
@ -182,7 +200,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
## Checkout pull request HEAD commit instead of merge commit ## Checkout pull request HEAD commit instead of merge commit
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
``` ```
@ -198,7 +216,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
``` ```
## Push a commit using the built-in token ## Push a commit using the built-in token
@ -209,7 +227,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- run: | - run: |
date > generated.txt date > generated.txt
git config user.name github-actions git config user.name github-actions

View File

@ -643,10 +643,11 @@ describe('git-auth-helper tests', () => {
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0) expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
}) })
const removeGlobalAuth_removesOverride = 'removeGlobalAuth removes override' const removeGlobalConfig_removesOverride =
it(removeGlobalAuth_removesOverride, async () => { 'removeGlobalConfig removes override'
it(removeGlobalConfig_removesOverride, async () => {
// Arrange // Arrange
await setup(removeGlobalAuth_removesOverride) await setup(removeGlobalConfig_removesOverride)
const authHelper = gitAuthHelper.createAuthHelper(git, settings) const authHelper = gitAuthHelper.createAuthHelper(git, settings)
await authHelper.configureAuth() await authHelper.configureAuth()
await authHelper.configureGlobalAuth() await authHelper.configureGlobalAuth()
@ -655,7 +656,7 @@ describe('git-auth-helper tests', () => {
await fs.promises.stat(path.join(git.env['HOME'], '.gitconfig')) await fs.promises.stat(path.join(git.env['HOME'], '.gitconfig'))
// Act // Act
await authHelper.removeGlobalAuth() await authHelper.removeGlobalConfig()
// Assert // Assert
expect(git.env['HOME']).toBeUndefined() expect(git.env['HOME']).toBeUndefined()
@ -776,7 +777,8 @@ async function setup(testName: string): Promise<void> {
sshKey: sshPath ? 'some ssh private key' : '', sshKey: sshPath ? 'some ssh private key' : '',
sshKnownHosts: '', sshKnownHosts: '',
sshStrict: true, sshStrict: true,
workflowOrganizationId: 123456 workflowOrganizationId: 123456,
setSafeDirectory: true
} }
} }

View File

@ -85,6 +85,7 @@ describe('input-helper tests', () => {
expect(settings.repositoryName).toBe('some-repo') expect(settings.repositoryName).toBe('some-repo')
expect(settings.repositoryOwner).toBe('some-owner') expect(settings.repositoryOwner).toBe('some-owner')
expect(settings.repositoryPath).toBe(gitHubWorkspace) expect(settings.repositoryPath).toBe(gitHubWorkspace)
expect(settings.setSafeDirectory).toBe(true)
}) })
it('qualifies ref', async () => { it('qualifies ref', async () => {

View File

@ -68,7 +68,10 @@ inputs:
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
converted to HTTPS. converted to HTTPS.
default: false default: false
set-safe-directory:
description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory <path>`
default: true
runs: runs:
using: node16 using: node12
main: dist/index.js main: dist/index.js
post: dist/index.js post: dist/index.js

200
dist/index.js vendored
View File

@ -3592,7 +3592,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.setSshKnownHostsPath = exports.setSshKeyPath = exports.setRepositoryPath = exports.SshKnownHostsPath = exports.SshKeyPath = exports.RepositoryPath = exports.IsPost = void 0; exports.setSafeDirectory = exports.setSshKnownHostsPath = exports.setSshKeyPath = exports.setRepositoryPath = exports.SshKnownHostsPath = exports.SshKeyPath = exports.PostSetSafeDirectory = exports.RepositoryPath = exports.IsPost = void 0;
const coreCommand = __importStar(__webpack_require__(431)); const coreCommand = __importStar(__webpack_require__(431));
/** /**
* Indicates whether the POST action is running * Indicates whether the POST action is running
@ -3602,6 +3602,10 @@ exports.IsPost = !!process.env['STATE_isPost'];
* The repository path for the POST action. The value is empty during the MAIN action. * The repository path for the POST action. The value is empty during the MAIN action.
*/ */
exports.RepositoryPath = process.env['STATE_repositoryPath'] || ''; exports.RepositoryPath = process.env['STATE_repositoryPath'] || '';
/**
* The set-safe-directory for the POST action. The value is set if input: 'safe-directory' is set during the MAIN action.
*/
exports.PostSetSafeDirectory = process.env['STATE_setSafeDirectory'] === 'true';
/** /**
* The SSH key path for the POST action. The value is empty during the MAIN action. * The SSH key path for the POST action. The value is empty during the MAIN action.
*/ */
@ -3631,6 +3635,13 @@ function setSshKnownHostsPath(sshKnownHostsPath) {
coreCommand.issueCommand('save-state', { name: 'sshKnownHostsPath' }, sshKnownHostsPath); coreCommand.issueCommand('save-state', { name: 'sshKnownHostsPath' }, sshKnownHostsPath);
} }
exports.setSshKnownHostsPath = setSshKnownHostsPath; exports.setSshKnownHostsPath = setSshKnownHostsPath;
/**
* Save the sef-safe-directory input so the POST action can retrieve the value.
*/
function setSafeDirectory() {
coreCommand.issueCommand('save-state', { name: 'setSafeDirectory' }, 'true');
}
exports.setSafeDirectory = setSafeDirectory;
// Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic. // Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic.
// This is necessary since we don't have a separate entry point. // This is necessary since we don't have a separate entry point.
if (!exports.IsPost) { if (!exports.IsPost) {
@ -4229,7 +4240,7 @@ module.exports = require("punycode");
/***/ 215: /***/ 215:
/***/ (function(module) { /***/ (function(module) {
module.exports = {"name":"@octokit/rest","version":"16.43.1","publishConfig":{"access":"public"},"description":"GitHub REST API client for Node.js","keywords":["octokit","github","rest","api-client"],"author":"Gregor Martynus (https://github.com/gr2m)","contributors":[{"name":"Mike de Boer","email":"info@mikedeboer.nl"},{"name":"Fabian Jakobs","email":"fabian@c9.io"},{"name":"Joe Gallo","email":"joe@brassafrax.com"},{"name":"Gregor Martynus","url":"https://github.com/gr2m"}],"repository":"https://github.com/octokit/rest.js","dependencies":{"@octokit/auth-token":"^2.4.0","@octokit/plugin-paginate-rest":"^1.1.1","@octokit/plugin-request-log":"^1.0.0","@octokit/plugin-rest-endpoint-methods":"2.4.0","@octokit/request":"^5.2.0","@octokit/request-error":"^1.0.2","atob-lite":"^2.0.0","before-after-hook":"^2.0.0","btoa-lite":"^1.0.0","deprecation":"^2.0.0","lodash.get":"^4.4.2","lodash.set":"^4.3.2","lodash.uniq":"^4.5.0","octokit-pagination-methods":"^1.1.0","once":"^1.4.0","universal-user-agent":"^4.0.0"},"devDependencies":{"@gimenete/type-writer":"^0.1.3","@octokit/auth":"^1.1.1","@octokit/fixtures-server":"^5.0.6","@octokit/graphql":"^4.2.0","@types/node":"^13.1.0","bundlesize":"^0.18.0","chai":"^4.1.2","compression-webpack-plugin":"^3.1.0","cypress":"^3.0.0","glob":"^7.1.2","http-proxy-agent":"^4.0.0","lodash.camelcase":"^4.3.0","lodash.merge":"^4.6.1","lodash.upperfirst":"^4.3.1","lolex":"^5.1.2","mkdirp":"^1.0.0","mocha":"^7.0.1","mustache":"^4.0.0","nock":"^11.3.3","npm-run-all":"^4.1.2","nyc":"^15.0.0","prettier":"^1.14.2","proxy":"^1.0.0","semantic-release":"^17.0.0","sinon":"^8.0.0","sinon-chai":"^3.0.0","sort-keys":"^4.0.0","string-to-arraybuffer":"^1.0.0","string-to-jsdoc-comment":"^1.0.0","typescript":"^3.3.1","webpack":"^4.0.0","webpack-bundle-analyzer":"^3.0.0","webpack-cli":"^3.0.0"},"types":"index.d.ts","scripts":{"coverage":"nyc report --reporter=html && open coverage/index.html","lint":"prettier --check '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","lint:fix":"prettier --write '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","pretest":"npm run -s lint","test":"nyc mocha test/mocha-node-setup.js \"test/*/**/*-test.js\"","test:browser":"cypress run --browser chrome","build":"npm-run-all build:*","build:ts":"npm run -s update-endpoints:typescript","prebuild:browser":"mkdirp dist/","build:browser":"npm-run-all build:browser:*","build:browser:development":"webpack --mode development --entry . --output-library=Octokit --output=./dist/octokit-rest.js --profile --json > dist/bundle-stats.json","build:browser:production":"webpack --mode production --entry . --plugin=compression-webpack-plugin --output-library=Octokit --output-path=./dist --output-filename=octokit-rest.min.js --devtool source-map","generate-bundle-report":"webpack-bundle-analyzer dist/bundle-stats.json --mode=static --no-open --report dist/bundle-report.html","update-endpoints":"npm-run-all update-endpoints:*","update-endpoints:fetch-json":"node scripts/update-endpoints/fetch-json","update-endpoints:typescript":"node scripts/update-endpoints/typescript","prevalidate:ts":"npm run -s build:ts","validate:ts":"tsc --target es6 --noImplicitAny index.d.ts","postvalidate:ts":"tsc --noEmit --target es6 test/typescript-validate.ts","start-fixtures-server":"octokit-fixtures-server"},"license":"MIT","files":["index.js","index.d.ts","lib","plugins"],"nyc":{"ignore":["test"]},"release":{"publish":["@semantic-release/npm",{"path":"@semantic-release/github","assets":["dist/*","!dist/*.map.gz"]}]},"bundlesize":[{"path":"./dist/octokit-rest.min.js.gz","maxSize":"33 kB"}]}; module.exports = {"name":"@octokit/rest","version":"16.43.1","publishConfig":{"access":"public"},"description":"GitHub REST API client for Node.js","keywords":["octokit","github","rest","api-client"],"author":"Gregor Martynus (https://github.com/gr2m)","contributors":[{"name":"Mike de Boer","email":"info@mikedeboer.nl"},{"name":"Fabian Jakobs","email":"fabian@c9.io"},{"name":"Joe Gallo","email":"joe@brassafrax.com"},{"name":"Gregor Martynus","url":"https://github.com/gr2m"}],"repository":"https://github.com/octokit/rest.js","dependencies":{"@octokit/auth-token":"^2.4.0","@octokit/plugin-paginate-rest":"^1.1.1","@octokit/plugin-request-log":"^1.0.0","@octokit/plugin-rest-endpoint-methods":"2.4.0","@octokit/request":"^5.2.0","@octokit/request-error":"^1.0.2","atob-lite":"^2.0.0","before-after-hook":"^2.0.0","btoa-lite":"^1.0.0","deprecation":"^2.0.0","lodash.get":"^4.4.2","lodash.set":"^4.3.2","lodash.uniq":"^4.5.0","octokit-pagination-methods":"^1.1.0","once":"^1.4.0","universal-user-agent":"^4.0.0"},"devDependencies":{"@gimenete/type-writer":"^0.1.3","@octokit/auth":"^1.1.1","@octokit/fixtures-server":"^5.0.6","@octokit/graphql":"^4.2.0","@types/node":"^13.1.0","bundlesize":"^0.18.0","chai":"^4.1.2","compression-webpack-plugin":"^3.1.0","cypress":"^3.0.0","glob":"^7.1.2","http-proxy-agent":"^4.0.0","lodash.camelcase":"^4.3.0","lodash.merge":"^4.6.1","lodash.upperfirst":"^4.3.1","lolex":"^5.1.2","mkdirp":"^1.0.0","mocha":"^7.0.1","mustache":"^4.0.0","nock":"^11.3.3","npm-run-all":"^4.1.2","nyc":"^15.0.0","prettier":"^1.14.2","proxy":"^1.0.0","semantic-release":"^17.0.0","sinon":"^8.0.0","sinon-chai":"^3.0.0","sort-keys":"^4.0.0","string-to-arraybuffer":"^1.0.0","string-to-jsdoc-comment":"^1.0.0","typescript":"^3.3.1","webpack":"^4.0.0","webpack-bundle-analyzer":"^3.0.0","webpack-cli":"^3.0.0"},"types":"index.d.ts","scripts":{"coverage":"nyc report --reporter=html && open coverage/index.html","lint":"prettier --check '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","lint:fix":"prettier --write '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","pretest":"npm run -s lint","test":"nyc mocha test/mocha-node-setup.js \"test/*/**/*-test.js\"","test:browser":"cypress run --browser chrome","build":"npm-run-all build:*","build:ts":"npm run -s update-endpoints:typescript","prebuild:browser":"mkdirp dist/","build:browser":"npm-run-all build:browser:*","build:browser:development":"webpack --mode development --entry . --output-library=Octokit --output=./dist/octokit-rest.js --profile --json > dist/bundle-stats.json","build:browser:production":"webpack --mode production --entry . --plugin=compression-webpack-plugin --output-library=Octokit --output-path=./dist --output-filename=octokit-rest.min.js --devtool source-map","generate-bundle-report":"webpack-bundle-analyzer dist/bundle-stats.json --mode=static --no-open --report dist/bundle-report.html","update-endpoints":"npm-run-all update-endpoints:*","update-endpoints:fetch-json":"node scripts/update-endpoints/fetch-json","update-endpoints:typescript":"node scripts/update-endpoints/typescript","prevalidate:ts":"npm run -s build:ts","validate:ts":"tsc --target es6 --noImplicitAny index.d.ts","postvalidate:ts":"tsc --noEmit --target es6 test/typescript-validate.ts","start-fixtures-server":"octokit-fixtures-server"},"license":"MIT","files":["index.js","index.d.ts","lib","plugins"],"nyc":{"ignore":["test"]},"release":{"publish":["@semantic-release/npm",{"path":"@semantic-release/github","assets":["dist/*","!dist/*.map.gz"]}]},"bundlesize":[{"path":"./dist/octokit-rest.min.js.gz","maxSize":"33 kB"}],"_resolved":"https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz","_integrity":"sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==","_from":"@octokit/rest@16.43.1"};
/***/ }), /***/ }),
@ -6572,9 +6583,13 @@ class GitAuthHelper {
yield this.configureToken(); yield this.configureToken();
}); });
} }
configureGlobalAuth() { configureTempGlobalConfig() {
var _a; var _a, _b;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// Already setup global config
if (((_a = this.temporaryHomePath) === null || _a === void 0 ? void 0 : _a.length) > 0) {
return path.join(this.temporaryHomePath, '.gitconfig');
}
// Create a temp home directory // Create a temp home directory
const runnerTemp = process.env['RUNNER_TEMP'] || ''; const runnerTemp = process.env['RUNNER_TEMP'] || '';
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); assert.ok(runnerTemp, 'RUNNER_TEMP is not defined');
@ -6590,7 +6605,7 @@ class GitAuthHelper {
configExists = true; configExists = true;
} }
catch (err) { catch (err) {
if (((_a = err) === null || _a === void 0 ? void 0 : _a.code) !== 'ENOENT') { if (((_b = err) === null || _b === void 0 ? void 0 : _b.code) !== 'ENOENT') {
throw err; throw err;
} }
} }
@ -6601,10 +6616,17 @@ class GitAuthHelper {
else { else {
yield fs.promises.writeFile(newGitConfigPath, ''); yield fs.promises.writeFile(newGitConfigPath, '');
} }
// Override HOME
core.info(`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`);
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath);
return newGitConfigPath;
});
}
configureGlobalAuth() {
return __awaiter(this, void 0, void 0, function* () {
// 'configureTempGlobalConfig' noops if already set, just returns the path
const newGitConfigPath = yield this.configureTempGlobalConfig();
try { try {
// Override HOME
core.info(`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`);
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath);
// Configure the token // Configure the token
yield this.configureToken(newGitConfigPath, true); yield this.configureToken(newGitConfigPath, true);
// Configure HTTPS instead of SSH // Configure HTTPS instead of SSH
@ -6657,11 +6679,14 @@ class GitAuthHelper {
yield this.removeToken(); yield this.removeToken();
}); });
} }
removeGlobalAuth() { removeGlobalConfig() {
var _a;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
core.debug(`Unsetting HOME override`); if (((_a = this.temporaryHomePath) === null || _a === void 0 ? void 0 : _a.length) > 0) {
this.git.removeEnvironmentVariable('HOME'); core.debug(`Unsetting HOME override`);
yield io.rmRF(this.temporaryHomePath); this.git.removeEnvironmentVariable('HOME');
yield io.rmRF(this.temporaryHomePath);
}
}); });
} }
configureSsh() { configureSsh() {
@ -7326,40 +7351,59 @@ function getSource(settings) {
core.startGroup('Getting Git version info'); core.startGroup('Getting Git version info');
const git = yield getGitCommandManager(settings); const git = yield getGitCommandManager(settings);
core.endGroup(); core.endGroup();
// Prepare existing directory, otherwise recreate let authHelper = null;
if (isExisting) {
yield gitDirectoryHelper.prepareExistingDirectory(git, settings.repositoryPath, repositoryUrl, settings.clean, settings.ref);
}
if (!git) {
// Downloading using REST API
core.info(`The repository will be downloaded using the GitHub REST API`);
core.info(`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`);
if (settings.submodules) {
throw new Error(`Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`);
}
else if (settings.sshKey) {
throw new Error(`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`);
}
yield githubApiHelper.downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
return;
}
// Save state for POST action
stateHelper.setRepositoryPath(settings.repositoryPath);
// Initialize the repository
if (!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))) {
core.startGroup('Initializing the repository');
yield git.init();
yield git.remoteAdd('origin', repositoryUrl);
core.endGroup();
}
// Disable automatic garbage collection
core.startGroup('Disabling automatic garbage collection');
if (!(yield git.tryDisableAutomaticGarbageCollection())) {
core.warning(`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`);
}
core.endGroup();
const authHelper = gitAuthHelper.createAuthHelper(git, settings);
try { try {
if (git) {
authHelper = gitAuthHelper.createAuthHelper(git, settings);
if (settings.setSafeDirectory) {
// Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail
// Otherwise all git commands we run in a container fail
yield authHelper.configureTempGlobalConfig();
core.info(`Adding repository directory to the temporary git global config as a safe directory`);
yield git
.config('safe.directory', settings.repositoryPath, true, true)
.catch(error => {
core.info(`Failed to initialize safe directory with error: ${error}`);
});
stateHelper.setSafeDirectory();
}
}
// Prepare existing directory, otherwise recreate
if (isExisting) {
yield gitDirectoryHelper.prepareExistingDirectory(git, settings.repositoryPath, repositoryUrl, settings.clean, settings.ref);
}
if (!git) {
// Downloading using REST API
core.info(`The repository will be downloaded using the GitHub REST API`);
core.info(`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`);
if (settings.submodules) {
throw new Error(`Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`);
}
else if (settings.sshKey) {
throw new Error(`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`);
}
yield githubApiHelper.downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
return;
}
// Save state for POST action
stateHelper.setRepositoryPath(settings.repositoryPath);
// Initialize the repository
if (!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))) {
core.startGroup('Initializing the repository');
yield git.init();
yield git.remoteAdd('origin', repositoryUrl);
core.endGroup();
}
// Disable automatic garbage collection
core.startGroup('Disabling automatic garbage collection');
if (!(yield git.tryDisableAutomaticGarbageCollection())) {
core.warning(`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`);
}
core.endGroup();
// If we didn't initialize it above, do it now
if (!authHelper) {
authHelper = gitAuthHelper.createAuthHelper(git, settings);
}
// Configure auth // Configure auth
core.startGroup('Setting up auth'); core.startGroup('Setting up auth');
yield authHelper.configureAuth(); yield authHelper.configureAuth();
@ -7415,27 +7459,21 @@ function getSource(settings) {
core.endGroup(); core.endGroup();
// Submodules // Submodules
if (settings.submodules) { if (settings.submodules) {
try { // Temporarily override global config
// Temporarily override global config core.startGroup('Setting up auth for fetching submodules');
core.startGroup('Setting up auth for fetching submodules'); yield authHelper.configureGlobalAuth();
yield authHelper.configureGlobalAuth(); core.endGroup();
// Checkout submodules
core.startGroup('Fetching submodules');
yield git.submoduleSync(settings.nestedSubmodules);
yield git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules);
yield git.submoduleForeach('git config --local gc.auto 0', settings.nestedSubmodules);
core.endGroup();
// Persist credentials
if (settings.persistCredentials) {
core.startGroup('Persisting credentials for submodules');
yield authHelper.configureSubmoduleAuth();
core.endGroup(); core.endGroup();
// Checkout submodules
core.startGroup('Fetching submodules');
yield git.submoduleSync(settings.nestedSubmodules);
yield git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules);
yield git.submoduleForeach('git config --local gc.auto 0', settings.nestedSubmodules);
core.endGroup();
// Persist credentials
if (settings.persistCredentials) {
core.startGroup('Persisting credentials for submodules');
yield authHelper.configureSubmoduleAuth();
core.endGroup();
}
}
finally {
// Remove temporary global config override
yield authHelper.removeGlobalAuth();
} }
} }
// Get commit information // Get commit information
@ -7447,10 +7485,13 @@ function getSource(settings) {
} }
finally { finally {
// Remove auth // Remove auth
if (!settings.persistCredentials) { if (authHelper) {
core.startGroup('Removing auth'); if (!settings.persistCredentials) {
yield authHelper.removeAuth(); core.startGroup('Removing auth');
core.endGroup(); yield authHelper.removeAuth();
core.endGroup();
}
authHelper.removeGlobalConfig();
} }
} }
}); });
@ -7472,7 +7513,23 @@ function cleanup(repositoryPath) {
} }
// Remove auth // Remove auth
const authHelper = gitAuthHelper.createAuthHelper(git); const authHelper = gitAuthHelper.createAuthHelper(git);
yield authHelper.removeAuth(); try {
if (stateHelper.PostSetSafeDirectory) {
// Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail
// Otherwise all git commands we run in a container fail
yield authHelper.configureTempGlobalConfig();
core.info(`Adding repository directory to the temporary git global config as a safe directory`);
yield git
.config('safe.directory', repositoryPath, true, true)
.catch(error => {
core.info(`Failed to initialize safe directory with error: ${error}`);
});
}
yield authHelper.removeAuth();
}
finally {
yield authHelper.removeGlobalConfig();
}
}); });
} }
exports.cleanup = cleanup; exports.cleanup = cleanup;
@ -17244,6 +17301,9 @@ function getInputs() {
(core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE'; (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE';
// Workflow organization ID // Workflow organization ID
result.workflowOrganizationId = yield workflowContextHelper.getOrganizationId(); result.workflowOrganizationId = yield workflowContextHelper.getOrganizationId();
// Set safe.directory in git global config.
result.setSafeDirectory =
(core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE';
return result; return result;
}); });
} }

View File

@ -19,8 +19,9 @@ export interface IGitAuthHelper {
configureAuth(): Promise<void> configureAuth(): Promise<void>
configureGlobalAuth(): Promise<void> configureGlobalAuth(): Promise<void>
configureSubmoduleAuth(): Promise<void> configureSubmoduleAuth(): Promise<void>
configureTempGlobalConfig(): Promise<string>
removeAuth(): Promise<void> removeAuth(): Promise<void>
removeGlobalAuth(): Promise<void> removeGlobalConfig(): Promise<void>
} }
export function createAuthHelper( export function createAuthHelper(
@ -80,7 +81,11 @@ class GitAuthHelper {
await this.configureToken() await this.configureToken()
} }
async configureGlobalAuth(): Promise<void> { async configureTempGlobalConfig(): Promise<string> {
// Already setup global config
if (this.temporaryHomePath?.length > 0) {
return path.join(this.temporaryHomePath, '.gitconfig')
}
// Create a temp home directory // Create a temp home directory
const runnerTemp = process.env['RUNNER_TEMP'] || '' const runnerTemp = process.env['RUNNER_TEMP'] || ''
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
@ -110,13 +115,19 @@ class GitAuthHelper {
await fs.promises.writeFile(newGitConfigPath, '') await fs.promises.writeFile(newGitConfigPath, '')
} }
try { // Override HOME
// Override HOME core.info(
core.info( `Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`
`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes` )
) this.git.setEnvironmentVariable('HOME', this.temporaryHomePath)
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath)
return newGitConfigPath
}
async configureGlobalAuth(): Promise<void> {
// 'configureTempGlobalConfig' noops if already set, just returns the path
const newGitConfigPath = await this.configureTempGlobalConfig()
try {
// Configure the token // Configure the token
await this.configureToken(newGitConfigPath, true) await this.configureToken(newGitConfigPath, true)
@ -181,10 +192,12 @@ class GitAuthHelper {
await this.removeToken() await this.removeToken()
} }
async removeGlobalAuth(): Promise<void> { async removeGlobalConfig(): Promise<void> {
core.debug(`Unsetting HOME override`) if (this.temporaryHomePath?.length > 0) {
this.git.removeEnvironmentVariable('HOME') core.debug(`Unsetting HOME override`)
await io.rmRF(this.temporaryHomePath) this.git.removeEnvironmentVariable('HOME')
await io.rmRF(this.temporaryHomePath)
}
} }
private async configureSsh(): Promise<void> { private async configureSsh(): Promise<void> {

View File

@ -36,68 +36,94 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
const git = await getGitCommandManager(settings) const git = await getGitCommandManager(settings)
core.endGroup() core.endGroup()
// Prepare existing directory, otherwise recreate let authHelper: gitAuthHelper.IGitAuthHelper | null = null
if (isExisting) { try {
await gitDirectoryHelper.prepareExistingDirectory( if (git) {
git, authHelper = gitAuthHelper.createAuthHelper(git, settings)
settings.repositoryPath, if (settings.setSafeDirectory) {
repositoryUrl, // Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail
settings.clean, // Otherwise all git commands we run in a container fail
settings.ref await authHelper.configureTempGlobalConfig()
) core.info(
} `Adding repository directory to the temporary git global config as a safe directory`
)
if (!git) { await git
// Downloading using REST API .config('safe.directory', settings.repositoryPath, true, true)
core.info(`The repository will be downloaded using the GitHub REST API`) .catch(error => {
core.info( core.info(
`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH` `Failed to initialize safe directory with error: ${error}`
) )
if (settings.submodules) { })
throw new Error(
`Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.` stateHelper.setSafeDirectory()
) }
} else if (settings.sshKey) { }
throw new Error(
`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.` // Prepare existing directory, otherwise recreate
if (isExisting) {
await gitDirectoryHelper.prepareExistingDirectory(
git,
settings.repositoryPath,
repositoryUrl,
settings.clean,
settings.ref
) )
} }
await githubApiHelper.downloadRepository( if (!git) {
settings.authToken, // Downloading using REST API
settings.repositoryOwner, core.info(`The repository will be downloaded using the GitHub REST API`)
settings.repositoryName, core.info(
settings.ref, `To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
settings.commit, )
settings.repositoryPath if (settings.submodules) {
) throw new Error(
return `Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
} )
} else if (settings.sshKey) {
throw new Error(
`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
)
}
// Save state for POST action await githubApiHelper.downloadRepository(
stateHelper.setRepositoryPath(settings.repositoryPath) settings.authToken,
settings.repositoryOwner,
settings.repositoryName,
settings.ref,
settings.commit,
settings.repositoryPath
)
return
}
// Initialize the repository // Save state for POST action
if ( stateHelper.setRepositoryPath(settings.repositoryPath)
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
) { // Initialize the repository
core.startGroup('Initializing the repository') if (
await git.init() !fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
await git.remoteAdd('origin', repositoryUrl) ) {
core.startGroup('Initializing the repository')
await git.init()
await git.remoteAdd('origin', repositoryUrl)
core.endGroup()
}
// Disable automatic garbage collection
core.startGroup('Disabling automatic garbage collection')
if (!(await git.tryDisableAutomaticGarbageCollection())) {
core.warning(
`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`
)
}
core.endGroup() core.endGroup()
}
// Disable automatic garbage collection // If we didn't initialize it above, do it now
core.startGroup('Disabling automatic garbage collection') if (!authHelper) {
if (!(await git.tryDisableAutomaticGarbageCollection())) { authHelper = gitAuthHelper.createAuthHelper(git, settings)
core.warning( }
`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`
)
}
core.endGroup()
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
try {
// Configure auth // Configure auth
core.startGroup('Setting up auth') core.startGroup('Setting up auth')
await authHelper.configureAuth() await authHelper.configureAuth()
@ -170,34 +196,26 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
// Submodules // Submodules
if (settings.submodules) { if (settings.submodules) {
try { // Temporarily override global config
// Temporarily override global config core.startGroup('Setting up auth for fetching submodules')
core.startGroup('Setting up auth for fetching submodules') await authHelper.configureGlobalAuth()
await authHelper.configureGlobalAuth() core.endGroup()
core.endGroup()
// Checkout submodules // Checkout submodules
core.startGroup('Fetching submodules') core.startGroup('Fetching submodules')
await git.submoduleSync(settings.nestedSubmodules) await git.submoduleSync(settings.nestedSubmodules)
await git.submoduleUpdate( await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules)
settings.fetchDepth, await git.submoduleForeach(
settings.nestedSubmodules 'git config --local gc.auto 0',
) settings.nestedSubmodules
await git.submoduleForeach( )
'git config --local gc.auto 0', core.endGroup()
settings.nestedSubmodules
)
core.endGroup()
// Persist credentials // Persist credentials
if (settings.persistCredentials) { if (settings.persistCredentials) {
core.startGroup('Persisting credentials for submodules') core.startGroup('Persisting credentials for submodules')
await authHelper.configureSubmoduleAuth() await authHelper.configureSubmoduleAuth()
core.endGroup() core.endGroup()
}
} finally {
// Remove temporary global config override
await authHelper.removeGlobalAuth()
} }
} }
@ -218,10 +236,13 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
) )
} finally { } finally {
// Remove auth // Remove auth
if (!settings.persistCredentials) { if (authHelper) {
core.startGroup('Removing auth') if (!settings.persistCredentials) {
await authHelper.removeAuth() core.startGroup('Removing auth')
core.endGroup() await authHelper.removeAuth()
core.endGroup()
}
authHelper.removeGlobalConfig()
} }
} }
} }
@ -244,7 +265,26 @@ export async function cleanup(repositoryPath: string): Promise<void> {
// Remove auth // Remove auth
const authHelper = gitAuthHelper.createAuthHelper(git) const authHelper = gitAuthHelper.createAuthHelper(git)
await authHelper.removeAuth() try {
if (stateHelper.PostSetSafeDirectory) {
// Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail
// Otherwise all git commands we run in a container fail
await authHelper.configureTempGlobalConfig()
core.info(
`Adding repository directory to the temporary git global config as a safe directory`
)
await git
.config('safe.directory', repositoryPath, true, true)
.catch(error => {
core.info(`Failed to initialize safe directory with error: ${error}`)
})
}
await authHelper.removeAuth()
} finally {
await authHelper.removeGlobalConfig()
}
} }
async function getGitCommandManager( async function getGitCommandManager(

View File

@ -78,4 +78,9 @@ export interface IGitSourceSettings {
* Organization ID for the currently running workflow (used for auth settings) * Organization ID for the currently running workflow (used for auth settings)
*/ */
workflowOrganizationId: number | undefined workflowOrganizationId: number | undefined
/**
* Indicates whether to add repositoryPath as safe.directory in git global config
*/
setSafeDirectory: boolean
} }

View File

@ -122,5 +122,8 @@ export async function getInputs(): Promise<IGitSourceSettings> {
// Workflow organization ID // Workflow organization ID
result.workflowOrganizationId = await workflowContextHelper.getOrganizationId() result.workflowOrganizationId = await workflowContextHelper.getOrganizationId()
// Set safe.directory in git global config.
result.setSafeDirectory =
(core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE'
return result return result
} }

View File

@ -120,7 +120,7 @@ function updateUsage(
} }
updateUsage( updateUsage(
'actions/checkout@v3', 'actions/checkout@v2',
path.join(__dirname, '..', '..', 'action.yml'), path.join(__dirname, '..', '..', 'action.yml'),
path.join(__dirname, '..', '..', 'README.md') path.join(__dirname, '..', '..', 'README.md')
) )

View File

@ -11,6 +11,12 @@ export const IsPost = !!process.env['STATE_isPost']
export const RepositoryPath = export const RepositoryPath =
(process.env['STATE_repositoryPath'] as string) || '' (process.env['STATE_repositoryPath'] as string) || ''
/**
* The set-safe-directory for the POST action. The value is set if input: 'safe-directory' is set during the MAIN action.
*/
export const PostSetSafeDirectory =
(process.env['STATE_setSafeDirectory'] as string) === 'true'
/** /**
* The SSH key path for the POST action. The value is empty during the MAIN action. * The SSH key path for the POST action. The value is empty during the MAIN action.
*/ */
@ -51,6 +57,13 @@ export function setSshKnownHostsPath(sshKnownHostsPath: string) {
) )
} }
/**
* Save the sef-safe-directory input so the POST action can retrieve the value.
*/
export function setSafeDirectory() {
coreCommand.issueCommand('save-state', {name: 'setSafeDirectory'}, 'true')
}
// Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic. // Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic.
// This is necessary since we don't have a separate entry point. // This is necessary since we don't have a separate entry point.
if (!IsPost) { if (!IsPost) {