autoproj-jenkins 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +41 -0
  6. data/Rakefile +11 -0
  7. data/autoproj-jenkins.gemspec +28 -0
  8. data/lib/autoproj/cli/jenkins.rb +100 -0
  9. data/lib/autoproj/cli/main_jenkins.rb +123 -0
  10. data/lib/autoproj/cli/test_postprocessing.rb +85 -0
  11. data/lib/autoproj/jenkins/credentials.rb +67 -0
  12. data/lib/autoproj/jenkins/exceptions.rb +7 -0
  13. data/lib/autoproj/jenkins/relativize.rb +74 -0
  14. data/lib/autoproj/jenkins/render_template.rb +92 -0
  15. data/lib/autoproj/jenkins/server.rb +79 -0
  16. data/lib/autoproj/jenkins/templates/abort-if-upstream-failed.pipeline.erb +18 -0
  17. data/lib/autoproj/jenkins/templates/bootstrap.pipeline.erb +27 -0
  18. data/lib/autoproj/jenkins/templates/buildconf-Gemfile +3 -0
  19. data/lib/autoproj/jenkins/templates/buildconf-config.yml.erb +12 -0
  20. data/lib/autoproj/jenkins/templates/buildconf-vagrant-Gemfile +4 -0
  21. data/lib/autoproj/jenkins/templates/buildconf.pipeline.erb +43 -0
  22. data/lib/autoproj/jenkins/templates/buildconf.xml.erb +24 -0
  23. data/lib/autoproj/jenkins/templates/handle-downstream.pipeline.erb +20 -0
  24. data/lib/autoproj/jenkins/templates/import-archive.pipeline.erb +2 -0
  25. data/lib/autoproj/jenkins/templates/import-git.pipeline.erb +16 -0
  26. data/lib/autoproj/jenkins/templates/import-svn.pipeline.erb +13 -0
  27. data/lib/autoproj/jenkins/templates/jenkins_dependency_overrides.rb.erb +5 -0
  28. data/lib/autoproj/jenkins/templates/package-Gemfile +2 -0
  29. data/lib/autoproj/jenkins/templates/package.pipeline.erb +102 -0
  30. data/lib/autoproj/jenkins/templates/package.xml.erb +23 -0
  31. data/lib/autoproj/jenkins/templates/setup-git-credentials.pipeline.erb +8 -0
  32. data/lib/autoproj/jenkins/templates/wait-upstream.pipeline.erb +160 -0
  33. data/lib/autoproj/jenkins/test_format_converters/boost-test.xsl +347 -0
  34. data/lib/autoproj/jenkins/updater.rb +214 -0
  35. data/lib/autoproj/jenkins/version.rb +5 -0
  36. data/lib/autoproj/jenkins.rb +19 -0
  37. data/lib/autoproj-jenkins.rb +8 -0
  38. metadata +179 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f5d96120cc78350275e92584c61af35c413bcafb
4
+ data.tar.gz: d1bd6d3060ea76972df27145c9fceed592354ba9
5
+ SHA512:
6
+ metadata.gz: 6d3f538eac2dd5471fb6bcf43f5f788e7babbe67ce8ced5edc8cc73a10eefeeece7e57dc9c55eacf37c2ebbac9b38ee27b2eec1e8112a78ef14fd28ad883e80d
7
+ data.tar.gz: 31525d9641c6df34e2b1b41af1700d3436381ca40bdacb3a4ec1b8b145598babbee284c4def436bb58d686ef721c3e89cf2f7d11da01be6e5682642846bd5e3e
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.sw?
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in autoproj-jenkins.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Sylvain Joyeux
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,41 @@
1
+ # Autoproj::Jenkins
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/autoproj/jenkins`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'autoproj-jenkins'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install autoproj-jenkins
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ 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.
30
+
31
+ 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).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/autoproj-jenkins.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ t.warning = false
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'autoproj/jenkins/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "autoproj-jenkins"
8
+ spec.version = Autoproj::Jenkins::VERSION
9
+ spec.authors = ["Sylvain Joyeux"]
10
+ spec.email = ["sylvain.joyeux@m4x.org"]
11
+
12
+ spec.summary = %q{Exporting an autoproj build to a jenkins server}
13
+ spec.homepage = "https://github.com/doudou/autoproj-jenkins"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'jenkins_api_client'
22
+ spec.add_dependency 'autoproj', '>= 2.0.0.rc20'
23
+ spec.add_development_dependency "bundler", "~> 1.12"
24
+ spec.add_development_dependency "rake", "~> 11.0"
25
+ spec.add_development_dependency "minitest", "~> 5.0"
26
+ spec.add_development_dependency "simplecov"
27
+ spec.add_development_dependency "flexmock"
28
+ end
@@ -0,0 +1,100 @@
1
+ require 'autoproj/cli/inspection_tool'
2
+ require 'autoproj/jenkins'
3
+
4
+ module Autoproj
5
+ module CLI
6
+ class Jenkins < Autoproj::CLI::InspectionTool
7
+ attr_reader :server
8
+ attr_reader :updater
9
+
10
+ def initialize(ws, job_prefix: '', **options)
11
+ super(ws)
12
+ @server = Autoproj::Jenkins::Server.new(**options)
13
+ @updater = Autoproj::Jenkins::Updater.new(ws, server, job_prefix: job_prefix)
14
+ end
15
+
16
+ def parse_vcs_credentials(credentials)
17
+ results = Autoproj::Jenkins::Credentials.new
18
+ credentials.each do |argument|
19
+ credential = Autoproj::Jenkins::Credentials.parse(argument)
20
+ results.add(credential)
21
+ end
22
+ results
23
+ end
24
+
25
+ def create_or_update_buildconf_job(*package_names, force: false, dev: false, credentials_id: nil, vcs_credentials: [])
26
+ initialize_and_load
27
+
28
+ if dev
29
+ gemfile = 'buildconf-vagrant-Gemfile'
30
+ autoproj_install_path = '/opt/autoproj/bin/autoproj_install'
31
+ else
32
+ gemfile = 'buildconf-Gemfile'
33
+ autoproj_install_path = nil
34
+ end
35
+
36
+ finalize_setup(package_names, recursive: false, non_imported_packages: :return)
37
+ updater.create_or_update_buildconf_job(*package_names, gemfile: gemfile,
38
+ autoproj_install_path: autoproj_install_path, dev: dev,
39
+ credentials_id: credentials_id,
40
+ vcs_credentials: parse_vcs_credentials(vcs_credentials))
41
+ end
42
+
43
+ def add_or_update_packages(*package_names, dev: false, vcs_credentials: [])
44
+ initialize_and_load
45
+ source_packages, _ = finalize_setup(package_names, non_imported_packages: nil)
46
+ source_packages = source_packages.map do |package_name|
47
+ ws.manifest.package_definition_by_name(package_name)
48
+ end
49
+
50
+ if dev
51
+ gemfile = 'buildconf-vagrant-Gemfile'
52
+ autoproj_install_path = '/opt/autoproj/bin/autoproj_install'
53
+ else
54
+ gemfile = 'buildconf-Gemfile'
55
+ autoproj_install_path = nil
56
+ end
57
+
58
+ updater.update(
59
+ *source_packages,
60
+ gemfile: gemfile,
61
+ autoproj_install_path: autoproj_install_path,
62
+ vcs_credentials: parse_vcs_credentials(vcs_credentials))
63
+ end
64
+
65
+ # Returns the "roots" in the trigger graph
66
+ #
67
+ # The trigger graph is the inverse of the dependency graph
68
+ # (a package's dependencies are built before the package itself)
69
+ #
70
+ # @param [Array<String>] package_names the packages whose trigger
71
+ # roots we want to find
72
+ # @return [Array<String>] the trigger root packages
73
+ def trigger_root_packages(*package_names)
74
+ package_names.find_all do |pkg_name|
75
+ pkg = ws.manifest.find_autobuild_package(pkg_name)
76
+ if !pkg
77
+ raise ArgumentError, "#{pkg_name} is not a known package"
78
+ end
79
+ pkg.dependencies.all? do |dep_name|
80
+ !package_names.include?(dep_name)
81
+ end
82
+ end
83
+ end
84
+
85
+ # Trigger the build of the given packages
86
+ #
87
+ # It actually only triggers the jobs that are roots in the trigger
88
+ # graph
89
+ #
90
+ # @param [Array<String>] package_names the names of the packages to
91
+ # build
92
+ def trigger_packages(*package_names)
93
+ trigger_root_packages(*package_names).each do |pkg_name|
94
+ server.trigger_job(updater.job_name_from_package_name(pkg_name))
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+
@@ -0,0 +1,123 @@
1
+ module Autoproj
2
+ module CLI
3
+ # The 'jenkins' subcommand for autoproj
4
+ class MainJenkins < Thor
5
+ class_option :username, desc: 'username to the Jenkins CLI (a password will be requested if --password is not given)'
6
+ class_option :password, desc: 'password to the Jenkins CLI (needs --username)'
7
+ class_option :job_prefix, desc: 'string that should be used as prefix to all generated job names',
8
+ type: :string, default: ''
9
+
10
+ namespace 'jenkins'
11
+
12
+ no_commands do
13
+ def request_password
14
+ STDOUT.print "Password: "
15
+ STDOUT.flush
16
+ STDIN.noecho do |io|
17
+ io.readline.chomp
18
+ end
19
+ end
20
+
21
+ def create_ops(url, target_os: nil)
22
+ if username = options[:username]
23
+ password = options[:password] || request_password
24
+ auth = Hash[username: username,
25
+ password: password]
26
+ elsif options[:password]
27
+ raise ArgumentError, "--password given without --username"
28
+ else
29
+ auth = Hash.new
30
+ end
31
+
32
+ workspace_options = Hash.new
33
+ if target_os
34
+ names, versions = target_os.split(':')
35
+ names = names.split(',')
36
+ names << 'default'
37
+ versions = versions.split(',')
38
+ versions << 'default'
39
+ workspace_options[:os_package_resolver] = OSPackageResolver.new(operating_system: [names, versions])
40
+ end
41
+ ws = Autoproj::Workspace.default(**workspace_options)
42
+
43
+ STDERR.puts "connecting to jenkins '#{url}' with prefix '#{options[:prefix]}'"
44
+ Jenkins.new(ws,
45
+ job_prefix: options[:job_prefix],
46
+ server_url: url,
47
+ **auth)
48
+ end
49
+ end
50
+
51
+ desc 'init URL [PACKAGE NAMES]', 'initialize the jenkins server by creating the base build job, optionally restricting the build to certain packages'
52
+ option :force, desc: 'if set, delete any existing job'
53
+ option :trigger, desc: 'trigger the job once created',
54
+ type: :boolean, default: false
55
+ option :dev, desc: 'assume that the jenkins instance is a development instance under vagrant and that autoproj-jenkins is made available as /opt/autoproj-jenkins',
56
+ type: :boolean, default: false
57
+ option :target_os, desc: "the autoproj definition for the target OS as name0,name1:version0,version1",
58
+ default: nil
59
+ option :credentials_id, desc: "the credentials ID of the username/password credentials that autoproj-jenkins should use to access the jenkins CLI",
60
+ default: 'autoproj-jenkins-cli'
61
+ option :vcs_credentials, desc: 'list of vcs_type:URLs for which credentials should be provided (see documentation)',
62
+ type: :array, default: []
63
+ def init(url, *package_names)
64
+ require 'autoproj/cli/jenkins'
65
+ ops = create_ops(url, target_os: options[:target_os])
66
+
67
+ ops.create_or_update_buildconf_job(
68
+ *package_names,
69
+ credentials_id: options[:credentials_id],
70
+ force: options[:force],
71
+ vcs_credentials: options[:vcs_credentials],
72
+ dev: options[:dev])
73
+ if options[:trigger]
74
+ ops.trigger_buildconf_job
75
+ end
76
+ end
77
+
78
+
79
+ desc 'update [PACKAGE_NAMES]', 'add the following package and its dependencies to the jenkins build'
80
+ option :force, desc: 'ignore the current state, generate jobs as if nothing was ever done'
81
+ option :dev, desc: 'assume that the jenkins instance is a development instance under vagrant and that autoproj-jenkins is made available as /opt/autoproj-jenkins',
82
+ type: :boolean, default: false
83
+ option :vcs_credentials, desc: 'list of vcs_type:URLs for which credentials should be provided (see documentation)',
84
+ type: :array, default: []
85
+ def update(url, *package_names)
86
+ require 'autoproj/cli/jenkins'
87
+ ops = create_ops(url)
88
+ Autoproj.report(silent: !options[:debug], debug: options[:debug]) do
89
+ updated_jobs = ops.add_or_update_packages(*package_names, dev: options[:dev], vcs_credentials: options[:vcs_credentials])
90
+ updated_jobs.sort.each do |job_name|
91
+ puts job_name
92
+ end
93
+ end
94
+ end
95
+
96
+ desc 'postprocess-tests OUTPUT_DIR [PACKAGE_NAME]', 'postprocesses test result formatted in various formats to convert them into the JUnit XML format understood by Jenkins',
97
+ hide: true
98
+ option :after, desc: "if provided, any report file that is older than this file will be ignored",
99
+ default: nil
100
+ def postprocess_tests(output_dir, *package_names)
101
+ require 'autoproj/cli/test_postprocessing'
102
+ ops = TestPostprocessing.new(Workspace.default)
103
+ if options[:after]
104
+ reference_time = File.stat(options[:after]).mtime
105
+ end
106
+ ops.process(output_dir, *package_names, after: reference_time)
107
+ end
108
+
109
+ desc 'relativize ROOT_DIR INPUT_TEXT OUTPUT_TEXT', 'replaces INPUT_TEXT by OUTPUT_TEXT in all files that can contain absolute paths',
110
+ hide: true
111
+ def relativize(root_dir, input_text, output_text)
112
+ require 'autoproj/jenkins'
113
+ relativize = Autoproj::Jenkins::Relativize.new(Pathname.new(root_dir), input_text, output_text)
114
+ processed_paths = relativize.process
115
+ puts "modified #{processed_paths.size} file"
116
+ processed_paths.each do |p|
117
+ puts " #{p}"
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+
@@ -0,0 +1,85 @@
1
+ require 'autoproj/cli/inspection_tool'
2
+
3
+ module Autoproj
4
+ module CLI
5
+ class TestPostprocessing < Autoproj::CLI::InspectionTool
6
+ attr_reader :convertions
7
+
8
+ def self.test_format_converter_dir
9
+ File.expand_path(
10
+ File.join('..', 'jenkins', 'test_format_converters'),
11
+ __dir__)
12
+ end
13
+
14
+ TEST_RESULT_CONVERTERS = Hash[
15
+ '*.boost.xml' => File.join(test_format_converter_dir, 'boost-test.xsl'),
16
+ '*.junit.xml' => nil
17
+ ]
18
+
19
+ def initialize(ws, convertions: TEST_RESULT_CONVERTERS)
20
+ super(ws)
21
+ @convertions = convertions
22
+ end
23
+
24
+ class ConvertionFailed < RuntimeError; end
25
+
26
+ def process(output_dir, *package_names, after: nil)
27
+ initialize_and_load
28
+ source_packages, _ = finalize_setup(package_names, recursive: false)
29
+ source_packages = source_packages.map do |package_name|
30
+ ws.manifest.package_definition_by_name(package_name)
31
+ end
32
+
33
+ has_failures = false
34
+ source_packages.each do |pkg|
35
+ utility = pkg.autobuild.test_utility
36
+ found_something = false
37
+ convertions.each do |glob, xsl|
38
+ Dir.glob(File.join(utility.target_dir, glob)) do |input_file|
39
+ input_mtime = File.stat(input_file).mtime
40
+ if after && input_mtime < after
41
+ Autoproj.message " ignoring #{input_file}, its modification time is #{input_mtime} which is after #{after}"
42
+ next
43
+ end
44
+
45
+ found_something = true
46
+ FileUtils.mkdir_p output_dir
47
+ output_file = File.join(output_dir, File.basename(input_file))
48
+ begin
49
+ if xsl
50
+ xsl_process(input_file, xsl, output_file)
51
+ else
52
+ FileUtils.copy_file input_file, output_file
53
+ end
54
+ Autoproj.message " generated #{output_file} from #{input_file} for #{pkg.name}"
55
+ rescue Exception => e
56
+ Autoproj.error e.message
57
+ has_failures = true
58
+ end
59
+ end
60
+ end
61
+
62
+ if !found_something
63
+ Autoproj.message "found no test results for #{pkg.name}"
64
+ end
65
+ end
66
+ ensure
67
+ if has_failures
68
+ raise ConvertionFailed, "some files failed to convert, see output for more details"
69
+ end
70
+ end
71
+
72
+ def xsl_process(input_file, stylesheet, output_file)
73
+ if File.read(input_file).strip.empty?
74
+ return
75
+ end
76
+
77
+ if !system('saxonb-xslt', "-o:#{output_file}", "-xsl:#{stylesheet}", "-s:#{input_file}")
78
+ raise ArgumentError, "failed to convert #{input_file} using #{stylesheet}"
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+
@@ -0,0 +1,67 @@
1
+ module Autoproj::Jenkins
2
+ # Management of the mapping from a VCS description to the corresponding
3
+ # Jenkins credential
4
+ class Credentials
5
+ Credential = Struct.new :vcs, :protocol, :host do
6
+ # Whether this credential object matches the given VCS
7
+ #
8
+ # @param [Autoproj::VCSDefinition] vcs
9
+ def matches?(vcs)
10
+ return if vcs.type.to_sym != self.vcs
11
+ vcs_url = URI.parse(vcs.url)
12
+ vcs_url.scheme == protocol &&
13
+ vcs_url.host == host
14
+ end
15
+
16
+ # The ID of the jenkins credential
17
+ def jenkins_id
18
+ "autoproj-#{vcs}-#{protocol}-#{host}"
19
+ end
20
+ end
21
+
22
+ # Parse a string that represents a single credential and return it
23
+ #
24
+ # @param [String] string a string of the form "vcs_type:URI", e.g.
25
+ # "git:https://github.com"
26
+ # @return [Credential]
27
+ def self.parse(string)
28
+ vcs, *uri = string.split(':')
29
+ if !vcs || vcs.empty? || uri.empty?
30
+ raise ArgumentError, "expected VCS:URL but got #{string}"
31
+ elsif !Autoproj::Jenkins.vcs_supported?(vcs)
32
+ raise UnhandledVCS, "#{vcs} is not a supported VCS"
33
+ end
34
+ uri = URI.parse(uri.join(':'))
35
+ Credential.new(vcs.to_sym, uri.scheme, uri.host)
36
+ end
37
+
38
+ attr_reader :credentials
39
+
40
+ def initialize
41
+ @credentials = Hash.new
42
+ end
43
+
44
+ def empty?
45
+ credentials.empty?
46
+ end
47
+
48
+ def [](vcs)
49
+ credentials[vcs.to_sym] || Array.new
50
+ end
51
+
52
+ def add(credential)
53
+ (credentials[credential.vcs] ||= Array.new) << credential
54
+ end
55
+
56
+ # Return the credential description for the given VCS
57
+ #
58
+ # @param [Autoproj::VCSDefinition] vcs
59
+ # @return [nil,Credential]
60
+ def for(vcs)
61
+ self[vcs.type].find do |c|
62
+ c.matches?(vcs)
63
+ end
64
+ end
65
+ end
66
+ end
67
+
@@ -0,0 +1,7 @@
1
+ module Autoproj::Jenkins
2
+ # Exception raised when trying to handle a package whose VCS we don't
3
+ # integrate
4
+ class UnhandledVCS < RuntimeError
5
+ end
6
+ end
7
+
@@ -0,0 +1,74 @@
1
+ module Autoproj
2
+ module Jenkins
3
+ # Process files that might contain full paths and transform them by
4
+ # replacing the original path with a new one
5
+ class Relativize
6
+ FILE_PATTERN = Regexp.union(
7
+ /\/pkgconfig\/.*.pc$/,
8
+ /\.cmake$/,
9
+ /\.txt$/
10
+ )
11
+
12
+ # The path of the root to be filtered
13
+ #
14
+ # @return [Pathname]
15
+ attr_reader :root_path
16
+
17
+ # The text to be replaced
18
+ #
19
+ # @return [String]
20
+ attr_reader :original_text
21
+
22
+ # The text replacing {#original_text}
23
+ #
24
+ # @return [String]
25
+ attr_reader :replacement_text
26
+
27
+ # A pattern used to filter which files should be filtered
28
+ #
29
+ # @return [#===]
30
+ attr_reader :file_pattern
31
+
32
+ def initialize(root_path, original_text, replacement_text, file_pattern: FILE_PATTERN)
33
+ @root_path = root_path
34
+ @original_text = original_text
35
+ @replacement_text = replacement_text
36
+ @file_pattern = file_pattern
37
+ end
38
+
39
+ # Process all files matching {#file_pattern} within {#root_path}
40
+ #
41
+ # @return [Array<Pathname>] the files that have been processed
42
+ def process
43
+ processed_files = Array.new
44
+ root_path.find do |candidate|
45
+ if candidate.file? && (file_pattern === candidate.to_s)
46
+ if process_file(candidate)
47
+ processed_files << candidate
48
+ end
49
+ end
50
+ end
51
+ processed_files
52
+ end
53
+
54
+ # @api private
55
+ #
56
+ # Replaces text in a given file
57
+ #
58
+ # @return [Boolean] true if the pattern was found
59
+ def process_file(path)
60
+ replaced = false
61
+ filtered = path.each_line.map do |line|
62
+ line.gsub(original_text) { replaced = true; replacement_text }
63
+ end
64
+ if replaced
65
+ path.open('w') do |io|
66
+ io.puts filtered.join
67
+ end
68
+ true
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+