pygitpub 0.0.58__tar.gz → 0.0.60__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.
- pygitpub-0.0.60/.gitignore +280 -0
- {pygitpub-0.0.58 → pygitpub-0.0.60}/LICENSE +1 -1
- {pygitpub-0.0.58 → pygitpub-0.0.60}/PKG-INFO +11 -9
- {pygitpub-0.0.58 → pygitpub-0.0.60}/README.md +6 -5
- {pygitpub-0.0.58 → pygitpub-0.0.60}/pyproject.toml +27 -4
- {pygitpub-0.0.58 → pygitpub-0.0.60}/src/pygitpub/configs.py +17 -0
- {pygitpub-0.0.58 → pygitpub-0.0.60}/src/pygitpub/main.py +81 -26
- {pygitpub-0.0.58 → pygitpub-0.0.60}/src/pygitpub/static.py +1 -1
- pygitpub-0.0.60/src/pygitpub/utils/lua.py +68 -0
- {pygitpub-0.0.58 → pygitpub-0.0.60}/src/pygitpub/utils/misc.py +4 -4
- pygitpub-0.0.58/.gitignore +0 -163
- pygitpub-0.0.58/src/pygitpub/utils/importlib.py +0 -16
- {pygitpub-0.0.58 → pygitpub-0.0.60}/src/pygitpub/__init__.py +0 -0
- {pygitpub-0.0.58 → pygitpub-0.0.60}/src/pygitpub/utils/__init__.py +0 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# generated
|
|
2
|
+
/toolchain/
|
|
3
|
+
/dist/
|
|
4
|
+
/out/
|
|
5
|
+
/debug/
|
|
6
|
+
/download/
|
|
7
|
+
*.out
|
|
8
|
+
/.mypy_cache/
|
|
9
|
+
/.ruff_cache/
|
|
10
|
+
/.rumdl_cache/
|
|
11
|
+
|
|
12
|
+
# folders to git ignore
|
|
13
|
+
/*.gi
|
|
14
|
+
/gi/
|
|
15
|
+
|
|
16
|
+
# gems
|
|
17
|
+
/gems/
|
|
18
|
+
|
|
19
|
+
# hive
|
|
20
|
+
derby.log
|
|
21
|
+
dfs/
|
|
22
|
+
metastore_db/
|
|
23
|
+
|
|
24
|
+
# c, c++ stuff
|
|
25
|
+
*.o
|
|
26
|
+
*.so
|
|
27
|
+
*.a
|
|
28
|
+
*.dis
|
|
29
|
+
/Makefile.bak
|
|
30
|
+
*.o.ur-safe
|
|
31
|
+
*.oo
|
|
32
|
+
*.elf
|
|
33
|
+
*.dep
|
|
34
|
+
/core.*
|
|
35
|
+
lib*.so.*
|
|
36
|
+
*.gch
|
|
37
|
+
*.pchi
|
|
38
|
+
*.gcda
|
|
39
|
+
*.map
|
|
40
|
+
*.bin
|
|
41
|
+
|
|
42
|
+
# kernel stuff
|
|
43
|
+
*.ko
|
|
44
|
+
# *.mod
|
|
45
|
+
*.mod.c
|
|
46
|
+
*.mod.cmd
|
|
47
|
+
.*.o.cmd
|
|
48
|
+
.*.ko.cmd
|
|
49
|
+
.*.o.tmp
|
|
50
|
+
.*.o.d
|
|
51
|
+
.tmp_versions
|
|
52
|
+
Module.symvers
|
|
53
|
+
modules.order
|
|
54
|
+
*.objdump
|
|
55
|
+
/src/kernel/.*.tmp
|
|
56
|
+
.modules.*.cmd
|
|
57
|
+
.Module.*.cmd
|
|
58
|
+
/flags.cfg
|
|
59
|
+
# device tree binary files and binary overlay files
|
|
60
|
+
*.dtb
|
|
61
|
+
*.dtbo
|
|
62
|
+
|
|
63
|
+
# sphinx
|
|
64
|
+
/docs/_build/
|
|
65
|
+
/docs/.doctrees/
|
|
66
|
+
|
|
67
|
+
# mdBook build output
|
|
68
|
+
/docs/book/
|
|
69
|
+
|
|
70
|
+
# installations
|
|
71
|
+
/external/
|
|
72
|
+
/install/
|
|
73
|
+
*.tar.gz
|
|
74
|
+
|
|
75
|
+
# python
|
|
76
|
+
__pycache__/
|
|
77
|
+
*.py[oc]
|
|
78
|
+
/wheel/
|
|
79
|
+
/.venv/
|
|
80
|
+
.coverage
|
|
81
|
+
.coverage.*
|
|
82
|
+
coverage.xml
|
|
83
|
+
*.egg-info/
|
|
84
|
+
|
|
85
|
+
# mine
|
|
86
|
+
# error files left by the bashy prompt plugins (.uv.sync.errors,
|
|
87
|
+
# .pydmt.build.errors, ...); unanchored because a sub-project's
|
|
88
|
+
# pyproject root can sit below the git root
|
|
89
|
+
.*.errors
|
|
90
|
+
/.pydmt/
|
|
91
|
+
*.stamp
|
|
92
|
+
*.stamp.*
|
|
93
|
+
|
|
94
|
+
# java
|
|
95
|
+
*.class
|
|
96
|
+
*.jar
|
|
97
|
+
*.war
|
|
98
|
+
*.ear
|
|
99
|
+
*.jsa
|
|
100
|
+
/.mtj.tmp/
|
|
101
|
+
/hs_err_pid*
|
|
102
|
+
|
|
103
|
+
# node modules
|
|
104
|
+
node_modules/
|
|
105
|
+
|
|
106
|
+
# ppt, pdf
|
|
107
|
+
/doc/odp/*.pdf
|
|
108
|
+
/doc/odp/*.ppt
|
|
109
|
+
|
|
110
|
+
# intel compiler pgo
|
|
111
|
+
*.dyn
|
|
112
|
+
*.dpi
|
|
113
|
+
# *.lock
|
|
114
|
+
|
|
115
|
+
# dwarf dynamic debug info
|
|
116
|
+
*.dwo
|
|
117
|
+
|
|
118
|
+
# locks by open office
|
|
119
|
+
.~lock.*
|
|
120
|
+
|
|
121
|
+
# haskell
|
|
122
|
+
*.hi
|
|
123
|
+
|
|
124
|
+
# clojure
|
|
125
|
+
.cpcache/
|
|
126
|
+
|
|
127
|
+
# repositries of deb packages
|
|
128
|
+
/deb/mypackage/DEBIAN/control
|
|
129
|
+
|
|
130
|
+
# generic
|
|
131
|
+
*.log
|
|
132
|
+
*.swp
|
|
133
|
+
|
|
134
|
+
# scons
|
|
135
|
+
.sconsign.dblite
|
|
136
|
+
|
|
137
|
+
# DS_Store
|
|
138
|
+
.DS_Store
|
|
139
|
+
|
|
140
|
+
# Java stuff
|
|
141
|
+
.gradle/
|
|
142
|
+
target/
|
|
143
|
+
build/
|
|
144
|
+
|
|
145
|
+
# Jupyter notebooks
|
|
146
|
+
.ipynb_checkpoints/
|
|
147
|
+
|
|
148
|
+
# c#
|
|
149
|
+
# **/obj/ and **/bin/ match at any depth, which is what .NET output needs
|
|
150
|
+
# (src/<proj>/bin, src/<proj>/obj). The un-globbed [Oo]bj/ and [Bb]in/ that
|
|
151
|
+
# used to follow were redundant with these two and are gone.
|
|
152
|
+
**/obj/
|
|
153
|
+
**/bin/
|
|
154
|
+
# perl-meta keeps real Perl sources under perl/bin/Meta/, not build output.
|
|
155
|
+
!/perl/bin/
|
|
156
|
+
|
|
157
|
+
# flash/air
|
|
158
|
+
*.swf
|
|
159
|
+
*.air
|
|
160
|
+
*.apk
|
|
161
|
+
*.ipa
|
|
162
|
+
bin-debug/
|
|
163
|
+
bin-release/
|
|
164
|
+
|
|
165
|
+
# vscode
|
|
166
|
+
.vscode/
|
|
167
|
+
|
|
168
|
+
# qemu
|
|
169
|
+
*.qcow2
|
|
170
|
+
*.iso
|
|
171
|
+
|
|
172
|
+
# jetbrains and eclipse
|
|
173
|
+
.cache
|
|
174
|
+
.settings
|
|
175
|
+
.settings/
|
|
176
|
+
.classpath
|
|
177
|
+
.project
|
|
178
|
+
.project.xml
|
|
179
|
+
.idea/
|
|
180
|
+
build.properties
|
|
181
|
+
|
|
182
|
+
# spark
|
|
183
|
+
spark-warehouse/
|
|
184
|
+
|
|
185
|
+
# terraform
|
|
186
|
+
.terraform*
|
|
187
|
+
/.terraform/
|
|
188
|
+
terraform.tfstate
|
|
189
|
+
terraform.tfstate.backup
|
|
190
|
+
/terraform.lock.hcl
|
|
191
|
+
|
|
192
|
+
# scala
|
|
193
|
+
# Anchored: sbt's build dir is at the repo root. Unanchored, this also
|
|
194
|
+
# swallowed source directories named project/ nested under src/.
|
|
195
|
+
/project/
|
|
196
|
+
|
|
197
|
+
# rust
|
|
198
|
+
**/*.rs.bk
|
|
199
|
+
**/mutants.out*/
|
|
200
|
+
*.pdb
|
|
201
|
+
# Anchored: this is the root debug build dir. Unanchored (and without a
|
|
202
|
+
# trailing slash) it matched any file or directory named debug at any depth,
|
|
203
|
+
# including authored example content.
|
|
204
|
+
/debug/
|
|
205
|
+
/.rsconstruct_cache.json
|
|
206
|
+
|
|
207
|
+
# claude
|
|
208
|
+
/.claude/
|
|
209
|
+
/claude/
|
|
210
|
+
.claude/projects/
|
|
211
|
+
.claude/settings.local.json
|
|
212
|
+
|
|
213
|
+
# static sites
|
|
214
|
+
/_site/
|
|
215
|
+
/public/
|
|
216
|
+
/media/
|
|
217
|
+
/static/build_info.toml
|
|
218
|
+
/static/shared-themes/
|
|
219
|
+
/build_info.json
|
|
220
|
+
|
|
221
|
+
# manim
|
|
222
|
+
/.manim_scratch/
|
|
223
|
+
|
|
224
|
+
# terms
|
|
225
|
+
/terms.ambiguous/
|
|
226
|
+
/terms.single_meaning/
|
|
227
|
+
|
|
228
|
+
# stm32 demos
|
|
229
|
+
/STM32CubeWL/
|
|
230
|
+
/exercises/*/*/.obj/
|
|
231
|
+
/exercises/*/*/app*.elf
|
|
232
|
+
/exercises/*/*/app*.map
|
|
233
|
+
/exercises/*/*/firmware*.bin
|
|
234
|
+
/exercises/common/.hal_obj_cm0/
|
|
235
|
+
/exercises/common/.hal_obj_cm4/
|
|
236
|
+
/exercises/common/.rsc_hal_obj_cm0/
|
|
237
|
+
/exercises/common/.rsc_hal_obj_cm4/
|
|
238
|
+
/exercises/common/libhal_cm0.a
|
|
239
|
+
/exercises/common/libhal_cm4.a
|
|
240
|
+
/exercises/common/libhal_rsc_cm0.a
|
|
241
|
+
/exercises/common/libhal_rsc_cm4.a
|
|
242
|
+
|
|
243
|
+
# secrets
|
|
244
|
+
creds.txt
|
|
245
|
+
|
|
246
|
+
# rsconstruct
|
|
247
|
+
/.rsconstruct/
|
|
248
|
+
|
|
249
|
+
# generated website pieces (built into the Pages artifact by CI)
|
|
250
|
+
/docs/output/
|
|
251
|
+
/docs/index.html
|
|
252
|
+
# sphinx html output: the 14 py* repos render docs/ from sphinx/ and publish
|
|
253
|
+
# it as the Pages artifact, so none of it is committed. Listed path by path
|
|
254
|
+
# rather than as /docs/ because the rs* repos keep hand-written mdBook
|
|
255
|
+
# sources there, and several others keep authored html.
|
|
256
|
+
/docs/_modules/
|
|
257
|
+
/docs/_sources/
|
|
258
|
+
/docs/_static/
|
|
259
|
+
/docs/.buildinfo
|
|
260
|
+
/docs/objects.inv
|
|
261
|
+
/docs/searchindex.js
|
|
262
|
+
/docs/.nojekyll
|
|
263
|
+
# sphinx's top-level html. Not a blanket /docs/*.html: js-jschess and the
|
|
264
|
+
# site-personal-* repos keep hand-authored html under docs/. The fixed pages
|
|
265
|
+
# (genindex, modules, search) and the authored-rst pages some repos render
|
|
266
|
+
# (architecture, notes, ...) are listed by name; the one-page-per-module
|
|
267
|
+
# output is /docs/py*.html, since every package here is named py*.
|
|
268
|
+
/docs/architecture.html
|
|
269
|
+
/docs/cli-reference.html
|
|
270
|
+
/docs/development.html
|
|
271
|
+
/docs/genindex.html
|
|
272
|
+
/docs/modules.html
|
|
273
|
+
/docs/notes.html
|
|
274
|
+
/docs/overview.html
|
|
275
|
+
/docs/py*.html
|
|
276
|
+
/docs/quota-guide.html
|
|
277
|
+
/docs/search.html
|
|
278
|
+
|
|
279
|
+
# per-machine tool resolution cache (absolute paths, not portable)
|
|
280
|
+
/.tools.versions
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: pygitpub
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.60
|
|
4
4
|
Summary: Pygitpub will help you work with github
|
|
5
5
|
Project-URL: Homepage, https://github.com/veltzer/pygitpub
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/veltzer/pygitpub/issues
|
|
@@ -19,9 +19,10 @@ Classifier: Operating System :: OS Independent
|
|
|
19
19
|
Classifier: Programming Language :: Python
|
|
20
20
|
Classifier: Programming Language :: Python :: 3
|
|
21
21
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
23
|
Classifier: Topic :: Utilities
|
|
24
|
-
Requires-Python: >=3.
|
|
24
|
+
Requires-Python: >=3.14
|
|
25
|
+
Requires-Dist: lupa
|
|
25
26
|
Requires-Dist: pyapikey
|
|
26
27
|
Requires-Dist: pygithub
|
|
27
28
|
Requires-Dist: pylogconf
|
|
@@ -29,6 +30,9 @@ Requires-Dist: pytconf
|
|
|
29
30
|
Requires-Dist: pyyaml
|
|
30
31
|
Description-Content-Type: text/markdown
|
|
31
32
|
|
|
33
|
+
<!-- This file is generated. Do not edit it by hand: your changes will be
|
|
34
|
+
lost on the next build. Edit the template it is rendered from, or the
|
|
35
|
+
values that template reads, and build again. -->
|
|
32
36
|
# *pygitpub* project by Mark Veltzer
|
|
33
37
|
|
|
34
38
|
description: Pygitpub will help you work with github
|
|
@@ -37,7 +41,7 @@ project website: https://veltzer.github.io/pygitpub
|
|
|
37
41
|
|
|
38
42
|
author: Mark Veltzer
|
|
39
43
|
|
|
40
|
-
version: 0.0.
|
|
44
|
+
version: 0.0.60
|
|
41
45
|
|
|
42
46
|

|
|
43
47
|
|
|
@@ -63,12 +67,10 @@ version: 0.0.58
|
|
|
63
67
|

|
|
64
68
|

|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
70
|
## contact me
|
|
71
|
+
|
|
69
72
|
[mailto](mailto:mark.veltzer@gmail.com)
|
|
70
|
-

|
|
71
73
|

|
|
72
74
|

|
|
73
75
|
|
|
74
|
-
Mark Veltzer, Copyright © 2022, 2023, 2024, 2025
|
|
76
|
+
Mark Veltzer, Copyright © 2022, 2023, 2024, 2025, 2026
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
<!-- This file is generated. Do not edit it by hand: your changes will be
|
|
2
|
+
lost on the next build. Edit the template it is rendered from, or the
|
|
3
|
+
values that template reads, and build again. -->
|
|
1
4
|
# *pygitpub* project by Mark Veltzer
|
|
2
5
|
|
|
3
6
|
description: Pygitpub will help you work with github
|
|
@@ -6,7 +9,7 @@ project website: https://veltzer.github.io/pygitpub
|
|
|
6
9
|
|
|
7
10
|
author: Mark Veltzer
|
|
8
11
|
|
|
9
|
-
version: 0.0.
|
|
12
|
+
version: 0.0.60
|
|
10
13
|
|
|
11
14
|

|
|
12
15
|
|
|
@@ -32,12 +35,10 @@ version: 0.0.58
|
|
|
32
35
|

|
|
33
36
|

|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
38
|
## contact me
|
|
39
|
+
|
|
38
40
|
[mailto](mailto:mark.veltzer@gmail.com)
|
|
39
|
-

|
|
40
41
|

|
|
41
42
|

|
|
42
43
|
|
|
43
|
-
Mark Veltzer, Copyright © 2022, 2023, 2024, 2025
|
|
44
|
+
Mark Veltzer, Copyright © 2022, 2023, 2024, 2025, 2026
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pygitpub"
|
|
7
|
-
version = "0.0.
|
|
8
|
-
requires-python = ">=3.
|
|
7
|
+
version = "0.0.60"
|
|
8
|
+
requires-python = ">=3.14"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Mark Veltzer", email = "mark.veltzer@gmail.com" }
|
|
11
11
|
]
|
|
@@ -26,7 +26,7 @@ classifiers = [
|
|
|
26
26
|
"Programming Language :: Python",
|
|
27
27
|
"Programming Language :: Python :: 3",
|
|
28
28
|
"Programming Language :: Python :: 3 :: Only",
|
|
29
|
-
"Programming Language :: Python :: 3.
|
|
29
|
+
"Programming Language :: Python :: 3.14",
|
|
30
30
|
"Topic :: Utilities",
|
|
31
31
|
"License :: OSI Approved :: MIT License",
|
|
32
32
|
]
|
|
@@ -36,6 +36,16 @@ dependencies = [
|
|
|
36
36
|
"PyGithub",
|
|
37
37
|
"pyapikey",
|
|
38
38
|
"pyyaml",
|
|
39
|
+
"lupa",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[dependency-groups]
|
|
43
|
+
dev = [
|
|
44
|
+
"mypy",
|
|
45
|
+
"pylint",
|
|
46
|
+
"pytest",
|
|
47
|
+
"ruff",
|
|
48
|
+
"sphinx",
|
|
39
49
|
]
|
|
40
50
|
|
|
41
51
|
[project.urls]
|
|
@@ -74,10 +84,23 @@ exclude = [
|
|
|
74
84
|
]
|
|
75
85
|
|
|
76
86
|
[tool.mypy]
|
|
77
|
-
python_version = "3.
|
|
87
|
+
python_version = "3.14"
|
|
78
88
|
warn_return_any = true
|
|
79
89
|
warn_unused_configs = true
|
|
90
|
+
mypy_path = "src:python:scripts"
|
|
91
|
+
explicit_package_bases = true
|
|
92
|
+
namespace_packages = true
|
|
80
93
|
|
|
94
|
+
# Third-party libraries without type stubs; `foo.*` also matches `foo` itself.
|
|
95
|
+
[[tool.mypy.overrides]]
|
|
96
|
+
module = [
|
|
97
|
+
"github.*",
|
|
98
|
+
"lupa.*",
|
|
99
|
+
"pyapikey.*",
|
|
100
|
+
"pylogconf.*",
|
|
101
|
+
"pytconf.*",
|
|
102
|
+
]
|
|
103
|
+
ignore_missing_imports = true
|
|
81
104
|
[tool.hatch.envs.default]
|
|
82
105
|
installer = "uv"
|
|
83
106
|
|
|
@@ -3,6 +3,14 @@ All configurations for pygitpub
|
|
|
3
3
|
"""
|
|
4
4
|
from pytconf import Config, ParamCreator
|
|
5
5
|
|
|
6
|
+
# The values github accepts in the "affiliation" parameter of its
|
|
7
|
+
# "list repositories" api. They are sent over the wire verbatim.
|
|
8
|
+
AFFILIATIONS = [
|
|
9
|
+
"owner",
|
|
10
|
+
"collaborator",
|
|
11
|
+
"organization_member",
|
|
12
|
+
]
|
|
13
|
+
|
|
6
14
|
|
|
7
15
|
class ConfigGithub(Config):
|
|
8
16
|
"""
|
|
@@ -40,10 +48,19 @@ class ConfigAlgo(Config):
|
|
|
40
48
|
help_string="Only include repos owned by this owner (None for dont mind)",
|
|
41
49
|
default=None,
|
|
42
50
|
)
|
|
51
|
+
affiliation = ParamCreator.create_str(
|
|
52
|
+
help_string="Comma separated list of how you are affiliated with the repos to work on"
|
|
53
|
+
f" (any of {','.join(AFFILIATIONS)})",
|
|
54
|
+
default="owner",
|
|
55
|
+
)
|
|
43
56
|
show_extra = ParamCreator.create_bool(
|
|
44
57
|
help_string="Show extra git repos lying around?",
|
|
45
58
|
default=True,
|
|
46
59
|
)
|
|
60
|
+
base_dir = ParamCreator.create_str(
|
|
61
|
+
help_string="Base directory under which repos are cloned",
|
|
62
|
+
default="~/git",
|
|
63
|
+
)
|
|
47
64
|
|
|
48
65
|
|
|
49
66
|
class ConfigOutput(Config):
|
|
@@ -2,26 +2,52 @@
|
|
|
2
2
|
main entry point to the program
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
import os
|
|
6
7
|
import subprocess
|
|
7
8
|
import sys
|
|
8
|
-
import json
|
|
9
|
-
|
|
10
|
-
import pylogconf.core
|
|
11
|
-
from pytconf import register_main, config_arg_parse_and_launch, register_endpoint
|
|
12
9
|
|
|
13
10
|
import github
|
|
14
11
|
import pyapikey
|
|
15
|
-
|
|
12
|
+
import pylogconf.core
|
|
13
|
+
from pytconf import config_arg_parse_and_launch, register_endpoint, register_main
|
|
14
|
+
from pytconf.pydoc import get_first_line
|
|
15
|
+
from pytconf.registry import the_registry
|
|
16
|
+
|
|
16
17
|
import pygitpub.static
|
|
18
|
+
from pygitpub.configs import AFFILIATIONS, ConfigAlgo, ConfigGithub, ConfigOutput
|
|
19
|
+
from pygitpub.utils.lua import config_path, load_lua_file
|
|
17
20
|
from pygitpub.utils.misc import delete, get_all_git_repos
|
|
18
|
-
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_base_dir() -> str:
|
|
24
|
+
base_dir: str = ConfigAlgo.base_dir
|
|
25
|
+
return os.path.expanduser(base_dir)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_repo_folder(repo) -> str:
|
|
29
|
+
return os.path.join(get_base_dir(), repo.name)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_affiliation() -> str:
|
|
33
|
+
""" validate the affiliation parameter and return it in the form github wants """
|
|
34
|
+
values = [x.strip() for x in ConfigAlgo.affiliation.split(",")]
|
|
35
|
+
values = [x for x in values if x != ""]
|
|
36
|
+
if not values:
|
|
37
|
+
raise ValueError("affiliation must not be empty")
|
|
38
|
+
for value in values:
|
|
39
|
+
if value not in AFFILIATIONS:
|
|
40
|
+
raise ValueError(f"bad affiliation [{value}], must be one of [{','.join(AFFILIATIONS)}]")
|
|
41
|
+
return ",".join(values)
|
|
19
42
|
|
|
20
43
|
|
|
21
44
|
def yield_repos():
|
|
22
45
|
apikey = pyapikey.get_key(ConfigGithub.apikey)
|
|
23
46
|
g = github.Github(login_or_token=apikey)
|
|
24
|
-
|
|
47
|
+
# github filters by affiliation server side, so repos we are not interested
|
|
48
|
+
# in (org repos, by default) are never fetched in the first place
|
|
49
|
+
affiliation = get_affiliation()
|
|
50
|
+
for repo in g.get_user().get_repos(affiliation=affiliation):
|
|
25
51
|
reason = "no reason set"
|
|
26
52
|
skip = False
|
|
27
53
|
if not ConfigAlgo.fork and repo.fork:
|
|
@@ -53,32 +79,31 @@ def yield_repos():
|
|
|
53
79
|
def fix_metadata() -> None:
|
|
54
80
|
orig_folder = os.getcwd()
|
|
55
81
|
for repo in yield_repos():
|
|
56
|
-
|
|
57
|
-
folder = os.path.join(owner, repo.name)
|
|
82
|
+
folder = get_repo_folder(repo)
|
|
58
83
|
if not os.path.isdir(folder):
|
|
59
84
|
# print(f"folder [{folder}] does not exist, continuing...")
|
|
60
85
|
continue
|
|
61
86
|
os.chdir(folder)
|
|
62
87
|
# print(f"doing [{folder}]...")
|
|
63
|
-
file_path = "
|
|
88
|
+
file_path = config_path("project")
|
|
64
89
|
if not os.path.isfile(file_path):
|
|
65
90
|
os.chdir(orig_folder)
|
|
66
91
|
continue
|
|
67
|
-
|
|
68
|
-
if not
|
|
92
|
+
project = load_lua_file(file_path)
|
|
93
|
+
if "DESCRIPTION_SHORT" not in project:
|
|
69
94
|
os.chdir(orig_folder)
|
|
70
95
|
continue
|
|
71
|
-
description_short =
|
|
96
|
+
description_short = project["DESCRIPTION_SHORT"]
|
|
72
97
|
if description_short != repo.description:
|
|
73
98
|
print(f"in {folder}...")
|
|
74
99
|
print(f"description_short is [{description_short}]")
|
|
75
100
|
print(f"repo.description is [{repo.description}]")
|
|
76
101
|
repo.edit(description=description_short)
|
|
77
|
-
if not
|
|
102
|
+
if "KEYWORDS" not in project:
|
|
78
103
|
print(f"{folder} no keywords")
|
|
79
104
|
os.chdir(orig_folder)
|
|
80
105
|
continue
|
|
81
|
-
keywords =
|
|
106
|
+
keywords = project["KEYWORDS"]
|
|
82
107
|
if set(keywords) != set(repo.get_topics()):
|
|
83
108
|
print(f"in {folder}...")
|
|
84
109
|
print(f"keywords is [{keywords}]")
|
|
@@ -273,14 +298,15 @@ def runs_show_not_success() -> None:
|
|
|
273
298
|
description="Pull all projects from github",
|
|
274
299
|
configs=[
|
|
275
300
|
ConfigGithub,
|
|
301
|
+
ConfigAlgo,
|
|
276
302
|
],
|
|
277
303
|
)
|
|
278
304
|
def pull_all() -> None:
|
|
279
305
|
orig_folder = os.getcwd()
|
|
306
|
+
base_dir = get_base_dir()
|
|
280
307
|
for repo in yield_repos():
|
|
281
|
-
owner = repo.owner.login
|
|
282
308
|
project = repo.name
|
|
283
|
-
folder =
|
|
309
|
+
folder = get_repo_folder(repo)
|
|
284
310
|
if os.path.isdir(folder):
|
|
285
311
|
if not os.path.isfile(os.path.join(folder, ".skip")):
|
|
286
312
|
print(f"project [{project}] exists, pulling it...")
|
|
@@ -296,13 +322,15 @@ def pull_all() -> None:
|
|
|
296
322
|
else:
|
|
297
323
|
print(f"project [{project}] exists, skipping it because of .skip file...")
|
|
298
324
|
else:
|
|
299
|
-
print(f"project [{project}] does not exists, cloning it from [{repo.ssh_url}]...")
|
|
325
|
+
print(f"project [{project}] does not exists, cloning it from [{repo.ssh_url}] to [{folder}]...")
|
|
326
|
+
os.makedirs(base_dir, exist_ok=True)
|
|
300
327
|
subprocess.check_call(
|
|
301
328
|
[
|
|
302
329
|
"git",
|
|
303
330
|
"clone",
|
|
304
331
|
repo.ssh_url,
|
|
305
|
-
]
|
|
332
|
+
],
|
|
333
|
+
cwd=base_dir,
|
|
306
334
|
)
|
|
307
335
|
|
|
308
336
|
|
|
@@ -315,10 +343,10 @@ def pull_all() -> None:
|
|
|
315
343
|
)
|
|
316
344
|
def clone_all() -> None:
|
|
317
345
|
all_repo_folders = set()
|
|
346
|
+
base_dir = get_base_dir()
|
|
318
347
|
for repo in yield_repos():
|
|
319
|
-
owner = repo.owner.login
|
|
320
348
|
project = repo.name
|
|
321
|
-
folder =
|
|
349
|
+
folder = get_repo_folder(repo)
|
|
322
350
|
all_repo_folders.add(folder)
|
|
323
351
|
# print(f"considering [{project}] from [{repo.ssh_url}]...")
|
|
324
352
|
if os.path.isfile(folder):
|
|
@@ -329,21 +357,20 @@ def clone_all() -> None:
|
|
|
329
357
|
continue
|
|
330
358
|
if ConfigAlgo.dryrun:
|
|
331
359
|
continue
|
|
332
|
-
print(f"cloning [{
|
|
333
|
-
|
|
334
|
-
os.mkdir(owner)
|
|
360
|
+
print(f"cloning [{project}] from [{repo.ssh_url}] to [{folder}]")
|
|
361
|
+
os.makedirs(base_dir, exist_ok=True)
|
|
335
362
|
subprocess.check_call(
|
|
336
363
|
[
|
|
337
364
|
"git",
|
|
338
365
|
"clone",
|
|
339
366
|
repo.ssh_url,
|
|
340
367
|
],
|
|
341
|
-
cwd=
|
|
368
|
+
cwd=base_dir,
|
|
342
369
|
stdout=subprocess.DEVNULL,
|
|
343
370
|
stderr=subprocess.DEVNULL,
|
|
344
371
|
)
|
|
345
372
|
if ConfigAlgo.show_extra:
|
|
346
|
-
disk_repos = get_all_git_repos()
|
|
373
|
+
disk_repos = get_all_git_repos(base_dir)
|
|
347
374
|
extra_repos = disk_repos - all_repo_folders
|
|
348
375
|
if extra_repos:
|
|
349
376
|
print("extra repos follow:")
|
|
@@ -368,6 +395,34 @@ def workflows_run() -> None:
|
|
|
368
395
|
print(f"{ret}")
|
|
369
396
|
|
|
370
397
|
|
|
398
|
+
@register_endpoint(
|
|
399
|
+
description="Show all configuration options and their current values",
|
|
400
|
+
configs=[
|
|
401
|
+
ConfigGithub,
|
|
402
|
+
ConfigAlgo,
|
|
403
|
+
ConfigOutput,
|
|
404
|
+
],
|
|
405
|
+
)
|
|
406
|
+
def config() -> None:
|
|
407
|
+
for config_class in [ConfigGithub, ConfigAlgo, ConfigOutput]:
|
|
408
|
+
print(f"{config_class.__name__}: {get_first_line(config_class, 'undocumented')}")
|
|
409
|
+
for name, param in the_registry.yield_name_data_for_config(config_class):
|
|
410
|
+
value = getattr(config_class, name)
|
|
411
|
+
if param.required and value is param.default:
|
|
412
|
+
value_str = "MANDATORY (not set)"
|
|
413
|
+
else:
|
|
414
|
+
value_str = param.t2s(value)
|
|
415
|
+
print(f" {name} [{param.get_type_name()}]: {value_str}")
|
|
416
|
+
print(f" {param.help_string}")
|
|
417
|
+
if not param.required:
|
|
418
|
+
print(f" default: {param.t2s(param.default)}")
|
|
419
|
+
more_help = param.more_help()
|
|
420
|
+
if more_help is not None:
|
|
421
|
+
print(f" {more_help}")
|
|
422
|
+
print()
|
|
423
|
+
print(f"repos are cloned to and pulled from: {get_base_dir()}/<repo-name>")
|
|
424
|
+
|
|
425
|
+
|
|
371
426
|
@register_main(
|
|
372
427
|
main_description=pygitpub.static.DESCRIPTION,
|
|
373
428
|
app_name=pygitpub.static.APP_NAME,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Load "config/*.lua" data files.
|
|
3
|
+
|
|
4
|
+
The config files used to be python modules loaded via importlib; they are now
|
|
5
|
+
lua data files (config/project.lua and friends). A file is run in a fresh lua
|
|
6
|
+
runtime and the globals it declares are returned as a plain dict.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os.path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
# lupa exposes LuaRuntime through a module level __getattr__ that points at the
|
|
13
|
+
# bundled lua version (lupa.lua54, lupa.lua55, ...), so it cannot be imported
|
|
14
|
+
# by name. Going through the package attribute works for any bundled version.
|
|
15
|
+
import lupa
|
|
16
|
+
|
|
17
|
+
CONFIG_FOLDER = "config"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def lua_to_python(value: Any) -> Any:
|
|
21
|
+
"""
|
|
22
|
+
Recursively convert a lua value into its python equivalent.
|
|
23
|
+
|
|
24
|
+
Lua has a single table type, so a table is a list if its keys are exactly
|
|
25
|
+
1..n and a dict otherwise. An empty table becomes an empty list, which
|
|
26
|
+
matches how the config files use them.
|
|
27
|
+
"""
|
|
28
|
+
if not hasattr(value, "values"):
|
|
29
|
+
return value
|
|
30
|
+
keys = list(value.keys())
|
|
31
|
+
if keys == list(range(1, len(keys) + 1)):
|
|
32
|
+
return [lua_to_python(x) for x in value.values()]
|
|
33
|
+
return {k: lua_to_python(value[k]) for k in keys}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def config_path(name: str) -> str:
|
|
37
|
+
""" return the path of a config file by its short name ("project") """
|
|
38
|
+
return os.path.join(CONFIG_FOLDER, f"{name}.lua")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_lua_file(path: str) -> dict[str, Any]:
|
|
42
|
+
"""
|
|
43
|
+
Run a lua file and return the globals it declared as a dict.
|
|
44
|
+
|
|
45
|
+
Raises FileNotFoundError if the file does not exist.
|
|
46
|
+
"""
|
|
47
|
+
if not os.path.isfile(path):
|
|
48
|
+
raise FileNotFoundError(path)
|
|
49
|
+
runtime = lupa.LuaRuntime()
|
|
50
|
+
with open(path, encoding="utf-8") as stream:
|
|
51
|
+
runtime.execute(stream.read())
|
|
52
|
+
globals_table = runtime.globals()
|
|
53
|
+
values = {}
|
|
54
|
+
for key in globals_table:
|
|
55
|
+
# skip the lua standard library, we only want what the file declared
|
|
56
|
+
if key in _LUA_BUILTINS:
|
|
57
|
+
continue
|
|
58
|
+
values[key] = lua_to_python(globals_table[key])
|
|
59
|
+
return values
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
_LUA_BUILTINS = frozenset({
|
|
63
|
+
"_G", "_VERSION", "assert", "collectgarbage", "coroutine", "debug", "dofile",
|
|
64
|
+
"error", "getmetatable", "io", "ipairs", "load", "loadfile", "math", "next",
|
|
65
|
+
"os", "package", "pairs", "pcall", "print", "python", "rawequal", "rawget",
|
|
66
|
+
"rawlen", "rawset", "require", "select", "setmetatable", "string", "table",
|
|
67
|
+
"tonumber", "tostring", "type", "utf8", "warn", "xpcall",
|
|
68
|
+
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
""" misc.py """
|
|
2
2
|
|
|
3
|
+
import glob
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import os.path
|
|
6
|
-
import glob
|
|
7
7
|
|
|
8
8
|
from pygitpub import LOGGER_NAME
|
|
9
9
|
|
|
@@ -28,8 +28,8 @@ def delete(workflow_run):
|
|
|
28
28
|
return status == 204
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def get_all_git_repos() -> set[str]:
|
|
32
|
-
""" we use
|
|
31
|
+
def get_all_git_repos(base_dir: str) -> set[str]:
|
|
32
|
+
""" we use */.git under base_dir with recursive=False (which is the default)
|
|
33
33
|
because otherwise we would find submodules too
|
|
34
34
|
"""
|
|
35
|
-
return {os.path.dirname(x) for x in glob.glob("
|
|
35
|
+
return {os.path.dirname(x) for x in glob.glob(os.path.join(base_dir, "*/.git"))}
|
pygitpub-0.0.58/.gitignore
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
# generated
|
|
2
|
-
/build/
|
|
3
|
-
/toolchain/
|
|
4
|
-
/dist/
|
|
5
|
-
/target/
|
|
6
|
-
/out/
|
|
7
|
-
*.out
|
|
8
|
-
/.mypy_cache/
|
|
9
|
-
|
|
10
|
-
# folders to git ignore
|
|
11
|
-
/*.gi
|
|
12
|
-
|
|
13
|
-
# gems
|
|
14
|
-
/gems/
|
|
15
|
-
|
|
16
|
-
# hive
|
|
17
|
-
derby.log
|
|
18
|
-
dfs/
|
|
19
|
-
metastore_db/
|
|
20
|
-
|
|
21
|
-
# c, c++ stuff
|
|
22
|
-
*.o
|
|
23
|
-
*.so
|
|
24
|
-
*.a
|
|
25
|
-
*.dis
|
|
26
|
-
/Makefile.bak
|
|
27
|
-
*.o.ur-safe
|
|
28
|
-
*.oo
|
|
29
|
-
*.elf
|
|
30
|
-
*.dep
|
|
31
|
-
/core.*
|
|
32
|
-
lib*.so.*
|
|
33
|
-
*.gch
|
|
34
|
-
*.pchi
|
|
35
|
-
*.gcda
|
|
36
|
-
|
|
37
|
-
# kernel stuff
|
|
38
|
-
*.ko
|
|
39
|
-
# *.mod
|
|
40
|
-
*.mod.c
|
|
41
|
-
*.mod.cmd
|
|
42
|
-
.*.o.cmd
|
|
43
|
-
.*.ko.cmd
|
|
44
|
-
.*.o.tmp
|
|
45
|
-
.*.o.d
|
|
46
|
-
.tmp_versions
|
|
47
|
-
Module.symvers
|
|
48
|
-
modules.order
|
|
49
|
-
*.objdump
|
|
50
|
-
/src/kernel/.*.tmp
|
|
51
|
-
.modules.*.cmd
|
|
52
|
-
.Module.*.cmd
|
|
53
|
-
/flags.cfg
|
|
54
|
-
# device tree binary files and binary overlay files
|
|
55
|
-
*.dtb
|
|
56
|
-
*.dtbo
|
|
57
|
-
|
|
58
|
-
# sphinx
|
|
59
|
-
/docs/_build/
|
|
60
|
-
/docs/.doctrees/environment.pickle
|
|
61
|
-
|
|
62
|
-
# installations
|
|
63
|
-
/external/
|
|
64
|
-
/install/
|
|
65
|
-
*.tar.gz
|
|
66
|
-
|
|
67
|
-
# python
|
|
68
|
-
__pycache__/
|
|
69
|
-
*.py[oc]
|
|
70
|
-
/wheel/
|
|
71
|
-
/.venv/
|
|
72
|
-
.coverage
|
|
73
|
-
.coverage.*
|
|
74
|
-
coverage.xml
|
|
75
|
-
*.egg-info/
|
|
76
|
-
|
|
77
|
-
# mine
|
|
78
|
-
/.pydmt.build.errors
|
|
79
|
-
/.pydmt/
|
|
80
|
-
*.stamp
|
|
81
|
-
*.stamp.*
|
|
82
|
-
|
|
83
|
-
# java
|
|
84
|
-
*.class
|
|
85
|
-
*.jar
|
|
86
|
-
*.war
|
|
87
|
-
*.ear
|
|
88
|
-
*.jsa
|
|
89
|
-
/.mtj.tmp/
|
|
90
|
-
/hs_err_pid*
|
|
91
|
-
|
|
92
|
-
# node modules
|
|
93
|
-
node_modules/
|
|
94
|
-
|
|
95
|
-
# ppt, pdf
|
|
96
|
-
/doc/odp/*.pdf
|
|
97
|
-
/doc/odp/*.ppt
|
|
98
|
-
|
|
99
|
-
# intel compiler pgo
|
|
100
|
-
*.dyn
|
|
101
|
-
*.dpi
|
|
102
|
-
# *.lock
|
|
103
|
-
|
|
104
|
-
# dwarf dynamic debug info
|
|
105
|
-
*.dwo
|
|
106
|
-
|
|
107
|
-
# locks by open office
|
|
108
|
-
.~lock.*
|
|
109
|
-
|
|
110
|
-
# haskell
|
|
111
|
-
*.hi
|
|
112
|
-
|
|
113
|
-
# repositries of deb packages
|
|
114
|
-
/deb/mypackage/DEBIAN/control
|
|
115
|
-
|
|
116
|
-
# generic
|
|
117
|
-
*.log
|
|
118
|
-
|
|
119
|
-
# scons
|
|
120
|
-
.sconsign.dblite
|
|
121
|
-
|
|
122
|
-
# DS_Store
|
|
123
|
-
.DS_Store
|
|
124
|
-
|
|
125
|
-
# Java stuff
|
|
126
|
-
.gradle/
|
|
127
|
-
target/
|
|
128
|
-
build/
|
|
129
|
-
|
|
130
|
-
# Jupyter notebooks
|
|
131
|
-
.ipynb_checkpoints/
|
|
132
|
-
|
|
133
|
-
# c#
|
|
134
|
-
**/obj/
|
|
135
|
-
**/bin/
|
|
136
|
-
|
|
137
|
-
# vscode
|
|
138
|
-
.vscode/
|
|
139
|
-
|
|
140
|
-
# qemu
|
|
141
|
-
*.qcow2
|
|
142
|
-
*.iso
|
|
143
|
-
|
|
144
|
-
# jetbrains and eclipse
|
|
145
|
-
.cache
|
|
146
|
-
.settings
|
|
147
|
-
.classpath
|
|
148
|
-
.project
|
|
149
|
-
.project.xml
|
|
150
|
-
.idea/
|
|
151
|
-
build.properties
|
|
152
|
-
|
|
153
|
-
# spark
|
|
154
|
-
spark-warehouse/
|
|
155
|
-
|
|
156
|
-
# terraform
|
|
157
|
-
.terraform*
|
|
158
|
-
terraform.tfstate
|
|
159
|
-
terraform.tfstate.backup
|
|
160
|
-
|
|
161
|
-
# scala
|
|
162
|
-
target/
|
|
163
|
-
project/
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
""" importlib.py """
|
|
2
|
-
|
|
3
|
-
import importlib.util
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def module_exists(module_name):
|
|
7
|
-
""" return if a module exists or not """
|
|
8
|
-
spec = importlib.util.find_spec(module_name)
|
|
9
|
-
return spec is not None
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def import_file(file_path):
|
|
13
|
-
spec = importlib.util.spec_from_file_location("foo", file_path)
|
|
14
|
-
mod = importlib.util.module_from_spec(spec)
|
|
15
|
-
spec.loader.exec_module(mod)
|
|
16
|
-
return mod
|
|
File without changes
|
|
File without changes
|