Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel910 committed Mar 7, 2018
0 parents commit 7c1d933
Show file tree
Hide file tree
Showing 57 changed files with 8,706 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"presets": [
["babel-preset-env", {
"targets": {
"node": "8.9"
}
}],
"babel-preset-flow"
],
"plugins": [
["babel-plugin-transform-object-rest-spread", {"useBuiltIns": true}],
["babel-plugin-transform-builtin-extend", {"globals": ["Error"]}],
["babel-plugin-transform-runtime"],
["babel-plugin-dynamic-import-node"]
]
}
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/flow-typed
**/lib
**/tests
24 changes: 24 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module.exports = {
parser: "babel-eslint",
extends: "eslint:recommended",
plugins: ["flowtype"],
env: {
mocha: true,
commonjs: true,
node: true,
es6: true
},
parserOptions: {
ecmaVersion: 6,
sourceType: "module"
},
rules: {
"flowtype/define-flow-type": 1,
"no-console": "off"
},
settings: {
flowtype: {
onlyFilesWithFlowAnnotation: true
}
}
};
10 changes: 10 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[ignore]
[include]
[libs]
flow-typed
[lints]
[options]
module.file_ext=.js
esproposal.class_static_fields=enable
suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore
[strict]
39 changes: 39 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Auto detect text files and perform LF normalization

* text=auto

*.html text diff=html
*.css text
*.js text
*.sql text
*.php text
*.md text
*.txt text
*.tpl text diff=html
*.json text
*.pdf diff=astextplain
*.h text
*.sh text
*.less text
*.svg text
*.yml text
*.xml text
Makefile text
Procfile text
*.c text
*.cfg text
*.coffee text
*.ini text

*.png binary
*.jpg binary
*.gif binary
*.bmp binary
*.bz2 binary
*.jar binary
*.jpe binary
*.tar.gz binary
*.zip binary

*.gitattributes text
*.gitignore text
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.idea/
npm-debug.log
node_modules
.DS_Store
coverage
.nyc_output
lib/
5 changes: 5 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
printWidth: 100,
parser: "flow",
tabWidth: 4
};
24 changes: 24 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
language: node_js
sudo: false
node_js:
- "8.9"
cache: yarn
branches:
only:
- master
- development
install:
- yarn install
jobs:
include:
- stage: test
script: yarn ci
- stage: release
if: branch = master
script: yarn build && yarn release
notifications:
slack:
on_success: change
on_failure: always
rooms:
- webiny:rw9auyqJpDRRGcVmFCektrH5
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 Webiny

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.
231 changes: 231 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# webiny-semantic-release

A tool for automated and reliable versioning inspired by `semantic-release`.

- supports single package repositories
- supports monorepo structure (Lerna or custom)
- supports plugins and 100% customizable release/publish process
- supports presets so you can easily share your preset via `npm`
- detailed preview of actual release (dry run) if you want to review what is about to happen

## Why not simply use `semantic-release`?
Kudos to the `semantic-release` team for starting this movement!
We greatly support it and think it is a very important part for a stable open-source ecosystem.

But, the primary problem for us was - it does not support monorepos.
We did try to wrap it with some custom logic to make it work in a monorepo environment but we very soon hit a wall.
We gave up when we had to update versions of inter-dependent packages (when one of the packages in your monorepo depends on another package in te same monorepo).
There were also other problems, but these were the deciding ones.

Still, you can use the existing `semantic-release` plugins!
We use 2 of their plugins to analyze commits and generate release notes by simply wrapping it with our own plugin (read further) and adding some bells and whistles.

## How to use
1. Using CLI.
2. Using custom script via Node.

By default we only allow release from `master` branch.
Also we use a `default` preset which defines a common set of plugins:



| Order | Plugin | Description |
| :---: | :---: | :--- |
| 1. | githubVerify | verifies `GH_TOKEN` or `GITHUB_TOKEN` and repo permissions. |
| 2. | npmVerify | verifies `NPM_TOKEN`. |
| 3. | analyzeCommits | analyzes commit history and determines version type. |
| 4. | updatePackageJson | updates package version and versions of dependencies. |
| 5. | releaseNotes | generates release notes for GitHub release. |
| 6. | githubPublish | publishes a new release to GitHub. |
| 7. | npmPublish | publishes the package to npm. |

If you need a custom set of plugins, either create your own preset as a separate
npm package or define the `plugins` array.


### CLI
```bash
yarn add webiny-semantic-release

webiny-semantic-release [options]

Options:
--branch Allow release only from this branch. [default: "master"]
--ci Execute release ONLY in a CI environment. [default: true]
--project Project type (for custom structures use Node API to configure the
release). [choices: "single", "lerna"]
--preview Run release preview without actually performing a release.
[default: false]
--preset Use given preset as a source of plugins. A preset is a module that
exports `plugins` array (named export). [default: "default"]
--require Require the given module (ex: "babel-register").
--version Show version number [boolean]
--help Show help [boolean]
```

## Node API
To run the release process from JS, import the main function and pass the desired config.

```js
import release, { getSinglePackage, getLernaPackages } from "webiny-semantic-release";
// or whatever form of require you prefer
const {default: release, getSinglePackage, getLernaPackages } = require("webiny-semantic-release");

// NOTE: you are responsible for defining an array of packages!
// This makes this tool very flexible as it does not care about your project structure,
// only about the packages you pass to it.

// For single package repos
// NOTE: config is optional. `process.cwd()` is used as package root by default.
const projectPackages = getSinglePackage({root: process.cwd()});

// For Lerna projects
const projectPackages = getLernaPackages();

// For custom project structures you just need to specify your packages using the following template:
const projectPackages = [
{
name: 'package-1',
location: '/my/project/packages/package-1',
packageJSON: {
// Here goes the ENTIRE content of `package.json` file
name: 'package-1',
version: '0.0.0-semantically-released',
dependencies: {},
// ...
}
}
];

// Run release (returns a Promise)
release({
ci: true,
preview: false,
branch: 'master',
packages: projectPackages,
preset: 'default'
}).catch(e => {
console.log(e);
process.exit(1);
});
```

### Plugin system
Our plugin system is very straightforward. It works almost the same way `express` middleware does.
Under the hood we use the `webiny-compose` package, which is a very simple tool to compose middleware functions using arbitrary `params`, a `next` callback and a `finish` callback. `params` are passed to each consecutive plugin and are mutable so all plugins share data and can modify data.

These Flow types will make everything much clearer:

```flow
declare type Package = {
name: string,
location: string,
packageJSON: Object
};
declare type Params = {
packages: Array<Package>,
logger: { log: Function, error: Function },
config: {
ci: Boolean,
preview: Boolean,
repositoryUrl: string,
branch: string,
tagFormat: string | (pkg: Package) => string
}
};
declare type Plugin = (params: Params, next: Function, finish: Function) => void;
```

### Creating a plugin
To create a plugin you need to defined a simple function of type `Plugin` (see the Flow types above).

Let's create a plugin which checks all the packages for the presence of `README.md` file:

```js
import path from "path";
import fs from "fs";

// I always recommend to export a factory function which can optionally take a config object.
// That way you are safe for possible future upgrades or parameters.
export default () => {
return ({packages, logger}, next, finish) => {
let errors = [];
packages.map(pkg => {
if (!fs.existsSync(path.join(pkg.location, "README.md"))) {
errors.push(pkg.name);
}
});

if (errors.length) {
logger.error(
"Missing README.md file in the following packages:\n\t- %s",
errors.join("\n- ")
);
return finish();
}
next();
}
};
```

### Using built-in plugins + some of your own

We provide a set of plugins to handle publishing to `npm` and `GitHub`.

By default, you don't need to do anything to use them. Just use the `default` preset.

However, if you want to create your own publishing process and maybe remove or add some of the plugins, you can do it like this:


```js
// We will remove the `npm` plugins, and instead add 2 new plugins

import release, {
getSinglePackage,
analyzeCommits,
releaseNotes,
githubVerify,
githubPublish,
} from "webiny-semantic-release";

release({
preview: true,
packages: getSinglePackage(),
plugins: [
githubVerify(),
checkReadmeFile(), // Use your new plugin for README.md verification (see "Creating a plugin")
analyzeCommits(),
releaseNotes(),
// This plugin will modify the release notes of each package and add a custom footer.
// After the `releaseNotes` plugin did its job, each package `nextRelease` will contain a `notes` key with the generated notes.
({packages}, next) => {
packages.map(pkg => {
pkg.nextRelease.notes += "\nI MUST have this at the bottom of each release!"
});
next();
},
// Publish plugin will now use the new `notes` because they were modified by your plugin
githubPublish()
]
});
```

### Creating a preset
A preset is a simple module that exports a `plugins` function and optionally a `packages` function.
Both functions can be synchronous and asynchronous.

```js
export const plugins = () => {
return [
// Whatever plugins you need
];
};

export const packages = () => {
return [
// Packages
];
};
```
Loading

0 comments on commit 7c1d933

Please sign in to comment.