pull_request_builder 0.1.1

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