From f69c6b5dd25d0170bed3f414d9a779e744ce4928 Mon Sep 17 00:00:00 2001 From: Allen Evans Date: Sun, 22 Sep 2019 08:01:14 +0100 Subject: [PATCH] feat: set-env action BREAKING: First major release --- .editorconfig | 14 ++++++++ .github/workflows/build.yml | 41 ++++++++++++++++++++++ .github/workflows/release.yml | 46 ++++++++++++++++++++++++ .gitignore | 20 +++++++++++ .npmrc | 2 ++ .prettierrc | 10 ++++++ .releaserc | 10 ++++++ LICENSE | 21 +++++++++++ README.md | 25 +++++++++++++ action.yml | 6 ++++ docs/contributors.md | 1 + jest.config.js | 17 +++++++++ package.json | 48 +++++++++++++++++++++++++ src/main.spec.ts | 54 ++++++++++++++++++++++++++++ src/main.ts | 14 ++++++++ tsconfig.json | 66 +++++++++++++++++++++++++++++++++++ tslint.json | 14 ++++++++ webpack.config.js | 26 ++++++++++++++ 18 files changed, 435 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 .prettierrc create mode 100644 .releaserc create mode 100644 LICENSE create mode 100644 action.yml create mode 100644 docs/contributors.md create mode 100644 jest.config.js create mode 100644 package.json create mode 100644 src/main.spec.ts create mode 100644 src/main.ts create mode 100644 tsconfig.json create mode 100644 tslint.json create mode 100644 webpack.config.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b6de60a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 140 + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..60e797b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,41 @@ +name: "build" + +on: [pull_request] + +jobs: + validation: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: "Config" + run: | + git config --global user.email "git-ci@techinity.com" + git config --global user.name "gitci" + + - name: "Install dependencies" + run: npm install + + - name: "Build" + run: npm run build + + - name: "Test" + run: npm run test + + - name: "Lint" + run: npm run lint + + - name: "Commit build output" + run: | + git checkout -B "dist/artifacts-${{github.event.number}}" + git reset ${{github.event.after}} + git add -f dist/ + git commit -m "ci: build distributables" + git push -f https://${{secrets.GH_ACTION_TOKEN}}@github.com/allenevans/set-env.git HEAD:dist/artifacts-${{github.event.number}} + + - name: "Check for uncommitted changes" + run: | + git diff --exit-code --stat -- . ':!node_modules' \ + || (echo "##[error] found changed filces after build." \ + && exit 1) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..cfb52f3 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,46 @@ +name: "release" +on: + push: + branches: + - 'master' + +jobs: + validation: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: "Config" + run: | + echo "Release triggered by ${{github.actor}}" + echo "Commit ${{github.event.commits[0].message}}" + git config --global user.email "git-ci@techinity.com" + git config --global user.name "gitci" + + - name: "Install dependencies" + run: npm install + + - name: "Build" + run: npm run build + + - name: "Test" + run: npm run test + + - name: "Commit build artifacts" + if: | + contains(github.event.commits[0].message, '[skip ci]') == false && + contains(github.event.commits[0].message, 'release artifacts') == false + run: | + git add --ignore-errors -f dist/ + git diff-index --quiet HEAD -- || git commit -m 'release artifacts' + + - name: "Semantic version" + if: | + contains(github.event.commits[0].message, '[skip ci]') == false && + contains(github.event.commits[0].message, 'release artifacts') == false + env: + GH_TOKEN: ${{secrets.GITHUB_ACTION_TOKEN}} + run: | + npm run release + git status diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de7a20c --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +package-lock.json + +# Logs +logs +*.log +npm-debug.log* + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# Dependency directories +node_modules/ + +# Optional REPL history +.node_repl_history + +# Output +dist/ +*.tgz diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..2d5519e --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +package-lock=false +save-exact diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..f1cf745 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "arrowParens": "always", + "jsxBracketSameLine": false, + "parser": "typescript", + "printWidth": 140, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false +} diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000..c1aab77 --- /dev/null +++ b/.releaserc @@ -0,0 +1,10 @@ +{ + "branch": "master", + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/git" + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3d832d1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Allen Evans + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 612f632..26c8f68 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,27 @@ # set-env Github action to set environment variables + +Example usage:- + +``` +name: example-pipline +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: changed packages + id: changed_packages + uses: allenevans/set-env@master + + - name: Dump steps context + env: + STEPS_CONTEXT: ${{ toJson(steps) }} + run: | + echo "$STEPS_CONTEXT" + echo "scope=${{steps.changed_packages.outputs.scope}}" +``` diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..7b80195 --- /dev/null +++ b/action.yml @@ -0,0 +1,6 @@ +name: 'set-env' +description: 'Set environment variables that can be used in other steps' +author: 'Allen Evans' +runs: + using: 'node12' + main: 'dist/set-env.js' diff --git a/docs/contributors.md b/docs/contributors.md new file mode 100644 index 0000000..77e8114 --- /dev/null +++ b/docs/contributors.md @@ -0,0 +1 @@ +# Contributors diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..9825868 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,17 @@ +module.exports = { + collectCoverage: true, + moduleFileExtensions: ['ts', 'js', 'json', 'node'], + resetMocks: true, + testMatch: [ + '**/?(*.)spec.ts', + ], + testPathIgnorePatterns: [ + 'dist/', + 'node_modules/', + ], + testURL: 'http://localhost/', + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, + verbose: true, +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..3809835 --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "set-env", + "version": "0.0.1", + "private": true, + "description": "Set environment variables that can be used in other steps", + "main": "dist/set-env.js", + "scripts": { + "build": "webpack --config ./webpack.config.js", + "clean": "rimraf dist/", + "lint": "tslint -c tslint.json --project tsconfig.json", + "prebuild": "npm run clean", + "release": "semantic-release", + "test": "jest --config=jest.config.js --silent" + }, + "repository": { + "type": "git", + "url": "/~https://github.com/allenevans/set-env.git" + }, + "keywords": [ + "actions", + "node", + "setup" + ], + "author": "Allen Evans", + "license": "MIT", + "devDependencies": { + "@actions/core": "1.1.1", + "@semantic-release/changelog": "3.0.4", + "@semantic-release/commit-analyzer": "6.3.0", + "@semantic-release/git": "7.0.16", + "@semantic-release/npm": "5.1.15", + "@semantic-release/release-notes-generator": "7.3.0", + "@types/jest": "24.0.18", + "@types/node": "12.7.5", + "jest": "24.9.0", + "prettier": "1.18.2", + "rimraf": "3.0.0", + "semantic-release": "15.13.24", + "ts-jest": "24.1.0", + "ts-loader": "6.1.2", + "ts-node": "8.4.1", + "tslint": "5.20.0", + "tslint-config-airbnb": "5.11.2", + "typescript": "3.6.3", + "webpack": "4.40.2", + "webpack-cli": "3.3.9" + } +} diff --git a/src/main.spec.ts b/src/main.spec.ts new file mode 100644 index 0000000..1943769 --- /dev/null +++ b/src/main.spec.ts @@ -0,0 +1,54 @@ +import * as core from '@actions/core'; + +jest.mock('@actions/core', () => ({ + exportVariable: jest.fn(), + setFailed: jest.fn(), +})); + +describe('set-env', () => { + afterEach(() => { + Object.keys(process.env) + .filter((key) => key.match(/^INPUT_/)) + .forEach((key) => { + delete process.env[key]; + }); + }); + + it('should export all environment variables beginning with INPUT_', () => { + process.env.INPUT_VERSION = '1.2.3'; + + jest.isolateModules(() => require('./main')); + + expect(core.exportVariable).toHaveBeenCalledTimes(1); + expect(core.exportVariable).toHaveBeenCalledWith('VERSION', '1.2.3'); + }); + + it('should export multiple environment variables beginning with INPUT_', () => { + process.env.INPUT_A = '1'; + process.env.INPUT_B = '2'; + process.env.INPUT_C = '3'; + + jest.isolateModules(() => require('./main')); + + expect(core.exportVariable).toHaveBeenCalledTimes(3); + expect(core.exportVariable).toHaveBeenCalledWith('A', '1'); + expect(core.exportVariable).toHaveBeenCalledWith('B', '2'); + expect(core.exportVariable).toHaveBeenCalledWith('C', '3'); + }); + + it('should report exceptions', () => { + (core.exportVariable).mockImplementation(() => { + throw new Error('FAIL'); + }); + jest.spyOn(process, 'exit').mockImplementation((code?: number): never => { + throw new Error(`${code}`); + }); + + process.env.INPUT_TEST = 'test'; + + jest.isolateModules(() => expect(() => require('./main')).toThrowError('1')); + + expect(core.setFailed).toHaveBeenCalledTimes(1); + expect(core.setFailed).toHaveBeenCalledWith('FAIL'); + }); +}); diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..4b04eed --- /dev/null +++ b/src/main.ts @@ -0,0 +1,14 @@ +import * as core from '@actions/core'; + +(function run() { + try { + Object.keys(process.env) + .filter((key) => /^INPUT_/.test(key)) + .forEach((key) => { + core.exportVariable(key.replace(/^INPUT_/, ''), `${process.env[key]}`); + }); + } catch (error) { + core.setFailed(error.message); + process.exit(1); + } +})(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..931901e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,66 @@ +{ + "exclude": [ + "dist", + "node_modules" + ], + "compilerOptions": { + /* Basic Options */ + "target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "module": "commonjs", /* 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": "./dist/", /* Redirect output structure to the directory. */ + "rootDir": "./src/", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "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. */ + // "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": [ + "@types/jest", + "@types/node" + ], /* 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. */ + + /* 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. */ + } +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..9bc97b0 --- /dev/null +++ b/tslint.json @@ -0,0 +1,14 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint-config-airbnb" + ], + "jsRules": {}, + "rules": { + "align": [true, "parameters", "statements"], + "import-name": [ false ], + "max-line-length": [true, 140], + "ter-arrow-parens": [true, "always"] + }, + "rulesDirectory": [] +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..fe735a2 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,26 @@ +const path = require('path'); + +module.exports = { + mode: 'production', + entry: { + ['set-env']: path.resolve('src/main'), + }, + resolve: { + extensions: ['.js', '.json', '.ts'], + }, + output: { + libraryTarget: 'commonjs', + path: path.join(__dirname, 'dist'), + filename: '[name].js', + }, + target: 'node', + module: { + rules: [ + // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader` + { + test: /\.ts$/, + loader: 'ts-loader', + }, + ], + }, +};