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.
Files changed (68) hide show
  1. github_rest_api-0.52.0/.github/workflows/check-workflow-template.yaml +20 -0
  2. github_rest_api-0.52.0/.github/workflows/claude.yaml +27 -0
  3. github_rest_api-0.52.0/.github/workflows/create-pr-dev-to-main.yaml +9 -0
  4. github_rest_api-0.52.0/.github/workflows/create-pr-to-dev.yaml +11 -0
  5. github_rest_api-0.52.0/.github/workflows/create-pr-to-main.yaml +10 -0
  6. 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
  7. github_rest_api-0.52.0/.github/workflows/pr-workflow.yaml +34 -0
  8. github_rest_api-0.52.0/.github/workflows/release.yaml +12 -0
  9. github_rest_api-0.52.0/.github/workflows/remove-branch.yaml +9 -0
  10. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/PKG-INFO +3 -3
  11. github_rest_api-0.52.0/README.md +3 -0
  12. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/create_github_repo.py +116 -33
  13. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/upload_image_to_issue.py +3 -6
  14. github_rest_api-0.52.0/github_rest_api/scripts/lint_repo.py +791 -0
  15. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/pyproject.toml +2 -1
  16. github_rest_api-0.52.0/tests/test_create_github_repo.py +326 -0
  17. github_rest_api-0.52.0/tests/test_lint_repo.py +910 -0
  18. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_upload_image_to_issue.py +9 -8
  19. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/uv.lock +1 -1
  20. github_rest_api-0.50.0/.github/workflows/claude-code-review.yml +0 -67
  21. github_rest_api-0.50.0/.github/workflows/claude.yml +0 -50
  22. github_rest_api-0.50.0/.github/workflows/create_pr_to_main.yaml +0 -20
  23. github_rest_api-0.50.0/.github/workflows/lint.yaml +0 -34
  24. github_rest_api-0.50.0/.github/workflows/release.yaml +0 -22
  25. github_rest_api-0.50.0/.github/workflows/remove_branch.yaml +0 -18
  26. github_rest_api-0.50.0/.github/workflows/test.yaml +0 -31
  27. github_rest_api-0.50.0/README.md +0 -3
  28. github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/auto_merge_pull_request.yaml +0 -30
  29. github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/create_pr_dev_to_main.yaml +0 -19
  30. github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/create_pr_to_dev.yaml +0 -21
  31. github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/create_pr_to_main.yaml +0 -20
  32. github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/python/lint.yaml +0 -27
  33. github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/python/test.yaml +0 -31
  34. github_rest_api-0.50.0/github_rest_api/scripts/github/workflows/remove_branch.yaml +0 -18
  35. github_rest_api-0.50.0/tests/test_create_github_repo.py +0 -128
  36. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/.devcontainer/devcontainer.json +0 -0
  37. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/.gitignore +0 -0
  38. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/AGENTS.md +0 -0
  39. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/CLAUDE.md +0 -0
  40. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/__init__.py +0 -0
  41. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/github.py +0 -0
  42. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/pr_content.py +0 -0
  43. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/__init__.py +0 -0
  44. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/cargo/__init__.py +0 -0
  45. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/cargo/benchmark.py +0 -0
  46. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/cargo/profiling.py +0 -0
  47. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/cargo/utils.py +0 -0
  48. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/container/__init__.py +0 -0
  49. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/container/build_container_images.py +0 -0
  50. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/container/config_container.py +0 -0
  51. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/container/update_version_containerfile.py +0 -0
  52. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/__init__.py +0 -0
  53. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/auto_merge_pull_request.py +0 -0
  54. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/create_pull_request.py +0 -0
  55. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/release_on_github.py +0 -0
  56. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/github/remove_branch.py +0 -0
  57. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/scripts/utils.py +0 -0
  58. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/github_rest_api/utils.py +0 -0
  59. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/memory/MEMORY.md +0 -0
  60. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/memory/feedback_test_runner.md +0 -0
  61. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/__init__.py +0 -0
  62. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_auto_merge_pull_request.py +0 -0
  63. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_build_container_images.py +0 -0
  64. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_create_pull_request.py +0 -0
  65. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_github.py +0 -0
  66. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_pr_content.py +0 -0
  67. {github_rest_api-0.50.0 → github_rest_api-0.52.0}/tests/test_release_on_github.py +0 -0
  68. {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
@@ -0,0 +1,9 @@
1
+ name: Create PR From dev To main
2
+ on:
3
+ push:
4
+ branches:
5
+ - dev
6
+ jobs:
7
+ create_pr_dev_to_main:
8
+ uses: legendu-net/.github/.github/workflows/create-pr-dev-to-main.yaml@main
9
+ secrets: inherit
@@ -0,0 +1,11 @@
1
+ name: Create PR to dev
2
+ on:
3
+ push:
4
+ branches-ignore:
5
+ - main
6
+ - dev
7
+ - gh-pages*
8
+ jobs:
9
+ create_pr_to_dev:
10
+ uses: legendu-net/.github/.github/workflows/create-pr-to-dev.yaml@main
11
+ secrets: inherit
@@ -0,0 +1,10 @@
1
+ name: Create PR To main
2
+ on:
3
+ push:
4
+ branches-ignore:
5
+ - main
6
+ - _**
7
+ jobs:
8
+ create_pr_main:
9
+ uses: legendu-net/.github/.github/workflows/create-pr-to-main.yaml@main
10
+ secrets: inherit
@@ -1,4 +1,4 @@
1
- name: Auto Merge PR
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
- - Claude Code Review
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: auto-merge-pull-request
50
+ group: merge-pull-request
53
51
  cancel-in-progress: false
54
52
  jobs:
55
- auto_merge_pull_request:
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
- runs-on: ubuntu-latest
64
- # The concurrency group above serializes every run, so a hung run would
65
- # block all merges until the default 6h timeout.
66
- timeout-minutes: 10
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
@@ -0,0 +1,12 @@
1
+ name: Release
2
+ on:
3
+ release:
4
+ types: [published]
5
+ jobs:
6
+ release:
7
+ permissions:
8
+ contents: write
9
+ issues: write
10
+ pull-requests: write
11
+ uses: legendu-net/.github/.github/workflows/release.yaml@main
12
+ secrets: inherit
@@ -0,0 +1,9 @@
1
+ name: Remove Branches
2
+ on:
3
+ schedule:
4
+ - cron: '0 3 * * *'
5
+ timezone: 'America/Los_Angeles'
6
+ jobs:
7
+ remove_branch:
8
+ uses: legendu-net/.github/.github/workflows/remove-branch.yaml@main
9
+ secrets: inherit
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: github-rest-api
3
- Version: 0.50.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 | [@GitHub](https://github.com/legendu-net/github_rest_api) | [@PyPI](https://pypi.org/project/github-rest-api/)
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.
@@ -0,0 +1,3 @@
1
+ # GitHub REST APIs | [@GitHub](https://github.com/legendu-net/github_rest_api) | [@PyPI](https://pypi.org/project/github-rest-api/)
2
+
3
+ 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", _remote_url(repo, protocol))
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, language)
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 _add_workflow(path: Path, language: str, workflow_dir: Path | None = None) -> None:
183
- if workflow_dir is None:
184
- workflow_dir = Path(__file__).parent / "workflows"
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 yaml in workflow_dir.glob("*.yaml"):
188
- if not (dir_dest / yaml.name).exists():
189
- shutil.copy2(yaml, dir_dest)
190
- logger.info("Copied workflow %s to %s", yaml.name, dir_dest)
191
- if not language:
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, leaving stdout to the issue
512
- # number alone.
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
- issue_number = upload_image_to_issue(
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