modulesync 4.4.0 → 4.6.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 +4 -4
- data/.github/workflows/ci.yml +5 -5
- data/.github/workflows/labeler.yml +1 -1
- data/.github/workflows/release.yml +3 -3
- data/CHANGELOG.md +23 -2
- data/README.md +189 -194
- data/features/cli.feature +5 -0
- data/lib/modulesync/cli.rb +12 -0
- data/lib/modulesync/git_service/github.rb +11 -2
- data/lib/modulesync/git_service/gitlab.rb +11 -2
- data/lib/modulesync/puppet_module.rb +2 -2
- data/lib/modulesync/repository.rb +61 -12
- data/lib/modulesync/version.rb +1 -1
- data/lib/modulesync.rb +5 -3
- data/spec/unit/module_sync/git_service/github_spec.rb +23 -2
- data/spec/unit/module_sync/git_service/gitlab_spec.rb +22 -2
- data/spec/unit/module_sync/repository_spec.rb +115 -1
- data/spec/unit/module_sync_spec.rb +19 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42cf2d8364d939be480302cbb0856e69df96468c5b35a460c29380dbb1455049
|
|
4
|
+
data.tar.gz: 93ee25b205bd960afe750188b5e1bf7c640aace8bdb91aac8345aed93d980a23
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de6493978a435981fdc1b28bb0d937df4631f8eea6cdfe820bfb543b829a1c51d77672f31e510caaf18ef62754ead0988beab47040ad1ecc8c6e75723517d3fa
|
|
7
|
+
data.tar.gz: 46094244ce217f1e3dfbfd57d87b6aba64d0533dfe42b94d96ef48a66d2ef17dcc0cae9e24e08d54344bb0b9fa1553f1eb8898170489b0b79bfc9fab9acb5081
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -16,16 +16,16 @@ jobs:
|
|
|
16
16
|
outputs:
|
|
17
17
|
ruby: ${{ steps.ruby.outputs.versions }}
|
|
18
18
|
steps:
|
|
19
|
-
- uses: actions/checkout@
|
|
19
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
20
20
|
- name: Setup ruby
|
|
21
|
-
uses: ruby/setup-ruby@
|
|
21
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.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@
|
|
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@
|
|
39
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
40
40
|
- name: Install Ruby ${{ matrix.ruby }}
|
|
41
|
-
uses: ruby/setup-ruby@
|
|
41
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
42
42
|
with:
|
|
43
43
|
ruby-version: ${{ matrix.ruby }}
|
|
44
44
|
bundler-cache: true
|
|
@@ -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@
|
|
18
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
19
19
|
- name: Install Ruby
|
|
20
|
-
uses: ruby/setup-ruby@
|
|
20
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.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@
|
|
99
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
100
100
|
with:
|
|
101
101
|
ruby-version: 'ruby'
|
|
102
102
|
- name: Wait for release to propagate
|
data/CHANGELOG.md
CHANGED
|
@@ -2,9 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [4.
|
|
5
|
+
## [4.6.0](https://github.com/voxpupuli/modulesync/tree/4.6.0) (2026-07-31)
|
|
6
6
|
|
|
7
|
-
[Full Changelog](https://github.com/voxpupuli/modulesync/compare/v4.
|
|
7
|
+
[Full Changelog](https://github.com/voxpupuli/modulesync/compare/v4.5.0...4.6.0)
|
|
8
|
+
|
|
9
|
+
**Implemented enhancements:**
|
|
10
|
+
|
|
11
|
+
- 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))
|
|
12
|
+
|
|
13
|
+
## [v4.5.0](https://github.com/voxpupuli/modulesync/tree/v4.5.0) (2026-07-15)
|
|
14
|
+
|
|
15
|
+
[Full Changelog](https://github.com/voxpupuli/modulesync/compare/v4.4.0...v4.5.0)
|
|
16
|
+
|
|
17
|
+
**Implemented enhancements:**
|
|
18
|
+
|
|
19
|
+
- feat: update pr/mr title if needed [\#358](https://github.com/voxpupuli/modulesync/pull/358) ([rwaffen](https://github.com/rwaffen))
|
|
20
|
+
- feat: add option to rebase modulesync branch with the latest changes from the origin’s default branch [\#356](https://github.com/voxpupuli/modulesync/pull/356) ([rwaffen](https://github.com/rwaffen))
|
|
21
|
+
|
|
22
|
+
**Merged pull requests:**
|
|
23
|
+
|
|
24
|
+
- Enhance README.md [\#298](https://github.com/voxpupuli/modulesync/pull/298) ([bastelfreak](https://github.com/bastelfreak))
|
|
25
|
+
|
|
26
|
+
## [v4.4.0](https://github.com/voxpupuli/modulesync/tree/v4.4.0) (2026-07-09)
|
|
27
|
+
|
|
28
|
+
[Full Changelog](https://github.com/voxpupuli/modulesync/compare/v4.3.0...v4.4.0)
|
|
8
29
|
|
|
9
30
|
**Implemented enhancements:**
|
|
10
31
|
|
data/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
ModuleSync
|
|
2
|
-
===========
|
|
1
|
+
# ModuleSync
|
|
3
2
|
|
|
4
3
|
[](https://github.com/voxpupuli/modulesync/blob/master/LICENSE)
|
|
5
4
|
[](https://github.com/voxpupuli/modulesync/actions/workflows/ci.yml)
|
|
@@ -9,198 +8,202 @@ ModuleSync
|
|
|
9
8
|
[](https://rubygems.org/gems/modulesync)
|
|
10
9
|
[](#transfer-notice)
|
|
11
10
|
|
|
12
|
-
Table of Contents
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [ModuleSync](#modulesync)
|
|
14
|
+
- [Table of Contents](#table-of-contents)
|
|
15
|
+
- [Usage TLDR](#usage-tldr)
|
|
16
|
+
- [Overview](#overview)
|
|
17
|
+
- [How It Works](#how-it-works)
|
|
18
|
+
- [Installing](#installing)
|
|
19
|
+
- [Workflow](#workflow)
|
|
20
|
+
- [Default mode](#default-mode)
|
|
21
|
+
- [Make changes](#make-changes)
|
|
22
|
+
- [Dry-run](#dry-run)
|
|
23
|
+
- [Offline support](#offline-support)
|
|
24
|
+
- [Damage mode](#damage-mode)
|
|
25
|
+
- [Automating Updates](#automating-updates)
|
|
26
|
+
- [Submitting PRs/MRs to GitHub or GitLab](#submitting-prsmrs-to-github-or-gitlab)
|
|
27
|
+
- [Using Forks and Non-master branches](#using-forks-and-non-master-branches)
|
|
28
|
+
- [Dry-run](#dry-run-1)
|
|
29
|
+
- [Damage mode](#damage-mode-1)
|
|
30
|
+
- [Configuring ModuleSync defaults](#configuring-modulesync-defaults)
|
|
31
|
+
- [Example](#example)
|
|
32
|
+
- [GitHub](#github)
|
|
33
|
+
- [GitLab](#gitlab)
|
|
34
|
+
- [Gerrit](#gerrit)
|
|
35
|
+
- [Filtering Repositories](#filtering-repositories)
|
|
36
|
+
- [Pushing to a different remote branch](#pushing-to-a-different-remote-branch)
|
|
37
|
+
- [Automating updates](#automating-updates-1)
|
|
38
|
+
- [Updating metadata.json](#updating-metadatajson)
|
|
39
|
+
- [Tagging repositories](#tagging-repositories)
|
|
40
|
+
- [Setting the tag pattern](#setting-the-tag-pattern)
|
|
41
|
+
- [Executing bundle command with a shared bundle path and gemfile](#executing-bundle-command-with-a-shared-bundle-path-and-gemfile)
|
|
42
|
+
- [Updating the CHANGELOG](#updating-the-changelog)
|
|
43
|
+
- [Working with templates](#working-with-templates)
|
|
44
|
+
- [The Templates](#the-templates)
|
|
45
|
+
- [Transfer Notice](#transfer-notice)
|
|
46
|
+
- [License](#license)
|
|
47
|
+
- [Release information](#release-information)
|
|
48
|
+
|
|
49
|
+
## Usage TLDR
|
|
50
|
+
|
|
51
|
+
```shell
|
|
26
52
|
gem install modulesync
|
|
27
53
|
msync --help
|
|
28
54
|
```
|
|
29
55
|
|
|
30
|
-
Overview
|
|
31
|
-
--------
|
|
32
|
-
|
|
33
|
-
ModuleSync was written as a simple script with ERB templates to help the
|
|
34
|
-
Puppet Labs module engineers manage the zoo of Puppet modules on GitHub, and
|
|
35
|
-
has now been restructured and generalized to be used within other
|
|
36
|
-
organizations. Puppet modules within an organization tend to have a number of
|
|
37
|
-
meta-files that are identical or very similar between modules, such as the
|
|
38
|
-
`Gemfile`, `.travis.yml`, `.gitignore`, or `spec_helper.rb`. If a file needs to
|
|
39
|
-
change in one module, it likely needs to change in the same way in every other
|
|
40
|
-
module that the organization manages.
|
|
41
|
-
|
|
42
|
-
One approach to this problem is to use sed in a bash for loop on the modules to
|
|
43
|
-
make a single change across every module. This approach falls short if there is
|
|
44
|
-
a single file that is purposefully different than the others, but still needs
|
|
45
|
-
to be managed. Moreover, this approach does not help if two files are
|
|
46
|
-
structured differently but need to be changed with the same meaning; for
|
|
47
|
-
instance, if the .travis.yml of one module uses a list of environments to
|
|
48
|
-
include, and another uses a matrix of includes with a list of excludes, adding
|
|
49
|
-
a test environment to both modules will require entirely different approaches.
|
|
50
|
-
|
|
51
|
-
ModuleSync provides the advantage of defining a standard template for each
|
|
52
|
-
file to follow, so it becomes clear what a file is supposed to look like. Two
|
|
53
|
-
files with the same semantics should also have the same syntax. A difference
|
|
54
|
-
between two files should have clear reasons, and old cruft should not be left
|
|
55
|
-
in files of one module as the files of another module march forward.
|
|
56
|
-
|
|
57
|
-
Another advantage of ModuleSync is the ability to run in no-op mode, which
|
|
58
|
-
makes local changes and shows the diffs, but does not make permanent changes in
|
|
59
|
-
the remote repository.
|
|
60
|
-
|
|
61
|
-
How It Works
|
|
62
|
-
------------
|
|
63
|
-
|
|
64
|
-
ModuleSync is a gem that uses the GitHub workflow to clone, update, and push module
|
|
65
|
-
repositories. It expects to be activated from a directory containing
|
|
66
|
-
configuration for modulesync and the modules, or you can pass it the location
|
|
67
|
-
of this configuration directory. [The configuration for the Vox Pupuli
|
|
68
|
-
modules](https://github.com/voxpupuli/modulesync_config), can be used as an
|
|
69
|
-
example for your own configuration. The configuration directory contains a
|
|
70
|
-
directory called moduleroot which mirrors the structure of a module. The files
|
|
71
|
-
in the moduleroot are ERB templates, and MUST be named after the target file,
|
|
72
|
-
with `.erb.` appended. The templates are
|
|
73
|
-
rendered using values from a file called `config_defaults.yml` in the root (not
|
|
74
|
-
moduleroot) of the configuration directory. The default values can be
|
|
75
|
-
overridden or extended by adding a file called `.sync.yml` to the module itself.
|
|
76
|
-
This allows us to, for example, have a set of "required" gems that are added
|
|
77
|
-
to all Gemfiles, and a set of "optional" gems that a single module might add.
|
|
78
|
-
|
|
79
|
-
Within the templates, values can be accessed in the `@configs` hash, which is
|
|
80
|
-
merged from the values under the keys `:global` and the target file name (no
|
|
81
|
-
`.erb` suffix).
|
|
82
|
-
|
|
83
|
-
The list of modules to manage is in `managed_modules.yml` in the configuration
|
|
84
|
-
directory. This lists just the names of the modules to be managed.
|
|
85
|
-
|
|
86
|
-
ModuleSync can be called from the command line with parameters to change the
|
|
87
|
-
branch you're working on or the remote to clone from and push to. You can also
|
|
88
|
-
define these parameters in a file named `modulesync.yml` in the configuration
|
|
89
|
-
directory.
|
|
90
|
-
|
|
91
|
-
Installing
|
|
92
|
-
----------
|
|
56
|
+
## Overview
|
|
93
57
|
|
|
94
|
-
|
|
58
|
+
ModuleSync was written as a simple script with ERB templates to help the Puppet Labs module engineers manage the zoo of Puppet modules on GitHub, and has now been restructured and generalized to be used within other organizations.
|
|
59
|
+
Puppet modules within an organization tend to have a number of meta-files that are identical or very similar between modules, such as the `Gemfile`, `.travis.yml`, `.gitignore`, or `spec_helper.rb`.
|
|
60
|
+
If a file needs to change in one module, it likely needs to change in the same way in every other module that the organization manages.
|
|
61
|
+
|
|
62
|
+
One approach to this problem is to use sed in a bash for loop on the modules to make a single change across every module.
|
|
63
|
+
This approach falls short if there is a single file that is purposefully different than the others, but still needs to be managed.
|
|
64
|
+
Moreover, this approach does not help if two files are structured differently but need to be changed with the same meaning;
|
|
65
|
+
for instance, if the .travis.yml of one module uses a list of environments to include, and another uses a matrix of includes with a list of excludes, adding a test environment to both modules will require entirely different approaches.
|
|
66
|
+
|
|
67
|
+
ModuleSync provides the advantage of defining a standard template for each file to follow, so it becomes clear what a file is supposed to look like.
|
|
68
|
+
Two files with the same semantics should also have the same syntax.
|
|
69
|
+
A difference between two files should have clear reasons, and old cruft should not be left in files of one module as the files of another module march forward.
|
|
70
|
+
|
|
71
|
+
Another advantage of ModuleSync is the ability to run in no-op mode, which makes local changes and shows the diffs, but does not make permanent changes in the remote repository.
|
|
72
|
+
|
|
73
|
+
## How It Works
|
|
74
|
+
|
|
75
|
+
ModuleSync is a gem that uses the GitHub workflow to clone, update, and push module repositories.
|
|
76
|
+
It expects to be activated from a directory containing configuration for modulesync and the modules, or you can pass it the location of this configuration directory.
|
|
77
|
+
[The configuration for the Vox Pupuli modules](https://github.com/voxpupuli/modulesync_configs), can be used as an example for your own configuration.
|
|
78
|
+
The configuration directory contains a directory called moduleroot which mirrors the structure of a module.
|
|
79
|
+
The files in the moduleroot are ERB templates, and MUST be named after the target file, with `.erb.` appended.
|
|
80
|
+
The templates are rendered using values from a file called `config_defaults.yml` in the root (not moduleroot) of the configuration directory.
|
|
81
|
+
The default values can be overridden or extended by adding a file called `.sync.yml` to the module itself.
|
|
82
|
+
This allows us to, for example, have a set of "required" gems that are added to all Gemfiles, and a set of "optional" gems that a single module might add.
|
|
83
|
+
|
|
84
|
+
Within the templates, values can be accessed in the `@configs` hash, which is merged from the values under the keys `:global` and the target file name (no `.erb` suffix).
|
|
85
|
+
|
|
86
|
+
The list of modules to manage is in `managed_modules.yml` in the configuration directory.
|
|
87
|
+
This lists just the names of the modules to be managed.
|
|
88
|
+
|
|
89
|
+
ModuleSync can be called from the command line with parameters to change the branch you're working on or the remote to clone from and push to.
|
|
90
|
+
You can also define these parameters in a file named `modulesync.yml` in the configuration directory.
|
|
91
|
+
|
|
92
|
+
## Installing
|
|
93
|
+
|
|
94
|
+
```shell
|
|
95
95
|
gem install modulesync
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
For developers:
|
|
99
99
|
|
|
100
|
-
```
|
|
100
|
+
```shell
|
|
101
101
|
gem build modulesync.gemspec
|
|
102
102
|
gem install modulesync-*.gem
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
Workflow
|
|
106
|
-
--------
|
|
105
|
+
## Workflow
|
|
107
106
|
|
|
108
107
|
### Default mode
|
|
109
108
|
|
|
110
|
-
With no additional arguments, ModuleSync clones modules from the puppetlabs
|
|
111
|
-
github organization and pushes to the master branch.
|
|
109
|
+
With no additional arguments, ModuleSync clones modules from the puppetlabs github organization and pushes to the master branch.
|
|
112
110
|
|
|
113
111
|
#### Make changes
|
|
114
112
|
|
|
115
|
-
Make changes to a file in the moduleroot.
|
|
116
|
-
and push these changes, but in this mode the update will be rendered from the
|
|
117
|
-
state of the files locally.
|
|
113
|
+
Make changes to a file in the moduleroot.
|
|
114
|
+
For sanity's sake you should commit and push these changes, but in this mode the update will be rendered from the state of the files locally.
|
|
118
115
|
|
|
119
116
|
#### Dry-run
|
|
120
117
|
|
|
121
|
-
Do a dry-run to see what files will be changed, added and removed.
|
|
122
|
-
the modules to `modules/<namespace>-<modulename>` in the current working
|
|
123
|
-
directory, or if the modules are already cloned, does an effective `git fetch
|
|
124
|
-
origin; git checkout master; git reset --hard origin/master` on the modules.
|
|
125
|
-
Don't run modulesync if the current working directory contains a modules/
|
|
126
|
-
directory with changes you want to keep. The dry-run makes local changes there,
|
|
127
|
-
but does not commit or push changes. It is still destructive in that it
|
|
128
|
-
overwrites local changes.
|
|
118
|
+
Do a dry-run to see what files will be changed, added and removed.
|
|
119
|
+
This clones the modules to `modules/<namespace>-<modulename>` in the current working directory, or if the modules are already cloned, does an effective
|
|
129
120
|
|
|
121
|
+
```shell
|
|
122
|
+
git fetch origin
|
|
123
|
+
git checkout master
|
|
124
|
+
git reset --hard origin/master
|
|
130
125
|
```
|
|
126
|
+
|
|
127
|
+
on the modules.
|
|
128
|
+
Don't run modulesync if the current working directory contains a `modules/` directory with changes you want to keep.
|
|
129
|
+
The dry-run makes local changes there, but does not commit or push changes.
|
|
130
|
+
It is still destructive in that it overwrites local changes.
|
|
131
|
+
|
|
132
|
+
```shell
|
|
131
133
|
msync update --noop
|
|
132
134
|
```
|
|
133
135
|
|
|
134
136
|
#### Offline support
|
|
135
137
|
|
|
136
|
-
The --offline flag was added to allow a user to disable git support within
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
fail to update correctly.
|
|
138
|
+
The --offline flag was added to allow a user to disable git support within msync.
|
|
139
|
+
One reason for this is because the user wants to control git commands external to msync.
|
|
140
|
+
Note, when using this command, msync assumes you have create the folder structure and git repositories correctly.
|
|
141
|
+
If not, msync will fail to update correctly.
|
|
141
142
|
|
|
142
|
-
```
|
|
143
|
+
```shell
|
|
143
144
|
msync update --offline
|
|
144
145
|
```
|
|
145
146
|
|
|
146
147
|
#### Damage mode
|
|
147
148
|
|
|
148
|
-
Make changes for real and push them back to master.
|
|
149
|
-
pre-cloned modules from the dry-run or clones them fresh if the modules aren't
|
|
150
|
-
found.
|
|
149
|
+
Make changes for real and push them back to master.
|
|
150
|
+
This operates on the pre-cloned modules from the dry-run or clones them fresh if the modules aren't found.
|
|
151
151
|
|
|
152
|
-
```
|
|
152
|
+
```shell
|
|
153
153
|
msync update -m "Commit message"
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
Amend the commit if changes are needed.
|
|
157
157
|
|
|
158
|
-
```
|
|
158
|
+
```shell
|
|
159
159
|
msync update --amend
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
-
For most workflows you will need to force-push an amended commit.
|
|
163
|
-
for gerrit.
|
|
162
|
+
For most workflows you will need to force-push an amended commit.
|
|
163
|
+
Not required for gerrit.
|
|
164
164
|
|
|
165
|
-
```
|
|
165
|
+
```shell
|
|
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
|
-
You can install a pre-push git hook to automatically clone, update, and push
|
|
172
|
-
|
|
173
|
-
include a noop mode.
|
|
182
|
+
You can install a pre-push git hook to automatically clone, update, and push modules upon pushing changes to the configuration directory.
|
|
183
|
+
This does not include a noop mode.
|
|
174
184
|
|
|
175
|
-
```
|
|
185
|
+
```shell
|
|
176
186
|
msync hook activate
|
|
177
187
|
```
|
|
178
188
|
|
|
179
|
-
If you have activated the hook but want to make changes to the configuration
|
|
180
|
-
directory (such as changes to `managed_modules.yml` or `modulesync.yml`) without
|
|
181
|
-
touching the modules, you can deactivate the hook.
|
|
189
|
+
If you have activated the hook but want to make changes to the configuration directory (such as changes to `managed_modules.yml` or `modulesync.yml`) without touching the modules, you can deactivate the hook.
|
|
182
190
|
|
|
183
|
-
```
|
|
191
|
+
```shell
|
|
184
192
|
msync hook deactivate
|
|
185
193
|
```
|
|
186
194
|
|
|
187
195
|
#### Submitting PRs/MRs to GitHub or GitLab
|
|
188
196
|
|
|
189
|
-
You can have modulesync submit Pull Requests on GitHub or Merge Requests on
|
|
190
|
-
GitLab automatically with the `--pr` CLI option.
|
|
197
|
+
You can have modulesync submit Pull Requests on GitHub or Merge Requests on GitLab automatically with the `--pr` CLI option.
|
|
191
198
|
|
|
192
|
-
```
|
|
199
|
+
```shell
|
|
193
200
|
msync update --pr
|
|
194
201
|
```
|
|
195
202
|
|
|
196
|
-
In order for GitHub PRs or GitLab MRs to work, you must either provide
|
|
197
|
-
the `
|
|
198
|
-
or set them per repository in `managed_modules.yml`, using the `github` or
|
|
199
|
-
`gitlab` keys respectively.
|
|
203
|
+
In order for GitHub PRs or GitLab MRs to work, you must either provide the `GITHUB_TOKEN` or `GITLAB_TOKEN` environment variables.
|
|
204
|
+
You can also set them per repository in `managed_modules.yml`, using the `github` or `gitlab` keys respectively.
|
|
200
205
|
|
|
201
|
-
For GitHub Enterprise and self-hosted GitLab instances you also need to set the
|
|
202
|
-
`GITHUB_BASE_URL` or `GITLAB_BASE_URL` environment variables, or specify the
|
|
203
|
-
`base_url` parameter per repository in `managed_modules.yml`:
|
|
206
|
+
For GitHub Enterprise and self-hosted GitLab instances you also need to set the `GITHUB_BASE_URL` or `GITLAB_BASE_URL` environment variables, or specify the `base_url` parameter in `modulesync.yml`:
|
|
204
207
|
|
|
205
208
|
```yaml
|
|
206
209
|
---
|
|
@@ -217,13 +220,9 @@ repo2:
|
|
|
217
220
|
|
|
218
221
|
Then:
|
|
219
222
|
|
|
220
|
-
* Set the PR/MR title with `--pr-title` or in `modulesync.yml` with the
|
|
221
|
-
|
|
222
|
-
*
|
|
223
|
-
the `pr_labels` attribute. **NOTE:** `pr_labels` should be a list. When
|
|
224
|
-
using the `--pr-labels` CLI option, you should use a comma separated list.
|
|
225
|
-
* Set the target branch with `--pr-target-branch` or in `modulesync.yml` with
|
|
226
|
-
the `pr_target_branch` attribute.
|
|
223
|
+
* Set the PR/MR title with `--pr-title` or in `modulesync.yml` with the `pr_title` attribute.
|
|
224
|
+
* Assign labels to the PR/MR with `--pr-labels` or in `modulesync.yml` with the `pr_labels` attribute. **NOTE:** `pr_labels` should be a list. When using the `--pr-labels` CLI option, you should use a comma separated list.
|
|
225
|
+
* Set the target branch with `--pr-target-branch` or in `modulesync.yml` with the `pr_target_branch` attribute.
|
|
227
226
|
|
|
228
227
|
More details for GitHub:
|
|
229
228
|
|
|
@@ -231,42 +230,35 @@ More details for GitHub:
|
|
|
231
230
|
|
|
232
231
|
### Using Forks and Non-master branches
|
|
233
232
|
|
|
234
|
-
The default functionality is to run ModuleSync on the
|
|
235
|
-
you
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
cloning from one remote and pushing to another, you are expected to fork
|
|
239
|
-
manually. It does not support automating pull requests.
|
|
233
|
+
The default functionality is to run ModuleSync on the Vox Pupuli modules, but you can use this on your own organization's modules.
|
|
234
|
+
This functionality also applies if you want to work on a fork of the Vox Pupuli modules or work on a non-master branch of any organization's modules.
|
|
235
|
+
ModuleSync does not support cloning from one remote and pushing to another, you are expected to fork manually.
|
|
236
|
+
It does not support automating pull requests.
|
|
240
237
|
|
|
241
238
|
#### Dry-run
|
|
242
239
|
|
|
243
|
-
If you dry-run before doing the live update, you need to specify what namespace
|
|
244
|
-
|
|
245
|
-
already cloned. The namespace should be your fork, not the upstream module (if
|
|
246
|
-
working on a fork).
|
|
240
|
+
If you dry-run before doing the live update, you need to specify what namespace to clone from because the live update will not re-clone if the modules are already cloned.
|
|
241
|
+
The namespace should be your fork, not the upstream module (if working on a fork).
|
|
247
242
|
|
|
248
|
-
```
|
|
249
|
-
msync update -n
|
|
243
|
+
```shell
|
|
244
|
+
msync update -n voxpupuli --noop
|
|
250
245
|
```
|
|
251
246
|
|
|
252
247
|
#### Damage mode
|
|
253
248
|
|
|
254
|
-
You don't technically need to specify the namespace if the modules are already
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
push to if you are not pushing to master.
|
|
249
|
+
You don't technically need to specify the namespace if the modules are already cloned from the dry-run, but it doesn't hurt.
|
|
250
|
+
You do need to specify the namespace if the modules are not pre-cloned.
|
|
251
|
+
You need to specify a branch to push to if you are not pushing to master.
|
|
258
252
|
|
|
259
|
-
```
|
|
260
|
-
msync update -n
|
|
253
|
+
```shell
|
|
254
|
+
msync update -n voxpupuli -b sync_branch -m "Commit message"
|
|
261
255
|
```
|
|
262
256
|
|
|
263
257
|
#### Configuring ModuleSync defaults
|
|
264
258
|
|
|
265
|
-
If you're not using
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
configuration directory that provides these arguments automatically. This file
|
|
269
|
-
has a form such as:
|
|
259
|
+
If you're not using Vox Pupuli modules or only ever pushing to a fork of them, then specifying the namespace and branch every time you use ModuleSync probably seems excessive.
|
|
260
|
+
You can create a file called modulesync.yml in the configuration directory that provides these arguments automatically.
|
|
261
|
+
This file has a form such as:
|
|
270
262
|
|
|
271
263
|
```yaml
|
|
272
264
|
---
|
|
@@ -276,17 +268,20 @@ branch: modulesyncbranch
|
|
|
276
268
|
|
|
277
269
|
Then you can run ModuleSync without extra arguments:
|
|
278
270
|
|
|
279
|
-
```
|
|
271
|
+
```shell
|
|
280
272
|
msync update --noop
|
|
281
273
|
msync update -m "Commit message"
|
|
282
274
|
```
|
|
283
275
|
|
|
276
|
+
Use `--rebase` to update the local working branch with the latest remote default
|
|
277
|
+
branch before ModuleSync changes any files. If the rebase conflicts, ModuleSync
|
|
278
|
+
aborts the rebase and reports an error for that module. A branch changed by the
|
|
279
|
+
rebase is pushed even when the template sync itself produces no changes.
|
|
280
|
+
|
|
284
281
|
Available parameters for modulesync.yml
|
|
285
282
|
|
|
286
283
|
* `git_base` : The default URL to git clone from (Default: 'git@github.com:')
|
|
287
|
-
* `namespace` : Namespace of the projects to manage (Default: 'puppetlabs').
|
|
288
|
-
This value can be overridden in the module name (e.g. 'namespace/mod') or by
|
|
289
|
-
using the `namespace` key for the module in `managed_modules.yml`.
|
|
284
|
+
* `namespace` : Namespace of the projects to manage (Default: 'puppetlabs'). This value can be overridden in the module name (e.g. 'namespace/mod') or by using the `namespace` key for the module in `managed_modules.yml`.
|
|
290
285
|
* `branch` : Branch to push to (Default: 'master')
|
|
291
286
|
* `remote_branch` : Remote branch to push to (Default: Same value as branch)
|
|
292
287
|
* `message` : Commit message to apply to updated modules.
|
|
@@ -333,14 +328,14 @@ pre_commit_script: openstack-commit-msg-hook.sh
|
|
|
333
328
|
|
|
334
329
|
If you only want to sync some of the repositories in your managed_modules.yml, use the `-f` flag to filter by a regex:
|
|
335
330
|
|
|
336
|
-
```
|
|
331
|
+
```shell
|
|
337
332
|
msync update -f augeas -m "Commit message" # acts only on the augeas module
|
|
338
333
|
msync update -f puppet-a..o "Commit message"
|
|
339
334
|
```
|
|
340
335
|
|
|
341
336
|
If you want to skip syncing some of the repositories in your managed_modules.yml, use the `-x` flag to filter by a regex:
|
|
342
337
|
|
|
343
|
-
```
|
|
338
|
+
```shell
|
|
344
339
|
msync update -x augeas -m "Commit message" # acts on all modules except the augeas module
|
|
345
340
|
msync update -x puppet-a..o "Commit message"
|
|
346
341
|
```
|
|
@@ -351,45 +346,47 @@ If no `-f` is specified, all repository are processed, if no `-x` is specified n
|
|
|
351
346
|
|
|
352
347
|
If you want to push the modified branch to a different remote branch, you can use the -r flag:
|
|
353
348
|
|
|
354
|
-
```
|
|
349
|
+
```shell
|
|
355
350
|
msync update -r master_new -m "Commit message"
|
|
356
351
|
```
|
|
357
352
|
|
|
358
353
|
#### Automating updates
|
|
359
354
|
|
|
360
|
-
If you install a git hook, you need to tell it what remote and branch to push
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
arguments.
|
|
355
|
+
If you install a git hook, you need to tell it what remote and branch to push to.
|
|
356
|
+
This may not work properly if you already have the modules cloned from a different remote.
|
|
357
|
+
The hook will also look in modulesync.yml for default arguments.
|
|
364
358
|
|
|
365
|
-
```
|
|
366
|
-
msync hook activate -n
|
|
359
|
+
```shell
|
|
360
|
+
msync hook activate -n voxpupuli -b sync_branch
|
|
367
361
|
```
|
|
368
362
|
|
|
369
363
|
#### Updating metadata.json
|
|
370
364
|
|
|
371
|
-
Modulesync can optionally bump the minor version in `metadata.json` for each
|
|
372
|
-
modified modules if you add the `--bump` flag to the command line:
|
|
365
|
+
Modulesync can optionally bump the minor version in `metadata.json` for each modified modules if you add the `--bump` flag to the command line:
|
|
373
366
|
|
|
374
|
-
```
|
|
367
|
+
```shell
|
|
375
368
|
msync update -m "Commit message" --bump
|
|
376
369
|
```
|
|
377
370
|
|
|
378
371
|
#### Tagging repositories
|
|
379
372
|
|
|
380
|
-
If you wish to tag the modified repositories with the newly bumped version,
|
|
381
|
-
you can do so by using the `--tag` flag:
|
|
373
|
+
If you wish to tag the modified repositories with the newly bumped version, you can do so by using the `--tag` flag:
|
|
382
374
|
|
|
383
|
-
```
|
|
375
|
+
```shell
|
|
384
376
|
msync update -m "Commit message" --bump --tag
|
|
385
377
|
```
|
|
386
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
|
+
|
|
387
385
|
#### Setting the tag pattern
|
|
388
386
|
|
|
389
|
-
You can also set the format of the tag to be used (`printf`-formatted)
|
|
390
|
-
by setting the `tag_pattern` option:
|
|
387
|
+
You can also set the format of the tag to be used (`printf`-formatted) by setting the `tag_pattern` option:
|
|
391
388
|
|
|
392
|
-
```
|
|
389
|
+
```shell
|
|
393
390
|
msync update -m "Commit message" --bump --tag --tag_pattern 'v%s'
|
|
394
391
|
```
|
|
395
392
|
|
|
@@ -399,30 +396,28 @@ The default for the tag pattern is `%s`.
|
|
|
399
396
|
|
|
400
397
|
By default `msync execute` will remove bundler related env vars (^BUNDLE|^SOURCE_DATE_EPOCH$|^GEM_|RUBY), you can use `--env` flag to keep specified env vars:
|
|
401
398
|
|
|
402
|
-
```
|
|
399
|
+
```shell
|
|
403
400
|
msync execute --env BUNDLE_PATH,BUNDLE_GEMFILE bundle exec rake lint
|
|
404
401
|
```
|
|
405
402
|
|
|
406
403
|
#### Updating the CHANGELOG
|
|
407
404
|
|
|
408
|
-
When bumping the version in `metadata.json`, modulesync can let you
|
|
409
|
-
updating `CHANGELOG.md` in each modified repository.
|
|
405
|
+
When bumping the version in `metadata.json`, modulesync can let you updating `CHANGELOG.md` in each modified repository.
|
|
410
406
|
|
|
411
407
|
This is one by using the `--changelog` flag:
|
|
412
408
|
|
|
413
|
-
```
|
|
409
|
+
```shell
|
|
414
410
|
msync update -m "Commit message" --bump --changelog
|
|
415
411
|
```
|
|
416
412
|
|
|
417
|
-
This flag will cause the `CHANGELOG.md` file to be updated with the
|
|
418
|
-
current date, bumped (minor) version, and commit message.
|
|
413
|
+
This flag will cause the `CHANGELOG.md` file to be updated with the current date, bumped (minor) version, and commit message.
|
|
419
414
|
|
|
420
415
|
If `CHANGELOG.md` is absent in the repository, nothing will happen.
|
|
421
416
|
|
|
422
|
-
|
|
423
417
|
#### Working with templates
|
|
424
418
|
|
|
425
|
-
As mentioned, files in the moduleroot directory must be ERB templates (they must have an .erb extension, or they will be ignored).
|
|
419
|
+
As mentioned, files in the moduleroot directory must be ERB templates (they must have an .erb extension, or they will be ignored).
|
|
420
|
+
These files have direct access to @configs hash, which gets values from config_defaults.yml file and from the module being processed:
|
|
426
421
|
|
|
427
422
|
```erb
|
|
428
423
|
<%= @configs[:git_base] %>
|
|
@@ -430,8 +425,8 @@ As mentioned, files in the moduleroot directory must be ERB templates (they must
|
|
|
430
425
|
<%= @configs[:puppet_module] %>
|
|
431
426
|
```
|
|
432
427
|
|
|
433
|
-
Alternatively some meta data is passed to the template.
|
|
434
|
-
template, reading other files from the module, to make the template system more adaptive.
|
|
428
|
+
Alternatively some meta data is passed to the template.
|
|
429
|
+
This will allow you to add custom Ruby extensions inside the template, reading other files from the module, to make the template system more adaptive.
|
|
435
430
|
|
|
436
431
|
```erb
|
|
437
432
|
module: <%= @metadata[:module_name] %>
|
|
@@ -441,16 +436,15 @@ workdir: <%= @metadata[:workdir] %>
|
|
|
441
436
|
|
|
442
437
|
Will result in something like:
|
|
443
438
|
|
|
444
|
-
```
|
|
439
|
+
```yaml
|
|
445
440
|
module: puppet-test
|
|
446
441
|
target: modules/github-org/puppet-test/test
|
|
447
442
|
workdir: modules/github-org/puppet-test
|
|
448
443
|
```
|
|
449
444
|
|
|
450
|
-
The Templates
|
|
451
|
-
-------------
|
|
445
|
+
## The Templates
|
|
452
446
|
|
|
453
|
-
See [Vox Pupuli's modulesync\_config](https://github.com/voxpupuli/modulesync_config)
|
|
447
|
+
See [Vox Pupuli's modulesync\_config](https://github.com/voxpupuli/modulesync_config) repository for templates currently in use.
|
|
454
448
|
|
|
455
449
|
## Transfer Notice
|
|
456
450
|
|
|
@@ -467,6 +461,7 @@ This gem is licensed under the Apache-2 license.
|
|
|
467
461
|
## Release information
|
|
468
462
|
|
|
469
463
|
To make a new release, please do:
|
|
464
|
+
|
|
470
465
|
* update the version in `lib/modulesync/version.rb`
|
|
471
466
|
* Install gems with `bundle install --with release --path .vendor`
|
|
472
467
|
* generate the changelog with `bundle exec rake changelog`
|
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:
|
data/lib/modulesync/cli.rb
CHANGED
|
@@ -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',
|
|
@@ -114,6 +122,10 @@ module ModuleSync
|
|
|
114
122
|
type: :boolean,
|
|
115
123
|
desc: 'Do not run any Git commands. Allows the user to manage Git outside of ModuleSync.',
|
|
116
124
|
default: false
|
|
125
|
+
option :rebase,
|
|
126
|
+
type: :boolean,
|
|
127
|
+
desc: 'Rebase the local branch onto the latest remote default branch before syncing.',
|
|
128
|
+
default: false
|
|
117
129
|
option :bump,
|
|
118
130
|
type: :boolean,
|
|
119
131
|
desc: 'Bump module version to the next minor',
|
|
@@ -41,8 +41,17 @@ module ModuleSync
|
|
|
41
41
|
base: target_branch,
|
|
42
42
|
head: head)
|
|
43
43
|
unless pull_requests.empty?
|
|
44
|
-
#
|
|
45
|
-
|
|
44
|
+
# assumption: there should only be one PR for a given source and target branch, so we just take the first one
|
|
45
|
+
pr = pull_requests.first
|
|
46
|
+
|
|
47
|
+
if pr['title'] == title
|
|
48
|
+
$stdout.puts "Skipped! #{pull_requests.length} PRs found for branch '#{source_branch}'"
|
|
49
|
+
$stdout.puts "Skipped! Existing PR ##{pr['number']} already has title '#{title}'"
|
|
50
|
+
else
|
|
51
|
+
@api.update_pull_request(repo_path, pr['number'], title: title)
|
|
52
|
+
$stdout.puts "Updated title of existing PR ##{pr['number']} to '#{title}'"
|
|
53
|
+
end
|
|
54
|
+
|
|
46
55
|
return
|
|
47
56
|
end
|
|
48
57
|
|
|
@@ -40,8 +40,17 @@ module ModuleSync
|
|
|
40
40
|
source_branch: source_branch,
|
|
41
41
|
target_branch: target_branch)
|
|
42
42
|
unless merge_requests.empty?
|
|
43
|
-
#
|
|
44
|
-
|
|
43
|
+
# assumption: there should only be one MR for a given source and target branch, so we just take the first one
|
|
44
|
+
mr = merge_requests.first
|
|
45
|
+
|
|
46
|
+
if mr['title'] == title
|
|
47
|
+
$stdout.puts "Skipped! #{merge_requests.length} MRs found for branch '#{source_branch}'"
|
|
48
|
+
$stdout.puts "Skipped! Existing MR !#{mr['iid']} already has title '#{title}'"
|
|
49
|
+
else
|
|
50
|
+
@api.update_merge_request(repo_path, mr['iid'], title: title)
|
|
51
|
+
$stdout.puts "Updated title of existing MR !#{mr['iid']} to '#{title}'"
|
|
52
|
+
end
|
|
53
|
+
|
|
45
54
|
return
|
|
46
55
|
end
|
|
47
56
|
|
|
@@ -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.
|
|
34
|
+
repository.commit_changes("Release version #{new}", signing_options)
|
|
35
35
|
repository.git.push
|
|
36
36
|
new
|
|
37
37
|
end
|
|
@@ -42,6 +42,12 @@ module ModuleSync
|
|
|
42
42
|
commits.any?
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def branch_behind?(branch, target_branch)
|
|
46
|
+
log = repo.log(1).between(branch, "origin/#{target_branch}")
|
|
47
|
+
commits = log.respond_to?(:execute) ? log.execute : log
|
|
48
|
+
commits.any?
|
|
49
|
+
end
|
|
50
|
+
|
|
45
51
|
def default_branch
|
|
46
52
|
# `Git.default_branch` requires ruby-git >= 1.17.0
|
|
47
53
|
return Git.default_branch(repo.dir) if Git.respond_to? :default_branch
|
|
@@ -52,6 +58,12 @@ module ModuleSync
|
|
|
52
58
|
%r{remotes/origin/HEAD\s+->\s+origin/(?<branch>.+?)$}.match(symbolic_ref.full)[:branch]
|
|
53
59
|
end
|
|
54
60
|
|
|
61
|
+
def remote_default_branch
|
|
62
|
+
return Git.default_branch(@remote) if Git.respond_to? :default_branch
|
|
63
|
+
|
|
64
|
+
default_branch
|
|
65
|
+
end
|
|
66
|
+
|
|
55
67
|
def switch(branch:)
|
|
56
68
|
unless branch
|
|
57
69
|
branch = default_branch
|
|
@@ -87,13 +99,16 @@ module ModuleSync
|
|
|
87
99
|
@git = Git.clone(@remote, @directory)
|
|
88
100
|
end
|
|
89
101
|
|
|
90
|
-
def prepare_workspace(branch:, operate_offline:)
|
|
102
|
+
def prepare_workspace(branch:, operate_offline:, rebase: false)
|
|
103
|
+
@rebased = false
|
|
91
104
|
if cloned?
|
|
92
105
|
puts "Overriding any local changes to repository in '#{@directory}'"
|
|
93
106
|
git.fetch 'origin', prune: true unless operate_offline
|
|
94
107
|
git.reset_hard
|
|
108
|
+
rebase_target = remote_default_branch if rebase && !operate_offline
|
|
95
109
|
switch(branch: branch)
|
|
96
110
|
git.pull('origin', branch) if !operate_offline && remote_branch_exists?(branch)
|
|
111
|
+
rebase_onto(rebase_target) if rebase_target
|
|
97
112
|
else
|
|
98
113
|
raise ModuleSync::Error, 'Unable to clone in offline mode.' if operate_offline
|
|
99
114
|
|
|
@@ -102,6 +117,31 @@ module ModuleSync
|
|
|
102
117
|
end
|
|
103
118
|
end
|
|
104
119
|
|
|
120
|
+
def rebase_onto(branch)
|
|
121
|
+
return false if repo.current_branch == branch || !branch_behind?(repo.current_branch, branch)
|
|
122
|
+
|
|
123
|
+
puts "Rebasing #{repo.current_branch} onto origin/#{branch}"
|
|
124
|
+
repo.lib.send(:command, 'rebase', "origin/#{branch}")
|
|
125
|
+
@rebased = true
|
|
126
|
+
rescue Git::Error => e
|
|
127
|
+
begin
|
|
128
|
+
repo.lib.send(:command, 'rebase', '--abort')
|
|
129
|
+
rescue Git::Error
|
|
130
|
+
# Preserve the original rebase error if Git had no rebase to abort.
|
|
131
|
+
end
|
|
132
|
+
raise ModuleSync::Error, "Rebase onto origin/#{branch} failed and was aborted: #{e.message}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def push_changes(branch, remote_branch, options)
|
|
136
|
+
refspec = remote_branch ? "#{branch}:#{remote_branch}" : branch
|
|
137
|
+
if @rebased && !options[:force]
|
|
138
|
+
repo.lib.send(:command, 'push', '--force-with-lease', 'origin', refspec)
|
|
139
|
+
else
|
|
140
|
+
opts_push = options[:force] ? { force: true } : {}
|
|
141
|
+
repo.push('origin', refspec, opts_push)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
105
145
|
def default_reset_branch(branch)
|
|
106
146
|
remote_branch_exists?(branch) ? branch : default_branch
|
|
107
147
|
end
|
|
@@ -125,10 +165,10 @@ module ModuleSync
|
|
|
125
165
|
end
|
|
126
166
|
end
|
|
127
167
|
|
|
128
|
-
def tag(version, tag_pattern)
|
|
168
|
+
def tag(version, tag_pattern, sign: false)
|
|
129
169
|
tag = tag_pattern % version
|
|
130
170
|
puts "Tagging with #{tag}"
|
|
131
|
-
repo.add_tag(tag)
|
|
171
|
+
repo.add_tag(tag, sign: sign)
|
|
132
172
|
repo.push('origin', tag)
|
|
133
173
|
end
|
|
134
174
|
|
|
@@ -138,6 +178,19 @@ module ModuleSync
|
|
|
138
178
|
selected_branch
|
|
139
179
|
end
|
|
140
180
|
|
|
181
|
+
def commit_changes(message, options)
|
|
182
|
+
commit_options = {}
|
|
183
|
+
commit_options[:amend] = true if options[:amend]
|
|
184
|
+
commit_options[:gpg_sign] = true if options[:sign]
|
|
185
|
+
return repo.commit(message, commit_options) unless options[:signoff]
|
|
186
|
+
|
|
187
|
+
arguments = ["--message=#{message}"]
|
|
188
|
+
arguments.push('--amend', '--no-edit') if options[:amend]
|
|
189
|
+
arguments << '--gpg-sign' if options[:sign]
|
|
190
|
+
arguments << '--signoff'
|
|
191
|
+
repo.lib.send(:command, 'commit', *arguments)
|
|
192
|
+
end
|
|
193
|
+
|
|
141
194
|
# Git add/rm, git commit, git push
|
|
142
195
|
def submit_changes(files, options)
|
|
143
196
|
message = options[:message]
|
|
@@ -150,27 +203,23 @@ module ModuleSync
|
|
|
150
203
|
end
|
|
151
204
|
end
|
|
152
205
|
begin
|
|
153
|
-
opts_commit = {}
|
|
154
|
-
opts_push = {}
|
|
155
|
-
opts_commit = { amend: true } if options[:amend]
|
|
156
|
-
opts_push = { force: true } if options[:force]
|
|
157
206
|
if options[:pre_commit_script]
|
|
158
207
|
script = "#{File.dirname(__FILE__, 3)}/contrib/#{options[:pre_commit_script]}"
|
|
159
208
|
system(script, @directory)
|
|
160
209
|
end
|
|
161
210
|
if repo.status.changed.empty? && repo.status.added.empty? && repo.status.deleted.empty?
|
|
162
211
|
puts "There were no changes in '#{@directory}'. Not committing."
|
|
163
|
-
return false
|
|
212
|
+
return false unless @rebased
|
|
164
213
|
else
|
|
165
|
-
|
|
214
|
+
commit_changes(message, options)
|
|
166
215
|
end
|
|
167
216
|
if options[:remote_branch]
|
|
168
|
-
if remote_branch_differ?(branch, options[:remote_branch])
|
|
169
|
-
|
|
217
|
+
if @rebased || remote_branch_differ?(branch, options[:remote_branch])
|
|
218
|
+
push_changes(branch, options[:remote_branch], options)
|
|
170
219
|
puts "Changes have been pushed to: '#{branch}:#{options[:remote_branch]}'"
|
|
171
220
|
end
|
|
172
221
|
else
|
|
173
|
-
|
|
222
|
+
push_changes(branch, nil, options)
|
|
174
223
|
puts "Changes have been pushed to: '#{branch}'"
|
|
175
224
|
end
|
|
176
225
|
rescue Git::Error => e
|
data/lib/modulesync/version.rb
CHANGED
data/lib/modulesync.rb
CHANGED
|
@@ -123,7 +123,8 @@ module ModuleSync
|
|
|
123
123
|
# but we totally skip the workspace preparation to keep the current behavior
|
|
124
124
|
unless options[:offline]
|
|
125
125
|
puppet_module.repository.prepare_workspace(branch: options[:branch],
|
|
126
|
-
operate_offline: false
|
|
126
|
+
operate_offline: false,
|
|
127
|
+
rebase: options[:rebase])
|
|
127
128
|
end
|
|
128
129
|
|
|
129
130
|
module_configs = Util.parse_config puppet_module.path(MODULE_CONF_FILE)
|
|
@@ -150,8 +151,9 @@ module ModuleSync
|
|
|
150
151
|
pushed = puppet_module.repository.submit_changes(files_to_manage, options)
|
|
151
152
|
# Only bump/tag if pushing didn't fail (i.e. there were changes)
|
|
152
153
|
if pushed && options[:bump]
|
|
153
|
-
|
|
154
|
-
puppet_module.
|
|
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]
|
|
155
157
|
end
|
|
156
158
|
options[:pr] && (pushed || puppet_module.pull_request_branch_ready?) && puppet_module.open_pull_request
|
|
157
159
|
end
|
|
@@ -54,7 +54,7 @@ describe ModuleSync::GitService::GitHub do
|
|
|
54
54
|
expect { @it.open_pull_request(**args) }.to output(/Submitted PR/).to_stdout
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
it '
|
|
57
|
+
it 'updates the title if an existing PR has a different title' do
|
|
58
58
|
pr = {
|
|
59
59
|
'title' => 'Test title',
|
|
60
60
|
'html_url' => 'https://example.com/pulls/44',
|
|
@@ -66,7 +66,28 @@ describe ModuleSync::GitService::GitHub do
|
|
|
66
66
|
state: 'open',
|
|
67
67
|
base: 'master',
|
|
68
68
|
head: "#{args[:namespace]}:#{args[:source_branch]}").and_return([pr])
|
|
69
|
-
expect
|
|
69
|
+
expect(@client).to receive(:update_pull_request)
|
|
70
|
+
.with(args[:repo_path], pr['number'], title: args[:title])
|
|
71
|
+
expect { @it.open_pull_request(**args) }
|
|
72
|
+
.to output("Updated title of existing PR #44 to 'Test PR is submitted'\n").to_stdout
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'skips updating an existing PR if its title is unchanged' do
|
|
76
|
+
pr = {
|
|
77
|
+
'title' => args[:title],
|
|
78
|
+
'html_url' => 'https://example.com/pulls/44',
|
|
79
|
+
'number' => '44',
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
expect(@client).to receive(:pull_requests)
|
|
83
|
+
.with(args[:repo_path],
|
|
84
|
+
state: 'open',
|
|
85
|
+
base: 'master',
|
|
86
|
+
head: "#{args[:namespace]}:#{args[:source_branch]}").and_return([pr])
|
|
87
|
+
expect(@client).not_to receive(:update_pull_request)
|
|
88
|
+
expect { @it.open_pull_request(**args) }
|
|
89
|
+
.to output("Skipped! 1 PRs found for branch 'test'\n" \
|
|
90
|
+
"Skipped! Existing PR #44 already has title 'Test PR is submitted'\n").to_stdout
|
|
70
91
|
end
|
|
71
92
|
|
|
72
93
|
context 'when labels are set' do
|
|
@@ -44,7 +44,7 @@ describe ModuleSync::GitService::GitLab do
|
|
|
44
44
|
expect { @it.open_pull_request(**args) }.to output(/Submitted MR/).to_stdout
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
it '
|
|
47
|
+
it 'updates the title if an existing MR has a different title' do
|
|
48
48
|
mr = {
|
|
49
49
|
'title' => 'Test title',
|
|
50
50
|
'html_url' => 'https://example.com/pulls/44',
|
|
@@ -56,8 +56,28 @@ describe ModuleSync::GitService::GitLab do
|
|
|
56
56
|
state: 'opened',
|
|
57
57
|
source_branch: args[:source_branch],
|
|
58
58
|
target_branch: 'master').and_return([mr])
|
|
59
|
+
expect(@client).to receive(:update_merge_request)
|
|
60
|
+
.with(args[:repo_path], mr['iid'], title: args[:title])
|
|
61
|
+
expect { @it.open_pull_request(**args) }
|
|
62
|
+
.to output("Updated title of existing MR !44 to 'Test MR is submitted'\n").to_stdout
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'skips updating an existing MR if its title is unchanged' do
|
|
66
|
+
mr = {
|
|
67
|
+
'title' => args[:title],
|
|
68
|
+
'html_url' => 'https://example.com/pulls/44',
|
|
69
|
+
'iid' => '44',
|
|
70
|
+
}
|
|
59
71
|
|
|
60
|
-
expect
|
|
72
|
+
expect(@client).to receive(:merge_requests)
|
|
73
|
+
.with(args[:repo_path],
|
|
74
|
+
state: 'opened',
|
|
75
|
+
source_branch: args[:source_branch],
|
|
76
|
+
target_branch: 'master').and_return([mr])
|
|
77
|
+
expect(@client).not_to receive(:update_merge_request)
|
|
78
|
+
expect { @it.open_pull_request(**args) }
|
|
79
|
+
.to output("Skipped! 1 MRs found for branch 'test'\n" \
|
|
80
|
+
"Skipped! Existing MR !44 already has title 'Test MR is submitted'\n").to_stdout
|
|
61
81
|
end
|
|
62
82
|
|
|
63
83
|
context 'when labels are set' do
|
|
@@ -15,10 +15,12 @@ describe ModuleSync::Repository do
|
|
|
15
15
|
end
|
|
16
16
|
let(:branches) { instance_double(Git::Branches, remote: remote_branches) }
|
|
17
17
|
let(:log) { instance_double(Git::Log) }
|
|
18
|
-
let(:
|
|
18
|
+
let(:git_lib) { instance_double(Git::Lib) }
|
|
19
|
+
let(:git) { instance_double(Git::Base, branches: branches, lib: git_lib, log: log) }
|
|
19
20
|
|
|
20
21
|
before do
|
|
21
22
|
repository.instance_variable_set(:@git, git)
|
|
23
|
+
ModuleSync.instance_variable_set(:@options, verbose: false)
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
it 'detects commits on a remote source branch that are missing from the target branch' do
|
|
@@ -33,4 +35,116 @@ describe ModuleSync::Repository do
|
|
|
33
35
|
|
|
34
36
|
expect(repository.remote_branch_ahead?('modulesync', 'main')).to be false
|
|
35
37
|
end
|
|
38
|
+
|
|
39
|
+
describe '#rebase_onto' do
|
|
40
|
+
before do
|
|
41
|
+
allow(git).to receive(:current_branch).and_return('modulesync')
|
|
42
|
+
allow(log).to receive(:between).with('modulesync', 'origin/main').and_return(log)
|
|
43
|
+
allow(log).to receive(:execute).and_return([instance_double(Git::Object::Commit)])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'rebases the current branch onto the remote default branch' do
|
|
47
|
+
expect(git_lib).to receive(:send).with(:command, 'rebase', 'origin/main')
|
|
48
|
+
|
|
49
|
+
repository.rebase_onto('main')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'does not rebase the default branch onto itself' do
|
|
53
|
+
allow(git).to receive(:current_branch).and_return('main')
|
|
54
|
+
expect(git_lib).not_to receive(:send)
|
|
55
|
+
|
|
56
|
+
repository.rebase_onto('main')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'does not rebase a branch that already contains the remote default branch' do
|
|
60
|
+
allow(log).to receive(:execute).and_return([])
|
|
61
|
+
expect(git_lib).not_to receive(:send)
|
|
62
|
+
|
|
63
|
+
repository.rebase_onto('main')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'aborts and reports a failed rebase' do
|
|
67
|
+
allow(git_lib).to receive(:send).with(:command, 'rebase', 'origin/main').and_raise(Git::Error, 'merge conflict')
|
|
68
|
+
expect(git_lib).to receive(:send).with(:command, 'rebase', '--abort')
|
|
69
|
+
|
|
70
|
+
expect { repository.rebase_onto('main') }
|
|
71
|
+
.to raise_error(ModuleSync::Error, %r{Rebase onto origin/main failed and was aborted: merge conflict})
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe '#prepare_workspace' do
|
|
76
|
+
it 'fetches and rebases the selected branch onto the remote default branch when requested' do
|
|
77
|
+
allow(Dir).to receive(:exist?).with('/tmp/example/.git').and_return(true)
|
|
78
|
+
allow(Git).to receive(:default_branch).with('https://github.com/example/repository.git').and_return('main')
|
|
79
|
+
allow(git).to receive(:current_branch).and_return('modulesync')
|
|
80
|
+
allow(log).to receive(:between).with('modulesync', 'origin/main').and_return(log)
|
|
81
|
+
allow(log).to receive(:execute).and_return([instance_double(Git::Object::Commit)])
|
|
82
|
+
expect(git).to receive(:fetch).with('origin', prune: true).ordered
|
|
83
|
+
expect(git).to receive(:reset_hard).ordered
|
|
84
|
+
expect(git).to receive(:pull).with('origin', 'modulesync').ordered
|
|
85
|
+
expect(git_lib).to receive(:send).with(:command, 'rebase', 'origin/main').ordered
|
|
86
|
+
|
|
87
|
+
repository.prepare_workspace(branch: 'modulesync', operate_offline: false, rebase: true)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#submit_changes' do
|
|
92
|
+
it 'does not push an unchanged branch that was not rebased' do
|
|
93
|
+
status = instance_double(Git::Status, added: {}, changed: {}, deleted: {})
|
|
94
|
+
branch = instance_double(Git::Branch, checkout: true)
|
|
95
|
+
allow(git).to receive(:branch).with('modulesync').and_return(branch)
|
|
96
|
+
allow(git).to receive(:status).and_return(status)
|
|
97
|
+
expect(git).not_to receive(:push)
|
|
98
|
+
expect(git_lib).not_to receive(:send)
|
|
99
|
+
|
|
100
|
+
result = repository.submit_changes([], branch: 'modulesync', message: 'Update', force: false)
|
|
101
|
+
|
|
102
|
+
expect(result).to be false
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'pushes a rebased branch even when ModuleSync made no file changes' do
|
|
106
|
+
status = instance_double(Git::Status, added: {}, changed: {}, deleted: {})
|
|
107
|
+
branch = instance_double(Git::Branch, checkout: true)
|
|
108
|
+
repository.instance_variable_set(:@rebased, true)
|
|
109
|
+
allow(git).to receive(:branch).with('modulesync').and_return(branch)
|
|
110
|
+
allow(git).to receive(:status).and_return(status)
|
|
111
|
+
expect(git_lib).to receive(:send)
|
|
112
|
+
.with(:command, 'push', '--force-with-lease', 'origin', 'modulesync')
|
|
113
|
+
|
|
114
|
+
result = repository.submit_changes([], branch: 'modulesync', message: 'Update', force: false)
|
|
115
|
+
|
|
116
|
+
expect(result).to be true
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe '#commit_changes' do
|
|
121
|
+
it 'signs the commit when requested' do
|
|
122
|
+
expect(git).to receive(:commit).with('Update', { gpg_sign: true })
|
|
123
|
+
|
|
124
|
+
repository.commit_changes('Update', sign: true, signoff: false)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'uses the Git signoff option when requested' do
|
|
128
|
+
expect(git_lib).to receive(:send)
|
|
129
|
+
.with(:command, 'commit', '--message=Update', '--signoff')
|
|
130
|
+
|
|
131
|
+
repository.commit_changes('Update', sign: false, signoff: true)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'combines amend, signing, and signoff options' do
|
|
135
|
+
expect(git_lib).to receive(:send)
|
|
136
|
+
.with(:command, 'commit', '--message=Update', '--amend', '--no-edit', '--gpg-sign', '--signoff')
|
|
137
|
+
|
|
138
|
+
repository.commit_changes('Update', amend: true, sign: true, signoff: true)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
describe '#tag' do
|
|
143
|
+
it 'signs and pushes the tag when requested' do
|
|
144
|
+
expect(git).to receive(:add_tag).with('v1.2.3', sign: true)
|
|
145
|
+
expect(git).to receive(:push).with('origin', 'v1.2.3')
|
|
146
|
+
|
|
147
|
+
repository.tag('1.2.3', 'v%s', sign: true)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
36
150
|
end
|
|
@@ -63,5 +63,24 @@ describe ModuleSync do
|
|
|
63
63
|
|
|
64
64
|
described_class.manage_module(puppet_module, [], {})
|
|
65
65
|
end
|
|
66
|
+
|
|
67
|
+
it 'requests a rebase before syncing when enabled' do
|
|
68
|
+
described_class.instance_variable_set(:@options, described_class.config_defaults.merge(rebase: true))
|
|
69
|
+
expect(repository).to receive(:prepare_workspace).with(branch: nil, operate_offline: false, rebase: true)
|
|
70
|
+
|
|
71
|
+
described_class.manage_module(puppet_module, [], {})
|
|
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
|
|
66
85
|
end
|
|
67
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.
|
|
4
|
+
version: 4.6.0
|
|
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.
|
|
269
|
+
rubygems_version: 4.0.16
|
|
270
270
|
specification_version: 4
|
|
271
271
|
summary: Puppet Module Synchronizer
|
|
272
272
|
test_files: []
|