From f906a303ccbf771088bbefb7fa3a78e4cc299981 Mon Sep 17 00:00:00 2001 From: Emmanuel Raviart <emmanuel@raviart.com> Date: Wed, 27 Oct 2021 10:57:56 +0200 Subject: [PATCH] Add GitLab CI. --- .gitignore | 4 +- .gitlab-ci.yml | 31 +++ gitlab-ci/src/gitlab-ci.ts | 392 +++++++++++++++++++++++++++++++++++ gitlab-ci/tsconfig.json | 64 ++++++ package-lock.json | 409 ++++++++++++++++++++++++++++++++++++- package.json | 3 +- 6 files changed, 900 insertions(+), 3 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 gitlab-ci/src/gitlab-ci.ts create mode 100644 gitlab-ci/tsconfig.json diff --git a/.gitignore b/.gitignore index b2e8eafe3..c49306878 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ node_modules /*.env !/example.env /.svelte-kit -/build/ +/build +/gitlab-ci/build + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..a05e84544 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,31 @@ +default: + image: node:16-bullseye + + cache: + paths: + - node_modules + +ci: + stage: build + before_script: + # Without apt update, apt install fails. + - apt update --yes + + # Install ssh-agent if not already installed, it is required by Docker. + - "which ssh-agent || apt install -y openssh-client" + # Run ssh-agent (inside the build environment) + - eval $(ssh-agent -s) + + # `esbuild` 0.13.8 fails when rebuilding it twice. + # => Reinstall it every time. + - rm -Rf node_modules/esbuild + # + - npm install + # Needed when Node version changes: + - npm rebuild + + # Compile gitlab-ci TypeScript script. + - npx tsc --declaration --project gitlab-ci/tsconfig.json + script: + # Execute gitlab-ci JavaScript script. + - node --experimental-specifier-resolution=node gitlab-ci/build/gitlab-ci.js diff --git a/gitlab-ci/src/gitlab-ci.ts b/gitlab-ci/src/gitlab-ci.ts new file mode 100644 index 000000000..999d3dde0 --- /dev/null +++ b/gitlab-ci/src/gitlab-ci.ts @@ -0,0 +1,392 @@ +import assert from "assert" +import { $, fetch, fs } from "zx" + +export interface VersionObject { + major: number + minor: number + patch: number +} + +const { + CI_COMMIT_BRANCH, + CI_COMMIT_TAG, + CI_COMMIT_TITLE, + CI_DEFAULT_BRANCH, + CI_JOB_TOKEN, + CI_PIPELINE_SOURCE, + CI_PROJECT_NAME, + CI_PROJECT_NAMESPACE, + CI_SERVER_HOST, + CI_SERVER_URL, + // NPM_EMAIL, + // NPM_TOKEN, + SSH_KNOWN_HOSTS, + SSH_PRIVATE_KEY, +} = process.env +const packagePath = "package.json" + +function checkVersionObject( + { + major: majorReference, + minor: minorReference, + patch: patchReference, + }: VersionObject, + versionObject: VersionObject | undefined, +): boolean { + if (versionObject === undefined) { + return true + } + const { major, minor, patch } = versionObject + if (major < majorReference) { + return true + } + if (major === majorReference) { + if (minor < minorReference) { + return true + } + if (minor === minorReference) { + return patch <= patchReference || patch === patchReference + 1 + } + return minor === minorReference + 1 && patch === 0 + } + return major === majorReference + 1 && minor === 0 && patch === 0 +} + +async function commitAndPushWithUpdatedVersions( + nextVersionObject?: VersionObject | undefined, +) { + if (nextVersionObject === undefined) { + // Retrieve next version of project. + nextVersionObject = await latestVersionObjectFromTags() + assert.notStrictEqual(nextVersionObject, undefined) + nextVersionObject!.patch++ + } + const nextVersion = versionFromObject(nextVersionObject!) + + if ((await $`git diff --quiet --staged`.exitCode) !== 0) { + let packageJson = await fs.readFile(packagePath, "utf-8") + packageJson = packageJson.replace( + /^ "version": "(.*?)",$/m, + ` "version": "${nextVersion}",`, + ) + await fs.writeFile(packagePath, packageJson, "utf-8") + } + + await $`git add .` + if ((await $`git diff --quiet --staged`.exitCode) !== 0) { + await $`git commit -m ${nextVersion}` + await $`git push --set-upstream` + } + await $`git tag -a ${nextVersion} -m ${nextVersion}` + await $`git push --set-upstream --tags` + + // // Note: Don't fail when there is nothing new to publish. + // if (NPM_EMAIL !== undefined && NPM_TOKEN !== undefined) { + // await nothrow($`npm publish`) + // } +} + +async function configureGit() { + console.log("Configuring git…") + + // Set the Git user name and email. + await $`git config --global user.email "admin+leximpact-socio-fiscal-ui-ci@tax-benefit.org"` + await $`git config --global user.name "Leximpact socio-fiscal UI CI"` + + console.log("Git configuration completed.") +} + +// async function configureNpm() { +// console.log("Configuring npm…") + +// // Configure npm to be able to publish packages. +// if (NPM_EMAIL !== undefined && NPM_TOKEN !== undefined) { +// await $`echo ${`//registry.npmjs.org/:_authToken=${NPM_TOKEN}`} > ~/.npmrc` +// await $`echo ${`email=${NPM_EMAIL}`} >> ~/.npmrc` +// } + +// console.log("Npm configuration completed.") +// } + +async function configureSsh() { + console.log("Configuring ssh…") + + // Note: `eval $(ssh-agent -s)` must be done before calling this script because it + // creates 2 environment variables (then used by ssh clients). + // // Install ssh-agent if not already installed, to be able to use git with ssh. + // if ((await $`which ssh-agent`.exitCode) !== 0) { + // await $`apt install -y openssh-client` + // } + // // Run ssh-agent (inside the build environment) + // await $`eval $(ssh-agent -s)` + + // Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store + // Use `tr` to fix line endings which makes ed25519 keys work without + // extra base64 encoding. + // https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 + if (SSH_PRIVATE_KEY !== undefined) { + await $`echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -` + } + + if (SSH_KNOWN_HOSTS !== undefined) { + // Create the SSH directory and give it the right permissions + await $`mkdir -p ~/.ssh` + await $`chmod 700 ~/.ssh` + // Accept the SSH host keys of GitLab server. + await $`echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts` + await $`chmod 644 ~/.ssh/known_hosts` + } + + console.log("Ssh configuration completed.") +} + +async function latestVersionObjectFromTags(): Promise< + VersionObject | undefined +> { + // Retrieve all tags. + await $`git fetch --all --tags` + + return (await $`git tag --list`).stdout + .split("\n") + .map(objectFromVersion) + .filter((versionObject) => versionObject !== undefined) + .sort((versionObject1, versionObject2) => + versionObject1!.major !== versionObject2!.major + ? versionObject2!.major - versionObject1!.major + : versionObject1!.minor !== versionObject2!.minor + ? versionObject2!.minor - versionObject1!.minor + : versionObject2!.patch - versionObject1!.patch, + )[0] +} + +async function main() { + console.log("Starting gitlab-ci.ts…") + + await configureGit() + // await configureNpm() + await configureSsh() + + console.log(`Handling "${CI_PIPELINE_SOURCE}" event…`) + switch (CI_PIPELINE_SOURCE) { + case "api": + case "pipeline": + case "schedule": + case "trigger": + case "web": { + if (CI_COMMIT_BRANCH === CI_DEFAULT_BRANCH) { + // A pipeline has been triggered (for example when OpenFisca-France has been updated or a user as + // launched a new pipeline manually or…). + // => A new version of project must be created if and only if a dependency has + // changed. + + console.log(`Handling trigger…`) + + await resetGitRepository() + + // Test project with the current dependencies. + await $`ln -s example.env .env` + await $`npm run build` + // TODO: Add tests. + + await triggerDevDeployPipeline() + // await triggerProdDeployPipeline() + } else { + console.log( + `Unhandled event "${CI_PIPELINE_SOURCE}" in branch "${CI_COMMIT_BRANCH}".`, + ) + } + break + } + case "push": { + if (CI_COMMIT_BRANCH !== undefined) { + console.log(`Push to branch ${CI_COMMIT_BRANCH}`) + + if (CI_COMMIT_BRANCH.match(/^\d+_\d+_\d+$/) != null) { + console.log( + `Ignoring commit in version branch (for branch ${CI_COMMIT_BRANCH}).`, + ) + } else if (CI_COMMIT_TITLE?.match(/^\d+\.\d+\.\d+( |$)/) != null) { + console.log( + `Ignoring version commit (for version ${CI_COMMIT_TITLE}).`, + ) + } else { + await resetGitRepository() + + // Retrieve current version of project. + const tagVersionObject = (await latestVersionObjectFromTags()) ?? { + major: 0, + minor: 0, + patch: 0, + } + const tagVersion = versionFromObject(tagVersionObject) + let nextVersionObject = { + ...tagVersionObject, + patch: tagVersionObject!.patch + 1, + } + + // Ensure that the version numbers of project packages are + // compatible with last version tag. + const pkg = await fs.readJson(packagePath) + const { version: packageVersion } = pkg + const packageVersionObject = objectFromVersion(packageVersion) + assert( + checkVersionObject(tagVersionObject, packageVersionObject), + `In ${packagePath}, project version should be compatible with ${tagVersion}, got "${packageVersion}" instead.`, + ) + nextVersionObject = maxVersionObject( + nextVersionObject, + packageVersionObject, + ) + + // Test project with the current dependencies. + await $`ln -s example.env .env` + await $`npm run build` + // TODO: Add tests. + + if (CI_COMMIT_BRANCH === CI_DEFAULT_BRANCH) { + // A merge request has been merged into master (ie content of project has been changed). + // => Create a new version of project. + await commitAndPushWithUpdatedVersions(nextVersionObject) + await triggerDevDeployPipeline() + // await triggerProdDeployPipeline() + } + } + } else if (CI_COMMIT_TAG !== undefined) { + console.log(`Pushing commit tag "${CI_COMMIT_TAG}"…`) + } else if (CI_COMMIT_TAG !== undefined) { + console.log(`Unhandled push event.`) + } + break + } + default: + console.log( + `Unhandled event "${CI_PIPELINE_SOURCE}" in branch "${CI_COMMIT_BRANCH}".`, + ) + } +} + +function maxVersionObject( + versionObject1: VersionObject, + versionObject2: VersionObject | undefined, +): VersionObject { + if (versionObject2 === undefined) { + return versionObject1 + } + const { major: major1, minor: minor1, patch: patch1 } = versionObject1 + const { major: major2, minor: minor2, patch: patch2 } = versionObject2 + if (major1 < major2) { + return versionObject2 + } + if (major1 > major2) { + return versionObject1 + } + if (minor1 < minor2) { + return versionObject2 + } + if (minor1 > minor2) { + return versionObject1 + } + if (patch1 < patch2) { + return versionObject2 + } + if (patch1 > patch2) { + return versionObject1 + } + // Both versions are the same. Return one of them. + return versionObject1 +} + +function objectFromVersion( + version: string | undefined, +): VersionObject | undefined { + if (version === undefined) { + return undefined + } + const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/) as string[] + if (match === null) { + return undefined + } + return { + major: parseInt(match[1]), + minor: parseInt(match[2]), + patch: parseInt(match[3]), + } +} + +async function resetGitRepository() { + // Reset current repo, because it may have been tranformed by a previous CI that failed. + await $`git switch ${CI_COMMIT_BRANCH}` + await $`git fetch origin ${CI_COMMIT_BRANCH}` + await $`git reset --hard origin/${CI_COMMIT_BRANCH}` + await $`git status` + if (CI_COMMIT_BRANCH === CI_DEFAULT_BRANCH) { + await $`git remote set-url origin git@${CI_SERVER_HOST}:${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}.git` + } else { + await $`git remote set-url origin https://${CI_SERVER_HOST}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}.git` + } +} + +async function triggerDevDeployPipeline() { + const url = new URL( + `/api/v4/projects/28/trigger/pipeline`, + CI_SERVER_URL, + ).toString() + console.log( + "Triggering LexImpact socio-fiscal Dev Deploy pipeline on master branch…", + ) + const response = await fetch(url, { + body: new URLSearchParams({ + ref: "master", + token: CI_JOB_TOKEN!, + }).toString(), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + method: "POST", + }) + assert( + response.ok, + `Unexpected response from ${url}: ${response.status} ${ + response.statusText + }\n${await response.text()}`, + ) +} + +// async function triggerProdDeployPipeline() { +// const url = new URL( +// `/api/v4/projects/29/trigger/pipeline`, +// CI_SERVER_URL +// ).toString(); +// console.log( +// "Triggering LexImpact socio-fiscal Prod Deploy pipeline on master branch…" +// ); +// const response = await fetch(url, { +// body: new URLSearchParams({ +// ref: "master", +// token: CI_JOB_TOKEN!, +// }).toString(), +// headers: { +// "Content-Type": "application/x-www-form-urlencoded", +// }, +// method: "POST", +// }); +// assert( +// response.ok, +// `Unexpected response from ${url}: ${response.status} ${ +// response.statusText +// }\n${await response.text()}` +// ); +// } + +function versionFromObject({ major, minor, patch }: VersionObject): string { + return `${major}.${minor}.${patch}` +} + +main() + .then(() => { + process.exit(0) + }) + .catch((error: unknown) => { + console.error(error) + process.exit(1) + }) diff --git a/gitlab-ci/tsconfig.json b/gitlab-ci/tsconfig.json new file mode 100644 index 000000000..9ee50ee27 --- /dev/null +++ b/gitlab-ci/tsconfig.json @@ -0,0 +1,64 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "build" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "incremental": true, /* Enable incremental compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + }, + "include": ["src/*.ts"] +} diff --git a/package-lock.json b/package-lock.json index 2c132604c..eee8e7103 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,7 +54,8 @@ "tailwindcss": "^2.0.3", "tslib": "^2.0.0", "typescript": "^4.0.0", - "uuid": "^8.3.2" + "uuid": "^8.3.2", + "zx": "^4.2.0" } }, "node_modules/@auditors/core": { @@ -2053,6 +2054,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, "node_modules/@types/node": { "version": "16.10.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.5.tgz", @@ -3348,6 +3355,12 @@ "node": ">=10" } }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -4004,6 +4017,21 @@ "node": ">=0.10.0" } }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "node_modules/fast-deep-copy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-deep-copy/-/fast-deep-copy-1.0.0.tgz", @@ -4155,6 +4183,12 @@ "url": "https://www.patreon.com/infusion" } }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, "node_modules/fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -4888,6 +4922,12 @@ "semver": "bin/semver" } }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -5310,6 +5350,15 @@ "node": ">=8" } }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, "node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", @@ -5922,6 +5971,21 @@ "node": ">=0.4.0" } }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -6398,6 +6462,18 @@ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -6410,6 +6486,15 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -6835,6 +6920,12 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, "node_modules/timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", @@ -7174,6 +7265,145 @@ "engines": { "node": ">= 6" } + }, + "node_modules/zx": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/zx/-/zx-4.2.0.tgz", + "integrity": "sha512-/4f7FaJecA9I655KXKXIHO3CFNYjAz2uSmTz6v2eNlKdrQKyz4VyF3RjqFuP6nQG+Hd3+NjOvrVNBkv8Ne9d4Q==", + "dev": true, + "dependencies": { + "@types/fs-extra": "^9.0.12", + "@types/minimist": "^1.2.2", + "@types/node": "^16.6", + "@types/node-fetch": "^2.5.12", + "chalk": "^4.1.2", + "fs-extra": "^10.0.0", + "globby": "^12.0.1", + "minimist": "^1.2.5", + "node-fetch": "^2.6.1", + "ps-tree": "^1.2.0", + "which": "^2.0.2" + }, + "bin": { + "zx": "zx.mjs" + }, + "engines": { + "node": ">= 14.13.0" + } + }, + "node_modules/zx/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/zx/node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zx/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/zx/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/zx/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/zx/node_modules/globby": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.0.2.tgz", + "integrity": "sha512-lAsmb/5Lww4r7MM9nCCliDZVIKbZTavrsunAsHLr9oHthrZP1qi7/gAnHOsUs9bLvEt2vKVJhHmxuL7QbDuPdQ==", + "dev": true, + "dependencies": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.8", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zx/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/zx/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zx/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } } }, "dependencies": { @@ -8581,6 +8811,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, "@types/node": { "version": "16.10.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.5.tgz", @@ -9550,6 +9786,12 @@ "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", "dev": true }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -9987,6 +10229,21 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "fast-deep-copy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-deep-copy/-/fast-deep-copy-1.0.0.tgz", @@ -10110,6 +10367,12 @@ "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", "dev": true }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -10700,6 +10963,12 @@ } } }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -11014,6 +11283,15 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "~2.3" + } + }, "picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", @@ -11397,6 +11675,15 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "requires": { + "event-stream": "=3.3.4" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -11759,6 +12046,15 @@ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -11771,6 +12067,15 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -12050,6 +12355,12 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, "timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", @@ -12295,6 +12606,102 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true + }, + "zx": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/zx/-/zx-4.2.0.tgz", + "integrity": "sha512-/4f7FaJecA9I655KXKXIHO3CFNYjAz2uSmTz6v2eNlKdrQKyz4VyF3RjqFuP6nQG+Hd3+NjOvrVNBkv8Ne9d4Q==", + "dev": true, + "requires": { + "@types/fs-extra": "^9.0.12", + "@types/minimist": "^1.2.2", + "@types/node": "^16.6", + "@types/node-fetch": "^2.5.12", + "chalk": "^4.1.2", + "fs-extra": "^10.0.0", + "globby": "^12.0.1", + "minimist": "^1.2.5", + "node-fetch": "^2.6.1", + "ps-tree": "^1.2.0", + "which": "^2.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "globby": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.0.2.tgz", + "integrity": "sha512-lAsmb/5Lww4r7MM9nCCliDZVIKbZTavrsunAsHLr9oHthrZP1qi7/gAnHOsUs9bLvEt2vKVJhHmxuL7QbDuPdQ==", + "dev": true, + "requires": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.8", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } } } } diff --git a/package.json b/package.json index 57e5cc242..97044ebcb 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,8 @@ "tailwindcss": "^2.0.3", "tslib": "^2.0.0", "typescript": "^4.0.0", - "uuid": "^8.3.2" + "uuid": "^8.3.2", + "zx": "^4.2.0" }, "type": "module" } -- GitLab