Compare commits

...

2 Commits

Author SHA1 Message Date
CrazyMax
20e0a61734 chore: update generated content
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2026-01-06 17:18:40 +01:00
CrazyMax
f40589c4bf Add scope input to set scopes for the authentication token
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2026-01-06 17:17:09 +01:00
8 changed files with 207 additions and 67 deletions

View File

@@ -207,7 +207,7 @@ jobs:
with: with:
registry: public.ecr.aws registry: public.ecr.aws
github-container: ghcr:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -356,3 +356,125 @@ jobs:
echo "::error::Should have failed" echo "::error::Should have failed"
exit 1 exit 1
fi 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

@@ -568,13 +568,13 @@ The following inputs can be used as `step.with` keys:
| `registry` | String | `docker.io` | Server address of Docker registry. If not set then will default to Docker Hub | | `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 | | `username` | String | | Username for authenticating to the Docker registry |
| `password` | String | | Password or personal access token for authenticating 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`) | | `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 | | `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 | | `registry-auth` | YAML | | Raw authentication to registries, defined as YAML objects |
> [!NOTE] > [!NOTE]
> The `registry-auth` input is mutually exclusive with `registry`, `username`, > The `registry-auth` input cannot be used with other inputs except `logout`.
> `password` and `ecr` inputs.
## Contributing ## Contributing

View File

@@ -19,6 +19,9 @@ inputs:
ecr: ecr:
description: 'Specifies whether the given registry is ECR (auto, true or false)' description: 'Specifies whether the given registry is ECR (auto, true or false)'
required: false required: false
scope:
description: 'Scope for the authentication token'
required: false
logout: logout:
description: 'Log out from the Docker registry at the end of a job' description: 'Log out from the Docker registry at the end of a job'
default: 'true' default: 'true'

24
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

View File

@@ -4,6 +4,7 @@ export interface Inputs {
registry: string; registry: string;
username: string; username: string;
password: string; password: string;
scope: string;
ecr: string; ecr: string;
logout: boolean; logout: boolean;
registryAuth: string; registryAuth: string;
@@ -14,6 +15,7 @@ export function getInputs(): Inputs {
registry: core.getInput('registry'), registry: core.getInput('registry'),
username: core.getInput('username'), username: core.getInput('username'),
password: core.getInput('password'), password: core.getInput('password'),
scope: core.getInput('scope'),
ecr: core.getInput('ecr'), ecr: core.getInput('ecr'),
logout: core.getBooleanInput('logout'), logout: core.getBooleanInput('logout'),
registryAuth: core.getInput('registry-auth') registryAuth: core.getInput('registry-auth')

View File

@@ -1,13 +1,23 @@
import path from 'path';
import * as aws from './aws'; import * as aws from './aws';
import * as core from '@actions/core'; import * as core from '@actions/core';
import {Docker} from '@docker/actions-toolkit/lib/docker/docker'; import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx';
export async function login(registry: string, username: string, password: string, ecr: string): Promise<void> { export interface Auth {
if (/true/i.test(ecr) || (ecr == 'auto' && aws.isECR(registry))) { registry: string;
await loginECR(registry, username, password); username: string;
password: string;
scope?: string;
ecr: string;
}
export async function login(auth: 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 { } else {
await loginStandard(registry, username, password); await loginStandard(auth.registry, auth.username, auth.password, auth.scope);
} }
} }
@@ -21,7 +31,7 @@ export async function logout(registry: string): Promise<void> {
}); });
} }
export async function loginStandard(registry: string, username: string, password: string): Promise<void> { export async function loginStandard(registry: string, username: string, password: string, scope?: string): Promise<void> {
if (!username && !password) { if (!username && !password) {
throw new Error('Username and password required'); throw new Error('Username and password required');
} }
@@ -31,38 +41,44 @@ export async function loginStandard(registry: string, username: string, password
if (!password) { if (!password) {
throw new Error('Password required'); throw new Error('Password required');
} }
await loginExec(registry, username, password, scope);
}
const loginArgs: Array<string> = ['login', '--password-stdin']; export async function loginECR(registry: string, username: string, password: string, scope?: string): Promise<void> {
loginArgs.push('--username', username); core.info(`Retrieving registries data through AWS SDK...`);
loginArgs.push(registry); 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 ${registry}...`); async function loginExec(registry: string, username: string, password: string, scope?: string): Promise<void> {
await Docker.getExecOutput(loginArgs, { let envs: {[key: string]: string} | undefined;
if (scope && scope.length > 0) {
let dockerConfigDir = path.join(Buildx.configDir, 'config', registry === 'docker.io' ? 'registry-1.docker.io' : registry);
if (scope.startsWith('@')) {
dockerConfigDir += scope;
} else {
dockerConfigDir = path.join(dockerConfigDir, scope);
}
envs = Object.assign({}, process.env, {
DOCKER_CONFIG: dockerConfigDir
}) as {
[key: string]: string;
};
core.info(`Logging into ${registry} with scope ${scope}...`);
} else {
core.info(`Logging into ${registry}...`);
}
await Docker.getExecOutput(['login', '--password-stdin', '--username', username, registry], {
ignoreReturnCode: true, ignoreReturnCode: true,
silent: true, silent: true,
input: Buffer.from(password) input: Buffer.from(password),
env: envs
}).then(res => { }).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) { if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.trim()); throw new Error(res.stderr.trim());
} }
core.info(`Login Succeeded!`); core.info('Login Succeeded!');
}); });
} }
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) {
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!');
});
}
}

View File

@@ -6,45 +6,42 @@ import * as context from './context';
import * as docker from './docker'; import * as docker from './docker';
import * as stateHelper from './state-helper'; import * as stateHelper from './state-helper';
interface Auth {
registry: string;
username: string;
password: string;
ecr: string;
}
export async function main(): Promise<void> { export async function main(): Promise<void> {
const inputs: context.Inputs = context.getInputs(); const inputs: context.Inputs = context.getInputs();
stateHelper.setLogout(inputs.logout); stateHelper.setLogout(inputs.logout);
if (inputs.registryAuth && (inputs.registry || inputs.username || inputs.password || inputs.ecr)) { if (inputs.registryAuth && (inputs.registry || inputs.username || inputs.password || inputs.scope || inputs.ecr)) {
throw new Error('Cannot use registry-auth with other inputs'); throw new Error('Cannot use registry-auth with other inputs');
} }
if (!inputs.registryAuth) { if (!inputs.registryAuth) {
stateHelper.setRegistries([inputs.registry || 'docker.io']); stateHelper.setRegistries([inputs.registry || 'docker.io']);
await docker.login(inputs.registry || 'docker.io', inputs.username, inputs.password, inputs.ecr || 'auto'); await docker.login({
registry: inputs.registry || 'docker.io',
username: inputs.username,
password: inputs.password,
scope: inputs.scope,
ecr: inputs.ecr || 'auto'
});
return; return;
} }
const auths = yaml.load(inputs.registryAuth) as Auth[]; const auths = (yaml.load(inputs.registryAuth) as docker.Auth[]).map(auth => ({
registry: auth.registry || 'docker.io',
username: auth.username,
password: auth.password,
scope: auth.scope,
ecr: auth.ecr || 'auto'
}));
if (auths.length == 0) { if (auths.length == 0) {
throw new Error('No registry to login'); throw new Error('No registry to login');
} }
const registries: string[] = []; stateHelper.setRegistries(auths.map(auth => auth.registry).filter((value, index, self) => self.indexOf(value) === index));
for (const auth of auths) {
if (!auth.registry) {
registries.push('docker.io');
} else {
registries.push(auth.registry);
}
}
stateHelper.setRegistries(registries.filter((value, index, self) => self.indexOf(value) === index));
for (const auth of auths) { for (const auth of auths) {
await core.group(`Login to ${auth.registry || 'docker.io'}`, async () => { await core.group(`Login to ${auth.registry}`, async () => {
await docker.login(auth.registry || 'docker.io', auth.username, auth.password, auth.ecr || 'auto'); await docker.login(auth);
}); });
} }
} }