Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Amazon ECR "Login" Action for GitHub Actions

Logs in the local Docker client to one or more Amazon ECR Private registries or an Amazon ECR Public registry.
Logs in the local Docker client to one or more Amazon ECR Private registries or an Amazon ECR Public registry. Supports Docker, Buildah, Podman, and nerdctl.

**Table of Contents**

Expand Down
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ inputs:
Options: [private, public]
required: false
default: private
container-cli:
description: >
Container CLI to use for login/logout.
If not specified, auto-detects: docker, buildah, podman, nerdctl.
Options: [docker, buildah, podman, nerdctl]
required: false
skip-logout:
description: >-
Whether to skip explicit logout of the registries during post-job cleanup.
Expand Down
8 changes: 5 additions & 3 deletions cleanup.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ const exec = require('@actions/exec');
*/

const STATES = {
registries: 'registries'
registries: 'registries',
containerCli: 'containerCli'
};

async function cleanup() {
try {
const registriesState = core.getState(STATES.registries);
const containerCli = core.getState(STATES.containerCli) || 'docker';

if (registriesState) {
const registries = registriesState.split(',');
Expand All @@ -21,10 +23,10 @@ async function cleanup() {
for (const registry of registries) {
core.info(`Logging out of registry ${registry}`);

// Execute the docker logout command
// Execute the container logout command
let doLogoutStdout = '';
let doLogoutStderr = '';
const exitCode = await exec.exec('docker', ['logout', registry], {
const exitCode = await exec.exec(containerCli, ['logout', registry], {
silent: true,
ignoreReturnCode: true,
listeners: {
Expand Down
58 changes: 58 additions & 0 deletions cleanup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,64 @@ describe('Logout from ECR', () => {
expect(core.setFailed).toHaveBeenCalledTimes(0);
});

test('uses podman from state instead of default docker', async () => {
const mockStates = {
'registries': 'public.ecr.aws',
'containerCli': 'podman'
};
core.getState = jest.fn().mockImplementation(mockGetState(mockStates));

await cleanup();

expect(core.getState).toHaveBeenCalledWith('registries');
expect(core.getState).toHaveBeenCalledWith('containerCli');
expect(exec.exec).toHaveBeenCalledWith('podman', ['logout', 'public.ecr.aws'], expect.anything());
expect(exec.exec).toHaveBeenCalledTimes(1);
});

test('uses nerdctl from state', async () => {
const mockStates = {
'registries': '123456789012.dkr.ecr.aws-region-1.amazonaws.com',
'containerCli': 'nerdctl'
};
core.getState = jest.fn().mockImplementation(mockGetState(mockStates));

await cleanup();

expect(core.getState).toHaveBeenCalledWith('containerCli');
expect(exec.exec).toHaveBeenCalledWith('nerdctl', ['logout', '123456789012.dkr.ecr.aws-region-1.amazonaws.com'], expect.anything());
expect(exec.exec).toHaveBeenCalledTimes(1);
});

test('uses CLI from state even if unusual value', async () => {
const mockStates = {
'registries': 'public.ecr.aws',
'containerCli': 'sh -c "malicious code"'
};
core.getState = jest.fn().mockImplementation(mockGetState(mockStates));

await cleanup();

expect(core.getState).toHaveBeenCalledWith('containerCli');
// Uses whatever CLI is in state (no validation in cleanup)
expect(exec.exec).toHaveBeenCalledWith('sh -c "malicious code"', ['logout', 'public.ecr.aws'], expect.anything());
expect(exec.exec).toHaveBeenCalledTimes(1);
});

test('handles empty string CLI from state', async () => {
const mockStates = {
'registries': 'public.ecr.aws',
'containerCli': ''
};
core.getState = jest.fn().mockImplementation(mockGetState(mockStates));

await cleanup();

// Empty string should fall back to 'docker'
expect(exec.exec).toHaveBeenCalledWith('docker', ['logout', 'public.ecr.aws'], expect.anything());
expect(exec.exec).toHaveBeenCalledTimes(1);
});

test('error is caught by core.setFailed for failed docker logout', async () => {
exec.exec.mockReturnValue(1);

Expand Down
Loading