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
on:
pull_request:
branches:
@ -14,14 +15,16 @@ on:
jobs:
test:
name: Test on ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12.x'
@ -29,7 +32,12 @@ jobs:
- run: npm ci
- name: Prettier Format Check
if: matrix.os == 'ubuntu-latest'
run: npm run format-check
- name: ESLint Check
if: matrix.os == 'ubuntu-latest'
run: npm run lint
- name: Build & Test
run: npm run test

View File

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

View File

@ -1,22 +1,23 @@
require('nock').disableNetConnect();
require("nock").disableNetConnect();
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
}
clearMocks: true,
moduleFileExtensions: ["js", "ts"],
testEnvironment: "node",
testMatch: ["**/*.test.ts"],
testRunner: "jest-circus/runner",
transform: {
"^.+\\.ts$": "ts-jest"
},
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) => {
// Core library will directly call process.stdout.write for commands
// We don't want :: commands to be executed by the runner during tests
if (!str.match(/^::/)) {
return processStdoutWrite(str, encoding, cb);
}
}
// Core library will directly call process.stdout.write for commands
// We don't want :: commands to be executed by the runner during tests
if (!str.match(/^::/)) {
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": {
"build": "tsc",
"test": "tsc --noEmit && jest --coverage",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"lint": "eslint '**/*.ts' --cache",
"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/"
},
"repository": {
@ -34,7 +35,14 @@
"@types/nock": "^11.1.0",
"@types/node": "^12.0.4",
"@types/uuid": "^3.4.5",
"@typescript-eslint/eslint-plugin": "^2.7.0",
"@typescript-eslint/parser": "^2.7.0",
"@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-circus": "^24.7.1",
"nock": "^11.7.0",

View File

@ -1,13 +1,40 @@
import * as core from "@actions/core";
import * as fs from "fs";
import { BearerCredentialHandler } from "typed-rest-client/Handlers";
import { HttpClient } from "typed-rest-client/HttpClient";
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";
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(
keys: string[]
): Promise<ArtifactCacheEntry | null> {
@ -43,16 +70,6 @@ export async function getCacheEntry(
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(
response: IHttpClientResponse,
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 token = process.env["ACTIONS_RUNTIME_TOKEN"] || "";
const bearerCredentialHandler = new BearerCredentialHandler(token);
@ -93,32 +124,3 @@ export async function saveCache(stream: NodeJS.ReadableStream, key: string) {
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 const Key = "key";
export const Path = "path";

View File

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

View File

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

View File

@ -4,7 +4,6 @@ import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as uuidV4 from "uuid/v4";
import { Outputs, State } from "../constants";
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(
key: string,
cacheResult?: ArtifactCacheEntry
) {
): void {
setCacheHitOutput(isExactKeyMatch(key, cacheResult));
// Store the cache result if it exists
cacheResult && setCacheState(cacheResult);
@ -65,14 +72,6 @@ export function getCacheState(): ArtifactCacheEntry | undefined {
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 {
if (filePath[0] === "~") {
const home = os.homedir();

View File

@ -5,7 +5,7 @@ function getInputName(name: string): string {
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;
}
@ -15,14 +15,14 @@ interface CacheInput {
restoreKeys?: string[];
}
export function setInputs(input: CacheInput) {
export function setInputs(input: CacheInput): void {
setInput(Inputs.Path, input.path);
setInput(Inputs.Key, input.key);
input.restoreKeys &&
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.Key)];
delete process.env[getInputName(Inputs.RestoreKeys)];