Compare commits

...

16 Commits

Author SHA1 Message Date
f26a08096a commit latest build artifacts 2019-09-29 08:16:42 -04:00
ba42ad9139 honor body_path input when provided. fixes #22 2019-09-29 02:15:58 -04:00
07c8c20669 upgrade deps 2019-09-29 02:04:47 -04:00
00c56dd770 funding 2019-09-18 23:14:09 +09:00
50c843ac1c set a better example. dont refer to @master 2019-09-18 13:38:56 +09:00
ef036888ec update changelog 2019-09-18 13:26:04 +09:00
2758344bdf bump version 2019-09-18 13:25:04 +09:00
18d8be76a0 Merge pull request #19 from softprops/pre-releases
add support for prereleases
2019-09-17 23:35:48 +09:00
2984051a42 update readme 2019-09-17 23:33:44 +09:00
a95bad53b2 add support for prereleases. fixes #17 2019-09-17 23:30:36 +09:00
bd839f3b8f Merge pull request #18 from softprops/newline-delimited-assets
add support for multi-line delimited assets
2019-09-17 23:23:56 +09:00
e812c4aca6 tabs to spaces 2019-09-17 23:22:34 +09:00
615fb448a2 changelog 2019-09-17 23:21:28 +09:00
77f9a4f575 update inputs.files.description 2019-09-17 23:16:32 +09:00
3af8783d41 support multi-line delimited assets. fixes #15 2019-09-17 23:14:30 +09:00
a6281eb824 Merge pull request #16 from softprops/draft-merger
Draft merger
2019-09-17 19:39:58 +09:00
12 changed files with 219 additions and 32 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
ko_fi: softprops

View File

@ -5,6 +5,25 @@
GitHub's api doesn't explicitly have a way of fetching a draft release by tag name which caused draft releases to appear as separate releases when used in a build matrix. GitHub's api doesn't explicitly have a way of fetching a draft release by tag name which caused draft releases to appear as separate releases when used in a build matrix.
This is now fixed. This is now fixed.
* Add support for newline-delimited asset list [#18](https://github.com/softprops/action-gh-release/pull/18)
GitHub actions inputs don't inherently support lists of things and one might like to append a list of files to include in a release. Previously this was possible using a comma-delimited list of asset path patterns to upload. You can now provide these as a newline delimieted list for better readability
```yaml
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
filea.txt
fileb.txt
filec.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
* Add support for prerelease annotated GitHub releases with the new input field `with.prerelease: true` [#19](https://github.com/softprops/action-gh-release/pull/19)
--- ---
## 0.1.1 ## 0.1.1

View File

@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@master uses: actions/checkout@v1
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
@ -51,7 +51,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@master uses: actions/checkout@v1
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
env: env:
@ -65,7 +65,7 @@ You can can configure a number of options for your
GitHub release and all are optional. GitHub release and all are optional.
A common case for GitHub releases is to upload your binary after its been validated and packaged. A common case for GitHub releases is to upload your binary after its been validated and packaged.
Use the `with.files` input to declare a comma-separated list of glob expressions matching the files Use the `with.files` input to declare a newline-delimited list of glob expressions matching the files
you wish to upload to GitHub releases. If you'd like you can just list the files by name directly. you wish to upload to GitHub releases. If you'd like you can just list the files by name directly.
Below is an example of uploading a single asset named `Release.txt` Below is an example of uploading a single asset named `Release.txt`
@ -80,7 +80,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@master uses: actions/checkout@v1
- name: Build - name: Build
run: echo ${{ github.sha }} > Release.txt run: echo ${{ github.sha }} > Release.txt
- name: Test - name: Test
@ -94,6 +94,36 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
``` ```
Below is an example of uploading more than one asset with a GitHub release
```yaml
name: Main
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: echo ${{ github.sha }} > Release.txt
- name: Test
run: cat Release.txt
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
Release.txt
LICENSE
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
> **⚠️ Note:** Notice the `|` in the yaml syntax above ☝️. That let's you effectively declare a multi-line yaml string. You can learn more about multi-line yaml syntax [here](https://yaml-multiline.info)
### 📝 External release notes ### 📝 External release notes
Many systems exist that can help generate release notes for you. This action supports Many systems exist that can help generate release notes for you. This action supports
@ -110,7 +140,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@master uses: actions/checkout@v1
- name: Generate Changelog - name: Generate Changelog
run: echo "# Good things have arrived" > ${{ github.workflow }}-CHANGELOG.txt run: echo "# Good things have arrived" > ${{ github.workflow }}-CHANGELOG.txt
- name: Release - name: Release
@ -133,7 +163,8 @@ The following are optional as `step.with` keys
| `body` | String | Text communicating notable changes in this release | | `body` | String | Text communicating notable changes in this release |
| `body_path` | String | Path to load text communicating notable changes in this release | | `body_path` | String | Path to load text communicating notable changes in this release |
| `draft` | Boolean | Indicator of whether or not this release is a draft | | `draft` | Boolean | Indicator of whether or not this release is a draft |
| `files` | String | Comma-delimited globs of paths to assets to upload for release | | `prerelease`| Boolean | Indicator of whether or not is a prerelease |
| `files` | String | Newline-delimited globs of paths to assets to upload for release|
| `name` | String | Name of the release. defaults to tag name | | `name` | String | Name of the release. defaults to tag name |
💡When providing a `body` and `body_path` at the same time, `body_path` will be attempted first, then falling back on `body` if the path can not be read from. 💡When providing a `body` and `body_path` at the same time, `body_path` will be attempted first, then falling back on `body` if the path can not be read from.

1
__tests__/release.txt Normal file
View File

@ -0,0 +1 @@
bar

View File

@ -1,7 +1,92 @@
import { isTag, paths } from "../src/util"; import {
releaseBody,
isTag,
paths,
parseConfig,
parseInputFiles
} from "../src/util";
import * as assert from "assert"; import * as assert from "assert";
describe("util", () => { describe("util", () => {
describe("parseInputFiles", () => {
it("parses empty strings", () => {
assert.deepStrictEqual(parseInputFiles(""), []);
});
it("parses comma-delimited strings", () => {
assert.deepStrictEqual(parseInputFiles("foo,bar"), ["foo", "bar"]);
});
it("parses newline and comma-delimited (and then some)", () => {
assert.deepStrictEqual(
parseInputFiles("foo,bar\nbaz,boom,\n\ndoom,loom "),
["foo", "bar", "baz", "boom", "doom", "loom"]
);
});
});
describe("releaseBody", () => {
it("uses input body", () => {
assert.equal(
"foo",
releaseBody({
github_ref: "",
github_repository: "",
github_token: "",
input_body: "foo",
input_body_path: undefined,
input_draft: false,
input_prerelease: false,
input_files: [],
input_name: undefined
})
);
});
it("uses input body path", () => {
assert.equal(
"bar",
releaseBody({
github_ref: "",
github_repository: "",
github_token: "",
input_body: undefined,
input_body_path: "__tests__/release.txt",
input_draft: false,
input_prerelease: false,
input_files: [],
input_name: undefined
})
);
});
it("defaults to body when both body and body path are provided", () => {
assert.equal(
"foo",
releaseBody({
github_ref: "",
github_repository: "",
github_token: "",
input_body: "foo",
input_body_path: "__tests__/release.txt",
input_draft: false,
input_prerelease: false,
input_files: [],
input_name: undefined
})
);
});
});
describe("parseConfig", () => {
it("parses basic config", () => {
assert.deepStrictEqual(parseConfig({}), {
github_ref: "",
github_repository: "",
github_token: "",
input_body: undefined,
input_body_path: undefined,
input_draft: false,
input_prerelease: false,
input_files: [],
input_name: undefined
});
});
});
describe("isTag", () => { describe("isTag", () => {
it("returns true for tags", async () => { it("returns true for tags", async () => {
assert.equal(isTag("refs/tags/foo"), true); assert.equal(isTag("refs/tags/foo"), true);

View File

@ -13,10 +13,13 @@ inputs:
description: 'Gives the release a custom name. Defaults to tag name' description: 'Gives the release a custom name. Defaults to tag name'
required: false required: false
draft: draft:
description: 'Creates a draft release' description: 'Creates a draft release. Defaults to false'
required: false
prerelease:
description: 'Identify the release as a prerelease. Defaults to false'
required: false required: false
files: files:
description: 'Comma-delimited list of path globs for asset files to upload' description: 'Newline-delimited list of path globs for asset files to upload'
required: false required: false
env: env:
'GITHUB_TOKEN': 'As provided by Github Actions' 'GITHUB_TOKEN': 'As provided by Github Actions'

View File

@ -16,6 +16,7 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const util_1 = require("./util");
const fs_1 = require("fs"); const fs_1 = require("fs");
const mime_1 = require("mime"); const mime_1 = require("mime");
const path_1 = require("path"); const path_1 = require("path");
@ -98,8 +99,9 @@ exports.release = (config, releaser) => __awaiter(void 0, void 0, void 0, functi
try { try {
const tag_name = tag; const tag_name = tag;
const name = config.input_name || tag; const name = config.input_name || tag;
const body = config.input_body; const body = util_1.releaseBody(config);
const draft = config.input_draft; const draft = config.input_draft;
const prerelease = config.input_prerelease;
console.log(`👩‍🏭 Creating new GitHub release for tag ${tag_name}...`); console.log(`👩‍🏭 Creating new GitHub release for tag ${tag_name}...`);
let release = yield releaser.createRelease({ let release = yield releaser.createRelease({
owner, owner,
@ -107,7 +109,8 @@ exports.release = (config, releaser) => __awaiter(void 0, void 0, void 0, functi
tag_name, tag_name,
name, name,
body, body,
draft draft,
prerelease
}); });
return release.data; return release.data;
} }

View File

@ -9,6 +9,17 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const glob = __importStar(require("glob")); const glob = __importStar(require("glob"));
const fs_1 = require("fs"); const fs_1 = require("fs");
exports.releaseBody = (config) => {
return (config.input_body ||
(config.input_body_path &&
fs_1.readFileSync(config.input_body_path).toString("utf8")));
};
exports.parseInputFiles = (files) => {
return files.split(/\r?\n/).reduce((acc, line) => acc
.concat(line.split(","))
.filter(pat => pat)
.map(pat => pat.trim()), []);
};
exports.parseConfig = (env) => { exports.parseConfig = (env) => {
return { return {
github_token: env.GITHUB_TOKEN || "", github_token: env.GITHUB_TOKEN || "",
@ -17,8 +28,9 @@ exports.parseConfig = (env) => {
input_name: env.INPUT_NAME, input_name: env.INPUT_NAME,
input_body: env.INPUT_BODY, input_body: env.INPUT_BODY,
input_body_path: env.INPUT_BODY_PATH, input_body_path: env.INPUT_BODY_PATH,
input_files: (env.INPUT_FILES || "").split(","), input_files: exports.parseInputFiles(env.INPUT_FILES || ""),
input_draft: env.INPUT_DRAFT === "true" input_draft: env.INPUT_DRAFT === "true",
input_prerelease: env.INPUT_PRERELEASE == "true"
}; };
}; };
exports.paths = (patterns) => { exports.paths = (patterns) => {

21
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "action-gh-release", "name": "action-gh-release",
"version": "0.1.1", "version": "0.1.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -3437,6 +3437,12 @@
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
}, },
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
"dev": true
},
"lodash.set": { "lodash.set": {
"version": "4.3.2", "version": "4.3.2",
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
@ -4826,15 +4832,16 @@
"dev": true "dev": true
}, },
"ts-jest": { "ts-jest": {
"version": "24.0.2", "version": "24.1.0",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.0.2.tgz", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.1.0.tgz",
"integrity": "sha512-h6ZCZiA1EQgjczxq+uGLXQlNgeg02WWJBbeT8j6nyIBRQdglqbvzDoHahTEIiS6Eor6x8mK6PfZ7brQ9Q6tzHw==", "integrity": "sha512-HEGfrIEAZKfu1pkaxB9au17b1d9b56YZSqz5eCVE8mX68+5reOvlM93xGOzzCREIov9mdH7JBG+s0UyNAqr0tQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"bs-logger": "0.x", "bs-logger": "0.x",
"buffer-from": "1.x", "buffer-from": "1.x",
"fast-json-stable-stringify": "2.x", "fast-json-stable-stringify": "2.x",
"json5": "2.x", "json5": "2.x",
"lodash.memoize": "4.x",
"make-error": "1.x", "make-error": "1.x",
"mkdirp": "0.x", "mkdirp": "0.x",
"resolve": "1.x", "resolve": "1.x",
@ -4884,9 +4891,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "3.6.2", "version": "3.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
"integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==", "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
"dev": true "dev": true
}, },
"typescript-formatter": { "typescript-formatter": {

View File

@ -1,6 +1,6 @@
{ {
"name": "action-gh-release", "name": "action-gh-release",
"version": "0.1.1", "version": "0.1.2",
"private": true, "private": true,
"description": "GitHub Action for creating GitHub Releases", "description": "GitHub Action for creating GitHub Releases",
"main": "lib/main.js", "main": "lib/main.js",
@ -26,14 +26,14 @@
}, },
"devDependencies": { "devDependencies": {
"@types/glob": "^7.1.1", "@types/glob": "^7.1.1",
"@types/jest": "^24.0.13", "@types/jest": "^24.0.18",
"@types/mime": "^2.0.1", "@types/mime": "^2.0.1",
"@types/node": "^12.7.4", "@types/node": "^12.7.4",
"jest": "^24.8.0", "jest": "^24.9.0",
"jest-circus": "^24.7.1", "jest-circus": "^24.9.0",
"prettier": "1.18.2", "prettier": "1.18.2",
"ts-jest": "^24.0.2", "ts-jest": "^24.1.0",
"typescript": "^3.5.1", "typescript": "^3.6.3",
"typescript-formatter": "^7.2.2" "typescript-formatter": "^7.2.2"
} }
} }

View File

@ -1,5 +1,5 @@
import { GitHub } from "@actions/github"; import { GitHub } from "@actions/github";
import { Config } from "./util"; import { Config, releaseBody } from "./util";
import { lstatSync, readFileSync } from "fs"; import { lstatSync, readFileSync } from "fs";
import { getType } from "mime"; import { getType } from "mime";
import { basename } from "path"; import { basename } from "path";
@ -31,6 +31,7 @@ export interface Releaser {
name: string; name: string;
body: string | undefined; body: string | undefined;
draft: boolean | undefined; draft: boolean | undefined;
prerelease: boolean | undefined;
}): Promise<{ data: Release }>; }): Promise<{ data: Release }>;
allReleases(params: { allReleases(params: {
@ -60,6 +61,7 @@ export class GitHubReleaser implements Releaser {
name: string; name: string;
body: string | undefined; body: string | undefined;
draft: boolean | undefined; draft: boolean | undefined;
prerelease: boolean | undefined;
}): Promise<{ data: Release }> { }): Promise<{ data: Release }> {
return this.github.repos.createRelease(params); return this.github.repos.createRelease(params);
} }
@ -136,8 +138,9 @@ export const release = async (
try { try {
const tag_name = tag; const tag_name = tag;
const name = config.input_name || tag; const name = config.input_name || tag;
const body = config.input_body; const body = releaseBody(config);
const draft = config.input_draft; const draft = config.input_draft;
const prerelease = config.input_prerelease;
console.log(`👩‍🏭 Creating new GitHub release for tag ${tag_name}...`); console.log(`👩‍🏭 Creating new GitHub release for tag ${tag_name}...`);
let release = await releaser.createRelease({ let release = await releaser.createRelease({
owner, owner,
@ -145,7 +148,8 @@ export const release = async (
tag_name, tag_name,
name, name,
body, body,
draft draft,
prerelease
}); });
return release.data; return release.data;
} catch (error) { } catch (error) {

View File

@ -1,5 +1,5 @@
import * as glob from "glob"; import * as glob from "glob";
import { lstatSync } from "fs"; import { lstatSync, readFileSync } from "fs";
export interface Config { export interface Config {
github_token: string; github_token: string;
@ -11,10 +11,30 @@ export interface Config {
input_body_path?: string; input_body_path?: string;
input_files?: string[]; input_files?: string[];
input_draft?: boolean; input_draft?: boolean;
input_prerelease?: boolean;
} }
export const releaseBody = (config: Config): string | undefined => {
return (
config.input_body ||
(config.input_body_path &&
readFileSync(config.input_body_path).toString("utf8"))
);
};
type Env = { [key: string]: string | undefined }; type Env = { [key: string]: string | undefined };
export const parseInputFiles = (files: string): string[] => {
return files.split(/\r?\n/).reduce<string[]>(
(acc, line) =>
acc
.concat(line.split(","))
.filter(pat => pat)
.map(pat => pat.trim()),
[]
);
};
export const parseConfig = (env: Env): Config => { export const parseConfig = (env: Env): Config => {
return { return {
github_token: env.GITHUB_TOKEN || "", github_token: env.GITHUB_TOKEN || "",
@ -23,8 +43,9 @@ export const parseConfig = (env: Env): Config => {
input_name: env.INPUT_NAME, input_name: env.INPUT_NAME,
input_body: env.INPUT_BODY, input_body: env.INPUT_BODY,
input_body_path: env.INPUT_BODY_PATH, input_body_path: env.INPUT_BODY_PATH,
input_files: (env.INPUT_FILES || "").split(","), input_files: parseInputFiles(env.INPUT_FILES || ""),
input_draft: env.INPUT_DRAFT === "true" input_draft: env.INPUT_DRAFT === "true",
input_prerelease: env.INPUT_PRERELEASE == "true"
}; };
}; };