-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 8504f9b
Showing
7 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pkg/ | ||
.bundle/ | ||
Gemfile.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
source 'https://rubygems.org/' | ||
|
||
gemspec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
This very simple gem provides a `git release` command, which will | ||
automatically fill out any and all "release tags" into fully-blown "Github | ||
Releases", complete with release notes, a heading, and all the other good | ||
things in life. | ||
|
||
Using this gem, you can turn the following tag annotation: | ||
|
||
First Release | ||
|
||
It is with much fanfare and blowing of horns that I bequeath the | ||
awesomeness of `git release` upon the world. | ||
|
||
Features in this release include: | ||
|
||
* Ability to create a release from a tag annotation or commit message; | ||
* Automatically generates an OAuth token if needed; | ||
* Feeds your cat while you're hacking(*) | ||
|
||
You should install it now! `gem install github-release` | ||
|
||
Into [this](https://github.com/mpalmer/github-release/releases/tag/v0.1.0) | ||
simply by running | ||
|
||
git release | ||
|
||
|
||
# Installation | ||
|
||
Simply install the gem: | ||
|
||
gem install github-release | ||
|
||
|
||
# Usage | ||
|
||
Using `git release` is very simple. Just make sure that your `origin` | ||
remote points to your Github repo, and then run `git release`. All tags | ||
that look like a "version tag" (see "Configuration", below) will be created | ||
as Github releases (if they don't already exist) and the message from the | ||
tag will be used as the release notes. | ||
|
||
The format of the release notes is quite straightforward -- the first line | ||
of the message associated with the commit will be used as the "name" of the | ||
release, with the rest of the message used as the "body" of the release. | ||
The body will be interpreted as Github-flavoured markdown, so if you'd like | ||
to get fancy, go for your life. | ||
|
||
The message associated with the "release tag" is either the tag's annotation | ||
message (if it is an annotated tag) or else the commit log of the commit on | ||
which the tag is placed. I *strongly* recommend annotated tags (but then | ||
again, [I'm biased...](http://theshed.hezmatt.org/git-version-bump)) | ||
|
||
The first time you use `git release`, it will ask you for your Github | ||
username and password. This is used to request an OAuth token to talk to | ||
the Github API, which is then stored in your global git config. Hence you | ||
*shouldn't* be asked for your credentials every time you use `git release`. | ||
If you need to use multiple github accounts for different repos, you can | ||
override the `release.api-token` config parameter in your repo configuration | ||
(but you'll have to get your own OAuth token). | ||
|
||
|
||
# Configuration | ||
|
||
There are a few things you can configure to make `git release` work slightly | ||
differently. None of them should be required for normal, sane use. | ||
|
||
* `release.remote` (default `origin`) -- The name of the remote which is | ||
used to determine what github repository to send release notes to. | ||
|
||
* `release.api-token` (default is runtime generated) -- The OAuth token | ||
to use to authenticate access to the Github API. When you first run `git | ||
release`, you'll be prompted for a username and password to use to | ||
generate an initial token; if you need to override it on a per-repo | ||
basis, this is the key you'll use. | ||
|
||
* `release.tag-regex` (default `v\d+\.\d+(\.\d+)?$`) -- The regular | ||
expression to filter which tags denote releases, as opposed to other tags | ||
you might have decided to make. Only tags which match this regular | ||
expression will be pushed up by `git release`, and only those tags will | ||
be marked as releases. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
require 'rubygems' | ||
require 'bundler' | ||
|
||
begin | ||
Bundler.setup(:default, :development) | ||
rescue Bundler::BundlerError => e | ||
$stderr.puts e.message | ||
$stderr.puts "Run `bundle install` to install missing gems" | ||
exit e.status_code | ||
end | ||
|
||
Bundler::GemHelper.install_tasks | ||
|
||
require 'rdoc/task' | ||
|
||
Rake::RDocTask.new do |rd| | ||
rd.main = "README.md" | ||
rd.title = 'github-release' | ||
rd.rdoc_files.include("README.md", "lib/**/*.rb") | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/usr/bin/env ruby | ||
|
||
require 'github-release' | ||
|
||
GithubRelease.new.run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
require 'git-version-bump' | ||
|
||
Gem::Specification.new do |s| | ||
s.name = "github-release" | ||
|
||
s.version = GVB.version | ||
s.date = GVB.date | ||
|
||
s.platform = Gem::Platform::RUBY | ||
|
||
s.homepage = "http://theshed.hezmatt.org/github-release" | ||
s.summary = "Upload tag annotations to github" | ||
s.authors = ["Matt Palmer"] | ||
|
||
s.extra_rdoc_files = ["README.md"] | ||
s.files = `git ls-files`.split("\n") | ||
s.executables = ["git-release"] | ||
|
||
s.add_dependency 'octokit' | ||
s.add_dependency 'git-version-bump' | ||
|
||
s.add_development_dependency 'rake' | ||
s.add_development_dependency 'bundler' | ||
s.add_development_dependency 'rdoc' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
require 'octokit' | ||
require 'io/console' | ||
|
||
class GithubRelease | ||
def run | ||
new_releases = tagged_releases.select { |r| !github_releases.include?(r) } | ||
|
||
if new_releases.empty? | ||
puts "No new release tags to push." | ||
end | ||
|
||
new_releases.each { |t| create_release(t) } | ||
|
||
puts "All done!" | ||
end | ||
|
||
private | ||
def api | ||
@api ||= Octokit::Client.new(:access_token => token) | ||
end | ||
|
||
def token | ||
@token ||= begin | ||
# We cannot use the 'defaults' functionality of git_config here, | ||
# because get_new_token would be evaluated before git_config ran | ||
git_config("release.api-token") || get_new_token | ||
end | ||
|
||
log_val(@token) | ||
end | ||
|
||
def get_new_token | ||
puts "Requesting a new OAuth token from Github..." | ||
print "Github username: " | ||
user = $stdin.gets.chomp | ||
print "Github password: " | ||
pass = $stdin.noecho(&:gets).chomp | ||
puts | ||
|
||
api = Octokit::Client.new(:login => user, :password => pass) | ||
begin | ||
res = api.create_authorization(:scopes => [:repo], :note => "git release") | ||
rescue Octokit::Unauthorized | ||
puts "Username or password incorrect. Please try again." | ||
return get_new_token | ||
end | ||
|
||
token = res[:token] | ||
|
||
system("git config --global release.api-token '#{token}'") | ||
|
||
log_val(token) | ||
end | ||
|
||
def tag_regex | ||
@tag_regex ||= `git config --get release.tag-regex`.strip | ||
@tag_regex = /^v\d+\.\d+(\.\d+)?$/ if @tag_regex.empty? | ||
log_val(@tag_regex) | ||
end | ||
|
||
def tagged_releases | ||
@tagged_releases ||= `git tag`.split("\n").map(&:strip).grep tag_regex | ||
log_val(@tagged_releases) | ||
end | ||
|
||
def repo_name | ||
@repo_name ||= begin | ||
case repo_url | ||
when %r{^https://github.com/([^/]+/[^/]+)} | ||
return $1.gsub(/\.git$/, '') | ||
when %r{^(?:git@)?github\.com:([^/]+/[^/]+)} | ||
return $1.gsub(/\.git$/, '') | ||
else | ||
raise RuntimeError, | ||
"I cannot recognise the format of the push URL for remote #{remote_name} (#{repo_url})" | ||
end | ||
end | ||
log_val(@repo_name) | ||
end | ||
|
||
def repo_url | ||
@repo_url ||= begin | ||
git_config("remote.#{remote_name}.pushurl") || git_config("remote.#{remote_name}.url") | ||
end | ||
log_val(@repo_url) | ||
end | ||
|
||
def remote_name | ||
@remote_name ||= git_config("release.remote", "origin") | ||
log_val(@remote_name) | ||
end | ||
|
||
def github_releases | ||
@github_releases ||= api.repo(repo_name).rels[:releases].get.data.map(&:tag_name) | ||
log_val(@github_releases) | ||
end | ||
|
||
def git_config(item, default = nil) | ||
@config_cache ||= {} | ||
|
||
@config_cache[item] ||= begin | ||
v = `git config #{item}`.strip | ||
v.empty? ? default : v | ||
end | ||
|
||
log_val(@config_cache[item], item) | ||
end | ||
|
||
def create_release(tag) | ||
print "Creating a release for #{tag}..." | ||
system("git push #{remote_name} tag #{tag} >/dev/null") | ||
|
||
msg = `git tag -l -n1000 '#{tag}'` | ||
|
||
# Ye ghods is is a horrific format to parse | ||
name, body = msg.split("\n", 2) | ||
name = name.gsub(/^#{tag}/, '').strip | ||
body = body.split("\n").map { |l| l.sub(/^ /, '') }.join("\n") | ||
|
||
api.create_release(repo_name, tag, :name => name, :body => body) | ||
|
||
puts " done!" | ||
end | ||
|
||
def log_val(v, note = nil) | ||
return v unless $DEBUG | ||
|
||
calling_func = caller[0].split("`")[-1].sub(/'$/, '') | ||
|
||
print "#{note}: " if note | ||
puts "#{calling_func} => #{v.inspect}" | ||
|
||
v | ||
end | ||
end |