yes_ship_it 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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)