github-rest-api 0.50.0__tar.gz → 0.52.0__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.
- github_rest_api-0.52.0/.github/workflows/check-workflow-template.yaml +20 -0
- github_rest_api-0.52.0/.github/workflows/claude.yaml +27 -0
- github_rest_api-0.52.0/.github/workflows/create-pr-dev-to-main.yaml +9 -0
- github_rest_api-0.52.0/.github/workflows/create-pr-to-dev.yaml +11 -0
- github_rest_api-0.52.0/.github/workflows/create-pr-to-main.yaml +10 -0
- github_rest_api-0.50.0/.github/workflows/auto_merge_pull_request.yaml → github_rest_api-0.52.0/.github/workflows/merge-pr.yaml +8 -25
- github_rest_api-0.52.0/.github/workflows/pr-workflow.yaml +34 -0
- github_rest_api-0.52.0/.github/workflows/release.yaml +12 -0
- github_rest_api-0.52.0/.github/workflows/remove-branch.yaml +9 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/PKG-INFO +3 -3
- github_rest_api-0.52.0/README.md +3 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/create_github_repo.py +116 -33
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/upload_image_to_issue.py +3 -6
- github_rest_api-0.52.0/github_rest_api/scripts/lint_repo.py +791 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/pyproject.toml +2 -1
- github_rest_api-0.52.0/tests/test_create_github_repo.py +326 -0
- github_rest_api-0.52.0/tests/test_lint_repo.py +910 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_upload_image_to_issue.py +9 -8
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/uv.lock +1 -1
- github_rest_api-0.50.0/.github/workflows/claude-code-review.yml +0 -67
- github_rest_api-0.50.0/.github/workflows/claude.yml +0 -50
- github_rest_api-0.50.0/.github/workflows/create_pr_to_main.yaml +0 -20
- github_rest_api-0.50.0/.github/workflows/lint.yaml +0 -34
- github_rest_api-0.50.0/.github/workflows/release.yaml +0 -22
- github_rest_api-0.50.0/.github/workflows/remove_branch.yaml +0 -18
- github_rest_api-0.50.0/.github/workflows/test.yaml +0 -31
- github_rest_api-0.50.0/README.md +0 -3
- github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/auto_merge_pull_request.yaml +0 -30
- github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/create_pr_dev_to_main.yaml +0 -19
- github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/create_pr_to_dev.yaml +0 -21
- github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/create_pr_to_main.yaml +0 -20
- github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/python/lint.yaml +0 -27
- github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/python/test.yaml +0 -31
- github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/remove_branch.yaml +0 -18
- github_rest_api-0.50.0/tests/test_create_github_repo.py +0 -128
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/.devcontainer/devcontainer.json +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/.gitignore +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/AGENTS.md +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/CLAUDE.md +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/__init__.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/github.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/pr_content.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/__init__.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/cargo/__init__.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/cargo/benchmark.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/cargo/profiling.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/cargo/utils.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/container/__init__.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/container/build_container_images.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/container/config_container.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/container/update_version_containerfile.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/__init__.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/auto_merge_pull_request.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/create_pull_request.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/release_on_github.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/remove_branch.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/utils.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/utils.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/memory/MEMORY.md +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/memory/feedback_test_runner.md +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/__init__.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_auto_merge_pull_request.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_build_container_images.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_create_pull_request.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_github.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_pr_content.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_release_on_github.py +0 -0
- {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_utils.py +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Check Workflow Templates
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- .github/workflows/**
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
check_workflow_template:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v6
|
|
13
|
+
- name: Check workflow templates use .yaml extension
|
|
14
|
+
run: |
|
|
15
|
+
yml_files=$(find .github/workflows/ -name '*.yml')
|
|
16
|
+
if [ -n "$yml_files" ]; then
|
|
17
|
+
echo "The following workflow templates must use the .yaml extension instead of .yml:"
|
|
18
|
+
echo "$yml_files"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Claude Code
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment:
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request_review_comment:
|
|
7
|
+
types: [created]
|
|
8
|
+
issues:
|
|
9
|
+
types: [opened, assigned]
|
|
10
|
+
pull_request_review:
|
|
11
|
+
types: [submitted]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude:
|
|
15
|
+
if: |
|
|
16
|
+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
17
|
+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
18
|
+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
|
19
|
+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
|
20
|
+
permissions:
|
|
21
|
+
actions: read
|
|
22
|
+
contents: read
|
|
23
|
+
issues: read
|
|
24
|
+
pull-requests: read
|
|
25
|
+
id-token: write
|
|
26
|
+
uses: legendu-net/.github/.github/workflows/claude.yaml@main
|
|
27
|
+
secrets: inherit
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: Merge PR
|
|
2
2
|
on:
|
|
3
3
|
# The Claude review runs with `track_progress`, which creates its tracking
|
|
4
4
|
# comment when the review *starts* and then edits that same comment as the
|
|
@@ -13,9 +13,7 @@ on:
|
|
|
13
13
|
# Renaming any of these silently disables the trigger.
|
|
14
14
|
workflow_run:
|
|
15
15
|
workflows:
|
|
16
|
-
-
|
|
17
|
-
- Lint Code
|
|
18
|
-
- Run Tests
|
|
16
|
+
- PR Workflow
|
|
19
17
|
types:
|
|
20
18
|
- completed
|
|
21
19
|
# Kept for approvals that come from a human rather than from Claude.
|
|
@@ -49,10 +47,10 @@ concurrency:
|
|
|
49
47
|
# and the newest run always sees the freshest state. Do not cancel in
|
|
50
48
|
# progress: a run cancelled midway through the merge API call is worse than a
|
|
51
49
|
# redundant one.
|
|
52
|
-
group:
|
|
50
|
+
group: merge-pull-request
|
|
53
51
|
cancel-in-progress: false
|
|
54
52
|
jobs:
|
|
55
|
-
|
|
53
|
+
merge_pull_request:
|
|
56
54
|
# A triggering workflow that failed, was cancelled or was skipped left the
|
|
57
55
|
# PR unmergeable or wrote no marker, so there is nothing new to act on.
|
|
58
56
|
# This job checks out no PR code, so the elevated `workflow_run` context is
|
|
@@ -60,22 +58,7 @@ jobs:
|
|
|
60
58
|
if: |
|
|
61
59
|
github.event_name != 'workflow_run'
|
|
62
60
|
|| github.event.workflow_run.conclusion == 'success'
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
steps:
|
|
68
|
-
- name: Install uv
|
|
69
|
-
run: |
|
|
70
|
-
curl -LsSf https://astral.sh/uv/install.sh | sudo env UV_INSTALL_DIR="/usr/local/bin" sh
|
|
71
|
-
- name: Auto Merge PR
|
|
72
|
-
# `--allowed-types` is intentionally left at the upstream default
|
|
73
|
-
# (build, chore, ci, docs, deps) so that only simple PRs auto-merge;
|
|
74
|
-
# anything substantive, `feat` and `fix` included, is merged by hand.
|
|
75
|
-
run: |
|
|
76
|
-
uvx --from github-rest-api@latest auto_merge_pull_request \
|
|
77
|
-
--authors dclong \
|
|
78
|
-
--approvers dclong 'claude[bot]' 'gemini-code-assist[bot]' \
|
|
79
|
-
--marker 'AUTO_MERGE_APPROVED' \
|
|
80
|
-
${{ inputs.dry_run && '--dry-run' || '' }} \
|
|
81
|
-
--token ${{ secrets.ACTIONS_TOKEN }}
|
|
61
|
+
uses: legendu-net/.github/.github/workflows/merge-pr.yaml@main
|
|
62
|
+
with:
|
|
63
|
+
dry_run: ${{ inputs.dry_run || false }}
|
|
64
|
+
secrets: inherit
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: PR Workflow
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize, ready_for_review, reopened]
|
|
6
|
+
branches:
|
|
7
|
+
- dev
|
|
8
|
+
- main
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
lint_code:
|
|
12
|
+
uses: legendu-net/.github/.github/workflows/lint.yaml@main
|
|
13
|
+
secrets: inherit
|
|
14
|
+
|
|
15
|
+
test_code:
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
pull-requests: read
|
|
19
|
+
uses: legendu-net/.github/.github/workflows/test.yaml@main
|
|
20
|
+
secrets: inherit
|
|
21
|
+
|
|
22
|
+
claude-review:
|
|
23
|
+
needs: [lint_code, test_code]
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
issues: write
|
|
27
|
+
pull-requests: write
|
|
28
|
+
id-token: write
|
|
29
|
+
uses: legendu-net/.github/.github/workflows/claude-code-review.yaml@main
|
|
30
|
+
with:
|
|
31
|
+
pr_number: ${{ github.event.pull_request.number }}
|
|
32
|
+
draft: ${{ github.event.pull_request.draft }}
|
|
33
|
+
head_repo_full_name: ${{ github.event.pull_request.head.repo.full_name }}
|
|
34
|
+
secrets: inherit
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: github-rest-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.52.0
|
|
4
4
|
Summary: Simple wrapper of GitHub REST APIs.
|
|
5
5
|
Author-email: Ben Du <longendu@yahoo.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -19,6 +19,6 @@ Provides-Extra: ai
|
|
|
19
19
|
Requires-Dist: litellm>=1.50; extra == 'ai'
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
|
|
22
|
-
# GitHub REST APIs
|
|
22
|
+
# GitHub REST APIs | [@GitHub](https://github.com/legendu-net/github_rest_api) | [@PyPI](https://pypi.org/project/github-rest-api/)
|
|
23
23
|
|
|
24
24
|
Simple wrapper of GitHub REST APIs with a focus on making GitHub Actions automation easy.
|
|
@@ -2,20 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import logging
|
|
5
|
-
import shutil
|
|
6
5
|
import sys
|
|
7
6
|
from collections.abc import Sequence
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
|
|
9
|
+
import requests
|
|
10
10
|
from dulwich import porcelain
|
|
11
11
|
from dulwich.refs import HEADREF, LOCAL_BRANCH_PREFIX, Ref
|
|
12
12
|
|
|
13
13
|
from github_rest_api import Organization, User
|
|
14
|
+
from github_rest_api.github import DEFAULT_TIMEOUT, URL_API, build_http_headers
|
|
14
15
|
from github_rest_api.scripts.utils import resolve_github_token, validate_repo
|
|
15
16
|
from github_rest_api.utils import as_str_sequence
|
|
16
17
|
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
18
19
|
|
|
20
|
+
# Workflows that configure this repository itself rather than the repositories
|
|
21
|
+
# created from these templates.
|
|
22
|
+
_REPO_ONLY_WORKFLOWS = frozenset({"check-workflow-template.yaml"})
|
|
23
|
+
|
|
24
|
+
# The default GitHub repo to copy workflow templates from.
|
|
25
|
+
DEFAULT_WORKFLOWS_REPO = "legendu-net/github_rest_api"
|
|
26
|
+
|
|
19
27
|
|
|
20
28
|
def _create_remote_repo(
|
|
21
29
|
repo: str, private: bool, token: str, is_owner_user: bool
|
|
@@ -38,11 +46,29 @@ def _remote_url(repo: str, protocol: str) -> str:
|
|
|
38
46
|
|
|
39
47
|
|
|
40
48
|
def _ensure_remote(path: Path, repo: str, protocol: str) -> None:
|
|
49
|
+
url = _remote_url(repo, protocol)
|
|
41
50
|
try:
|
|
42
|
-
porcelain.remote_add(path, "origin",
|
|
51
|
+
porcelain.remote_add(path, "origin", url)
|
|
43
52
|
logger.info("Added remote 'origin' for %s", repo)
|
|
44
53
|
except porcelain.RemoteExists:
|
|
45
54
|
pass
|
|
55
|
+
# `porcelain.remote_add` only writes the remote's `url`, unlike `git remote
|
|
56
|
+
# add`/`git clone`, which also set a `fetch` refspec. Without it, `git
|
|
57
|
+
# fetch`/`jj git fetch` never update `refs/remotes/origin/*`, so local
|
|
58
|
+
# branches silently stop tracking the remote. Repair it for a pre-existing
|
|
59
|
+
# 'origin' too (e.g. added by an older version of this script), but only
|
|
60
|
+
# when it already points at this repo, so an unrelated 'origin' remote is
|
|
61
|
+
# left untouched.
|
|
62
|
+
with porcelain.open_repo_closing(path) as r:
|
|
63
|
+
c = r.get_config()
|
|
64
|
+
try:
|
|
65
|
+
existing_url = c.get((b"remote", b"origin"), b"url")
|
|
66
|
+
except KeyError:
|
|
67
|
+
return
|
|
68
|
+
if existing_url != url.encode():
|
|
69
|
+
return
|
|
70
|
+
c.set((b"remote", b"origin"), b"fetch", b"+refs/heads/*:refs/remotes/origin/*")
|
|
71
|
+
c.write_to_path()
|
|
46
72
|
|
|
47
73
|
|
|
48
74
|
def _active_branch(path: Path) -> str:
|
|
@@ -73,12 +99,12 @@ def _head_and_branches(path: Path) -> tuple[str | None, set[str]]:
|
|
|
73
99
|
|
|
74
100
|
def _init_local_repo(
|
|
75
101
|
repo: str,
|
|
76
|
-
language: str,
|
|
77
102
|
dir_: str,
|
|
78
103
|
token: str,
|
|
79
104
|
protocol: str,
|
|
80
105
|
push: bool,
|
|
81
106
|
branches: Sequence[str] = ("main",),
|
|
107
|
+
workflows_repo: str = DEFAULT_WORKFLOWS_REPO,
|
|
82
108
|
) -> None:
|
|
83
109
|
branches = list(dict.fromkeys(branches))
|
|
84
110
|
if not branches:
|
|
@@ -147,19 +173,19 @@ def _init_local_repo(
|
|
|
147
173
|
logger.info("Successfully pushed branch '%s'.", branch)
|
|
148
174
|
else:
|
|
149
175
|
logger.info("Skipping push (pass --push to push branches to the remote).")
|
|
150
|
-
_add_workflow(path,
|
|
176
|
+
_add_workflow(path, workflows_repo=workflows_repo, token=token)
|
|
151
177
|
|
|
152
178
|
|
|
153
179
|
def create_github_repo(
|
|
154
180
|
repo: str,
|
|
155
181
|
private: bool,
|
|
156
|
-
language: str,
|
|
157
182
|
is_owner_user: bool,
|
|
158
183
|
dir_: str,
|
|
159
184
|
token: str,
|
|
160
185
|
protocol: str,
|
|
161
186
|
push: bool,
|
|
162
187
|
branches: Sequence[str] = ("main",),
|
|
188
|
+
workflows_repo: str = DEFAULT_WORKFLOWS_REPO,
|
|
163
189
|
) -> None:
|
|
164
190
|
branches = as_str_sequence(branches)
|
|
165
191
|
token = resolve_github_token(token)
|
|
@@ -170,35 +196,91 @@ def create_github_repo(
|
|
|
170
196
|
)
|
|
171
197
|
_init_local_repo(
|
|
172
198
|
repo=repo,
|
|
173
|
-
language=language,
|
|
174
199
|
dir_=dir_,
|
|
175
200
|
token=token,
|
|
176
201
|
branches=branches,
|
|
177
202
|
protocol=protocol,
|
|
178
203
|
push=push,
|
|
204
|
+
workflows_repo=workflows_repo,
|
|
179
205
|
)
|
|
180
206
|
|
|
181
207
|
|
|
182
|
-
def
|
|
183
|
-
|
|
184
|
-
|
|
208
|
+
def _list_workflow_entries(workflows_repo: str, token: str) -> list[dict]:
|
|
209
|
+
"""List the workflow template file entries available in a GitHub repo.
|
|
210
|
+
|
|
211
|
+
:param workflows_repo: The GitHub repo (in the format of owner/repo) to
|
|
212
|
+
list workflow templates from.
|
|
213
|
+
:param token: The GitHub token used to authenticate the request.
|
|
214
|
+
"""
|
|
215
|
+
url = f"{URL_API}/repos/{workflows_repo}/contents/.github/workflows"
|
|
216
|
+
resp = requests.get(
|
|
217
|
+
url=url, headers=build_http_headers(token), timeout=DEFAULT_TIMEOUT
|
|
218
|
+
)
|
|
219
|
+
resp.raise_for_status()
|
|
220
|
+
return [
|
|
221
|
+
entry
|
|
222
|
+
for entry in resp.json()
|
|
223
|
+
if entry["type"] == "file"
|
|
224
|
+
and entry["name"].endswith(".yaml")
|
|
225
|
+
and entry["name"] not in _REPO_ONLY_WORKFLOWS
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _download_workflow(workflows_repo: str, entry: dict, token: str) -> str:
|
|
230
|
+
"""Download the raw content of a single workflow template.
|
|
231
|
+
|
|
232
|
+
Prefers the entry's ``download_url`` (served from
|
|
233
|
+
raw.githubusercontent.com, which has a much higher rate limit than the
|
|
234
|
+
api.github.com contents endpoint used to list the entries), falling back
|
|
235
|
+
to a raw content request against the contents API for the rare case
|
|
236
|
+
where GitHub omits it.
|
|
237
|
+
|
|
238
|
+
:param workflows_repo: The GitHub repo (in the format of owner/repo) to
|
|
239
|
+
download the workflow template from.
|
|
240
|
+
:param entry: A file entry as returned by ``_list_workflow_entries``.
|
|
241
|
+
:param token: The GitHub token used to authenticate the request.
|
|
242
|
+
"""
|
|
243
|
+
download_url = entry.get("download_url")
|
|
244
|
+
if download_url:
|
|
245
|
+
# No Authorization header here: raw.githubusercontent.com already
|
|
246
|
+
# embeds any access it needs (e.g. a signed token for a private repo)
|
|
247
|
+
# in the URL itself, and forwarding an Authorization header scoped to
|
|
248
|
+
# a *different* repo makes it fail closed with a 404 even for public
|
|
249
|
+
# content.
|
|
250
|
+
resp = requests.get(url=download_url, timeout=DEFAULT_TIMEOUT)
|
|
251
|
+
else:
|
|
252
|
+
url = f"{URL_API}/repos/{workflows_repo}/contents/.github/workflows/{
|
|
253
|
+
entry['name']
|
|
254
|
+
}"
|
|
255
|
+
headers = build_http_headers(token) | {
|
|
256
|
+
"Accept": "application/vnd.github.raw+json"
|
|
257
|
+
}
|
|
258
|
+
resp = requests.get(url=url, headers=headers, timeout=DEFAULT_TIMEOUT)
|
|
259
|
+
resp.raise_for_status()
|
|
260
|
+
return resp.text
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _add_workflow(path: Path, workflows_repo: str, token: str) -> None:
|
|
264
|
+
"""Download workflow templates from a GitHub repo into a local repo.
|
|
265
|
+
|
|
266
|
+
:param path: The local repo directory to add workflow templates to.
|
|
267
|
+
:param workflows_repo: The GitHub repo (in the format of owner/repo) to
|
|
268
|
+
copy workflow templates from. An empty string skips this step.
|
|
269
|
+
:param token: The GitHub token used to authenticate the requests.
|
|
270
|
+
"""
|
|
271
|
+
if not workflows_repo:
|
|
272
|
+
logger.info("Skipping workflow templates (workflows_repo is empty).")
|
|
273
|
+
return
|
|
274
|
+
entries = _list_workflow_entries(workflows_repo, token)
|
|
275
|
+
if not entries:
|
|
276
|
+
raise FileNotFoundError(f"No workflow templates found in '{workflows_repo}'.")
|
|
185
277
|
dir_dest = path / ".github" / "workflows"
|
|
186
278
|
dir_dest.mkdir(parents=True, exist_ok=True)
|
|
187
|
-
for
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return
|
|
193
|
-
lang_dir = workflow_dir / language
|
|
194
|
-
if not lang_dir.exists():
|
|
195
|
-
return
|
|
196
|
-
for yaml in lang_dir.glob("*.yaml"):
|
|
197
|
-
if not (dir_dest / yaml.name).exists():
|
|
198
|
-
shutil.copy2(yaml, dir_dest)
|
|
199
|
-
logger.info(
|
|
200
|
-
"Copied %s specific workflow %s to %s", language, yaml.name, dir_dest
|
|
201
|
-
)
|
|
279
|
+
for entry in entries:
|
|
280
|
+
dest = dir_dest / entry["name"]
|
|
281
|
+
if not dest.exists():
|
|
282
|
+
dest.write_text(_download_workflow(workflows_repo, entry, token))
|
|
283
|
+
logger.info("Downloaded workflow %s from %s", entry["name"], workflows_repo)
|
|
202
284
|
|
|
203
285
|
|
|
204
286
|
def parse_args(args=None, namespace=None):
|
|
@@ -237,14 +319,6 @@ def parse_args(args=None, namespace=None):
|
|
|
237
319
|
action="store_false",
|
|
238
320
|
help="Whether to create the repository as public.",
|
|
239
321
|
)
|
|
240
|
-
parser.add_argument(
|
|
241
|
-
"-l",
|
|
242
|
-
"--lang",
|
|
243
|
-
"--language",
|
|
244
|
-
dest="language",
|
|
245
|
-
default="",
|
|
246
|
-
help="The language of the GitHub repository.",
|
|
247
|
-
)
|
|
248
322
|
parser.add_argument(
|
|
249
323
|
"-d",
|
|
250
324
|
"--dir",
|
|
@@ -274,6 +348,15 @@ def parse_args(args=None, namespace=None):
|
|
|
274
348
|
action="store_true",
|
|
275
349
|
help="Push branches to the remote (by default, nothing is pushed).",
|
|
276
350
|
)
|
|
351
|
+
parser.add_argument(
|
|
352
|
+
"-w",
|
|
353
|
+
"--workflows-repo",
|
|
354
|
+
dest="workflows_repo",
|
|
355
|
+
default=DEFAULT_WORKFLOWS_REPO,
|
|
356
|
+
help="The GitHub repo (in the format of owner/repo) to copy workflow "
|
|
357
|
+
f"templates from (default: {DEFAULT_WORKFLOWS_REPO}). "
|
|
358
|
+
"Pass an empty string to skip copying workflow templates.",
|
|
359
|
+
)
|
|
277
360
|
parser.add_argument(
|
|
278
361
|
"-v",
|
|
279
362
|
"--verbose",
|
|
@@ -291,13 +374,13 @@ def main() -> int:
|
|
|
291
374
|
create_github_repo(
|
|
292
375
|
repo=args.repo,
|
|
293
376
|
private=args.private,
|
|
294
|
-
language=args.language,
|
|
295
377
|
is_owner_user=args.is_owner_user,
|
|
296
378
|
dir_=args.dir,
|
|
297
379
|
token=args.token,
|
|
298
380
|
branches=args.branches,
|
|
299
381
|
protocol=args.protocol,
|
|
300
382
|
push=args.push,
|
|
383
|
+
workflows_repo=args.workflows_repo,
|
|
301
384
|
)
|
|
302
385
|
except Exception as e:
|
|
303
386
|
logger.error("Failed to create GitHub repo: %s", e, exc_info=args.verbose)
|
|
@@ -508,11 +508,11 @@ def upload_image_to_issue(
|
|
|
508
508
|
def main() -> int:
|
|
509
509
|
args = parse_args()
|
|
510
510
|
# The progress messages are emitted through logging, which the root logger
|
|
511
|
-
# would otherwise suppress. They go to stderr,
|
|
512
|
-
# number
|
|
511
|
+
# would otherwise suppress. They go to stderr, which is also where the
|
|
512
|
+
# number of the issue is reported.
|
|
513
513
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
514
514
|
try:
|
|
515
|
-
|
|
515
|
+
upload_image_to_issue(
|
|
516
516
|
token=args.token,
|
|
517
517
|
repo=args.repo,
|
|
518
518
|
issue_number=args.issue_number,
|
|
@@ -526,9 +526,6 @@ def main() -> int:
|
|
|
526
526
|
except Exception as e:
|
|
527
527
|
print(str(e), file=sys.stderr)
|
|
528
528
|
return 1
|
|
529
|
-
# The bare number is the only thing on stdout so that it can be captured
|
|
530
|
-
# with a command substitution and reused.
|
|
531
|
-
print(issue_number)
|
|
532
529
|
return 0
|
|
533
530
|
|
|
534
531
|
|