git-private2public 0.1.8__py3-none-any.whl
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.
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: git-private2public
|
|
3
|
+
Version: 0.1.8
|
|
4
|
+
Summary: Like .gitignore, but for what goes public. Keep a sanitized public mirror of your private repo.
|
|
5
|
+
Project-URL: Homepage, https://github.com/megamen32/git-private2public
|
|
6
|
+
Project-URL: Repository, https://github.com/megamen32/git-private2public
|
|
7
|
+
Project-URL: Issues, https://github.com/megamen32/git-private2public/issues
|
|
8
|
+
Author: megamen32
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: git,mirror,open-source,privacy,public,sanitization,secrets,security
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Requires-Dist: git-filter-repo>=2.38
|
|
25
|
+
Requires-Dist: pyyaml>=6.0
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# git-private2public
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/git-private2public/) [](https://github.com/megamen32/git-private2public/actions/workflows/ci.yml)
|
|
31
|
+
|
|
32
|
+
**[English](./README.md)** · **[Русский](./README.ru.md)**
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
**Like `.gitignore`, but for what goes public.**
|
|
37
|
+
|
|
38
|
+
Need the full rule-by-rule explanation? Read [Advanced configuration](./docs/ADVANCED.md) / [RU](./docs/ADVANCED.ru.md).
|
|
39
|
+
|
|
40
|
+
You have a private repo. You want a public one — without the secrets. This
|
|
41
|
+
tool keeps them in sync. Automatically.
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install git-private2public
|
|
47
|
+
git-private2public init # creates .gitpublic/ folder
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Edit `.gitpublic/config` — set source + target. Values can be `owner/repo`, a full Git URL, or a local path:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
source = you/private-repo
|
|
54
|
+
target = you/public-repo
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Edit `.gitpublic/ignore` — files to hide, one per line (like `.gitignore`):
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
.env
|
|
61
|
+
secrets/
|
|
62
|
+
*.key
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Publish:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git-private2public publish
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Done. Your public repo is clean.
|
|
72
|
+
|
|
73
|
+
## Auto-publish on every `git push`
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git-private2public hook enable # on
|
|
77
|
+
git push # also publishes public mirror
|
|
78
|
+
git-private2public hook disable # off
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Native git hook. No CI, no GitHub Actions. Works offline.
|
|
82
|
+
|
|
83
|
+
## Guard — pre-push safety net
|
|
84
|
+
|
|
85
|
+
`guard` installs a lightweight pre-push hook that **blocks** `git push` if
|
|
86
|
+
scanned content matches known secret patterns. Unlike `hook` (which also
|
|
87
|
+
rewrites history and force-pushes a public mirror), `guard` is purely a
|
|
88
|
+
refusal mechanism — no clone, no filter-repo, no push.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git-private2public guard enable # install pre-push hook
|
|
92
|
+
git-private2public guard status # is it on?
|
|
93
|
+
git-private2public guard disable # remove the hook
|
|
94
|
+
git-private2public guard run # manual scan (also what the hook does)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**What it scans — by default:**
|
|
98
|
+
1. Every tracked file in the working tree (`git ls-files`).
|
|
99
|
+
2. **Every blob in git history** — catches secrets committed in old commits
|
|
100
|
+
and later removed from HEAD but never rewritten via filter-repo.
|
|
101
|
+
Use `--no-history` to skip this (faster, but won't catch old leaks).
|
|
102
|
+
|
|
103
|
+
**Default secret patterns (always on, no `.gitpublic/` required):**
|
|
104
|
+
- OpenAI and Anthropic: `sk-...`, `sk-proj-...`, `sk-ant-...`
|
|
105
|
+
- GitHub: `ghp_...`, `github_pat_...`, `gho_...`, `ghs_...`, `ghr_...`
|
|
106
|
+
- HuggingFace and Slack: `hf_...`, `xox[baprs]-...`
|
|
107
|
+
- AWS long-term and temporary access key IDs: `AKIA...`, `ASIA...`
|
|
108
|
+
- Google API/OAuth keys and GitLab PATs
|
|
109
|
+
- Telegram bot, npm, PyPI, Stripe, SendGrid and Discord tokens
|
|
110
|
+
- Twilio, Mailgun and DigitalOcean API credentials
|
|
111
|
+
- Generic JWTs and PEM private-key headers
|
|
112
|
+
|
|
113
|
+
**Custom patterns:** drop them into `.gitpublic/scan`, one per line (literal
|
|
114
|
+
or `regex:...`). They layer on top of the defaults.
|
|
115
|
+
|
|
116
|
+
**Allowlist:** anything in `.gitpublic/allow` is treated as an exception for
|
|
117
|
+
broad regex rules that happen to match a public domain.
|
|
118
|
+
|
|
119
|
+
**Bypass for one push (NOT recommended):**
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
GIT_PRIVATE2PUBLIC_SKIP_GUARD=1 git push
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**What guard tells you when it refuses the push:**
|
|
126
|
+
|
|
127
|
+
The error message is tailored to where the secret lives:
|
|
128
|
+
|
|
129
|
+
- **In the working tree** — points at the file, suggests editing,
|
|
130
|
+
`.gitignore` + `git rm --cached`, and reminds you to rotate a live
|
|
131
|
+
secret before committing.
|
|
132
|
+
- **In git history** — explains that editing HEAD isn't enough, and
|
|
133
|
+
prints the exact next step:
|
|
134
|
+
- `git-private2public publish` (if you have a `.gitpublic/` set up), or
|
|
135
|
+
- `git filter-repo --replace-text replacements.txt --force` (manual),
|
|
136
|
+
with the `replacements.txt` format.
|
|
137
|
+
|
|
138
|
+
When both happen, you get both sections.
|
|
139
|
+
|
|
140
|
+
If you only want guard and not `hook` (publish) — `guard enable` works
|
|
141
|
+
independently. They share the same pre-push file, so `guard enable` refuses
|
|
142
|
+
to install if `hook` is already there; disable one before enabling the
|
|
143
|
+
other.
|
|
144
|
+
|
|
145
|
+
### Why scan history?
|
|
146
|
+
|
|
147
|
+
A secret leaked into a commit last month, removed from HEAD yesterday, but
|
|
148
|
+
the commit object still exists in `.git/objects/`. Pushing HEAD won't
|
|
149
|
+
expose it directly, but anyone who already has the repo and runs
|
|
150
|
+
`git log -p` will see it. Guard's history scan catches this. To actually
|
|
151
|
+
*remove* it from history, run `git-private2public publish` (which uses
|
|
152
|
+
filter-repo), or `git filter-repo --replace-text` manually.
|
|
153
|
+
|
|
154
|
+
## The `.gitpublic/` folder
|
|
155
|
+
|
|
156
|
+
Each file is one concern. Like `.gitignore` — one rule per line, `#` for
|
|
157
|
+
comments. If a file is missing, that setting is just empty.
|
|
158
|
+
|
|
159
|
+
| File | What goes in it | Format |
|
|
160
|
+
|------|-----------------|--------|
|
|
161
|
+
| `config` | source, target, push settings | `key = value` |
|
|
162
|
+
| `ignore` | files to NOT publish | one path/glob per line |
|
|
163
|
+
| `replace` | find → replace in file contents | `old ==> new` per line |
|
|
164
|
+
| `scan` | refuse to push if matched | one pattern per line |
|
|
165
|
+
| `allow` | exceptions for domain rules in `scan` | one allowed matched domain per line |
|
|
166
|
+
|
|
167
|
+
**Easy** — just edit `ignore`:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
.env
|
|
171
|
+
secrets/
|
|
172
|
+
*.key
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Medium** — also edit `replace`:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
<PRIVATE_IP> ==> 203.0.113.5
|
|
179
|
+
real-token ==> ***
|
|
180
|
+
regex:[A-Fa-f0-9]{64} ==> ***
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Hard** — also edit `scan` + `allow`:
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
# scan:
|
|
187
|
+
regex:github_pat_[A-Za-z0-9_]{30,}
|
|
188
|
+
regex:192\.168\.
|
|
189
|
+
regex:[a-z0-9.-]+\.[a-z]{2,}
|
|
190
|
+
|
|
191
|
+
# allow:
|
|
192
|
+
github.com
|
|
193
|
+
get.docker.com
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Commands
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
init create .gitpublic/ config
|
|
200
|
+
scan clean into a temp repo, scan, don't push
|
|
201
|
+
publish clean + push
|
|
202
|
+
hook enable / disable / status
|
|
203
|
+
guard enable / disable / status / run (pre-push secret scanner)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## How `allow` / domains work
|
|
207
|
+
|
|
208
|
+
Nothing is auto-blocked just because it is a domain.
|
|
209
|
+
|
|
210
|
+
`allow` is an exception list for `scan`. If `.gitpublic/scan` is missing or empty, `allow` does nothing and domains are not checked at all.
|
|
211
|
+
|
|
212
|
+
To block domain-looking strings, add a broad domain rule to `.gitpublic/scan`:
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
regex:[a-z0-9.-]+\.[a-z]{2,}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Now every matched domain fails the scan unless the matched domain itself is listed in `.gitpublic/allow`:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
github.com
|
|
222
|
+
get.docker.com
|
|
223
|
+
example.com
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`allow` does not replace private domains. Use `.gitpublic/replace` for that:
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
private.company.local ==> example.com
|
|
230
|
+
regex:.*\.corp\.internal ==> example.com
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Analogy: `scan` says “ban everything matching this pattern”, `allow` says “except these exact public domains”.
|
|
234
|
+
|
|
235
|
+
Rule of thumb:
|
|
236
|
+
|
|
237
|
+
| You want to... | File |
|
|
238
|
+
|---|---|
|
|
239
|
+
| remove files | `.gitpublic/ignore` |
|
|
240
|
+
| rewrite private text/domain/IP | `.gitpublic/replace` |
|
|
241
|
+
| fail if a secret/domain/IP survived | `.gitpublic/scan` |
|
|
242
|
+
| make exceptions for public domains caught by scan | `.gitpublic/allow` |
|
|
243
|
+
|
|
244
|
+
More examples: [Advanced configuration](./docs/ADVANCED.md) / [RU](./docs/ADVANCED.ru.md).
|
|
245
|
+
|
|
246
|
+
## Install
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
pip install git-private2public
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
That's it. Now you have the `git-private2public` command.
|
|
253
|
+
|
|
254
|
+
> No pip? [Single-file manual install](./git_private2public.py) — download +
|
|
255
|
+
> `chmod +x` (needs `pip install git-filter-repo pyyaml`).
|
|
256
|
+
|
|
257
|
+
## Why
|
|
258
|
+
|
|
259
|
+
Git has no "private file in a public repo". So you need two repos. This keeps
|
|
260
|
+
them in sync — without leaking.
|
|
261
|
+
|
|
262
|
+
| | delete files | replace text | scan | auto push | pre-push guard |
|
|
263
|
+
|---|:---:|:---:|:---:|:---:|:---:|
|
|
264
|
+
| git-filter-repo | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
265
|
+
| BFG | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
266
|
+
| dupligit | ❌ | ❌ | ❌ | ✅ | ❌ |
|
|
267
|
+
| gitleaks / trufflehog | ❌ | ❌ | ✅ | ❌ | ✅ (separate tool) |
|
|
268
|
+
| **git-private2public** | ✅ | ✅ | ✅ | ✅ | ✅ (built-in) |
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
MIT
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
git_private2public.py,sha256=Yp4tURZ06eJD9HqxxLz8XL2ooK8fjM4WmGA_WZM-Mbs,48685
|
|
2
|
+
git_private2public-0.1.8.dist-info/METADATA,sha256=4cioZV0wnx48io5XZBITsNW3Ns8Cjv4sW5KEy4xLv4M,8729
|
|
3
|
+
git_private2public-0.1.8.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
4
|
+
git_private2public-0.1.8.dist-info/entry_points.txt,sha256=l8j8vyxPhr_9VBhtCzu-hGouiCY1b5hC2F3FONKs5CA,63
|
|
5
|
+
git_private2public-0.1.8.dist-info/licenses/LICENSE,sha256=VG960k7djgyoFu3-gl-hSBtie7NDXGj345ojWU24fc8,1076
|
|
6
|
+
git_private2public-0.1.8.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Demiurge The Single
|
|
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.
|