yes_ship_it 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -0
  4. data/CHANGELOG.md +14 -0
  5. data/README.md +9 -1
  6. data/USERS.md +13 -0
  7. data/assertions/built_gem.rb +0 -6
  8. data/assertions/change_log.rb +0 -6
  9. data/assertions/published_gem.rb +1 -7
  10. data/assertions/release_archive.rb +43 -0
  11. data/assertions/release_branch.rb +37 -0
  12. data/assertions/submitted_rpm.rb +134 -0
  13. data/assertions/version.rb +22 -11
  14. data/assertions/working_directory.rb +42 -0
  15. data/bin/yes_ship_it +22 -5
  16. data/configs/ruby_gem.conf +2 -0
  17. data/lib/version.rb +1 -1
  18. data/lib/yes_ship_it.rb +3 -0
  19. data/lib/yes_ship_it/assertion.rb +26 -0
  20. data/lib/yes_ship_it/engine.rb +25 -5
  21. data/spec/data/obs/mycroft.spec.erb +11 -0
  22. data/spec/data/obs/oscrc +8 -0
  23. data/spec/data/red_herring-006.tar.gz +0 -0
  24. data/spec/data/version/version.go +5 -0
  25. data/spec/data/version/version.rb +3 -0
  26. data/spec/integration/cli_spec.rb +74 -0
  27. data/spec/system/data/red_herring-checkout-build.tar.gz +0 -0
  28. data/spec/system/data/red_herring-checkout-not-push.tar.gz +0 -0
  29. data/spec/system/data/red_herring-checkout-push.tar.gz +0 -0
  30. data/spec/system/data/red_herring-remote.tar.gz +0 -0
  31. data/spec/system/ruby_gem_spec.rb +119 -0
  32. data/spec/system/spec_helper.rb +1 -0
  33. data/spec/unit/assertion_spec.rb +46 -0
  34. data/spec/unit/assertions/release_archive_spec.rb +13 -0
  35. data/spec/unit/assertions/release_branch_spec.rb +29 -0
  36. data/spec/unit/assertions/submitted_rpm_spec.rb +48 -0
  37. data/spec/unit/assertions/version_spec.rb +44 -0
  38. data/spec/unit/assertions/working_directory_spec.rb +79 -0
  39. data/spec/unit/engine_spec.rb +14 -0
  40. data/yes_ship_it.gemspec +7 -1
  41. metadata +78 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f0a3bdc9066a3756da3d10312af339a670ebb3b5
4
- data.tar.gz: f0c52e701d5bc7ede706a76cdc1550d9d9199cfd
3
+ metadata.gz: 4392d3f5e95dc46ee07c90f82060e192d450961c
4
+ data.tar.gz: fca85312702536c2f0a3bdf4e85d87d293be352b
5
5
  SHA512:
6
- metadata.gz: 881cc76db9867a69368e18a8e0f45ab05c8de8c008160897806d01cf3ed425600bfefe9ad2c388c73e458d859eb5298b396d08e709293148b1fe959b51ce3a10
7
- data.tar.gz: 8593fe6bd2e0369073444351c81dfda0ba02c0728ace57d53401b97427c23c8c8a58e8d94ffd4d33d9d9397cea0c6e09f525c3a24a426159c57e16075a25f34b
6
+ metadata.gz: f40cf52945fc8beeac7f925bf14613a2defb151d4d800b0224da6458a41a5ff36d8cd5e0d1c65f328e4949d7cdcdeaecafeb6f3a6184858a6ec91de3c7622dd4
7
+ data.tar.gz: 0fcb4cc163dc5e891c95e73bc6cbb61b22d79d0562615ca831de7ceb0f964ef2279c067615ed10aa3a769df564161a63fb4c187a066e59aafb62f778c14333c7
data/.gitignore CHANGED
@@ -1 +1,3 @@
1
1
  Gemfile.lock
2
+ *.gem
3
+ tmp
data/.rspec CHANGED
@@ -1 +1,2 @@
1
1
  --color
2
+ -fd
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Change log of yes_ship_it
2
2
 
3
+ ## Version 0.0.3
4
+
5
+ * Implement minimal version of `yes_ship_it init`. The init command is supposed
6
+ to analyze an existing directory with code and create a suitable initial
7
+ configuration for yes_ship_it. The current implementation simply assumes that
8
+ it is a Ruby gem we are going to ship.
9
+ * Add assertion for submitting RPMs to the Open Build Service
10
+ * Add assertion for creating a release archive as tarball
11
+ * Support parsing version from a Go project
12
+ * Add assertion for clean working directory, which checks unstaged commits and
13
+ untracked files
14
+ * Add assertion which checks that the local checkout is on the release branch.
15
+ The default release branch is master.
16
+
3
17
  ## Version 0.0.2
4
18
 
5
19
  * Don't run assertions when dependent assertions have failed
data/README.md CHANGED
@@ -96,7 +96,15 @@ You get basic documentation from the tool itself by executing
96
96
  `yes_ship_it help`. There also is a simple man page with some pointers.
97
97
 
98
98
  The main documentation with details about different scenarios how `yes_ship_it`
99
- can be used is maintained in the Wiki. You are welcome to contribute there.
99
+ can be used is maintained in the
100
+ [Wiki](https://github.com/cornelius/yes_ship_it/wiki). You are welcome to
101
+ contribute there.
102
+
103
+ ## Users
104
+
105
+ There is a list of [users of yes_ship_it](https://github.com/cornelius/yes_ship_it/blob/master/USERS.md).
106
+ I'm happy to add you there. Just send me a pull requests for the `USERS.md`
107
+ file.
100
108
 
101
109
  ## License
102
110
 
data/USERS.md ADDED
@@ -0,0 +1,13 @@
1
+ # Users of yes_ship_it
2
+
3
+ This is a list of projects using yes_ship_it to do releases. If you want to get
4
+ your project added please send a pull request. If you include a contact email,
5
+ you will get notified when changes happen in yes_ship_it which are likely to
6
+ affect you.
7
+
8
+ * [yes_ship_it](https://github.com/cornelius/yes_ship_it)
9
+ * [Trollolo](https://github.com/openSUSE/trollolo)
10
+ * [Inqlude](https://github.com/cornelius/inqlude) ([Contact](mailto:schumacher@kde.org))
11
+ * [cli_tester](https://github.com/cornelius/cli_tester)
12
+ * [Mycroft](https://github.com/cornelius/mycroft)
13
+ * [Httpotemkin](https://github.com/cornelius/httpotemkin)
@@ -1,11 +1,5 @@
1
1
  module YSI
2
2
  class BuiltGem < Assertion
3
- attr_accessor :error
4
-
5
- def initialize(engine)
6
- @engine = engine
7
- end
8
-
9
3
  def display_name
10
4
  "built gem"
11
5
  end
@@ -2,12 +2,6 @@ module YSI
2
2
  class ChangeLog < Assertion
3
3
  needs "version"
4
4
 
5
- attr_reader :error
6
-
7
- def initialize(engine)
8
- @engine = engine
9
- end
10
-
11
5
  def display_name
12
6
  "change log"
13
7
  end
@@ -2,12 +2,6 @@ module YSI
2
2
  class PublishedGem < Assertion
3
3
  needs "built_gem"
4
4
 
5
- attr_accessor :error
6
-
7
- def initialize(engine)
8
- @engine = engine
9
- end
10
-
11
5
  def display_name
12
6
  "published gem"
13
7
  end
@@ -18,7 +12,7 @@ module YSI
18
12
 
19
13
  def check
20
14
  begin
21
- json = RestClient.get("http://rubygems.org/api/v1/versions/#{engine.project_name}.json")
15
+ json = RestClient.get("https://rubygems.org/api/v1/versions/#{engine.project_name}.json")
22
16
  rescue RestClient::ResourceNotFound
23
17
  return nil
24
18
  end
@@ -0,0 +1,43 @@
1
+ module YSI
2
+ class ReleaseArchive < Assertion
3
+ def display_name
4
+ "release archive"
5
+ end
6
+
7
+ def git_revision
8
+ `git rev-parse HEAD`.chomp
9
+ end
10
+
11
+ def filename
12
+ "#{engine.project_name}-#{engine.version}.tar.gz"
13
+ end
14
+
15
+ def release_archive
16
+ File.join(engine.data_dir, "release_archives", engine.project_name,
17
+ git_revision, filename)
18
+ end
19
+
20
+ def check
21
+ engine.release_archive = release_archive
22
+ if File.exist?(release_archive)
23
+ return filename
24
+ end
25
+ nil
26
+ end
27
+
28
+ def assert(dry_run: false)
29
+ if !dry_run
30
+ Dir.mktmpdir do |tmp_dir|
31
+ archive_dir = "#{engine.project_name}-#{engine.version}"
32
+ cmd = "git clone #{Dir.pwd} #{File.join(tmp_dir, archive_dir)} 2>/dev/null"
33
+ system(cmd)
34
+ FileUtils.mkdir_p(File.dirname(release_archive))
35
+ excludes = [".git", ".gitignore", "yes_ship_it.conf"]
36
+ exclude_options = excludes.map { |e| "--exclude '#{e}'" }.join(" ")
37
+ system("cd #{tmp_dir}; tar czf #{release_archive} #{exclude_options} #{archive_dir}")
38
+ end
39
+ end
40
+ filename
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ module YSI
2
+ class ReleaseBranch < Assertion
3
+ def display_name
4
+ "release branch"
5
+ end
6
+
7
+ def branch
8
+ "master"
9
+ end
10
+
11
+ def current_branch
12
+ git_branch.each_line do |line|
13
+ if line =~ /\* (.*)/
14
+ return $1
15
+ end
16
+ end
17
+ nil
18
+ end
19
+
20
+ def git_branch
21
+ `git branch`
22
+ end
23
+
24
+ def check
25
+ if current_branch != branch
26
+ @error = "Not on release branch '#{branch}'"
27
+ return nil
28
+ else
29
+ return branch
30
+ end
31
+ end
32
+
33
+ def assert(dry_run: false)
34
+ branch
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,134 @@
1
+ module YSI
2
+ class SubmittedRpm < Assertion
3
+ parameter :obs_project
4
+
5
+ attr_reader :obs_user, :obs_password
6
+ attr_reader :obs_package_files
7
+
8
+ def display_name
9
+ "submitted RPM"
10
+ end
11
+
12
+ def read_obs_credentials(file_name)
13
+ oscrc = IniFile.load(file_name)
14
+ @obs_user = oscrc["https://api.opensuse.org"]["user"]
15
+ @obs_password = oscrc["https://api.opensuse.org"]["pass"]
16
+ end
17
+
18
+ def archive_file_name
19
+ engine.release_archive_file_name
20
+ end
21
+
22
+ class RpmSpecHelpers
23
+ def initialize(engine)
24
+ @engine = engine
25
+ end
26
+
27
+ def get_binding
28
+ binding
29
+ end
30
+
31
+ def version
32
+ @engine.version
33
+ end
34
+
35
+ def release_archive
36
+ @engine.release_archive_file_name
37
+ end
38
+
39
+ def release_directory
40
+ "#{@engine.project_name}-#{@engine.version}"
41
+ end
42
+ end
43
+
44
+ def create_spec_file(template)
45
+ erb = ERB.new(File.read(template))
46
+ erb.result(RpmSpecHelpers.new(engine).get_binding)
47
+ end
48
+
49
+ def check
50
+ if !obs_project
51
+ @error = "OBS project is not set"
52
+ return nil
53
+ end
54
+ if !engine.release_archive
55
+ @error = "Release archive is not set. Assert release_archive before submitted_rpm"
56
+ return nil
57
+ end
58
+
59
+ read_obs_credentials(File.expand_path("~/.oscrc"))
60
+
61
+ begin
62
+ url = "https://#{obs_user}:#{obs_password}@api.opensuse.org/source/home:cschum:go/#{engine.project_name}"
63
+ xml = RestClient.get(url)
64
+ rescue RestClient::Exception => e
65
+ if e.is_a?(RestClient::ResourceNotFound)
66
+ return nil
67
+ elsif e.is_a?(RestClient::Unauthorized)
68
+ @error = "No credentials set for OBS. Use osc to do this."
69
+ return nil
70
+ else
71
+ @error = e.to_s
72
+ return nil
73
+ end
74
+ end
75
+
76
+ @obs_package_files = []
77
+ doc = REXML::Document.new(xml)
78
+ doc.elements.each("directory/entry") do |element|
79
+ file_name = element.attributes["name"]
80
+ @obs_package_files.push(file_name)
81
+ end
82
+ if @obs_package_files.include?(archive_file_name)
83
+ return archive_file_name
84
+ end
85
+ nil
86
+ end
87
+
88
+ def assert(dry_run: false)
89
+ engine.out.puts "..."
90
+
91
+ old_files = []
92
+ @obs_package_files.each do |file|
93
+ next if file == "#{engine.project_name}.spec"
94
+ next if file == archive_file_name
95
+ old_files.push(file)
96
+ end
97
+
98
+ engine.out.puts " Uploading release archive '#{archive_file_name}'"
99
+ if !dry_run
100
+ url = "https://#{obs_user}:#{obs_password}@api.opensuse.org/source/home:cschum:go/#{engine.project_name}/#{archive_file_name}"
101
+ file = File.new(engine.release_archive, "rb")
102
+ begin
103
+ RestClient.put(url, file, content_type: "application/x-gzip")
104
+ rescue RestClient::Exception => e
105
+ STDERR.puts e.inspect
106
+ end
107
+ end
108
+
109
+ spec_file = engine.project_name + ".spec"
110
+ engine.out.puts " Uploading spec file '#{spec_file}'"
111
+ if !dry_run
112
+ url = "https://#{obs_user}:#{obs_password}@api.opensuse.org/source/home:cschum:go/#{engine.project_name}/#{spec_file}"
113
+ content = create_spec_file("rpm/#{spec_file}.erb")
114
+ begin
115
+ RestClient.put(url, content, content_type: "text/plain")
116
+ rescue RestClient::Exception => e
117
+ STDERR.puts e.inspect
118
+ end
119
+ end
120
+
121
+ old_files.each do |file|
122
+ engine.out.puts " Removing '#{file}'"
123
+ if !dry_run
124
+ url = "https://#{obs_user}:#{obs_password}@api.opensuse.org/source/home:cschum:go/#{engine.project_name}/#{file}"
125
+ RestClient.delete(url)
126
+ end
127
+ end
128
+
129
+ engine.out.print "... "
130
+
131
+ "#{obs_project}/#{engine.project_name}"
132
+ end
133
+ end
134
+ end
@@ -1,27 +1,21 @@
1
1
  module YSI
2
2
  class Version < Assertion
3
- attr_accessor :error
4
-
5
- def initialize(engine)
6
- @engine = engine
7
- end
3
+ parameter :version_file, "lib/version.rb"
8
4
 
9
5
  def display_name
10
6
  "version number"
11
7
  end
12
8
 
13
9
  def check
14
- version_file = "lib/version.rb"
15
10
  if !File.exist?(version_file)
16
11
  @error = "Expected version in #{version_file}"
17
12
  return nil
18
13
  end
19
14
 
20
- File.read(version_file).each_line do |line|
21
- if line =~ /VERSION = "(.*)"/
22
- @engine.version = $1
23
- return @engine.version
24
- end
15
+ version = parse_version(version_file)
16
+ if version
17
+ @engine.version = version
18
+ return @engine.version
25
19
  end
26
20
  @error = "Couldn't find version in #{version_file}"
27
21
  nil
@@ -29,5 +23,22 @@ module YSI
29
23
 
30
24
  def assert(dry_run: false)
31
25
  end
26
+
27
+ def parse_version(file_name)
28
+ if file_name =~ /\.rb$/
29
+ File.read(file_name).each_line do |line|
30
+ if line =~ /VERSION = "(.*)"/
31
+ return $1
32
+ end
33
+ end
34
+ elsif file_name =~ /\.go$/
35
+ File.read(file_name).each_line do |line|
36
+ if line =~ /Version = "(.*)"/
37
+ return $1
38
+ end
39
+ end
40
+ end
41
+ nil
42
+ end
32
43
  end
33
44
  end
@@ -0,0 +1,42 @@
1
+ module YSI
2
+ class WorkingDirectory < Assertion
3
+ def display_name
4
+ "working directory"
5
+ end
6
+
7
+ def git_status
8
+ `git status`
9
+ end
10
+
11
+ def status
12
+ if !@status
13
+ g = git_status
14
+ if g =~ /working directory clean/
15
+ @status = "clean"
16
+ elsif g =~ /Changes to be committed/
17
+ @status = "uncommitted changes"
18
+ elsif g =~ /Untracked files/
19
+ @status = "untracked files"
20
+ elsif g =~ /Changes not staged/
21
+ @status = "unstaged changes"
22
+ else
23
+ @status = nil
24
+ end
25
+ end
26
+ @status
27
+ end
28
+
29
+ def check
30
+ if status == "clean"
31
+ return status
32
+ else
33
+ @error = status
34
+ return nil
35
+ end
36
+ end
37
+
38
+ def assert(dry_run: false)
39
+ "clean"
40
+ end
41
+ end
42
+ end
data/bin/yes_ship_it CHANGED
@@ -9,6 +9,7 @@ option_parser = OptionParser.new do |opts|
9
9
  opts.separator ""
10
10
  opts.separator "Commands:"
11
11
  opts.separator " changelog - show change log since last release"
12
+ opts.separator " init - initialize directory for shipping with yes_ship_it"
12
13
  opts.separator ""
13
14
  opts.separator "Specific options:"
14
15
 
@@ -16,6 +17,10 @@ option_parser = OptionParser.new do |opts|
16
17
  options[:dry_run] = v
17
18
  end
18
19
 
20
+ opts.on("--data-dir=DIR", "Set directory for storing internal data") do |v|
21
+ options[:data_dir] = v
22
+ end
23
+
19
24
  opts.on_tail("-h", "--help", "Show this message") do
20
25
  puts opts
21
26
  exit
@@ -25,23 +30,35 @@ option_parser.parse!
25
30
 
26
31
  config_file = "yes_ship_it.conf"
27
32
 
28
- if !File.exist?(config_file)
29
- STDERR.puts("Unable to find file `yes_ship_it.conf`. I need it.")
30
- exit 1
31
- end
32
-
33
33
  if ARGV == ["changelog"]
34
34
  engine = YSI::Engine.new
35
35
  engine.check_assertion(YSI::Version)
36
36
  tag = `git tag`.split("\n").last
37
37
  system("git log #{tag}..HEAD")
38
38
  exit 0
39
+ elsif ARGV == ["init"]
40
+ File.open("yes_ship_it.conf", "w") do |file|
41
+ file.puts "# Experimental release automation. See https://github.com/cornelius/yes_ship_it."
42
+ file.puts "include:"
43
+ file.puts " ruby_gem"
44
+ end
45
+ puts "Initialized directory for shipping."
46
+ puts
47
+ puts "Check the file `yes_ship_it.conf` and adapt it to your needs."
48
+ puts
49
+ puts "Happy shipping!"
39
50
  elsif ARGV.empty?
51
+ if !File.exist?(config_file)
52
+ STDERR.puts("Unable to find file `yes_ship_it.conf`. I need it.")
53
+ exit 1
54
+ end
55
+
40
56
  puts "Shipping..."
41
57
  puts
42
58
 
43
59
  engine = YSI::Engine.new
44
60
  engine.dry_run = options[:dry_run]
61
+ engine.data_dir = options[:data_dir] if options[:data_dir]
45
62
 
46
63
  begin
47
64
  engine.read(config_file)