Update and drop unnecessary deps (#662)

This commit is contained in:
Seth Vargo 2023-12-08 16:37:40 -05:00 committed by GitHub
parent 7c7fdb013a
commit c2599dbc0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 384 additions and 1050 deletions

View file

@ -24,4 +24,8 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
},
};

View file

@ -53,4 +53,4 @@ branding:
runs:
using: 'node20'
main: 'dist/main/index.js'
main: 'dist/index.js'

5
dist/index.js vendored Normal file

File diff suppressed because one or more lines are too long

5
dist/main/index.js vendored

File diff suppressed because one or more lines are too long

972
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,11 +4,11 @@
"description": "Setup gcloud GitHub action",
"main": "dist/main/index.js",
"scripts": {
"build": "ncc build -m src/main.ts -o dist/main",
"build": "ncc build -m src/main.ts",
"format": "prettier --write **/*.ts",
"integration": "mocha -r ts-node/register -t 180s 'tests/integration/*.test.ts'",
"integration": "node --require ts-node/register --test-reporter spec --test tests/integration.test.ts",
"lint": "eslint . --ext .ts,.tsx",
"test": "mocha -r ts-node/register -t 180s 'tests/*.test.ts'"
"test": "node --require ts-node/register --test-reporter spec --test tests/setup-gcloud.test.ts"
},
"repository": {
"type": "git",
@ -27,25 +27,19 @@
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/tool-cache": "^2.0.1",
"@google-github-actions/actions-utils": "^0.4.9",
"@google-github-actions/actions-utils": "^0.4.10",
"@google-github-actions/setup-cloud-sdk": "^1.1.3"
},
"devDependencies": {
"@types/chai": "^4.3.10",
"@types/mocha": "^10.0.4",
"@types/node": "^20.9.0",
"@types/sinon": "^17.0.1",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"@types/node": "^20.10.4",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"@vercel/ncc": "^0.38.1",
"chai": "^4.3.10",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.1",
"mocha": "^10.2.0",
"prettier": "^3.0.3",
"sinon": "^17.0.1",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
"eslint": "^8.55.0",
"prettier": "^3.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
}

106
tests/integration.test.ts Normal file
View file

@ -0,0 +1,106 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, it } from 'node:test';
import assert from 'node:assert';
import { getExecOutput, ExecOptions } from '@actions/exec';
const skipIfMissingEnvs = (...keys: string[]): { skip: string } | undefined => {
const missingKeys: string[] = [];
for (const key of keys) {
if (!(key in process.env)) {
missingKeys.push(key);
}
}
if (missingKeys.length > 0) {
return { skip: `missing $${missingKeys.join(', $')}` };
}
return undefined;
};
describe(
'#run',
skipIfMissingEnvs('TEST_ACCOUNT', 'TEST_PROJECT_ID', 'TEST_COMPONENTS'),
async () => {
const testAccount = process.env.TEST_ACCOUNT!;
const testProjectID = process.env.TEST_PROJECT_ID!;
const testComponents = process.env.TEST_COMPONENTS!;
it('has the correct account', async () => {
const raw = await gcloudRun([
'--quiet',
'auth',
'list',
'--filter',
'status:ACTIVE',
'--format',
'json',
]);
const result = JSON.parse(raw)[0]?.['account'] || '(unset)';
assert.deepStrictEqual(result, testAccount);
});
it('has the correct project_id', async () => {
const raw = await gcloudRun([
'--quiet',
'config',
'list',
'core/project',
'--format',
'json',
]);
const result = JSON.parse(raw)['core']?.['project'] || '(unset)';
assert.deepStrictEqual(result, testProjectID);
});
it('includes the given components', async () => {
const raw = await gcloudRun([
'--quiet',
'components',
'list',
'--only-local-state',
'--format',
'json',
]);
const result = JSON.parse(raw).map((entry: Record<string, any>) => entry['id']);
const members = testComponents.split(',').map((component) => component.trim());
const intersection = members.filter((v) => result.includes(v));
assert.deepStrictEqual(intersection, members);
});
},
);
async function gcloudRun(cmd: string[], options?: ExecOptions): Promise<string> {
// A workaround for https://github.com/actions/toolkit/issues/229
let toolCommand = 'gcloud';
if (process.platform == 'win32') {
toolCommand = 'gcloud.cmd';
}
const opts = Object.assign({}, { silent: true, ignoreReturnCode: true }, options);
const commandString = `${toolCommand} ${cmd.join(' ')}`;
const result = await getExecOutput(toolCommand, cmd, opts);
if (result.exitCode !== 0) {
const errMsg = result.stderr || `command exited ${result.exitCode}, but stderr had no output`;
throw new Error(`failed to execute command \`${commandString}\`: ${errMsg}`);
}
return result.stdout;
}

View file

@ -1,84 +0,0 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'mocha';
import { expect } from 'chai';
import { getExecOutput, ExecOptions } from '@actions/exec';
const { TEST_ACCOUNT, TEST_PROJECT_ID, TEST_COMPONENTS } = process.env;
describe('#run', function () {
it('has the correct account', async function () {
if (!TEST_ACCOUNT) this.skip();
const raw = await gcloudRun([
'--quiet',
'auth',
'list',
'--filter',
'status:ACTIVE',
'--format',
'json',
]);
const parsed = JSON.parse(raw)[0]?.['account'] || '(unset)';
expect(parsed).to.eql(TEST_ACCOUNT.trim());
});
it('has the correct project_id', async function () {
if (!TEST_PROJECT_ID) this.skip();
const raw = await gcloudRun(['--quiet', 'config', 'list', 'core/project', '--format', 'json']);
const parsed = JSON.parse(raw)['core']?.['project'] || '(unset)';
expect(parsed.trim()).to.eql(TEST_PROJECT_ID.trim());
});
it('includes the given components', async function () {
if (!TEST_COMPONENTS) this.skip();
const raw = await gcloudRun([
'--quiet',
'components',
'list',
'--only-local-state',
'--format',
'json',
]);
const parsed = JSON.parse(raw).map((entry: Record<string, any>) => entry['id']); // eslint-disable-line @typescript-eslint/no-explicit-any
const members = TEST_COMPONENTS.split(',').map((component) => component.trim());
expect(parsed).to.include.all.members(members);
});
});
async function gcloudRun(cmd: string[], options?: ExecOptions): Promise<string> {
// A workaround for https://github.com/actions/toolkit/issues/229
let toolCommand = 'gcloud';
if (process.platform == 'win32') {
toolCommand = 'gcloud.cmd';
}
const opts = Object.assign({}, { silent: true, ignoreReturnCode: true }, options);
const commandString = `${toolCommand} ${cmd.join(' ')}`;
const result = await getExecOutput(toolCommand, cmd, opts);
if (result.exitCode !== 0) {
const errMsg = result.stderr || `command exited ${result.exitCode}, but stderr had no output`;
throw new Error(`failed to execute command \`${commandString}\`: ${errMsg}`);
}
return result.stdout;
}

View file

@ -14,12 +14,8 @@
* limitations under the License.
*/
/*
* Tests setup-gcloud.
*/
import 'mocha';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { afterEach, beforeEach, describe, mock, it } from 'node:test';
import assert from 'node:assert';
import { promises as fs } from 'fs';
import * as setupGcloud from '@google-github-actions/setup-cloud-sdk';
@ -36,140 +32,199 @@ const fakeInputs: { [key: string]: string } = {
project_id: 'test',
};
function getInputMock(name: string): string {
return fakeInputs[name];
}
const defaultMocks = (
m: typeof mock,
overrideInputs?: Record<string, string>,
): Record<string, any> => {
const inputs = Object.assign({}, fakeInputs, overrideInputs);
return {
startGroup: m.method(core, 'startGroup', () => {}),
endGroup: m.method(core, 'endGroup', () => {}),
group: m.method(core, 'group', () => {}),
logDebug: m.method(core, 'debug', () => {}),
logError: m.method(core, 'error', () => {}),
logInfo: m.method(core, 'info', () => {}),
logNotice: m.method(core, 'notice', () => {}),
logWarning: m.method(core, 'warning', () => {}),
exportVariable: m.method(core, 'exportVariable', () => {}),
setSecret: m.method(core, 'setSecret', () => {}),
addPath: m.method(core, 'addPath', () => {}),
setOutput: m.method(core, 'setOutput', () => {}),
setFailed: m.method(core, 'setFailed', (msg: string) => {
throw new Error(msg);
}),
getBooleanInput: m.method(core, 'getBooleanInput', (name: string) => {
return !!inputs[name];
}),
getMultilineInput: m.method(core, 'getMultilineInput', (name: string) => {
return inputs[name];
}),
getInput: m.method(core, 'getInput', (name: string) => {
return inputs[name];
}),
describe('#run', function () {
beforeEach(async function () {
this.stubs = {
getInput: sinon.stub(core, 'getInput').callsFake(getInputMock),
getBooleanInput: sinon.stub(core, 'getBooleanInput').returns(false),
exportVariable: sinon.stub(core, 'exportVariable'),
authenticateGcloudSDK: sinon.stub(setupGcloud, 'authenticateGcloudSDK'),
installGcloudSDK: sinon.stub(setupGcloud, 'installGcloudSDK'),
setProject: sinon.stub(setupGcloud, 'setProject'),
installComponent: sinon.stub(setupGcloud, 'installComponent'),
writeFile: sinon.stub(fs, 'writeFile'),
};
authenticateGcloudSDK: m.method(setupGcloud, 'authenticateGcloudSDK', () => {}),
isInstalled: m.method(setupGcloud, 'isInstalled', () => {
return true;
}),
installGcloudSDK: m.method(setupGcloud, 'installGcloudSDK', async () => {
return '1.2.3';
}),
installComponent: m.method(setupGcloud, 'installComponent', () => {}),
setProject: m.method(setupGcloud, 'setProject', () => {}),
getLatestGcloudSDKVersion: m.method(setupGcloud, 'getLatestGcloudSDKVersion', () => {
return '1.2.3';
}),
process.env.GITHUB_PATH = '/';
sinon.stub(core, 'setFailed').throwsArg(0); // make setFailed throw exceptions
sinon.stub(core, 'addPath').callsFake(sinon.fake());
sinon.stub(core, 'debug').callsFake(sinon.fake());
sinon.stub(core, 'endGroup').callsFake(sinon.fake());
sinon.stub(core, 'info').callsFake(sinon.fake());
sinon.stub(core, 'startGroup').callsFake(sinon.fake());
sinon.stub(core, 'warning').callsFake(sinon.fake());
writeFile: m.method(fs, 'writeFile', () => {}),
};
};
describe('#run', async () => {
beforeEach(async () => {
await TestToolCache.start();
// process.env.GITHUB_PATH = '/';
});
afterEach(async function () {
Object.keys(this.stubs).forEach((k) => this.stubs[k].restore());
sinon.restore();
afterEach(async () => {
clearEnv((key: string) => key.startsWith(`GITHUB_`));
await TestToolCache.stop();
});
describe('download', () => {
it('downloads when no version is provided', async function () {
this.stubs.getInput.withArgs('version').returns('');
describe('download', async () => {
it('downloads when no version is provided', async (t) => {
const mocks = defaultMocks(t.mock, {
version: '',
});
await run();
const call = this.stubs.installGcloudSDK.firstCall;
expect(call.firstArg).to.match(/\d+\.\d+\.\d+/);
assert.match(mocks.installGcloudSDK.mock.calls?.at(0)?.arguments?.at(0), /\d+\.\d+\.\d+/);
});
it('downloads when version is "latest"', async function () {
this.stubs.getInput.withArgs('version').returns('latest');
it('downloads when version is "latest"', async (t) => {
const mocks = defaultMocks(t.mock, {
version: '',
});
await run();
const call = this.stubs.installGcloudSDK.firstCall;
expect(call.firstArg).to.match(/\d+\.\d+\.\d+/);
assert.match(mocks.installGcloudSDK.mock.calls?.at(0)?.arguments?.at(0), /\d+\.\d+\.\d+/);
});
it('downloads when version is not installed', async function () {
this.stubs.getInput.withArgs('version').returns('5.6.7');
it('downloads when version is not installed', async (t) => {
const mocks = defaultMocks(t.mock, {
version: '5.6.7',
});
await run();
const call = this.stubs.installGcloudSDK.firstCall;
expect(call.firstArg).to.eql('5.6.7');
assert.deepStrictEqual(mocks.installGcloudSDK.mock.calls?.at(0)?.arguments?.at(0), '5.6.7');
});
it('downloads when version constraint is not satisfied', async function () {
this.stubs.getInput.withArgs('version').returns('>= 10.0.0');
it('downloads when version constraint is not satisfied', async (t) => {
const mocks = defaultMocks(t.mock, {
version: '>= 10.0.0',
});
await run();
const call = this.stubs.installGcloudSDK.firstCall;
expect(call.firstArg).to.eql('>= 10.0.0');
assert.deepStrictEqual(
mocks.installGcloudSDK.mock.calls?.at(0)?.arguments?.at(0),
'>= 10.0.0',
);
});
it('downloads when a version is installed but the version constraint is not satisfied', async function () {
this.stubs.getInput.withArgs('version').returns('>= 10.0.0');
it('downloads when a version is installed but the version constraint is not satisfied', async (t) => {
const mocks = defaultMocks(t.mock, {
version: '>= 10.0.0',
});
await installFakeGcloud('9.8.7');
await run();
const call = this.stubs.installGcloudSDK.firstCall;
expect(call.firstArg).to.eql('>= 10.0.0');
assert.deepStrictEqual(
mocks.installGcloudSDK.mock.calls?.at(0)?.arguments?.at(0),
'>= 10.0.0',
);
});
it('does not download when version is installed', async function () {
this.stubs.getInput.withArgs('version').returns('1.2.3');
it('does not download when version is installed', async (t) => {
const mocks = defaultMocks(t.mock, {
version: '1.2.3',
});
await installFakeGcloud('1.2.3');
await run();
expect(this.stubs.installGcloudSDK.callCount).to.eq(0);
assert.deepStrictEqual(mocks.installGcloudSDK.mock.callCount(), 0);
});
it('does not download when a version constraint is satisfied', async function () {
this.stubs.getInput.withArgs('version').returns('>= 0.0.0');
it('does not download when a version constraint is satisfied', async (t) => {
const mocks = defaultMocks(t.mock, {
version: '>= 1.0.0',
});
await installFakeGcloud('1.2.3');
await run();
expect(this.stubs.installGcloudSDK.callCount).to.eq(0);
assert.deepStrictEqual(mocks.installGcloudSDK.mock.callCount(), 0);
});
});
describe('component installation', () => {
it('installs 1 additional component', async function () {
this.stubs.getInput.withArgs('install_components').returns('beta');
describe('component installation', async () => {
it('installs 1 additional component', async (t) => {
const mocks = defaultMocks(t.mock, {
install_components: 'beta',
});
await run();
const call = this.stubs.installComponent.firstCall;
expect(call.firstArg).to.eql(['beta']);
assert.deepStrictEqual(mocks.installComponent.mock.calls?.at(0)?.arguments?.at(0), ['beta']);
});
it('installs additional components', async function () {
this.stubs.getInput.withArgs('install_components').returns('beta, alpha');
it('installs additional components', async (t) => {
const mocks = defaultMocks(t.mock, {
install_components: 'alpha, beta',
});
await run();
const call = this.stubs.installComponent.firstCall;
expect(call.firstArg).to.eql(['beta', 'alpha']);
assert.deepStrictEqual(mocks.installComponent.mock.calls?.at(0)?.arguments?.at(0), [
'alpha',
'beta',
]);
});
});
describe('authentication', () => {
it('authenticates if GOOGLE_GHA_CREDS is set', async function () {
describe('authentication', async () => {
const originalEnv = Object.assign({}, process.env);
afterEach(async () => {
process.env = originalEnv;
});
it('authenticates if GOOGLE_GHA_CREDS is set', async (t) => {
const mocks = defaultMocks(t.mock, {
install_components: 'alpha, beta',
});
process.env.GOOGLE_GHA_CREDS_PATH = '/foo/bar/path.json';
await run();
expect(this.stubs.authenticateGcloudSDK.callCount).to.eq(1);
assert.deepStrictEqual(mocks.authenticateGcloudSDK.mock.callCount(), 1);
});
});
describe('configuration', () => {
it('sets the project ID if provided', async function () {
this.stubs.getInput.withArgs('project_id').returns('test');
describe('configuration', async () => {
it('sets the project ID if provided', async (t) => {
const mocks = defaultMocks(t.mock, {
project_id: 'test',
});
await run();
expect(this.stubs.setProject.withArgs('test').callCount).to.eq(1);
assert.deepStrictEqual(mocks.setProject.mock.calls?.at(0)?.arguments?.at(0), 'test');
});
it('does not set the project ID if not provided', async function () {
this.stubs.getInput.withArgs('project_id').returns('');
it('does not set the project ID if not provided', async (t) => {
const mocks = defaultMocks(t.mock, {
project_id: '',
});
await run();
expect(this.stubs.setProject.callCount).to.eq(0);
assert.deepStrictEqual(mocks.setProject.mock.callCount(), 0);
});
});
});
@ -178,6 +233,6 @@ describe('#run', function () {
* installFakeGcloud puts a fake gcloud version into the temporary toolcache.
* It's not actually gcloud and not actually executable.
*/
async function installFakeGcloud(version: string): Promise<string> {
const installFakeGcloud = async (version: string): Promise<string> => {
return await toolCache.cacheFile('action.yml', 'action.yml', 'gcloud', version);
}
};

View file

@ -15,6 +15,7 @@
*/
{
"compilerOptions": {
"alwaysStrict": true,
"target": "es6",
"module": "commonjs",
"lib": [
@ -26,5 +27,5 @@
"noImplicitAny": true,
"esModuleInterop": true
},
"exclude": ["node_modules", "**/*.test.ts"]
"exclude": ["node_modules/", "tests/"]
}