1 Commits
Author SHA1 Message Date
action-mirror e25175c3ba mirror docker/login-action@v3 2026-09-13 22:53:25 +08:00
42 changed files with 20269 additions and 12423 deletions
+11 -1
View File
@@ -1,2 +1,12 @@
/coverage
/node_modules
# Dependency directories
node_modules/
jspm_packages/
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
+3
View File
@@ -0,0 +1,3 @@
/dist/**
/coverage/**
/node_modules/**
+2 -1
View File
@@ -1,11 +1,12 @@
{
"env": {
"node": true,
"es2021": true,
"es6": true,
"jest": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended",
"plugin:prettier/recommended"
+2
View File
@@ -1,2 +1,4 @@
/.yarn/releases/** binary
/.yarn/plugins/** binary
/dist/** linguist-generated=true
/lib/** linguist-generated=true
-1
View File
@@ -1 +0,0 @@
* @crazy-max
+3
View File
@@ -0,0 +1,3 @@
# Code of conduct
- [Moby community guidelines](https://github.com/moby/moby/blob/master/CONTRIBUTING.md#moby-community-guidelines)
+101
View File
@@ -0,0 +1,101 @@
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Bug Report
description: Report a bug
labels:
- status/triage
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to report a bug!
If this is a security issue please report it to the [Docker Security team](mailto:security@docker.com).
- type: checkboxes
attributes:
label: Contributing guidelines
description: >
Make sure you've read the contributing guidelines before proceeding.
options:
- label: I've read the [contributing guidelines](https://github.com/docker/login-action/blob/master/.github/CONTRIBUTING.md) and wholeheartedly agree
required: true
- type: checkboxes
attributes:
label: "I've found a bug, and:"
description: |
Make sure that your request fulfills all of the following requirements.
If one requirement cannot be satisfied, explain in detail why.
options:
- label: The documentation does not mention anything about my problem
- label: There are no open or closed issues that are related to my problem
- type: textarea
attributes:
label: Description
description: >
Provide a brief description of the bug in 1-2 sentences.
validations:
required: true
- type: textarea
attributes:
label: Expected behaviour
description: >
Describe precisely what you'd expect to happen.
validations:
required: true
- type: textarea
attributes:
label: Actual behaviour
description: >
Describe precisely what is actually happening.
validations:
required: true
- type: input
attributes:
label: Repository URL
description: >
Enter the URL of the repository where you are experiencing the
issue. If your repository is private, provide a link to a minimal
repository that reproduces the issue.
- type: input
attributes:
label: Workflow run URL
description: >
Enter the URL of the GitHub Action workflow run if public (e.g.
`https://github.com/<user>/<repo>/actions/runs/<id>`)
- type: textarea
attributes:
label: YAML workflow
description: |
Provide the YAML of the workflow that's causing the issue.
Make sure to remove any sensitive information.
render: yaml
validations:
required: true
- type: textarea
attributes:
label: Workflow logs
description: >
[Attach](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/attaching-files)
the [log file of your workflow run](https://docs.github.com/en/actions/managing-workflow-runs/using-workflow-run-logs#downloading-logs)
and make sure to remove any sensitive information.
- type: textarea
attributes:
label: BuildKit logs
description: >
If applicable, provide the [BuildKit container logs](https://docs.docker.com/build/ci/github-actions/configure-builder/#buildkit-container-logs)
render: text
- type: textarea
attributes:
label: Additional info
description: |
Provide any additional information that could be useful.
-34
View File
@@ -1,34 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
### Behaviour
#### Steps to reproduce this issue
1.
2.
3.
#### Expected behaviour
> Tell us what should happen
#### Actual behaviour
> Tell us what happens instead
### Configuration
* Repository URL (if public):
* Build URL (if public):
```yml
# paste your YAML workflow file here and remove sensitive data
```
### Logs
> 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.
+9
View File
@@ -0,0 +1,9 @@
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
blank_issues_enabled: true
contact_links:
- name: Questions and Discussions
url: https://github.com/docker/login-action/discussions/new
about: Use Github Discussions to ask questions and/or open discussion topics.
- name: Documentation
url: https://docs.docker.com/build/ci/github-actions/
about: Read the documentation.
+15
View File
@@ -0,0 +1,15 @@
# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Feature request
description: Missing functionality? Come tell us about it!
labels:
- kind/enhancement
- status/triage
body:
- type: textarea
id: description
attributes:
label: Description
description: What is the feature you want to see?
validations:
required: true
+12
View File
@@ -0,0 +1,12 @@
# Reporting security issues
The project maintainers take security seriously. If you discover a security
issue, please bring it to their attention right away!
**Please _DO NOT_ file a public issue**, instead send your report privately to
[security@docker.com](mailto:security@docker.com).
Security reports are greatly appreciated, and we will publicly thank you for it.
We also like to send gifts&mdash;if you'd like Docker swag, make sure to let
us know. We currently do not offer a paid security bounty program, but are not
ruling it out in the future.
-29
View File
@@ -1,29 +0,0 @@
# Support [![](https://isitmaintained.com/badge/resolution/docker/login-action.svg)](https://isitmaintained.com/project/docker/login-action)
## Reporting an issue
Please do a search in [open issues](https://github.com/docker/login-action/issues?utf8=%E2%9C%93&q=) to see if the issue or feature request has already been filed.
If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment.
:+1: - upvote
:-1: - downvote
If you cannot find an existing issue that describes your bug or feature, submit an issue using the guidelines below.
## Writing good bug reports and feature requests
File a single issue per problem and feature request.
* Do not enumerate multiple bugs or feature requests in the same issue.
* Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes.
The more information you can provide, the more likely someone will be successful reproducing the issue and finding a fix.
You are now ready to [create a new issue](https://github.com/docker/login-action/issues/new/choose)!
## Closure policy
* Issues that don't have the information requested above (when applicable) will be closed immediately and the poster directed to the support guidelines.
* Issues that go a week without a response from original poster are subject to closure at our discretion.
+8
View File
@@ -11,6 +11,14 @@ updates:
directory: "/"
schedule:
interval: "daily"
versioning-strategy: "increase"
groups:
aws-sdk-dependencies:
patterns:
- "*aws-sdk*"
proxy-agent-dependencies:
patterns:
- "*-proxy-agent"
allow:
- dependency-type: "production"
labels:
+215 -17
View File
@@ -1,5 +1,9 @@
name: ci
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
schedule:
@@ -15,7 +19,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Stop docker
run: |
@@ -39,7 +43,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to GitHub Container Registry
uses: ./
@@ -56,7 +60,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to GitHub Container Registry
uses: ./
@@ -81,7 +85,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to ACR
uses: ./
@@ -101,7 +105,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to Docker Hub
uses: ./
@@ -120,7 +124,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to ECR
uses: ./
@@ -140,10 +144,10 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
uses: aws-actions/configure-aws-credentials@v5
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -165,9 +169,10 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to Public ECR
continue-on-error: ${{ matrix.os == 'windows-latest' }}
uses: ./
with:
registry: public.ecr.aws
@@ -187,21 +192,22 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
uses: aws-actions/configure-aws-credentials@v5
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
-
name: Login to ECR
name: Login to Public ECR
continue-on-error: ${{ matrix.os == 'windows-latest' }}
uses: ./
with:
registry: public.ecr.aws
github-container:
ghcr:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
@@ -212,7 +218,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to GitHub Container Registry
uses: ./
@@ -232,7 +238,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to GitLab
uses: ./
@@ -252,7 +258,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to Google Artifact Registry
uses: ./
@@ -272,7 +278,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Login to Google Container Registry
uses: ./
@@ -280,3 +286,195 @@ jobs:
registry: gcr.io
username: _json_key
password: ${{ secrets.GCR_JSON_KEY }}
registry-auth:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Login to registries
uses: ./
with:
registry-auth: |
- username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- registry: public.ecr.aws
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- registry: registry.gitlab.com
username: ${{ secrets.GITLAB_USERNAME }}
password: ${{ secrets.GITLAB_TOKEN }}
registry-auth-dup:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Login to registries
uses: ./
with:
registry-auth: |
- registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- registry: public.ecr.aws
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry-auth-exclusive:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Login to registries
id: login
continue-on-error: true
uses: ./
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry-auth: |
- username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Check
run: |
if [ "${{ steps.login.outcome }}" != "failure" ] || [ "${{ steps.login.conclusion }}" != "success" ]; then
echo "::error::Should have failed"
exit 1
fi
scope-dockerhub:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Login to Docker Hub
uses: ./
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
scope: '@push'
-
name: Print config.json files
shell: bash
run: |
shopt -s globstar nullglob
for file in ~/.docker/**/config.json; do
echo "## ${file}"
jq '(.auths[]?.auth) |= "REDACTED"' "$file"
echo ""
done
scope-dockerhub-repo:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Login to Docker Hub
uses: ./
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
scope: 'docker/buildx-bin@push'
-
name: Print config.json files
shell: bash
run: |
shopt -s globstar nullglob
for file in ~/.docker/**/config.json; do
echo "## ${file}"
jq '(.auths[]?.auth) |= "REDACTED"' "$file"
echo ""
done
scope-ghcr:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Login to GitHub Container Registry
uses: ./
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
scope: '@push'
-
name: Print config.json files
shell: bash
run: |
shopt -s globstar nullglob
for file in ~/.docker/**/config.json; do
echo "## ${file}"
jq '(.auths[]?.auth) |= "REDACTED"' "$file"
echo ""
done
scope-ghcr-repo:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Login to GitHub Container Registry
uses: ./
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
scope: 'docker/login-action@push'
-
name: Print config.json files
shell: bash
run: |
shopt -s globstar nullglob
for file in ~/.docker/**/config.json; do
echo "## ${file}"
jq '(.auths[]?.auth) |= "REDACTED"' "$file"
echo ""
done
+50
View File
@@ -0,0 +1,50 @@
name: codeql
on:
push:
branches:
- 'master'
- 'releases/v*'
paths:
- '.github/workflows/codeql.yml'
- 'dist/**'
- 'src/**'
pull_request:
paths:
- '.github/workflows/codeql.yml'
- 'dist/**'
- 'src/**'
permissions:
actions: read
contents: read
security-events: write
jobs:
analyze:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language:
- javascript-typescript
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
config: |
paths:
- src
-
name: Autobuild
uses: github/codeql-action/autobuild@v4
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
+17
View File
@@ -0,0 +1,17 @@
name: pr-assign-author
permissions:
contents: read
on:
pull_request_target:
types:
- opened
- reopened
jobs:
run:
uses: crazy-max/.github/.github/workflows/pr-assign-author.yml@1b673f36fad86812f538c1df9794904038a23cbf
permissions:
contents: read
pull-requests: write
+21
View File
@@ -0,0 +1,21 @@
name: publish
on:
release:
types:
- published
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
packages: write
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Publish
uses: actions/publish-immutable-action@v0.0.4
+10 -4
View File
@@ -1,5 +1,9 @@
name: test
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches:
@@ -13,14 +17,16 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Test
uses: docker/bake-action@v3
uses: docker/bake-action@v6
with:
source: .
targets: test
-
name: Upload coverage
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v5
with:
file: ./coverage/clover.xml
files: ./coverage/clover.xml
token: ${{ secrets.CODECOV_TOKEN }}
+12 -10
View File
@@ -1,5 +1,9 @@
name: validate
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches:
@@ -11,16 +15,17 @@ jobs:
prepare:
runs-on: ubuntu-latest
outputs:
targets: ${{ steps.targets.outputs.matrix }}
targets: ${{ steps.generate.outputs.targets }}
steps:
-
name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
-
name: Targets matrix
id: targets
run: |
echo "matrix=$(docker buildx bake validate --print | jq -cr '.group.validate.targets')" >> $GITHUB_OUTPUT
name: List targets
id: generate
uses: docker/bake-action/subaction/list-targets@v6
with:
target: validate
validate:
runs-on: ubuntu-latest
@@ -31,11 +36,8 @@ jobs:
matrix:
target: ${{ fromJson(needs.prepare.outputs.targets) }}
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Validate
uses: docker/bake-action@v3
uses: docker/bake-action@v6
with:
targets: ${{ matrix.target }}
+14 -52
View File
@@ -1,7 +1,5 @@
node_modules/
lib
# https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs
logs
*.log
@@ -9,6 +7,7 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
@@ -19,34 +18,14 @@ pids
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
@@ -56,36 +35,19 @@ typings/
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
# dotenv environment variable files
.env
.env.test
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
+6
View File
@@ -0,0 +1,6 @@
# Dependency directories
node_modules/
jspm_packages/
# yarn v2
.yarn/
+17
View File
@@ -0,0 +1,17 @@
# https://yarnpkg.com/configuration/yarnrc
compressionLevel: mixed
enableGlobalCache: false
enableHardenedMode: true
logFilters:
- code: YN0013
level: discard
- code: YN0019
level: discard
- code: YN0076
level: discard
- code: YN0086
level: discard
nodeLinker: node-modules
+254 -98
View File
@@ -23,16 +23,20 @@ ___
* [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)
* [DigitalOcean](#digitalocean-container-registry)
* [Authenticate to multiple registries](#authenticate-to-multiple-registries)
* [Set scopes for the authentication token](#set-scopes-for-the-authentication-token)
* [Customizing](#customizing)
* [inputs](#inputs)
* [Keep up-to-date with GitHub Dependabot](#keep-up-to-date-with-github-dependabot)
* [Contributing](#contributing)
## Usage
### Docker Hub
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.
When authenticating to [Docker Hub](https://hub.docker.com) with GitHub Actions,
use a [personal access token](https://docs.docker.com/docker-hub/access-tokens/).
Don't use your account password.
```yaml
name: ci
@@ -47,17 +51,17 @@ jobs:
steps:
-
name: Login to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
```
### GitHub Container Registry
To authenticate against the [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-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.
To authenticate to the [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry),
use the [`GITHUB_TOKEN`](https://docs.github.com/en/actions/reference/authentication-in-a-workflow)
secret.
```yaml
name: ci
@@ -72,7 +76,7 @@ jobs:
steps:
-
name: Login to GitHub Container Registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -100,20 +104,23 @@ jobs:
steps:
-
name: Login to GitLab
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: registry.gitlab.com
username: ${{ secrets.GITLAB_USERNAME }}
username: ${{ vars.GITLAB_USERNAME }}
password: ${{ secrets.GITLAB_PASSWORD }}
```
If you have [Two-Factor Authentication](https://gitlab.com/help/user/profile/account/two_factor_authentication) enabled, use a [Personal Access Token](https://gitlab.com/help/user/profile/personal_access_tokens) instead of a password.
If you have [Two-Factor Authentication](https://gitlab.com/help/user/profile/account/two_factor_authentication)
enabled, use a [Personal Access Token](https://gitlab.com/help/user/profile/personal_access_tokens)
instead of a password.
### Azure Container Registry (ACR)
[Create a service principal](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-auth-service-principal#create-a-service-principal)
with access to your container registry through the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
and take note of the generated service principal's ID (also called _client ID_) and password (also called _client secret_).
and take note of the generated service principal's ID (also called _client ID_)
and password (also called _client secret_).
```yaml
name: ci
@@ -128,10 +135,10 @@ jobs:
steps:
-
name: Login to ACR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: <registry-name>.azurecr.io
username: ${{ secrets.AZURE_CLIENT_ID }}
username: ${{ vars.AZURE_CLIENT_ID }}
password: ${{ secrets.AZURE_CLIENT_SECRET }}
```
@@ -139,16 +146,21 @@ jobs:
### 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)
> [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.
You can use either workload identity federation based keyless authentication or service account based authentication.
You can authenticate with workload identity federation or a service account.
#### Workload identity federation based authentication
#### Workload identity federation
Configure the workload identity federation for github actions in gcloud (for steps, [refer here](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation)). In the steps, your service account should the ability to push to GCR. Then use google-github-actions/auth action for authentication using workload identity like below:
Configure the workload identity federation for GitHub Actions in Google Cloud,
[see here](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation).
Your service account must have permission to push to GCR. Use the
`google-github-actions/auth` action to authenticate using workload identity as
shown in the following example:
```yaml
name: ci
@@ -161,33 +173,35 @@ jobs:
login:
runs-on: ubuntu-latest
steps:
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v0'
-
name: Authenticate to Google Cloud
id: auth
uses: google-github-actions/auth@v1
with:
token_format: 'access_token'
workload_identity_provider: '<workload_identity_provider>'
service_account: '<service_account>'
- name: Login to GCR
uses: docker/login-action@v2
token_format: access_token
workload_identity_provider: <workload_identity_provider>
service_account: <service_account>
-
name: Login to GCR
uses: docker/login-action@v3
with:
registry: gcr.io
username: oauth2accesstoken
password: ${{ steps.auth.outputs.access_token }}
```
> Replace `<workload_identity_provider>` with configured workload identity provider. For steps to configure, [refer here](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation).
> Replace `<workload_identity_provider>` with configured workload identity
> provider. For steps to configure, [see here](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation).
> Replace `<service_account>` with configured service account in workload identity provider which has access to push to GCR
> Replace `<service_account>` with configured service account in workload
> identity provider which has access to push to GCR
#### Service account based authentication
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
[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 `GCR_JSON_KEY` in your GitHub repo. Ensure you set the username to `_json_key`,
or `_json_key_base64` if you use a base64-encoded key.
Use a service account with permission to push to GCR and [configure access control](https://cloud.google.com/container-registry/docs/access-control).
Download the key for the service account as a JSON file. Save the contents of
the 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)
named `GCR_JSON_KEY` in your GitHub repository. Set the username to `_json_key`.
```yaml
name: ci
@@ -202,7 +216,7 @@ jobs:
steps:
-
name: Login to GCR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: gcr.io
username: _json_key
@@ -211,11 +225,13 @@ jobs:
### Google Artifact Registry (GAR)
You can use either workload identity federation based keyless authentication or service account based authentication.
You can authenticate with workload identity federation or a service account.
#### Workload identity federation based authentication
#### Workload identity federation
Configure the workload identity federation for github actions in gcloud (for steps, [refer here](https://github.com/google-github-actions/auth#setting-up-workload-identity-federation)). In the steps, your service account should the ability to push to GAR. Then use google-github-actions/auth action for authentication using workload identity like below:
Your service account must have permission to push to GAR. Use the
`google-github-actions/auth` action to authenticate using workload identity as
shown in the following example:
```yaml
name: ci
@@ -228,34 +244,38 @@ jobs:
login:
runs-on: ubuntu-latest
steps:
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v0'
-
name: Authenticate to Google Cloud
id: auth
uses: google-github-actions/auth@v1
with:
token_format: 'access_token'
workload_identity_provider: '<workload_identity_provider>'
service_account: '<service_account>'
- name: Login to GAR
uses: docker/login-action@v2
token_format: access_token
workload_identity_provider: <workload_identity_provider>
service_account: <service_account>
-
name: Login to GAR
uses: docker/login-action@v3
with:
registry: <location>-docker.pkg.dev
username: oauth2accesstoken
password: ${{ steps.auth.outputs.access_token }}
```
> Replace `<workload_identity_provider>` with configured workload identity provider
> Replace `<service_account>` with configured service account in workload identity provider which has access to push to GCR
> Replace `<workload_identity_provider>` with configured workload identity
> provider
> Replace `<service_account>` with configured service account in workload
> identity provider which has access to push to GCR
> 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.
#### Service account based authentication
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`,
Use a service account with permission to push to GAR and [configure access control](https://cloud.google.com/artifact-registry/docs/access-control).
Download the key for the service account as a JSON file. Save the contents of
the 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)
named `GAR_JSON_KEY` in your GitHub repository. Set the username to `_json_key`,
or `_json_key_base64` if you use a base64-encoded key.
```yaml
@@ -271,7 +291,7 @@ jobs:
steps:
-
name: Login to GAR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: <location>-docker.pkg.dev
username: _json_key
@@ -283,8 +303,8 @@ jobs:
### AWS Elastic Container Registry (ECR)
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)
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/security-iam-awsmanpol.html#security-iam-awsmanpol-AmazonEC2ContainerRegistryPowerUser).
Download the access keys and save them as `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
@@ -300,15 +320,15 @@ jobs:
steps:
-
name: Login to ECR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: <aws-account-number>.dkr.ecr.<region>.amazonaws.com
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
username: ${{ vars.AWS_ACCESS_KEY_ID }}
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:
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
@@ -323,10 +343,10 @@ jobs:
steps:
-
name: Login to ECR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: <aws-account-number>.dkr.ecr.<region>.amazonaws.com
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
username: ${{ vars.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
env:
AWS_ACCOUNT_IDS: 012345678910,023456789012
@@ -334,8 +354,8 @@ jobs:
> 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:
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
@@ -350,14 +370,14 @@ jobs:
steps:
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-access-key-id: ${{ vars.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: <region>
-
name: Login to ECR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: <aws-account-number>.dkr.ecr.<region>.amazonaws.com
```
@@ -366,9 +386,10 @@ jobs:
### 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.
Use an IAM user with permission to push to ECR Public, for example using [managed policies](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam-awsmanpol.html#security-iam-awsmanpol-AmazonEC2ContainerRegistryPowerUser).
Download the access keys and save them as `AWS_ACCESS_KEY_ID` and
`AWS_SECRET_ACCESS_KEY` [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 repository.
```yaml
name: ci
@@ -383,10 +404,10 @@ jobs:
steps:
-
name: Login to Public ECR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: public.ecr.aws
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
username: ${{ vars.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
env:
AWS_REGION: <region>
@@ -417,10 +438,10 @@ jobs:
steps:
-
name: Login to OCIR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: <region>.ocir.io
username: ${{ secrets.OCI_USERNAME }}
username: ${{ vars.OCI_USERNAME }}
password: ${{ secrets.OCI_TOKEN }}
```
@@ -428,7 +449,8 @@ jobs:
### 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.
Use a [Robot account](https://docs.quay.io/glossary/robot-accounts.html) with
permission to push to a Quay.io repository.
```yaml
name: ci
@@ -443,39 +465,173 @@ jobs:
steps:
-
name: Login to Quay.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
username: ${{ vars.QUAY_USERNAME }}
password: ${{ secrets.QUAY_ROBOT_TOKEN }}
```
### DigitalOcean Container Registry
Use your DigitalOcean registered email address and an API access token to authenticate.
```yaml
name: ci
on:
push:
branches: main
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to DigitalOcean Container Registry
uses: docker/login-action@v3
with:
registry: registry.digitalocean.com
username: ${{ vars.DIGITALOCEAN_USERNAME }}
password: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
```
### Authenticate to multiple registries
To authenticate against multiple registries, you can specify the login-action
step multiple times in your workflow:
```yaml
name: ci
on:
push:
branches: main
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
```
You can also use the `registry-auth` input for raw authentication to
registries, defined as YAML objects. Each object have the same attributes as
current inputs (except `logout`):
> [!WARNING]
> We don't recommend using this method, it's better to use the action multiple
> times as shown above.
```yaml
name: ci
on:
push:
branches: main
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to registries
uses: docker/login-action@v3
with:
registry-auth: |
- username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
```
### Set scopes for the authentication token
The `scope` input allows limiting registry credentials to a specific repository
or namespace scope when building images with Buildx.
This is useful in GitHub Actions to avoid overriding the Docker Hub
authentication token embedded in GitHub-hosted runners, which is used for
pulling images without rate limits. By scoping credentials, you can
authenticate only where needed (typically for pushing), while keeping
unauthenticated pulls for base images.
When `scope` is set, credentials are written to the Buildx configuration
instead of the global Docker configuration. This means:
* Authentication applies only to the specified scope
* The default Docker Hub credentials remain available for pulls
* Credentials are used only by Buildx during the build
> [!IMPORTANT]
> Credentials written to the Buildx configuration are only accessible by Buildx.
> They are not available to `docker pull`, `docker push`, or any other Docker
> CLI commands outside Buildx.
> [!NOTE]
> This feature requires Buildx version 0.31.0 or later.
```yaml
name: ci
on:
push:
branches: main
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to Docker Hub (scoped)
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
scope: 'myorg/myimage@push'
-
name: Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: myorg/myimage:latest
```
In this example, base images are pulled using the embedded GitHub-hosted runner
credentials, while authenticated access is used only to push `myorg/myimage`.
## Customizing
### inputs
Following inputs can be used as `step.with` keys
The following inputs can be used as `step.with` keys:
| Name | Type | Default | Description |
|------------------|---------|-----------------------------|------------------------------------|
| `registry` | String | | Server address of Docker registry. If not set then will default to Docker Hub |
| `username` | String | | Username used to log against the Docker registry |
| `password` | String | | Password or personal access token used to log against the Docker registry |
|-----------------|--------|-------------|-------------------------------------------------------------------------------|
| `registry` | String | `docker.io` | Server address of Docker registry. If not set then will default to Docker Hub |
| `username` | String | | Username for authenticating to the Docker registry |
| `password` | String | | Password or personal access token for authenticating the Docker registry |
| `scope` | String | | Scope for the authentication token |
| `ecr` | String | `auto` | Specifies whether the given registry is ECR (`auto`, `true` or `false`) |
| `logout` | Bool | `true` | Log out from the Docker registry at the end of a job |
| `registry-auth` | YAML | | Raw authentication to registries, defined as YAML objects |
## Keep up-to-date with GitHub Dependabot
> [!NOTE]
> The `registry-auth` input cannot be used with other inputs except `logout`.
Since [Dependabot](https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-github-dependabot)
has [native GitHub Actions support](https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem),
to enable it on your GitHub repo all you need to do is add the `.github/dependabot.yml` file:
## Contributing
```yaml
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
```
Want to contribute? Awesome! You can find information about contributing to
this project in the [CONTRIBUTING.md](/.github/CONTRIBUTING.md)
+13 -2
View File
@@ -1,5 +1,6 @@
import {beforeEach, describe, expect, jest, test} from '@jest/globals';
import {AuthorizationData} from '@aws-sdk/client-ecr';
import * as aws from '../src/aws';
describe('isECR', () => {
@@ -9,7 +10,10 @@ describe('isECR', () => {
['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]
['012345678901.dkr-ecr.eu-north-1.on.aws', true],
['012345678901.dkr.ecr.eusc-de-east-1.amazonaws.eu', true],
['public.ecr.aws', true],
['ecr-public.aws.com', true]
])('given registry %p', async (registry, expected) => {
expect(aws.isECR(registry)).toEqual(expected);
});
@@ -22,7 +26,10 @@ describe('isPubECR', () => {
['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]
['012345678901.dkr-ecr.eu-north-1.on.aws', false],
['012345678901.dkr.ecr.eusc-de-east-1.amazonaws.eu', false],
['public.ecr.aws', true],
['ecr-public.aws.com', true]
])('given registry %p', async (registry, expected) => {
expect(aws.isPubECR(registry)).toEqual(expected);
});
@@ -33,6 +40,8 @@ describe('getRegion', () => {
['012345678901.dkr.ecr.eu-west-3.amazonaws.com', 'eu-west-3'],
['876820548815.dkr.ecr.cn-north-1.amazonaws.com.cn', 'cn-north-1'],
['390948362332.dkr.ecr.cn-northwest-1.amazonaws.com.cn', 'cn-northwest-1'],
['012345678901.dkr-ecr.eu-north-1.on.aws', 'eu-north-1'],
['012345678901.dkr.ecr.eusc-de-east-1.amazonaws.eu', 'eusc-de-east-1'],
['public.ecr.aws', 'us-east-1']
])('given registry %p', async (registry, expected) => {
expect(aws.getRegion(registry)).toEqual(expected);
@@ -45,6 +54,8 @@ describe('getAccountIDs', () => {
['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']],
['876820548815.dkr-ecr.eu-north-1.on.aws', '012345678910,023456789012', ['876820548815', '012345678910', '023456789012']],
['012345678901.dkr.ecr.eusc-de-east-1.amazonaws.eu', '012345678910,023456789012', ['012345678901', '012345678910', '023456789012']],
['public.ecr.aws', undefined, []]
])('given registry %p', async (registry, accountIDsEnv, expected) => {
if (accountIDsEnv) {
+1
View File
@@ -1,4 +1,5 @@
import {expect, test} from '@jest/globals';
import {getInputs} from '../src/context';
test('with password and username getInputs does not throw error', async () => {
+21 -7
View File
@@ -1,14 +1,16 @@
import {expect, jest, test} from '@jest/globals';
import {loginStandard, logout} from '../src/docker';
import * as path from 'path';
import * as exec from '@actions/exec';
import {loginStandard, logout} from '../src/docker';
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
process.env['RUNNER_TEMP'] = path.join(__dirname, 'runner');
test('loginStandard calls exec', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const execSpy = jest.spyOn(exec, 'getExecOutput').mockImplementation(async () => {
const execSpy = jest.spyOn(Docker, 'getExecOutput').mockImplementation(async () => {
return {
exitCode: expect.any(Number),
stdout: expect.any(Function),
@@ -22,7 +24,13 @@ test('loginStandard calls exec', async () => {
await loginStandard(registry, username, password);
expect(execSpy).toHaveBeenCalledWith(`docker`, ['login', '--password-stdin', '--username', username, registry], {
expect(execSpy).toHaveBeenCalledTimes(1);
const callfunc = execSpy.mock.calls[0];
if (callfunc && callfunc[1]) {
// we don't want to check env opt
callfunc[1].env = undefined;
}
expect(execSpy).toHaveBeenCalledWith(['login', '--password-stdin', '--username', username, registry], {
input: Buffer.from(password),
silent: true,
ignoreReturnCode: true
@@ -32,7 +40,7 @@ test('loginStandard calls exec', async () => {
test('logout calls exec', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const execSpy = jest.spyOn(exec, 'getExecOutput').mockImplementation(async () => {
const execSpy = jest.spyOn(Docker, 'getExecOutput').mockImplementation(async () => {
return {
exitCode: expect.any(Number),
stdout: expect.any(Function),
@@ -42,9 +50,15 @@ test('logout calls exec', async () => {
const registry = 'https://ghcr.io';
await logout(registry);
await logout(registry, '');
expect(execSpy).toHaveBeenCalledWith(`docker`, ['logout', registry], {
expect(execSpy).toHaveBeenCalledTimes(1);
const callfunc = execSpy.mock.calls[0];
if (callfunc && callfunc[1]) {
// we don't want to check env opt
callfunc[1].env = undefined;
}
expect(execSpy).toHaveBeenCalledWith(['logout', registry], {
ignoreReturnCode: true
});
});
-65
View File
@@ -1,65 +0,0 @@
import {expect, jest, test} from '@jest/globals';
import osm = require('os');
import {main} from '../src/main';
import * as docker from '../src/docker';
import * as stateHelper from '../src/state-helper';
test('errors without username and password', async () => {
jest.spyOn(osm, 'platform').mockImplementation(() => 'linux');
process.env['INPUT_LOGOUT'] = 'true'; // default value
await expect(main()).rejects.toThrow(new Error('Username and password required'));
});
test('successful with username and password', async () => {
jest.spyOn(osm, 'platform').mockImplementation(() => 'linux');
const setRegistrySpy = jest.spyOn(stateHelper, 'setRegistry');
const setLogoutSpy = jest.spyOn(stateHelper, 'setLogout');
const dockerSpy = jest.spyOn(docker, 'login').mockImplementation(() => Promise.resolve());
const username = 'dbowie';
process.env[`INPUT_USERNAME`] = username;
const password = 'groundcontrol';
process.env[`INPUT_PASSWORD`] = password;
const ecr = 'auto';
process.env['INPUT_ECR'] = ecr;
const logout = false;
process.env['INPUT_LOGOUT'] = String(logout);
await main();
expect(setRegistrySpy).toHaveBeenCalledWith('');
expect(setLogoutSpy).toHaveBeenCalledWith(logout);
expect(dockerSpy).toHaveBeenCalledWith('', username, password, ecr);
});
test('calls docker login', async () => {
jest.spyOn(osm, 'platform').mockImplementation(() => 'linux');
const setRegistrySpy = jest.spyOn(stateHelper, 'setRegistry');
const setLogoutSpy = jest.spyOn(stateHelper, 'setLogout');
const dockerSpy = jest.spyOn(docker, 'login').mockImplementation(() => Promise.resolve());
const username = 'dbowie';
process.env[`INPUT_USERNAME`] = username;
const password = 'groundcontrol';
process.env[`INPUT_PASSWORD`] = password;
const registry = 'ghcr.io';
process.env[`INPUT_REGISTRY`] = registry;
const ecr = 'auto';
process.env['INPUT_ECR'] = ecr;
const logout = true;
process.env['INPUT_LOGOUT'] = String(logout);
await main();
expect(setRegistrySpy).toHaveBeenCalledWith(registry);
expect(setLogoutSpy).toHaveBeenCalledWith(logout);
expect(dockerSpy).toHaveBeenCalledWith(registry, username, password, ecr);
});
+7 -2
View File
@@ -18,14 +18,19 @@ inputs:
required: false
ecr:
description: 'Specifies whether the given registry is ECR (auto, true or false)'
default: 'auto'
required: false
scope:
description: 'Scope for the authentication token'
required: false
logout:
description: 'Log out from the Docker registry at the end of a job'
default: 'true'
required: false
registry-auth:
description: 'Raw authentication to registries, defined as YAML objects'
required: false
runs:
using: 'node16'
using: 'node20'
main: 'dist/index.js'
post: 'dist/index.js'
+25 -21
View File
@@ -1,15 +1,20 @@
# syntax=docker/dockerfile:1
ARG NODE_VERSION=16
ARG DOCKER_VERSION=20.10.13
ARG BUILDX_VERSION=0.8.1
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine AS base
RUN apk add --no-cache cpio findutils git
WORKDIR /src
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache <<EOT
corepack enable
yarn --version
yarn config set --home enableTelemetry 0
EOT
FROM base AS deps
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn install && mkdir /vendor && cp yarn.lock /vendor
@@ -18,18 +23,19 @@ COPY --from=deps /vendor /
FROM deps AS vendor-validate
RUN --mount=type=bind,target=.,rw <<EOT
set -e
git add -A
cp -rf /vendor/* .
if [ -n "$(git status --porcelain -- yarn.lock)" ]; then
echo >&2 'ERROR: Vendor result differs. Please vendor your package with "docker buildx bake vendor-update"'
set -e
git add -A
cp -rf /vendor/* .
if [ -n "$(git status --porcelain -- yarn.lock)" ]; then
echo >&2 'ERROR: Vendor result differs. Please vendor your package with "docker buildx bake vendor"'
git status --porcelain -- yarn.lock
exit 1
fi
fi
EOT
FROM deps AS build
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn run build && mkdir /out && cp -Rf dist /out/
@@ -38,41 +44,39 @@ COPY --from=build /out /
FROM build AS build-validate
RUN --mount=type=bind,target=.,rw <<EOT
set -e
git add -A
cp -rf /out/* .
if [ -n "$(git status --porcelain -- dist)" ]; then
set -e
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
fi
EOT
FROM deps AS format
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn run format \
&& mkdir /out && find . -name '*.ts' -not -path './node_modules/*' | cpio -pdm /out
&& mkdir /out && find . -name '*.ts' -not -path './node_modules/*' -not -path './.yarn/*' | cpio -pdm /out
FROM scratch AS format-update
COPY --from=format /out /
FROM deps AS lint
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn run lint
FROM docker:${DOCKER_VERSION} as docker
FROM docker/buildx-bin:${BUILDX_VERSION} as buildx
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/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
--mount=type=bind,from=docker,source=/usr/local/bin/docker,target=/usr/bin/docker \
--mount=type=bind,from=buildx,source=/buildx,target=/usr/libexec/docker/cli-plugins/docker-buildx \
yarn run test --coverageDirectory=/tmp/coverage
yarn run test --coverage --coverageDirectory=/tmp/coverage
FROM scratch AS test-coverage
COPY --from=test /tmp/coverage /
Generated Vendored
+102 -16
View File
File diff suppressed because one or more lines are too long
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
Generated Vendored
+10876 -7555
View File
File diff suppressed because it is too large Load Diff
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+15 -2
View File
@@ -1,9 +1,15 @@
target "_common" {
args = {
BUILDKIT_CONTEXT_KEEP_GIT_DIR = 1
}
}
group "default" {
targets = ["build"]
}
group "pre-checkin" {
targets = ["vendor-update", "format", "build"]
targets = ["vendor", "format", "build"]
}
group "validate" {
@@ -11,42 +17,49 @@ group "validate" {
}
target "build" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "build-update"
output = ["."]
}
target "build-validate" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "build-validate"
output = ["type=cacheonly"]
}
target "format" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "format-update"
output = ["."]
}
target "lint" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "lint"
output = ["type=cacheonly"]
}
target "vendor-update" {
target "vendor" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "vendor-update"
output = ["."]
}
target "vendor-validate" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "vendor-validate"
output = ["type=cacheonly"]
}
target "test" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "test-coverage"
output = ["./coverage"]
+1
View File
@@ -15,6 +15,7 @@ process.env = Object.assign({}, process.env, {
module.exports = {
clearMocks: true,
testEnvironment: 'node',
moduleFileExtensions: ['js', 'ts'],
testMatch: ['**/*.test.ts'],
transform: {
+33 -33
View File
@@ -1,13 +1,16 @@
{
"name": "docker-login",
"description": "GitHub Action to login against a Docker registry",
"main": "lib/main.js",
"main": "src/main.ts",
"scripts": {
"build": "ncc build src/main.ts --source-map --minify --license licenses.txt",
"lint": "eslint src/**/*.ts __tests__/**/*.ts",
"format": "eslint --fix src/**/*.ts __tests__/**/*.ts",
"test": "jest --coverage",
"all": "yarn run build && yarn run format && yarn test"
"build": "ncc build --source-map --minify --license licenses.txt",
"lint": "yarn run prettier && yarn run eslint",
"format": "yarn run prettier:fix && yarn run eslint:fix",
"eslint": "eslint --max-warnings=0 .",
"eslint:fix": "eslint --fix .",
"prettier": "prettier --check \"./**/*.ts\"",
"prettier:fix": "prettier --write \"./**/*.ts\"",
"test": "jest"
},
"repository": {
"type": "git",
@@ -18,35 +21,32 @@
"docker",
"login"
],
"author": "Docker",
"contributors": [
{
"name": "CrazyMax",
"url": "https://crazymax.dev"
}
],
"license": "MIT",
"author": "Docker Inc.",
"license": "Apache-2.0",
"packageManager": "yarn@4.9.2",
"dependencies": {
"@actions/core": "^1.10.0",
"@aws-sdk/client-ecr": "^3.347.1",
"@aws-sdk/client-ecr-public": "^3.347.1",
"@docker/actions-toolkit": "^0.2.0",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.0"
"@actions/core": "^1.11.1",
"@aws-sdk/client-ecr": "^3.890.0",
"@aws-sdk/client-ecr-public": "^3.890.0",
"@docker/actions-toolkit": "^0.63.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"js-yaml": "^4.1.0"
},
"devDependencies": {
"@types/node": "^16.18.21",
"@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^5.56.0",
"@vercel/ncc": "^0.36.1",
"eslint": "^8.36.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^29.5.0",
"prettier": "^2.8.7",
"ts-jest": "^29.0.5",
"ts-node": "^10.9.1",
"typescript": "^4.9.5"
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.19.9",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"@vercel/ncc": "^0.38.3",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.2",
"eslint-plugin-jest": "^28.14.0",
"eslint-plugin-prettier": "^5.5.4",
"jest": "^29.7.0",
"prettier": "^3.6.2",
"ts-jest": "^29.4.1",
"ts-node": "^10.9.2",
"typescript": "^5.9.2"
}
}
+5 -4
View File
@@ -1,18 +1,19 @@
import * as core from '@actions/core';
import {ECR} from '@aws-sdk/client-ecr';
import {ECRPUBLIC} from '@aws-sdk/client-ecr-public';
import {NodeHttpHandler} from '@aws-sdk/node-http-handler';
import {NodeHttpHandler} from '@smithy/node-http-handler';
import {HttpProxyAgent} from 'http-proxy-agent';
import {HttpsProxyAgent} from 'https-proxy-agent';
const ecrRegistryRegex = /^(([0-9]{12})\.dkr\.ecr\.(.+)\.amazonaws\.com(.cn)?)(\/([^:]+)(:.+)?)?$/;
const ecrRegistryRegex = /^(([0-9]{12})\.(dkr\.ecr|dkr-ecr)\.(.+)\.(on\.aws|amazonaws\.(com(.cn)?|eu)))(\/([^:]+)(:.+)?)?$/;
const ecrPublicRegistryRegex = /public\.ecr\.aws|ecr-public\.aws\.com/;
export const isECR = (registry: string): boolean => {
return ecrRegistryRegex.test(registry) || isPubECR(registry);
};
export const isPubECR = (registry: string): boolean => {
return registry === 'public.ecr.aws';
return ecrPublicRegistryRegex.test(registry);
};
export const getRegion = (registry: string): string => {
@@ -23,7 +24,7 @@ export const getRegion = (registry: string): string => {
if (!matches) {
return '';
}
return matches[3];
return matches[4];
};
export const getAccountIDs = (registry: string): string[] => {
+72 -1
View File
@@ -1,11 +1,27 @@
import path from 'path';
import * as core from '@actions/core';
import * as yaml from 'js-yaml';
import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx';
import {Util} from '@docker/actions-toolkit/lib/util';
export interface Inputs {
registry: string;
username: string;
password: string;
scope: string;
ecr: string;
logout: boolean;
registryAuth: string;
}
export interface Auth {
registry: string;
username: string;
password: string;
scope: string;
ecr: string;
configDir: string;
}
export function getInputs(): Inputs {
@@ -13,7 +29,62 @@ export function getInputs(): Inputs {
registry: core.getInput('registry'),
username: core.getInput('username'),
password: core.getInput('password'),
scope: core.getInput('scope'),
ecr: core.getInput('ecr'),
logout: core.getBooleanInput('logout')
logout: core.getBooleanInput('logout'),
registryAuth: core.getInput('registry-auth')
};
}
export function getAuthList(inputs: Inputs): Array<Auth> {
if (inputs.registryAuth && (inputs.registry || inputs.username || inputs.password || inputs.scope || inputs.ecr)) {
throw new Error('Cannot use registry-auth with other inputs');
}
let auths: Array<Auth> = [];
if (!inputs.registryAuth) {
auths.push({
registry: inputs.registry || 'docker.io',
username: inputs.username,
password: inputs.password,
scope: inputs.scope,
ecr: inputs.ecr || 'auto',
configDir: scopeToConfigDir(inputs.registry, inputs.scope)
});
} else {
auths = (yaml.load(inputs.registryAuth) as Array<Auth>).map(auth => {
core.setSecret(auth.password); // redacted in workflow logs
return {
registry: auth.registry || 'docker.io',
username: auth.username,
password: auth.password,
scope: auth.scope,
ecr: auth.ecr || 'auto',
configDir: scopeToConfigDir(auth.registry || 'docker.io', auth.scope)
};
});
}
if (auths.length == 0) {
throw new Error('No registry to login');
}
return auths;
}
export function scopeToConfigDir(registry: string, scope?: string): string {
if (scopeDisabled() || !scope || scope === '') {
return '';
}
let configDir = path.join(Buildx.configDir, 'config', registry === 'docker.io' ? 'registry-1.docker.io' : registry);
if (scope.startsWith('@')) {
configDir += scope;
} else {
configDir = path.join(configDir, scope);
}
return configDir;
}
function scopeDisabled(): boolean {
if (process.env.DOCKER_LOGIN_SCOPE_DISABLED) {
return Util.parseBool(process.env.DOCKER_LOGIN_SCOPE_DISABLED);
}
return false;
}
+50 -34
View File
@@ -1,18 +1,31 @@
import * as aws from './aws';
import * as core from '@actions/core';
import {Exec} from '@docker/actions-toolkit/lib/exec';
export async function login(registry: string, username: string, password: string, ecr: string): Promise<void> {
if (/true/i.test(ecr) || (ecr == 'auto' && aws.isECR(registry))) {
await loginECR(registry, username, password);
import * as aws from './aws';
import * as context from './context';
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
export async function login(auth: context.Auth): Promise<void> {
if (/true/i.test(auth.ecr) || (auth.ecr == 'auto' && aws.isECR(auth.registry))) {
await loginECR(auth.registry, auth.username, auth.password, auth.scope);
} else {
await loginStandard(registry, username, password);
await loginStandard(auth.registry, auth.username, auth.password, auth.scope);
}
}
export async function logout(registry: string): Promise<void> {
await Exec.getExecOutput('docker', ['logout', registry], {
ignoreReturnCode: true
export async function logout(registry: string, configDir: string): Promise<void> {
let envs: {[key: string]: string} | undefined;
if (configDir !== '') {
envs = Object.assign({}, process.env, {
DOCKER_CONFIG: configDir
}) as {
[key: string]: string;
};
core.info(`Alternative config dir: ${configDir}`);
}
await Docker.getExecOutput(['logout', registry], {
ignoreReturnCode: true,
env: envs
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
core.warning(res.stderr.trim());
@@ -20,46 +33,49 @@ export async function logout(registry: string): Promise<void> {
});
}
export async function loginStandard(registry: string, username: string, password: string): Promise<void> {
if (!username || !password) {
export async function loginStandard(registry: string, username: string, password: string, scope?: string): Promise<void> {
if (!username && !password) {
throw new Error('Username and password required');
}
const loginArgs: Array<string> = ['login', '--password-stdin'];
loginArgs.push('--username', username);
loginArgs.push(registry);
if (registry) {
core.info(`Logging into ${registry}...`);
} else {
core.info(`Logging into Docker Hub...`);
if (!username) {
throw new Error('Username required');
}
await Exec.getExecOutput('docker', loginArgs, {
ignoreReturnCode: true,
silent: true,
input: Buffer.from(password)
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.trim());
if (!password) {
throw new Error('Password required');
}
core.info(`Login Succeeded!`);
});
await loginExec(registry, username, password, scope);
}
export async function loginECR(registry: string, username: string, password: string): Promise<void> {
export async function loginECR(registry: string, username: string, password: string, scope?: string): Promise<void> {
core.info(`Retrieving registries data through AWS SDK...`);
const regDatas = await aws.getRegistriesData(registry, username, password);
for (const regData of regDatas) {
core.info(`Logging into ${regData.registry}...`);
await Exec.getExecOutput('docker', ['login', '--password-stdin', '--username', regData.username, regData.registry], {
await loginExec(regData.registry, regData.username, regData.password, scope);
}
}
async function loginExec(registry: string, username: string, password: string, scope?: string): Promise<void> {
let envs: {[key: string]: string} | undefined;
const configDir = context.scopeToConfigDir(registry, scope);
if (configDir !== '') {
envs = Object.assign({}, process.env, {
DOCKER_CONFIG: configDir
}) as {
[key: string]: string;
};
core.info(`Logging into ${registry} (scope ${scope})...`);
} else {
core.info(`Logging into ${registry}...`);
}
await Docker.getExecOutput(['login', '--password-stdin', '--username', username, registry], {
ignoreReturnCode: true,
silent: true,
input: Buffer.from(regData.password)
input: Buffer.from(password),
env: envs
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.trim());
}
core.info('Login Succeeded!');
});
}
}
+22 -5
View File
@@ -1,3 +1,4 @@
import * as core from '@actions/core';
import * as actionsToolkit from '@docker/actions-toolkit';
import * as context from './context';
@@ -5,17 +6,33 @@ import * as docker from './docker';
import * as stateHelper from './state-helper';
export async function main(): Promise<void> {
const input: context.Inputs = context.getInputs();
stateHelper.setRegistry(input.registry);
stateHelper.setLogout(input.logout);
await docker.login(input.registry, input.username, input.password, input.ecr);
const inputs: context.Inputs = context.getInputs();
stateHelper.setLogout(inputs.logout);
const auths = context.getAuthList(inputs);
stateHelper.setRegistries(Array.from(new Map(auths.map(auth => [`${auth.registry}|${auth.configDir}`, {registry: auth.registry, configDir: auth.configDir} as stateHelper.RegistryState])).values()));
if (auths.length === 1) {
await docker.login(auths[0]);
return;
}
for (const auth of auths) {
await core.group(`Login to ${auth.registry}`, async () => {
await docker.login(auth);
});
}
}
async function post(): Promise<void> {
if (!stateHelper.logout) {
return;
}
await docker.logout(stateHelper.registry);
for (const registryState of stateHelper.registries) {
await core.group(`Logout from ${registryState.registry}`, async () => {
await docker.logout(registryState.registry, registryState.configDir);
});
}
}
actionsToolkit.run(main, post);
+8 -3
View File
@@ -1,10 +1,15 @@
import * as core from '@actions/core';
export const registry = process.env['STATE_registry'] || '';
export const registries = process.env['STATE_registries'] ? (JSON.parse(process.env['STATE_registries']) as Array<RegistryState>) : [];
export const logout = /true/i.test(process.env['STATE_logout'] || '');
export function setRegistry(registry: string) {
core.saveState('registry', registry);
export interface RegistryState {
registry: string;
configDir: string;
}
export function setRegistries(registries: Array<RegistryState>) {
core.saveState('registries', JSON.stringify(registries));
}
export function setLogout(logout: boolean) {
+8219 -4409
View File
File diff suppressed because it is too large Load Diff