gitops-replacer 0.1.1__tar.gz
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.
- gitops_replacer-0.1.1/LICENSE +21 -0
- gitops_replacer-0.1.1/MANIFEST.in +5 -0
- gitops_replacer-0.1.1/PKG-INFO +345 -0
- gitops_replacer-0.1.1/README.md +313 -0
- gitops_replacer-0.1.1/example.json +11 -0
- gitops_replacer-0.1.1/pyproject.toml +59 -0
- gitops_replacer-0.1.1/setup.cfg +4 -0
- gitops_replacer-0.1.1/src/gitops_replacer/__init__.py +9 -0
- gitops_replacer-0.1.1/src/gitops_replacer/__main__.py +336 -0
- gitops_replacer-0.1.1/src/gitops_replacer.egg-info/PKG-INFO +345 -0
- gitops_replacer-0.1.1/src/gitops_replacer.egg-info/SOURCES.txt +13 -0
- gitops_replacer-0.1.1/src/gitops_replacer.egg-info/dependency_links.txt +1 -0
- gitops_replacer-0.1.1/src/gitops_replacer.egg-info/entry_points.txt +2 -0
- gitops_replacer-0.1.1/src/gitops_replacer.egg-info/requires.txt +2 -0
- gitops_replacer-0.1.1/src/gitops_replacer.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Simon Lauger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gitops-replacer
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Automated YAML/JSON value updates for GitOps repositories via GitHub API
|
|
5
|
+
Author-email: Simon Lauger <simon@lauger.de>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/slauger/gitops-replacer
|
|
8
|
+
Project-URL: Documentation, https://github.com/slauger/gitops-replacer#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/slauger/gitops-replacer
|
|
10
|
+
Project-URL: Issues, https://github.com/slauger/gitops-replacer/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/slauger/gitops-replacer/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: gitops,yaml,json,kubernetes,helm,automation,github,ci-cd,deployment
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
22
|
+
Classifier: Operating System :: OS Independent
|
|
23
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
24
|
+
Classifier: Topic :: System :: Systems Administration
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: requests>=2.25.0
|
|
30
|
+
Requires-Dist: PyYAML>=5.4.0
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# gitops-replacer
|
|
34
|
+
|
|
35
|
+
[](https://opensource.org/licenses/MIT)
|
|
36
|
+
[](https://www.python.org/downloads/)
|
|
37
|
+
[](https://pypi.org/project/gitops-replacer/)
|
|
38
|
+
[](https://pypi.org/project/gitops-replacer/)
|
|
39
|
+
|
|
40
|
+
A lightweight CLI tool that automates value updates in GitOps repositories using marker comments. Replace values across multiple GitHub repositories with a single command, enabling automated deployment workflows.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- **Marker-based Approach**: Uses `# gitops-replacer: <name>` comments to locate values
|
|
45
|
+
- **Format Preservation**: No YAML parsing - comments, quotes, and formatting are preserved
|
|
46
|
+
- **Multiple Dependencies**: Update different values in the same file via unique markers
|
|
47
|
+
- **Flexible Modes**: Dry-run for validation, apply mode for commits
|
|
48
|
+
- **CI/CD Integration**: Built-in CI mode with `GITHUB_REF` pattern matching
|
|
49
|
+
- **Multiple Repositories**: Update values across any number of repos and files
|
|
50
|
+
- **Configuration Formats**: JSON (default) and YAML support
|
|
51
|
+
- **Performance Optimized**: Response caching eliminates duplicate API calls
|
|
52
|
+
- **Robust HTTP**: Automatic retries, timeouts, and error handling
|
|
53
|
+
|
|
54
|
+
## Requirements
|
|
55
|
+
|
|
56
|
+
- Python 3.10+
|
|
57
|
+
- A GitHub/GitHub Enterprise token with content read/write access
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
### Via pip (recommended)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Install from PyPI
|
|
65
|
+
pip install gitops-replacer
|
|
66
|
+
|
|
67
|
+
# Verify installation
|
|
68
|
+
gitops-replacer --help
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### From source
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Clone repository
|
|
75
|
+
git clone https://github.com/slauger/gitops-replacer.git
|
|
76
|
+
cd gitops-replacer
|
|
77
|
+
|
|
78
|
+
# Install in development mode
|
|
79
|
+
pip install -e .
|
|
80
|
+
|
|
81
|
+
# Or run directly
|
|
82
|
+
python -m gitops_replacer --help
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Quick Start
|
|
86
|
+
|
|
87
|
+
1. Add marker comments to your target files (see [Marker Format](#marker-format))
|
|
88
|
+
2. Create a configuration file (default: `gitops-replacer.json`)
|
|
89
|
+
3. Run a dry-run:
|
|
90
|
+
```bash
|
|
91
|
+
gitops-replacer "1.2.3"
|
|
92
|
+
```
|
|
93
|
+
4. Apply changes (commit to target repos):
|
|
94
|
+
```bash
|
|
95
|
+
gitops-replacer --apply "1.2.3"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Marker Format
|
|
99
|
+
|
|
100
|
+
Add a comment **above** the line you want to update:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
dependencies:
|
|
104
|
+
# gitops-replacer: my-app
|
|
105
|
+
- name: my-app
|
|
106
|
+
version: "0.0.0-e0f72bb"
|
|
107
|
+
repository: oci://registry.example.com/charts
|
|
108
|
+
|
|
109
|
+
# gitops-replacer: another-chart
|
|
110
|
+
- name: another-chart
|
|
111
|
+
version: "1.0.0"
|
|
112
|
+
repository: oci://registry.example.com/charts
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The tool will:
|
|
116
|
+
1. Find the line with `# gitops-replacer: <depName>`
|
|
117
|
+
2. Replace the value on the **next line** (preserving key, quotes, and formatting)
|
|
118
|
+
|
|
119
|
+
### Examples
|
|
120
|
+
|
|
121
|
+
**Chart.yaml (Helm dependency version):**
|
|
122
|
+
```yaml
|
|
123
|
+
dependencies:
|
|
124
|
+
# gitops-replacer: my-app
|
|
125
|
+
- name: my-app
|
|
126
|
+
version: "0.0.0-e0f72bb"
|
|
127
|
+
repository: oci://registry.example.com/charts
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**values.yaml (image tag):**
|
|
131
|
+
```yaml
|
|
132
|
+
# gitops-replacer: my-app-image
|
|
133
|
+
image: registry.example.com/myorg/my-app:1.2.3
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Note:** Only YAML files are supported (JSON has no comments). GitOps manifests are typically YAML.
|
|
137
|
+
|
|
138
|
+
## CLI
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
usage: gitops-replacer [-h] [--config <file>] [--apply] [--ci]
|
|
142
|
+
[--name <string>] [--email <string>]
|
|
143
|
+
[--message <string>] [--api <string>]
|
|
144
|
+
[--verbose]
|
|
145
|
+
<string>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- `--config` Path to the configuration file (default: `gitops-replacer.json`). JSON recommended.
|
|
149
|
+
- `--apply` Apply changes (commit). Without this flag the tool runs in dry-run.
|
|
150
|
+
- `--ci` CI mode: validates `GITHUB_REF` against `when`/`except` regex patterns from config.
|
|
151
|
+
- `--name` Commit author name (default: env `GIT_COMMIT_NAME` or `Replacer Bot`).
|
|
152
|
+
- `--email` Commit author email (default: env `GIT_COMMIT_EMAIL` or `replacer-bot@localhost.localdomain`).
|
|
153
|
+
- `--message` Commit message template (default: `fix: update {} to {}`). First `{}` is depName, second is value.
|
|
154
|
+
- `--api` GitHub API URL (default: env `GITHUB_API_URL` or `https://api.github.com`).
|
|
155
|
+
- `--verbose` Print file contents and desired state (use with care in CI logs).
|
|
156
|
+
- Positional: `value` - the new value to set at the marked location.
|
|
157
|
+
|
|
158
|
+
### Environment
|
|
159
|
+
|
|
160
|
+
- `GITHUB_TOKEN` **(required)** – token with access to read/write repository contents.
|
|
161
|
+
- `GITHUB_REF` *(required when `--ci`)* – the current ref string, e.g., `refs/heads/main`. Falls back to `GIT_REF` for backwards compatibility.
|
|
162
|
+
|
|
163
|
+
Recommended token scopes:
|
|
164
|
+
- Public repos only: `public_repo`
|
|
165
|
+
- Private repos: `repo`
|
|
166
|
+
- GitHub Enterprise: equivalent content permissions
|
|
167
|
+
|
|
168
|
+
## Configuration
|
|
169
|
+
|
|
170
|
+
Default format is **JSON**. YAML (`.yaml`/`.yml`) is supported as well.
|
|
171
|
+
|
|
172
|
+
### JSON schema (per entry)
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"gitops-replacer": [
|
|
177
|
+
{
|
|
178
|
+
"repository": "acme/gitops",
|
|
179
|
+
"branch": "main",
|
|
180
|
+
"file": "apps/my-app/Chart.yaml",
|
|
181
|
+
"depName": "my-app",
|
|
182
|
+
"when": "^refs/heads/main$"
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Fields**
|
|
189
|
+
|
|
190
|
+
| Field | Description |
|
|
191
|
+
|-------|-------------|
|
|
192
|
+
| `repository` | Target repo on GitHub (`ORG/REPO` format) |
|
|
193
|
+
| `branch` | Target branch |
|
|
194
|
+
| `file` | Target file path relative to repo root |
|
|
195
|
+
| `depName` | Dependency name (must match marker in file) |
|
|
196
|
+
| `when` | Regex that must match `GITHUB_REF` when `--ci` is enabled (optional) |
|
|
197
|
+
| `except` | Regex that must **not** match `GITHUB_REF` when `--ci` is enabled (optional) |
|
|
198
|
+
|
|
199
|
+
> The tool uses `re.match` (anchored at the string start). Use `^...$` in your patterns if you require a full match.
|
|
200
|
+
|
|
201
|
+
### Examples
|
|
202
|
+
|
|
203
|
+
**JSON (default)**
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"gitops-replacer": [
|
|
208
|
+
{
|
|
209
|
+
"repository": "acme/gitops",
|
|
210
|
+
"branch": "main",
|
|
211
|
+
"file": "apps/my-app/Chart.yaml",
|
|
212
|
+
"depName": "my-app",
|
|
213
|
+
"when": "^refs/heads/(main|release/.*)$"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"repository": "acme/gitops",
|
|
217
|
+
"branch": "develop",
|
|
218
|
+
"file": "apps/my-app-dev/Chart.yaml",
|
|
219
|
+
"depName": "my-app",
|
|
220
|
+
"except": "^refs/heads/legacy/"
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**YAML (alternative)**
|
|
227
|
+
|
|
228
|
+
```yaml
|
|
229
|
+
gitops-replacer:
|
|
230
|
+
- repository: acme/gitops
|
|
231
|
+
branch: main
|
|
232
|
+
file: apps/my-app/Chart.yaml
|
|
233
|
+
depName: my-app
|
|
234
|
+
when: '^refs/heads/(main|release/.*)$'
|
|
235
|
+
- repository: acme/gitops
|
|
236
|
+
branch: develop
|
|
237
|
+
file: apps/my-app-dev/Chart.yaml
|
|
238
|
+
depName: my-app
|
|
239
|
+
except: '^refs/heads/legacy/'
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## How it works
|
|
243
|
+
|
|
244
|
+
1. **Validation**: Checks CLI arguments, environment variables, and configuration file
|
|
245
|
+
2. **Precheck Phase**: Validates access to all target repositories/files (caches responses)
|
|
246
|
+
3. **Replace Phase**: Downloads files (reuses cached data), finds marker comments, replaces values
|
|
247
|
+
4. **Commit Phase**: If `--apply` is set and changes detected, commits via GitHub Contents API
|
|
248
|
+
5. **Exit Codes**: Returns `0` on success, non-zero on failures
|
|
249
|
+
|
|
250
|
+
### Why Marker-based?
|
|
251
|
+
|
|
252
|
+
Traditional approaches parse YAML, modify the data structure, and serialize back. This often breaks:
|
|
253
|
+
- Comments are lost
|
|
254
|
+
- Quote styles change (`"1.0"` becomes `'1.0'` or `1.0`)
|
|
255
|
+
- Key ordering may change
|
|
256
|
+
- Multi-line strings get reformatted
|
|
257
|
+
|
|
258
|
+
The marker-based approach works on raw text:
|
|
259
|
+
- **Explicit**: Only marked lines are modified
|
|
260
|
+
- **Safe**: No risk of unintended changes
|
|
261
|
+
- **Preserving**: Comments, quotes, and formatting stay intact
|
|
262
|
+
|
|
263
|
+
## Exit Codes
|
|
264
|
+
|
|
265
|
+
- `0` success (no changes or committed changes)
|
|
266
|
+
- `1` validation or API error
|
|
267
|
+
|
|
268
|
+
## Use Cases
|
|
269
|
+
|
|
270
|
+
### Automated Deployment Pipeline
|
|
271
|
+
|
|
272
|
+
Update chart version when a new release is built:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# In your CI/CD pipeline after publishing a chart
|
|
276
|
+
gitops-replacer --ci --apply "0.1.0-abc123"
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Multi-Environment Updates
|
|
280
|
+
|
|
281
|
+
Use CI mode to update different environments based on branch:
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
{
|
|
285
|
+
"gitops-replacer": [
|
|
286
|
+
{
|
|
287
|
+
"repository": "myorg/gitops",
|
|
288
|
+
"branch": "main",
|
|
289
|
+
"file": "apps/production/Chart.yaml",
|
|
290
|
+
"depName": "myapp",
|
|
291
|
+
"when": "^refs/heads/main$"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"repository": "myorg/gitops",
|
|
295
|
+
"branch": "main",
|
|
296
|
+
"file": "apps/staging/Chart.yaml",
|
|
297
|
+
"depName": "myapp",
|
|
298
|
+
"when": "^refs/heads/(main|develop)$"
|
|
299
|
+
}
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Troubleshooting
|
|
305
|
+
|
|
306
|
+
### Common Issues
|
|
307
|
+
|
|
308
|
+
**401 Unauthorized**
|
|
309
|
+
- Verify `GITHUB_TOKEN` is set correctly
|
|
310
|
+
- Check token has `repo` or `public_repo` scope
|
|
311
|
+
- For GitHub Enterprise, confirm token has access to the organization
|
|
312
|
+
|
|
313
|
+
**404 Not Found**
|
|
314
|
+
- Verify `repository`, `branch`, and `file` paths in config
|
|
315
|
+
- Check branch name spelling (case-sensitive)
|
|
316
|
+
- Ensure file exists at the specified path
|
|
317
|
+
|
|
318
|
+
**No marker found**
|
|
319
|
+
- Confirm the marker comment exists in the target file
|
|
320
|
+
- Check `depName` in config matches the marker exactly
|
|
321
|
+
- Marker format: `# gitops-replacer: <depName>`
|
|
322
|
+
|
|
323
|
+
**No changes detected**
|
|
324
|
+
- The current value already matches the new value
|
|
325
|
+
- Use `--verbose` to see file contents
|
|
326
|
+
|
|
327
|
+
### Debug Mode
|
|
328
|
+
|
|
329
|
+
Run with `--verbose` to see:
|
|
330
|
+
- Full API URLs being called
|
|
331
|
+
- Complete file contents before replacement
|
|
332
|
+
- Desired file contents after replacement
|
|
333
|
+
|
|
334
|
+
**Warning**: Verbose mode may expose sensitive data in logs.
|
|
335
|
+
|
|
336
|
+
## Contributing
|
|
337
|
+
|
|
338
|
+
Contributions are welcome! Please ensure:
|
|
339
|
+
- Code follows existing style and patterns
|
|
340
|
+
- Changes are tested with both dry-run and apply modes
|
|
341
|
+
- Documentation is updated for new features
|
|
342
|
+
|
|
343
|
+
## License
|
|
344
|
+
|
|
345
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# gitops-replacer
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://pypi.org/project/gitops-replacer/)
|
|
6
|
+
[](https://pypi.org/project/gitops-replacer/)
|
|
7
|
+
|
|
8
|
+
A lightweight CLI tool that automates value updates in GitOps repositories using marker comments. Replace values across multiple GitHub repositories with a single command, enabling automated deployment workflows.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Marker-based Approach**: Uses `# gitops-replacer: <name>` comments to locate values
|
|
13
|
+
- **Format Preservation**: No YAML parsing - comments, quotes, and formatting are preserved
|
|
14
|
+
- **Multiple Dependencies**: Update different values in the same file via unique markers
|
|
15
|
+
- **Flexible Modes**: Dry-run for validation, apply mode for commits
|
|
16
|
+
- **CI/CD Integration**: Built-in CI mode with `GITHUB_REF` pattern matching
|
|
17
|
+
- **Multiple Repositories**: Update values across any number of repos and files
|
|
18
|
+
- **Configuration Formats**: JSON (default) and YAML support
|
|
19
|
+
- **Performance Optimized**: Response caching eliminates duplicate API calls
|
|
20
|
+
- **Robust HTTP**: Automatic retries, timeouts, and error handling
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Python 3.10+
|
|
25
|
+
- A GitHub/GitHub Enterprise token with content read/write access
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
### Via pip (recommended)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Install from PyPI
|
|
33
|
+
pip install gitops-replacer
|
|
34
|
+
|
|
35
|
+
# Verify installation
|
|
36
|
+
gitops-replacer --help
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### From source
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Clone repository
|
|
43
|
+
git clone https://github.com/slauger/gitops-replacer.git
|
|
44
|
+
cd gitops-replacer
|
|
45
|
+
|
|
46
|
+
# Install in development mode
|
|
47
|
+
pip install -e .
|
|
48
|
+
|
|
49
|
+
# Or run directly
|
|
50
|
+
python -m gitops_replacer --help
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
1. Add marker comments to your target files (see [Marker Format](#marker-format))
|
|
56
|
+
2. Create a configuration file (default: `gitops-replacer.json`)
|
|
57
|
+
3. Run a dry-run:
|
|
58
|
+
```bash
|
|
59
|
+
gitops-replacer "1.2.3"
|
|
60
|
+
```
|
|
61
|
+
4. Apply changes (commit to target repos):
|
|
62
|
+
```bash
|
|
63
|
+
gitops-replacer --apply "1.2.3"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Marker Format
|
|
67
|
+
|
|
68
|
+
Add a comment **above** the line you want to update:
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
dependencies:
|
|
72
|
+
# gitops-replacer: my-app
|
|
73
|
+
- name: my-app
|
|
74
|
+
version: "0.0.0-e0f72bb"
|
|
75
|
+
repository: oci://registry.example.com/charts
|
|
76
|
+
|
|
77
|
+
# gitops-replacer: another-chart
|
|
78
|
+
- name: another-chart
|
|
79
|
+
version: "1.0.0"
|
|
80
|
+
repository: oci://registry.example.com/charts
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The tool will:
|
|
84
|
+
1. Find the line with `# gitops-replacer: <depName>`
|
|
85
|
+
2. Replace the value on the **next line** (preserving key, quotes, and formatting)
|
|
86
|
+
|
|
87
|
+
### Examples
|
|
88
|
+
|
|
89
|
+
**Chart.yaml (Helm dependency version):**
|
|
90
|
+
```yaml
|
|
91
|
+
dependencies:
|
|
92
|
+
# gitops-replacer: my-app
|
|
93
|
+
- name: my-app
|
|
94
|
+
version: "0.0.0-e0f72bb"
|
|
95
|
+
repository: oci://registry.example.com/charts
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**values.yaml (image tag):**
|
|
99
|
+
```yaml
|
|
100
|
+
# gitops-replacer: my-app-image
|
|
101
|
+
image: registry.example.com/myorg/my-app:1.2.3
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Note:** Only YAML files are supported (JSON has no comments). GitOps manifests are typically YAML.
|
|
105
|
+
|
|
106
|
+
## CLI
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
usage: gitops-replacer [-h] [--config <file>] [--apply] [--ci]
|
|
110
|
+
[--name <string>] [--email <string>]
|
|
111
|
+
[--message <string>] [--api <string>]
|
|
112
|
+
[--verbose]
|
|
113
|
+
<string>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- `--config` Path to the configuration file (default: `gitops-replacer.json`). JSON recommended.
|
|
117
|
+
- `--apply` Apply changes (commit). Without this flag the tool runs in dry-run.
|
|
118
|
+
- `--ci` CI mode: validates `GITHUB_REF` against `when`/`except` regex patterns from config.
|
|
119
|
+
- `--name` Commit author name (default: env `GIT_COMMIT_NAME` or `Replacer Bot`).
|
|
120
|
+
- `--email` Commit author email (default: env `GIT_COMMIT_EMAIL` or `replacer-bot@localhost.localdomain`).
|
|
121
|
+
- `--message` Commit message template (default: `fix: update {} to {}`). First `{}` is depName, second is value.
|
|
122
|
+
- `--api` GitHub API URL (default: env `GITHUB_API_URL` or `https://api.github.com`).
|
|
123
|
+
- `--verbose` Print file contents and desired state (use with care in CI logs).
|
|
124
|
+
- Positional: `value` - the new value to set at the marked location.
|
|
125
|
+
|
|
126
|
+
### Environment
|
|
127
|
+
|
|
128
|
+
- `GITHUB_TOKEN` **(required)** – token with access to read/write repository contents.
|
|
129
|
+
- `GITHUB_REF` *(required when `--ci`)* – the current ref string, e.g., `refs/heads/main`. Falls back to `GIT_REF` for backwards compatibility.
|
|
130
|
+
|
|
131
|
+
Recommended token scopes:
|
|
132
|
+
- Public repos only: `public_repo`
|
|
133
|
+
- Private repos: `repo`
|
|
134
|
+
- GitHub Enterprise: equivalent content permissions
|
|
135
|
+
|
|
136
|
+
## Configuration
|
|
137
|
+
|
|
138
|
+
Default format is **JSON**. YAML (`.yaml`/`.yml`) is supported as well.
|
|
139
|
+
|
|
140
|
+
### JSON schema (per entry)
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"gitops-replacer": [
|
|
145
|
+
{
|
|
146
|
+
"repository": "acme/gitops",
|
|
147
|
+
"branch": "main",
|
|
148
|
+
"file": "apps/my-app/Chart.yaml",
|
|
149
|
+
"depName": "my-app",
|
|
150
|
+
"when": "^refs/heads/main$"
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Fields**
|
|
157
|
+
|
|
158
|
+
| Field | Description |
|
|
159
|
+
|-------|-------------|
|
|
160
|
+
| `repository` | Target repo on GitHub (`ORG/REPO` format) |
|
|
161
|
+
| `branch` | Target branch |
|
|
162
|
+
| `file` | Target file path relative to repo root |
|
|
163
|
+
| `depName` | Dependency name (must match marker in file) |
|
|
164
|
+
| `when` | Regex that must match `GITHUB_REF` when `--ci` is enabled (optional) |
|
|
165
|
+
| `except` | Regex that must **not** match `GITHUB_REF` when `--ci` is enabled (optional) |
|
|
166
|
+
|
|
167
|
+
> The tool uses `re.match` (anchored at the string start). Use `^...$` in your patterns if you require a full match.
|
|
168
|
+
|
|
169
|
+
### Examples
|
|
170
|
+
|
|
171
|
+
**JSON (default)**
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"gitops-replacer": [
|
|
176
|
+
{
|
|
177
|
+
"repository": "acme/gitops",
|
|
178
|
+
"branch": "main",
|
|
179
|
+
"file": "apps/my-app/Chart.yaml",
|
|
180
|
+
"depName": "my-app",
|
|
181
|
+
"when": "^refs/heads/(main|release/.*)$"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"repository": "acme/gitops",
|
|
185
|
+
"branch": "develop",
|
|
186
|
+
"file": "apps/my-app-dev/Chart.yaml",
|
|
187
|
+
"depName": "my-app",
|
|
188
|
+
"except": "^refs/heads/legacy/"
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**YAML (alternative)**
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
gitops-replacer:
|
|
198
|
+
- repository: acme/gitops
|
|
199
|
+
branch: main
|
|
200
|
+
file: apps/my-app/Chart.yaml
|
|
201
|
+
depName: my-app
|
|
202
|
+
when: '^refs/heads/(main|release/.*)$'
|
|
203
|
+
- repository: acme/gitops
|
|
204
|
+
branch: develop
|
|
205
|
+
file: apps/my-app-dev/Chart.yaml
|
|
206
|
+
depName: my-app
|
|
207
|
+
except: '^refs/heads/legacy/'
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## How it works
|
|
211
|
+
|
|
212
|
+
1. **Validation**: Checks CLI arguments, environment variables, and configuration file
|
|
213
|
+
2. **Precheck Phase**: Validates access to all target repositories/files (caches responses)
|
|
214
|
+
3. **Replace Phase**: Downloads files (reuses cached data), finds marker comments, replaces values
|
|
215
|
+
4. **Commit Phase**: If `--apply` is set and changes detected, commits via GitHub Contents API
|
|
216
|
+
5. **Exit Codes**: Returns `0` on success, non-zero on failures
|
|
217
|
+
|
|
218
|
+
### Why Marker-based?
|
|
219
|
+
|
|
220
|
+
Traditional approaches parse YAML, modify the data structure, and serialize back. This often breaks:
|
|
221
|
+
- Comments are lost
|
|
222
|
+
- Quote styles change (`"1.0"` becomes `'1.0'` or `1.0`)
|
|
223
|
+
- Key ordering may change
|
|
224
|
+
- Multi-line strings get reformatted
|
|
225
|
+
|
|
226
|
+
The marker-based approach works on raw text:
|
|
227
|
+
- **Explicit**: Only marked lines are modified
|
|
228
|
+
- **Safe**: No risk of unintended changes
|
|
229
|
+
- **Preserving**: Comments, quotes, and formatting stay intact
|
|
230
|
+
|
|
231
|
+
## Exit Codes
|
|
232
|
+
|
|
233
|
+
- `0` success (no changes or committed changes)
|
|
234
|
+
- `1` validation or API error
|
|
235
|
+
|
|
236
|
+
## Use Cases
|
|
237
|
+
|
|
238
|
+
### Automated Deployment Pipeline
|
|
239
|
+
|
|
240
|
+
Update chart version when a new release is built:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# In your CI/CD pipeline after publishing a chart
|
|
244
|
+
gitops-replacer --ci --apply "0.1.0-abc123"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Multi-Environment Updates
|
|
248
|
+
|
|
249
|
+
Use CI mode to update different environments based on branch:
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"gitops-replacer": [
|
|
254
|
+
{
|
|
255
|
+
"repository": "myorg/gitops",
|
|
256
|
+
"branch": "main",
|
|
257
|
+
"file": "apps/production/Chart.yaml",
|
|
258
|
+
"depName": "myapp",
|
|
259
|
+
"when": "^refs/heads/main$"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"repository": "myorg/gitops",
|
|
263
|
+
"branch": "main",
|
|
264
|
+
"file": "apps/staging/Chart.yaml",
|
|
265
|
+
"depName": "myapp",
|
|
266
|
+
"when": "^refs/heads/(main|develop)$"
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Troubleshooting
|
|
273
|
+
|
|
274
|
+
### Common Issues
|
|
275
|
+
|
|
276
|
+
**401 Unauthorized**
|
|
277
|
+
- Verify `GITHUB_TOKEN` is set correctly
|
|
278
|
+
- Check token has `repo` or `public_repo` scope
|
|
279
|
+
- For GitHub Enterprise, confirm token has access to the organization
|
|
280
|
+
|
|
281
|
+
**404 Not Found**
|
|
282
|
+
- Verify `repository`, `branch`, and `file` paths in config
|
|
283
|
+
- Check branch name spelling (case-sensitive)
|
|
284
|
+
- Ensure file exists at the specified path
|
|
285
|
+
|
|
286
|
+
**No marker found**
|
|
287
|
+
- Confirm the marker comment exists in the target file
|
|
288
|
+
- Check `depName` in config matches the marker exactly
|
|
289
|
+
- Marker format: `# gitops-replacer: <depName>`
|
|
290
|
+
|
|
291
|
+
**No changes detected**
|
|
292
|
+
- The current value already matches the new value
|
|
293
|
+
- Use `--verbose` to see file contents
|
|
294
|
+
|
|
295
|
+
### Debug Mode
|
|
296
|
+
|
|
297
|
+
Run with `--verbose` to see:
|
|
298
|
+
- Full API URLs being called
|
|
299
|
+
- Complete file contents before replacement
|
|
300
|
+
- Desired file contents after replacement
|
|
301
|
+
|
|
302
|
+
**Warning**: Verbose mode may expose sensitive data in logs.
|
|
303
|
+
|
|
304
|
+
## Contributing
|
|
305
|
+
|
|
306
|
+
Contributions are welcome! Please ensure:
|
|
307
|
+
- Code follows existing style and patterns
|
|
308
|
+
- Changes are tested with both dry-run and apply modes
|
|
309
|
+
- Documentation is updated for new features
|
|
310
|
+
|
|
311
|
+
## License
|
|
312
|
+
|
|
313
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|