pygitpub 0.0.63__tar.gz → 0.0.65__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.63 → pygitpub-0.0.65}/PKG-INFO +2 -4
- {pygitpub-0.0.63 → pygitpub-0.0.65}/README.md +1 -3
- {pygitpub-0.0.63 → pygitpub-0.0.65}/pyproject.toml +18 -15
- {pygitpub-0.0.63 → pygitpub-0.0.65}/src/pygitpub/configs.py +21 -4
- {pygitpub-0.0.63 → pygitpub-0.0.65}/src/pygitpub/main.py +163 -34
- {pygitpub-0.0.63 → pygitpub-0.0.65}/src/pygitpub/static.py +1 -1
- {pygitpub-0.0.63 → pygitpub-0.0.65}/src/pygitpub/utils/misc.py +0 -17
- {pygitpub-0.0.63 → pygitpub-0.0.65}/.gitignore +0 -0
- {pygitpub-0.0.63 → pygitpub-0.0.65}/LICENSE +0 -0
- {pygitpub-0.0.63 → pygitpub-0.0.65}/src/pygitpub/__init__.py +0 -0
- {pygitpub-0.0.63 → pygitpub-0.0.65}/src/pygitpub/utils/__init__.py +0 -0
- {pygitpub-0.0.63 → pygitpub-0.0.65}/src/pygitpub/utils/lua.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pygitpub
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.65
|
|
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
|
|
@@ -41,9 +41,7 @@ project website: https://veltzer.github.io/pygitpub
|
|
|
41
41
|
|
|
42
42
|
author: Mark Veltzer
|
|
43
43
|
|
|
44
|
-
version: 0.0.
|
|
45
|
-
|
|
46
|
-

|
|
44
|
+
version: 0.0.65
|
|
47
45
|
|
|
48
46
|
## github
|
|
49
47
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pygitpub"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.65"
|
|
8
8
|
requires-python = ">=3.14"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Mark Veltzer", email = "mark.veltzer@gmail.com" }
|
|
@@ -41,8 +41,7 @@ dependencies = [
|
|
|
41
41
|
|
|
42
42
|
[dependency-groups]
|
|
43
43
|
dev = [
|
|
44
|
-
"
|
|
45
|
-
"pylint",
|
|
44
|
+
"pyrefly",
|
|
46
45
|
"pytest",
|
|
47
46
|
"ruff",
|
|
48
47
|
"sphinx",
|
|
@@ -83,24 +82,28 @@ exclude = [
|
|
|
83
82
|
"*.md",
|
|
84
83
|
]
|
|
85
84
|
|
|
86
|
-
[tool.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
85
|
+
[tool.pyrefly]
|
|
86
|
+
# Type checking (rsconstruct's pyrefly processor). preset = "legacy" is
|
|
87
|
+
# pyrefly's mypy-compatible behavior; "." on the search path is mypy's
|
|
88
|
+
# implicit current directory.
|
|
89
|
+
preset = "legacy"
|
|
90
|
+
python-version = "3.14"
|
|
91
|
+
search-path = [
|
|
92
|
+
"src",
|
|
93
|
+
".",
|
|
94
|
+
]
|
|
95
|
+
# Third-party libraries without type information.
|
|
96
|
+
ignore-missing-imports = [
|
|
97
97
|
"github.*",
|
|
98
98
|
"lupa.*",
|
|
99
99
|
"pyapikey.*",
|
|
100
100
|
"pylogconf.*",
|
|
101
101
|
"pytconf.*",
|
|
102
102
|
]
|
|
103
|
-
|
|
103
|
+
|
|
104
|
+
[tool.pyrefly.errors]
|
|
105
|
+
no-any-return = "error"
|
|
106
|
+
|
|
104
107
|
[tool.hatch.envs.default]
|
|
105
108
|
installer = "uv"
|
|
106
109
|
|
|
@@ -14,11 +14,8 @@ AFFILIATIONS = [
|
|
|
14
14
|
|
|
15
15
|
class ConfigGithub(Config):
|
|
16
16
|
"""
|
|
17
|
-
|
|
17
|
+
Parameters for your github account
|
|
18
18
|
"""
|
|
19
|
-
username = ParamCreator.create_str(
|
|
20
|
-
help_string="What is your github username?",
|
|
21
|
-
)
|
|
22
19
|
apikey = ParamCreator.create_str(
|
|
23
20
|
help_string="What is the name of your apikey (see pyapikey)?",
|
|
24
21
|
)
|
|
@@ -63,6 +60,26 @@ class ConfigAlgo(Config):
|
|
|
63
60
|
)
|
|
64
61
|
|
|
65
62
|
|
|
63
|
+
# The ways the runs_show_failing and runs_show_not_success endpoints can
|
|
64
|
+
# query github. The first is the default.
|
|
65
|
+
RUNS_APIS = [
|
|
66
|
+
"graphql",
|
|
67
|
+
"rest",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ConfigRuns(Config):
|
|
72
|
+
"""
|
|
73
|
+
Parameters to control how workflow runs are queried
|
|
74
|
+
"""
|
|
75
|
+
api = ParamCreator.create_choice(
|
|
76
|
+
choice_list=RUNS_APIS,
|
|
77
|
+
help_string="graphql: check the default branch head commit of all repos in parallel batches (fast);"
|
|
78
|
+
" rest: walk every workflow's last run on any branch (slow, one call per workflow)",
|
|
79
|
+
default="graphql",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
66
83
|
class ConfigOutput(Config):
|
|
67
84
|
"""
|
|
68
85
|
Parameters to control output
|
|
@@ -6,6 +6,9 @@ import json
|
|
|
6
6
|
import os
|
|
7
7
|
import subprocess
|
|
8
8
|
import sys
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
11
|
+
from typing import Any
|
|
9
12
|
|
|
10
13
|
import github
|
|
11
14
|
import pyapikey
|
|
@@ -15,7 +18,7 @@ from pytconf.pydoc import get_first_line
|
|
|
15
18
|
from pytconf.registry import the_registry
|
|
16
19
|
|
|
17
20
|
import pygitpub.static
|
|
18
|
-
from pygitpub.configs import AFFILIATIONS, ConfigAlgo, ConfigGithub, ConfigOutput
|
|
21
|
+
from pygitpub.configs import AFFILIATIONS, ConfigAlgo, ConfigGithub, ConfigOutput, ConfigRuns
|
|
19
22
|
from pygitpub.utils.lua import config_path, load_lua_file
|
|
20
23
|
from pygitpub.utils.misc import get_all_git_repos
|
|
21
24
|
|
|
@@ -31,7 +34,7 @@ def get_repo_folder(repo) -> str:
|
|
|
31
34
|
|
|
32
35
|
def get_affiliation() -> str:
|
|
33
36
|
""" validate the affiliation parameter and return it in the form github wants """
|
|
34
|
-
values = [x.strip() for x in ConfigAlgo.affiliation.split(",")]
|
|
37
|
+
values: list[str] = [x.strip() for x in ConfigAlgo.affiliation.split(",")]
|
|
35
38
|
values = [x for x in values if x != ""]
|
|
36
39
|
if not values:
|
|
37
40
|
raise ValueError("affiliation must not be empty")
|
|
@@ -41,9 +44,13 @@ def get_affiliation() -> str:
|
|
|
41
44
|
return ",".join(values)
|
|
42
45
|
|
|
43
46
|
|
|
44
|
-
def
|
|
47
|
+
def get_github(**kwargs: Any) -> github.Github:
|
|
45
48
|
apikey = pyapikey.get_key(ConfigGithub.apikey)
|
|
46
|
-
|
|
49
|
+
return github.Github(auth=github.Auth.Token(apikey), **kwargs)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def yield_repos():
|
|
53
|
+
g = get_github()
|
|
47
54
|
# github filters by affiliation server side, so repos we are not interested
|
|
48
55
|
# in (org repos, by default) are never fetched in the first place
|
|
49
56
|
affiliation = get_affiliation()
|
|
@@ -92,7 +99,8 @@ def fix_metadata() -> None:
|
|
|
92
99
|
print(f"in {folder}...")
|
|
93
100
|
print(f"description_short is [{description_short}]")
|
|
94
101
|
print(f"repo.description is [{repo.description}]")
|
|
95
|
-
|
|
102
|
+
if not ConfigAlgo.dryrun:
|
|
103
|
+
repo.edit(description=description_short)
|
|
96
104
|
if "KEYWORDS" not in project:
|
|
97
105
|
print(f"{folder} no keywords")
|
|
98
106
|
continue
|
|
@@ -101,32 +109,36 @@ def fix_metadata() -> None:
|
|
|
101
109
|
print(f"in {folder}...")
|
|
102
110
|
print(f"keywords is [{keywords}]")
|
|
103
111
|
print(f"repo.get_topics() is [{repo.get_topics()}]")
|
|
104
|
-
|
|
112
|
+
if not ConfigAlgo.dryrun:
|
|
113
|
+
repo.replace_topics(keywords)
|
|
105
114
|
|
|
106
115
|
|
|
107
116
|
@register_endpoint(
|
|
108
117
|
description="Set the correct website for the project on all repos where it is not correct",
|
|
109
118
|
configs=[
|
|
110
119
|
ConfigGithub,
|
|
120
|
+
ConfigOutput,
|
|
111
121
|
ConfigAlgo,
|
|
112
122
|
],
|
|
113
123
|
)
|
|
114
124
|
def homepage_fix() -> None:
|
|
115
125
|
for repo in yield_repos():
|
|
126
|
+
owner = repo.owner.login
|
|
127
|
+
# the user site repo is served at the root of the site, usually under
|
|
128
|
+
# a custom domain, so the pattern below does not apply to it
|
|
129
|
+
if repo.name.lower() == f"{owner.lower()}.github.io":
|
|
130
|
+
if ConfigOutput.verbose:
|
|
131
|
+
print(f"skipping [{repo.name}] because it is the user site")
|
|
132
|
+
continue
|
|
116
133
|
if repo.has_pages:
|
|
117
|
-
right_homepage = f"https://{
|
|
118
|
-
if repo.homepage != right_homepage:
|
|
119
|
-
print(f"{repo.name} is wrong!")
|
|
120
|
-
print(f"homepage: {repo.homepage}")
|
|
121
|
-
print(f"right_homepage: {right_homepage}")
|
|
122
|
-
print(f"patching [{repo.name}]...")
|
|
123
|
-
repo.edit(repo.name, homepage=right_homepage)
|
|
134
|
+
right_homepage = f"https://{owner}.github.io/{repo.name}/"
|
|
124
135
|
else:
|
|
125
|
-
right_homepage = f"https://github.com/{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
136
|
+
right_homepage = f"https://github.com/{owner}/{repo.name}/"
|
|
137
|
+
if repo.homepage != right_homepage:
|
|
138
|
+
print(f"{repo.name} is wrong!")
|
|
139
|
+
print(f"homepage: {repo.homepage}")
|
|
140
|
+
print(f"right_homepage: {right_homepage}")
|
|
141
|
+
if not ConfigAlgo.dryrun:
|
|
130
142
|
print(f"patching [{repo.name}]...")
|
|
131
143
|
repo.edit(repo.name, homepage=right_homepage)
|
|
132
144
|
|
|
@@ -195,20 +207,23 @@ def repos_list() -> None:
|
|
|
195
207
|
],
|
|
196
208
|
)
|
|
197
209
|
def cleanup() -> None:
|
|
198
|
-
# pylint: disable=too-many-branches,too-many-statements
|
|
199
210
|
for repo in yield_repos():
|
|
200
211
|
# 1. Cleanup workflow runs
|
|
201
212
|
for workflow in repo.get_workflows():
|
|
202
213
|
existing = 0
|
|
203
214
|
for run in workflow.get_runs():
|
|
204
215
|
if ConfigOutput.verbose:
|
|
205
|
-
print(f"inspecting [workflow run] {repo.name} {workflow.name} {run.conclusion}")
|
|
216
|
+
print(f"inspecting [workflow run] {repo.name} {workflow.name} {run.status} {run.conclusion}")
|
|
217
|
+
# github refuses to delete a run that is queued or still
|
|
218
|
+
# running, so leave those alone and don't count them
|
|
219
|
+
if run.status != "completed":
|
|
220
|
+
continue
|
|
206
221
|
delete_it = False
|
|
207
|
-
# if its a pages build delete it
|
|
208
|
-
if workflow.name == "pages-build-deployment"
|
|
222
|
+
# if its a pages build delete it
|
|
223
|
+
if workflow.name == "pages-build-deployment":
|
|
209
224
|
delete_it = True
|
|
210
|
-
# if its not on the
|
|
211
|
-
if run.head_branch !=
|
|
225
|
+
# if its not on the default branch, delete it
|
|
226
|
+
if run.head_branch != repo.default_branch:
|
|
212
227
|
delete_it = True
|
|
213
228
|
# if its not a paged build and it failed then delete it
|
|
214
229
|
if workflow.name != "pages-build-deployment" and run.conclusion in (
|
|
@@ -219,6 +234,8 @@ def cleanup() -> None:
|
|
|
219
234
|
delete_it = True
|
|
220
235
|
if delete_it:
|
|
221
236
|
print(f"deleting [workflow run] {repo.name} {workflow.name} {run.head_branch} {run.conclusion} {run.url}")
|
|
237
|
+
if ConfigAlgo.dryrun:
|
|
238
|
+
continue
|
|
222
239
|
try:
|
|
223
240
|
run.delete()
|
|
224
241
|
except github.GithubException as e:
|
|
@@ -242,12 +259,13 @@ def cleanup() -> None:
|
|
|
242
259
|
|
|
243
260
|
if delete_it:
|
|
244
261
|
print(f"deleting [deployment] {repo.name} {deployment.id}")
|
|
262
|
+
if ConfigAlgo.dryrun:
|
|
263
|
+
continue
|
|
245
264
|
try:
|
|
246
265
|
deployment.create_status("inactive")
|
|
247
266
|
except github.GithubException as e:
|
|
248
267
|
print(f"Failed to deactivate deployment: {e}")
|
|
249
268
|
try:
|
|
250
|
-
# pylint: disable=protected-access
|
|
251
269
|
status, _, _ = deployment._requester.requestJson("DELETE", deployment.url)
|
|
252
270
|
if status != 204:
|
|
253
271
|
print(f"Failed to delete deployment: HTTP {status}")
|
|
@@ -259,6 +277,8 @@ def cleanup() -> None:
|
|
|
259
277
|
for release in repo.get_releases():
|
|
260
278
|
if existing_releases >= 4:
|
|
261
279
|
print(f"deleting [release] {repo.name} {release.title}")
|
|
280
|
+
if ConfigAlgo.dryrun:
|
|
281
|
+
continue
|
|
262
282
|
try:
|
|
263
283
|
release.delete_release()
|
|
264
284
|
except github.GithubException as e:
|
|
@@ -292,8 +312,108 @@ def runs_show_running() -> None:
|
|
|
292
312
|
for repo in yield_repos():
|
|
293
313
|
for workflow in repo.get_workflows():
|
|
294
314
|
for run in workflow.get_runs():
|
|
295
|
-
if run.
|
|
296
|
-
print(f"{repo.name}: {workflow.name} {run.
|
|
315
|
+
if run.status in ("in_progress", "queued"):
|
|
316
|
+
print(f"{repo.name}: {workflow.name} {run.status}")
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
# repos per GraphQL query; measured fastest at around 10
|
|
320
|
+
GRAPHQL_BATCH_SIZE = 10
|
|
321
|
+
# GraphQL queries in flight at once, well below github's concurrency limits
|
|
322
|
+
GRAPHQL_WORKERS = 26
|
|
323
|
+
|
|
324
|
+
GRAPHQL_ROLLUP_FIELD = """
|
|
325
|
+
r{index}: repository(owner: {owner}, name: {name}) {{
|
|
326
|
+
defaultBranchRef {{ target {{ ... on Commit {{ statusCheckRollup {{ state }} }} }} }}
|
|
327
|
+
}}
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
GRAPHQL_SUITES_QUERY = """
|
|
331
|
+
query($owner: String!, $name: String!) {
|
|
332
|
+
repository(owner: $owner, name: $name) {
|
|
333
|
+
defaultBranchRef {
|
|
334
|
+
target {
|
|
335
|
+
... on Commit {
|
|
336
|
+
checkSuites(first: 50) {
|
|
337
|
+
nodes {
|
|
338
|
+
status
|
|
339
|
+
conclusion
|
|
340
|
+
workflowRun { workflow { name } }
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def graphql_show_runs(bad_state: Callable[[str], bool], bad_conclusion: Callable[[str], bool]) -> None:
|
|
352
|
+
"""
|
|
353
|
+
Print the workflows that are not green on the default branch head commit
|
|
354
|
+
of every repo.
|
|
355
|
+
|
|
356
|
+
Resolving statusCheckRollup is slow on github's side (~4.5s per 50 repos)
|
|
357
|
+
and cursor pagination would run the pages one after another, so the repos
|
|
358
|
+
are split into small batches, each batch is one query with an aliased
|
|
359
|
+
`repository` field per repo, and the batches run in parallel. Repos whose
|
|
360
|
+
rollup state is bad then get one more query each to name the workflows.
|
|
361
|
+
Only the head commit of the default branch is looked at, so a failed run
|
|
362
|
+
on another branch, or a scheduled run on an older commit, does not show.
|
|
363
|
+
"""
|
|
364
|
+
# the default client sleeps a second between POSTs, and every GraphQL
|
|
365
|
+
# query is a POST, which would serialize the parallel queries
|
|
366
|
+
g = get_github(
|
|
367
|
+
pool_size=GRAPHQL_WORKERS,
|
|
368
|
+
seconds_between_requests=None,
|
|
369
|
+
seconds_between_writes=None,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
def graphql(query: str, variables: dict[str, str]) -> dict[str, Any]:
|
|
373
|
+
_headers, data = g.requester.graphql_query(query, variables)
|
|
374
|
+
result: dict[str, Any] = data["data"]
|
|
375
|
+
return result
|
|
376
|
+
|
|
377
|
+
def get_states(batch: list[str]) -> list[str | None]:
|
|
378
|
+
fields = []
|
|
379
|
+
for index, full_name in enumerate(batch):
|
|
380
|
+
owner, name = full_name.split("/", 1)
|
|
381
|
+
# json.dumps gives a correctly quoted GraphQL string literal
|
|
382
|
+
fields.append(GRAPHQL_ROLLUP_FIELD.format(index=index, owner=json.dumps(owner), name=json.dumps(name)))
|
|
383
|
+
data = graphql("query {" + "".join(fields) + "}", {})
|
|
384
|
+
states: list[str | None] = []
|
|
385
|
+
for index in range(len(batch)):
|
|
386
|
+
ref = data[f"r{index}"]["defaultBranchRef"]
|
|
387
|
+
rollup = ref and ref["target"].get("statusCheckRollup")
|
|
388
|
+
states.append(rollup and rollup["state"])
|
|
389
|
+
return states
|
|
390
|
+
|
|
391
|
+
def get_bad_workflows(full_name: str) -> list[tuple[str, str]]:
|
|
392
|
+
owner, name = full_name.split("/", 1)
|
|
393
|
+
data = graphql(GRAPHQL_SUITES_QUERY, {"owner": owner, "name": name})
|
|
394
|
+
workflows = []
|
|
395
|
+
for suite in data["repository"]["defaultBranchRef"]["target"]["checkSuites"]["nodes"]:
|
|
396
|
+
# github creates an empty, never-run suite for every installed
|
|
397
|
+
# app; without a workflow run behind it, it tells us nothing
|
|
398
|
+
if suite["workflowRun"] is None:
|
|
399
|
+
continue
|
|
400
|
+
# an unfinished suite has no conclusion yet; report its status
|
|
401
|
+
conclusion = suite["conclusion"] or suite["status"]
|
|
402
|
+
if bad_conclusion(conclusion):
|
|
403
|
+
workflows.append((suite["workflowRun"]["workflow"]["name"], conclusion.lower()))
|
|
404
|
+
return workflows
|
|
405
|
+
|
|
406
|
+
full_names = [repo.full_name for repo in yield_repos()]
|
|
407
|
+
batches = [full_names[i:i + GRAPHQL_BATCH_SIZE] for i in range(0, len(full_names), GRAPHQL_BATCH_SIZE)]
|
|
408
|
+
with ThreadPoolExecutor(GRAPHQL_WORKERS) as executor:
|
|
409
|
+
states = [state for result in executor.map(get_states, batches) for state in result]
|
|
410
|
+
# repos without any checks have no state and are skipped, as the rest
|
|
411
|
+
# api path skips workflows that never ran
|
|
412
|
+
bad = [full_name for full_name, state in zip(full_names, states) if state is not None and bad_state(state)]
|
|
413
|
+
for full_name, workflows in zip(bad, executor.map(get_bad_workflows, bad)):
|
|
414
|
+
name = full_name.split("/", 1)[1]
|
|
415
|
+
for workflow, conclusion in workflows:
|
|
416
|
+
print(f"{name}: {workflow} {conclusion}")
|
|
297
417
|
|
|
298
418
|
|
|
299
419
|
@register_endpoint(
|
|
@@ -301,9 +421,16 @@ def runs_show_running() -> None:
|
|
|
301
421
|
configs=[
|
|
302
422
|
ConfigGithub,
|
|
303
423
|
ConfigAlgo,
|
|
424
|
+
ConfigRuns,
|
|
304
425
|
],
|
|
305
426
|
)
|
|
306
427
|
def runs_show_failing() -> None:
|
|
428
|
+
if ConfigRuns.api == "graphql":
|
|
429
|
+
graphql_show_runs(
|
|
430
|
+
lambda state: state in ("FAILURE", "ERROR"),
|
|
431
|
+
lambda conclusion: conclusion == "FAILURE",
|
|
432
|
+
)
|
|
433
|
+
return
|
|
307
434
|
for repo in yield_repos():
|
|
308
435
|
for workflow in repo.get_workflows():
|
|
309
436
|
last_run = next(iter(workflow.get_runs()), None)
|
|
@@ -316,9 +443,16 @@ def runs_show_failing() -> None:
|
|
|
316
443
|
configs=[
|
|
317
444
|
ConfigGithub,
|
|
318
445
|
ConfigAlgo,
|
|
446
|
+
ConfigRuns,
|
|
319
447
|
],
|
|
320
448
|
)
|
|
321
449
|
def runs_show_not_success() -> None:
|
|
450
|
+
if ConfigRuns.api == "graphql":
|
|
451
|
+
graphql_show_runs(
|
|
452
|
+
lambda state: state != "SUCCESS",
|
|
453
|
+
lambda conclusion: conclusion != "SUCCESS",
|
|
454
|
+
)
|
|
455
|
+
return
|
|
322
456
|
for repo in yield_repos():
|
|
323
457
|
for workflow in repo.get_workflows():
|
|
324
458
|
last_run = next(iter(workflow.get_runs()), None)
|
|
@@ -345,7 +479,6 @@ def pull_all() -> None:
|
|
|
345
479
|
[
|
|
346
480
|
"git",
|
|
347
481
|
"pull",
|
|
348
|
-
# "--tags",
|
|
349
482
|
],
|
|
350
483
|
cwd=folder,
|
|
351
484
|
)
|
|
@@ -365,7 +498,7 @@ def pull_all() -> None:
|
|
|
365
498
|
|
|
366
499
|
|
|
367
500
|
@register_endpoint(
|
|
368
|
-
description="
|
|
501
|
+
description="Clone all projects from github",
|
|
369
502
|
configs=[
|
|
370
503
|
ConfigGithub,
|
|
371
504
|
ConfigAlgo,
|
|
@@ -378,12 +511,9 @@ def clone_all() -> None:
|
|
|
378
511
|
project = repo.name
|
|
379
512
|
folder = get_repo_folder(repo)
|
|
380
513
|
all_repo_folders.add(folder)
|
|
381
|
-
# print(f"considering [{project}] from [{repo.ssh_url}]...")
|
|
382
514
|
if os.path.isfile(folder):
|
|
383
|
-
# print(f"skipping [{folder}] as it is not to be cloned...")
|
|
384
515
|
continue
|
|
385
516
|
if os.path.isdir(folder):
|
|
386
|
-
# print(f"skipping [{folder}] as it is already cloned...")
|
|
387
517
|
continue
|
|
388
518
|
if ConfigAlgo.dryrun:
|
|
389
519
|
continue
|
|
@@ -397,7 +527,6 @@ def clone_all() -> None:
|
|
|
397
527
|
],
|
|
398
528
|
cwd=base_dir,
|
|
399
529
|
stdout=subprocess.DEVNULL,
|
|
400
|
-
stderr=subprocess.DEVNULL,
|
|
401
530
|
)
|
|
402
531
|
if ConfigAlgo.show_extra:
|
|
403
532
|
disk_repos = get_all_git_repos(base_dir)
|
|
@@ -421,7 +550,7 @@ def workflows_run() -> None:
|
|
|
421
550
|
for workflow in repo.get_workflows():
|
|
422
551
|
print(f"{repo.name}: {workflow.name}...", end="")
|
|
423
552
|
sys.stdout.flush()
|
|
424
|
-
ret = workflow.create_dispatch(ref=
|
|
553
|
+
ret = workflow.create_dispatch(ref=repo.default_branch)
|
|
425
554
|
print(f"{ret}")
|
|
426
555
|
|
|
427
556
|
|
|
@@ -1,25 +1,8 @@
|
|
|
1
1
|
""" misc.py """
|
|
2
2
|
|
|
3
3
|
import glob
|
|
4
|
-
import logging
|
|
5
|
-
import os
|
|
6
4
|
import os.path
|
|
7
5
|
|
|
8
|
-
from pygitpub import LOGGER_NAME
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def get_logger():
|
|
12
|
-
return logging.getLogger(LOGGER_NAME)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def get_number_of_files(folder: str) -> int:
|
|
16
|
-
count = 0
|
|
17
|
-
for _root, _directories, files in os.walk(folder):
|
|
18
|
-
count += len(files)
|
|
19
|
-
return count
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
6
|
|
|
24
7
|
def get_all_git_repos(base_dir: str) -> set[str]:
|
|
25
8
|
""" we use */.git under base_dir with recursive=False (which is the default)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|