Skip to content

Commit

Permalink
feat: Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdlg committed Nov 21, 2017
1 parent 4fd033b commit 9eb65fb
Show file tree
Hide file tree
Showing 17 changed files with 1,252 additions and 3 deletions.
131 changes: 131 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@

# Created by https://www.gitignore.io/api/macos,windows,linux,node

### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### macOS ###
*.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env


### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msm
*.msp

# Windows shortcuts
*.lnk

# End of https://www.gitignore.io/api/macos,windows,linux,node

package-lock.json
yarn.lock
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
26 changes: 26 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
language: node_js
services:
- docker
notifications:
email: false
node_js:
- 9
- 8

# Trigger a push build on master and greenkeeper branches + PRs build on every branches
# Avoid double build on PRs (See https://github.com/travis-ci/travis-ci/issues/1147)
branches:
only:
- master
- /^greenkeeper.*$/

# Retry install on fail to avoid failing a build on network/disk/external errors
install:
- travis_retry npm install

script:
- npm run test

after_success:
- npm run codecov
- npm run semantic-release
1 change: 1 addition & 0 deletions .yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--install.no-lockfile true
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017
Copyright (c) 2017 Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
97 changes: 95 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,95 @@
# github
Set of semantic-release plugins for publishing a Github release
# @semantic-release/github

Set of [semantic-release](https://github.com/semantic-release/semantic-release) plugins for publishing a [Github release](https://help.github.com/articles/about-releases).

[![Travis](https://img.shields.io/travis/semantic-release/github.svg)](https://travis-ci.org/semantic-release/github)
[![Codecov](https://img.shields.io/codecov/c/github/semantic-release/github.svg)](https://codecov.io/gh/semantic-release/github)
[![Greenkeeper badge](https://badges.greenkeeper.io/semantic-release/github.svg)](https://greenkeeper.io/)

## verifyConditions

Verify the presence and the validity of the `githubToken` (set via option or environment variable).

### Options

| Option | Description | Default |
| --------------------- | --------------------------------------------------------- | ------------------------------------------------------ |
| `githubToken` | **Required.** The token used to authenticate with GitHub. | `process.env.GH_TOKEN` or `process.env.GITHUB_TOKEN` |
| `githubUrl` | The GitHub Enterprise endpoint. | `process.env.GH_URL` or `process.env.GITHUB_URL` |
| `githubApiPathPrefix` | The GitHub Enterprise API prefix. | `process.env.GH_PREFIX` or `process.env.GITHUB_PREFIX` |

## publish

Publish a [Github release](https://help.github.com/articles/about-releases).

### Options

| Option | Description | Default |
| --------------------- | --------------------------------------------------------- | ------------------------------------------------------ |
| `githubToken` | **Required.** The token used to authenticate with GitHub. | `process.env.GH_TOKEN` or `process.env.GITHUB_TOKEN` |
| `githubUrl` | The GitHub Enterprise endpoint. | `process.env.GH_URL` or `process.env.GITHUB_URL` |
| `githubApiPathPrefix` | The GitHub Enterprise API prefix. | `process.env.GH_PREFIX` or `process.env.GITHUB_PREFIX` |
| `assets` | An array of files to upload to the release. | -

#### assets option

Each element of the array can be a path to the file or an `object` with the properties:

| Property | Description | Default |
| -------- | ------------------------------------------------------------------------ | ------------------------------------ |
| `path` | **Required.** The file path to upload relative to the project directory. | - |
| `name` | The name of the downloadable file on the Github release. | File name extracted from the `path`. |
| `label` | Short description of the file displayed on the Github release. | - |

## Configuration

The plugins are used by default by [semantic-release](https://github.com/semantic-release/semantic-release) so no specific configuration is requiered if `githubToken`, `githubUrl` and `githubApiPathPrefix` are set via environment variable.

Each individual plugin can be disabled, replaced or used with other plugins in the `package.json`:
```json
{
"release": {
"verifyConditions": ["@semantic-release/github", "verify-other-condition"],
"getLastRelease": "custom-get-last-release",
"publish": [
"custom-publish",
{
"path": "@semantic-release/github",
"assets": [
{"path": "dist/asset.min.css", "label": "CSS distribution"},
{"path": "dist/asset.min.js", "label": "JS distribution"}
]
}
]
}
}
```

The same configuration for Github Enterprise:
```json
{
"release": {
"verifyConditions": [
{
"path": "@semantic-release/github",
"githubUrl": "https://my-ghe.com",
"githubApiPathPrefix": "/api-prefix"
},
"verify-other-condition"
],
"getLastRelease": "custom-get-last-release",
"publish": [
"custom-publish",
{
"path": "@semantic-release/github",
"githubUrl": "https://my-ghe.com",
"githubApiPathPrefix": "/api-prefix",
"assets": [
{"path": "dist/asset.min.css", "label": "CSS distribution"},
{"path": "dist/asset.min.js", "label": "JS distribution"}
]
}
]
}
}
```
30 changes: 30 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const {callbackify} = require('util');
const verifyGithub = require('./lib/verify');
const publishGit = require('./lib/publish');

let verified;

async function verifyConditions(pluginConfig, {pkg, options: {publish}}) {
// If the Github publish plugin is used and has `assets` configured, validate it now in order to prevent any release if the configuration is wrong
if (publish) {
const publishPlugin = (Array.isArray(publish) ? publish : [publish]).find(
config => config.path && config.path === '@semantic-release/github'
);
if (publishPlugin && publishPlugin.assets) {
pluginConfig.assets = publishPlugin.assets;
}
}

await verifyGithub(pluginConfig, pkg);
verified = true;
}

async function publish(pluginConfig, {pkg, nextRelease, options, logger}) {
if (!verified) {
await verifyGithub(pluginConfig, pkg);
verified = true;
}
await publishGit(pluginConfig, options, pkg, nextRelease, logger);
}

module.exports = {verifyConditions: callbackify(verifyConditions), publish: callbackify(publish)};
58 changes: 58 additions & 0 deletions lib/publish.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const {basename} = require('path');
const {parse} = require('url');
const {stat} = require('fs-extra');
const gitUrlParse = require('git-url-parse');
const GitHubApi = require('github');
const resolveConfig = require('./resolve-config');
const pEachSeries = require('p-each-series');
const debug = require('debug')('semantic-release:publish-github');

module.exports = async (pluginConfig, {branch}, {repository}, {version, gitHead, gitTag, notes}, logger) => {
const {githubToken, githubUrl, githubApiPathPrefix, assets} = resolveConfig(pluginConfig);
const {name: repo, owner} = gitUrlParse(repository.url);
let {port, protocol, hostname: host} = githubUrl ? parse(githubUrl) : {};
protocol = (protocol || '').split(':')[0] || null;

const github = new GitHubApi({port, protocol, host, pathPrefix: githubApiPathPrefix});
github.authenticate({type: 'token', token: githubToken});

const release = {owner, repo, tag_name: gitTag, name: gitTag, target_commitish: branch, body: notes};
debug('release owner: %o', owner);
debug('release repo: %o', repo);
debug('release name: %o', gitTag);
debug('release branch: %o', branch);
const ref = `refs/tags/${gitTag}`;

debug('Create git tag %o with commit %o', ref, gitHead);
await github.gitdata.createReference({owner, repo, ref, sha: gitHead});
const {data: {id, html_url}} = await github.repos.createRelease(release);
logger.log('Published Github release: %s', html_url);

if (assets && assets.length > 0) {
// Make requests serially to avoid hitting the rate limit (https://developer.github.com/v3/guides/best-practices-for-integrators/#dealing-with-abuse-rate-limits)
await pEachSeries(assets, async asset => {
const filePath = typeof asset === 'object' ? asset.path : asset;
let file;
try {
file = await stat(filePath);
} catch (err) {
logger.error('The asset %s cannot be read, and will be ignored.', filePath);
return;
}
if (!file || !file.isFile()) {
logger.error('The asset %s is not a file, and will be ignored.', filePath);
return;
}
const fileName = asset.name || basename(filePath);
const upload = {owner, repo, id, filePath, name: fileName};
debug('file path: %o', filePath);
debug('file name: %o', fileName);
if (asset.label) {
upload.label = asset.label;
}

const {data: {browser_download_url}} = await github.repos.uploadAsset(upload);
logger.log('Published file %s', browser_download_url);
});
}
};
6 changes: 6 additions & 0 deletions lib/resolve-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = ({githubToken, githubUrl, githubApiPathPrefix, assets}) => ({
githubToken: githubToken || process.env.GH_TOKEN || process.env.GITHUB_TOKEN,
githubUrl: githubUrl || process.env.GH_URL || process.env.GITHUB_URL,
githubApiPathPrefix: githubApiPathPrefix || process.env.GH_PREFIX || process.env.GITHUB_PREFIX,
assets: assets ? (Array.isArray(assets) ? assets : [assets]) : assets,
});

0 comments on commit 9eb65fb

Please sign in to comment.