Compare commits

...

51 Commits

Author SHA1 Message Date
CrazyMax
28218f9b04 Merge pull request #66 from crazy-max/remove-os-limitation
Remove os limitation
2021-04-28 00:38:38 +02:00
CrazyMax
7439f8b467 Remove os limitation
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-04-28 00:34:32 +02:00
CrazyMax
4b206288bd Merge pull request #61 from crazy-max/workflow
Enhance workflow
2021-03-31 18:49:11 +02:00
CrazyMax
4abf7e9de3 Enhance workflow
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-03-31 18:34:49 +02:00
CrazyMax
175e20ba61 Merge pull request #60 from syscll/docs-quay-io
Add Quay.io usage docs
2021-03-31 09:37:40 +02:00
Dan Bond
92a2593650 Update TOC 2021-03-26 14:58:30 -07:00
Dan Bond
957a8f0d4a README: add quay.io usage docs 2021-03-26 11:37:33 -07:00
CrazyMax
971b76aea9 Merge pull request #59 from crazy-max/ghcr-github-token
Use GITHUB_TOKEN with GHCR
2021-03-25 00:14:10 +01:00
CrazyMax
5b092cf2f7 Use GITHUB_TOKEN with GHCR
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-03-24 23:24:06 +01:00
CrazyMax
ab81ae06bb Merge pull request #58 from crazy-max/cleanup
Remove label workflow
2021-03-18 20:09:00 +01:00
CrazyMax
f4a3bbc2c6 Remove label workflow
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-03-18 20:07:30 +01:00
CrazyMax
e2302b10cc Merge pull request #55 from crazy-max/ghcr-doc
Improve GHCR doc
2021-02-16 13:30:19 +01:00
CrazyMax
c2c723b5d1 Improve GHCR doc
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-02-16 13:28:22 +01:00
CrazyMax
ab80d026d4 Merge pull request #48 from crazy-max/e2e-gar
Test login against GAR
2020-12-22 17:34:18 +01:00
CrazyMax
13fa0663e1 Test login against GAR
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-22 11:27:40 +01:00
CrazyMax
75e7be0db4 Merge pull request #42 from crazy-max/login-test-3
Test login against GCR
2020-12-22 11:12:35 +01:00
CrazyMax
d6f5c68835 Test login against GCR
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-22 11:11:31 +01:00
CrazyMax
f3364599c6 Merge pull request #40 from crazy-max/registry-ids
Handle Amazon ECR registries associated with other accounts
2020-12-18 07:41:31 +01:00
CrazyMax
24646ef465 Get AccountID from registry URL and handle ECR registry through regexp
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-17 20:22:02 +01:00
CrazyMax
9f189206e8 Merge pull request #44 from crazy-max/update-readme
Fix ci badge in README
2020-12-17 12:04:43 +01:00
CrazyMax
aed1d0c0c1 Fix ci badge in README
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-17 12:04:18 +01:00
CrazyMax
31722002f5 Merge pull request #43 from crazy-max/no-region-public-ecr
Keep default region for public ECR
2020-12-17 11:41:28 +01:00
CrazyMax
2a481b4109 Keep default region for public ECR
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-17 11:39:52 +01:00
CrazyMax
9bed62818a Merge pull request #27 from crazy-max/login-test-2
Test login against registries pt.2
2020-12-17 11:28:39 +01:00
CrazyMax
c718c795e7 Update for public ECR
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-17 11:06:21 +01:00
CrazyMax
7b79d7e834 Merge pull request #41 from crazy-max/fix-public-ecr
Fix public ECR login with AWS CLI v1
2020-12-17 07:52:03 +01:00
CrazyMax
f53ca527f7 Fix public ECR login with AWS CLI v1
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-17 07:49:50 +01:00
CrazyMax
d3160f671f Handle Amazon ECR registries associated with other accounts
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-16 21:53:24 +01:00
Chad Metcalf
3b14bab101 Merge pull request #37 from crazy-max/public-ecr
Add support for public ECR
2020-12-11 00:48:46 -08:00
CrazyMax
1e75de0e0e Add support for public ECR
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-11 07:15:35 +01:00
CrazyMax
7c9afe235c Merge pull request #35 from crazy-max/dind
Add example with DinD
2020-12-11 06:34:43 +01:00
CrazyMax
7dc3c3a70e Add example with DinD
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-08 19:12:44 +01:00
CrazyMax
b17cf6ab8f Merge branch 'master' into login-test-2 2020-11-18 18:49:20 +01:00
CrazyMax
27c3146301 Test login against registries part 2
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-11-18 18:47:53 +01:00
CrazyMax
12fd63324c Merge pull request #31 from crazy-max/master
Use major version of actions
2020-11-18 18:42:59 +01:00
CrazyMax
a3de3de177 Use major version of actions
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-11-18 18:41:33 +01:00
CrazyMax
9cbd4f95c4 Merge pull request #28 from pgressa/ocir
Extend documentation by OCI OCIR
2020-11-10 17:38:01 +01:00
Pavol Gressa
ad9eb3b250 Extend documentation by OCI OCIR 2020-11-10 17:24:49 +01:00
CrazyMax
71b3c789fa Merge pull request #23 from crazy-max/login-test
Test login against registries pt.1
2020-11-10 10:05:20 +01:00
CrazyMax
5e0bc83b38 Test login against registries
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-11-10 10:02:42 +01:00
CrazyMax
1f68ce02d7 Add workflow_dispatch event
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-10-28 17:03:42 +01:00
CrazyMax
767b2f4b7b Merge branch 'master' into login-test 2020-10-28 15:31:07 +01:00
Tõnis Tiigi
ddf06e1fed Merge pull request #22 from crazy-max/gar-example
Add example for Google Artifact Registry
2020-10-23 10:56:23 -07:00
CrazyMax
a5aa81adc8 Test login against registries
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-10-23 18:09:56 +02:00
CrazyMax
3b9e1f51cd Add example for Google Artifact Registry
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-10-23 16:30:05 +02:00
Tõnis Tiigi
adb73476b6 Merge pull request #21 from crazy-max/handle-aws-env
Handle AWS credentials
2020-10-20 09:14:56 -07:00
CrazyMax
5df5104555 Handle AWS credentials
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-10-20 14:42:02 +02:00
Tõnis Tiigi
39ef12fb7a Merge pull request #18 from crazy-max/username-required
Username required
2020-10-16 15:54:07 -07:00
CrazyMax
1c402b7c97 Fix tests
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-10-16 18:34:48 +02:00
CrazyMax
1c2cf9942d Username required
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-10-16 18:24:41 +02:00
Bryan Clark
4b15841c41 Mostly tests and some small changes (#16)
* Create docker.test.ts
* Add context tests
* test main
2020-10-09 10:30:45 +00:00
24 changed files with 955 additions and 353 deletions

2
.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
/coverage
/node_modules

View File

@@ -2,20 +2,24 @@
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE). Contributions to this project are [released](https://docs.github.com/en/github/site-policy/github-terms-of-service#6-contributions-under-repository-license)
to the public under the [project's open source license](LICENSE).
## Submitting a pull request ## Submitting a pull request
1. [Fork](https://github.com/docker/login-action/fork) and clone the repository 1. [Fork](https://github.com/docker/login-action/fork) and clone the repository
2. Configure and install the dependencies: `yarn install` 2. Configure and install the dependencies: `yarn install`
4. Create a new branch: `git checkout -b my-branch-name` 3. Create a new branch: `git checkout -b my-branch-name`
5. Make your change 4. Make your changes
6. Run pre-checkin: `yarn run pre-checkin` 5. Make sure the tests pass: `docker buildx bake test`
7. Push to your fork and [submit a pull request](https://github.com/docker/login-action/compare) 6. Format code and build javascript artifacts: `docker buildx bake pre-checkin`
8. Pat your self on the back and wait for your pull request to be reviewed and merged. 7. Validate all code has correctly formatted and built: `docker buildx bake validate`
8. Push to your fork and [submit a pull request](https://github.com/docker/login-action/compare)
9. Pat your self on the back and wait for your pull request to be reviewed and merged.
Here are a few things you can do that will increase the likelihood of your pull request being accepted: Here are a few things you can do that will increase the likelihood of your pull request being accepted:
- Write tests.
- Make sure the `README.md` and any other relevant **documentation are kept up-to-date**. - Make sure the `README.md` and any other relevant **documentation are kept up-to-date**.
- We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as **separate pull requests**. - Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as **separate pull requests**.
@@ -24,5 +28,5 @@ Here are a few things you can do that will increase the likelihood of your pull
## Resources ## Resources
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) - [Using Pull Requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests)
- [GitHub Help](https://help.github.com) - [GitHub Help](https://docs.github.com/en)

View File

@@ -30,4 +30,5 @@ about: Create a report to help us improve
### Logs ### Logs
> Download the [log file of your build](https://help.github.com/en/actions/configuring-and-managing-workflows/managing-a-workflow-run#downloading-logs) and [attach it](https://help.github.com/en/github/managing-your-work-on-github/file-attachments-on-issues-and-pull-requests) to this issue. > Download the [log file of your build](https://docs.github.com/en/actions/managing-workflow-runs/using-workflow-run-logs#downloading-logs)
> and [attach it](https://docs.github.com/en/github/managing-your-work-on-github/file-attachments-on-issues-and-pull-requests) to this issue.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
.github/ghcr-manage-actions-access.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

77
.github/labels.yml vendored
View File

@@ -1,77 +0,0 @@
## more info https://github.com/crazy-max/ghaction-github-labeler
- # automerge
name: ":bell: automerge"
color: "8f4fbc"
description: ""
- # bot
name: ":robot: bot"
color: "69cde9"
description: ""
- # bug
name: ":bug: bug"
color: "b60205"
description: ""
- # dependencies
name: ":game_die: dependencies"
color: "0366d6"
description: ""
- # documentation
name: ":memo: documentation"
color: "c5def5"
description: ""
- # duplicate
name: ":busts_in_silhouette: duplicate"
color: "cccccc"
description: ""
- # enhancement
name: ":sparkles: enhancement"
color: "0054ca"
description: ""
- # feature request
name: ":bulb: feature request"
color: "0e8a16"
description: ""
- # feedback
name: ":mega: feedback"
color: "03a9f4"
description: ""
- # future maybe
name: ":rocket: future maybe"
color: "fef2c0"
description: ""
- # good first issue
name: ":hatching_chick: good first issue"
color: "7057ff"
description: ""
- # help wanted
name: ":pray: help wanted"
color: "4caf50"
description: ""
- # hold
name: ":hand: hold"
color: "24292f"
description: ""
- # invalid
name: ":no_entry_sign: invalid"
color: "e6e6e6"
description: ""
- # maybe bug
name: ":interrobang: maybe bug"
color: "ff5722"
description: ""
- # needs more info
name: ":thinking: needs more info"
color: "795548"
description: ""
- # question
name: ":question: question"
color: "3f51b5"
description: ""
- # upstream
name: ":eyes: upstream"
color: "fbca04"
description: ""
- # wontfix
name: ":coffin: wontfix"
color: "ffffff"
description: ""

View File

@@ -1,53 +1,154 @@
name: ci name: ci
on: on:
workflow_dispatch:
schedule:
- cron: '0 10 * * *' # everyday at 10am
push: push:
branches: branches:
- master - 'master'
- releases/v* - 'releases/v*'
jobs: jobs:
# dockerhub: stop-docker:
# runs-on: ${{ matrix.os }}
# strategy:
# fail-fast: false
# matrix:
# os:
# - ubuntu-20.04
# - ubuntu-18.04
# - ubuntu-16.04
# logout:
# - true
# - false
# steps:
# -
# name: Checkout
# uses: actions/checkout@v2.3.2
# -
# name: Login to DockerHub
# uses: ./
# with:
# username: ${{ secrets.DOCKERHUB_USERNAME_TEST }}
# password: ${{ secrets.DOCKERHUB_PASSWORD_TEST }}
# logout: ${{ matrix.logout }}
# -
# name: Clear
# if: always()
# run: |
# rm -f ${HOME}/.docker/config.json
gpr:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
logout:
- true
- false
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2.3.2 uses: actions/checkout@v2
-
name: Stop docker
run: |
sudo systemctl stop docker
-
name: Login to GitHub Container Registry
uses: ./
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
dind:
runs-on: ubuntu-latest
env:
DOCKER_CONFIG: $HOME/.docker
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Login to GitHub Container Registry
uses: ./
with:
registry: ghcr.io
username: ${{ secrets.GHCR_USERNAME }}
password: ${{ secrets.GHCR_PAT }}
-
name: DinD
uses: docker://docker
with:
entrypoint: docker
args: pull ghcr.io/docker-ghactiontest/test
-
name: Pull private image
run: |
docker image prune -a -f >/dev/null 2>&1
docker pull ghcr.io/docker-ghactiontest/test
dockerhub:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-20.04
- ubuntu-18.04
- ubuntu-16.04
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Login to Docker Hub
uses: ./
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
ecr:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-20.04
- ubuntu-18.04
- ubuntu-16.04
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Login to ECR
uses: ./
with:
registry: ${{ secrets.AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
ecr-aws-creds:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-20.04
- ubuntu-18.04
- ubuntu-16.04
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
-
name: Login to ECR
uses: ./
with:
registry: ${{ secrets.AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com
ecr-public:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-20.04
- ubuntu-18.04
- ubuntu-16.04
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Login to Public ECR
uses: ./
with:
registry: public.ecr.aws
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
github-package:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
- -
name: Login to GitHub Package Registry name: Login to GitHub Package Registry
uses: ./ uses: ./
@@ -55,65 +156,59 @@ jobs:
registry: docker.pkg.github.com registry: docker.pkg.github.com
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
logout: ${{ matrix.logout }}
-
name: Clear
if: always()
run: |
rm -f ${HOME}/.docker/config.json
# gitlab: github-container:
# runs-on: ubuntu-latest runs-on: ubuntu-latest
# strategy: steps:
# fail-fast: false -
# matrix: name: Checkout
# logout: uses: actions/checkout@v2
# - true -
# - false name: Login to GitHub Container Registry
# steps: uses: ./
# - with:
# name: Checkout registry: ghcr.io
# uses: actions/checkout@v2.3.2 username: ${{ github.repository_owner }}
# - password: ${{ secrets.GITHUB_TOKEN }}
# name: Login to GitLab
# uses: ./ gitlab:
# with: runs-on: ubuntu-latest
# registry: registry.gitlab.com steps:
# username: ${{ secrets.GITLAB_USERNAME_TEST }} -
# password: ${{ secrets.GITLAB_PASSWORD_TEST }} name: Checkout
# logout: ${{ matrix.logout }} uses: actions/checkout@v2
# - -
# name: Clear name: Login to GitLab
# if: always() uses: ./
# run: | with:
# rm -f ${HOME}/.docker/config.json registry: registry.gitlab.com
# username: ${{ secrets.GITLAB_USERNAME }}
# ecr: password: ${{ secrets.GITLAB_TOKEN }}
# runs-on: ${{ matrix.os }}
# strategy: google-artifact:
# fail-fast: false runs-on: ubuntu-latest
# matrix: steps:
# os: -
# - ubuntu-20.04 name: Checkout
# - ubuntu-18.04 uses: actions/checkout@v2
# - ubuntu-16.04 -
# logout: name: Login to Google Artifact Registry
# - true uses: ./
# - false with:
# steps: registry: ${{ secrets.GAR_LOCATION }}-docker.pkg.dev
# - username: _json_key
# name: Checkout password: ${{ secrets.GAR_JSON_KEY }}
# uses: actions/checkout@v2.3.1
# - google-container:
# name: Login to ECR runs-on: ubuntu-latest
# uses: ./ steps:
# with: -
# registry: ${{ secrets.AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com name: Checkout
# username: ${{ secrets.AWS_ACCESS_KEY_ID }} uses: actions/checkout@v2
# password: ${{ secrets.AWS_SECRET_ACCESS_KEY }} -
# logout: ${{ matrix.logout }} name: Login to Google Container Registry
# - uses: ./
# name: Clear with:
# if: always() registry: gcr.io
# run: | username: _json_key
# rm -f ${HOME}/.docker/config.json password: ${{ secrets.GCR_JSON_KEY }}

View File

@@ -1,20 +0,0 @@
name: labels
on:
push:
branches:
- 'master'
paths:
- '.github/labels.yml'
- '.github/workflows/labels.yml'
jobs:
labeler:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2.3.2
-
name: Run Labeler
uses: crazy-max/ghaction-github-labeler@v3.0.0

View File

@@ -3,38 +3,32 @@ name: test
on: on:
push: push:
branches: branches:
- master - 'master'
- releases/v* - 'releases/v*'
paths-ignore:
- '**.md'
pull_request: pull_request:
paths-ignore: branches:
- '**.md' - 'master'
- 'releases/v*'
jobs: jobs:
test: test:
runs-on: ${{ matrix.os }} runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os:
- ubuntu-20.04
- ubuntu-18.04
- ubuntu-16.04
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2.3.2 uses: actions/checkout@v2
- -
name: Install name: Validate
run: yarn install uses: docker/bake-action@v1
with:
targets: validate
- -
name: Test name: Test
run: yarn run test uses: docker/bake-action@v1
with:
targets: test
- -
name: Upload coverage name: Upload coverage
uses: codecov/codecov-action@v1.0.13 uses: codecov/codecov-action@v1
if: success()
with: with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage/clover.xml file: ./coverage/clover.xml

220
README.md
View File

@@ -1,6 +1,6 @@
[![GitHub release](https://img.shields.io/github/release/docker/login-action.svg?style=flat-square)](https://github.com/docker/login-action/releases/latest) [![GitHub release](https://img.shields.io/github/release/docker/login-action.svg?style=flat-square)](https://github.com/docker/login-action/releases/latest)
[![GitHub marketplace](https://img.shields.io/badge/marketplace-docker--login-blue?logo=github&style=flat-square)](https://github.com/marketplace/actions/docker-login) [![GitHub marketplace](https://img.shields.io/badge/marketplace-docker--login-blue?logo=github&style=flat-square)](https://github.com/marketplace/actions/docker-login)
[![CI workflow](https://img.shields.io/github/workflow/status/docker/login-action/test?label=ci&logo=github&style=flat-square)](https://github.com/docker/login-action/actions?workflow=ci) [![CI workflow](https://img.shields.io/github/workflow/status/docker/login-action/ci?label=ci&logo=github&style=flat-square)](https://github.com/docker/login-action/actions?workflow=ci)
[![Test workflow](https://img.shields.io/github/workflow/status/docker/login-action/test?label=test&logo=github&style=flat-square)](https://github.com/docker/login-action/actions?workflow=test) [![Test workflow](https://img.shields.io/github/workflow/status/docker/login-action/test?label=test&logo=github&style=flat-square)](https://github.com/docker/login-action/actions?workflow=test)
[![Codecov](https://img.shields.io/codecov/c/github/docker/login-action?logo=codecov&style=flat-square)](https://codecov.io/gh/docker/login-action) [![Codecov](https://img.shields.io/codecov/c/github/docker/login-action?logo=codecov&style=flat-square)](https://codecov.io/gh/docker/login-action)
@@ -8,33 +8,31 @@
GitHub Action to login against a Docker registry. GitHub Action to login against a Docker registry.
> :bulb: See also:
> * [setup-buildx](https://github.com/docker/setup-buildx-action) action
> * [setup-qemu](https://github.com/docker/setup-qemu-action) action
> * [build-push](https://github.com/docker/build-push-action) action
![Screenshot](.github/docker-login.png) ![Screenshot](.github/docker-login.png)
___ ___
* [Usage](#usage) * [Usage](#usage)
* [DockerHub](#dockerhub) * [Docker Hub](#docker-hub)
* [GitHub Packages Docker Registry](#github-packages-docker-registry) * [GitHub Packages Docker Registry](#github-packages-docker-registry)
* [GitHub Container Registry](#github-container-registry) * [GitHub Container Registry](#github-container-registry)
* [GitLab](#gitlab) * [GitLab](#gitlab)
* [Azure Container Registry (ACR)](#azure-container-registry-acr) * [Azure Container Registry (ACR)](#azure-container-registry-acr)
* [Google Container Registry (GCR)](#google-container-registry-gcr) * [Google Container Registry (GCR)](#google-container-registry-gcr)
* [Google Artifact Registry (GAR)](#google-artifact-registry-gar)
* [AWS Elastic Container Registry (ECR)](#aws-elastic-container-registry-ecr) * [AWS Elastic Container Registry (ECR)](#aws-elastic-container-registry-ecr)
* [AWS Public Elastic Container Registry (ECR)](#aws-public-elastic-container-registry-ecr)
* [OCI Oracle Cloud Infrastructure Registry (OCIR)](#oci-oracle-cloud-infrastructure-registry-ocir)
* [Quay.io](#quayio)
* [Customizing](#customizing) * [Customizing](#customizing)
* [inputs](#inputs) * [inputs](#inputs)
* [Keep up-to-date with GitHub Dependabot](#keep-up-to-date-with-github-dependabot) * [Keep up-to-date with GitHub Dependabot](#keep-up-to-date-with-github-dependabot)
* [Limitation](#limitation)
## Usage ## Usage
### DockerHub ### Docker Hub
To authenticate against [DockerHub](https://hub.docker.com) it's strongly recommended to create a To authenticate against [Docker Hub](https://hub.docker.com) it's strongly recommended to create a
[personal access token](https://docs.docker.com/docker-hub/access-tokens/) as an alternative to your password. [personal access token](https://docs.docker.com/docker-hub/access-tokens/) as an alternative to your password.
```yaml ```yaml
@@ -49,7 +47,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- -
name: Login to DockerHub name: Login to Docker Hub
uses: docker/login-action@v1 uses: docker/login-action@v1
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -86,9 +84,12 @@ jobs:
### GitHub Container Registry ### GitHub Container Registry
To authenticate against the [GitHub Container Registry](https://docs.github.com/en/packages/getting-started-with-github-container-registry), To use the [GitHub Container Registry](https://docs.github.com/en/packages/getting-started-with-github-container-registry),
you will need to create a new [personal access token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) you need to [enable this feature for your personal or organization account](https://docs.github.com/en/packages/guides/enabling-improved-container-support).
with the [appropriate scopes](https://docs.github.com/en/packages/getting-started-with-github-container-registry/migrating-to-github-container-registry-for-docker-images#authenticating-with-the-container-registry).
To [authenticate against it](https://docs.github.com/en/packages/guides/migrating-to-github-container-registry-for-docker-images#authenticating-with-the-container-registry),
use the [`GITHUB_TOKEN`](https://docs.github.com/en/actions/reference/authentication-in-a-workflow) for the best
security and experience.
```yaml ```yaml
name: ci name: ci
@@ -107,9 +108,16 @@ jobs:
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.CR_PAT }} password: ${{ secrets.GITHUB_TOKEN }}
``` ```
You may need to manage write and read access of GitHub Actions for repositories in the container settings:
![Manage Actions access](.github/ghcr-manage-actions-access.gif)
You can also use a [personal access token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token)
with the [appropriate scopes](https://docs.github.com/en/packages/getting-started-with-github-container-registry/migrating-to-github-container-registry-for-docker-images#authenticating-with-the-container-registry).
### GitLab ### GitLab
```yaml ```yaml
@@ -162,6 +170,11 @@ jobs:
### Google Container Registry (GCR) ### Google Container Registry (GCR)
> [Google Artifact Registry](#google-artifact-registry-gar) is the evolution of Google Container Registry. As a
> fully-managed service with support for both container images and non-container artifacts. If you currently use
> Google Container Registry, use the information [on this page](https://cloud.google.com/artifact-registry/docs/transition/transition-from-gcr)
> to learn about transitioning to Google Artifact Registry.
Use a service account with the ability to push to GCR and [configure access control](https://cloud.google.com/container-registry/docs/access-control). Use a service account with the ability to push to GCR and [configure access control](https://cloud.google.com/container-registry/docs/access-control).
Then create and download the JSON key for this service account and save content of `.json` file Then create and download the JSON key for this service account and save content of `.json` file
[as a secret](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository) [as a secret](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository)
@@ -187,9 +200,39 @@ jobs:
password: ${{ secrets.GCR_JSON_KEY }} password: ${{ secrets.GCR_JSON_KEY }}
``` ```
### Google Artifact Registry (GAR)
Use a service account with the ability to push to GAR and [configure access control](https://cloud.google.com/artifact-registry/docs/access-control).
Then create and download the JSON key for this service account and save content of `.json` file
[as a secret](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository)
called `GAR_JSON_KEY` in your GitHub repo. Ensure you set the username to `_json_key`.
```yaml
name: ci
on:
push:
branches: master
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to GAR
uses: docker/login-action@v1
with:
registry: <location>-docker.pkg.dev
username: _json_key
password: ${{ secrets.GAR_JSON_KEY }}
```
> Replace `<location>` with the regional or multi-regional [location](https://cloud.google.com/artifact-registry/docs/repo-organize#locations)
> of the repository where the image is stored.
### AWS Elastic Container Registry (ECR) ### AWS Elastic Container Registry (ECR)
Use an IAM user with the [ability to push to ECR](https://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html). Use an IAM user with the ability to [push to ECR with `AmazonEC2ContainerRegistryPowerUser` managed policy for example](https://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html#AmazonEC2ContainerRegistryPowerUser).
Then create and download access keys and save `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` [as secrets](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository) Then create and download access keys and save `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` [as secrets](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository)
in your GitHub repo. in your GitHub repo.
@@ -213,8 +256,149 @@ jobs:
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }} password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
``` ```
If you need to log in to Amazon ECR registries associated with other accounts, you can use the `AWS_ACCOUNT_IDS`
environment variable:
```yaml
name: ci
on:
push:
branches: master
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to ECR
uses: docker/login-action@v1
with:
registry: <aws-account-number>.dkr.ecr.<region>.amazonaws.com
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
env:
AWS_ACCOUNT_IDS: 012345678910,023456789012
```
> Only available with [AWS CLI version 1](https://docs.aws.amazon.com/cli/latest/reference/ecr/get-login.html)
You can also use the [Configure AWS Credentials](https://github.com/aws-actions/configure-aws-credentials) action in
combination with this action:
```yaml
name: ci
on:
push:
branches: master
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: <region>
-
name: Login to ECR
uses: docker/login-action@v1
with:
registry: <aws-account-number>.dkr.ecr.<region>.amazonaws.com
```
> Replace `<aws-account-number>` and `<region>` with their respective values. > Replace `<aws-account-number>` and `<region>` with their respective values.
### AWS Public Elastic Container Registry (ECR)
Use an IAM user with the ability to [push to ECR Public with `AmazonElasticContainerRegistryPublicPowerUser` managed policy for example](https://docs.aws.amazon.com/AmazonECR/latest/public/public-ecr-managed-policies.html#AmazonElasticContainerRegistryPublicPowerUser).
Then create and download access keys and save `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` [as secrets](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository)
in your GitHub repo.
```yaml
name: ci
on:
push:
branches: master
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to Public ECR
uses: docker/login-action@v1
with:
registry: public.ecr.aws
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
env:
AWS_REGION: <region>
```
> Replace `<region>` with its respective value (default `us-east-1`).
### OCI Oracle Cloud Infrastructure Registry (OCIR)
To push into OCIR in specific tenancy the [username](https://www.oracle.com/webfolder/technetwork/tutorials/obe/oci/registry/index.html#LogintoOracleCloudInfrastructureRegistryfromtheDockerCLI)
must be placed in format `<tenancy>/<username>` (in case of federated tenancy use the format
`<tenancy-namespace>/oracleidentitycloudservice/<username>`).
For password [create an auth token](https://www.oracle.com/webfolder/technetwork/tutorials/obe/oci/registry/index.html#GetanAuthToken).
Save username and token [as a secrets](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository)
in your GitHub repo.
```yaml
name: ci
on:
push:
branches: master
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to OCIR
uses: docker/login-action@v1
with:
registry: <region>.ocir.io
username: ${{ secrets.OCI_USERNAME }}
password: ${{ secrets.OCI_TOKEN }}
```
> Replace `<region>` with their respective values from [availability regions](https://docs.cloud.oracle.com/iaas/Content/Registry/Concepts/registryprerequisites.htm#Availab)
### Quay.io
Use a [Robot account](https://docs.quay.io/glossary/robot-accounts.html) with the ability to push to a public/private Quay.io repository.
```yaml
name: ci
on:
push:
branches: master
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to Quay.io
uses: docker/login-action@v1
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_ROBOT_TOKEN }}
```
## Customizing ## Customizing
### inputs ### inputs
@@ -243,7 +427,3 @@ updates:
schedule: schedule:
interval: "daily" interval: "daily"
``` ```
## Limitation
This action is only available for Linux [virtual environments](https://help.github.com/en/articles/virtual-environments-for-github-actions#supported-virtual-environments-and-hardware-resources).

View File

@@ -5,12 +5,28 @@ describe('isECR', () => {
test.each([ test.each([
['registry.gitlab.com', false], ['registry.gitlab.com', false],
['gcr.io', false], ['gcr.io', false],
['012345678901.dkr.ecr.eu-west-3.amazonaws.com', true] ['012345678901.dkr.ecr.eu-west-3.amazonaws.com', true],
['876820548815.dkr.ecr.cn-north-1.amazonaws.com.cn', true],
['390948362332.dkr.ecr.cn-northwest-1.amazonaws.com.cn', true],
['public.ecr.aws', true]
])('given registry %p', async (registry, expected) => { ])('given registry %p', async (registry, expected) => {
expect(await aws.isECR(registry)).toEqual(expected); expect(await aws.isECR(registry)).toEqual(expected);
}); });
}); });
describe('isPubECR', () => {
test.each([
['registry.gitlab.com', false],
['gcr.io', false],
['012345678901.dkr.ecr.eu-west-3.amazonaws.com', false],
['876820548815.dkr.ecr.cn-north-1.amazonaws.com.cn', false],
['390948362332.dkr.ecr.cn-northwest-1.amazonaws.com.cn', false],
['public.ecr.aws', true]
])('given registry %p', async (registry, expected) => {
expect(await aws.isPubECR(registry)).toEqual(expected);
});
});
describe('getCLI', () => { describe('getCLI', () => {
it('exists', async () => { it('exists', async () => {
const awsPath = await aws.getCLI(); const awsPath = await aws.getCLI();
@@ -45,10 +61,39 @@ describe('parseCLIVersion', () => {
}); });
describe('getRegion', () => { describe('getRegion', () => {
test.each([['012345678901.dkr.ecr.eu-west-3.amazonaws.com', 'eu-west-3']])( test.each([
'given registry %p', ['012345678901.dkr.ecr.eu-west-3.amazonaws.com', 'eu-west-3'],
async (registry, expected) => { ['876820548815.dkr.ecr.cn-north-1.amazonaws.com.cn', 'cn-north-1'],
expect(await aws.getRegion(registry)).toEqual(expected); ['390948362332.dkr.ecr.cn-northwest-1.amazonaws.com.cn', 'cn-northwest-1'],
} ['public.ecr.aws', 'us-east-1']
); ])('given registry %p', async (registry, expected) => {
expect(await aws.getRegion(registry)).toEqual(expected);
});
});
describe('getAccountIDs', () => {
test.each([
['012345678901.dkr.ecr.eu-west-3.amazonaws.com', undefined, ['012345678901']],
[
'012345678901.dkr.ecr.eu-west-3.amazonaws.com',
'012345678910,023456789012',
['012345678901', '012345678910', '023456789012']
],
[
'012345678901.dkr.ecr.eu-west-3.amazonaws.com',
'012345678901,012345678910,023456789012',
['012345678901', '012345678910', '023456789012']
],
[
'390948362332.dkr.ecr.cn-northwest-1.amazonaws.com.cn',
'012345678910,023456789012',
['390948362332', '012345678910', '023456789012']
],
['public.ecr.aws', undefined, []]
])('given registry %p', async (registry, accountIDsEnv, expected) => {
if (accountIDsEnv) {
process.env.AWS_ACCOUNT_IDS = accountIDsEnv;
}
expect(await aws.getAccountIDs(registry)).toEqual(expected);
});
}); });

11
__tests__/context.test.ts Normal file
View File

@@ -0,0 +1,11 @@
import osm = require('os');
import {getInputs} from '../src/context';
test('with password and username getInputs does not throw error', async () => {
process.env['INPUT_USERNAME'] = 'dbowie';
process.env['INPUT_PASSWORD'] = 'groundcontrol';
expect(() => {
getInputs();
}).not.toThrowError();
});

49
__tests__/docker.test.ts Normal file
View File

@@ -0,0 +1,49 @@
import {loginStandard, logout} from '../src/docker';
import * as path from 'path';
import * as exec from '@actions/exec';
process.env['RUNNER_TEMP'] = path.join(__dirname, 'runner');
test('loginStandard calls exec', async () => {
const execSpy: jest.SpyInstance = jest.spyOn(exec, 'exec');
// don't let exec try to actually run the commands
execSpy.mockImplementation(() => {});
const username: string = 'dbowie';
const password: string = 'groundcontrol';
const registry: string = 'https://ghcr.io';
await loginStandard(registry, username, password);
expect(execSpy).toHaveBeenCalledWith(`docker`, ['login', '--password-stdin', '--username', username, registry], {
input: Buffer.from(password),
silent: true,
ignoreReturnCode: true,
listeners: expect.objectContaining({
stdout: expect.any(Function),
stderr: expect.any(Function)
})
});
});
test('logout calls exec', async () => {
const execSpy: jest.SpyInstance = jest.spyOn(exec, 'exec');
// don't let exec try to actually run the commands
execSpy.mockImplementation(() => {});
const registry: string = 'https://ghcr.io';
await logout(registry);
expect(execSpy).toHaveBeenCalledWith(`docker`, ['logout', registry], {
silent: false,
ignoreReturnCode: true,
input: Buffer.from(''),
listeners: expect.objectContaining({
stdout: expect.any(Function),
stderr: expect.any(Function)
})
});
});

68
__tests__/main.test.ts Normal file
View File

@@ -0,0 +1,68 @@
import osm = require('os');
import {run} from '../src/main';
import * as docker from '../src/docker';
import * as stateHelper from '../src/state-helper';
import * as core from '@actions/core';
test('errors without username and password', async () => {
const platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => 'linux');
const coreSpy: jest.SpyInstance = jest.spyOn(core, 'setFailed');
await run();
expect(coreSpy).toHaveBeenCalledWith('Username and password required');
});
test('successful with username and password', async () => {
const platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => 'linux');
const setRegistrySpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setRegistry');
const setLogoutSpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setLogout');
const dockerSpy: jest.SpyInstance = jest.spyOn(docker, 'login');
dockerSpy.mockImplementation(() => {});
const username: string = 'dbowie';
process.env[`INPUT_USERNAME`] = username;
const password: string = 'groundcontrol';
process.env[`INPUT_PASSWORD`] = password;
await run();
expect(setRegistrySpy).toHaveBeenCalledWith('');
expect(setLogoutSpy).toHaveBeenCalledWith('');
expect(dockerSpy).toHaveBeenCalledWith('', username, password);
});
test('calls docker login', async () => {
const platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => 'linux');
const setRegistrySpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setRegistry');
const setLogoutSpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setLogout');
const dockerSpy: jest.SpyInstance = jest.spyOn(docker, 'login');
dockerSpy.mockImplementation(() => {});
const username: string = 'dbowie';
process.env[`INPUT_USERNAME`] = username;
const password: string = 'groundcontrol';
process.env[`INPUT_PASSWORD`] = password;
const registry: string = 'ghcr.io';
process.env[`INPUT_REGISTRY`] = registry;
const logout: string = 'true';
process.env['INPUT_LOGOUT'] = logout;
await run();
expect(setRegistrySpy).toHaveBeenCalledWith(registry);
expect(setLogoutSpy).toHaveBeenCalledWith(logout);
expect(dockerSpy).toHaveBeenCalledWith(registry, username, password);
});

View File

@@ -15,7 +15,7 @@ inputs:
required: false required: false
password: password:
description: 'Password or personal access token used to log against the Docker registry' description: 'Password or personal access token used to log against the Docker registry'
required: true required: false
logout: logout:
description: 'Log out from the Docker registry at the end of a job' description: 'Log out from the Docker registry at the end of a job'
default: 'true' default: 'true'

148
dist/index.js generated vendored
View File

@@ -496,28 +496,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const os = __importStar(__webpack_require__(87)); exports.run = void 0;
const core = __importStar(__webpack_require__(186)); const core = __importStar(__webpack_require__(186));
const context_1 = __webpack_require__(842); const context = __importStar(__webpack_require__(842));
const docker = __importStar(__webpack_require__(758)); const docker = __importStar(__webpack_require__(758));
const stateHelper = __importStar(__webpack_require__(647)); const stateHelper = __importStar(__webpack_require__(647));
function run() { function run() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
if (os.platform() !== 'linux') { const { registry, username, password, logout } = context.getInputs();
core.setFailed('Only supported on linux platform'); stateHelper.setRegistry(registry);
return; stateHelper.setLogout(logout);
} yield docker.login(registry, username, password);
let inputs = yield context_1.getInputs();
stateHelper.setRegistry(inputs.registry);
stateHelper.setLogout(inputs.logout);
yield docker.login(inputs.registry, inputs.username, inputs.password);
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
} }
}); });
} }
exports.run = run;
function logout() { function logout() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
if (!stateHelper.logout) { if (!stateHelper.logout) {
@@ -3061,22 +3058,23 @@ function logout(registry) {
exports.logout = logout; exports.logout = logout;
function loginStandard(registry, username, password) { function loginStandard(registry, username, password) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let loginArgs = ['login', '--password-stdin']; if (!username || !password) {
if (username) { throw new Error('Username and password required');
loginArgs.push('--username', username);
} }
let loginArgs = ['login', '--password-stdin'];
loginArgs.push('--username', username);
loginArgs.push(registry); loginArgs.push(registry);
if (registry) { if (registry) {
core.info(`🔑 Logging into ${registry}...`); core.info(`Logging into ${registry}...`);
} }
else { else {
core.info(`🔑 Logging into DockerHub...`); core.info(`Logging into Docker Hub...`);
} }
yield execm.exec('docker', loginArgs, true, password).then(res => { yield execm.exec('docker', loginArgs, true, password).then(res => {
if (res.stderr != '' && !res.success) { if (res.stderr != '' && !res.success) {
throw new Error(res.stderr); throw new Error(res.stderr);
} }
core.info('🎉 Login Succeeded!'); core.info(`Login Succeeded!`);
}); });
}); });
} }
@@ -3086,17 +3084,30 @@ function loginECR(registry, username, password) {
const cliPath = yield aws.getCLI(); const cliPath = yield aws.getCLI();
const cliVersion = yield aws.getCLIVersion(); const cliVersion = yield aws.getCLIVersion();
const region = yield aws.getRegion(registry); const region = yield aws.getRegion(registry);
core.info(`💡 AWS ECR detected with ${region} region`); const accountIDs = yield aws.getAccountIDs(registry);
process.env.AWS_ACCESS_KEY_ID = username; if (yield aws.isPubECR(registry)) {
process.env.AWS_SECRET_ACCESS_KEY = password; core.info(`AWS Public ECR detected with ${region} region`);
core.info(`⬇️ Retrieving docker login command through AWS CLI ${cliVersion} (${cliPath})...`); }
const loginCmd = yield aws.getDockerLoginCmd(cliVersion, registry, region); else {
core.info(`🔑 Logging into ${registry}...`); core.info(`AWS ECR detected with ${region} region`);
execm.exec(loginCmd, [], true).then(res => { }
if (res.stderr != '' && !res.success) { process.env.AWS_ACCESS_KEY_ID = username || process.env.AWS_ACCESS_KEY_ID;
throw new Error(res.stderr); process.env.AWS_SECRET_ACCESS_KEY = password || process.env.AWS_SECRET_ACCESS_KEY;
} core.info(`Retrieving docker login command through AWS CLI ${cliVersion} (${cliPath})...`);
core.info('🎉 Login Succeeded!'); const loginCmds = yield aws.getDockerLoginCmds(cliVersion, registry, region, accountIDs);
core.info(`Logging into ${registry}...`);
loginCmds.forEach((loginCmd, index) => {
execm.exec(loginCmd, [], true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(res.stderr);
}
if (loginCmds.length > 1) {
core.info(`Login Succeeded! (${index}/${loginCmds.length})`);
}
else {
core.info('Login Succeeded!');
}
});
}); });
}); });
} }
@@ -3640,27 +3651,16 @@ var __importStar = (this && this.__importStar) || function (mod) {
__setModuleDefault(result, mod); __setModuleDefault(result, mod);
return result; return result;
}; };
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getInputs = void 0; exports.getInputs = void 0;
const core = __importStar(__webpack_require__(186)); const core = __importStar(__webpack_require__(186));
function getInputs() { function getInputs() {
return __awaiter(this, void 0, void 0, function* () { return {
return { registry: core.getInput('registry'),
registry: core.getInput('registry'), username: core.getInput('username'),
username: core.getInput('username'), password: core.getInput('password'),
password: core.getInput('password', { required: true }), logout: core.getInput('logout')
logout: core.getInput('logout') };
};
});
} }
exports.getInputs = getInputs; exports.getInputs = getInputs;
//# sourceMappingURL=context.js.map //# sourceMappingURL=context.js.map
@@ -4164,16 +4164,41 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getDockerLoginCmd = exports.parseCLIVersion = exports.getCLIVersion = exports.execCLI = exports.getCLI = exports.getRegion = exports.isECR = void 0; exports.getDockerLoginCmds = exports.parseCLIVersion = exports.getCLIVersion = exports.execCLI = exports.getCLI = exports.getAccountIDs = exports.getRegion = exports.isPubECR = exports.isECR = void 0;
const semver = __importStar(__webpack_require__(383)); const semver = __importStar(__webpack_require__(383));
const io = __importStar(__webpack_require__(436)); const io = __importStar(__webpack_require__(436));
const execm = __importStar(__webpack_require__(757)); const execm = __importStar(__webpack_require__(757));
exports.isECR = (registry) => __awaiter(void 0, void 0, void 0, function* () { const ecrRegistryRegex = /^(([0-9]{12})\.dkr\.ecr\.(.+)\.amazonaws\.com(.cn)?)(\/([^:]+)(:.+)?)?$/;
return registry.includes('amazonaws'); exports.isECR = (registry) => {
}); return ecrRegistryRegex.test(registry) || exports.isPubECR(registry);
exports.getRegion = (registry) => __awaiter(void 0, void 0, void 0, function* () { };
return registry.substring(registry.indexOf('ecr.') + 4, registry.indexOf('.amazonaws')); exports.isPubECR = (registry) => {
}); return registry === 'public.ecr.aws';
};
exports.getRegion = (registry) => {
if (exports.isPubECR(registry)) {
return process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-1';
}
const matches = registry.match(ecrRegistryRegex);
if (!matches) {
return '';
}
return matches[3];
};
exports.getAccountIDs = (registry) => {
if (exports.isPubECR(registry)) {
return [];
}
const matches = registry.match(ecrRegistryRegex);
if (!matches) {
return [];
}
let accountIDs = [matches[2]];
if (process.env.AWS_ACCOUNT_IDS) {
accountIDs.push(...process.env.AWS_ACCOUNT_IDS.split(','));
}
return accountIDs.filter((item, index) => accountIDs.indexOf(item) === index);
};
exports.getCLI = () => __awaiter(void 0, void 0, void 0, function* () { exports.getCLI = () => __awaiter(void 0, void 0, void 0, function* () {
return io.which('aws', true); return io.which('aws', true);
}); });
@@ -4200,15 +4225,24 @@ exports.parseCLIVersion = (stdout) => __awaiter(void 0, void 0, void 0, function
} }
return semver.clean(matches[1]); return semver.clean(matches[1]);
}); });
exports.getDockerLoginCmd = (cliVersion, registry, region) => __awaiter(void 0, void 0, void 0, function* () { exports.getDockerLoginCmds = (cliVersion, registry, region, accountIDs) => __awaiter(void 0, void 0, void 0, function* () {
if (semver.satisfies(cliVersion, '>=2.0.0')) { let ecrCmd = (yield exports.isPubECR(registry)) ? 'ecr-public' : 'ecr';
return exports.execCLI(['ecr', 'get-login-password', '--region', region]).then(pwd => { if (semver.satisfies(cliVersion, '>=2.0.0') || (yield exports.isPubECR(registry))) {
return `docker login --username AWS --password ${pwd} ${registry}`; return exports.execCLI([ecrCmd, 'get-login-password', '--region', region]).then(pwd => {
return [`docker login --username AWS --password ${pwd} ${registry}`];
}); });
} }
else { else {
return exports.execCLI(['ecr', 'get-login', '--region', region, '--no-include-email']).then(dockerLoginCmd => { return exports.execCLI([
return dockerLoginCmd; ecrCmd,
'get-login',
'--region',
region,
'--registry-ids',
accountIDs.join(' '),
'--no-include-email'
]).then(dockerLoginCmds => {
return dockerLoginCmds.trim().split(`\n`);
}); });
} }
}); });

67
docker-bake.hcl Normal file
View File

@@ -0,0 +1,67 @@
variable "NODE_VERSION" {
default = "12"
}
target "node-version" {
args = {
NODE_VERSION = NODE_VERSION
}
}
group "default" {
targets = ["build"]
}
group "pre-checkin" {
targets = ["vendor-update", "format", "build"]
}
group "validate" {
targets = ["format-validate", "build-validate", "vendor-validate"]
}
target "build" {
inherits = ["node-version"]
dockerfile = "./hack/build.Dockerfile"
target = "build-update"
output = ["."]
}
target "build-validate" {
inherits = ["node-version"]
dockerfile = "./hack/build.Dockerfile"
target = "build-validate"
}
target "format" {
inherits = ["node-version"]
dockerfile = "./hack/build.Dockerfile"
target = "format-update"
output = ["."]
}
target "format-validate" {
inherits = ["node-version"]
dockerfile = "./hack/build.Dockerfile"
target = "format-validate"
}
target "vendor-update" {
inherits = ["node-version"]
dockerfile = "./hack/vendor.Dockerfile"
target = "update"
output = ["."]
}
target "vendor-validate" {
inherits = ["node-version"]
dockerfile = "./hack/vendor.Dockerfile"
target = "validate"
}
target "test" {
inherits = ["node-version"]
dockerfile = "./hack/test.Dockerfile"
target = "test-coverage"
output = ["./coverage"]
}

42
hack/build.Dockerfile Normal file
View File

@@ -0,0 +1,42 @@
# syntax=docker/dockerfile:1.2
ARG NODE_VERSION
FROM node:${NODE_VERSION}-alpine AS base
RUN apk add --no-cache cpio findutils git
WORKDIR /src
FROM base AS deps
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/node_modules \
yarn install
FROM deps AS build
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/node_modules \
yarn run build && mkdir /out && cp -Rf dist /out/
FROM scratch AS build-update
COPY --from=build /out /
FROM build AS build-validate
RUN --mount=type=bind,target=.,rw \
git add -A && cp -rf /out/* .; \
if [ -n "$(git status --porcelain -- dist)" ]; then \
echo >&2 'ERROR: Build result differs. Please build first with "docker buildx bake build"'; \
git status --porcelain -- dist; \
exit 1; \
fi
FROM deps AS format
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/node_modules \
yarn run format \
&& mkdir /out && find . -name '*.ts' -not -path './node_modules/*' | cpio -pdm /out
FROM scratch AS format-update
COPY --from=format /out /
FROM deps AS format-validate
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/node_modules \
yarn run format-check \

34
hack/test.Dockerfile Normal file
View File

@@ -0,0 +1,34 @@
# syntax=docker/dockerfile:1.2
ARG NODE_VERSION
FROM node:${NODE_VERSION}-alpine AS base
RUN apk add --no-cache binutils curl git unzip
ENV GLIBC_VER=2.31-r0
RUN curl -sL "https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub" -o "/etc/apk/keys/sgerrand.rsa.pub" \
&& curl -sLO "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-${GLIBC_VER}.apk" \
&& curl -sLO "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-bin-${GLIBC_VER}.apk" \
&& apk add --no-cache \
glibc-${GLIBC_VER}.apk \
glibc-bin-${GLIBC_VER}.apk \
&& curl -sL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
&& unzip -qq "awscliv2.zip" \
&& ./aws/install \
&& aws --version
WORKDIR /src
FROM base AS deps
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/node_modules \
yarn install
FROM deps AS test
ENV RUNNER_TEMP=/tmp/github_runner
ENV RUNNER_TOOL_CACHE=/tmp/github_tool_cache
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/node_modules \
--mount=type=bind,from=crazymax/docker,source=/usr/libexec/docker/cli-plugins/docker-buildx,target=/usr/libexec/docker/cli-plugins/docker-buildx \
--mount=type=bind,from=crazymax/docker,source=/usr/local/bin/docker,target=/usr/bin/docker \
yarn run test --coverageDirectory=/tmp/coverage
FROM scratch AS test-coverage
COPY --from=test /tmp/coverage /

23
hack/vendor.Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
# syntax=docker/dockerfile:1.2
ARG NODE_VERSION
FROM node:${NODE_VERSION}-alpine AS base
RUN apk add --no-cache git
WORKDIR /src
FROM base AS vendored
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/node_modules \
yarn install && mkdir /out && cp yarn.lock /out
FROM scratch AS update
COPY --from=vendored /out /
FROM vendored AS validate
RUN --mount=type=bind,target=.,rw \
git add -A && cp -rf /out/* .; \
if [ -n "$(git status --porcelain -- yarn.lock)" ]; then \
echo >&2 'ERROR: Vendor result differs. Please vendor your package with "docker buildx bake vendor-update"'; \
git status --porcelain -- yarn.lock; \
exit 1; \
fi

View File

@@ -2,12 +2,40 @@ import * as semver from 'semver';
import * as io from '@actions/io'; import * as io from '@actions/io';
import * as execm from './exec'; import * as execm from './exec';
export const isECR = async (registry: string): Promise<boolean> => { const ecrRegistryRegex = /^(([0-9]{12})\.dkr\.ecr\.(.+)\.amazonaws\.com(.cn)?)(\/([^:]+)(:.+)?)?$/;
return registry.includes('amazonaws');
export const isECR = (registry: string): boolean => {
return ecrRegistryRegex.test(registry) || isPubECR(registry);
}; };
export const getRegion = async (registry: string): Promise<string> => { export const isPubECR = (registry: string): boolean => {
return registry.substring(registry.indexOf('ecr.') + 4, registry.indexOf('.amazonaws')); return registry === 'public.ecr.aws';
};
export const getRegion = (registry: string): string => {
if (isPubECR(registry)) {
return process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-1';
}
const matches = registry.match(ecrRegistryRegex);
if (!matches) {
return '';
}
return matches[3];
};
export const getAccountIDs = (registry: string): string[] => {
if (isPubECR(registry)) {
return [];
}
const matches = registry.match(ecrRegistryRegex);
if (!matches) {
return [];
}
let accountIDs: Array<string> = [matches[2]];
if (process.env.AWS_ACCOUNT_IDS) {
accountIDs.push(...process.env.AWS_ACCOUNT_IDS.split(','));
}
return accountIDs.filter((item, index) => accountIDs.indexOf(item) === index);
}; };
export const getCLI = async (): Promise<string> => { export const getCLI = async (): Promise<string> => {
@@ -38,14 +66,28 @@ export const parseCLIVersion = async (stdout: string): Promise<string> => {
return semver.clean(matches[1]); return semver.clean(matches[1]);
}; };
export const getDockerLoginCmd = async (cliVersion: string, registry: string, region: string): Promise<string> => { export const getDockerLoginCmds = async (
if (semver.satisfies(cliVersion, '>=2.0.0')) { cliVersion: string,
return execCLI(['ecr', 'get-login-password', '--region', region]).then(pwd => { registry: string,
return `docker login --username AWS --password ${pwd} ${registry}`; region: string,
accountIDs: string[]
): Promise<string[]> => {
let ecrCmd = (await isPubECR(registry)) ? 'ecr-public' : 'ecr';
if (semver.satisfies(cliVersion, '>=2.0.0') || (await isPubECR(registry))) {
return execCLI([ecrCmd, 'get-login-password', '--region', region]).then(pwd => {
return [`docker login --username AWS --password ${pwd} ${registry}`];
}); });
} else { } else {
return execCLI(['ecr', 'get-login', '--region', region, '--no-include-email']).then(dockerLoginCmd => { return execCLI([
return dockerLoginCmd; ecrCmd,
'get-login',
'--region',
region,
'--registry-ids',
accountIDs.join(' '),
'--no-include-email'
]).then(dockerLoginCmds => {
return dockerLoginCmds.trim().split(`\n`);
}); });
} }
}; };

View File

@@ -7,11 +7,11 @@ export interface Inputs {
logout: string; logout: string;
} }
export async function getInputs(): Promise<Inputs> { export function getInputs(): Inputs {
return { return {
registry: core.getInput('registry'), registry: core.getInput('registry'),
username: core.getInput('username'), username: core.getInput('username'),
password: core.getInput('password', {required: true}), password: core.getInput('password'),
logout: core.getInput('logout') logout: core.getInput('logout')
}; };
} }

View File

@@ -19,22 +19,24 @@ export async function logout(registry: string): Promise<void> {
} }
export async function loginStandard(registry: string, username: string, password: string): Promise<void> { export async function loginStandard(registry: string, username: string, password: string): Promise<void> {
let loginArgs: Array<string> = ['login', '--password-stdin']; if (!username || !password) {
if (username) { throw new Error('Username and password required');
loginArgs.push('--username', username);
} }
let loginArgs: Array<string> = ['login', '--password-stdin'];
loginArgs.push('--username', username);
loginArgs.push(registry); loginArgs.push(registry);
if (registry) { if (registry) {
core.info(`🔑 Logging into ${registry}...`); core.info(`Logging into ${registry}...`);
} else { } else {
core.info(`🔑 Logging into DockerHub...`); core.info(`Logging into Docker Hub...`);
} }
await execm.exec('docker', loginArgs, true, password).then(res => { await execm.exec('docker', loginArgs, true, password).then(res => {
if (res.stderr != '' && !res.success) { if (res.stderr != '' && !res.success) {
throw new Error(res.stderr); throw new Error(res.stderr);
} }
core.info('🎉 Login Succeeded!'); core.info(`Login Succeeded!`);
}); });
} }
@@ -42,19 +44,31 @@ export async function loginECR(registry: string, username: string, password: str
const cliPath = await aws.getCLI(); const cliPath = await aws.getCLI();
const cliVersion = await aws.getCLIVersion(); const cliVersion = await aws.getCLIVersion();
const region = await aws.getRegion(registry); const region = await aws.getRegion(registry);
core.info(`💡 AWS ECR detected with ${region} region`); const accountIDs = await aws.getAccountIDs(registry);
process.env.AWS_ACCESS_KEY_ID = username; if (await aws.isPubECR(registry)) {
process.env.AWS_SECRET_ACCESS_KEY = password; core.info(`AWS Public ECR detected with ${region} region`);
} else {
core.info(`AWS ECR detected with ${region} region`);
}
core.info(`⬇️ Retrieving docker login command through AWS CLI ${cliVersion} (${cliPath})...`); process.env.AWS_ACCESS_KEY_ID = username || process.env.AWS_ACCESS_KEY_ID;
const loginCmd = await aws.getDockerLoginCmd(cliVersion, registry, region); process.env.AWS_SECRET_ACCESS_KEY = password || process.env.AWS_SECRET_ACCESS_KEY;
core.info(`🔑 Logging into ${registry}...`); core.info(`Retrieving docker login command through AWS CLI ${cliVersion} (${cliPath})...`);
execm.exec(loginCmd, [], true).then(res => { const loginCmds = await aws.getDockerLoginCmds(cliVersion, registry, region, accountIDs);
if (res.stderr != '' && !res.success) {
throw new Error(res.stderr); core.info(`Logging into ${registry}...`);
} loginCmds.forEach((loginCmd, index) => {
core.info('🎉 Login Succeeded!'); execm.exec(loginCmd, [], true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(res.stderr);
}
if (loginCmds.length > 1) {
core.info(`Login Succeeded! (${index}/${loginCmds.length})`);
} else {
core.info('Login Succeeded!');
}
});
}); });
} }

View File

@@ -1,20 +1,14 @@
import * as os from 'os';
import * as core from '@actions/core'; import * as core from '@actions/core';
import {getInputs, Inputs} from './context'; import * as context from './context';
import * as docker from './docker'; import * as docker from './docker';
import * as stateHelper from './state-helper'; import * as stateHelper from './state-helper';
async function run(): Promise<void> { export async function run(): Promise<void> {
try { try {
if (os.platform() !== 'linux') { const {registry, username, password, logout} = context.getInputs();
core.setFailed('Only supported on linux platform'); stateHelper.setRegistry(registry);
return; stateHelper.setLogout(logout);
} await docker.login(registry, username, password);
let inputs: Inputs = await getInputs();
stateHelper.setRegistry(inputs.registry);
stateHelper.setLogout(inputs.logout);
await docker.login(inputs.registry, inputs.username, inputs.password);
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
} }