Compare commits

..

8 Commits

7 changed files with 179 additions and 36 deletions

View File

@ -11,7 +11,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 # todo: switch to v2 - uses: actions/checkout@v2
- run: npm ci - run: npm ci
- run: npm run build - run: npm run build
- run: npm run format-check - run: npm run format-check

157
README.md
View File

@ -6,27 +6,26 @@
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.
By default, the repository that triggered the workflow is checked-out, for the ref/SHA that triggered the event. Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth` to fetch more history. Refer [here](https://help.github.com/en/articles/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events.
Refer [here](https://help.github.com/en/articles/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events. The auth token is persisted in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup. Set `persist-credentials: false` to opt-out.
When Git 2.18 or higher is not in your PATH, falls back to the REST API to download the files.
# What's new # What's new
- Improved fetch performance - Improved performance
- The default behavior now fetches only the commit being checked-out - Fetches only a single commit by default
- Script authenticated git commands - Script authenticated git commands
- Persists the input `token` in the local git config - Auth token persisted 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`
- Creates a local branch - Creates a local branch
- No longer detached HEAD when checking out a branch - No longer detached HEAD when checking out a branch
- A local branch is created with the corresponding upstream branch set
- Improved layout - Improved layout
- The input `path` is always relative to $GITHUB_WORKSPACE - The input `path` is always relative to $GITHUB_WORKSPACE
- Aligns better with container actions, where $GITHUB_WORKSPACE gets mapped in - Aligns better with container actions, where $GITHUB_WORKSPACE gets mapped in
- Fallback to REST API download - 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 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
- Removed input `submodules` - Removed input `submodules`
Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous versions. Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous versions.
@ -48,7 +47,8 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
# Auth token used to fetch the repository. The token is stored in the local git # 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 # config, which enables your scripts to run authenticated git commands. The
# post-job step removes the token from the git config. # post-job step removes the token from the git config. [Learn more about creating
# and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
# Default: ${{ github.token }} # Default: ${{ github.token }}
token: '' token: ''
@ -73,26 +73,86 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
``` ```
<!-- end usage --> <!-- end usage -->
# Scenarios
- [Checkout a different branch](#Checkout-a-different-branch)
- [Checkout HEAD^](#Checkout-HEAD)
- [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side)
- [Checkout multiple repos (nested)](#Checkout-multiple-repos-nested)
- [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
- [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit)
- [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event)
- [Checkout submodules](#Checkout-submodules)
- [Checkout private submodules](#Checkout-private-submodules)
- [Fetch all tags](#Fetch-all-tags)
- [Fetch all branches](#Fetch-all-branches)
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
## Checkout a different branch ## Checkout a different branch
```yaml ```yaml
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
ref: some-branch ref: my-branch
``` ```
## Checkout a different, private repository ## Checkout HEAD^
```yaml ```yaml
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
repository: myAccount/myRepository fetch-depth: 2
ref: refs/heads/master - run: git checkout HEAD^
token: ${{ secrets.GitHub_PAT }} # `GitHub_PAT` is a secret that contains your PAT
``` ```
> - `${{ github.token }}` is scoped to the current repository, so if you want to checkout another repository that is private you will need to provide your own [PAT](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line).
## Checkout the HEAD commit of a PR, rather than the merge commit ## Checkout multiple repos (side by side)
```yaml
- name: Checkout
uses: actions/checkout@v2
with:
path: main
- name: Checkout tools repo
uses: actions/checkout@v2
with:
repository: my-org/my-tools
path: my-tools
```
## Checkout multiple repos (nested)
```yaml
- name: Checkout
uses: actions/checkout@v2
- name: Checkout tools repo
uses: actions/checkout@v2
with:
repository: my-org/my-tools
path: my-tools
```
## Checkout multiple repos (private)
```yaml
- name: Checkout
uses: actions/checkout@v2
with:
path: main
- name: Checkout private tools
uses: actions/checkout@v2
with:
repository: my-org/my-private-tools
token: ${{ secrets.GitHub_PAT }} # `GitHub_PAT` is a secret that contains your PAT
path: my-tools
```
> - `${{ github.token }}` is scoped to the current repository, so if you want to checkout a different repository that is private you will need to provide your own [PAT](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line).
## Checkout pull request HEAD commit instead of merge commit
```yaml ```yaml
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -100,6 +160,69 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
``` ```
## Checkout pull request on closed event
```yaml
on:
pull_request:
branches: [master]
types: [opened, synchronize, closed]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
```
## Checkout submodules
```yaml
- uses: actions/checkout@v2
- name: Checkout submodules
shell: bash
run: |
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
git submodule sync --recursive
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
```
## Checkout private submodules
```yaml
- uses: actions/checkout@v2
with:
token: ${{ secrets.MY_GITHUB_PAT }}
- name: Checkout submodules
shell: bash
run: |
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
git submodule sync --recursive
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
```
## Fetch all tags
```yaml
- uses: actions/checkout@v2
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
```
## Fetch all branches
```yaml
- uses: actions/checkout@v2
- run: |
git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*
```
## Fetch all history for all tags and branches
```yaml
- uses: actions/checkout@v2
- run: |
git fetch --prune --unshallow
```
# License # License
The scripts and documentation in this project are released under the [MIT License](LICENSE) The scripts and documentation in this project are released under the [MIT License](LICENSE)

View File

@ -13,7 +13,8 @@ inputs:
description: > description: >
Auth token used to fetch the repository. The token is stored in the local 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. git config, which enables your scripts to run authenticated git commands.
The post-job step removes the token from the git config. The post-job step removes the token from the git config. [Learn more about
creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
default: ${{ github.token }} default: ${{ github.token }}
persist-credentials: persist-credentials:
description: 'Whether to persist the token in the git config' description: 'Whether to persist the token in the git config'

20
dist/index.js vendored
View File

@ -4799,9 +4799,11 @@ class GitCommandManager {
branchList(remote) { branchList(remote) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const result = []; const result = [];
// Note, this implementation uses "rev-parse --symbolic" because the output from // Note, this implementation uses "rev-parse --symbolic-full-name" because the output from
// "branch --list" is more difficult when in a detached HEAD state. // "branch --list" is more difficult when in a detached HEAD state.
const args = ['rev-parse', '--symbolic']; // Note, this implementation uses "rev-parse --symbolic-full-name" because there is a bug
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names.
const args = ['rev-parse', '--symbolic-full-name'];
if (remote) { if (remote) {
args.push('--remotes=origin'); args.push('--remotes=origin');
} }
@ -4812,6 +4814,12 @@ class GitCommandManager {
for (let branch of output.stdout.trim().split('\n')) { for (let branch of output.stdout.trim().split('\n')) {
branch = branch.trim(); branch = branch.trim();
if (branch) { if (branch) {
if (branch.startsWith('refs/heads/')) {
branch = branch.substr('refs/heads/'.length);
}
else if (branch.startsWith('refs/remotes/')) {
branch = branch.substr('refs/remotes/'.length);
}
result.push(branch); result.push(branch);
} }
} }
@ -4887,11 +4895,9 @@ class GitCommandManager {
} }
isDetached() { isDetached() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// Note, this implementation uses "branch --show-current" because // Note, "branch --show-current" would be simpler but isn't available until Git 2.22
// "rev-parse --symbolic-full-name HEAD" can fail on a new repo const output = yield this.execGit(['rev-parse', '--symbolic-full-name', '--verify', '--quiet', 'HEAD'], true);
// with nothing checked out. return !output.stdout.trim().startsWith('refs/heads/');
const output = yield this.execGit(['branch', '--show-current']);
return output.stdout.trim() === '';
}); });
} }
lfsFetch(ref) { lfsFetch(ref) {

View File

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

View File

@ -77,10 +77,12 @@ class GitCommandManager {
async branchList(remote: boolean): Promise<string[]> { async branchList(remote: boolean): Promise<string[]> {
const result: string[] = [] const result: string[] = []
// Note, this implementation uses "rev-parse --symbolic" because the output from // Note, this implementation uses "rev-parse --symbolic-full-name" because the output from
// "branch --list" is more difficult when in a detached HEAD state. // "branch --list" is more difficult when in a detached HEAD state.
// Note, this implementation uses "rev-parse --symbolic-full-name" because there is a bug
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names.
const args = ['rev-parse', '--symbolic'] const args = ['rev-parse', '--symbolic-full-name']
if (remote) { if (remote) {
args.push('--remotes=origin') args.push('--remotes=origin')
} else { } else {
@ -92,6 +94,12 @@ class GitCommandManager {
for (let branch of output.stdout.trim().split('\n')) { for (let branch of output.stdout.trim().split('\n')) {
branch = branch.trim() branch = branch.trim()
if (branch) { if (branch) {
if (branch.startsWith('refs/heads/')) {
branch = branch.substr('refs/heads/'.length)
} else if (branch.startsWith('refs/remotes/')) {
branch = branch.substr('refs/remotes/'.length)
}
result.push(branch) result.push(branch)
} }
} }
@ -170,12 +178,12 @@ class GitCommandManager {
} }
async isDetached(): Promise<boolean> { async isDetached(): Promise<boolean> {
// Note, this implementation uses "branch --show-current" because // Note, "branch --show-current" would be simpler but isn't available until Git 2.22
// "rev-parse --symbolic-full-name HEAD" can fail on a new repo const output = await this.execGit(
// with nothing checked out. ['rev-parse', '--symbolic-full-name', '--verify', '--quiet', 'HEAD'],
true
const output = await this.execGit(['branch', '--show-current']) )
return output.stdout.trim() === '' return !output.stdout.trim().startsWith('refs/heads/')
} }
async lfsFetch(ref: string): Promise<void> { async lfsFetch(ref: string): Promise<void> {

View File

@ -65,9 +65,14 @@ function updateUsage(
let segment: string = description let segment: string = description
if (description.length > width) { if (description.length > width) {
segment = description.substr(0, width + 1) segment = description.substr(0, width + 1)
while (!segment.endsWith(' ')) { while (!segment.endsWith(' ') && segment) {
segment = segment.substr(0, segment.length - 1) segment = segment.substr(0, segment.length - 1)
} }
// Trimmed too much?
if (segment.length < width * 0.67) {
segment = description
}
} else { } else {
segment = description segment = description
} }