Compare commits

..

5 Commits

Author SHA1 Message Date
Fedor Dikarev
98b2971c0d Merge 087884b3d2 into 327cd5a69d 2025-01-09 11:00:08 +00:00
Fedor Dikarev
087884b3d2 mistype
Signed-off-by: Fedor Dikarev <fedor.dikarev@gmail.com>
2024-10-30 06:26:57 +01:00
Fedor Dikarev
ca362a507b add attempts to the action.yml
Signed-off-by: Fedor Dikarev <fedor.dikarev@gmail.com>
2024-10-29 22:28:24 +01:00
Fedor Dikarev
162c32cf05 trim stderr for checking
Signed-off-by: Fedor Dikarev <fedor.dikarev@gmail.com>
2024-10-29 22:14:18 +01:00
Fedor Dikarev
8479e9040e add retries for 502
Signed-off-by: Fedor Dikarev <fedor.dikarev@gmail.com>
2024-10-29 22:14:18 +01:00
23 changed files with 4160 additions and 4450 deletions

View File

@@ -19,7 +19,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Stop docker
run: |
@@ -43,7 +43,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to GitHub Container Registry
uses: ./
@@ -60,7 +60,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to GitHub Container Registry
uses: ./
@@ -85,7 +85,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to ACR
uses: ./
@@ -105,7 +105,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to Docker Hub
uses: ./
@@ -124,7 +124,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to ECR
uses: ./
@@ -144,10 +144,10 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v5
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -169,7 +169,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to Public ECR
continue-on-error: ${{ matrix.os == 'windows-latest' }}
@@ -192,10 +192,10 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v5
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -207,7 +207,7 @@ jobs:
with:
registry: public.ecr.aws
ghcr:
github-container:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
@@ -218,7 +218,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to GitHub Container Registry
uses: ./
@@ -238,7 +238,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to GitLab
uses: ./
@@ -258,7 +258,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to Google Artifact Registry
uses: ./
@@ -278,7 +278,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Login to Google Container Registry
uses: ./
@@ -286,195 +286,3 @@ 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

View File

@@ -31,10 +31,10 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Initialize CodeQL
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
config: |
@@ -42,9 +42,9 @@ jobs:
- src
-
name: Autobuild
uses: github/codeql-action/autobuild@v4
uses: github/codeql-action/autobuild@v3
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@@ -1,17 +0,0 @@
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

View File

@@ -15,7 +15,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Publish
uses: actions/publish-immutable-action@v0.0.4

View File

@@ -15,18 +15,14 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Test
uses: docker/bake-action@v6
with:
source: .
targets: test
-
name: Upload coverage
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v4
with:
files: ./coverage/clover.xml
file: ./coverage/clover.xml
token: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -19,7 +19,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: List targets
id: generate

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +1,3 @@
# https://yarnpkg.com/configuration/yarnrc
compressionLevel: mixed
enableGlobalCache: false
enableHardenedMode: true
logFilters:
- code: YN0013
level: discard
@@ -11,7 +5,9 @@ logFilters:
level: discard
- code: YN0076
level: discard
- code: YN0086
level: discard
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"

139
README.md
View File

@@ -24,8 +24,6 @@ ___
* [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)
* [Contributing](#contributing)
@@ -303,7 +301,7 @@ 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/security-iam-awsmanpol.html#security-iam-awsmanpol-AmazonEC2ContainerRegistryPowerUser).
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).
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.
@@ -496,140 +494,19 @@ jobs:
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
The following inputs can be used as `step.with` keys:
| Name | Type | Default | Description |
|-----------------|--------|-------------|-------------------------------------------------------------------------------|
| `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 |
> [!NOTE]
> The `registry-auth` input cannot be used with other inputs except `logout`.
| Name | Type | Default | Description |
|------------|--------|---------|-------------------------------------------------------------------------------|
| `registry` | String | | 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 |
| `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 |
## Contributing

View File

@@ -10,9 +10,7 @@ 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],
['012345678901.dkr-ecr.eu-north-1.on.aws', true],
['public.ecr.aws', true],
['ecr-public.aws.com', true]
['public.ecr.aws', true]
])('given registry %p', async (registry, expected) => {
expect(aws.isECR(registry)).toEqual(expected);
});
@@ -25,9 +23,7 @@ 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],
['012345678901.dkr-ecr.eu-north-1.on.aws', false],
['public.ecr.aws', true],
['ecr-public.aws.com', true]
['public.ecr.aws', true]
])('given registry %p', async (registry, expected) => {
expect(aws.isPubECR(registry)).toEqual(expected);
});
@@ -38,7 +34,6 @@ 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'],
['public.ecr.aws', 'us-east-1']
])('given registry %p', async (registry, expected) => {
expect(aws.getRegion(registry)).toEqual(expected);
@@ -51,7 +46,6 @@ 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']],
['public.ecr.aws', undefined, []]
])('given registry %p', async (registry, accountIDsEnv, expected) => {
if (accountIDsEnv) {

View File

@@ -50,7 +50,7 @@ test('logout calls exec', async () => {
const registry = 'https://ghcr.io';
await logout(registry, '');
await logout(registry);
expect(execSpy).toHaveBeenCalledTimes(1);
const callfunc = execSpy.mock.calls[0];

View File

@@ -18,16 +18,15 @@ inputs:
required: false
ecr:
description: 'Specifies whether the given registry is ECR (auto, true or false)'
required: false
scope:
description: 'Scope for the authentication token'
default: 'auto'
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'
attempts:
description: 'Number of attempts to try in case of server-side errors'
default: '1'
required: false
runs:

38
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

1031
dist/licenses.txt generated vendored

File diff suppressed because it is too large Load Diff

2
dist/sourcemap-register.js generated vendored

File diff suppressed because one or more lines are too long

View File

@@ -23,30 +23,28 @@
],
"author": "Docker Inc.",
"license": "Apache-2.0",
"packageManager": "yarn@4.9.2",
"packageManager": "yarn@3.6.3",
"dependencies": {
"@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",
"@aws-sdk/client-ecr": "^3.664.0",
"@aws-sdk/client-ecr-public": "^3.664.0",
"@docker/actions-toolkit": "^0.42.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"js-yaml": "^4.1.0"
"https-proxy-agent": "^7.0.5"
},
"devDependencies": {
"@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",
"@types/node": "^20.12.12",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^28.5.0",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"prettier": "^3.6.2",
"ts-jest": "^29.4.1",
"prettier": "^3.2.5",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.9.2"
"typescript": "^5.4.5"
}
}

View File

@@ -5,15 +5,14 @@ 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|dkr-ecr)\.(.+)\.(on\.aws|amazonaws\.com(.cn)?))(\/([^:]+)(:.+)?)?$/;
const ecrPublicRegistryRegex = /public\.ecr\.aws|ecr-public\.aws\.com/;
const ecrRegistryRegex = /^(([0-9]{12})\.dkr\.ecr\.(.+)\.amazonaws\.com(.cn)?)(\/([^:]+)(:.+)?)?$/;
export const isECR = (registry: string): boolean => {
return ecrRegistryRegex.test(registry) || isPubECR(registry);
};
export const isPubECR = (registry: string): boolean => {
return ecrPublicRegistryRegex.test(registry);
return registry === 'public.ecr.aws';
};
export const getRegion = (registry: string): string => {
@@ -24,7 +23,7 @@ export const getRegion = (registry: string): string => {
if (!matches) {
return '';
}
return matches[4];
return matches[3];
};
export const getAccountIDs = (registry: string): string[] => {

View File

@@ -1,27 +1,12 @@
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;
attempts: number;
}
export function getInputs(): Inputs {
@@ -29,62 +14,8 @@ 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'),
registryAuth: core.getInput('registry-auth')
attempts: Number.parseInt(core.getInput('attempts'))
};
}
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;
}

View File

@@ -1,31 +1,19 @@
import * as core from '@actions/core';
import * as aws from './aws';
import * as context from './context';
import * as core from '@actions/core';
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);
export async function login(registry: string, username: string, password: string, ecr: string, attempts: number): Promise<void> {
if (/true/i.test(ecr) || (ecr == 'auto' && aws.isECR(registry))) {
await loginECR(registry, username, password);
} else {
await loginStandard(auth.registry, auth.username, auth.password, auth.scope);
await loginStandard(registry, username, password, attempts);
}
}
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}`);
}
export async function logout(registry: string): Promise<void> {
await Docker.getExecOutput(['logout', registry], {
ignoreReturnCode: true,
env: envs
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
core.warning(res.stderr.trim());
@@ -33,7 +21,7 @@ export async function logout(registry: string, configDir: string): Promise<void>
});
}
export async function loginStandard(registry: string, username: string, password: string, scope?: string): Promise<void> {
export async function loginStandard(registry: string, username: string, password: string, attempts: number): Promise<void> {
if (!username && !password) {
throw new Error('Username and password required');
}
@@ -43,39 +31,55 @@ export async function loginStandard(registry: string, username: string, password
if (!password) {
throw new Error('Password required');
}
await loginExec(registry, username, password, scope);
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...`);
}
let attempt: number = 1
let succeeded: boolean = false
for (let attempt = 1; (attempt <= attempts) && (!succeeded); attempt++) {
await Docker.getExecOutput(loginArgs, {
ignoreReturnCode: true,
silent: true,
input: Buffer.from(password)
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
let isRetriable: boolean
isRetriable = res.stderr.trim().endsWith("502 Bad Gateway")
if (!isRetriable || (attempt >= attempts) {
throw new Error(res.stderr.trim());
}
} else {
core.info(`Login Succeeded!`);
succeeded = true;
}
});
if ((attempt < attempts) && !succeeded) {
await new Promise(r => setTimeout(r, 10000))
}
}
}
export async function loginECR(registry: string, username: string, password: string, scope?: string): Promise<void> {
export async function loginECR(registry: string, username: string, password: string): Promise<void> {
core.info(`Retrieving registries data through AWS SDK...`);
const regDatas = await aws.getRegistriesData(registry, username, password);
for (const regData of regDatas) {
await loginExec(regData.registry, regData.username, regData.password, scope);
core.info(`Logging into ${regData.registry}...`);
await Docker.getExecOutput(['login', '--password-stdin', '--username', regData.username, regData.registry], {
ignoreReturnCode: true,
silent: true,
input: Buffer.from(regData.password)
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.trim());
}
core.info('Login Succeeded!');
});
}
}
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(password),
env: envs
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.trim());
}
core.info('Login Succeeded!');
});
}

View File

@@ -1,4 +1,3 @@
import * as core from '@actions/core';
import * as actionsToolkit from '@docker/actions-toolkit';
import * as context from './context';
@@ -6,33 +5,17 @@ import * as docker from './docker';
import * as stateHelper from './state-helper';
export async function main(): Promise<void> {
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);
});
}
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, input.attempts);
}
async function post(): Promise<void> {
if (!stateHelper.logout) {
return;
}
for (const registryState of stateHelper.registries) {
await core.group(`Logout from ${registryState.registry}`, async () => {
await docker.logout(registryState.registry, registryState.configDir);
});
}
await docker.logout(stateHelper.registry);
}
actionsToolkit.run(main, post);

View File

@@ -1,15 +1,10 @@
import * as core from '@actions/core';
export const registries = process.env['STATE_registries'] ? (JSON.parse(process.env['STATE_registries']) as Array<RegistryState>) : [];
export const registry = process.env['STATE_registry'] || '';
export const logout = /true/i.test(process.env['STATE_logout'] || '');
export interface RegistryState {
registry: string;
configDir: string;
}
export function setRegistries(registries: Array<RegistryState>) {
core.saveState('registries', JSON.stringify(registries));
export function setRegistry(registry: string) {
core.saveState('registry', registry);
}
export function setLogout(logout: boolean) {

6309
yarn.lock

File diff suppressed because it is too large Load Diff