Add set-safe-directory input to allow customers to take control. (#770)

* Add set-safe-directory input to allow customers to take control.
This commit is contained in:
Tingluo Huang 2022-04-20 21:37:43 -04:00 committed by GitHub
parent dcd71f6466
commit 0ffe6f9c55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 144 additions and 32 deletions

@ -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

@ -92,6 +92,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 -->

@ -777,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
} }
} }

@ -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 () => {

@ -68,6 +68,9 @@ 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: node16
main: dist/index.js main: dist/index.js

49
dist/index.js vendored

@ -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) {
@ -6572,7 +6583,7 @@ class GitAuthHelper {
yield this.configureToken(); yield this.configureToken();
}); });
} }
configureTempGlobalConfig(repositoryPath) { configureTempGlobalConfig() {
var _a, _b; var _a, _b;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// Already setup global config // Already setup global config
@ -6608,14 +6619,6 @@ class GitAuthHelper {
// Override HOME // Override HOME
core.info(`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`); core.info(`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`);
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath); this.git.setEnvironmentVariable('HOME', this.temporaryHomePath);
// Setup the workspace 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
core.info(`Adding working directory to the temporary git global config as a safe directory`);
yield this.git
.config('safe.directory', repositoryPath !== null && repositoryPath !== void 0 ? repositoryPath : this.settings.repositoryPath, true, true)
.catch(error => {
core.info(`Failed to initialize safe directory with error: ${error}`);
});
return newGitConfigPath; return newGitConfigPath;
}); });
} }
@ -7352,7 +7355,18 @@ function getSource(settings) {
try { try {
if (git) { if (git) {
authHelper = gitAuthHelper.createAuthHelper(git, settings); 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(); 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 // Prepare existing directory, otherwise recreate
if (isExisting) { if (isExisting) {
@ -7500,7 +7514,17 @@ function cleanup(repositoryPath) {
// Remove auth // Remove auth
const authHelper = gitAuthHelper.createAuthHelper(git); const authHelper = gitAuthHelper.createAuthHelper(git);
try { try {
yield authHelper.configureTempGlobalConfig(repositoryPath); 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(); yield authHelper.removeAuth();
} }
finally { finally {
@ -17303,6 +17327,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;
}); });
} }

@ -19,7 +19,7 @@ export interface IGitAuthHelper {
configureAuth(): Promise<void> configureAuth(): Promise<void>
configureGlobalAuth(): Promise<void> configureGlobalAuth(): Promise<void>
configureSubmoduleAuth(): Promise<void> configureSubmoduleAuth(): Promise<void>
configureTempGlobalConfig(repositoryPath?: string): Promise<string> configureTempGlobalConfig(): Promise<string>
removeAuth(): Promise<void> removeAuth(): Promise<void>
removeGlobalConfig(): Promise<void> removeGlobalConfig(): Promise<void>
} }
@ -81,7 +81,7 @@ class GitAuthHelper {
await this.configureToken() await this.configureToken()
} }
async configureTempGlobalConfig(repositoryPath?: string): Promise<string> { async configureTempGlobalConfig(): Promise<string> {
// Already setup global config // Already setup global config
if (this.temporaryHomePath?.length > 0) { if (this.temporaryHomePath?.length > 0) {
return path.join(this.temporaryHomePath, '.gitconfig') return path.join(this.temporaryHomePath, '.gitconfig')
@ -121,21 +121,6 @@ class GitAuthHelper {
) )
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath) this.git.setEnvironmentVariable('HOME', this.temporaryHomePath)
// Setup the workspace 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
core.info(
`Adding working directory to the temporary git global config as a safe directory`
)
await this.git
.config(
'safe.directory',
repositoryPath ?? this.settings.repositoryPath,
true,
true
)
.catch(error => {
core.info(`Failed to initialize safe directory with error: ${error}`)
})
return newGitConfigPath return newGitConfigPath
} }

@ -40,7 +40,24 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
try { try {
if (git) { if (git) {
authHelper = gitAuthHelper.createAuthHelper(git, settings) 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
await authHelper.configureTempGlobalConfig() await authHelper.configureTempGlobalConfig()
core.info(
`Adding repository directory to the temporary git global config as a safe directory`
)
await 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 // Prepare existing directory, otherwise recreate
@ -249,7 +266,21 @@ export async function cleanup(repositoryPath: string): Promise<void> {
// Remove auth // Remove auth
const authHelper = gitAuthHelper.createAuthHelper(git) const authHelper = gitAuthHelper.createAuthHelper(git)
try { try {
await authHelper.configureTempGlobalConfig(repositoryPath) 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() await authHelper.removeAuth()
} finally { } finally {
await authHelper.removeGlobalConfig() await authHelper.removeGlobalConfig()

@ -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
} }

@ -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
} }

@ -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) {