github-release 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ pkg/
2
+ .bundle/
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gemspec
@@ -0,0 +1,80 @@
1
+ This very simple gem provides a `git release` command, which will
2
+ automatically fill out any and all "release tags" into fully-blown "Github
3
+ Releases", complete with release notes, a heading, and all the other good
4
+ things in life.
5
+
6
+ Using this gem, you can turn the following tag annotation:
7
+
8
+ First Release
9
+
10
+ It is with much fanfare and blowing of horns that I bequeath the
11
+ awesomeness of `git release` upon the world.
12
+
13
+ Features in this release include:
14
+
15
+ * Ability to create a release from a tag annotation or commit message;
16
+ * Automatically generates an OAuth token if needed;
17
+ * Feeds your cat while you're hacking(*)
18
+
19
+ You should install it now! `gem install github-release`
20
+
21
+ Into [this](https://github.com/mpalmer/github-release/releases/tag/v0.1.0)
22
+ simply by running
23
+
24
+ git release
25
+
26
+
27
+ # Installation
28
+
29
+ Simply install the gem:
30
+
31
+ gem install github-release
32
+
33
+
34
+ # Usage
35
+
36
+ Using `git release` is very simple. Just make sure that your `origin`
37
+ remote points to your Github repo, and then run `git release`. All tags
38
+ that look like a "version tag" (see "Configuration", below) will be created
39
+ as Github releases (if they don't already exist) and the message from the
40
+ tag will be used as the release notes.
41
+
42
+ The format of the release notes is quite straightforward -- the first line
43
+ of the message associated with the commit will be used as the "name" of the
44
+ release, with the rest of the message used as the "body" of the release.
45
+ The body will be interpreted as Github-flavoured markdown, so if you'd like
46
+ to get fancy, go for your life.
47
+
48
+ The message associated with the "release tag" is either the tag's annotation
49
+ message (if it is an annotated tag) or else the commit log of the commit on
50
+ which the tag is placed. I *strongly* recommend annotated tags (but then
51
+ again, [I'm biased...](http://theshed.hezmatt.org/git-version-bump))
52
+
53
+ The first time you use `git release`, it will ask you for your Github
54
+ username and password. This is used to request an OAuth token to talk to
55
+ the Github API, which is then stored in your global git config. Hence you
56
+ *shouldn't* be asked for your credentials every time you use `git release`.
57
+ If you need to use multiple github accounts for different repos, you can
58
+ override the `release.api-token` config parameter in your repo configuration
59
+ (but you'll have to get your own OAuth token).
60
+
61
+
62
+ # Configuration
63
+
64
+ There are a few things you can configure to make `git release` work slightly
65
+ differently. None of them should be required for normal, sane use.
66
+
67
+ * `release.remote` (default `origin`) -- The name of the remote which is
68
+ used to determine what github repository to send release notes to.
69
+
70
+ * `release.api-token` (default is runtime generated) -- The OAuth token
71
+ to use to authenticate access to the Github API. When you first run `git
72
+ release`, you'll be prompted for a username and password to use to
73
+ generate an initial token; if you need to override it on a per-repo
74
+ basis, this is the key you'll use.
75
+
76
+ * `release.tag-regex` (default `v\d+\.\d+(\.\d+)?$`) -- The regular
77
+ expression to filter which tags denote releases, as opposed to other tags
78
+ you might have decided to make. Only tags which match this regular
79
+ expression will be pushed up by `git release`, and only those tags will
80
+ be marked as releases.
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+
12
+ Bundler::GemHelper.install_tasks
13
+
14
+ require 'rdoc/task'
15
+
16
+ Rake::RDocTask.new do |rd|
17
+ rd.main = "README.md"
18
+ rd.title = 'github-release'
19
+ rd.rdoc_files.include("README.md", "lib/**/*.rb")
20
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'github-release'
4
+
5
+ GithubRelease.new.run
@@ -0,0 +1,25 @@
1
+ require 'git-version-bump'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "github-release"
5
+
6
+ s.version = GVB.version
7
+ s.date = GVB.date
8
+
9
+ s.platform = Gem::Platform::RUBY
10
+
11
+ s.homepage = "http://theshed.hezmatt.org/github-release"
12
+ s.summary = "Upload tag annotations to github"
13
+ s.authors = ["Matt Palmer"]
14
+
15
+ s.extra_rdoc_files = ["README.md"]
16
+ s.files = `git ls-files`.split("\n")
17
+ s.executables = ["git-release"]
18
+
19
+ s.add_dependency 'octokit'
20
+ s.add_dependency 'git-version-bump'
21
+
22
+ s.add_development_dependency 'rake'
23
+ s.add_development_dependency 'bundler'
24
+ s.add_development_dependency 'rdoc'
25
+ end
@@ -0,0 +1,135 @@
1
+ require 'octokit'
2
+ require 'io/console'
3
+
4
+ class GithubRelease
5
+ def run
6
+ new_releases = tagged_releases.select { |r| !github_releases.include?(r) }
7
+
8
+ if new_releases.empty?
9
+ puts "No new release tags to push."
10
+ end
11
+
12
+ new_releases.each { |t| create_release(t) }
13
+
14
+ puts "All done!"
15
+ end
16
+
17
+ private
18
+ def api
19
+ @api ||= Octokit::Client.new(:access_token => token)
20
+ end
21
+
22
+ def token
23
+ @token ||= begin
24
+ # We cannot use the 'defaults' functionality of git_config here,
25
+ # because get_new_token would be evaluated before git_config ran
26
+ git_config("release.api-token") || get_new_token
27
+ end
28
+
29
+ log_val(@token)
30
+ end
31
+
32
+ def get_new_token
33
+ puts "Requesting a new OAuth token from Github..."
34
+ print "Github username: "
35
+ user = $stdin.gets.chomp
36
+ print "Github password: "
37
+ pass = $stdin.noecho(&:gets).chomp
38
+ puts
39
+
40
+ api = Octokit::Client.new(:login => user, :password => pass)
41
+ begin
42
+ res = api.create_authorization(:scopes => [:repo], :note => "git release")
43
+ rescue Octokit::Unauthorized
44
+ puts "Username or password incorrect. Please try again."
45
+ return get_new_token
46
+ end
47
+
48
+ token = res[:token]
49
+
50
+ system("git config --global release.api-token '#{token}'")
51
+
52
+ log_val(token)
53
+ end
54
+
55
+ def tag_regex
56
+ @tag_regex ||= `git config --get release.tag-regex`.strip
57
+ @tag_regex = /^v\d+\.\d+(\.\d+)?$/ if @tag_regex.empty?
58
+ log_val(@tag_regex)
59
+ end
60
+
61
+ def tagged_releases
62
+ @tagged_releases ||= `git tag`.split("\n").map(&:strip).grep tag_regex
63
+ log_val(@tagged_releases)
64
+ end
65
+
66
+ def repo_name
67
+ @repo_name ||= begin
68
+ case repo_url
69
+ when %r{^https://github.com/([^/]+/[^/]+)}
70
+ return $1.gsub(/\.git$/, '')
71
+ when %r{^(?:git@)?github\.com:([^/]+/[^/]+)}
72
+ return $1.gsub(/\.git$/, '')
73
+ else
74
+ raise RuntimeError,
75
+ "I cannot recognise the format of the push URL for remote #{remote_name} (#{repo_url})"
76
+ end
77
+ end
78
+ log_val(@repo_name)
79
+ end
80
+
81
+ def repo_url
82
+ @repo_url ||= begin
83
+ git_config("remote.#{remote_name}.pushurl") || git_config("remote.#{remote_name}.url")
84
+ end
85
+ log_val(@repo_url)
86
+ end
87
+
88
+ def remote_name
89
+ @remote_name ||= git_config("release.remote", "origin")
90
+ log_val(@remote_name)
91
+ end
92
+
93
+ def github_releases
94
+ @github_releases ||= api.repo(repo_name).rels[:releases].get.data.map(&:tag_name)
95
+ log_val(@github_releases)
96
+ end
97
+
98
+ def git_config(item, default = nil)
99
+ @config_cache ||= {}
100
+
101
+ @config_cache[item] ||= begin
102
+ v = `git config #{item}`.strip
103
+ v.empty? ? default : v
104
+ end
105
+
106
+ log_val(@config_cache[item], item)
107
+ end
108
+
109
+ def create_release(tag)
110
+ print "Creating a release for #{tag}..."
111
+ system("git push #{remote_name} tag #{tag} >/dev/null")
112
+
113
+ msg = `git tag -l -n1000 '#{tag}'`
114
+
115
+ # Ye ghods is is a horrific format to parse
116
+ name, body = msg.split("\n", 2)
117
+ name = name.gsub(/^#{tag}/, '').strip
118
+ body = body.split("\n").map { |l| l.sub(/^ /, '') }.join("\n")
119
+
120
+ api.create_release(repo_name, tag, :name => name, :body => body)
121
+
122
+ puts " done!"
123
+ end
124
+
125
+ def log_val(v, note = nil)
126
+ return v unless $DEBUG
127
+
128
+ calling_func = caller[0].split("`")[-1].sub(/'$/, '')
129
+
130
+ print "#{note}: " if note
131
+ puts "#{calling_func} => #{v.inspect}"
132
+
133
+ v
134
+ end
135
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: github-release
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matt Palmer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: octokit
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: git-version-bump
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rdoc
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description:
95
+ email:
96
+ executables:
97
+ - git-release
98
+ extensions: []
99
+ extra_rdoc_files:
100
+ - README.md
101
+ files:
102
+ - .gitignore
103
+ - Gemfile
104
+ - README.md
105
+ - Rakefile
106
+ - bin/git-release
107
+ - github-release.gemspec
108
+ - lib/github-release.rb
109
+ homepage: http://theshed.hezmatt.org/github-release
110
+ licenses: []
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ segments:
122
+ - 0
123
+ hash: -1742702825423313202
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ segments:
131
+ - 0
132
+ hash: -1742702825423313202
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 1.8.23
136
+ signing_key:
137
+ specification_version: 3
138
+ summary: Upload tag annotations to github
139
+ test_files: []