Skip to content

Commit

Permalink
Merge pull request #12 from kylef/kylef/release-config
Browse files Browse the repository at this point in the history
Support Releaser Configuration
  • Loading branch information
kylef committed Aug 28, 2017
2 parents 1268ac6 + 0a37873 commit 0f379d1
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 72 deletions.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Maintain supports the following releasers:
.. toctree::
:maxdepth: 1

release/hooks
release/git
release/github
release/version-file
Expand Down
9 changes: 9 additions & 0 deletions docs/release/git.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# git Releaser

## Configuration

```yaml
release:
git:
commit_format: Release {version}
tag_format: {version}
```

## Detect

Detects Git repositories.
Expand Down
19 changes: 19 additions & 0 deletions docs/release/hooks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Hooks Releaser

By creating a `.maintain.yml` file in the root of your repository, you can add
hooks to various stages of the release process.

```yaml
release:
hooks:
bump:
pre:
- echo 'version will be bumped'
post:
- echo 'version was bumped'
release:
pre:
- echo 'version will be released'
post:
- echo 'version was released'
```
38 changes: 0 additions & 38 deletions docs/release/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,41 +49,3 @@ $ maintain release 0.3.1 --no-bump
- [CocoaPods](https://cocoapods.org)
- [NPM](https://www.npmjs.com)
- [RubyGems](https://rubygems.org)

#### Custom Hooks

By creating a `.maintain.yml` file in the root of your repository, you can add
hooks to various stages of the release process.

```yaml
release:
bump:
pre:
- echo 'version will be bumped'
post:
- echo 'version was bumped'
release:
pre:
post:
```

You may also disable a releaser from bumping, for example, if your
configuration file reads the version from a `VERSION` file instead of having a
copy.

```yaml
release:
bump:
disable:
- CocoaPods
```

Custom bumping files:

```yaml
release:
bump:
file:
- filenmae.rb
foo
```
20 changes: 1 addition & 19 deletions maintain/commands/release.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import subprocess

import click
from click.exceptions import MissingParameter
Expand All @@ -23,7 +22,7 @@ def release(version, dry_run, bump, pull_request):
else:
config = {}

releaser = AggregateReleaser()
releaser = AggregateReleaser(config.get('release', {}))

git_releasers = filter(lambda releaser: isinstance(releaser, GitReleaser), releaser.releasers)
github_releasers = filter(lambda releaser: isinstance(releaser, GitHubReleaser), releaser.releasers)
Expand Down Expand Up @@ -82,9 +81,7 @@ def release(version, dry_run, bump, pull_request):
ref = git_releaser.repo.create_head(branch, git_releaser.repo.head)
git_releaser.repo.head.set_reference(ref)

execute_hooks('bump', 'pre', config)
releaser.bump(version)
execute_hooks('bump', 'post', config)

if not dry_run:
if git_releaser.has_origin():
Expand All @@ -94,9 +91,7 @@ def release(version, dry_run, bump, pull_request):
github_releaser.create_pull_request(version)

if not dry_run and not pull_request:
execute_hooks('publish', 'pre', config)
releaser.release(version)
execute_hooks('publish', 'post', config)


def bump_version(version, bump):
Expand All @@ -106,16 +101,3 @@ def bump_version(version, bump):
exit(1)

return getattr(version, 'next_{}'.format(bump))()


def execute_hooks(phase, action, config):
release_config = config.get('release', {})
phase_config = release_config.get(phase, {})
hooks = phase_config.get(action, [])

if len(hooks) > 0:
click.echo('Running {} {} hooks'.format(phase, action))

for hook in hooks:
click.echo('- ' + hook)
subprocess.check_output(hook, shell=True)
39 changes: 33 additions & 6 deletions maintain/release/aggregate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from maintain.release.base import Releaser
from maintain.release.hooks import HookReleaser
from maintain.release.version_file import VersionFileReleaser
from maintain.release.python import PythonReleaser
from maintain.release.cocoapods import CocoaPodsReleaser
Expand All @@ -17,6 +18,7 @@ def releasers(cls):
"""

return [
HookReleaser,
VersionFileReleaser,
PythonReleaser,
CocoaPodsReleaser,
Expand All @@ -28,21 +30,27 @@ def releasers(cls):
]

@classmethod
def detected_releasers(cls):
def detected_releasers(cls, config):
"""
Returns all of the releasers that are compatible with the project.
"""

def get_config(releaser):
if config:
return config.get(releaser.name.lower(), {})

return {}

releasers_cls = filter(lambda r: r.detect(), cls.releasers())
releasers = map(lambda r: r(), releasers_cls)
releasers = map(lambda r: r(get_config(r)), releasers_cls)
return list(releasers)

@classmethod
def detect(cls):
return len(cls.detected_releasers()) > 0

def __init__(self, releasers=None):
self.releasers = releasers or self.detected_releasers()
def __init__(self, config=None, releasers=None):
self.releasers = releasers or self.detected_releasers(config)
self.check_version_consistency()

def check_version_consistency(self):
Expand All @@ -54,7 +62,10 @@ def check_version_consistency(self):
releaser_name = None

for releaser in self.releasers:
next_version = releaser.determine_current_version()
try:
next_version = releaser.determine_current_version()
except NotImplementedError:
continue

if next_version and version and version != next_version:
raise Exception('Inconsistent versions, {} is at {} but {} is at {}.'.format(
Expand All @@ -64,7 +75,11 @@ def check_version_consistency(self):
releaser_name = releaser.name

def determine_current_version(self):
return self.releasers[0].determine_current_version()
for releaser in self.releasers:
try:
return releaser.determine_current_version()
except NotImplementedError:
continue

def determine_next_version(self):
version = None
Expand All @@ -85,9 +100,21 @@ def determine_next_version(self):
return version

def bump(self, new_version):
for releaser in self.releasers:
releaser.pre_bump(new_version)

for releaser in self.releasers:
releaser.bump(new_version)

for releaser in self.releasers:
releaser.post_bump(new_version)

def release(self, new_version):
for releaser in self.releasers:
releaser.pre_release(new_version)

for releaser in self.releasers:
releaser.release(new_version)

for releaser in self.releasers:
releaser.post_release(new_version)
35 changes: 31 additions & 4 deletions maintain/release/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,58 @@ def detect(cls):

return False

def __init__(self, config=None):
pass

def determine_current_version(self):
"""
Called to determine the current version number.
"""
raise NotImplemented
raise NotImplementedError()

def determine_next_version(self):
"""
Called to determine the next version number.
"""
return None

def pre_bump(self, new_version):
"""
Called before bumping the version.
"""
pass

def bump(self, new_version):
"""
Called to bump the version number in the project.
After called, ``determine_current_version()`` should return
the new version.
"""

raise NotImplemented
raise NotImplementedError()

def post_bump(self, new_version):
"""
Called after bumping the version.
"""
pass

def release(self):
def pre_release(self, new_version):
"""
Called before releasing the version.
"""
pass

def release(self, new_version):
"""
This method is called to perform actual release actions
such as submission to a package manager.
"""

raise NotImplemented
raise NotImplementedError()

def post_release(self, new_version):
"""
Called after releasing the version.
"""
pass
11 changes: 7 additions & 4 deletions maintain/release/git_releaser.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ class GitReleaser(Releaser):
def detect(cls):
return os.path.exists('.git')

def __init__(self):
def __init__(self, config=None):
self.repo = Repo()

self.commit_format = (config or {}).get('commit_format', 'Release {version}')
self.tag_format = (config or {}).get('tag_format', '{version}')

if self.repo.head.ref != self.repo.heads.master:
# TODO: Support releasing from stable/hotfix branches
raise Exception('You need to be on the `master` branch in order to do a release.')
Expand All @@ -42,12 +45,12 @@ def determine_current_version(self):

def bump(self, new_version):
if self.repo.is_dirty():
message = 'Release {}'.format(new_version)
self.repo.index.add('*')
self.repo.index.commit(message)
self.repo.index.commit(self.commit_format.format(version=new_version))

def release(self, version):
tag = self.repo.create_tag(str(version), message='Release {}'.format(version))
tag_name = self.tag_format.format(version=version)
tag = self.repo.create_tag(tag_name, message='Release {}'.format(version))

if self.has_origin():
self.repo.remotes.origin.push(tag)
Expand Down
2 changes: 1 addition & 1 deletion maintain/release/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def detect(cls):

return url.startswith('https://github.com') or url.startswith('git@github.com')

def __init__(self):
def __init__(self, config):
if not cmd_exists('hub'):
raise Exception('GitHub releases require hub. Missing dependency for hub: https://github.com/github/hub. Please install `hub` and try again.')

Expand Down
49 changes: 49 additions & 0 deletions maintain/release/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import subprocess

import click

from maintain.release.base import Releaser


class HookReleaser(Releaser):
name = 'Hooks'

@classmethod
def detect(cls):
return True

def __init__(self, config):
self.bump_commands = []
self.release_commands = []

self.pre_bump_commands = config.get('bump', {}).get('pre', [])
self.post_bump_commands = config.get('bump', {}).get('post', [])

self.pre_release_commands = config.get('publish', {}).get('pre', [])
self.post_release_commands = config.get('publish', {}).get('post', [])

def pre_bump(self, new_version):
self.execute_hooks('pre bump', self.pre_bump_commands)

def bump(self, new_version):
self.execute_hooks('bump', self.bump_commands)

def post_bump(self, new_version):
self.execute_hooks('post bump', self.post_bump_commands)

def pre_release(self, new_version):
self.execute_hooks('pre release', self.pre_release_commands)

def release(self, new_version):
self.execute_hooks('release', self.release_commands)

def post_release(self, new_version):
self.execute_hooks('post release', self.post_release_commands)

def execute_hooks(self, phase, commands):
if len(commands) > 0:
click.echo('Running {} hooks'.format(phase))

for hook in commands:
click.echo('- ' + hook)
subprocess.check_output(hook, shell=True)

0 comments on commit 0f379d1

Please sign in to comment.