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 20, 2017
1 parent 41ea539 commit 55c1014
Show file tree
Hide file tree
Showing 23 changed files with 1,122 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
49 changes: 47 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,47 @@
# npm
Set of semantic-release plugins to publish to a npm registry
# @semantic-release/npm

Set of [semantic-release](https://github.com/semantic-release/semantic-release) plugins for publishing to a [npm](https://www.npmjs.com/) registry.

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

## verifyConditions

Verify the presence of the `NPM_TOKEN` environment variable, create or update the `.npmrc` file with the token and verify the token is valid.

## getLastRelease

Determine the last release of the package on the `npm` registry.

## publish

Publish the package on the `npm` registry.

## Configuration

For each plugin, the `npm` authentication token has to be configured with the environment variable `NPM_TOKEN`.

All the plugins are based on `npm` and will use the configuration from `.npmrc`. Any parameter returned by `npm config list` will be used by each plugin.

The registry and dist-tag can be configured in the `package.json` and will take precedence on the configuration in `.npmrc`:
```json
{
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"tag": "latest"
}
}
```
The plugins are used by default by [semantic-release](https://github.com/semantic-release/semantic-release) so no specific configuration is requiered to use them.

Each individual plugin can be disabled, replaced or used with other plugins in the `package.json`:
```json
{
"release": {
"verifyConditions": ["@semantic-release/npm", "verify-other-condition"],
"getLastRelease": "custom-get-last-release",
"publish": ["@semantic-release/npm", "custom-publish"]
}
}
```
33 changes: 33 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const {callbackify} = require('util');
const verifyNpm = require('./lib/verify');
const publishNpm = require('./lib/publish');
const getLastReleaseNpm = require('./lib/get-last-release');

let verified;

async function verifyConditions(pluginConfig, {pkg, logger}) {
await verifyNpm(pkg, logger);
verified = true;
}

async function getLastRelease(pluginConfig, {pkg, logger}) {
if (!verified) {
await verifyNpm(pkg, logger);
verified = true;
}
return getLastReleaseNpm(pkg, logger);
}

async function publish(pluginConfig, {pkg, nextRelease: {version}, logger}) {
if (!verified) {
await verifyNpm(pkg, logger);
verified = true;
}
await publishNpm(version, logger);
}

module.exports = {
verifyConditions: callbackify(verifyConditions),
getLastRelease: callbackify(getLastRelease),
publish: callbackify(publish),
};
26 changes: 26 additions & 0 deletions lib/get-client-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module.exports = config => {
// Form https://github.com/npm/npm/blob/d081cc6c8d73f2aa698aab36605377c95e916224/lib/cache/caching-client.js#L194
return {
proxy: {
http: config.get('proxy'),
https: config.get('https-proxy'),
localAddress: config.get('local-address'),
},
ssl: {
certificate: config.get('cert'),
key: config.get('key'),
ca: config.get('ca'),
strict: config.get('strict-ssl'),
},
retry: {
retries: parseInt(config.get('fetch-retries'), 10),
factor: parseInt(config.get('fetch-retry-factor'), 10),
minTimeout: parseInt(config.get('fetch-retry-mintimeout'), 10),
maxTimeout: parseInt(config.get('fetch-retry-maxtimeout'), 10),
},
userAgent: config.get('user-agent'),
defaultTag: config.get('tag'),
couchToken: config.get('_token'),
maxSockets: parseInt(config.get('maxsockets'), 10),
};
};
41 changes: 41 additions & 0 deletions lib/get-last-release.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const {promisify} = require('util');
const {resolve: urlResolve} = require('url');
const npmConf = require('npm-conf');
const RegClient = require('npm-registry-client');
const getClientConfig = require('./get-client-config');
const getRegistry = require('./get-registry');

module.exports = async ({publishConfig, name}, logger) => {
const config = npmConf();
const tag = (publishConfig || {}).tag || config.get('tag');
const {NPM_TOKEN, NPM_USERNAME, NPM_PASSWORD, NPM_EMAIL} = process.env;
const client = new RegClient(getClientConfig(config));
const registry = await getRegistry(publishConfig, name);

try {
const uri = urlResolve(registry, name.replace('/', '%2F'));
const data = await promisify(client.get.bind(client))(uri, {
auth: NPM_TOKEN ? {token: NPM_TOKEN} : {username: NPM_USERNAME, password: NPM_PASSWORD, email: NPM_EMAIL},
});
if (data && !data['dist-tags']) {
logger.log('No version found of package %s found on %s', name, registry);
return {};
}
const distTags = data['dist-tags'];
let version;
if (distTags[tag]) {
version = distTags[tag];
logger.log('Found version %s of package %s with dist-tag %s', version, name, tag);
} else {
version = distTags.latest;
logger.log('Found version %s of package %s with dist-tag %s', version, name, 'latest');
}
return {version, gitHead: data.versions[version].gitHead};
} catch (err) {
if (err.statusCode === 404 || /not found/i.test(err.message)) {
logger.log('No version found of package %s found on %s', name, registry);
return {};
}
throw err;
}
};
4 changes: 4 additions & 0 deletions lib/get-registry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const getRegistryUrl = require('registry-auth-token/registry-url');

module.exports = async (publishConfig, name) =>
publishConfig && publishConfig.registry ? publishConfig.registry : getRegistryUrl(name.split('/')[0]);
10 changes: 10 additions & 0 deletions lib/publish.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const execa = require('execa');
const updatePackageVersion = require('./update-package-version');

module.exports = async (version, logger) => {
await updatePackageVersion(version, logger);

logger.log('Publishing version %s to npm registry', version);
const shell = await execa('npm', ['publish']);
process.stdout.write(shell.stdout);
};
29 changes: 29 additions & 0 deletions lib/set-npmrc-auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const {appendFile} = require('fs-extra');
const getAuthToken = require('registry-auth-token');
const nerfDart = require('nerf-dart');
const SemanticReleaseError = require('@semantic-release/error');
const getRegistry = require('./get-registry');

module.exports = async ({publishConfig, name}, logger) => {
const registry = await getRegistry(publishConfig, name);
logger.log('Verify authentication for registry %s', registry);
const {NPM_TOKEN, NPM_USERNAME, NPM_PASSWORD, NPM_EMAIL} = process.env;

if (getAuthToken(registry)) {
return;
}
if (NPM_USERNAME && NPM_PASSWORD && NPM_EMAIL) {
// Using the old auth token format is not considered part of the public API
// This might go away anytime (i.e. once we have a better testing strategy)
await appendFile(
'./.npmrc',
`\n_auth = ${Buffer.from(`${NPM_USERNAME}:${NPM_PASSWORD}`, 'utf8').toString('base64')}\nemail = \${NPM_EMAIL}`
);
logger.log('Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to .npmrc.');
} else if (NPM_TOKEN) {
await appendFile('./.npmrc', `\n${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);
logger.log('Wrote NPM_TOKEN to .npmrc.');
} else {
throw new SemanticReleaseError('No npm token specified.', 'ENONPMTOKEN');
}
};
15 changes: 15 additions & 0 deletions lib/update-package-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const {readJson, writeJson, pathExists} = require('fs-extra');

module.exports = async (version, logger) => {
const pkg = await readJson('./package.json');

await writeJson('./package.json', Object.assign(pkg, {version}));
logger.log('Wrote version %s to package.json', version);

if (await pathExists('./npm-shrinkwrap.json')) {
const shrinkwrap = await readJson('./npm-shrinkwrap.json');
shrinkwrap.version = version;
await writeJson('./npm-shrinkwrap.json', shrinkwrap);
logger.log('Wrote version %s to npm-shrinkwrap.json', version);
}
};
7 changes: 7 additions & 0 deletions lib/verify-pkg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const SemanticReleaseError = require('@semantic-release/error');

module.exports = ({name}) => {
if (!name) {
throw new SemanticReleaseError('No "name" found in package.json.', 'ENOPKGNAME');
}
};
Loading

0 comments on commit 55c1014

Please sign in to comment.