modulesync 4.2.0 → 4.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79b92d3120eabd8034f28fec21a10f0a5d67327cb4317018067493260b87082d
4
- data.tar.gz: 4c3fffab7cd0aa697a90164485b36efad501e6432170373aabe1e911a9d8c7c1
3
+ metadata.gz: f9495a02958b177ad86c12e69ee471e77e03058d0067c8e814c991607f730e29
4
+ data.tar.gz: 7c349b62e686857b15d423bbf5a584e0a7e4e12a2359e2bb6c5b226e87dcb78c
5
5
  SHA512:
6
- metadata.gz: 98cd5f5b84af7b68b042ecb240f995e3d597eae0e9cb86eab1001f56df7ff0ac22916cd4c21654409c79955cfb74455a53a4fa748685b8764819c7dbf53f334b
7
- data.tar.gz: 589f8009a87934a82935434e4e657f87065efc179e7326f713a74bf3c5c403ef3e93b11856532e3b2f5466e24d24afacceb3b2cddd82af78f52da0871a6f27c2
6
+ metadata.gz: 15b92a3cfc0a9681bb879d0a86eec2a04e1649b7799b4762cf58617b770cfada67041689630cb05f843b317565dee11626b35761fe0e8362c991c73a3f37e5c8
7
+ data.tar.gz: 073b867ed92cf55d1f61fde7fea9bdf46f28b161ceda6f0e4f84c05433c4cdd6fc634eec9fb85af2a13ea684474b86fef5ca16dabebf012fe3967c0ac69d27e4
@@ -16,7 +16,7 @@ jobs:
16
16
  outputs:
17
17
  ruby: ${{ steps.ruby.outputs.versions }}
18
18
  steps:
19
- - uses: actions/checkout@v5
19
+ - uses: actions/checkout@v7
20
20
  - name: Setup ruby
21
21
  uses: ruby/setup-ruby@v1
22
22
  with:
@@ -25,7 +25,7 @@ jobs:
25
25
  - name: Run linter
26
26
  run: bundle exec rake rubocop
27
27
  - id: ruby
28
- uses: voxpupuli/ruby-version@v1
28
+ uses: voxpupuli/ruby-version@v2
29
29
 
30
30
  test:
31
31
  name: "Ruby ${{ matrix.ruby }}"
@@ -36,7 +36,7 @@ jobs:
36
36
  matrix:
37
37
  ruby: ${{ fromJSON(needs.rubocop_and_matrix.outputs.ruby) }}
38
38
  steps:
39
- - uses: actions/checkout@v5
39
+ - uses: actions/checkout@v7
40
40
  - name: Install Ruby ${{ matrix.ruby }}
41
41
  uses: ruby/setup-ruby@v1
42
42
  with:
@@ -15,7 +15,7 @@ jobs:
15
15
  name: Build the gem
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
- - uses: actions/checkout@v5
18
+ - uses: actions/checkout@v7
19
19
  - name: Install Ruby
20
20
  uses: ruby/setup-ruby@v1
21
21
  with:
@@ -24,7 +24,7 @@ jobs:
24
24
  shell: bash
25
25
  run: gem build --verbose *.gemspec
26
26
  - name: Upload gem to GitHub cache
27
- uses: actions/upload-artifact@v4
27
+ uses: actions/upload-artifact@v7
28
28
  with:
29
29
  name: gem-artifact
30
30
  path: '*.gem'
@@ -39,7 +39,7 @@ jobs:
39
39
  contents: write # clone repo and create release
40
40
  steps:
41
41
  - name: Download gem from GitHub cache
42
- uses: actions/download-artifact@v5
42
+ uses: actions/download-artifact@v8
43
43
  with:
44
44
  name: gem-artifact
45
45
  - name: Create Release
@@ -56,7 +56,7 @@ jobs:
56
56
  packages: write # publish to rubygems.pkg.github.com
57
57
  steps:
58
58
  - name: Download gem from GitHub cache
59
- uses: actions/download-artifact@v5
59
+ uses: actions/download-artifact@v8
60
60
  with:
61
61
  name: gem-artifact
62
62
  - name: Publish gem to GitHub packages
@@ -73,10 +73,10 @@ jobs:
73
73
  id-token: write # rubygems.org authentication
74
74
  steps:
75
75
  - name: Download gem from GitHub cache
76
- uses: actions/download-artifact@v5
76
+ uses: actions/download-artifact@v8
77
77
  with:
78
78
  name: gem-artifact
79
- - uses: rubygems/configure-rubygems-credentials@v1.0.0
79
+ - uses: rubygems/configure-rubygems-credentials@v2.1.0
80
80
  - name: Publish gem to rubygems.org
81
81
  shell: bash
82
82
  run: gem push *.gem
@@ -92,7 +92,7 @@ jobs:
92
92
  - release-to-rubygems
93
93
  steps:
94
94
  - name: Download gem from GitHub cache
95
- uses: actions/download-artifact@v5
95
+ uses: actions/download-artifact@v8
96
96
  with:
97
97
  name: gem-artifact
98
98
  - name: Install Ruby
data/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [4.3.0](https://github.com/voxpupuli/modulesync/tree/4.3.0) (2026-07-09)
6
+
7
+ [Full Changelog](https://github.com/voxpupuli/modulesync/compare/4.2.0...4.3.0)
8
+
9
+ **Implemented enhancements:**
10
+
11
+ - Add Ruby 4.0 support [\#338](https://github.com/voxpupuli/modulesync/pull/338) ([dependabot[bot]](https://github.com/apps/dependabot))
12
+
13
+ **Fixed bugs:**
14
+
15
+ - fix: validate PR credentials and retry PR creation [\#347](https://github.com/voxpupuli/modulesync/pull/347) ([rwaffen](https://github.com/rwaffen))
16
+
17
+ **Merged pull requests:**
18
+
19
+ - Update cucumber requirement from ~\> 10.1 to ~\> 11.0 [\#344](https://github.com/voxpupuli/modulesync/pull/344) ([dependabot[bot]](https://github.com/apps/dependabot))
20
+ - Update voxpupuli-rubocop requirement from ~\> 5.1.0 to ~\> 5.2.0 [\#341](https://github.com/voxpupuli/modulesync/pull/341) ([dependabot[bot]](https://github.com/apps/dependabot))
21
+ - Update voxpupuli-rubocop requirement from ~\> 5.0.0 to ~\> 5.1.0 [\#334](https://github.com/voxpupuli/modulesync/pull/334) ([dependabot[bot]](https://github.com/apps/dependabot))
22
+
5
23
  ## [4.2.0](https://github.com/voxpupuli/modulesync/tree/4.2.0) (2025-10-16)
6
24
 
7
25
  [Full Changelog](https://github.com/voxpupuli/modulesync/compare/4.1.0...4.2.0)
@@ -88,7 +88,7 @@ Feature: Create a pull-request/merge-request after update
88
88
  """
89
89
  <%= @configs['name'] %>
90
90
  """
91
- When I run `msync update --noop --pr`
91
+ When I run `msync update --message "Update test" --pr`
92
92
  Then the stderr should contain "A token is required to use services from gitlab"
93
93
  And the exit status should be 1
94
94
  And the puppet module "puppet-test" from "fakenamespace" should have no commits made by "Aruba"
@@ -32,6 +32,16 @@ module ModuleSync
32
32
  repo.diff("#{local_branch}..origin/#{remote_branch}").any?
33
33
  end
34
34
 
35
+ # This method checks if the source branch is ahead of the target branch in the remote repository.
36
+ # It does this by checking if there are any commits in the source branch that are not in the target branch.
37
+ def remote_branch_ahead?(source_branch, target_branch)
38
+ return false unless remote_branch_exists?(source_branch) && remote_branch_exists?(target_branch)
39
+
40
+ log = repo.log(1).between("origin/#{target_branch}", "origin/#{source_branch}")
41
+ commits = log.respond_to?(:execute) ? log.execute : log
42
+ commits.any?
43
+ end
44
+
35
45
  def default_branch
36
46
  # `Git.default_branch` requires ruby-git >= 1.17.0
37
47
  return Git.default_branch(repo.dir) if Git.respond_to? :default_branch
@@ -68,13 +68,31 @@ module ModuleSync
68
68
  namespace: repository_namespace,
69
69
  title: ModuleSync.options[:pr_title],
70
70
  message: ModuleSync.options[:message],
71
- source_branch: ModuleSync.options[:remote_branch] || ModuleSync.options[:branch] || repository.default_branch,
72
- target_branch: ModuleSync.options[:pr_target_branch] || repository.default_branch,
71
+ source_branch: pull_request_source_branch,
72
+ target_branch: pull_request_target_branch,
73
73
  labels: ModuleSync::Util.parse_list(ModuleSync.options[:pr_labels]),
74
74
  noop: ModuleSync.options[:noop],
75
75
  )
76
76
  end
77
77
 
78
+ # This method checks if the pull request branch is ready to be opened.
79
+ # It does this by checking if the source branch is ahead of the target branch in the remote repository.
80
+ def pull_request_branch_ready?
81
+ repository.remote_branch_ahead?(pull_request_source_branch, pull_request_target_branch)
82
+ end
83
+
84
+ # This method returns the source branch for the pull request.
85
+ # It first checks if the `remote_branch` option is set, then checks if the `branch` option is set, and finally defaults to the repository's default branch.
86
+ def pull_request_source_branch
87
+ ModuleSync.options[:remote_branch] || ModuleSync.options[:branch] || repository.default_branch
88
+ end
89
+
90
+ # This method returns the target branch for the pull request.
91
+ # It first checks if the `pr_target_branch` option is set, and if not, it defaults to the repository's default branch.
92
+ def pull_request_target_branch
93
+ ModuleSync.options[:pr_target_branch] || repository.default_branch
94
+ end
95
+
78
96
  private
79
97
 
80
98
  def _repository_remote
data/lib/modulesync.rb CHANGED
@@ -49,8 +49,8 @@ module ModuleSync
49
49
  def self.find_template_files(local_template_dir)
50
50
  if File.exist?(local_template_dir)
51
51
  Find.find(local_template_dir).find_all { |p| p =~ /.erb$/ && !File.directory?(p) }
52
- .collect { |p| p.chomp('.erb') }
53
- .to_a
52
+ .collect { |p| p.chomp('.erb') }
53
+ .to_a
54
54
  else
55
55
  warn "#{local_template_dir} does not exist. " \
56
56
  'Check that you are working in your module configs directory or ' \
@@ -152,7 +152,7 @@ module ModuleSync
152
152
  new = puppet_module.bump(options[:message], options[:changelog])
153
153
  puppet_module.repository.tag(new, options[:tag_pattern]) if options[:tag]
154
154
  end
155
- pushed && options[:pr] && puppet_module.open_pull_request
155
+ options[:pr] && (pushed || puppet_module.pull_request_branch_ready?) && puppet_module.open_pull_request
156
156
  end
157
157
  end
158
158
 
@@ -174,9 +174,13 @@ module ModuleSync
174
174
  local_files = find_template_files(local_template_dir)
175
175
  module_files = relative_names(local_files, local_template_dir)
176
176
 
177
+ # Initialize every Git service before updating any repository.
178
+ # This validates PR credentials before an update can commit or push changes.
179
+ initialized_modules = managed_modules
180
+ initialized_modules.each(&:git_service) if options[:pr]
181
+
177
182
  errors = false
178
- # managed_modules is either an array or a hash
179
- managed_modules.each do |puppet_module|
183
+ initialized_modules.each do |puppet_module|
180
184
  manage_module(puppet_module, module_files, defaults)
181
185
  rescue ModuleSync::Error, Git::Error => e
182
186
  message = e.message || 'Error during `update`'
data/modulesync.gemspec CHANGED
@@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'modulesync'
8
- spec.version = '4.2.0'
8
+ spec.version = '4.3.0'
9
9
  spec.authors = ['Vox Pupuli']
10
10
  spec.email = ['voxpupuli@groups.io']
11
11
  spec.summary = 'Puppet Module Synchronizer'
@@ -19,10 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_development_dependency 'aruba', '~>2.0'
22
- spec.add_development_dependency 'cucumber', '~> 10.1'
22
+ spec.add_development_dependency 'cucumber', '~> 11.0'
23
23
  spec.add_development_dependency 'rake', '~> 13.2', '>= 13.2.1'
24
24
  spec.add_development_dependency 'rspec', '~> 3.13'
25
- spec.add_development_dependency 'voxpupuli-rubocop', '~> 5.0.0'
25
+ spec.add_development_dependency 'voxpupuli-rubocop', '~> 5.2.0'
26
26
 
27
27
  spec.add_dependency 'git', '>= 3.0', '< 5'
28
28
  spec.add_dependency 'gitlab', '>=4', '<7'
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe ModuleSync::Repository do
6
+ subject(:repository) do
7
+ described_class.new(directory: '/tmp/example', remote: 'https://github.com/example/repository.git')
8
+ end
9
+
10
+ let(:remote_branches) do
11
+ [
12
+ instance_double(Git::Branch, name: 'main'),
13
+ instance_double(Git::Branch, name: 'modulesync'),
14
+ ]
15
+ end
16
+ let(:branches) { instance_double(Git::Branches, remote: remote_branches) }
17
+ let(:log) { instance_double(Git::Log) }
18
+ let(:git) { instance_double(Git::Base, branches: branches, log: log) }
19
+
20
+ before do
21
+ repository.instance_variable_set(:@git, git)
22
+ end
23
+
24
+ it 'detects commits on a remote source branch that are missing from the target branch' do
25
+ allow(log).to receive(:between).with('origin/main', 'origin/modulesync').and_return(log)
26
+ allow(log).to receive(:execute).and_return([instance_double(Git::Object::Commit)])
27
+
28
+ expect(repository.remote_branch_ahead?('modulesync', 'main')).to be true
29
+ end
30
+
31
+ it 'rejects a source branch that does not exist remotely' do
32
+ remote_branches.pop
33
+
34
+ expect(repository.remote_branch_ahead?('modulesync', 'main')).to be false
35
+ end
36
+ end
@@ -7,9 +7,12 @@ describe ModuleSync::SourceCode do
7
7
  described_class.new('namespace/name', nil)
8
8
  end
9
9
 
10
+ let(:repository) { instance_double(ModuleSync::Repository, default_branch: 'main') }
11
+
10
12
  before do
11
13
  options = ModuleSync.config_defaults.merge({ git_base: 'file:///tmp/dummy' })
12
14
  ModuleSync.instance_variable_set :@options, options
15
+ allow(ModuleSync::Repository).to receive(:new).and_return(repository)
13
16
  end
14
17
 
15
18
  it 'has a repository namespace sets to "namespace"' do
@@ -19,4 +22,11 @@ describe ModuleSync::SourceCode do
19
22
  it 'has a repository name sets to "name"' do
20
23
  expect(subject.repository_name).to eq 'name'
21
24
  end
25
+
26
+ it 'reports a remote source branch ahead of the PR target as ready' do
27
+ ModuleSync.options[:branch] = 'modulesync'
28
+ allow(repository).to receive(:remote_branch_ahead?).with('modulesync', 'main').and_return(true)
29
+
30
+ expect(subject.pull_request_branch_ready?).to be true
31
+ end
22
32
  end
@@ -12,5 +12,56 @@ describe ModuleSync do
12
12
  options = { managed_modules_conf: 'test_file.yml' }
13
13
  described_class.update(options)
14
14
  end
15
+
16
+ it 'validates PR credentials for every module before managing any module' do
17
+ allow(ModuleSync::Util).to receive(:parse_config).with('./config_defaults.yml').and_return({})
18
+ puppet_module = double
19
+ allow(described_class).to receive_messages(find_template_files: [], managed_modules: [puppet_module])
20
+
21
+ expect(puppet_module).to receive(:git_service).ordered
22
+ expect(described_class).to receive(:manage_module).with(puppet_module, [], {}).ordered
23
+
24
+ described_class.update(pr: true)
25
+ end
26
+
27
+ it 'does not manage modules when PR credentials are missing' do
28
+ allow(ModuleSync::Util).to receive(:parse_config).with('./config_defaults.yml').and_return({})
29
+ puppet_module = double
30
+ allow(described_class).to receive_messages(find_template_files: [], managed_modules: [puppet_module])
31
+ allow(puppet_module).to receive(:git_service)
32
+ .and_raise(ModuleSync::GitService::MissingCredentialsError, 'missing token')
33
+ expect(described_class).not_to receive(:manage_module)
34
+
35
+ expect { described_class.update(pr: true) }
36
+ .to raise_error(ModuleSync::GitService::MissingCredentialsError, 'missing token')
37
+ end
38
+ end
39
+
40
+ context '::manage_module' do
41
+ let(:repository) { instance_double(ModuleSync::Repository) }
42
+ let(:settings) { instance_double(ModuleSync::Settings) }
43
+ let(:puppet_module) do
44
+ instance_double(ModuleSync::PuppetModule,
45
+ given_name: 'puppet-test',
46
+ repository: repository,
47
+ repository_name: 'puppet-test',
48
+ repository_namespace: 'example')
49
+ end
50
+
51
+ before do
52
+ described_class.instance_variable_set(:@options, described_class.config_defaults.merge(pr: true))
53
+ allow(repository).to receive_messages(prepare_workspace: nil, submit_changes: false)
54
+ allow(puppet_module).to receive(:path).with(ModuleSync::MODULE_CONF_FILE).and_return('module-config.yml')
55
+ allow(ModuleSync::Util).to receive(:parse_config).with('module-config.yml').and_return({})
56
+ allow(ModuleSync::Settings).to receive(:new).and_return(settings)
57
+ allow(settings).to receive_messages(unmanaged_files: [], managed_files: [])
58
+ allow(puppet_module).to receive(:pull_request_branch_ready?).and_return(true)
59
+ end
60
+
61
+ it 'opens a requested PR for an unchanged branch that is ahead of its target' do
62
+ expect(puppet_module).to receive(:open_pull_request)
63
+
64
+ described_class.manage_module(puppet_module, [], {})
65
+ end
15
66
  end
16
67
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modulesync
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vox Pupuli
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '10.1'
32
+ version: '11.0'
33
33
  type: :development
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '10.1'
39
+ version: '11.0'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: rake
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -77,14 +77,14 @@ dependencies:
77
77
  requirements:
78
78
  - - "~>"
79
79
  - !ruby/object:Gem::Version
80
- version: 5.0.0
80
+ version: 5.2.0
81
81
  type: :development
82
82
  prerelease: false
83
83
  version_requirements: !ruby/object:Gem::Requirement
84
84
  requirements:
85
85
  - - "~>"
86
86
  - !ruby/object:Gem::Version
87
- version: 5.0.0
87
+ version: 5.2.0
88
88
  - !ruby/object:Gem::Dependency
89
89
  name: git
90
90
  requirement: !ruby/object:Gem::Requirement
@@ -241,6 +241,7 @@ files:
241
241
  - spec/unit/module_sync/git_service/github_spec.rb
242
242
  - spec/unit/module_sync/git_service/gitlab_spec.rb
243
243
  - spec/unit/module_sync/git_service_spec.rb
244
+ - spec/unit/module_sync/repository_spec.rb
244
245
  - spec/unit/module_sync/settings_spec.rb
245
246
  - spec/unit/module_sync/source_code_spec.rb
246
247
  - spec/unit/module_sync_spec.rb
@@ -262,7 +263,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
262
263
  - !ruby/object:Gem::Version
263
264
  version: '0'
264
265
  requirements: []
265
- rubygems_version: 3.6.9
266
+ rubygems_version: 4.0.10
266
267
  specification_version: 4
267
268
  summary: Puppet Module Synchronizer
268
269
  test_files: []