Compare commits

...

6 Commits

Author SHA1 Message Date
Tõnis Tiigi
adb73476b6 Merge pull request #21 from crazy-max/handle-aws-env
Handle AWS credentials
2020-10-20 09:14:56 -07:00
CrazyMax
5df5104555 Handle AWS credentials
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-10-20 14:42:02 +02:00
Tõnis Tiigi
39ef12fb7a Merge pull request #18 from crazy-max/username-required
Username required
2020-10-16 15:54:07 -07:00
CrazyMax
1c402b7c97 Fix tests
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-10-16 18:34:48 +02:00
CrazyMax
1c2cf9942d Username required
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-10-16 18:24:41 +02:00
Bryan Clark
4b15841c41 Mostly tests and some small changes (#16)
* Create docker.test.ts
* Add context tests
* test main
2020-10-09 10:30:45 +00:00
11 changed files with 233 additions and 43 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -117,3 +117,34 @@ jobs:
# if: always() # if: always()
# run: | # run: |
# rm -f ${HOME}/.docker/config.json # rm -f ${HOME}/.docker/config.json
#
# ecr-aws-creds:
# runs-on: ${{ matrix.os }}
# strategy:
# fail-fast: false
# matrix:
# os:
# - ubuntu-20.04
# - ubuntu-18.04
# - ubuntu-16.04
# steps:
# -
# name: Checkout
# uses: actions/checkout@v2.3.1
# -
# name: Configure AWS Credentials
# uses: aws-actions/configure-aws-credentials@v1
# with:
# aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
# aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# aws-region: ${{ secrets.AWS_REGION }}
# -
# name: Login to ECR
# uses: ./
# with:
# registry: ${{ secrets.AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com
# -
# name: Clear
# if: always()
# run: |
# rm -f ${HOME}/.docker/config.json

View File

@@ -213,6 +213,34 @@ jobs:
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }} password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
``` ```
You can also use the [Configure AWS Credentials](https://github.com/aws-actions/configure-aws-credentials) action in
combination with this action:
```yaml
name: ci
on:
push:
branches: master
jobs:
login:
runs-on: ubuntu-latest
steps:
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: <region>
-
name: Login to ECR
uses: docker/login-action@v1
with:
registry: <aws-account-number>.dkr.ecr.<region>.amazonaws.com
```
> Replace `<aws-account-number>` and `<region>` with their respective values. > Replace `<aws-account-number>` and `<region>` with their respective values.
## Customizing ## Customizing

11
__tests__/context.test.ts Normal file
View File

@@ -0,0 +1,11 @@
import osm = require('os');
import {getInputs} from '../src/context';
test('with password and username getInputs does not throw error', async () => {
process.env['INPUT_USERNAME'] = 'dbowie';
process.env['INPUT_PASSWORD'] = 'groundcontrol';
expect(() => {
getInputs();
}).not.toThrowError();
});

49
__tests__/docker.test.ts Normal file
View File

@@ -0,0 +1,49 @@
import {loginStandard, logout} from '../src/docker';
import * as path from 'path';
import * as exec from '@actions/exec';
process.env['RUNNER_TEMP'] = path.join(__dirname, 'runner');
test('loginStandard calls exec', async () => {
const execSpy: jest.SpyInstance = jest.spyOn(exec, 'exec');
// don't let exec try to actually run the commands
execSpy.mockImplementation(() => {});
const username: string = 'dbowie';
const password: string = 'groundcontrol';
const registry: string = 'https://ghcr.io';
await loginStandard(registry, username, password);
expect(execSpy).toHaveBeenCalledWith(`docker`, ['login', '--password-stdin', '--username', username, registry], {
input: Buffer.from(password),
silent: true,
ignoreReturnCode: true,
listeners: expect.objectContaining({
stdout: expect.any(Function),
stderr: expect.any(Function)
})
});
});
test('logout calls exec', async () => {
const execSpy: jest.SpyInstance = jest.spyOn(exec, 'exec');
// don't let exec try to actually run the commands
execSpy.mockImplementation(() => {});
const registry: string = 'https://ghcr.io';
await logout(registry);
expect(execSpy).toHaveBeenCalledWith(`docker`, ['logout', registry], {
silent: false,
ignoreReturnCode: true,
input: Buffer.from(''),
listeners: expect.objectContaining({
stdout: expect.any(Function),
stderr: expect.any(Function)
})
});
});

79
__tests__/main.test.ts Normal file
View File

@@ -0,0 +1,79 @@
import osm = require('os');
import {run} from '../src/main';
import * as docker from '../src/docker';
import * as stateHelper from '../src/state-helper';
import * as core from '@actions/core';
test('errors when not run on linux platform', async () => {
const platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => 'netbsd');
const coreSpy: jest.SpyInstance = jest.spyOn(core, 'setFailed');
await run();
expect(coreSpy).toHaveBeenCalledWith('Only supported on linux platform');
});
test('errors without username and password', async () => {
const platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => 'linux');
const coreSpy: jest.SpyInstance = jest.spyOn(core, 'setFailed');
await run();
expect(coreSpy).toHaveBeenCalledWith('Username and password required');
});
test('successful with username and password', async () => {
const platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => 'linux');
const setRegistrySpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setRegistry');
const setLogoutSpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setLogout');
const dockerSpy: jest.SpyInstance = jest.spyOn(docker, 'login');
dockerSpy.mockImplementation(() => {});
const username: string = 'dbowie';
process.env[`INPUT_USERNAME`] = username;
const password: string = 'groundcontrol';
process.env[`INPUT_PASSWORD`] = password;
await run();
expect(setRegistrySpy).toHaveBeenCalledWith('');
expect(setLogoutSpy).toHaveBeenCalledWith('');
expect(dockerSpy).toHaveBeenCalledWith('', username, password);
});
test('calls docker login', async () => {
const platSpy = jest.spyOn(osm, 'platform');
platSpy.mockImplementation(() => 'linux');
const setRegistrySpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setRegistry');
const setLogoutSpy: jest.SpyInstance = jest.spyOn(stateHelper, 'setLogout');
const dockerSpy: jest.SpyInstance = jest.spyOn(docker, 'login');
dockerSpy.mockImplementation(() => {});
const username: string = 'dbowie';
process.env[`INPUT_USERNAME`] = username;
const password: string = 'groundcontrol';
process.env[`INPUT_PASSWORD`] = password;
const registry: string = 'ghcr.io';
process.env[`INPUT_REGISTRY`] = registry;
const logout: string = 'true';
process.env['INPUT_LOGOUT'] = logout;
await run();
expect(setRegistrySpy).toHaveBeenCalledWith(registry);
expect(setLogoutSpy).toHaveBeenCalledWith(logout);
expect(dockerSpy).toHaveBeenCalledWith(registry, username, password);
});

View File

@@ -15,7 +15,7 @@ inputs:
required: false required: false
password: password:
description: 'Password or personal access token used to log against the Docker registry' description: 'Password or personal access token used to log against the Docker registry'
required: true 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'

47
dist/index.js generated vendored
View File

@@ -496,6 +496,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.run = void 0;
const os = __importStar(__webpack_require__(87)); const os = __importStar(__webpack_require__(87));
const core = __importStar(__webpack_require__(186)); const core = __importStar(__webpack_require__(186));
const context_1 = __webpack_require__(842); const context_1 = __webpack_require__(842);
@@ -505,19 +506,19 @@ function run() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
if (os.platform() !== 'linux') { if (os.platform() !== 'linux') {
core.setFailed('Only supported on linux platform'); throw new Error('Only supported on linux platform');
return;
} }
let inputs = yield context_1.getInputs(); const { registry, username, password, logout } = context_1.getInputs();
stateHelper.setRegistry(inputs.registry); stateHelper.setRegistry(registry);
stateHelper.setLogout(inputs.logout); stateHelper.setLogout(logout);
yield docker.login(inputs.registry, inputs.username, inputs.password); yield docker.login(registry, username, password);
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
} }
}); });
} }
exports.run = run;
function logout() { function logout() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
if (!stateHelper.logout) { if (!stateHelper.logout) {
@@ -3061,10 +3062,11 @@ function logout(registry) {
exports.logout = logout; exports.logout = logout;
function loginStandard(registry, username, password) { function loginStandard(registry, username, password) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let loginArgs = ['login', '--password-stdin']; if (!username || !password) {
if (username) { throw new Error('Username and password required');
loginArgs.push('--username', username);
} }
let loginArgs = ['login', '--password-stdin'];
loginArgs.push('--username', username);
loginArgs.push(registry); loginArgs.push(registry);
if (registry) { if (registry) {
core.info(`🔑 Logging into ${registry}...`); core.info(`🔑 Logging into ${registry}...`);
@@ -3087,8 +3089,8 @@ function loginECR(registry, username, password) {
const cliVersion = yield aws.getCLIVersion(); const cliVersion = yield aws.getCLIVersion();
const region = yield aws.getRegion(registry); const region = yield aws.getRegion(registry);
core.info(`💡 AWS ECR detected with ${region} region`); core.info(`💡 AWS ECR detected with ${region} region`);
process.env.AWS_ACCESS_KEY_ID = username; process.env.AWS_ACCESS_KEY_ID = username || process.env.AWS_ACCESS_KEY_ID;
process.env.AWS_SECRET_ACCESS_KEY = password; process.env.AWS_SECRET_ACCESS_KEY = password || process.env.AWS_SECRET_ACCESS_KEY;
core.info(`⬇️ Retrieving docker login command through AWS CLI ${cliVersion} (${cliPath})...`); core.info(`⬇️ Retrieving docker login command through AWS CLI ${cliVersion} (${cliPath})...`);
const loginCmd = yield aws.getDockerLoginCmd(cliVersion, registry, region); const loginCmd = yield aws.getDockerLoginCmd(cliVersion, registry, region);
core.info(`🔑 Logging into ${registry}...`); core.info(`🔑 Logging into ${registry}...`);
@@ -3640,27 +3642,16 @@ var __importStar = (this && this.__importStar) || function (mod) {
__setModuleDefault(result, mod); __setModuleDefault(result, mod);
return result; return result;
}; };
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getInputs = void 0; exports.getInputs = void 0;
const core = __importStar(__webpack_require__(186)); const core = __importStar(__webpack_require__(186));
function getInputs() { function getInputs() {
return __awaiter(this, void 0, void 0, function* () { return {
return { registry: core.getInput('registry'),
registry: core.getInput('registry'), username: core.getInput('username'),
username: core.getInput('username'), password: core.getInput('password'),
password: core.getInput('password', { required: true }), logout: core.getInput('logout')
logout: core.getInput('logout') };
};
});
} }
exports.getInputs = getInputs; exports.getInputs = getInputs;
//# sourceMappingURL=context.js.map //# sourceMappingURL=context.js.map

View File

@@ -7,11 +7,11 @@ export interface Inputs {
logout: string; logout: string;
} }
export async function getInputs(): Promise<Inputs> { export function getInputs(): Inputs {
return { return {
registry: core.getInput('registry'), registry: core.getInput('registry'),
username: core.getInput('username'), username: core.getInput('username'),
password: core.getInput('password', {required: true}), password: core.getInput('password'),
logout: core.getInput('logout') logout: core.getInput('logout')
}; };
} }

View File

@@ -19,10 +19,12 @@ 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): Promise<void> {
let loginArgs: Array<string> = ['login', '--password-stdin']; if (!username || !password) {
if (username) { throw new Error('Username and password required');
loginArgs.push('--username', username);
} }
let loginArgs: Array<string> = ['login', '--password-stdin'];
loginArgs.push('--username', username);
loginArgs.push(registry); loginArgs.push(registry);
if (registry) { if (registry) {
@@ -44,8 +46,8 @@ export async function loginECR(registry: string, username: string, password: str
const region = await aws.getRegion(registry); const region = await aws.getRegion(registry);
core.info(`💡 AWS ECR detected with ${region} region`); core.info(`💡 AWS ECR detected with ${region} region`);
process.env.AWS_ACCESS_KEY_ID = username; process.env.AWS_ACCESS_KEY_ID = username || process.env.AWS_ACCESS_KEY_ID;
process.env.AWS_SECRET_ACCESS_KEY = password; process.env.AWS_SECRET_ACCESS_KEY = password || process.env.AWS_SECRET_ACCESS_KEY;
core.info(`⬇️ Retrieving docker login command through AWS CLI ${cliVersion} (${cliPath})...`); core.info(`⬇️ Retrieving docker login command through AWS CLI ${cliVersion} (${cliPath})...`);
const loginCmd = await aws.getDockerLoginCmd(cliVersion, registry, region); const loginCmd = await aws.getDockerLoginCmd(cliVersion, registry, region);

View File

@@ -4,17 +4,16 @@ import {getInputs, Inputs} from './context';
import * as docker from './docker'; import * as docker from './docker';
import * as stateHelper from './state-helper'; import * as stateHelper from './state-helper';
async function run(): Promise<void> { export async function run(): Promise<void> {
try { try {
if (os.platform() !== 'linux') { if (os.platform() !== 'linux') {
core.setFailed('Only supported on linux platform'); throw new Error('Only supported on linux platform');
return;
} }
let inputs: Inputs = await getInputs(); const {registry, username, password, logout} = getInputs();
stateHelper.setRegistry(inputs.registry); stateHelper.setRegistry(registry);
stateHelper.setLogout(inputs.logout); stateHelper.setLogout(logout);
await docker.login(inputs.registry, inputs.username, inputs.password); await docker.login(registry, username, password);
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
} }