modulesync 4.5.0 → 4.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8ccf6e8a41d6921a17a4171187f6ee1a63478ac231125c5bede16e52e9a2082
4
- data.tar.gz: 5e65db0d1e4c83cdcaa4bd6537bb2080f5318551ae6ce10cbb62c1897217910d
3
+ metadata.gz: b2b460c8084356169de564afc59efed808706c5a08aa3501f8fe1f6f3930fbd3
4
+ data.tar.gz: 637c12c11b80a3545949cb8e28e3c941e522d9b41fb2a7ba180a9a51e832b223
5
5
  SHA512:
6
- metadata.gz: c16e333b6556c1141385a8d3e17c7a53c74b9fc74d648cdab58a655d8f055155c33ed37cb89d90ea16905b3b28ac9bb3fb98b520748ace87bd4a5dca85c33beb
7
- data.tar.gz: fa3990dfd4ea5c958146832effa2292c5388b8100897a28d7548c3954c385da17fdb16cf1daf7c96aece91c951b69624106c4dcf487b36d17ab06cae67b6a775
6
+ metadata.gz: 431a276bdaa1fef73a7cc9d670d5c7ee398c7eb82a84e32dd9ce14959d7f001656f7b8bada9d906f76d42ae51affa9298e9b03d629c54ae497b03485d63cc4c1
7
+ data.tar.gz: e9a0a7842ff1f908a7ffc81f99045186068919b052c5ffd0f4ed886d50ae1e6fd4338726db141333e0326221b79077b837e7e65da0f93bd032677f20c218e8ee
@@ -16,16 +16,16 @@ jobs:
16
16
  outputs:
17
17
  ruby: ${{ steps.ruby.outputs.versions }}
18
18
  steps:
19
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
19
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
20
  - name: Setup ruby
21
- uses: ruby/setup-ruby@6e5d382445ae5590b7449d8b3bc8cb1c2c27f617 # v1.317.0
21
+ uses: ruby/setup-ruby@e8944e80fb94b20106697132f8c20c665fab29e9 # v1.325.0
22
22
  with:
23
23
  ruby-version: "3.4"
24
24
  bundler-cache: true
25
25
  - name: Run linter
26
26
  run: bundle exec rake rubocop
27
27
  - id: ruby
28
- uses: voxpupuli/ruby-version@656370e339050da63b86b1c631f5f88a3f4c0803 # 1.0.1
28
+ uses: voxpupuli/ruby-version@be0467ce7366d206b2e87f5b7a032a030120b7cb # 2.0.0
29
29
 
30
30
  test:
31
31
  name: "Ruby ${{ matrix.ruby }}"
@@ -36,9 +36,9 @@ jobs:
36
36
  matrix:
37
37
  ruby: ${{ fromJSON(needs.rubocop_and_matrix.outputs.ruby) }}
38
38
  steps:
39
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
39
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
40
40
  - name: Install Ruby ${{ matrix.ruby }}
41
- uses: ruby/setup-ruby@6e5d382445ae5590b7449d8b3bc8cb1c2c27f617 # v1.317.0
41
+ uses: ruby/setup-ruby@e8944e80fb94b20106697132f8c20c665fab29e9 # v1.325.0
42
42
  with:
43
43
  ruby-version: ${{ matrix.ruby }}
44
44
  bundler-cache: true
@@ -14,4 +14,4 @@ jobs:
14
14
  permissions:
15
15
  pull-requests: write
16
16
  steps:
17
- - uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0
17
+ - uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0
@@ -15,9 +15,9 @@ jobs:
15
15
  name: Build the gem
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
18
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
19
19
  - name: Install Ruby
20
- uses: ruby/setup-ruby@6e5d382445ae5590b7449d8b3bc8cb1c2c27f617 # v1.317.0
20
+ uses: ruby/setup-ruby@e8944e80fb94b20106697132f8c20c665fab29e9 # v1.325.0
21
21
  with:
22
22
  ruby-version: 'ruby'
23
23
  - name: Build gem
@@ -96,7 +96,7 @@ jobs:
96
96
  with:
97
97
  name: gem-artifact
98
98
  - name: Install Ruby
99
- uses: ruby/setup-ruby@6e5d382445ae5590b7449d8b3bc8cb1c2c27f617 # v1.317.0
99
+ uses: ruby/setup-ruby@e8944e80fb94b20106697132f8c20c665fab29e9 # v1.325.0
100
100
  with:
101
101
  ruby-version: 'ruby'
102
102
  - name: Wait for release to propagate
data/CHANGELOG.md CHANGED
@@ -2,9 +2,25 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [4.5.0](https://github.com/voxpupuli/modulesync/tree/4.5.0) (2026-07-15)
5
+ ## [4.6.1](https://github.com/voxpupuli/modulesync/tree/4.6.1) (2026-09-25)
6
6
 
7
- [Full Changelog](https://github.com/voxpupuli/modulesync/compare/v4.4.0...4.5.0)
7
+ [Full Changelog](https://github.com/voxpupuli/modulesync/compare/v4.6.0...4.6.1)
8
+
9
+ **Fixed bugs:**
10
+
11
+ - fix: rebase existing remote branches after a fresh clone [\#376](https://github.com/voxpupuli/modulesync/pull/376) ([rwaffen](https://github.com/rwaffen))
12
+
13
+ ## [v4.6.0](https://github.com/voxpupuli/modulesync/tree/v4.6.0) (2026-07-31)
14
+
15
+ [Full Changelog](https://github.com/voxpupuli/modulesync/compare/v4.5.0...v4.6.0)
16
+
17
+ **Implemented enhancements:**
18
+
19
+ - feat: add support for cryptographic signing and signoff options in commits and tags [\#368](https://github.com/voxpupuli/modulesync/pull/368) ([rwaffen](https://github.com/rwaffen))
20
+
21
+ ## [v4.5.0](https://github.com/voxpupuli/modulesync/tree/v4.5.0) (2026-07-15)
22
+
23
+ [Full Changelog](https://github.com/voxpupuli/modulesync/compare/v4.4.0...v4.5.0)
8
24
 
9
25
  **Implemented enhancements:**
10
26
 
data/README.md CHANGED
@@ -166,6 +166,17 @@ Not required for gerrit.
166
166
  msync update --amend --force
167
167
  ```
168
168
 
169
+ Use `--sign` to cryptographically sign commits and tags with Git's configured signing key and backend.
170
+ ModuleSync only passes Git's signing flags (`git commit --gpg-sign` and `git tag --sign`).
171
+ It does not configure the signing key, format, or backend; users must configure these settings in their Git configuration.
172
+ Use `--signoff` to add Git's `Signed-off-by` trailer.
173
+ The options can be combined and also apply to the release commit created by `--bump`.
174
+ When combined with `--tag`, `--sign` creates signed annotated tags using `git tag --sign`.
175
+
176
+ ```shell
177
+ msync update -m "Commit message" --sign --signoff
178
+ ```
179
+
169
180
  #### Automating Updates
170
181
 
171
182
  You can install a pre-push git hook to automatically clone, update, and push modules upon pushing changes to the configuration directory.
@@ -365,6 +376,12 @@ If you wish to tag the modified repositories with the newly bumped version, you
365
376
  msync update -m "Commit message" --bump --tag
366
377
  ```
367
378
 
379
+ Add `--sign` to cryptographically sign both the release commit and tag:
380
+
381
+ ```shell
382
+ msync update -m "Commit message" --bump --tag --sign
383
+ ```
384
+
368
385
  #### Setting the tag pattern
369
386
 
370
387
  You can also set the format of the tag to be used (`printf`-formatted) by setting the `tag_pattern` option:
data/features/cli.feature CHANGED
@@ -28,6 +28,11 @@ Feature: CLI
28
28
  msync clone
29
29
  """
30
30
 
31
+ Scenario: Update help lists commit signing options
32
+ When I successfully run `msync update --help`
33
+ Then the output should match /--sign/
34
+ And the output should match /--signoff/
35
+
31
36
  Scenario: When overriding a setting from the config file on the command line
32
37
  Given a puppet module "puppet-test" from "fakenamespace"
33
38
  And a file named "managed_modules.yml" with:
@@ -88,6 +88,14 @@ module ModuleSync
88
88
  type: :boolean,
89
89
  desc: 'Amend previous commit',
90
90
  default: false
91
+ option :sign,
92
+ type: :boolean,
93
+ desc: 'Cryptographically sign commits and tags',
94
+ default: false
95
+ option :signoff,
96
+ type: :boolean,
97
+ desc: 'Add a Signed-off-by trailer to commits',
98
+ default: false
91
99
  option :force,
92
100
  type: :boolean,
93
101
  desc: 'Force push amended commit',
@@ -25,13 +25,13 @@ module ModuleSync
25
25
  end
26
26
  end
27
27
 
28
- def bump(message, changelog = false)
28
+ def bump(message, changelog = false, signing_options = {})
29
29
  m = Blacksmith::Modulefile.new path('metadata.json')
30
30
  new = m.bump!
31
31
  puts "Bumped to version #{new}"
32
32
  repository.git.add('metadata.json')
33
33
  update_changelog(new, message) if changelog
34
- repository.git.commit("Release version #{new}")
34
+ repository.commit_changes("Release version #{new}", signing_options)
35
35
  repository.git.push
36
36
  new
37
37
  end
@@ -105,16 +105,17 @@ module ModuleSync
105
105
  puts "Overriding any local changes to repository in '#{@directory}'"
106
106
  git.fetch 'origin', prune: true unless operate_offline
107
107
  git.reset_hard
108
- rebase_target = remote_default_branch if rebase && !operate_offline
109
108
  switch(branch: branch)
110
109
  git.pull('origin', branch) if !operate_offline && remote_branch_exists?(branch)
111
- rebase_onto(rebase_target) if rebase_target
112
110
  else
113
111
  raise ModuleSync::Error, 'Unable to clone in offline mode.' if operate_offline
114
112
 
115
113
  clone
116
114
  switch(branch: branch)
117
115
  end
116
+
117
+ rebase_target = remote_default_branch if rebase && !operate_offline
118
+ rebase_onto(rebase_target) if rebase_target
118
119
  end
119
120
 
120
121
  def rebase_onto(branch)
@@ -165,10 +166,10 @@ module ModuleSync
165
166
  end
166
167
  end
167
168
 
168
- def tag(version, tag_pattern)
169
+ def tag(version, tag_pattern, sign: false)
169
170
  tag = tag_pattern % version
170
171
  puts "Tagging with #{tag}"
171
- repo.add_tag(tag)
172
+ repo.add_tag(tag, sign: sign)
172
173
  repo.push('origin', tag)
173
174
  end
174
175
 
@@ -178,6 +179,19 @@ module ModuleSync
178
179
  selected_branch
179
180
  end
180
181
 
182
+ def commit_changes(message, options)
183
+ commit_options = {}
184
+ commit_options[:amend] = true if options[:amend]
185
+ commit_options[:gpg_sign] = true if options[:sign]
186
+ return repo.commit(message, commit_options) unless options[:signoff]
187
+
188
+ arguments = ["--message=#{message}"]
189
+ arguments.push('--amend', '--no-edit') if options[:amend]
190
+ arguments << '--gpg-sign' if options[:sign]
191
+ arguments << '--signoff'
192
+ repo.lib.send(:command, 'commit', *arguments)
193
+ end
194
+
181
195
  # Git add/rm, git commit, git push
182
196
  def submit_changes(files, options)
183
197
  message = options[:message]
@@ -190,8 +204,6 @@ module ModuleSync
190
204
  end
191
205
  end
192
206
  begin
193
- opts_commit = {}
194
- opts_commit = { amend: true } if options[:amend]
195
207
  if options[:pre_commit_script]
196
208
  script = "#{File.dirname(__FILE__, 3)}/contrib/#{options[:pre_commit_script]}"
197
209
  system(script, @directory)
@@ -200,7 +212,7 @@ module ModuleSync
200
212
  puts "There were no changes in '#{@directory}'. Not committing."
201
213
  return false unless @rebased
202
214
  else
203
- repo.commit(message, opts_commit)
215
+ commit_changes(message, options)
204
216
  end
205
217
  if options[:remote_branch]
206
218
  if @rebased || remote_branch_differ?(branch, options[:remote_branch])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ModuleSync
4
- VERSION = '4.5.0'
4
+ VERSION = '4.6.1'
5
5
  end
data/lib/modulesync.rb CHANGED
@@ -151,8 +151,9 @@ module ModuleSync
151
151
  pushed = puppet_module.repository.submit_changes(files_to_manage, options)
152
152
  # Only bump/tag if pushing didn't fail (i.e. there were changes)
153
153
  if pushed && options[:bump]
154
- new = puppet_module.bump(options[:message], options[:changelog])
155
- puppet_module.repository.tag(new, options[:tag_pattern]) if options[:tag]
154
+ signing_options = { sign: options[:sign], signoff: options[:signoff] }
155
+ new = puppet_module.bump(options[:message], options[:changelog], signing_options)
156
+ puppet_module.repository.tag(new, options[:tag_pattern], sign: options[:sign]) if options[:tag]
156
157
  end
157
158
  options[:pr] && (pushed || puppet_module.pull_request_branch_ready?) && puppet_module.open_pull_request
158
159
  end
@@ -73,6 +73,50 @@ describe ModuleSync::Repository do
73
73
  end
74
74
 
75
75
  describe '#prepare_workspace' do
76
+ context 'with a fresh clone and an existing remote modulesync branch' do
77
+ before do
78
+ allow(Dir).to receive(:exist?).with('/tmp/example/.git').and_return(false)
79
+ allow(Git).to receive(:clone).with('https://github.com/example/repository.git', '/tmp/example').and_return(git)
80
+ allow(Git).to receive(:default_branch).with('https://github.com/example/repository.git').and_return('main')
81
+ allow(git).to receive(:current_branch).and_return('main', 'modulesync')
82
+ allow(branches).to receive(:local).and_return([])
83
+ allow(git).to receive(:checkout).with('origin/modulesync')
84
+ allow(git).to receive(:branch).with('modulesync').and_return(instance_double(Git::Branch, checkout: true))
85
+ allow(log).to receive(:between).with('modulesync', 'origin/main').and_return(log)
86
+ allow(log).to receive(:execute).and_return([instance_double(Git::Object::Commit)])
87
+ end
88
+
89
+ it 'rebases and pushes the existing branch even when rendering makes no file changes' do
90
+ allow(git).to receive(:status).and_return(instance_double(Git::Status, added: {}, changed: {}, deleted: {}))
91
+ expect(git).to receive(:checkout).with('origin/modulesync').ordered
92
+ expect(git_lib).to receive(:send).with(:command, 'rebase', 'origin/main').ordered
93
+ expect(git_lib).to receive(:send).with(:command, 'push', '--force-with-lease', 'origin', 'modulesync').ordered
94
+
95
+ repository.prepare_workspace(branch: 'modulesync', operate_offline: false, rebase: true)
96
+
97
+ expect(repository.submit_changes([], branch: 'modulesync', message: 'Update', force: false)).to be true
98
+ end
99
+
100
+ it 'does not rebase unless requested' do
101
+ expect(Git).not_to receive(:default_branch)
102
+ expect(git_lib).not_to receive(:send)
103
+
104
+ repository.prepare_workspace(branch: 'modulesync', operate_offline: false)
105
+ end
106
+ end
107
+
108
+ it 'does not rebase an existing workspace in offline mode' do
109
+ allow(Dir).to receive(:exist?).with('/tmp/example/.git').and_return(true)
110
+ allow(git).to receive(:current_branch).and_return('modulesync')
111
+ expect(git).to receive(:reset_hard)
112
+ expect(git).not_to receive(:fetch)
113
+ expect(git).not_to receive(:pull)
114
+ expect(Git).not_to receive(:default_branch)
115
+ expect(git_lib).not_to receive(:send)
116
+
117
+ repository.prepare_workspace(branch: 'modulesync', operate_offline: true, rebase: true)
118
+ end
119
+
76
120
  it 'fetches and rebases the selected branch onto the remote default branch when requested' do
77
121
  allow(Dir).to receive(:exist?).with('/tmp/example/.git').and_return(true)
78
122
  allow(Git).to receive(:default_branch).with('https://github.com/example/repository.git').and_return('main')
@@ -116,4 +160,35 @@ describe ModuleSync::Repository do
116
160
  expect(result).to be true
117
161
  end
118
162
  end
163
+
164
+ describe '#commit_changes' do
165
+ it 'signs the commit when requested' do
166
+ expect(git).to receive(:commit).with('Update', { gpg_sign: true })
167
+
168
+ repository.commit_changes('Update', sign: true, signoff: false)
169
+ end
170
+
171
+ it 'uses the Git signoff option when requested' do
172
+ expect(git_lib).to receive(:send)
173
+ .with(:command, 'commit', '--message=Update', '--signoff')
174
+
175
+ repository.commit_changes('Update', sign: false, signoff: true)
176
+ end
177
+
178
+ it 'combines amend, signing, and signoff options' do
179
+ expect(git_lib).to receive(:send)
180
+ .with(:command, 'commit', '--message=Update', '--amend', '--no-edit', '--gpg-sign', '--signoff')
181
+
182
+ repository.commit_changes('Update', amend: true, sign: true, signoff: true)
183
+ end
184
+ end
185
+
186
+ describe '#tag' do
187
+ it 'signs and pushes the tag when requested' do
188
+ expect(git).to receive(:add_tag).with('v1.2.3', sign: true)
189
+ expect(git).to receive(:push).with('origin', 'v1.2.3')
190
+
191
+ repository.tag('1.2.3', 'v%s', sign: true)
192
+ end
193
+ end
119
194
  end
@@ -70,5 +70,17 @@ describe ModuleSync do
70
70
 
71
71
  described_class.manage_module(puppet_module, [], {})
72
72
  end
73
+
74
+ it 'passes signing options to a release commit and tag' do
75
+ options = described_class.config_defaults.merge(message: 'Update', bump: true, tag: true, sign: true, signoff: true)
76
+ described_class.instance_variable_set(:@options, options)
77
+ allow(repository).to receive(:submit_changes).and_return(true)
78
+ expect(puppet_module).to receive(:bump)
79
+ .with('Update', nil, { sign: true, signoff: true })
80
+ .and_return('1.2.3')
81
+ expect(repository).to receive(:tag).with('1.2.3', '%s', sign: true)
82
+
83
+ described_class.manage_module(puppet_module, [], {})
84
+ end
73
85
  end
74
86
  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.5.0
4
+ version: 4.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vox Pupuli
@@ -266,7 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
266
266
  - !ruby/object:Gem::Version
267
267
  version: '0'
268
268
  requirements: []
269
- rubygems_version: 4.0.16
269
+ rubygems_version: 4.0.20
270
270
  specification_version: 4
271
271
  summary: Puppet Module Synchronizer
272
272
  test_files: []