autopkg-wrapper 2025.11.1__tar.gz → 2026.2.2__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 (36) hide show
  1. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/.github/workflows/build-publish.yml +8 -9
  2. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/.github/workflows/codeql.yml +3 -3
  3. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/.github/workflows/dependency-review.yml +1 -1
  4. autopkg_wrapper-2026.2.2/PKG-INFO +105 -0
  5. autopkg_wrapper-2026.2.2/README.md +86 -0
  6. autopkg_wrapper-2026.2.2/actions-demo/requirements.txt +3 -0
  7. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/autopkg_wrapper/autopkg_wrapper.py +54 -16
  8. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/autopkg_wrapper/utils/args.py +43 -0
  9. autopkg_wrapper-2026.2.2/autopkg_wrapper/utils/report_processor.py +674 -0
  10. autopkg_wrapper-2026.2.2/mise.toml +3 -0
  11. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/pyproject.toml +3 -2
  12. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/uv.lock +126 -49
  13. autopkg_wrapper-2025.11.1/.tool-versions +0 -1
  14. autopkg_wrapper-2025.11.1/PKG-INFO +0 -54
  15. autopkg_wrapper-2025.11.1/README.md +0 -36
  16. autopkg_wrapper-2025.11.1/actions-demo/requirements.txt +0 -3
  17. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/.github/dependabot.yml +0 -0
  18. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/.gitignore +0 -0
  19. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/.pre-commit-config.yaml +0 -0
  20. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/CONTRIBUTING +0 -0
  21. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/LICENSE +0 -0
  22. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/actions-demo/.github/workflows/autopkg-wrapper-demo.yml +0 -0
  23. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/actions-demo/overrides/Google_Chrome.pkg.recipe.yaml +0 -0
  24. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/actions-demo/repo_list.txt +0 -0
  25. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/autopkg_wrapper/__init__.py +0 -0
  26. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/autopkg_wrapper/notifier/__init__.py +0 -0
  27. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/autopkg_wrapper/notifier/slack.py +0 -0
  28. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/autopkg_wrapper/utils/__init__.py +0 -0
  29. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/autopkg_wrapper/utils/git_functions.py +0 -0
  30. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/autopkg_wrapper/utils/logging.py +0 -0
  31. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/tests/__init__.py +0 -0
  32. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/tests/prefs.json +0 -0
  33. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/tests/prefs.plist +0 -0
  34. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/tests/recipe_list.json +0 -0
  35. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/tests/recipe_list.txt +0 -0
  36. {autopkg_wrapper-2025.11.1 → autopkg_wrapper-2026.2.2}/tests/recipe_list.yaml +0 -0
@@ -34,7 +34,6 @@ jobs:
34
34
  contents: write
35
35
 
36
36
  steps:
37
- # - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
38
37
  - id: check-inputs
39
38
  env:
40
39
  INPUT_DRY_RUN: ${{ github.event.inputs.dry_run }}
@@ -104,17 +103,17 @@ jobs:
104
103
  id-token: write
105
104
 
106
105
  steps:
107
- - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
106
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
108
107
 
109
108
  - name: Setup UV
110
- uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
109
+ uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1
111
110
  with:
112
111
  activate-environment: true
113
112
  enable-cache: true
114
113
  cache-dependency-glob: uv.lock
115
114
 
116
115
  - name: Setup Python
117
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
116
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
118
117
  with:
119
118
  python-version-file: pyproject.toml
120
119
 
@@ -123,7 +122,7 @@ jobs:
123
122
  uv version ${{ needs.release.outputs.version }}
124
123
  uv build
125
124
  - name: Upload Package Artifacts
126
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
125
+ uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
127
126
  with:
128
127
  name: python-package-distributions
129
128
  path: dist/
@@ -142,7 +141,7 @@ jobs:
142
141
 
143
142
  steps:
144
143
  - name: Download Package Artifacts
145
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
144
+ uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
146
145
  with:
147
146
  name: python-package-distributions
148
147
  path: dist/
@@ -166,7 +165,7 @@ jobs:
166
165
 
167
166
  steps:
168
167
  - name: Download Package Artifacts
169
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
168
+ uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
170
169
  with:
171
170
  name: python-package-distributions
172
171
  path: dist/
@@ -185,11 +184,11 @@ jobs:
185
184
 
186
185
  steps:
187
186
  - name: Download Package Artifacts
188
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
187
+ uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
189
188
  with:
190
189
  name: python-package-distributions
191
190
  path: dist/
192
- - uses: sigstore/gh-action-sigstore-python@f832326173235dcb00dd5d92cd3f353de3188e6c # v3.1.0
191
+ - uses: sigstore/gh-action-sigstore-python@a5caf349bc536fbef3668a10ed7f5cd309a4b53d # v3.2.0
193
192
  with:
194
193
  inputs: |
195
194
  dist/*.whl
@@ -27,14 +27,14 @@ jobs:
27
27
 
28
28
  steps:
29
29
  - name: Checkout repository
30
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
30
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
31
31
 
32
32
  - name: Initialize CodeQL
33
- uses: github/codeql-action/init@382a50a0284c0de445104889a9d6003acb4b3c1d # v2.15.4
33
+ uses: github/codeql-action/init@25a224b8085c21d4d61b7fc051468805fc3ac490 # codeql-bundle-v2.24.0
34
34
  with:
35
35
  languages: ${{ matrix.language }}
36
36
 
37
37
  - name: Perform CodeQL Analysis
38
- uses: github/codeql-action/analyze@382a50a0284c0de445104889a9d6003acb4b3c1d # v2.15.4
38
+ uses: github/codeql-action/analyze@25a224b8085c21d4d61b7fc051468805fc3ac490 # codeql-bundle-v2.24.0
39
39
  with:
40
40
  category: "/language:${{matrix.language}}"
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - name: "Checkout Repository"
14
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
14
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
15
15
 
16
16
  - name: "Dependency Review"
17
17
  uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: autopkg-wrapper
3
+ Version: 2026.2.2
4
+ Summary: A package used to execute some autopkg functions, primarily within the context of a GitHub Actions runner.
5
+ Project-URL: Repository, https://github.com/smithjw/autopkg-wrapper
6
+ Author-email: James Smith <james@smithjw.me>
7
+ License-Expression: BSD-3-Clause
8
+ License-File: LICENSE
9
+ Requires-Python: ~=3.14.0
10
+ Requires-Dist: chardet
11
+ Requires-Dist: idna
12
+ Requires-Dist: jamf-pro-sdk
13
+ Requires-Dist: pygithub
14
+ Requires-Dist: requests
15
+ Requires-Dist: ruamel-yaml
16
+ Requires-Dist: toml
17
+ Requires-Dist: urllib3
18
+ Description-Content-Type: text/markdown
19
+
20
+ # autopkg-wrapper
21
+
22
+ `autopkg_wrapper` is a small package that can be used to run [`autopkg`](https://github.com/autopkg/autopkg) within CI/CD environments such as GitHub Actions.
23
+
24
+ The easiest way to run it is by installing with pip.
25
+
26
+ ```shell
27
+ pip install autopkg-wrapper
28
+ ```
29
+
30
+ ## Command Line Parameters
31
+
32
+ ```shell
33
+ -h, --help Show this help message and exit
34
+ --recipe-file RECIPE_FILE Path to a list of recipes to run (cannot be run with --recipes)
35
+ --recipes [RECIPES ...] Recipes to run with autopkg (cannot be run with --recipe-file)
36
+ --debug Enable debug logging when running script
37
+ --disable-recipe-trust-check If this option is used, recipe trust verification will not be run prior to a recipe run.
38
+ --github-token GITHUB_TOKEN A token used to publish a PR to your GitHub repo if overrides require their trust to be updated
39
+ --branch-name BRANCH_NAME Branch name to be used where recipe overrides have failed their trust verification and need to be updated.
40
+ By default, this will be in the format of "fix/update_trust_information/YYYY-MM-DDTHH-MM-SS"
41
+ --create-pr If enabled, autopkg_wrapper will open a PR for updated trust information
42
+ --create-issues Create a GitHub issue for recipes that fail during processing
43
+ --disable-git-commands If this option is used, git commands won't be run
44
+ --post-processors [POST_PROCESSORS ...]
45
+ One or more autopkg post processors to run after each recipe execution
46
+ --autopkg-prefs AW_AUTOPKG_PREFS_FILE
47
+ Path to the autopkg preferences you'd like to use
48
+ --overrides-repo-path AUTOPKG_OVERRIDES_REPO_PATH
49
+ The path on disk to the git repository containing the autopkg overrides directory. If none is provided, we will try to determine it for you.
50
+ --concurrency CONCURRENCY Number of recipes to run in parallel (default: 1)
51
+ --process-reports Process autopkg report directories or zip and emit markdown summaries (runs after recipes complete)
52
+ --reports-zip REPORTS_ZIP Path to an autopkg_report-*.zip to extract and process
53
+ --reports-extract-dir REPORTS_EXTRACT_DIR
54
+ Directory to extract the zip into (default: autopkg_reports_summary/reports)
55
+ --reports-dir REPORTS_DIR Directory of reports to process (if no zip provided). Defaults to /private/tmp/autopkg when processing after a run
56
+ --reports-out-dir REPORTS_OUT_DIR
57
+ Directory to write markdown outputs (default: autopkg_reports_summary/summary)
58
+ --reports-run-date REPORTS_RUN_DATE
59
+ Run date string to include in the summary
60
+ --reports-strict Exit non-zero if any errors are detected in processed reports
61
+ ```
62
+
63
+ ## Examples
64
+
65
+ Run recipes (serial):
66
+
67
+ ```bash
68
+ autopkg_wrapper --recipes Foo.download Bar.download
69
+ ```
70
+
71
+ Run 3 recipes concurrently and process reports afterward:
72
+
73
+ ```bash
74
+ autopkg_wrapper \
75
+ --recipe-file /path/to/recipe_list.txt \
76
+ --concurrency 3 \
77
+ --disable-git-commands \
78
+ --process-reports \
79
+ --reports-out-dir /tmp/autopkg_reports_summary \
80
+ --reports-strict
81
+ ```
82
+
83
+ Process a reports zip explicitly (no recipe run):
84
+
85
+ ```bash
86
+ autopkg_wrapper \
87
+ --process-reports \
88
+ --reports-zip /path/to/autopkg_report-2026-02-02.zip \
89
+ --reports-extract-dir /tmp/autopkg_reports \
90
+ --reports-out-dir /tmp/autopkg_reports_summary
91
+ ```
92
+
93
+ Notes:
94
+
95
+ - During recipe runs, per‑recipe plist reports are written to `/private/tmp/autopkg`.
96
+ - When `--process-reports` is supplied without `--reports-zip` or `--reports-dir`, the tool processes `/private/tmp/autopkg`.
97
+ - If `AUTOPKG_JSS_URL`, `AUTOPKG_CLIENT_ID`, and `AUTOPKG_CLIENT_SECRET` are set, uploaded package rows are enriched with Jamf package links.
98
+ - No extra CLI flag is required; enrichment runs automatically when all three env vars are present.
99
+
100
+ An example folder structure and GitHub Actions Workflow is available within the [`actions-demo`](actions-demo)
101
+
102
+ ## Credits
103
+
104
+ - [`autopkg_tools` from Facebook](https://github.com/facebook/IT-CPE/tree/main/legacy/autopkg_tools)
105
+ - [`autopkg_tools` from Facebook, modified by Gusto](https://github.com/Gusto/it-cpe-opensource/tree/main/autopkg)
@@ -0,0 +1,86 @@
1
+ # autopkg-wrapper
2
+
3
+ `autopkg_wrapper` is a small package that can be used to run [`autopkg`](https://github.com/autopkg/autopkg) within CI/CD environments such as GitHub Actions.
4
+
5
+ The easiest way to run it is by installing with pip.
6
+
7
+ ```shell
8
+ pip install autopkg-wrapper
9
+ ```
10
+
11
+ ## Command Line Parameters
12
+
13
+ ```shell
14
+ -h, --help Show this help message and exit
15
+ --recipe-file RECIPE_FILE Path to a list of recipes to run (cannot be run with --recipes)
16
+ --recipes [RECIPES ...] Recipes to run with autopkg (cannot be run with --recipe-file)
17
+ --debug Enable debug logging when running script
18
+ --disable-recipe-trust-check If this option is used, recipe trust verification will not be run prior to a recipe run.
19
+ --github-token GITHUB_TOKEN A token used to publish a PR to your GitHub repo if overrides require their trust to be updated
20
+ --branch-name BRANCH_NAME Branch name to be used where recipe overrides have failed their trust verification and need to be updated.
21
+ By default, this will be in the format of "fix/update_trust_information/YYYY-MM-DDTHH-MM-SS"
22
+ --create-pr If enabled, autopkg_wrapper will open a PR for updated trust information
23
+ --create-issues Create a GitHub issue for recipes that fail during processing
24
+ --disable-git-commands If this option is used, git commands won't be run
25
+ --post-processors [POST_PROCESSORS ...]
26
+ One or more autopkg post processors to run after each recipe execution
27
+ --autopkg-prefs AW_AUTOPKG_PREFS_FILE
28
+ Path to the autopkg preferences you'd like to use
29
+ --overrides-repo-path AUTOPKG_OVERRIDES_REPO_PATH
30
+ The path on disk to the git repository containing the autopkg overrides directory. If none is provided, we will try to determine it for you.
31
+ --concurrency CONCURRENCY Number of recipes to run in parallel (default: 1)
32
+ --process-reports Process autopkg report directories or zip and emit markdown summaries (runs after recipes complete)
33
+ --reports-zip REPORTS_ZIP Path to an autopkg_report-*.zip to extract and process
34
+ --reports-extract-dir REPORTS_EXTRACT_DIR
35
+ Directory to extract the zip into (default: autopkg_reports_summary/reports)
36
+ --reports-dir REPORTS_DIR Directory of reports to process (if no zip provided). Defaults to /private/tmp/autopkg when processing after a run
37
+ --reports-out-dir REPORTS_OUT_DIR
38
+ Directory to write markdown outputs (default: autopkg_reports_summary/summary)
39
+ --reports-run-date REPORTS_RUN_DATE
40
+ Run date string to include in the summary
41
+ --reports-strict Exit non-zero if any errors are detected in processed reports
42
+ ```
43
+
44
+ ## Examples
45
+
46
+ Run recipes (serial):
47
+
48
+ ```bash
49
+ autopkg_wrapper --recipes Foo.download Bar.download
50
+ ```
51
+
52
+ Run 3 recipes concurrently and process reports afterward:
53
+
54
+ ```bash
55
+ autopkg_wrapper \
56
+ --recipe-file /path/to/recipe_list.txt \
57
+ --concurrency 3 \
58
+ --disable-git-commands \
59
+ --process-reports \
60
+ --reports-out-dir /tmp/autopkg_reports_summary \
61
+ --reports-strict
62
+ ```
63
+
64
+ Process a reports zip explicitly (no recipe run):
65
+
66
+ ```bash
67
+ autopkg_wrapper \
68
+ --process-reports \
69
+ --reports-zip /path/to/autopkg_report-2026-02-02.zip \
70
+ --reports-extract-dir /tmp/autopkg_reports \
71
+ --reports-out-dir /tmp/autopkg_reports_summary
72
+ ```
73
+
74
+ Notes:
75
+
76
+ - During recipe runs, per‑recipe plist reports are written to `/private/tmp/autopkg`.
77
+ - When `--process-reports` is supplied without `--reports-zip` or `--reports-dir`, the tool processes `/private/tmp/autopkg`.
78
+ - If `AUTOPKG_JSS_URL`, `AUTOPKG_CLIENT_ID`, and `AUTOPKG_CLIENT_SECRET` are set, uploaded package rows are enriched with Jamf package links.
79
+ - No extra CLI flag is required; enrichment runs automatically when all three env vars are present.
80
+
81
+ An example folder structure and GitHub Actions Workflow is available within the [`actions-demo`](actions-demo)
82
+
83
+ ## Credits
84
+
85
+ - [`autopkg_tools` from Facebook](https://github.com/facebook/IT-CPE/tree/main/legacy/autopkg_tools)
86
+ - [`autopkg_tools` from Facebook, modified by Gusto](https://github.com/Gusto/it-cpe-opensource/tree/main/autopkg)
@@ -0,0 +1,3 @@
1
+ autopkg-wrapper==2025.11.1 \
2
+ --hash=sha256:1f6e650fb386f7bc010a02be7fc019d90b9a6ebab9b37638492fde1deb962d87 \
3
+ --hash=sha256:7304c6aa7a944ad86526583ec89115d704b49fe450d0bf4f2b4f7a2ab2aa57d6
@@ -4,6 +4,7 @@ import logging
4
4
  import plistlib
5
5
  import subprocess
6
6
  import sys
7
+ from concurrent.futures import ThreadPoolExecutor, as_completed
7
8
  from datetime import datetime
8
9
  from itertools import chain
9
10
  from pathlib import Path
@@ -12,6 +13,7 @@ import autopkg_wrapper.utils.git_functions as git
12
13
  from autopkg_wrapper.notifier import slack
13
14
  from autopkg_wrapper.utils.args import setup_args
14
15
  from autopkg_wrapper.utils.logging import setup_logger
16
+ from autopkg_wrapper.utils.report_processor import process_reports
15
17
 
16
18
 
17
19
  class Recipe(object):
@@ -94,7 +96,7 @@ class Recipe(object):
94
96
  self.results["failed"] = True
95
97
  self.results["imported"] = ""
96
98
  else:
97
- report_dir = Path("/tmp/autopkg")
99
+ report_dir = Path("/private/tmp/autopkg")
98
100
  report_time = datetime.now().strftime("%Y-%m-%dT%H-%M-%S")
99
101
  report_name = Path(f"{self.name}-{report_time}.plist")
100
102
 
@@ -358,31 +360,50 @@ def main():
358
360
 
359
361
  failed_recipes = []
360
362
 
361
- for recipe in recipe_list:
362
- logging.info(f"Processing Recipe: {recipe.name}")
363
+ # Run recipes concurrently using a thread pool to parallelize subprocess calls
364
+ max_workers = max(1, int(getattr(args, "concurrency", 1)))
365
+ logging.info(f"Running recipes with concurrency={max_workers}")
366
+
367
+ def run_one(r: Recipe):
368
+ logging.info(f"Processing Recipe: {r.name}")
363
369
  process_recipe(
364
- recipe=recipe,
370
+ recipe=r,
365
371
  disable_recipe_trust_check=args.disable_recipe_trust_check,
366
372
  args=args,
367
373
  )
374
+ # Git updates and notifications are applied serially after all recipes finish
375
+ return r
376
+
377
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
378
+ futures = [executor.submit(run_one, r) for r in recipe_list]
379
+ for fut in as_completed(futures):
380
+ r = fut.result()
381
+ if r.error or r.results.get("failed"):
382
+ failed_recipes.append(r)
383
+
384
+ # Apply git updates serially to avoid branch/commit conflicts when concurrency > 1
385
+ for r in recipe_list:
368
386
  update_recipe_repo(
369
387
  git_info=override_repo_info,
370
- recipe=recipe,
388
+ recipe=r,
371
389
  disable_recipe_trust_check=args.disable_recipe_trust_check,
372
390
  args=args,
373
391
  )
374
- slack.send_notification(
375
- recipe=recipe, token=args.slack_token
376
- ) if args.slack_token else None
377
-
378
- if recipe.error or recipe.results.get("failed"):
379
- failed_recipes.append(recipe)
380
392
 
381
- recipe.pr_url = (
382
- git.create_pull_request(git_info=override_repo_info, recipe=recipe)
383
- if args.create_pr
384
- else None
385
- )
393
+ # Send notifications serially to simplify rate limiting and ordering
394
+ if args.slack_token:
395
+ for r in recipe_list:
396
+ slack.send_notification(recipe=r, token=args.slack_token)
397
+
398
+ # Optionally open a PR for updated trust information
399
+ if args.create_pr and recipe_list:
400
+ # Choose a representative recipe for the PR title/body
401
+ rep_recipe = next(
402
+ (r for r in recipe_list if r.updated is True or r.verified is False),
403
+ recipe_list[0],
404
+ )
405
+ pr_url = git.create_pull_request(git_info=override_repo_info, recipe=rep_recipe)
406
+ logging.info(f"Created Pull Request for trust info updates: {pr_url}")
386
407
 
387
408
  # Create GitHub issue for failed recipes
388
409
  if args.create_issues and failed_recipes and args.github_token:
@@ -390,3 +411,20 @@ def main():
390
411
  git_info=override_repo_info, failed_recipes=failed_recipes
391
412
  )
392
413
  logging.info(f"Created GitHub issue for failed recipes: {issue_url}")
414
+
415
+ # Optionally process reports after running recipes
416
+ if getattr(args, "process_reports", False):
417
+ rc = process_reports(
418
+ zip_file=getattr(args, "reports_zip", None),
419
+ extract_dir=getattr(
420
+ args, "reports_extract_dir", "autopkg_reports_summary/reports"
421
+ ),
422
+ reports_dir=(getattr(args, "reports_dir", None) or "/private/tmp/autopkg"),
423
+ environment="",
424
+ run_date=getattr(args, "reports_run_date", ""),
425
+ out_dir=getattr(args, "reports_out_dir", "autopkg_reports_summary/summary"),
426
+ debug=bool(getattr(args, "debug", False)),
427
+ strict=bool(getattr(args, "reports_strict", False)),
428
+ )
429
+ if rc:
430
+ sys.exit(rc)
@@ -90,6 +90,12 @@ def setup_args():
90
90
  If this option is used, git commands won't be run
91
91
  """,
92
92
  )
93
+ parser.add_argument(
94
+ "--concurrency",
95
+ type=int,
96
+ default=int(os.getenv("AW_CONCURRENCY", "1")),
97
+ help="Number of recipes to run in parallel (default: 1)",
98
+ )
93
99
  parser.add_argument(
94
100
  "--slack-token",
95
101
  default=os.getenv("SLACK_WEBHOOK_TOKEN", None),
@@ -144,4 +150,41 @@ def setup_args():
144
150
  """,
145
151
  )
146
152
 
153
+ # Report processing options
154
+ parser.add_argument(
155
+ "--process-reports",
156
+ action="store_true",
157
+ help="Process autopkg report directories or zip and emit markdown summaries",
158
+ )
159
+ parser.add_argument(
160
+ "--reports-zip",
161
+ default=os.getenv("AW_REPORTS_ZIP", None),
162
+ help="Path to an autopkg_report-*.zip to extract and process",
163
+ )
164
+ parser.add_argument(
165
+ "--reports-extract-dir",
166
+ default=os.getenv("AW_REPORTS_EXTRACT_DIR", "autopkg_reports_summary/reports"),
167
+ help="Directory to extract the zip into (default: autopkg_reports_summary/reports)",
168
+ )
169
+ parser.add_argument(
170
+ "--reports-dir",
171
+ default=os.getenv("AW_REPORTS_DIR", None),
172
+ help="Directory of reports to process (if no zip provided)",
173
+ )
174
+ parser.add_argument(
175
+ "--reports-out-dir",
176
+ default=os.getenv("AW_REPORTS_OUT_DIR", "autopkg_reports_summary/summary"),
177
+ help="Directory to write markdown outputs (default: autopkg_reports_summary/summary)",
178
+ )
179
+ parser.add_argument(
180
+ "--reports-run-date",
181
+ default=os.getenv("AW_REPORTS_RUN_DATE", ""),
182
+ help="Run date string to include in the summary",
183
+ )
184
+ parser.add_argument(
185
+ "--reports-strict",
186
+ action="store_true",
187
+ help="Exit non-zero if any errors are detected in processed reports",
188
+ )
189
+
147
190
  return parser.parse_args()