gem-release 1.0.0 → 2.0.0.dev.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +72 -0
  4. data/{lib/gem_release/templates/LICENSE → MIT-LICENSE.md} +3 -2
  5. data/README.md +646 -0
  6. data/README.md.erb +159 -0
  7. data/TODO.txt +194 -0
  8. data/lib/gem/release.rb +13 -0
  9. data/lib/gem/release/cmds.rb +16 -0
  10. data/lib/gem/release/cmds/base.rb +101 -0
  11. data/lib/gem/release/cmds/bootstrap.rb +222 -0
  12. data/lib/gem/release/cmds/bump.rb +165 -0
  13. data/lib/gem/release/cmds/gemspec.rb +85 -0
  14. data/lib/gem/release/cmds/release.rb +100 -0
  15. data/lib/gem/release/cmds/runner.rb +52 -0
  16. data/lib/gem/release/cmds/tag.rb +93 -0
  17. data/lib/gem/release/config.rb +42 -0
  18. data/lib/gem/release/config/env.rb +49 -0
  19. data/lib/gem/release/config/files.rb +35 -0
  20. data/lib/gem/release/context.rb +76 -0
  21. data/lib/gem/release/context/gem.rb +28 -0
  22. data/lib/gem/release/context/gemspec.rb +41 -0
  23. data/lib/gem/release/context/paths.rb +87 -0
  24. data/lib/gem/release/context/system.rb +36 -0
  25. data/lib/gem/release/data.rb +60 -0
  26. data/lib/gem/release/files/template.rb +61 -0
  27. data/lib/gem/release/files/template/context.rb +30 -0
  28. data/lib/gem/release/files/templates.rb +59 -0
  29. data/lib/gem/release/files/templates/group.rb +47 -0
  30. data/lib/gem/release/files/version.rb +68 -0
  31. data/lib/gem/release/helper.rb +45 -0
  32. data/lib/gem/release/helper/hash.rb +35 -0
  33. data/lib/gem/release/helper/string.rb +43 -0
  34. data/lib/gem/release/support/gem_command.rb +67 -0
  35. data/lib/gem/release/support/registry.rb +59 -0
  36. data/lib/gem/release/templates/.gitignore +8 -0
  37. data/lib/gem/release/templates/Gemfile +3 -0
  38. data/lib/gem/release/templates/gemspec +19 -0
  39. data/lib/gem/release/templates/licenses/mit.md +21 -0
  40. data/lib/gem/release/templates/licenses/mpl-2.md +373 -0
  41. data/lib/gem/release/templates/main.rb +1 -0
  42. data/lib/gem/release/templates/rspec/.rspec +3 -0
  43. data/lib/gem/release/templates/rspec/spec/spec_helper.rb +4 -0
  44. data/lib/gem/release/templates/travis/.travis.yml +1 -0
  45. data/lib/gem/release/templates/version.rb +1 -0
  46. data/lib/gem/release/version.rb +5 -0
  47. data/lib/gem/release/version/number.rb +102 -0
  48. data/lib/rubygems/commands/bootstrap_command.rb +4 -93
  49. data/lib/rubygems/commands/bump_command.rb +4 -110
  50. data/lib/rubygems/commands/gemspec_command.rb +5 -29
  51. data/lib/rubygems/commands/release_command.rb +4 -70
  52. data/lib/rubygems/commands/tag_command.rb +5 -46
  53. data/lib/rubygems_plugin.rb +8 -2
  54. metadata +56 -83
  55. data/lib/core_ext/hash/symbolize_keys.rb +0 -13
  56. data/lib/core_ext/kernel/silence.rb +0 -18
  57. data/lib/core_ext/string/camelize.rb +0 -5
  58. data/lib/gem_release.rb +0 -10
  59. data/lib/gem_release/command_options.rb +0 -31
  60. data/lib/gem_release/configuration.rb +0 -33
  61. data/lib/gem_release/gemspec_template.rb +0 -35
  62. data/lib/gem_release/helpers.rb +0 -104
  63. data/lib/gem_release/template.rb +0 -52
  64. data/lib/gem_release/templates/Gemfile +0 -3
  65. data/lib/gem_release/templates/README.md +0 -1
  66. data/lib/gem_release/templates/Rakefile +0 -10
  67. data/lib/gem_release/templates/gemspec +0 -18
  68. data/lib/gem_release/templates/gitignore +0 -25
  69. data/lib/gem_release/templates/test/test_helper.rb +0 -2
  70. data/lib/gem_release/templates/version.rb +0 -12
  71. data/lib/gem_release/version.rb +0 -3
  72. data/lib/gem_release/version_file.rb +0 -115
  73. data/lib/gem_release/version_template.rb +0 -14
@@ -0,0 +1,100 @@
1
+ require 'gem/release/cmds/base'
2
+ require 'rubygems/commands/build_command'
3
+ require 'rubygems/commands/push_command'
4
+
5
+ module Gem
6
+ module Release
7
+ module Cmds
8
+ class Release < Base
9
+ summary 'Releases one or all gems in this directory.'
10
+
11
+ description <<~str
12
+ Builds one or many gems from the given gemspec(s), pushes them to rubygems.org
13
+ (or another, compatible host), and removes the left over gem file.
14
+
15
+ Optionally invoke `gem tag`.
16
+
17
+ If no argument is given the first gemspec's name is assumed as the gem name.
18
+ If one or many arguments are given then these will be used. If `--recurse` is
19
+ given then all gem names from all gemspecs in this directory or any of its
20
+ subdirectories will be used.
21
+ str
22
+
23
+ arg :gem_name, 'name of the gem (optional, will use the first gemspec, or all gemspecs if --recurse is given)'
24
+
25
+ DESCR = {
26
+ host: 'Push to a compatible host other than rubygems.org',
27
+ key: 'Use the API key from ~/.gem/credentials',
28
+ tag: 'Shortcut for running the `gem tag` command',
29
+ recurse: 'Recurse into directories that contain gemspec files'
30
+ }
31
+
32
+ opt '-h', '--host HOST', DESCR[:host] do |value|
33
+ opts[:host] = value
34
+ end
35
+
36
+ opt '-k', '--key KEY', DESCR[:key] do |value|
37
+ opts[:key] = value
38
+ end
39
+
40
+ opt '-t', '--tag', DESCR[:tag] do |value|
41
+ opts[:tag] = value
42
+ end
43
+
44
+ opt '--recurse', DESCR[:recurse] do |value|
45
+ opts[:recurse] = value
46
+ end
47
+
48
+ MSGS = {
49
+ release: 'Releasing %s with version %s',
50
+ build: 'Building %s',
51
+ push: 'Pushing %s',
52
+ cleanup: 'Deleting left over gem file %s'
53
+ }
54
+
55
+ CMDS = {
56
+ cleanup: 'rm -f %s'
57
+ }
58
+
59
+ def run
60
+ in_gem_dirs do
61
+ release
62
+ end
63
+ tag if opts[:tag]
64
+ end
65
+
66
+ private
67
+
68
+ def release
69
+ announce :release, gem.name, gem.version
70
+ build
71
+ push
72
+ ensure
73
+ cleanup
74
+ end
75
+
76
+ def tag
77
+ Tag.new(context, args, opts).run
78
+ end
79
+
80
+ def build
81
+ gem_cmd :build, gem.spec_filename
82
+ end
83
+
84
+ def push
85
+ gem_cmd :push, gem.filename, *push_args
86
+ end
87
+
88
+ def push_args
89
+ args = [:key, :host].map { |opt| ["--#{opt}", opts[opt]] if opts[opt] }
90
+ args << "--quiet" if quiet?
91
+ args.compact.flatten
92
+ end
93
+
94
+ def cleanup
95
+ cmd :cleanup, gem.filename
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,52 @@
1
+ require 'gem/release/context'
2
+
3
+ module Gem
4
+ module Release
5
+ module Cmds
6
+ class Runner < Struct.new(:context, :name, :args, :opts)
7
+ def run
8
+ run_cmd
9
+ success
10
+ end
11
+
12
+ private
13
+
14
+ def run_cmd
15
+ const.new(context.class.new, args, opts).run
16
+ end
17
+
18
+ def const
19
+ Base[name]
20
+ end
21
+
22
+ def opts
23
+ except(super, :args, :build_args)
24
+ end
25
+
26
+ def args
27
+ super.select { |arg| arg.is_a?(String) && arg[0] != '-' }
28
+ end
29
+
30
+ def success
31
+ context.announce "All is good, thanks my friend." unless quiet?
32
+ end
33
+
34
+ def quiet?
35
+ opts[:quiet] || opts[:silent]
36
+ end
37
+
38
+ def opts
39
+ @opts ||= config.merge(super)
40
+ end
41
+
42
+ def config
43
+ context.config.for(name.to_sym)
44
+ end
45
+
46
+ def except(hash, *keys)
47
+ hash.reject { |key, _| keys.include?(key) }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,93 @@
1
+ require 'gem/release/cmds/base'
2
+
3
+ module Gem
4
+ module Release
5
+ module Cmds
6
+ class Tag < Base
7
+ summary "Tags the HEAD commit with the gem's current version."
8
+
9
+ description <<~str
10
+ Creates an annotated tag for the current HEAD commit, using the gem's
11
+ current version.
12
+
13
+ Optionally pushes the tag to the origin repository.
14
+
15
+ If one or many arguments are given then gemspecs with the same names
16
+ will be searched, and the working directory changed to their respective
17
+ directories. If `--recurse` is given then the directories all gem names from
18
+ all gemspecs in this directory or any of its subdirectories will be used.
19
+ This assumes that these directories are separate git repositories.
20
+
21
+ The tag name will be `v[version]`. For example, if the current version is
22
+ `1.0.0`, then The tag is created using the command `git tag -am "tag v1.0.0"
23
+ v1.0.0`.
24
+ str
25
+
26
+ DEFAULTS = {
27
+ push: true,
28
+ remote: 'origin'
29
+ }
30
+
31
+ DESCR = {
32
+ push: 'Push tag to the remote git repository',
33
+ remote: 'Git remote to push to (defaults to origin)',
34
+ }
35
+
36
+ opt '-p', '--[no]-push', DESCR[:push] do
37
+ opts[:push] = true
38
+ end
39
+
40
+ opt '--remote REMOTE', DESCR[:remote] do |value|
41
+ opts[:remote] = value
42
+ end
43
+
44
+ MSGS = {
45
+ tag: 'Tagging %s as version %s',
46
+ git_tag: 'Creating git tag %s',
47
+ git_push: 'Pushing tags to the %s git repository',
48
+ no_remote: 'Cannot push to missing git remote %s',
49
+ }
50
+
51
+ CMDS = {
52
+ git_tag: 'git tag -am "tag %s" %s',
53
+ git_push: 'git push --tags %s'
54
+ }
55
+
56
+ def run
57
+ in_gem_dirs do
58
+ announce :tag, gem.name, gem.version
59
+ validate
60
+ tag
61
+ push if opts[:push]
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def validate
68
+ abort :no_remote, remote if push? && !git_remotes.include?(remote)
69
+ end
70
+
71
+ def tag
72
+ cmd :git_tag, tag_name, tag_name
73
+ end
74
+
75
+ def push
76
+ cmd :git_push, remote
77
+ end
78
+
79
+ def tag_name
80
+ "v#{gem.version}"
81
+ end
82
+
83
+ def push?
84
+ opts[:push] || opts[:push_commit]
85
+ end
86
+
87
+ def remote
88
+ opts[:remote]
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,42 @@
1
+ require 'gem/release/config/env'
2
+ require 'gem/release/config/files'
3
+ require 'gem/release/helper/hash'
4
+
5
+ module Gem
6
+ module Release
7
+ class Config
8
+ include Helper::Hash
9
+
10
+ attr_reader :opts
11
+
12
+ SOURCES = [Env, Files]
13
+
14
+ def initialize
15
+ @opts = load
16
+ end
17
+
18
+ def [](key)
19
+ opts[key]
20
+ end
21
+
22
+ def for(key)
23
+ common.merge(self[key] || {})
24
+ end
25
+
26
+ def common
27
+ opts.reject { |_, value| value.is_a?(Hash) }
28
+ end
29
+
30
+ private
31
+
32
+ def load
33
+ opts = sources.map(&:load)
34
+ opts.inject { |one, other| deep_merge(one, other) }
35
+ end
36
+
37
+ def sources
38
+ SOURCES.map(&:new)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+ require 'gem/release/helper/hash'
2
+
3
+ module Gem
4
+ module Release
5
+ class Config
6
+ class Env
7
+ include Helper::Hash
8
+
9
+ PREFIX = 'GEM_RELEASE_'
10
+ TRUE = /^(true|yes|on)$/
11
+ FALSE = /^(false|no|off)$/
12
+
13
+ def load
14
+ opts = vars.map { |key, value| to_hash(keys_for(key), cast(value)) }
15
+ opts = opts.inject { |one, other| deep_merge(one, other) }
16
+ opts || {}
17
+ end
18
+
19
+ private
20
+
21
+ def vars
22
+ ENV.select { |key, _| key.start_with?(PREFIX) }
23
+ end
24
+
25
+ def keys_for(key)
26
+ key.sub(PREFIX, '').split('_').map(&:downcase).map(&:to_sym)
27
+ end
28
+
29
+ def to_hash(keys, value)
30
+ keys = keys.reverse
31
+ keys.inject(keys.shift => value) { |value, key| { key => value } }
32
+ end
33
+
34
+ def cast(value)
35
+ case value
36
+ when TRUE
37
+ true
38
+ when FALSE
39
+ false
40
+ when ''
41
+ false
42
+ else
43
+ value
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,35 @@
1
+ require 'yaml'
2
+ require 'gem/release/helper/hash'
3
+
4
+ module Gem
5
+ module Release
6
+ class Config
7
+ class Files
8
+ include Helper::Hash
9
+
10
+ PATHS = %w(
11
+ ./.gem_release/config.yml
12
+ ./.gem_release.yml
13
+ ~/.gem_release/config.yml
14
+ ~/.gem_release.yml
15
+ )
16
+
17
+ def load
18
+ return {} unless path
19
+ symbolize_keys(YAML.load_file(path) || {})
20
+ end
21
+
22
+ private
23
+
24
+ def path
25
+ @path ||= paths.first
26
+ end
27
+
28
+ def paths
29
+ paths = PATHS.map { |path| File.expand_path(path) }
30
+ paths.select { |path| File.exists?(path) }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,76 @@
1
+ require 'gem/release/context/gem'
2
+ require 'gem/release/context/paths'
3
+ require 'gem/release/context/system'
4
+
5
+ module Gem
6
+ module Release
7
+ class Context
8
+ class << self
9
+ attr_accessor :last
10
+ end
11
+
12
+ attr_accessor :config, :gem, :system
13
+
14
+ COLORS = {
15
+ red: "\e[31m",
16
+ green: "\e[32m",
17
+ yellow: "\e[33m",
18
+ blue: "\e[34m",
19
+ gray: "\e[37m",
20
+ reset: "\e[0m"
21
+ }
22
+
23
+ def initialize(name = nil)
24
+ @config = Config.new
25
+ @gem = Gem.new(name || File.basename(Dir.pwd))
26
+ @system = System.new
27
+ end
28
+
29
+ def announce(str)
30
+ puts colored(:green, with_spacing(str, true))
31
+ end
32
+
33
+ def info(str)
34
+ puts colored(:blue, with_spacing(str, true))
35
+ end
36
+
37
+ def notice(str)
38
+ puts colored(:gray, with_spacing(str, false))
39
+ end
40
+
41
+ def warn(str)
42
+ puts colored(:yellow, with_spacing(str, false))
43
+ end
44
+
45
+ def error(str)
46
+ puts colored(:red, with_spacing(str, true))
47
+ end
48
+
49
+ def abort(str)
50
+ error(str)
51
+ exit 1
52
+ end
53
+
54
+ def in_dirs(args, opts, &block)
55
+ Paths::ByNames.new(args, opts).in_dirs(&block)
56
+ end
57
+
58
+ def in_gem_dirs(args, opts, &block)
59
+ Paths::ByGemspecs.new(args, opts).in_dirs(&block)
60
+ end
61
+
62
+ private
63
+
64
+ def colored(color, str)
65
+ [COLORS[color], str, COLORS[:reset]].join
66
+ end
67
+
68
+ def with_spacing(str, space)
69
+ space = false if self.class.last.nil?
70
+ str = "\n#{str}" if space && !self.class.last
71
+ self.class.last = space
72
+ str
73
+ end
74
+ end
75
+ end
76
+ end