Feature: Enable Multi Registry login Authentication

Signed-off-by: unknown <arunkumarthiyagarajan95@gmail.com>
This commit is contained in:
unknown 2025-08-04 11:04:11 +05:30
parent ef38ec311a
commit 8d9860ef0e
6 changed files with 5287 additions and 8077 deletions

View File

@ -24,6 +24,7 @@ ___
* [OCI Oracle Cloud Infrastructure Registry (OCIR)](#oci-oracle-cloud-infrastructure-registry-ocir)
* [Quay.io](#quayio)
* [DigitalOcean](#digitalocean-container-registry)
* [Multiple Registries](#multiple-registries)
* [Customizing](#customizing)
* [inputs](#inputs)
* [Contributing](#contributing)
@ -494,6 +495,34 @@ jobs:
password: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
```
### Multiple Registries
You can login to multiple registries by providing a multiline string for the registry input. Each registry will be processed sequentically using the same username and password.
```yaml
name: ci
on:
push:
branches: main
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Login to Multiple Regsitries
uses: docker/login-action@v7
with:
registry: |
ghcr.io
docker.io
registry.gitlab.com
# for single registry,
# registry: "ghcr.io"
username: ${{ vars.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
```
## Customizing
### inputs
@ -502,7 +531,7 @@ 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 |
| `registry` | String | | Server address of Docker registry. If not set then will default to Docker Hub. Supports multiline string for multiplr registries. |
| `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`) |

View File

@ -20,17 +20,34 @@ test('loginStandard calls exec', async () => {
const username = 'dbowie';
const password = 'groundcontrol';
const registry = 'https://ghcr.io';
// Define const registry as multiline input
const registry = `https://ghcr.io
https://docker.io`;
await loginStandard(registry, username, password);
const registryArray = registry.split('\n').map(r => r.trim());
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;
for (const reg of registryArray) {
await loginStandard(reg, username, password);
}
expect(execSpy).toHaveBeenCalledWith(['login', '--password-stdin', '--username', username, registry], {
expect(execSpy).toHaveBeenCalledTimes(2);
const firstcall = execSpy.mock.calls[0];
if (firstcall && firstcall[1]) {
// we don't want to check env opt
firstcall[1].env = undefined;
}
expect(execSpy).toHaveBeenCalledWith(['login', '--password-stdin', '--username', username, registryArray[0]], {
input: Buffer.from(password),
silent: true,
ignoreReturnCode: true
});
const secondcall = execSpy.mock.calls[1];
if (secondcall && secondcall[1]) {
// we don't want to check env opt
secondcall[1].env = undefined;
}
expect(execSpy).toHaveBeenCalledWith(['login', '--password-stdin', '--username', username, registryArray[1]], {
input: Buffer.from(password),
silent: true,
ignoreReturnCode: true
@ -48,17 +65,32 @@ test('logout calls exec', async () => {
};
});
const registry = 'https://ghcr.io';
const registry = `https://ghcr.io
https://docker.io`;
await logout(registry);
const registryArray = registry.split('\n').map(r => r.trim());
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;
for (const reg of registryArray) {
await logout(reg);
}
expect(execSpy).toHaveBeenCalledWith(['logout', registry], {
expect(execSpy).toHaveBeenCalledTimes(2);
const firstcall = execSpy.mock.calls[0];
if (firstcall && firstcall[1]) {
// we don't want to check env opt
firstcall[1].env = undefined;
}
expect(execSpy).toHaveBeenCalledWith(['logout', registryArray[0]], {
ignoreReturnCode: true
});
const secondcall = execSpy.mock.calls[1];
if (secondcall && secondcall[1]) {
// we don't want to check env opt
secondcall[1].env = undefined;
}
expect(execSpy).toHaveBeenCalledWith(['logout', registryArray[1]], {
ignoreReturnCode: true
});
});

View File

@ -1,7 +1,7 @@
import * as core from '@actions/core';
export interface Inputs {
registry: string;
registry: string[];
username: string;
password: string;
ecr: string;
@ -10,7 +10,7 @@ export interface Inputs {
export function getInputs(): Inputs {
return {
registry: core.getInput('registry'),
registry: core.getMultilineInput('registry'),
username: core.getInput('username'),
password: core.getInput('password'),
ecr: core.getInput('ecr'),

View File

@ -4,18 +4,23 @@ import * as context from './context';
import * as docker from './docker';
import * as stateHelper from './state-helper';
const input: context.Inputs = context.getInputs();
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);
for (const reg of input.registry) {
await docker.login(reg, input.username, input.password, input.ecr);
}
}
async function post(): Promise<void> {
if (!stateHelper.logout) {
return;
}
await docker.logout(stateHelper.registry);
for (const reg of input.registry) {
await docker.logout(reg);
}
}
actionsToolkit.run(main, post);

View File

@ -3,8 +3,9 @@ import * as core from '@actions/core';
export const registry = process.env['STATE_registry'] || '';
export const logout = /true/i.test(process.env['STATE_logout'] || '');
export function setRegistry(registry: string) {
core.saveState('registry', registry);
export function setRegistry(registry: string[]) {
core.info(`Setting registry: ${registry.join(',')}`);
core.saveState('registry', registry.join(','));
}
export function setLogout(logout: boolean) {

13249
yarn.lock

File diff suppressed because it is too large Load Diff