pull_request_builder 0.1.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 60d7abe1e0be293477b893b5b08eac24e6a475dbe08960f13f11f470279b633c
4
+ data.tar.gz: 8dea6cea080452363a407a3d5b87293230796abf0d85863badcb84fa6c562a71
5
+ SHA512:
6
+ metadata.gz: 07347bac8092ae29f604700c986e879a5411f984547f2e3943460c47ace43e3085ec1d0548d745913968c79de7086179997cd0770888d2a8b8076ed4cd5aa481
7
+ data.tar.gz: 115cbb111f15776d3911284930c8bfa85964be00ece2ebead22077f7fa1a944c718f51ad772a3f09cadfcfb7dd4fbaac445da441147cae54e8024ae2d7343f3a
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.swp
10
+ config/config.yml
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ -I lib --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,92 @@
1
+ # This is the configuration used to check the rubocop source code.
2
+
3
+ AllCops:
4
+ Exclude:
5
+ - 'fixtures/**/*'
6
+ TargetRubyVersion: 2.6
7
+
8
+ Naming/PredicateName:
9
+ # Method define macros for dynamically generated method.
10
+ MethodDefinitionMacros:
11
+ - define_method
12
+ - define_singleton_method
13
+ - def_node_matcher
14
+ - def_node_search
15
+
16
+ Style/FrozenStringLiteralComment:
17
+ EnforcedStyle: always
18
+
19
+ Style/FormatStringToken:
20
+ # Because we parse a lot of source codes from strings. Percent arrays
21
+ # look like unannotated format string tokens to this cop.
22
+ Exclude:
23
+ - spec/**/*
24
+
25
+ Style/IpAddresses:
26
+ # The test for this cop includes strings that would cause offenses
27
+ Exclude:
28
+ - spec/rubocop/cop/style/ip_addresses_spec.rb
29
+
30
+ Layout/EndOfLine:
31
+ EnforcedStyle: lf
32
+
33
+ Layout/ClassStructure:
34
+ Enabled: true
35
+ Categories:
36
+ module_inclusion:
37
+ - include
38
+ - prepend
39
+ - extend
40
+ ExpectedOrder:
41
+ - module_inclusion
42
+ - constants
43
+ - public_class_methods
44
+ - initializer
45
+ - instance_methods
46
+ - protected_methods
47
+ - private_methods
48
+
49
+ Layout/IndentHeredoc:
50
+ EnforcedStyle: powerpack
51
+
52
+ # Trailing white space is meaningful in code examples
53
+ Layout/TrailingWhitespace:
54
+ AllowInHeredoc: true
55
+
56
+ Lint/AmbiguousBlockAssociation:
57
+ Exclude:
58
+ - 'spec/**/*.rb'
59
+
60
+ Lint/InterpolationCheck:
61
+ Exclude:
62
+ - 'spec/**/*.rb'
63
+
64
+ Lint/UselessAccessModifier:
65
+ MethodCreatingMethods:
66
+ - 'def_matcher'
67
+ - 'def_node_matcher'
68
+
69
+ Lint/BooleanSymbol:
70
+ Enabled: false
71
+
72
+ Metrics/LineLength:
73
+ Max: 150
74
+
75
+ Metrics/BlockLength:
76
+ Exclude:
77
+ - '*.gemspec'
78
+ - 'Rakefile'
79
+ - '**/*.rake'
80
+ - 'spec/**/*.rb'
81
+
82
+ Style/MixinUsage:
83
+ Exclude:
84
+ - 'spec/**/*.rb'
85
+
86
+ Metrics/ModuleLength:
87
+ Exclude:
88
+ - 'spec/**/*.rb'
89
+
90
+ Performance/Caller:
91
+ Exclude:
92
+ - spec/rubocop/cop/performance/caller_spec.rb
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.0
7
+ before_install: gem install bundler -v 1.16.6
data/Dockerfile ADDED
@@ -0,0 +1,17 @@
1
+ FROM opensuse/tumbleweed
2
+
3
+ RUN zypper -n in osc ruby2.6 ruby2.6-devel git gcc make sudo libxml2-devel zlib-devel libxslt-devel
4
+ RUN gem install bundler
5
+ RUN bundle config build.nokogiri --use-system-libraries
6
+
7
+ RUN useradd -ms /bin/bash puller
8
+
9
+ RUN echo "puller ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/user && chmod 0440 /etc/sudoers.d/user
10
+
11
+ RUN mkdir -p /home/puller/.config/osc/
12
+ COPY oscrc /home/puller/.config/osc/
13
+ RUN chown -R puller:users /home/puller/
14
+
15
+ USER puller
16
+
17
+ WORKDIR /home/puller/pull_request_package
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in pull_request_builder.gemspec
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,116 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pull_request_builder (0.1.0)
5
+ activemodel (~> 5.2)
6
+ cheetah (~> 0.5.0)
7
+ nokogiri (~> 1.10)
8
+ octokit (~> 4.9)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ abstract_method (1.2.1)
14
+ activemodel (5.2.2)
15
+ activesupport (= 5.2.2)
16
+ activesupport (5.2.2)
17
+ concurrent-ruby (~> 1.0, >= 1.0.2)
18
+ i18n (>= 0.7, < 2)
19
+ minitest (~> 5.1)
20
+ tzinfo (~> 1.1)
21
+ addressable (2.5.2)
22
+ public_suffix (>= 2.0.2, < 4.0)
23
+ ast (2.4.0)
24
+ byebug (11.0.0)
25
+ cheetah (0.5.0)
26
+ abstract_method (~> 1.2)
27
+ coderay (1.1.2)
28
+ concurrent-ruby (1.1.4)
29
+ crack (0.4.3)
30
+ safe_yaml (~> 1.0.0)
31
+ diff-lcs (1.3)
32
+ faraday (0.15.4)
33
+ multipart-post (>= 1.2, < 3)
34
+ hashdiff (0.3.8)
35
+ i18n (1.5.3)
36
+ concurrent-ruby (~> 1.0)
37
+ jaro_winkler (1.5.2)
38
+ method_source (0.9.2)
39
+ mini_portile2 (2.4.0)
40
+ minitest (5.11.3)
41
+ multipart-post (2.0.0)
42
+ nokogiri (1.10.1)
43
+ mini_portile2 (~> 2.4.0)
44
+ octokit (4.13.0)
45
+ sawyer (~> 0.8.0, >= 0.5.3)
46
+ parallel (1.14.0)
47
+ parser (2.6.0.0)
48
+ ast (~> 2.4.0)
49
+ powerpack (0.1.2)
50
+ pry (0.12.2)
51
+ coderay (~> 1.1.0)
52
+ method_source (~> 0.9.0)
53
+ psych (3.1.0)
54
+ public_suffix (3.0.3)
55
+ rainbow (3.0.0)
56
+ rake (10.5.0)
57
+ rspec (3.8.0)
58
+ rspec-core (~> 3.8.0)
59
+ rspec-expectations (~> 3.8.0)
60
+ rspec-mocks (~> 3.8.0)
61
+ rspec-core (3.8.0)
62
+ rspec-support (~> 3.8.0)
63
+ rspec-expectations (3.8.2)
64
+ diff-lcs (>= 1.2.0, < 2.0)
65
+ rspec-support (~> 3.8.0)
66
+ rspec-html-matchers (0.9.1)
67
+ nokogiri (~> 1)
68
+ rspec (>= 3.0.0.a, < 4)
69
+ rspec-mocks (3.8.0)
70
+ diff-lcs (>= 1.2.0, < 2.0)
71
+ rspec-support (~> 3.8.0)
72
+ rspec-support (3.8.0)
73
+ rubocop (0.65.0)
74
+ jaro_winkler (~> 1.5.1)
75
+ parallel (~> 1.10)
76
+ parser (>= 2.5, != 2.5.1.1)
77
+ powerpack (~> 0.1)
78
+ psych (>= 3.1.0)
79
+ rainbow (>= 2.2.2, < 4.0)
80
+ ruby-progressbar (~> 1.7)
81
+ unicode-display_width (~> 1.4.0)
82
+ rubocop-rspec (1.32.0)
83
+ rubocop (>= 0.60.0)
84
+ ruby-progressbar (1.10.0)
85
+ safe_yaml (1.0.5)
86
+ sawyer (0.8.1)
87
+ addressable (>= 2.3.5, < 2.6)
88
+ faraday (~> 0.8, < 1.0)
89
+ thread_safe (0.3.6)
90
+ tzinfo (1.2.5)
91
+ thread_safe (~> 0.1)
92
+ unicode-display_width (1.4.1)
93
+ vcr (4.0.0)
94
+ webmock (3.5.1)
95
+ addressable (>= 2.3.6)
96
+ crack (>= 0.3.2)
97
+ hashdiff
98
+
99
+ PLATFORMS
100
+ ruby
101
+
102
+ DEPENDENCIES
103
+ bundler (~> 2.0.1)
104
+ byebug (~> 11.0.0)
105
+ pry (~> 0.12)
106
+ pull_request_builder!
107
+ rake (~> 10.0)
108
+ rspec (~> 3.8)
109
+ rspec-html-matchers (~> 0.9.1)
110
+ rubocop (~> 0.64)
111
+ rubocop-rspec (~> 1.32)
112
+ vcr (~> 4.0)
113
+ webmock (~> 3.5)
114
+
115
+ BUNDLED WITH
116
+ 2.0.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # PullRequestBuilder
2
+
3
+ Integrate your CI pipeline with Open Build Service, being possible to build your PR as package in the trusty OBS!
4
+
5
+ ## Build Status
6
+ [![Build Status](https://travis-ci.com/vpereira/pull_request_builder.svg?branch=master)](https://travis-ci.com/vpereira/pull_request_builder)
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'pull_request_builder'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install pull_request_builder
23
+
24
+ ## Usage
25
+
26
+ We have it being used here: https://github.com/openSUSE/obs-tools/tree/master/pull_request_package
27
+
28
+ Please look our [Wiki](https://github.com/vpereira/pull_request_builder/wiki/running) if you need more specific information
29
+
30
+ ## Development
31
+
32
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
33
+
34
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
35
+
36
+ ## Contributing
37
+
38
+ Bug reports and pull requests are welcome on GitHub at https://github.com/vpereira/pull_request_builder.
39
+
40
+ ## License
41
+
42
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
43
+
44
+
45
+ ## Thanks
46
+
47
+ To all contributors from the original code https://github.com/openSUSE/obs-tools/tree/master/pull_request_package
48
+
49
+ Eduardoj, coolo, ChrisBr and many others!
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+ RuboCop::RakeTask.new
9
+
10
+ task default: :spec
11
+ rescue LoadError
12
+ # no rspec available
13
+ end
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'pull_request_builder'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/pull_request_builder'
5
+
6
+ # The "runner used" by obs-tools
7
+ if $PROGRAM_NAME == __FILE__
8
+ config_file = if ARGV.first.nil?
9
+ 'config/config.yml'
10
+ else
11
+ ARGV.first
12
+ end
13
+ config = YAML.load_file(config_file)
14
+ fetcher = PullRequestBuilder::GithubPullRequestFetcher.new(config)
15
+ fetcher.pull
16
+ fetcher.delete
17
+ end
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ # generate and customize your token here
2
+ # https://github.com/settings/tokens
3
+ #
4
+ :credentials:
5
+ :access_token: my-oath-tokens
6
+ :logging: false
7
+ :build_server: https://build.opensuse.org
8
+ :build_server_project_integration_prefix: OBS:Server:Unstable:TestGithub:PR
9
+ :build_server_project: OBS:Server:Unstable
10
+ :build_server_package_name: obs-server
11
+ :git_repository: openSUSE/open-buildservice
12
+ :git_server: https://github.com/
13
+ :git_branch: master
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cheetah'
4
+ require 'erb'
5
+ require 'ostruct'
6
+ require 'logger'
7
+ require 'yaml'
8
+ require 'open3'
9
+ require 'tempfile'
10
+ require 'fileutils'
11
+ require 'nokogiri'
12
+ require 'active_model'
13
+ require 'octokit'
14
+
15
+ require_relative 'pull_request_builder/osc'
16
+ require_relative 'pull_request_builder/version'
17
+ require_relative 'pull_request_builder/project_meta'
18
+ require_relative 'pull_request_builder/builder_config'
19
+ require_relative 'pull_request_builder/package_template'
20
+ require_relative 'pull_request_builder/github_status_reporter'
21
+ require_relative 'pull_request_builder/obs_pull_request_package'
22
+ require_relative 'pull_request_builder/github_pull_request_fetcher'
23
+
24
+ module PullRequestBuilder
25
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PullRequestBuilder
4
+ class BuilderConfig
5
+ attr_accessor :octokit_client, :logger, :build_server,
6
+ :build_server_project_integration_prefix,
7
+ :build_server_project, :build_server_package_name,
8
+ :git_server, :git_repository, :git_branch, :osc
9
+
10
+ def initialize(config = {})
11
+ @octokit_client = Octokit::Client.new(config[:credentials])
12
+ @logger = config[:logging] ? Logger.new(STDOUT) : Logger.new(nil)
13
+ @build_server_project = config.fetch(:build_server_project, 'OBS:Server:Unstable')
14
+ @git_branch = config.fetch(:git_branch, 'master')
15
+ @git_server = config.fetch(:git_repository, 'https://github.com')
16
+ @git_repository = config.fetch(:git_repository, 'openSUSE/open-build-service')
17
+ @build_server = config.fetch(:build_server, 'https://build.opensuse.org')
18
+ @build_server_package_name = config.fetch(:build_server_package_name, 'obs-server')
19
+ @build_server_project_integration_prefix = config.fetch(:build_server_project_integration_prefix,
20
+ 'OBS:Server:Unstable:TestGithub:PR')
21
+ @osc = OSC.new(apiurl: @build_server, logger: @logger)
22
+ end
23
+
24
+ def git_repository_full_address
25
+ File.join(@git_server, @git_repository)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PullRequestBuilder
4
+ class GithubPullRequestFetcher
5
+ attr_reader :packages
6
+
7
+ def initialize(config = {})
8
+ @config = BuilderConfig.new(config)
9
+ @packages = []
10
+ end
11
+
12
+ def pull
13
+ @packages = @config.octokit_client.pull_requests(@config.git_repository).collect do |pull_request|
14
+ next if pull_request.base.ref != @config.git_branch
15
+
16
+ @config.logger.info('')
17
+ @config.logger.info(line_seperator(pull_request))
18
+ package = ObsPullRequestPackage.new(pull_request: pull_request, logger: @config.logger,
19
+ obs_project_name_prefix: @config.build_server_project_integration_prefix,
20
+ obs_package_name: @config.build_server_package_name, obs_project_name: @config.build_server_project,
21
+ osc: @config.osc)
22
+ package.create
23
+ GithubStatusReporter.new(repository: @config.git_repository, package: package, client: @config.octokit_client, logger: @config.logger, osc: OSC.new).report
24
+ package
25
+ end
26
+ end
27
+
28
+ def delete
29
+ ObsPullRequestPackage.all(@config.logger, @config.build_server_project_integration_prefix).each do |obs_package|
30
+ next if @packages.any? { |pr_package| pr_package.pull_request.number == obs_package.pull_request.number }
31
+
32
+ @config.logger.info('Delete obs_package')
33
+ @config.build_server_package_name
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def line_seperator(pull_request, separator_char = '=', separation_size = 15)
40
+ separator_char * separation_size + " #{pull_request.title} (#{pull_request.number}) " + separator_char * separation_size
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PullRequestBuilder
4
+ class GithubStatusReporter
5
+ include ActiveModel::Model
6
+ attr_accessor :client, :logger, :package, :repository, :osc
7
+
8
+ def report
9
+ if update?
10
+ logger.info("Update status to state #{state}.")
11
+ client.create_status(repository, package.commit_sha, state, options)
12
+ else
13
+ logger.info('State did not change, continue...')
14
+ end
15
+ rescue Octokit::Error => e
16
+ logger.error("Status not updated: #{e}.")
17
+ end
18
+
19
+ private
20
+
21
+ def update?
22
+ statuses = client.statuses(repository, package.commit_sha)
23
+ build_status = statuses.select { |state| state.context == context }.first
24
+ build_status.nil? || state.to_s != build_status.state || description != build_status.description
25
+ end
26
+
27
+ def context
28
+ 'OBS Package Build'
29
+ end
30
+
31
+ def description
32
+ count_all = summary[:success] + summary[:failure] + summary[:pending]
33
+ count_finished = count_all - summary[:pending]
34
+
35
+ result = String.new("#{count_finished}/#{count_all} processed")
36
+ result << " | #{summary[:failure]} failures" if summary[:failure].positive?
37
+ result
38
+ end
39
+
40
+ def state
41
+ if summary[:failure].positive?
42
+ :failure
43
+ elsif summary[:pending].positive? || summary[:success].zero?
44
+ :pending
45
+ else
46
+ :success
47
+ end
48
+ end
49
+
50
+ def options
51
+ {
52
+ context: context,
53
+ target_url: package.url,
54
+ description: description
55
+ }
56
+ end
57
+
58
+ def judge_code(code)
59
+ case code
60
+ when 'succeeded'
61
+ :success
62
+ when 'excluded', 'disabled'
63
+ :exclusion
64
+ when 'broken', 'failed', 'unresolvable'
65
+ :failure
66
+ when 'building', 'dispatching', 'scheduled', 'finished', 'blocked'
67
+ :pending
68
+ else
69
+ logger.error("Unmapped status result #{code} in #{package.obs_package_name}")
70
+ :pending
71
+ end
72
+ end
73
+
74
+ def summary
75
+ return @summary if @summary
76
+
77
+ @summary = { failure: 0, success: 0, pending: 0, exclusion: 0 }
78
+ result = osc.build_result(package)
79
+ node = Nokogiri::XML(result).root
80
+ node.xpath('.//status').each do |status|
81
+ @summary[judge_code(status['code'])] += 1
82
+ end
83
+ @summary
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PullRequestBuilder
4
+ class ObsPullRequestPackage
5
+ include ActiveModel::Model
6
+ attr_accessor :pull_request, :logger, :template_directory, :obs_project_name_prefix,
7
+ :obs_package_name, :obs_project_name, :obs_project_pr_name, :osc
8
+ PullRequest = Struct.new(:number)
9
+
10
+ def self.all(logger, obs_project_name_prefix, osc = OSC.new)
11
+ result = osc.search_project(obs_project_name_prefix)
12
+ xml = Nokogiri::XML(result)
13
+ xml.xpath('//project').map do |project|
14
+ pull_request_number = project.attribute('name').to_s.split('-').last.to_i
15
+ ObsPullRequestPackage.new(pull_request: PullRequest.new(pull_request_number), logger: logger)
16
+ end
17
+ end
18
+
19
+ def delete
20
+ osc.delete_project("source/#{obs_project_name}")
21
+ end
22
+
23
+ def ==(other)
24
+ pull_request.number == other.pull_request.number
25
+ end
26
+
27
+ def eql?(other)
28
+ pull_request.number.eql(other.pull_request.number)
29
+ end
30
+
31
+ def hash
32
+ pull_request.number.hash
33
+ end
34
+
35
+ def pull_request_number
36
+ pull_request.number
37
+ end
38
+
39
+ def commit_sha
40
+ pull_request.head.sha
41
+ end
42
+
43
+ def merge_sha
44
+ # github test merge commit
45
+ pull_request.merge_commit_sha
46
+ end
47
+
48
+ def obs_project_pr_name
49
+ "#{obs_project_name_prefix}-#{pull_request_number}"
50
+ end
51
+
52
+ # TODO
53
+ # address must be configurable
54
+ def url
55
+ "https://build.opensuse.org/package/show/#{obs_project_pr_name}/#{obs_package_name}"
56
+ end
57
+
58
+ def last_commited_sha
59
+ # if its a new PR, get_history will fail with a 404 and we have to ignore it
60
+
61
+ result = osc.get_history("#{obs_project_pr_name}/#{obs_package_name}")
62
+ node = Nokogiri::XML(result).root
63
+ node.xpath('.//revision/comment').last.content
64
+ rescue StandardError, Cheetah::ExecutionFailed
65
+ ''
66
+ end
67
+
68
+ def create
69
+ if last_commited_sha == commit_sha
70
+ logger.info('Pull request did not change, skipping ...')
71
+ return
72
+ end
73
+ create_project
74
+ create_package
75
+ copy_files
76
+ end
77
+
78
+ private
79
+
80
+ def send_meta_file(filename, operation: :prj)
81
+ tmp_meta_file = Tempfile.open(filename)
82
+ begin
83
+ tmp_meta_file.puts(operation == :prj ? project_meta : package_meta)
84
+ tmp_meta_file.close
85
+ osc_meta(tmp_meta_file, operation)
86
+ ensure
87
+ tmp_meta_file.unlink
88
+ end
89
+ end
90
+
91
+ def osc_meta(tmpfile, operation)
92
+ case operation
93
+ when :prj
94
+ osc.meta_prj(obs_project_pr_name, tmpfile.path)
95
+ when :pkg
96
+ osc.meta_pkg(obs_project_pr_name, obs_package_name, tmpfile.path)
97
+ else
98
+ raise ArgumentError, "#{operation} not vaild"
99
+ end
100
+ end
101
+
102
+ def create_project
103
+ send_meta_file("#{pull_request_number}-meta", operation: :prj)
104
+ end
105
+
106
+ def package_meta
107
+ PackageTemplate.new(obs_package_name, obs_project_pr_name).to_xml
108
+ end
109
+
110
+ def project_meta
111
+ ProjectMeta.new(obs_project_pr_name, project_title, repositories_to_build).to_xml
112
+ end
113
+
114
+ def project_title
115
+ pull_request.html_url
116
+ end
117
+
118
+ # TODO
119
+ # make it configurable
120
+ def repositories_to_build
121
+ [
122
+ OpenStruct.new(name: 'SLE_15', path: 'OBS:Server:Unstable', arches: ['x86_64']),
123
+ OpenStruct.new(name: 'SLE_12_SP4', path: 'OBS:Server:Unstable', arches: ['x86_64'])
124
+ ]
125
+ end
126
+
127
+ # TODO
128
+ # package name should be configurable
129
+ def create_package
130
+ send_meta_file("#{obs_package_name}-meta", operation: :pkg)
131
+ end
132
+
133
+ def new_package_template
134
+ PackageTemplate.new(package_name = obs_package_name).to_xml
135
+ end
136
+
137
+ def copy_files
138
+ Dir.mktmpdir do |dir|
139
+ osc.checkout(File.join(obs_project_name, obs_package_name), File.join(dir, 'template'))
140
+ osc.checkout(File.join(obs_project_pr_name, obs_package_name), File.join(dir, obs_project_pr_name))
141
+ copy_package_files(dir)
142
+ osc.add_remove(File.join(dir, obs_project_pr_name))
143
+ osc.commit(File.join(dir, obs_project_pr_name), commit_sha)
144
+ end
145
+ end
146
+
147
+ def copy_package_files(dir)
148
+ Dir.entries("#{dir}/template").reject { |name| name.start_with?('.') }.each do |file|
149
+ path = File.join(dir, 'template', file)
150
+ target_path = File.join(dir, obs_project_pr_name, file)
151
+ if file == '_service'
152
+ copy_service_file(path, target_path)
153
+ else
154
+ FileUtils.cp path, target_path
155
+ end
156
+ end
157
+ end
158
+
159
+ def copy_service_file(path, target_path)
160
+ File.open(target_path, 'w') do |f|
161
+ f.write(service_file(path))
162
+ end
163
+ end
164
+
165
+ def service_file(path)
166
+ content = File.read(path)
167
+ xml = Nokogiri::XML(content)
168
+ node = xml.root.at_xpath(".//param[@name='revision']")
169
+ node.content = merge_sha
170
+ xml.to_s
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PullRequestBuilder
4
+ class OSC
5
+ include ActiveModel::Model
6
+
7
+ attr_accessor :apiurl, :logger
8
+
9
+ def checkout(project, dir)
10
+ execute(['co', project, '--output-dir', dir])
11
+ end
12
+
13
+ def delete_project(project)
14
+ execute(['api', '-X', 'DELETE', project])
15
+ end
16
+
17
+ def get_history(project)
18
+ execute(['api', "/source/#{project}/_history"])
19
+ end
20
+
21
+ def add_remove(file_path)
22
+ execute(['ar', file_path])
23
+ end
24
+
25
+ def meta_pkg(project, package, meta_file)
26
+ meta(operation: :pkg, project: project, package: package, meta_file: meta_file)
27
+ end
28
+
29
+ def meta_prj(project, meta_file)
30
+ meta(operation: :prj, project: project, meta_file: meta_file)
31
+ end
32
+
33
+ def commit(file_path, message = 'ok')
34
+ execute(['commit', file_path, '-m', message])
35
+ end
36
+
37
+ def build_result(package)
38
+ execute(['api', File.join('build', package.obs_project_pr_name, '_result')])
39
+ end
40
+
41
+ def search_project(name_prefix)
42
+ search_string = "search/project?match=starts-with(@name,'#{name_prefix}')"
43
+ execute(['api', search_string])
44
+ end
45
+
46
+ def apiurl
47
+ @apiurl ||= 'https://api.opensuse.org'
48
+ end
49
+
50
+ private
51
+
52
+ def meta(params = {})
53
+ case params[:operation]
54
+ when :prj
55
+ execute(['meta', 'prj', params[:project], '--file', params[:meta_file]])
56
+ when :pkg
57
+ execute(['meta', 'pkg', params[:project], params[:package], '--file', params[:meta_file]])
58
+ else
59
+ raise ArgumentError, "#{operation} not vaild"
60
+ end
61
+ end
62
+
63
+ def execute(args)
64
+ ::Cheetah.run('osc', '-A', apiurl, *args, logger: logger, stdout: :capture)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PullRequestBuilder
4
+ class PackageTemplate
5
+ attr_reader :package_name, :project_name
6
+
7
+ def initialize(package_name, project_name)
8
+ @package_name = package_name
9
+ @project_name = project_name
10
+ end
11
+
12
+ def template
13
+ File.read(File.join(File.dirname(__FILE__), '..', 'views', 'new_package.xml.erb'))
14
+ end
15
+
16
+ def to_xml
17
+ ERB.new(template).result(binding)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PullRequestBuilder
4
+ class ProjectMeta
5
+ attr_accessor :name, :title, :repositories_to_build
6
+
7
+ def initialize(name, title, repositories_to_build)
8
+ @name = name
9
+ @title = title
10
+ @repositories_to_build = repositories_to_build
11
+ end
12
+
13
+ def template
14
+ File.read(File.join(File.dirname(__FILE__), '..', 'views', 'new_project.xml.erb'))
15
+ end
16
+
17
+ def to_xml
18
+ ERB.new(template).result(binding)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PullRequestBuilder
4
+ VERSION = '0.1.1'
5
+ end
@@ -0,0 +1,4 @@
1
+ <package name="<%= package_name %>" project="<%= project_name %>">
2
+ <title></title>
3
+ <description/>
4
+ </package>
@@ -0,0 +1,21 @@
1
+ <project name="<%= name %>">
2
+ <title><%=title%></title>
3
+ <description/>
4
+ <build>
5
+ <enable/>
6
+ </build>
7
+ <publish>
8
+ <disable/>
9
+ </publish>
10
+ <debuginfo>
11
+ <enable/>
12
+ </debuginfo>
13
+ <% repositories_to_build.each do |repository| %>
14
+ <repository name="<%= repository.name %>">
15
+ <path project="<%= repository.path %>" repository="<%= repository.name %>"/>
16
+ <% repository.arches.each do |arch| %>
17
+ <arch><%= arch %></arch>
18
+ <% end %>
19
+ </repository>
20
+ <% end %>
21
+ </project>
data/oscrc ADDED
@@ -0,0 +1,6 @@
1
+ [general]
2
+ apiurl = http://frontend:3000
3
+
4
+ [http://frontend:3000]
5
+ user = Admin
6
+ pass = opensuse
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'pull_request_builder/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'pull_request_builder'
9
+ spec.version = PullRequestBuilder::VERSION
10
+ spec.authors = ['Victor Pereira']
11
+ spec.email = ['vpereira@suse.de']
12
+
13
+ spec.summary = 'GEM to enable building PRs on openSUSE BuildServer'
14
+ spec.description = 'To leverage your CI pipelining, building every GitHub PRs on obs' \
15
+ 'this gem provide the necessary bits to do it'
16
+ spec.homepage = 'https://github.com/vpereira/pull_request_builder'
17
+ spec.license = 'MIT'
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = 'exe'
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_development_dependency 'bundler', '~> 2.0.1'
29
+ spec.add_development_dependency 'byebug', '~> 11.0.0'
30
+ spec.add_development_dependency 'pry', '~> 0.12'
31
+ spec.add_development_dependency 'rake', '~> 10.0'
32
+ spec.add_development_dependency 'rspec', '~> 3.8'
33
+ spec.add_development_dependency 'rspec-html-matchers', '~> 0.9.1'
34
+ spec.add_development_dependency 'rubocop', '~> 0.64'
35
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.32'
36
+ spec.add_development_dependency 'vcr', '~> 4.0'
37
+ spec.add_development_dependency 'webmock', '~> 3.5'
38
+
39
+ spec.add_runtime_dependency 'activemodel', '~> 5.2'
40
+ spec.add_runtime_dependency 'cheetah', '~> 0.5.0'
41
+ spec.add_runtime_dependency 'nokogiri', '~> 1.10'
42
+ spec.add_runtime_dependency 'octokit', '~> 4.9'
43
+ end
metadata ADDED
@@ -0,0 +1,267 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pull_request_builder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Victor Pereira
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-07-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.1
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 11.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 11.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.8'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.8'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-html-matchers
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.9.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.9.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.64'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.64'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.32'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.32'
125
+ - !ruby/object:Gem::Dependency
126
+ name: vcr
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '4.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '4.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: webmock
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '3.5'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '3.5'
153
+ - !ruby/object:Gem::Dependency
154
+ name: activemodel
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '5.2'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '5.2'
167
+ - !ruby/object:Gem::Dependency
168
+ name: cheetah
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 0.5.0
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 0.5.0
181
+ - !ruby/object:Gem::Dependency
182
+ name: nokogiri
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '1.10'
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '1.10'
195
+ - !ruby/object:Gem::Dependency
196
+ name: octokit
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '4.9'
202
+ type: :runtime
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '4.9'
209
+ description: To leverage your CI pipelining, building every GitHub PRs on obsthis
210
+ gem provide the necessary bits to do it
211
+ email:
212
+ - vpereira@suse.de
213
+ executables: []
214
+ extensions: []
215
+ extra_rdoc_files: []
216
+ files:
217
+ - ".gitignore"
218
+ - ".rspec"
219
+ - ".rubocop.yml"
220
+ - ".travis.yml"
221
+ - Dockerfile
222
+ - Gemfile
223
+ - Gemfile.lock
224
+ - LICENSE.txt
225
+ - README.md
226
+ - Rakefile
227
+ - bin/console
228
+ - bin/pull_request_builder
229
+ - bin/setup
230
+ - config/config.yml.example
231
+ - lib/pull_request_builder.rb
232
+ - lib/pull_request_builder/builder_config.rb
233
+ - lib/pull_request_builder/github_pull_request_fetcher.rb
234
+ - lib/pull_request_builder/github_status_reporter.rb
235
+ - lib/pull_request_builder/obs_pull_request_package.rb
236
+ - lib/pull_request_builder/osc.rb
237
+ - lib/pull_request_builder/package_template.rb
238
+ - lib/pull_request_builder/project_meta.rb
239
+ - lib/pull_request_builder/version.rb
240
+ - lib/views/new_package.xml.erb
241
+ - lib/views/new_project.xml.erb
242
+ - oscrc
243
+ - pull_request_builder.gemspec
244
+ homepage: https://github.com/vpereira/pull_request_builder
245
+ licenses:
246
+ - MIT
247
+ metadata: {}
248
+ post_install_message:
249
+ rdoc_options: []
250
+ require_paths:
251
+ - lib
252
+ required_ruby_version: !ruby/object:Gem::Requirement
253
+ requirements:
254
+ - - ">="
255
+ - !ruby/object:Gem::Version
256
+ version: '0'
257
+ required_rubygems_version: !ruby/object:Gem::Requirement
258
+ requirements:
259
+ - - ">="
260
+ - !ruby/object:Gem::Version
261
+ version: '0'
262
+ requirements: []
263
+ rubygems_version: 3.0.3
264
+ signing_key:
265
+ specification_version: 4
266
+ summary: GEM to enable building PRs on openSUSE BuildServer
267
+ test_files: []