diff --git a/.gitignore b/.gitignore index 692e74b..ca9bdff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ __tests__/runner/* +run.sh diff --git a/LICENSE b/LICENSE index a426ef2..39e0594 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,6 @@ - The MIT License (MIT) -Copyright (c) 2018 GitHub, Inc. and contributors +Copyright (c) 2019 Sven-Hendrik Haase Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,4 +18,4 @@ 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. \ No newline at end of file +THE SOFTWARE. diff --git a/README.md b/README.md index cdbe411..4f9090b 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,87 @@ -# JavaScript Action Template +# Upload files to a GitHub release -This template offers an easy way to get started writing a javascript action with TypeScript compile time support, unit testing with Jest and using the GitHub Actions Toolkit. +This action allows you to select which files to upload to the just-tagged release. +It runs on all operating systems types offered by GitHub. -## Getting Started +## Input variables: -See the walkthrough located [here](https://github.com/actions/toolkit/blob/master/docs/javascript-action.md). +You must provide: -In addition to walking your through how to create an action, it also provides strategies for versioning, releasing and referencing your actions. + - `repo_token`: Usually you'll want to set this to `${{ secrets.GITHUB_TOKEN }}` + - `file`: A local file to be uploaded as the asset. + - `asset_name`: The name the file gets as an asset on a release. + - `tag`: The tag to uploaded into. If you want the current event's tag, use `${{ github.event.ref }}` + - `overwrite`: If an asset with name already exists, overwrite it. + +## Usage + +This usage assumes you want to build on tag creations only. +This is a common use case as you will want to upload release binaries for your tags. + +Simple example: + + name: Publish + + on: + create: + tags: + + jobs: + build: + name: Publish binaries + runs-on: ubuntu-latest + + steps: + - uses: hecrj/setup-rust-action@master + with: + rust-version: stable + - uses: actions/checkout@v1 + - name: Build + run: cargo build --release + - name: Upload binaries to release + uses: svenstaro/upload-release-action@master + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: target/release/mything + asset_name: mything + tag: {{ github.event.ref }} + overwrite: true + +Complex example with more operating systems: + + name: Publish + + on: + create: + tags: + + jobs: + build: + name: Publish for ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + artifact_name: mything + asset_name: mything-linux-amd64 + - os: windows-latest + artifact_name: mything.exe + asset_name: mything-windows-amd64 + - os: macos-latest + artifact_name: mything + asset_name: mything-macos-amd64 + + steps: + - uses: hecrj/setup-rust-action@master + with: + rust-version: stable + - uses: actions/checkout@v1 + - name: Build + run: cargo build --release + - name: Upload binaries to release + uses: svenstaro/upload-release-action@master + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: target/release/${{ matrix.artifact_name }} + asset_name: ${{ matrix.asset_name }} diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 48f15ff..78e89d7 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -1,3 +1,6 @@ -describe('TODO - Add a test suite', () => { - it('TODO - Add a test', async () => {}); +import * as main from '../src/main'; + +// Frankly, tests would be entirely useless unless we can mock GitHub somehow. +describe('Upload Release Action', () => { + it('', async () => {}); }); diff --git a/action.yml b/action.yml index 2b3b1b0..69402ec 100644 --- a/action.yml +++ b/action.yml @@ -1,10 +1,17 @@ -name: 'Node 12 Template Action' -description: 'Get started with Node actions' -author: 'GitHub' -inputs: - myInput: - description: 'Input to use' - default: 'world' +name: 'Upload files to a GitHub release' +description: 'Upload files to a GitHub release (cross-platform)' +author: 'Sven-Hendrik Haase' +inputs: + repo_token: + description: 'GitHub token' + file: + description: 'Local file to upload' + asset_name: + description: 'Name of the asset' + tag: + description: 'Tag to use as a release' + overwrite: + description: 'Overwrite the release in case it already exists' runs: using: 'node12' main: 'lib/main.js' diff --git a/lib/main.js b/lib/main.js new file mode 100644 index 0000000..cf42e0a --- /dev/null +++ b/lib/main.js @@ -0,0 +1,92 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __importStar(require("fs")); +const core = __importStar(require("@actions/core")); +const github = __importStar(require("@actions/github")); +function get_release_by_tag(tag, octokit, context) { + return __awaiter(this, void 0, void 0, function* () { + try { + core.debug(`Getting release by tag ${tag}.`); + return yield octokit.repos.getReleaseByTag(Object.assign({}, context.repo, { tag: tag })); + } + catch (error) { + // If this returns 404, we need to create the release first. + if (error.status === 404) { + core.debug(`Release for tag ${tag} doesn't exist yet so we'll create it now.`); + return yield octokit.repos.createRelease(Object.assign({}, context.repo, { tag_name: tag })); + } + else { + throw error; + } + } + }); +} +function upload_to_release(release, file, asset_name, tag, overwrite, octokit, context) { + return __awaiter(this, void 0, void 0, function* () { + const file_size = fs.statSync(file).size; + const file_bytes = fs.readFileSync(file); + // Check for duplicates. + const assets = yield octokit.repos.listAssetsForRelease(Object.assign({}, context.repo, { release_id: release.data.id })); + const duplicate_asset = assets.data.find(a => a.name === asset_name); + if (duplicate_asset !== undefined) { + if (overwrite === "true") { + core.debug(`An asset called ${asset_name} already exists in release ${tag} so we'll overwrite it.`); + yield octokit.repos.deleteReleaseAsset(Object.assign({}, context.repo, { asset_id: duplicate_asset.id })); + } + else { + core.setFailed(`An asset called ${asset_name} already exists.`); + return; + } + } + else { + core.debug(`No pre-existing asset called ${asset_name} found in release ${tag}. All good.`); + } + core.debug(`Uploading ${file} to ${asset_name} in release ${tag}.`); + yield octokit.repos.uploadReleaseAsset({ + url: release.data.upload_url, + name: asset_name, + file: file_bytes, + headers: { + "content-type": "binary/octet-stream", + "content-length": file_size + }, + }); + }); +} +function run() { + return __awaiter(this, void 0, void 0, function* () { + try { + const token = core.getInput('repo_token', { required: true }); + const file = core.getInput('file', { required: true }); + const asset_name = core.getInput('asset_name', { required: true }); + const tag = core.getInput('tag', { required: true }); + const overwrite = core.getInput('overwrite'); + if (!fs.existsSync(file)) { + core.setFailed(`File ${file} wasn't found.`); + } + const octokit = new github.GitHub(token); + const context = github.context; + const release = yield get_release_by_tag(tag, octokit, context); + yield upload_to_release(release, file, asset_name, tag, overwrite, octokit, context); + } + catch (error) { + core.setFailed(error.message); + } + }); +} +run(); diff --git a/package-lock.json b/package-lock.json index 3c556ab..c721776 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "node12-template-action", - "version": "0.0.0", + "name": "upload-release-action", + "version": "0.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -9,6 +9,15 @@ "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.0.0.tgz", "integrity": "sha512-aMIlkx96XH4E/2YZtEOeyrYQfhlas9jIRkfGPqMwXD095Rdkzo4lB6ZmbxPQSzD+e1M+Xsm98ZhuSMYGv/AlqA==" }, + "@actions/github": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-1.0.0.tgz", + "integrity": "sha512-PPbWZ5wFAD/Vr+RCECfR3KNHjTwYln4liJBihs9tQUL0/PCFqB2lSkIh9V94AcZFHxgKk8snImjuLaBE8bKR7A==", + "requires": { + "@octokit/graphql": "^2.0.1", + "@octokit/rest": "^16.15.0" + } + }, "@babel/code-frame": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", @@ -426,6 +435,99 @@ "@types/yargs": "^12.0.9" } }, + "@octokit/endpoint": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.3.2.tgz", + "integrity": "sha512-gRjteEM9I6f4D8vtwU2iGUTn9RX/AJ0SVXiqBUEuYEWVGGAVjSXdT0oNmghH5lvQNWs8mwt6ZaultuG6yXivNw==", + "requires": { + "deepmerge": "4.0.0", + "is-plain-object": "^3.0.0", + "universal-user-agent": "^3.0.0", + "url-template": "^2.0.8" + }, + "dependencies": { + "universal-user-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz", + "integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==", + "requires": { + "os-name": "^3.0.0" + } + } + } + }, + "@octokit/graphql": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-2.1.3.tgz", + "integrity": "sha512-XoXJqL2ondwdnMIW3wtqJWEwcBfKk37jO/rYkoxNPEVeLBDGsGO1TCWggrAlq3keGt/O+C/7VepXnukUxwt5vA==", + "requires": { + "@octokit/request": "^5.0.0", + "universal-user-agent": "^2.0.3" + } + }, + "@octokit/request": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.0.2.tgz", + "integrity": "sha512-z1BQr43g4kOL4ZrIVBMHwi68Yg9VbkRUyuAgqCp1rU3vbYa69+2gIld/+gHclw15bJWQnhqqyEb7h5a5EqgZ0A==", + "requires": { + "@octokit/endpoint": "^5.1.0", + "@octokit/request-error": "^1.0.1", + "deprecation": "^2.0.0", + "is-plain-object": "^3.0.0", + "node-fetch": "^2.3.0", + "once": "^1.4.0", + "universal-user-agent": "^3.0.0" + }, + "dependencies": { + "universal-user-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz", + "integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==", + "requires": { + "os-name": "^3.0.0" + } + } + } + }, + "@octokit/request-error": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.0.4.tgz", + "integrity": "sha512-L4JaJDXn8SGT+5G0uX79rZLv0MNJmfGa4vb4vy1NnpjSnWDLJRy6m90udGwvMmavwsStgbv2QNkPzzTCMmL+ig==", + "requires": { + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "16.28.7", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.28.7.tgz", + "integrity": "sha512-cznFSLEhh22XD3XeqJw51OLSfyL2fcFKUO+v2Ep9MTAFfFLS1cK1Zwd1yEgQJmJoDnj4/vv3+fGGZweG+xsbIA==", + "requires": { + "@octokit/request": "^5.0.0", + "@octokit/request-error": "^1.0.2", + "atob-lite": "^2.0.0", + "before-after-hook": "^2.0.0", + "btoa-lite": "^1.0.0", + "deprecation": "^2.0.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lodash.uniq": "^4.5.0", + "octokit-pagination-methods": "^1.1.0", + "once": "^1.4.0", + "universal-user-agent": "^3.0.0", + "url-template": "^2.0.8" + }, + "dependencies": { + "universal-user-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz", + "integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==", + "requires": { + "os-name": "^3.0.0" + } + } + } + }, "@types/babel__core": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", @@ -679,6 +781,11 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "atob-lite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", + "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -821,6 +928,11 @@ "tweetnacl": "^0.14.3" } }, + "before-after-hook": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -907,6 +1019,11 @@ "node-int64": "^0.4.0" } }, + "btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=" + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -1123,7 +1240,6 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, "requires": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -1135,8 +1251,7 @@ "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" } } }, @@ -1215,6 +1330,11 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.0.0.tgz", + "integrity": "sha512-YZ1rOP5+kHor4hMAH+HRQnBQHg+wvS1un1hAOuIcxcBy0hzcUf6Jg2a1w65kpoOUnurOfZbERwjI1TfZxNjcww==" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1277,6 +1397,11 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -1312,7 +1437,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -1400,7 +1524,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, "requires": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", @@ -2240,7 +2363,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, "requires": { "pump": "^3.0.0" } @@ -2604,6 +2726,14 @@ } } }, + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "requires": { + "isobject": "^4.0.0" + } + }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -2616,8 +2746,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-symbol": { "version": "1.0.2", @@ -2655,8 +2784,12 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==" }, "isstream": { "version": "0.1.2", @@ -3392,12 +3525,27 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3407,6 +3555,11 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "macos-release": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", + "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==" + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -3647,8 +3800,12 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" }, "node-int64": { "version": "0.4.0", @@ -3716,7 +3873,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, "requires": { "path-key": "^2.0.0" } @@ -3820,11 +3976,15 @@ } } }, + "octokit-pagination-methods": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", + "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -3880,6 +4040,15 @@ "mem": "^4.0.0" } }, + "os-name": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", + "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^3.1.0" + } + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -3898,8 +4067,7 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-is-promise": { "version": "2.1.0", @@ -3974,8 +4142,7 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { "version": "1.0.6", @@ -4069,7 +4236,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -4395,7 +4561,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -4403,8 +4568,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "shellwords": { "version": "0.1.1", @@ -4415,8 +4579,7 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "sisteransi": { "version": "1.0.2", @@ -4750,8 +4913,7 @@ "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "supports-color": { "version": "5.5.0", @@ -4958,6 +5120,14 @@ "set-value": "^2.0.1" } }, + "universal-user-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.1.0.tgz", + "integrity": "sha512-8itiX7G05Tu3mGDTdNY2fB4KJ8MgZLS54RdG6PkkfwMAavrXu1mV/lls/GABx9O3Rw4PnTtasxrvbMQoBYY92Q==", + "requires": { + "os-name": "^3.0.0" + } + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -5019,6 +5189,11 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -5122,7 +5297,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -5133,6 +5307,14 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "windows-release": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", + "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", + "requires": { + "execa": "^1.0.0" + } + }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", @@ -5189,8 +5371,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "2.4.1", diff --git a/package.json b/package.json index aad060a..de72a36 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "node12-template-action", - "version": "0.0.0", + "name": "upload-release-action", + "version": "0.0.1", "private": true, - "description": "Node 12 template action", + "description": "Upload files to a GitHub release", "main": "lib/main.js", "scripts": { "build": "tsc", @@ -10,17 +10,20 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/actions/node12-template.git" + "url": "git+https://github.com/svestaro/upload-release-action.git" }, "keywords": [ "actions", - "node", - "setup" + "upload", + "release", + "assets", + "asset" ], - "author": "GitHub", + "author": "Sven-Hendrik Haase", "license": "MIT", "dependencies": { - "@actions/core": "^1.0.0" + "@actions/core": "^1.0.0", + "@actions/github": "^1.0.0" }, "devDependencies": { "@types/jest": "^24.0.13", diff --git a/src/main.ts b/src/main.ts index fae8b12..89e6c88 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,85 @@ +import * as fs from 'fs'; import * as core from '@actions/core'; +import * as github from '@actions/github'; + +async function get_release_by_tag(tag: string, octokit: any, context: any): Promise { + try { + core.debug(`Getting release by tag ${tag}.`); + return await octokit.repos.getReleaseByTag({ + ...context.repo, + tag: tag, + }); + } catch (error) { + // If this returns 404, we need to create the release first. + if (error.status === 404) { + core.debug(`Release for tag ${tag} doesn't exist yet so we'll create it now.`) + return await octokit.repos.createRelease({ + ...context.repo, + tag_name: tag, + }) + } else { + throw error; + } + } +} + +async function upload_to_release(release: any, file: string, asset_name: string, tag: string, overwrite: string, octokit: any, context: any) { + const file_size = fs.statSync(file).size; + const file_bytes = fs.readFileSync(file); + + // Check for duplicates. + const assets = await octokit.repos.listAssetsForRelease({ + ...context.repo, + release_id: release.data.id, + }); + const duplicate_asset = assets.data.find(a => a.name === asset_name); + if (duplicate_asset !== undefined) { + if (overwrite === "true") { + core.debug(`An asset called ${asset_name} already exists in release ${tag} so we'll overwrite it.`) + await octokit.repos.deleteReleaseAsset({ + ...context.repo, + asset_id: duplicate_asset.id + }) + } else { + core.setFailed(`An asset called ${asset_name} already exists.`) + return; + } + } else { + core.debug(`No pre-existing asset called ${asset_name} found in release ${tag}. All good.`); + } + + core.debug(`Uploading ${file} to ${asset_name} in release ${tag}.`) + await octokit.repos.uploadReleaseAsset({ + url: release.data.upload_url, + name: asset_name, + file: file_bytes, + headers: { + "content-type": "binary/octet-stream", + "content-length": file_size + }, + }); +} async function run() { - try { - const myInput = core.getInput('myInput'); - core.debug(`Hello ${myInput}`); - } catch (error) { - core.setFailed(error.message); - } + try { + const token = core.getInput('repo_token', { required: true }); + const file = core.getInput('file', { required: true }); + const asset_name = core.getInput('asset_name', { required: true }); + const tag = core.getInput('tag', { required: true }); + const overwrite = core.getInput('overwrite'); + + if (!fs.existsSync(file)) { + core.setFailed(`File ${file} wasn't found.`); + } + + const octokit = new github.GitHub(token); + const context = github.context; + + const release = await get_release_by_tag(tag, octokit, context); + await upload_to_release(release, file, asset_name, tag, overwrite, octokit, context); + } catch (error) { + core.setFailed(error.message); + } } run();