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
89 changes: 66 additions & 23 deletions __tests__/docker.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import {expect, test, vi} from 'vitest';
import * as path from 'path';
import {afterEach, beforeEach, expect, test, vi} from 'vitest';

import {Docker} from '@docker/actions-toolkit/lib/docker/docker.js';

import {loginStandard, logout} from '../src/docker.js';

process.env['RUNNER_TEMP'] = path.join(__dirname, 'runner');
beforeEach(() => {
delete process.env.DOCKER_LOGIN_SKIP_IF_MISSING_CREDS;
});

afterEach(() => {
delete process.env.DOCKER_LOGIN_SKIP_IF_MISSING_CREDS;
});

test('loginStandard calls exec', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const execSpy = vi.spyOn(Docker, 'getExecOutput').mockImplementation(async () => {
return {
exitCode: expect.any(Number),
stdout: expect.any(Function),
stderr: expect.any(Function)
};
const execSpy = vi.spyOn(Docker, 'getExecOutput').mockResolvedValue({
exitCode: 0,
stdout: '',
stderr: ''
});

const username = 'dbowie';
const password = 'groundcontrol';
const registry = 'https://ghcr.io';
const registry = 'ghcr.io';

await loginStandard(registry, username, password);

Expand All @@ -30,34 +31,76 @@ test('loginStandard calls exec', async () => {
// 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
});
});

test('loginStandard throws if username and password are missing', () => {
const execSpy = vi.spyOn(Docker, 'getExecOutput');
const login = loginStandard('ghcr.io', '', '');
expect(execSpy).not.toHaveBeenCalled();
return expect(login).rejects.toThrow('Username and password required');
});

test('loginStandard throws if username is missing', () => {
const execSpy = vi.spyOn(Docker, 'getExecOutput');
const login = loginStandard('ghcr.io', '', 'groundcontrol');
expect(execSpy).not.toHaveBeenCalled();
return expect(login).rejects.toThrow('Username required');
});

test('loginStandard throws if password is missing', () => {
const execSpy = vi.spyOn(Docker, 'getExecOutput');
const login = loginStandard('ghcr.io', 'dbowie', '');
expect(execSpy).not.toHaveBeenCalled();
return expect(login).rejects.toThrow('Password required');
});

test('loginStandard skips if both credentials are missing and env opt-in is enabled', () => {
process.env.DOCKER_LOGIN_SKIP_IF_MISSING_CREDS = 'true';
const execSpy = vi.spyOn(Docker, 'getExecOutput');
const login = loginStandard('ghcr.io', '', '');
expect(execSpy).not.toHaveBeenCalled();
return expect(login).resolves.toBeUndefined();
});

test('loginStandard skips if username is missing and env opt-in is enabled', () => {
process.env.DOCKER_LOGIN_SKIP_IF_MISSING_CREDS = 'true';
const execSpy = vi.spyOn(Docker, 'getExecOutput');
const login = loginStandard('ghcr.io', '', 'groundcontrol');
expect(execSpy).not.toHaveBeenCalled();
return expect(login).resolves.toBeUndefined();
});

test('loginStandard skips if password is missing and env opt-in is enabled', () => {
process.env.DOCKER_LOGIN_SKIP_IF_MISSING_CREDS = 'true';
const execSpy = vi.spyOn(Docker, 'getExecOutput');
const login = loginStandard('ghcr.io', 'dbowie', '');
expect(execSpy).not.toHaveBeenCalled();
return expect(login).resolves.toBeUndefined();
});

test('logout calls exec', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const execSpy = vi.spyOn(Docker, 'getExecOutput').mockImplementation(async () => {
return {
exitCode: expect.any(Number),
stdout: expect.any(Function),
stderr: expect.any(Function)
};
const execSpy = vi.spyOn(Docker, 'getExecOutput').mockResolvedValue({
exitCode: 0,
stdout: '',
stderr: ''
});

const registry = 'https://ghcr.io';

const registry = 'ghcr.io';
await 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
});
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/docker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as core from '@actions/core';

import {Docker} from '@docker/actions-toolkit/lib/docker/docker.js';
import {Util} from '@docker/actions-toolkit/lib/util.js';

import * as aws from './aws.js';
import * as context from './context.js';
Expand Down Expand Up @@ -34,6 +35,10 @@ export async function logout(registry: string, configDir: string): Promise<void>
}

export async function loginStandard(registry: string, username: string, password: string, scope?: string): Promise<void> {
if ((!username || !password) && skipLoginIfMissingCredsEnabled()) {
core.info(`Skipping login to ${registry}. Username or password is not set and DOCKER_LOGIN_SKIP_IF_MISSING_CREDS is enabled.`);
return;
}
if (!username && !password) {
throw new Error('Username and password required');
}
Expand Down Expand Up @@ -79,3 +84,10 @@ async function loginExec(registry: string, username: string, password: string, s
core.info('Login Succeeded!');
});
}

function skipLoginIfMissingCredsEnabled(): boolean {
if (process.env.DOCKER_LOGIN_SKIP_IF_MISSING_CREDS) {
return Util.parseBool(process.env.DOCKER_LOGIN_SKIP_IF_MISSING_CREDS);
}
return false;
}
Loading