Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add input persist-credentials #107

Merged
merged 3 commits into from Dec 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/test.yml
Expand Up @@ -18,6 +18,7 @@ jobs:
- run: npm run lint
- run: npm run pack
- run: npm run gendocs
- run: npm test
- name: Verify no unstaged changes
run: __test__/verify-no-unstaged-changes.sh

Expand Down Expand Up @@ -84,15 +85,12 @@ jobs:

test-job-container:
runs-on: ubuntu-latest
container: pstauffer/curl:latest
container: alpine:latest
steps:
# Clone this repo
# todo: after v2-beta contains the latest changes, switch this to "uses: actions/checkout@v2-beta". Also switch to "alpine:latest"
# todo: after v2-beta contains the latest changes, switch this to "uses: actions/checkout@v2-beta"
- name: Checkout
run: |
curl --location --user token:${{ github.token }} --output checkout.tar.gz https://api.github.com/repos/actions/checkout/tarball/${{ github.sha }}
tar -xzf checkout.tar.gz
mv */* ./
uses: actions/checkout@a572f640b07e96fc5837b3adfa0e5a2ddd8dae21

# Basic checkout
- name: Basic checkout
Expand Down
18 changes: 12 additions & 6 deletions README.md
Expand Up @@ -15,16 +15,16 @@ Refer [here](https://help.github.com/en/articles/events-that-trigger-workflows)
- Improved fetch performance
- The default behavior now fetches only the commit being checked-out
- Script authenticated git commands
- Persists `with.token` in the local git config
- Persists the input `token` in the local git config
- Enables your scripts to run authenticated git commands
- Post-job cleanup removes the token
- Coming soon: Opt out by setting `with.persist-credentials` to `false`
- Opt out by setting the input `persist-credentials: false`
- Creates a local branch
- No longer detached HEAD when checking out a branch
- A local branch is created with the corresponding upstream branch set
- Improved layout
- `with.path` is always relative to `github.workspace`
- Aligns better with container actions, where `github.workspace` gets mapped in
- 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
- Removed input `submodules`
Expand All @@ -41,15 +41,21 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
# Default: ${{ github.repository }}
repository: ''

# The branch, tag or SHA to checkout. When checking out the repository that
# The branch, tag or SHA to checkout. When checking out the repository that
# triggered a workflow, this defaults to the reference or SHA for that event.
# Otherwise, defaults to `master`.
ref: ''

# Access token for clone repository
# Auth token used to fetch the repository. The token is stored in the local git
# config, which enables your scripts to run authenticated git commands. The
# post-job step removes the token from the git config.
# Default: ${{ github.token }}
token: ''

# Whether to persist the token in the git config
# Default: true
persist-credentials: ''

# Relative path under $GITHUB_WORKSPACE to place the repository
path: ''

Expand Down
2 changes: 1 addition & 1 deletion __test__/input-helper.test.ts
Expand Up @@ -63,7 +63,7 @@ describe('input-helper tests', () => {
it('sets defaults', () => {
const settings: ISourceSettings = inputHelper.getInputs()
expect(settings).toBeTruthy()
expect(settings.accessToken).toBeFalsy()
expect(settings.authToken).toBeFalsy()
expect(settings.clean).toBe(true)
expect(settings.commit).toBeTruthy()
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
Expand Down
14 changes: 10 additions & 4 deletions action.yml
Expand Up @@ -6,12 +6,18 @@ inputs:
default: ${{ github.repository }}
ref:
description: >
The branch, tag or SHA to checkout. When checking out the repository
that triggered a workflow, this defaults to the reference or SHA for
that event. Otherwise, defaults to `master`.
The branch, tag or SHA to checkout. When checking out the repository that
triggered a workflow, this defaults to the reference or SHA for that
event. Otherwise, defaults to `master`.
token:
description: 'Access token for clone repository'
description: >
Auth token used to fetch the repository. The token is stored in the local
git config, which enables your scripts to run authenticated git commands.
The post-job step removes the token from the git config.
default: ${{ github.token }}
persist-credentials:
description: 'Whether to persist the token in the git config'
default: true
path:
description: 'Relative path under $GITHUB_WORKSPACE to place the repository'
clean:
Expand Down
99 changes: 52 additions & 47 deletions dist/index.js
Expand Up @@ -4838,15 +4838,15 @@ class GitCommandManager {
}
config(configKey, configValue) {
return __awaiter(this, void 0, void 0, function* () {
yield this.execGit(['config', configKey, configValue]);
yield this.execGit(['config', '--local', configKey, configValue]);
});
}
configExists(configKey) {
return __awaiter(this, void 0, void 0, function* () {
const pattern = configKey.replace(/[^a-zA-Z0-9_]/g, x => {
return `\\${x}`;
});
const output = yield this.execGit(['config', '--name-only', '--get-regexp', pattern], true);
const output = yield this.execGit(['config', '--local', '--name-only', '--get-regexp', pattern], true);
return output.exitCode === 0;
});
}
Expand Down Expand Up @@ -4932,19 +4932,19 @@ class GitCommandManager {
}
tryConfigUnset(configKey) {
return __awaiter(this, void 0, void 0, function* () {
const output = yield this.execGit(['config', '--unset-all', configKey], true);
const output = yield this.execGit(['config', '--local', '--unset-all', configKey], true);
return output.exitCode === 0;
});
}
tryDisableAutomaticGarbageCollection() {
return __awaiter(this, void 0, void 0, function* () {
const output = yield this.execGit(['config', 'gc.auto', '0'], true);
const output = yield this.execGit(['config', '--local', 'gc.auto', '0'], true);
return output.exitCode === 0;
});
}
tryGetFetchUrl() {
return __awaiter(this, void 0, void 0, function* () {
const output = yield this.execGit(['config', '--get', 'remote.origin.url'], true);
const output = yield this.execGit(['config', '--local', '--get', 'remote.origin.url'], true);
if (output.exitCode !== 0) {
return '';
}
Expand Down Expand Up @@ -5121,7 +5121,7 @@ function getSource(settings) {
// 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`);
yield githubApiHelper.downloadRepository(settings.accessToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
yield githubApiHelper.downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
}
else {
// Save state for POST action
Expand All @@ -5137,30 +5137,34 @@ function getSource(settings) {
}
// Remove possible previous extraheader
yield removeGitConfig(git, authConfigKey);
// Add extraheader (auth)
const base64Credentials = Buffer.from(`x-access-token:${settings.accessToken}`, 'utf8').toString('base64');
core.setSecret(base64Credentials);
const authConfigValue = `AUTHORIZATION: basic ${base64Credentials}`;
yield git.config(authConfigKey, authConfigValue);
// LFS install
if (settings.lfs) {
yield git.lfsInstall();
}
// Fetch
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit);
yield git.fetch(settings.fetchDepth, refSpec);
// Checkout info
const checkoutInfo = yield refHelper.getCheckoutInfo(git, settings.ref, settings.commit);
// LFS fetch
// Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time).
// Explicit lfs fetch will fetch lfs objects in parallel.
if (settings.lfs) {
yield git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref);
try {
// Config auth token
yield configureAuthToken(git, settings.authToken);
// LFS install
if (settings.lfs) {
yield git.lfsInstall();
}
// Fetch
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit);
yield git.fetch(settings.fetchDepth, refSpec);
// Checkout info
const checkoutInfo = yield refHelper.getCheckoutInfo(git, settings.ref, settings.commit);
// LFS fetch
// Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time).
// Explicit lfs fetch will fetch lfs objects in parallel.
if (settings.lfs) {
yield git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref);
}
// Checkout
yield git.checkout(checkoutInfo.ref, checkoutInfo.startPoint);
// Dump some info about the checked out commit
yield git.log1();
}
finally {
if (!settings.persistCredentials) {
yield removeGitConfig(git, authConfigKey);
}
}
// Checkout
yield git.checkout(checkoutInfo.ref, checkoutInfo.startPoint);
// Dump some info about the checked out commit
yield git.log1();
}
});
}
Expand Down Expand Up @@ -5265,23 +5269,21 @@ function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) {
}
});
}
function configureAuthToken(git, authToken) {
return __awaiter(this, void 0, void 0, function* () {
// Add extraheader (auth)
const base64Credentials = Buffer.from(`x-access-token:${authToken}`, 'utf8').toString('base64');
core.setSecret(base64Credentials);
const authConfigValue = `AUTHORIZATION: basic ${base64Credentials}`;
yield git.config(authConfigKey, authConfigValue);
});
}
function removeGitConfig(git, configKey) {
return __awaiter(this, void 0, void 0, function* () {
if ((yield git.configExists(configKey)) &&
!(yield git.tryConfigUnset(configKey))) {
// Load the config contents
core.warning(`Failed to remove '${configKey}' from the git config. Attempting to remove the config value by editing the file directly.`);
const configPath = path.join(git.getWorkingDirectory(), '.git', 'config');
fsHelper.fileExistsSync(configPath);
let contents = fs.readFileSync(configPath).toString() || '';
// Filter - only includes lines that do not contain the config key
const upperConfigKey = configKey.toUpperCase();
const split = contents
.split('\n')
.filter(x => !x.toUpperCase().includes(upperConfigKey));
contents = split.join('\n');
// Rewrite the config file
fs.writeFileSync(configPath, contents);
core.warning(`Failed to remove '${configKey}' from the git config`);
}
});
}
Expand Down Expand Up @@ -8403,12 +8405,12 @@ const retryHelper = __importStar(__webpack_require__(587));
const toolCache = __importStar(__webpack_require__(533));
const v4_1 = __importDefault(__webpack_require__(826));
const IS_WINDOWS = process.platform === 'win32';
function downloadRepository(accessToken, owner, repo, ref, commit, repositoryPath) {
function downloadRepository(authToken, owner, repo, ref, commit, repositoryPath) {
return __awaiter(this, void 0, void 0, function* () {
// Download the archive
let archiveData = yield retryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
core.info('Downloading the archive');
return yield downloadArchive(accessToken, owner, repo, ref, commit);
return yield downloadArchive(authToken, owner, repo, ref, commit);
}));
// Write archive to disk
core.info('Writing archive to disk');
Expand Down Expand Up @@ -8449,9 +8451,9 @@ function downloadRepository(accessToken, owner, repo, ref, commit, repositoryPat
});
}
exports.downloadRepository = downloadRepository;
function downloadArchive(accessToken, owner, repo, ref, commit) {
function downloadArchive(authToken, owner, repo, ref, commit) {
return __awaiter(this, void 0, void 0, function* () {
const octokit = new github.GitHub(accessToken);
const octokit = new github.GitHub(authToken);
const params = {
owner: owner,
repo: repo,
Expand Down Expand Up @@ -12764,8 +12766,11 @@ function getInputs() {
// LFS
result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE';
core.debug(`lfs = ${result.lfs}`);
// Access token
result.accessToken = core.getInput('token');
// Auth token
result.authToken = core.getInput('token');
// Persist credentials
result.persistCredentials =
(core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE';
return result;
}
exports.getInputs = getInputs;
Expand Down
13 changes: 8 additions & 5 deletions src/git-command-manager.ts
Expand Up @@ -116,15 +116,15 @@ class GitCommandManager {
}

async config(configKey: string, configValue: string): Promise<void> {
await this.execGit(['config', configKey, configValue])
await this.execGit(['config', '--local', configKey, configValue])
}

async configExists(configKey: string): Promise<boolean> {
const pattern = configKey.replace(/[^a-zA-Z0-9_]/g, x => {
return `\\${x}`
})
const output = await this.execGit(
['config', '--name-only', '--get-regexp', pattern],
['config', '--local', '--name-only', '--get-regexp', pattern],
true
)
return output.exitCode === 0
Expand Down Expand Up @@ -211,20 +211,23 @@ class GitCommandManager {

async tryConfigUnset(configKey: string): Promise<boolean> {
const output = await this.execGit(
['config', '--unset-all', configKey],
['config', '--local', '--unset-all', configKey],
true
)
return output.exitCode === 0
}

async tryDisableAutomaticGarbageCollection(): Promise<boolean> {
const output = await this.execGit(['config', 'gc.auto', '0'], true)
const output = await this.execGit(
['config', '--local', 'gc.auto', '0'],
true
)
return output.exitCode === 0
}

async tryGetFetchUrl(): Promise<string> {
const output = await this.execGit(
['config', '--get', 'remote.origin.url'],
['config', '--local', '--get', 'remote.origin.url'],
true
)

Expand Down