mirror of
				https://github.com/actions/checkout.git
				synced 2025-11-04 15:58:09 +08:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			v2-beta
			...
			users/eric
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					43c99f2ebc | ||
| 
						 | 
					4a3a4ebf11 | ||
| 
						 | 
					a5ba5cb63a | ||
| 
						 | 
					31b1047b1f | ||
| 
						 | 
					89cbb18acd | ||
| 
						 | 
					1e6a918852 | 
							
								
								
									
										99
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										99
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,39 +1,25 @@
 | 
			
		||||
name: Build and Test
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  pull_request:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
      - releases/*
 | 
			
		||||
on: push
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v1 # todo: switch to v2
 | 
			
		||||
      - run: npm ci
 | 
			
		||||
      - run: npm run build
 | 
			
		||||
      - run: npm run format-check
 | 
			
		||||
      - 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
 | 
			
		||||
 | 
			
		||||
  test:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        runs-on: [ubuntu-latest, macos-latest, windows-latest]
 | 
			
		||||
    runs-on: ${{ matrix.runs-on }}
 | 
			
		||||
 | 
			
		||||
  test-archive:
 | 
			
		||||
    runs-on: windows-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      # Clone this repo
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v2-beta
 | 
			
		||||
        shell: bash
 | 
			
		||||
        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 */* ./
 | 
			
		||||
 | 
			
		||||
      # Basic checkout
 | 
			
		||||
      - shell: cmd
 | 
			
		||||
        run: |
 | 
			
		||||
          echo echo hello > git.cmd
 | 
			
		||||
          echo ::add-path::%CD%
 | 
			
		||||
 | 
			
		||||
      - name: Basic checkout
 | 
			
		||||
        uses: ./
 | 
			
		||||
        with:
 | 
			
		||||
@@ -41,62 +27,5 @@ jobs:
 | 
			
		||||
          path: basic
 | 
			
		||||
      - name: Verify basic
 | 
			
		||||
        shell: bash
 | 
			
		||||
        run: __test__/verify-basic.sh
 | 
			
		||||
        run: __test__/verify-basic.sh container
 | 
			
		||||
 | 
			
		||||
      # Clean
 | 
			
		||||
      - name: Modify work tree
 | 
			
		||||
        shell: bash
 | 
			
		||||
        run: __test__/modify-work-tree.sh
 | 
			
		||||
      - name: Clean checkout
 | 
			
		||||
        uses: ./
 | 
			
		||||
        with:
 | 
			
		||||
          ref: test-data/v2/basic
 | 
			
		||||
          path: basic
 | 
			
		||||
      - name: Verify clean
 | 
			
		||||
        shell: bash
 | 
			
		||||
        run: __test__/verify-clean.sh
 | 
			
		||||
 | 
			
		||||
      # Side by side
 | 
			
		||||
      - name: Side by side checkout 1
 | 
			
		||||
        uses: ./
 | 
			
		||||
        with:
 | 
			
		||||
          ref: test-data/v2/side-by-side-1
 | 
			
		||||
          path: side-by-side-1
 | 
			
		||||
      - name: Side by side checkout 2
 | 
			
		||||
        uses: ./
 | 
			
		||||
        with:
 | 
			
		||||
          ref: test-data/v2/side-by-side-2
 | 
			
		||||
          path: side-by-side-2
 | 
			
		||||
      - name: Verify side by side
 | 
			
		||||
        shell: bash
 | 
			
		||||
        run: __test__/verify-side-by-side.sh
 | 
			
		||||
 | 
			
		||||
      # LFS
 | 
			
		||||
      - name: LFS checkout
 | 
			
		||||
        uses: ./
 | 
			
		||||
        with:
 | 
			
		||||
          repository: actions/checkout # hardcoded, otherwise doesn't work from a fork
 | 
			
		||||
          ref: test-data/v2/lfs
 | 
			
		||||
          path: lfs
 | 
			
		||||
          lfs: true
 | 
			
		||||
      - name: Verify LFS
 | 
			
		||||
        shell: bash
 | 
			
		||||
        run: __test__/verify-lfs.sh
 | 
			
		||||
 | 
			
		||||
  test-job-container:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    container: alpine:latest
 | 
			
		||||
    steps:
 | 
			
		||||
      # Clone this repo
 | 
			
		||||
      # todo: after v2-beta contains the latest changes, switch this to "uses: actions/checkout@v2-beta"
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@a572f640b07e96fc5837b3adfa0e5a2ddd8dae21
 | 
			
		||||
 | 
			
		||||
      # Basic checkout
 | 
			
		||||
      - name: Basic checkout
 | 
			
		||||
        uses: ./
 | 
			
		||||
        with:
 | 
			
		||||
          ref: test-data/v2/basic
 | 
			
		||||
          path: basic
 | 
			
		||||
      - name: Verify basic
 | 
			
		||||
        run: __test__/verify-basic.sh --archive
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								README.md
									
									
									
									
									
								
							@@ -13,20 +13,18 @@ Refer [here](https://help.github.com/en/articles/events-that-trigger-workflows)
 | 
			
		||||
# What's new
 | 
			
		||||
 | 
			
		||||
- Improved fetch performance
 | 
			
		||||
  - The default behavior now fetches only the commit being checked-out
 | 
			
		||||
  - The default behavior now fetches only the SHA being checked-out
 | 
			
		||||
- Script authenticated git commands
 | 
			
		||||
  - Persists the input `token` in the local git config
 | 
			
		||||
  - Persists `with.token` in the local git config
 | 
			
		||||
  - Enables your scripts to run authenticated git commands
 | 
			
		||||
  - Post-job cleanup removes the token
 | 
			
		||||
  - Opt out by setting the input `persist-credentials: false`
 | 
			
		||||
  - Coming soon: Opt out by setting `with.persist-credentials` to `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
 | 
			
		||||
  - 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
 | 
			
		||||
  - `with.path` is always relative to `github.workspace`
 | 
			
		||||
  - Aligns better with container actions, where `github.workspace` gets mapped in
 | 
			
		||||
- Removed input `submodules`
 | 
			
		||||
 | 
			
		||||
Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous versions.
 | 
			
		||||
@@ -46,16 +44,10 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
 | 
			
		||||
    # Otherwise, defaults to `master`.
 | 
			
		||||
    ref: ''
 | 
			
		||||
 | 
			
		||||
    # 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.
 | 
			
		||||
    # Access token for clone repository
 | 
			
		||||
    # 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: ''
 | 
			
		||||
 | 
			
		||||
@@ -97,7 +89,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
 | 
			
		||||
```yaml
 | 
			
		||||
- uses: actions/checkout@v2-beta
 | 
			
		||||
  with:
 | 
			
		||||
    ref: ${{ github.event.pull_request.head.sha }}
 | 
			
		||||
    ref: ${{ github.event.after }}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# License
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ describe('input-helper tests', () => {
 | 
			
		||||
  it('sets defaults', () => {
 | 
			
		||||
    const settings: ISourceSettings = inputHelper.getInputs()
 | 
			
		||||
    expect(settings).toBeTruthy()
 | 
			
		||||
    expect(settings.authToken).toBeFalsy()
 | 
			
		||||
    expect(settings.accessToken).toBeFalsy()
 | 
			
		||||
    expect(settings.clean).toBe(true)
 | 
			
		||||
    expect(settings.commit).toBeTruthy()
 | 
			
		||||
    expect(settings.commit).toBe('1234567890123456789012345678901234567890')
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ if [ ! -f "./basic/basic-file.txt" ]; then
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$1" = "--archive" ]; then
 | 
			
		||||
if [ "$1" = "container" ]; then
 | 
			
		||||
  # Verify no .git folder
 | 
			
		||||
  if [ -d "./basic/.git" ]; then
 | 
			
		||||
    echo "Did not expect ./basic/.git folder to exist"
 | 
			
		||||
@@ -20,5 +20,5 @@ else
 | 
			
		||||
 | 
			
		||||
  # Verify auth token
 | 
			
		||||
  cd basic
 | 
			
		||||
  git fetch --no-tags --depth=1 origin +refs/heads/master:refs/remotes/origin/master
 | 
			
		||||
  git fetch --depth=1
 | 
			
		||||
fi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								action.yml
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								action.yml
									
									
									
									
									
								
							@@ -6,18 +6,12 @@ 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: >
 | 
			
		||||
      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.
 | 
			
		||||
    description: 'Access token for clone repository'
 | 
			
		||||
    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:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										88
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										88
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							@@ -2620,7 +2620,7 @@ exports.IsPost = !!process.env['STATE_isPost'];
 | 
			
		||||
/**
 | 
			
		||||
 * 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'];
 | 
			
		||||
/**
 | 
			
		||||
 * Save the repository path so the POST action can retrieve the value.
 | 
			
		||||
 */
 | 
			
		||||
@@ -4838,7 +4838,7 @@ class GitCommandManager {
 | 
			
		||||
    }
 | 
			
		||||
    config(configKey, configValue) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            yield this.execGit(['config', '--local', configKey, configValue]);
 | 
			
		||||
            yield this.execGit(['config', configKey, configValue]);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    configExists(configKey) {
 | 
			
		||||
@@ -4846,7 +4846,7 @@ class GitCommandManager {
 | 
			
		||||
            const pattern = configKey.replace(/[^a-zA-Z0-9_]/g, x => {
 | 
			
		||||
                return `\\${x}`;
 | 
			
		||||
            });
 | 
			
		||||
            const output = yield this.execGit(['config', '--local', '--name-only', '--get-regexp', pattern], true);
 | 
			
		||||
            const output = yield this.execGit(['config', '--name-only', '--get-regexp', pattern], true);
 | 
			
		||||
            return output.exitCode === 0;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
@@ -4932,19 +4932,19 @@ class GitCommandManager {
 | 
			
		||||
    }
 | 
			
		||||
    tryConfigUnset(configKey) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const output = yield this.execGit(['config', '--local', '--unset-all', configKey], true);
 | 
			
		||||
            const output = yield this.execGit(['config', '--unset-all', configKey], true);
 | 
			
		||||
            return output.exitCode === 0;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    tryDisableAutomaticGarbageCollection() {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const output = yield this.execGit(['config', '--local', 'gc.auto', '0'], true);
 | 
			
		||||
            const output = yield this.execGit(['config', 'gc.auto', '0'], true);
 | 
			
		||||
            return output.exitCode === 0;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    tryGetFetchUrl() {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const output = yield this.execGit(['config', '--local', '--get', 'remote.origin.url'], true);
 | 
			
		||||
            const output = yield this.execGit(['config', '--get', 'remote.origin.url'], true);
 | 
			
		||||
            if (output.exitCode !== 0) {
 | 
			
		||||
                return '';
 | 
			
		||||
            }
 | 
			
		||||
@@ -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.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
 | 
			
		||||
            yield githubApiHelper.downloadRepository(settings.accessToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // Save state for POST action
 | 
			
		||||
@@ -5137,9 +5137,11 @@ function getSource(settings) {
 | 
			
		||||
            }
 | 
			
		||||
            // Remove possible previous extraheader
 | 
			
		||||
            yield removeGitConfig(git, authConfigKey);
 | 
			
		||||
            try {
 | 
			
		||||
                // Config auth token
 | 
			
		||||
                yield configureAuthToken(git, settings.authToken);
 | 
			
		||||
            // 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();
 | 
			
		||||
@@ -5160,12 +5162,6 @@ function getSource(settings) {
 | 
			
		||||
            // Dump some info about the checked out commit
 | 
			
		||||
            yield git.log1();
 | 
			
		||||
        }
 | 
			
		||||
            finally {
 | 
			
		||||
                if (!settings.persistCredentials) {
 | 
			
		||||
                    yield removeGitConfig(git, authConfigKey);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
exports.getSource = getSource;
 | 
			
		||||
@@ -5269,34 +5265,23 @@ function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) {
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
function configureAuthToken(git, authToken) {
 | 
			
		||||
    return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
        // Configure a placeholder value. This approach avoids the credential being captured
 | 
			
		||||
        // by process creation audit events, which are commonly logged. For more information,
 | 
			
		||||
        // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | 
			
		||||
        const placeholder = `AUTHORIZATION: basic ***`;
 | 
			
		||||
        yield git.config(authConfigKey, placeholder);
 | 
			
		||||
        // Determine the basic credential value
 | 
			
		||||
        const basicCredential = Buffer.from(`x-access-token:${authToken}`, 'utf8').toString('base64');
 | 
			
		||||
        core.setSecret(basicCredential);
 | 
			
		||||
        // Replace the value in the config file
 | 
			
		||||
        const configPath = path.join(git.getWorkingDirectory(), '.git', 'config');
 | 
			
		||||
        let content = (yield fs.promises.readFile(configPath)).toString();
 | 
			
		||||
        const placeholderIndex = content.indexOf(placeholder);
 | 
			
		||||
        if (placeholderIndex < 0 ||
 | 
			
		||||
            placeholderIndex != content.lastIndexOf(placeholder)) {
 | 
			
		||||
            throw new Error('Unable to replace auth placeholder in .git/config');
 | 
			
		||||
        }
 | 
			
		||||
        content = content.replace(placeholder, `AUTHORIZATION: basic ${basicCredential}`);
 | 
			
		||||
        yield fs.promises.writeFile(configPath, content);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
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`);
 | 
			
		||||
            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);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
@@ -8418,12 +8403,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(authToken, owner, repo, ref, commit, repositoryPath) {
 | 
			
		||||
function downloadRepository(accessToken, 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(authToken, owner, repo, ref, commit);
 | 
			
		||||
            return yield downloadArchive(accessToken, owner, repo, ref, commit);
 | 
			
		||||
        }));
 | 
			
		||||
        // Write archive to disk
 | 
			
		||||
        core.info('Writing archive to disk');
 | 
			
		||||
@@ -8453,20 +8438,15 @@ function downloadRepository(authToken, owner, repo, ref, commit, repositoryPath)
 | 
			
		||||
        for (const fileName of yield fs.promises.readdir(tempRepositoryPath)) {
 | 
			
		||||
            const sourcePath = path.join(tempRepositoryPath, fileName);
 | 
			
		||||
            const targetPath = path.join(repositoryPath, fileName);
 | 
			
		||||
            if (IS_WINDOWS) {
 | 
			
		||||
                yield io.cp(sourcePath, targetPath, { recursive: true }); // Copy on Windows (Windows Defender may have a lock)
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
            yield io.mv(sourcePath, targetPath);
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
        io.rmRF(extractPath);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
exports.downloadRepository = downloadRepository;
 | 
			
		||||
function downloadArchive(authToken, owner, repo, ref, commit) {
 | 
			
		||||
function downloadArchive(accessToken, owner, repo, ref, commit) {
 | 
			
		||||
    return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
        const octokit = new github.GitHub(authToken);
 | 
			
		||||
        const octokit = new github.GitHub(accessToken);
 | 
			
		||||
        const params = {
 | 
			
		||||
            owner: owner,
 | 
			
		||||
            repo: repo,
 | 
			
		||||
@@ -8475,7 +8455,7 @@ function downloadArchive(authToken, owner, repo, ref, commit) {
 | 
			
		||||
        };
 | 
			
		||||
        const response = yield octokit.repos.getArchiveLink(params);
 | 
			
		||||
        if (response.status != 200) {
 | 
			
		||||
            throw new Error(`Unexpected response from GitHub API. Status: ${response.status}, Data: ${response.data}`);
 | 
			
		||||
            throw new Error(`Unexpected response from GitHub API. Status: '${response.status}'`);
 | 
			
		||||
        }
 | 
			
		||||
        return Buffer.from(response.data); // response.data is ArrayBuffer
 | 
			
		||||
    });
 | 
			
		||||
@@ -9822,9 +9802,6 @@ class RetryHelper {
 | 
			
		||||
        this.maxAttempts = maxAttempts;
 | 
			
		||||
        this.minSeconds = Math.floor(minSeconds);
 | 
			
		||||
        this.maxSeconds = Math.floor(maxSeconds);
 | 
			
		||||
        if (this.minSeconds > this.maxSeconds) {
 | 
			
		||||
            throw new Error('min seconds should be less than or equal to max seconds');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    execute(action) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
@@ -12779,11 +12756,8 @@ function getInputs() {
 | 
			
		||||
    // LFS
 | 
			
		||||
    result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE';
 | 
			
		||||
    core.debug(`lfs = ${result.lfs}`);
 | 
			
		||||
    // Auth token
 | 
			
		||||
    result.authToken = core.getInput('token');
 | 
			
		||||
    // Persist credentials
 | 
			
		||||
    result.persistCredentials =
 | 
			
		||||
        (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE';
 | 
			
		||||
    // Access token
 | 
			
		||||
    result.accessToken = core.getInput('token');
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
exports.getInputs = getInputs;
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ class GitCommandManager {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async config(configKey: string, configValue: string): Promise<void> {
 | 
			
		||||
    await this.execGit(['config', '--local', configKey, configValue])
 | 
			
		||||
    await this.execGit(['config', configKey, configValue])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async configExists(configKey: string): Promise<boolean> {
 | 
			
		||||
@@ -124,7 +124,7 @@ class GitCommandManager {
 | 
			
		||||
      return `\\${x}`
 | 
			
		||||
    })
 | 
			
		||||
    const output = await this.execGit(
 | 
			
		||||
      ['config', '--local', '--name-only', '--get-regexp', pattern],
 | 
			
		||||
      ['config', '--name-only', '--get-regexp', pattern],
 | 
			
		||||
      true
 | 
			
		||||
    )
 | 
			
		||||
    return output.exitCode === 0
 | 
			
		||||
@@ -211,23 +211,20 @@ class GitCommandManager {
 | 
			
		||||
 | 
			
		||||
  async tryConfigUnset(configKey: string): Promise<boolean> {
 | 
			
		||||
    const output = await this.execGit(
 | 
			
		||||
      ['config', '--local', '--unset-all', configKey],
 | 
			
		||||
      ['config', '--unset-all', configKey],
 | 
			
		||||
      true
 | 
			
		||||
    )
 | 
			
		||||
    return output.exitCode === 0
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async tryDisableAutomaticGarbageCollection(): Promise<boolean> {
 | 
			
		||||
    const output = await this.execGit(
 | 
			
		||||
      ['config', '--local', 'gc.auto', '0'],
 | 
			
		||||
      true
 | 
			
		||||
    )
 | 
			
		||||
    const output = await this.execGit(['config', 'gc.auto', '0'], true)
 | 
			
		||||
    return output.exitCode === 0
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async tryGetFetchUrl(): Promise<string> {
 | 
			
		||||
    const output = await this.execGit(
 | 
			
		||||
      ['config', '--local', '--get', 'remote.origin.url'],
 | 
			
		||||
      ['config', '--get', 'remote.origin.url'],
 | 
			
		||||
      true
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import * as core from '@actions/core'
 | 
			
		||||
import * as coreCommand from '@actions/core/lib/command'
 | 
			
		||||
import * as fs from 'fs'
 | 
			
		||||
import * as fsHelper from './fs-helper'
 | 
			
		||||
import * as gitCommandManager from './git-command-manager'
 | 
			
		||||
@@ -20,8 +21,7 @@ export interface ISourceSettings {
 | 
			
		||||
  clean: boolean
 | 
			
		||||
  fetchDepth: number
 | 
			
		||||
  lfs: boolean
 | 
			
		||||
  authToken: string
 | 
			
		||||
  persistCredentials: boolean
 | 
			
		||||
  accessToken: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function getSource(settings: ISourceSettings): Promise<void> {
 | 
			
		||||
@@ -65,7 +65,7 @@ export async function getSource(settings: ISourceSettings): Promise<void> {
 | 
			
		||||
      `To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
 | 
			
		||||
    )
 | 
			
		||||
    await githubApiHelper.downloadRepository(
 | 
			
		||||
      settings.authToken,
 | 
			
		||||
      settings.accessToken,
 | 
			
		||||
      settings.repositoryOwner,
 | 
			
		||||
      settings.repositoryName,
 | 
			
		||||
      settings.ref,
 | 
			
		||||
@@ -94,9 +94,14 @@ export async function getSource(settings: ISourceSettings): Promise<void> {
 | 
			
		||||
    // Remove possible previous extraheader
 | 
			
		||||
    await removeGitConfig(git, authConfigKey)
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      // Config auth token
 | 
			
		||||
      await configureAuthToken(git, settings.authToken)
 | 
			
		||||
    // Add extraheader (auth)
 | 
			
		||||
    const base64Credentials = Buffer.from(
 | 
			
		||||
      `x-access-token:${settings.accessToken}`,
 | 
			
		||||
      'utf8'
 | 
			
		||||
    ).toString('base64')
 | 
			
		||||
    core.setSecret(base64Credentials)
 | 
			
		||||
    const authConfigValue = `AUTHORIZATION: basic ${base64Credentials}`
 | 
			
		||||
    await git.config(authConfigKey, authConfigValue)
 | 
			
		||||
 | 
			
		||||
    // LFS install
 | 
			
		||||
    if (settings.lfs) {
 | 
			
		||||
@@ -126,11 +131,6 @@ export async function getSource(settings: ISourceSettings): Promise<void> {
 | 
			
		||||
 | 
			
		||||
    // Dump some info about the checked out commit
 | 
			
		||||
    await git.log1()
 | 
			
		||||
    } finally {
 | 
			
		||||
      if (!settings.persistCredentials) {
 | 
			
		||||
        await removeGitConfig(git, authConfigKey)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -255,40 +255,6 @@ async function prepareExistingDirectory(
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function configureAuthToken(
 | 
			
		||||
  git: IGitCommandManager,
 | 
			
		||||
  authToken: string
 | 
			
		||||
): Promise<void> {
 | 
			
		||||
  // Configure a placeholder value. This approach avoids the credential being captured
 | 
			
		||||
  // by process creation audit events, which are commonly logged. For more information,
 | 
			
		||||
  // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | 
			
		||||
  const placeholder = `AUTHORIZATION: basic ***`
 | 
			
		||||
  await git.config(authConfigKey, placeholder)
 | 
			
		||||
 | 
			
		||||
  // Determine the basic credential value
 | 
			
		||||
  const basicCredential = Buffer.from(
 | 
			
		||||
    `x-access-token:${authToken}`,
 | 
			
		||||
    'utf8'
 | 
			
		||||
  ).toString('base64')
 | 
			
		||||
  core.setSecret(basicCredential)
 | 
			
		||||
 | 
			
		||||
  // Replace the value in the config file
 | 
			
		||||
  const configPath = path.join(git.getWorkingDirectory(), '.git', 'config')
 | 
			
		||||
  let content = (await fs.promises.readFile(configPath)).toString()
 | 
			
		||||
  const placeholderIndex = content.indexOf(placeholder)
 | 
			
		||||
  if (
 | 
			
		||||
    placeholderIndex < 0 ||
 | 
			
		||||
    placeholderIndex != content.lastIndexOf(placeholder)
 | 
			
		||||
  ) {
 | 
			
		||||
    throw new Error('Unable to replace auth placeholder in .git/config')
 | 
			
		||||
  }
 | 
			
		||||
  content = content.replace(
 | 
			
		||||
    placeholder,
 | 
			
		||||
    `AUTHORIZATION: basic ${basicCredential}`
 | 
			
		||||
  )
 | 
			
		||||
  await fs.promises.writeFile(configPath, content)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function removeGitConfig(
 | 
			
		||||
  git: IGitCommandManager,
 | 
			
		||||
  configKey: string
 | 
			
		||||
@@ -298,6 +264,21 @@ async function removeGitConfig(
 | 
			
		||||
    !(await git.tryConfigUnset(configKey))
 | 
			
		||||
  ) {
 | 
			
		||||
    // Load the config contents
 | 
			
		||||
    core.warning(`Failed to remove '${configKey}' from the git config`)
 | 
			
		||||
    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)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import {ReposGetArchiveLinkParams} from '@octokit/rest'
 | 
			
		||||
const IS_WINDOWS = process.platform === 'win32'
 | 
			
		||||
 | 
			
		||||
export async function downloadRepository(
 | 
			
		||||
  authToken: string,
 | 
			
		||||
  accessToken: string,
 | 
			
		||||
  owner: string,
 | 
			
		||||
  repo: string,
 | 
			
		||||
  ref: string,
 | 
			
		||||
@@ -22,7 +22,7 @@ export async function downloadRepository(
 | 
			
		||||
  // Download the archive
 | 
			
		||||
  let archiveData = await retryHelper.execute(async () => {
 | 
			
		||||
    core.info('Downloading the archive')
 | 
			
		||||
    return await downloadArchive(authToken, owner, repo, ref, commit)
 | 
			
		||||
    return await downloadArchive(accessToken, owner, repo, ref, commit)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // Write archive to disk
 | 
			
		||||
@@ -58,23 +58,19 @@ export async function downloadRepository(
 | 
			
		||||
  for (const fileName of await fs.promises.readdir(tempRepositoryPath)) {
 | 
			
		||||
    const sourcePath = path.join(tempRepositoryPath, fileName)
 | 
			
		||||
    const targetPath = path.join(repositoryPath, fileName)
 | 
			
		||||
    if (IS_WINDOWS) {
 | 
			
		||||
      await io.cp(sourcePath, targetPath, {recursive: true}) // Copy on Windows (Windows Defender may have a lock)
 | 
			
		||||
    } else {
 | 
			
		||||
    await io.mv(sourcePath, targetPath)
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
  io.rmRF(extractPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function downloadArchive(
 | 
			
		||||
  authToken: string,
 | 
			
		||||
  accessToken: string,
 | 
			
		||||
  owner: string,
 | 
			
		||||
  repo: string,
 | 
			
		||||
  ref: string,
 | 
			
		||||
  commit: string
 | 
			
		||||
): Promise<Buffer> {
 | 
			
		||||
  const octokit = new github.GitHub(authToken)
 | 
			
		||||
  const octokit = new github.GitHub(accessToken)
 | 
			
		||||
  const params: ReposGetArchiveLinkParams = {
 | 
			
		||||
    owner: owner,
 | 
			
		||||
    repo: repo,
 | 
			
		||||
@@ -84,7 +80,7 @@ async function downloadArchive(
 | 
			
		||||
  const response = await octokit.repos.getArchiveLink(params)
 | 
			
		||||
  if (response.status != 200) {
 | 
			
		||||
    throw new Error(
 | 
			
		||||
      `Unexpected response from GitHub API. Status: ${response.status}, Data: ${response.data}`
 | 
			
		||||
      `Unexpected response from GitHub API. Status: '${response.status}'`
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -97,12 +97,8 @@ export function getInputs(): ISourceSettings {
 | 
			
		||||
  result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE'
 | 
			
		||||
  core.debug(`lfs = ${result.lfs}`)
 | 
			
		||||
 | 
			
		||||
  // Auth token
 | 
			
		||||
  result.authToken = core.getInput('token')
 | 
			
		||||
 | 
			
		||||
  // Persist credentials
 | 
			
		||||
  result.persistCredentials =
 | 
			
		||||
    (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE'
 | 
			
		||||
  // Access token
 | 
			
		||||
  result.accessToken = core.getInput('token')
 | 
			
		||||
 | 
			
		||||
  return result
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,9 +17,6 @@ export class RetryHelper {
 | 
			
		||||
    this.maxAttempts = maxAttempts
 | 
			
		||||
    this.minSeconds = Math.floor(minSeconds)
 | 
			
		||||
    this.maxSeconds = Math.floor(maxSeconds)
 | 
			
		||||
    if (this.minSeconds > this.maxSeconds) {
 | 
			
		||||
      throw new Error('min seconds should be less than or equal to max seconds')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async execute<T>(action: () => Promise<T>): Promise<T> {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,7 @@ export const IsPost = !!process.env['STATE_isPost']
 | 
			
		||||
/**
 | 
			
		||||
 * The repository path for the POST action. The value is empty during the MAIN action.
 | 
			
		||||
 */
 | 
			
		||||
export const RepositoryPath =
 | 
			
		||||
  (process.env['STATE_repositoryPath'] as string) || ''
 | 
			
		||||
export const RepositoryPath = process.env['STATE_repositoryPath'] as string
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Save the repository path so the POST action can retrieve the value.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user