From 43cf54b000b92fac3589e521c62b8affa843238e Mon Sep 17 00:00:00 2001 From: Christian Dupuis Date: Wed, 8 Jul 2020 17:06:40 +0200 Subject: [PATCH] Initial version --- .dockerignore | 25 ++++ Dockerfile | 36 +++++ docs/images/icon.svg | 10 +- graphql/query/repos.graphql | 6 - graphql/subscription/buildOnPush.graphql | 27 ++++ lib/commands/helloWorld.ts | 22 --- lib/configuration.ts | 4 +- lib/events/buildOnPush.ts | 173 +++++++++++++++++++++++ lib/events/helloWorld.ts | 22 --- package-lock.json | 10 ++ package.json | 6 +- skill.package.yaml | 4 +- skill.ts | 40 +++--- tsconfig.json | 2 +- 14 files changed, 310 insertions(+), 77 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile delete mode 100644 graphql/query/repos.graphql create mode 100644 graphql/subscription/buildOnPush.graphql delete mode 100644 lib/commands/helloWorld.ts create mode 100644 lib/events/buildOnPush.ts delete mode 100644 lib/events/helloWorld.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4bb74db --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +.idea/ +*.iml +.vscode/ +**/*~ +**/.#* +.npm* +.travis.yml +.atomist/ +.nyc_output/ +assets/kubectl/ +node_modules/ +# build/ +coverage/ +doc/ +log/ +scripts/ +src/ +test/ +CO*.md +**/*.log +**/*.txt + +build/test/ +build/typedoc/ +img/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cbc37fc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:focal + +# tools +RUN apt-get update && apt-get install -y \ + curl \ + wget \ + gnupg \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# nvm +RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash +RUN echo 'export NVM_DIR="$HOME/.nvm"' >> "$HOME/.bashrc" \ + && echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm' >> "$HOME/.bashrc" + +# nodejs and tools +RUN bash -c "source $HOME/.nvm/nvm.sh \ + && nvm install 10 \ + && nvm install 12 \ + && nvm install 14 \ + && nvm use --lts" + +WORKDIR "/skill" + +COPY package.json package-lock.json ./ + +RUN bash -c "source $HOME/.nvm/nvm.sh \ + && npm ci --no-optional \ + && npm cache clean --force" + +COPY . ./ + +WORKDIR "/atm/home" + +ENTRYPOINT ["node", "--no-deprecation", "--trace-warnings", "--expose_gc", "--optimize_for_size", "--always_compact", "--max_old_space_size=512", "/skill/node_modules/.bin/atm-skill"] +CMD ["run"] diff --git a/docs/images/icon.svg b/docs/images/icon.svg index 37230a5..36d99e8 100644 --- a/docs/images/icon.svg +++ b/docs/images/icon.svg @@ -1 +1,9 @@ - + + + + + + + + + diff --git a/graphql/query/repos.graphql b/graphql/query/repos.graphql deleted file mode 100644 index 81afad5..0000000 --- a/graphql/query/repos.graphql +++ /dev/null @@ -1,6 +0,0 @@ -query Repositories { - Repo { - name - owner - } -} diff --git a/graphql/subscription/buildOnPush.graphql b/graphql/subscription/buildOnPush.graphql new file mode 100644 index 0000000..ebbe436 --- /dev/null +++ b/graphql/subscription/buildOnPush.graphql @@ -0,0 +1,27 @@ +subscription buildOnPush { + Push { + repo { + url + owner + name + org { + provider { + apiUrl + gitUrl + } + } + channels { + name + team { + id + } + } + } + branch + after { + timestamp + sha + url + } + } +} diff --git a/lib/commands/helloWorld.ts b/lib/commands/helloWorld.ts deleted file mode 100644 index 2a9a6db..0000000 --- a/lib/commands/helloWorld.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright © 2020 Atomist, Inc. - * - * 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 { CommandHandler, log } from "@atomist/skill"; -import { Configuration } from "../configuration"; - -export const handler: CommandHandler = async ctx => { - log.debug("Incoming parameters: %s", JSON.stringify(ctx.parameters)); -}; diff --git a/lib/configuration.ts b/lib/configuration.ts index 51695f4..801f3a9 100644 --- a/lib/configuration.ts +++ b/lib/configuration.ts @@ -15,5 +15,7 @@ */ export interface Configuration { - world: string; + version: string; + scripts: string[]; + docker_cache: string[]; } diff --git a/lib/events/buildOnPush.ts b/lib/events/buildOnPush.ts new file mode 100644 index 0000000..ac4c768 --- /dev/null +++ b/lib/events/buildOnPush.ts @@ -0,0 +1,173 @@ +/* + * Copyright © 2020 Atomist, Inc. + * + * 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 { + EventContext, + EventHandler, + Step, + secret, + repository, + project, + runSteps, + StepListener, + HandlerStatus, + github, +} from "@atomist/skill"; +import { Configuration } from "../configuration"; +import { BuildOnPushSubscription } from "../typings/types"; +import * as fs from "fs-extra"; +import * as df from "dateformat"; + +interface NpmParameters { + project: project.Project; + version: string; + check: github.Check; +} + +type NpmStep = Step, NpmParameters>; + +const LoadProjectStep: NpmStep = { + name: "load", + run: async (ctx, params) => { + const push = ctx.data.Push[0]; + const repo = push.repo; + + const credential = await ctx.credential.resolve( + secret.gitHubAppToken({ owner: repo.owner, repo: repo.name, apiUrl: repo.org.provider.apiUrl }), + ); + + const project: project.Project = await ctx.project.load( + repository.gitHub({ + owner: repo.owner, + repo: repo.name, + credential, + }), + process.cwd(), + ); + params.project = project; + + return { + visibility: "hidden", + code: 0, + }; + }, +}; + +const ValidateStep: NpmStep = { + name: "validate", + run: async (ctx, params) => { + if (!(await fs.pathExists(params.project.path("package.json")))) { + return { + visibility: "hidden", + code: 1, + reason: `Ignoring push to non-NPM project`, + }; + } + return { + visibility: "hidden", + code: 0, + }; + }, +}; + +const SetupNodeStep: NpmStep = { + name: "setup node", + run: async (ctx, params) => { + const cfg = ctx.configuration?.[0]?.parameters; + // Set up node version + const result = await params.project.spawn("nvm", ["install", cfg.version]); + return { + code: result.status, + }; + }, +}; + +const NodeVersionStep: NpmStep = { + name: "version", + run: async (ctx, params) => { + const pj = await fs.readJson(params.project.path("package.json")); + const branch = ctx.data.Push[0].branch.split("/").join("."); + const branchSuffix = `${branch}.`; + + let pjVersion = pj.version; + if (!pjVersion || pjVersion.length === 0) { + pjVersion = "0.0.1"; + } + + const version = `${pjVersion}-${gitBranchToNpmVersion(branchSuffix)}${formatDate()}`; + params.version = version; + const result = await params.project.spawn("npm", ["version", "--no-git-tag-version", version]); + return { + code: result.status, + }; + }, +}; + +function gitBranchToNpmVersion(branchName: string): string { + return branchName + .replace(/\//g, "-") + .replace(/_/g, "-") + .replace(/@/g, ""); +} + +function formatDate(date = new Date(), format = "yyyymmddHHMMss", utc = true) { + return df(date, format, utc); +} + +const NpmInstallStep: NpmStep = { + name: "npm install", + run: async (ctx, params) => { + const opts = { env: { ...process.env, NODE_ENV: "development" } }; + let result; + if (await fs.pathExists(params.project.path("package-lock.json"))) { + result = await params.project.spawn("npm", ["ci"], opts); + } else { + result = await params.project.spawn("npm", ["install"], opts); + } + + return { + code: result.status, + }; + }, +}; + +const NodeScriptsStep: NpmStep = { + name: "npm run", + run: async (ctx, params) => { + const cfg = ctx.configuration?.[0]?.parameters; + const scripts = cfg.scripts; + // Run scripts + for (const script of scripts) { + const result = await params.project.spawn("npm", ["run", "--if-present", script]); + if (result.status !== 0) { + return { + code: result.status, + }; + } + } + return { + code: 0, + }; + }, +}; + +export const handler: EventHandler = async ctx => { + return runSteps({ + context: ctx, + steps: [LoadProjectStep, ValidateStep, SetupNodeStep, NodeVersionStep, NpmInstallStep, NodeScriptsStep], + listeners: [checkListener], + }); +}; diff --git a/lib/events/helloWorld.ts b/lib/events/helloWorld.ts deleted file mode 100644 index 90d2221..0000000 --- a/lib/events/helloWorld.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright © 2020 Atomist, Inc. - * - * 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 { EventHandler, log } from "@atomist/skill"; -import { Configuration } from "../configuration"; - -export const handler: EventHandler = async ctx => { - log.debug("Incoming event: %s", JSON.stringify(ctx.data)); -}; diff --git a/package-lock.json b/package-lock.json index cebf0ce..c7a20fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1780,6 +1780,11 @@ "@types/node": "*" } }, + "@types/dateformat": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-3.0.1.tgz", + "integrity": "sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g==" + }, "@types/debounce": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.0.tgz", @@ -3384,6 +3389,11 @@ "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", "dev": true }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" + }, "de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", diff --git a/package.json b/package.json index 97e8d2e..8d32198 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,9 @@ { "main": "node_modules/@atomist/skill/lib/function.js", "dependencies": { - "@atomist/skill": "0.1.0-master.20200702145252" + "@atomist/skill": "0.1.0-master.20200702145252", + "@types/dateformat": "^3.0.1", + "dateformat": "^3.0.3" }, "devDependencies": { "@google-cloud/functions-framework": "^1.6.0", @@ -48,7 +50,7 @@ "start": "functions-framework --target=entryPoint --signature-type=event", "test": "mocha --require espower-typescript/guess \"test/**/*.test.ts\" --reporter min", "test:one": "mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"", - "skill": "run-s compile test skill:generate skill:bundle skill:package", + "skill": "run-s compile test skill:generate", "skill:generate": "atm-skill generate", "skill:clean": "atm-skill clean", "skill:bundle": "atm-skill bundle --minify --source-map", diff --git a/skill.package.yaml b/skill.package.yaml index e4972cc..0c561f8 100644 --- a/skill.package.yaml +++ b/skill.package.yaml @@ -14,6 +14,8 @@ # limitations under the License. # +--- apiVersion: 1 package: - type: atomist/node-skill + type: atomist/container-skill + command: npm ci && npm run skill diff --git a/skill.ts b/skill.ts index e58f44c..86d177d 100644 --- a/skill.ts +++ b/skill.ts @@ -14,48 +14,46 @@ * limitations under the License. */ -import { parameter, ParameterType, resourceProvider, skill } from "@atomist/skill"; +import { Category, parameter, ParameterType, resourceProvider, skill } from "@atomist/skill"; import { Configuration } from "./lib/configuration"; export const Skill = skill({ name: "npm-skill", namespace: "atomist", - displayName: "NPM", - author: "atomist-skills", - categories: [], + displayName: "NPM Scripts", + author: "Atomist", + categories: [Category.DevEx], license: "Apache-2.0", homepageUrl: "https://github.com/atomist-skills/npm-skill", repositoryUrl: "https://github.com/atomist-skills/npm-skill.git", iconUrl: "file://docs/images/icon.svg", - runtime: { - memory: 2048, - timeout: 540, - }, - resourceProviders: { github: resourceProvider.gitHub({ minRequired: 1 }), chat: resourceProvider.chat({ minRequired: 0 }), }, parameters: { - world: { + version: { type: ParameterType.String, - displayName: "World", - description: "", + displayName: "Node.js version", + description: "Version of Node.js to install (should be valid nvm alias or version)", + required: false, + }, + scripts: { + type: ParameterType.StringArray, + displayName: "NPM scripts", + description: "Provide name of NPM scripts to run in order", + required: true, + }, + docker_cache: { + type: ParameterType.StringArray, + displayName: "Cache files or folders", + description: "Cache and restore file system content between executions of this skill", required: false, }, repos: parameter.repoFilter(), }, - commands: [ - { - name: "helloWorld", - displayName: "HelloWorld", - pattern: /^hello world$/, - description: "Simple hello world command", - }, - ], - subscriptions: ["file://graphql/subscription/*.graphql"], }); diff --git a/tsconfig.json b/tsconfig.json index 9e69ac6..f9665ba 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "lib": [ "DOM" ], - "strict": true, + "strict": false, "forceConsistentCasingInFileNames": true, "noImplicitReturns": true, "noUnusedLocals": true,