Add initial eslint setup (#88)

pull/89/head
BSKY 2019-11-13 06:48:02 +09:00 committed by Josh Gross
parent 31508256ff
commit fb50aa45ec
12 changed files with 1044 additions and 91 deletions

16
.eslintrc.json Normal file
View File

@ -0,0 +1,16 @@
{
"env": { "node": true, "jest": true },
"parser": "@typescript-eslint/parser",
"parserOptions": { "ecmaVersion": 2020, "sourceType": "module" },
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"plugin:prettier/recommended",
"prettier/@typescript-eslint"
],
"plugins": ["@typescript-eslint", "jest"]
}

View File

@ -1,4 +1,5 @@
name: Tests name: Tests
on: on:
pull_request: pull_request:
branches: branches:
@ -14,9 +15,11 @@ on:
jobs: jobs:
test: test:
name: Test on ${{ matrix.os }} name: Test on ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
@ -29,7 +32,12 @@ jobs:
- run: npm ci - run: npm ci
- name: Prettier Format Check - name: Prettier Format Check
if: matrix.os == 'ubuntu-latest'
run: npm run format-check run: npm run format-check
- name: ESLint Check
if: matrix.os == 'ubuntu-latest'
run: npm run lint
- name: Build & Test - name: Build & Test
run: npm run test run: npm run test

View File

@ -1,9 +1,7 @@
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as exec from "@actions/exec"; import * as exec from "@actions/exec";
import * as io from "@actions/io"; import * as io from "@actions/io";
import * as path from "path"; import * as path from "path";
import * as cacheHttpClient from "../src/cacheHttpClient"; import * as cacheHttpClient from "../src/cacheHttpClient";
import { Inputs } from "../src/constants"; import { Inputs } from "../src/constants";
import { ArtifactCacheEntry } from "../src/contracts"; import { ArtifactCacheEntry } from "../src/contracts";
@ -107,7 +105,7 @@ test("restore with no cache found", async () => {
const stateMock = jest.spyOn(core, "saveState"); const stateMock = jest.spyOn(core, "saveState");
const clientMock = jest.spyOn(cacheHttpClient, "getCacheEntry"); const clientMock = jest.spyOn(cacheHttpClient, "getCacheEntry");
clientMock.mockImplementation(_ => { clientMock.mockImplementation(() => {
return Promise.resolve(null); return Promise.resolve(null);
}); });
@ -134,7 +132,7 @@ test("restore with server error should fail", async () => {
const stateMock = jest.spyOn(core, "saveState"); const stateMock = jest.spyOn(core, "saveState");
const clientMock = jest.spyOn(cacheHttpClient, "getCacheEntry"); const clientMock = jest.spyOn(cacheHttpClient, "getCacheEntry");
clientMock.mockImplementation(_ => { clientMock.mockImplementation(() => {
throw new Error("HTTP Error Occurred"); throw new Error("HTTP Error Occurred");
}); });
@ -168,7 +166,7 @@ test("restore with restore keys and no cache found", async () => {
const stateMock = jest.spyOn(core, "saveState"); const stateMock = jest.spyOn(core, "saveState");
const clientMock = jest.spyOn(cacheHttpClient, "getCacheEntry"); const clientMock = jest.spyOn(cacheHttpClient, "getCacheEntry");
clientMock.mockImplementation(_ => { clientMock.mockImplementation(() => {
return Promise.resolve(null); return Promise.resolve(null);
}); });
@ -202,7 +200,7 @@ test("restore with cache found", async () => {
archiveLocation: "www.actionscache.test/download" archiveLocation: "www.actionscache.test/download"
}; };
const getCacheMock = jest.spyOn(cacheHttpClient, "getCacheEntry"); const getCacheMock = jest.spyOn(cacheHttpClient, "getCacheEntry");
getCacheMock.mockImplementation(_ => { getCacheMock.mockImplementation(() => {
return Promise.resolve(cacheEntry); return Promise.resolve(cacheEntry);
}); });
const tempPath = "/foo/bar"; const tempPath = "/foo/bar";
@ -278,7 +276,7 @@ test("restore with cache found for restore key", async () => {
archiveLocation: "www.actionscache.test/download" archiveLocation: "www.actionscache.test/download"
}; };
const getCacheMock = jest.spyOn(cacheHttpClient, "getCacheEntry"); const getCacheMock = jest.spyOn(cacheHttpClient, "getCacheEntry");
getCacheMock.mockImplementation(_ => { getCacheMock.mockImplementation(() => {
return Promise.resolve(cacheEntry); return Promise.resolve(cacheEntry);
}); });
const tempPath = "/foo/bar"; const tempPath = "/foo/bar";

View File

@ -1,22 +1,23 @@
require('nock').disableNetConnect(); require("nock").disableNetConnect();
module.exports = { module.exports = {
clearMocks: true, clearMocks: true,
moduleFileExtensions: ['js', 'ts'], moduleFileExtensions: ["js", "ts"],
testEnvironment: 'node', testEnvironment: "node",
testMatch: ['**/*.test.ts'], testMatch: ["**/*.test.ts"],
testRunner: 'jest-circus/runner', testRunner: "jest-circus/runner",
transform: { transform: {
'^.+\\.ts$': 'ts-jest' "^.+\\.ts$": "ts-jest"
}, },
verbose: true verbose: true
} };
const processStdoutWrite = process.stdout.write.bind(process.stdout) const processStdoutWrite = process.stdout.write.bind(process.stdout);
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
process.stdout.write = (str, encoding, cb) => { process.stdout.write = (str, encoding, cb) => {
// Core library will directly call process.stdout.write for commands // Core library will directly call process.stdout.write for commands
// We don't want :: commands to be executed by the runner during tests // We don't want :: commands to be executed by the runner during tests
if (!str.match(/^::/)) { if (!str.match(/^::/)) {
return processStdoutWrite(str, encoding, cb); return processStdoutWrite(str, encoding, cb);
} }
} };

924
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,8 +7,9 @@
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"test": "tsc --noEmit && jest --coverage", "test": "tsc --noEmit && jest --coverage",
"format": "prettier --write **/*.ts", "lint": "eslint '**/*.ts' --cache",
"format-check": "prettier --check **/*.ts", "format": "prettier --write '**/*.ts'",
"format-check": "prettier --check '**/*.ts'",
"release": "ncc build -o dist/restore src/restore.ts && ncc build -o dist/save src/save.ts && git add -f dist/" "release": "ncc build -o dist/restore src/restore.ts && ncc build -o dist/save src/save.ts && git add -f dist/"
}, },
"repository": { "repository": {
@ -34,7 +35,14 @@
"@types/nock": "^11.1.0", "@types/nock": "^11.1.0",
"@types/node": "^12.0.4", "@types/node": "^12.0.4",
"@types/uuid": "^3.4.5", "@types/uuid": "^3.4.5",
"@typescript-eslint/eslint-plugin": "^2.7.0",
"@typescript-eslint/parser": "^2.7.0",
"@zeit/ncc": "^0.20.5", "@zeit/ncc": "^0.20.5",
"eslint": "^6.6.0",
"eslint-config-prettier": "^6.5.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jest": "^23.0.3",
"eslint-plugin-prettier": "^3.1.1",
"jest": "^24.8.0", "jest": "^24.8.0",
"jest-circus": "^24.7.1", "jest-circus": "^24.7.1",
"nock": "^11.7.0", "nock": "^11.7.0",

View File

@ -1,13 +1,40 @@
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as fs from "fs"; import * as fs from "fs";
import { BearerCredentialHandler } from "typed-rest-client/Handlers"; import { BearerCredentialHandler } from "typed-rest-client/Handlers";
import { HttpClient } from "typed-rest-client/HttpClient"; import { HttpClient } from "typed-rest-client/HttpClient";
import { IHttpClientResponse } from "typed-rest-client/Interfaces"; import { IHttpClientResponse } from "typed-rest-client/Interfaces";
import { RestClient, IRequestOptions } from "typed-rest-client/RestClient"; import { IRequestOptions, RestClient } from "typed-rest-client/RestClient";
import { ArtifactCacheEntry } from "./contracts"; import { ArtifactCacheEntry } from "./contracts";
function getCacheUrl(): string {
// Ideally we just use ACTIONS_CACHE_URL
const cacheUrl: string = (
process.env["ACTIONS_CACHE_URL"] ||
process.env["ACTIONS_RUNTIME_URL"] ||
""
).replace("pipelines", "artifactcache");
if (!cacheUrl) {
throw new Error(
"Cache Service Url not found, unable to restore cache."
);
}
core.debug(`Cache Url: ${cacheUrl}`);
return cacheUrl;
}
function createAcceptHeader(type: string, apiVersion: string): string {
return `${type};api-version=${apiVersion}`;
}
function getRequestOptions(): IRequestOptions {
const requestOptions: IRequestOptions = {
acceptHeader: createAcceptHeader("application/json", "5.2-preview.1")
};
return requestOptions;
}
export async function getCacheEntry( export async function getCacheEntry(
keys: string[] keys: string[]
): Promise<ArtifactCacheEntry | null> { ): Promise<ArtifactCacheEntry | null> {
@ -43,16 +70,6 @@ export async function getCacheEntry(
return cacheResult; return cacheResult;
} }
export async function downloadCache(
cacheEntry: ArtifactCacheEntry,
archivePath: string
): Promise<void> {
const stream = fs.createWriteStream(archivePath);
const httpClient = new HttpClient("actions/cache");
const downloadResponse = await httpClient.get(cacheEntry.archiveLocation!);
await pipeResponseToStream(downloadResponse, stream);
}
async function pipeResponseToStream( async function pipeResponseToStream(
response: IHttpClientResponse, response: IHttpClientResponse,
stream: NodeJS.WritableStream stream: NodeJS.WritableStream
@ -64,7 +81,21 @@ async function pipeResponseToStream(
}); });
} }
export async function saveCache(stream: NodeJS.ReadableStream, key: string) { export async function downloadCache(
cacheEntry: ArtifactCacheEntry,
archivePath: string
): Promise<void> {
const stream = fs.createWriteStream(archivePath);
const httpClient = new HttpClient("actions/cache");
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const downloadResponse = await httpClient.get(cacheEntry.archiveLocation!);
await pipeResponseToStream(downloadResponse, stream);
}
export async function saveCache(
stream: NodeJS.ReadableStream,
key: string
): Promise<void> {
const cacheUrl = getCacheUrl(); const cacheUrl = getCacheUrl();
const token = process.env["ACTIONS_RUNTIME_TOKEN"] || ""; const token = process.env["ACTIONS_RUNTIME_TOKEN"] || "";
const bearerCredentialHandler = new BearerCredentialHandler(token); const bearerCredentialHandler = new BearerCredentialHandler(token);
@ -93,32 +124,3 @@ export async function saveCache(stream: NodeJS.ReadableStream, key: string) {
core.info("Cache saved successfully"); core.info("Cache saved successfully");
} }
function getRequestOptions(): IRequestOptions {
const requestOptions: IRequestOptions = {
acceptHeader: createAcceptHeader("application/json", "5.2-preview.1")
};
return requestOptions;
}
function createAcceptHeader(type: string, apiVersion: string): string {
return `${type};api-version=${apiVersion}`;
}
function getCacheUrl(): string {
// Ideally we just use ACTIONS_CACHE_URL
let cacheUrl: string = (
process.env["ACTIONS_CACHE_URL"] ||
process.env["ACTIONS_RUNTIME_URL"] ||
""
).replace("pipelines", "artifactcache");
if (!cacheUrl) {
throw new Error(
"Cache Service Url not found, unable to restore cache."
);
}
core.debug(`Cache Url: ${cacheUrl}`);
return cacheUrl;
}

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-namespace */
export namespace Inputs { export namespace Inputs {
export const Key = "key"; export const Key = "key";
export const Path = "path"; export const Path = "path";

View File

@ -1,14 +1,12 @@
import * as core from "@actions/core"; import * as core from "@actions/core";
import { exec } from "@actions/exec"; import { exec } from "@actions/exec";
import * as io from "@actions/io"; import * as io from "@actions/io";
import * as path from "path"; import * as path from "path";
import * as cacheHttpClient from "./cacheHttpClient"; import * as cacheHttpClient from "./cacheHttpClient";
import { Inputs, State } from "./constants"; import { Inputs, State } from "./constants";
import * as utils from "./utils/actionUtils"; import * as utils from "./utils/actionUtils";
async function run() { async function run(): Promise<void> {
try { try {
// Validate inputs, this can cause task failure // Validate inputs, this can cause task failure
let cachePath = utils.resolvePath( let cachePath = utils.resolvePath(

View File

@ -1,15 +1,13 @@
import * as core from "@actions/core"; import * as core from "@actions/core";
import { exec } from "@actions/exec"; import { exec } from "@actions/exec";
import * as io from "@actions/io"; import * as io from "@actions/io";
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import * as cacheHttpClient from "./cacheHttpClient"; import * as cacheHttpClient from "./cacheHttpClient";
import { Inputs, State } from "./constants"; import { Inputs, State } from "./constants";
import * as utils from "./utils/actionUtils"; import * as utils from "./utils/actionUtils";
async function run() { async function run(): Promise<void> {
try { try {
const state = utils.getCacheState(); const state = utils.getCacheState();

View File

@ -4,7 +4,6 @@ import * as fs from "fs";
import * as os from "os"; import * as os from "os";
import * as path from "path"; import * as path from "path";
import * as uuidV4 from "uuid/v4"; import * as uuidV4 from "uuid/v4";
import { Outputs, State } from "../constants"; import { Outputs, State } from "../constants";
import { ArtifactCacheEntry } from "../contracts"; import { ArtifactCacheEntry } from "../contracts";
@ -50,10 +49,18 @@ export function isExactKeyMatch(
); );
} }
export function setCacheState(state: ArtifactCacheEntry): void {
core.saveState(State.CacheResult, JSON.stringify(state));
}
export function setCacheHitOutput(isCacheHit: boolean): void {
core.setOutput(Outputs.CacheHit, isCacheHit.toString());
}
export function setOutputAndState( export function setOutputAndState(
key: string, key: string,
cacheResult?: ArtifactCacheEntry cacheResult?: ArtifactCacheEntry
) { ): void {
setCacheHitOutput(isExactKeyMatch(key, cacheResult)); setCacheHitOutput(isExactKeyMatch(key, cacheResult));
// Store the cache result if it exists // Store the cache result if it exists
cacheResult && setCacheState(cacheResult); cacheResult && setCacheState(cacheResult);
@ -65,14 +72,6 @@ export function getCacheState(): ArtifactCacheEntry | undefined {
return (stateData && JSON.parse(stateData)) as ArtifactCacheEntry; return (stateData && JSON.parse(stateData)) as ArtifactCacheEntry;
} }
export function setCacheState(state: ArtifactCacheEntry) {
core.saveState(State.CacheResult, JSON.stringify(state));
}
export function setCacheHitOutput(isCacheHit: boolean) {
core.setOutput(Outputs.CacheHit, isCacheHit.toString());
}
export function resolvePath(filePath: string): string { export function resolvePath(filePath: string): string {
if (filePath[0] === "~") { if (filePath[0] === "~") {
const home = os.homedir(); const home = os.homedir();

View File

@ -5,7 +5,7 @@ function getInputName(name: string): string {
return `INPUT_${name.replace(/ /g, "_").toUpperCase()}`; return `INPUT_${name.replace(/ /g, "_").toUpperCase()}`;
} }
export function setInput(name: string, value: string) { export function setInput(name: string, value: string): void {
process.env[getInputName(name)] = value; process.env[getInputName(name)] = value;
} }
@ -15,14 +15,14 @@ interface CacheInput {
restoreKeys?: string[]; restoreKeys?: string[];
} }
export function setInputs(input: CacheInput) { export function setInputs(input: CacheInput): void {
setInput(Inputs.Path, input.path); setInput(Inputs.Path, input.path);
setInput(Inputs.Key, input.key); setInput(Inputs.Key, input.key);
input.restoreKeys && input.restoreKeys &&
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n")); setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
} }
export function clearInputs() { export function clearInputs(): void {
delete process.env[getInputName(Inputs.Path)]; delete process.env[getInputName(Inputs.Path)];
delete process.env[getInputName(Inputs.Key)]; delete process.env[getInputName(Inputs.Key)];
delete process.env[getInputName(Inputs.RestoreKeys)]; delete process.env[getInputName(Inputs.RestoreKeys)];