Skip to content

Commit 24140d9

Browse files
committed
feat: enhance manifest generation to support assets from file input
Signed-off-by: Adilhusain Shaikh <[email protected]>
1 parent 4af40c7 commit 24140d9

File tree

3 files changed

+170
-3
lines changed

3 files changed

+170
-3
lines changed

.github/scripts/generate_partial_manifest.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,30 @@ def main() -> int:
111111
parser.add_argument("--tag", required=True, help="Release tag used for the manifest version field.")
112112
parser.add_argument("--owner", required=True, help="GitHub repository owner (e.g., 'IBM').")
113113
parser.add_argument("--repo", required=True, help="GitHub repository name (e.g., 'python-versions-pz').")
114-
parser.add_argument("--assets", required=True, help="JSON string describing release assets from GitHub API.")
114+
115+
# Mutually exclusive group: Accept EITHER string OR file
116+
group = parser.add_mutually_exclusive_group(required=True)
117+
group.add_argument("--assets", help="Raw JSON string (Legacy/Direct Input)")
118+
group.add_argument("--assets-file", help="Path to JSON file containing assets (Recommended for CI)")
119+
115120
args = parser.parse_args()
116121

122+
# Logic to load assets from either source
123+
assets = []
117124
try:
118-
assets = json.loads(args.assets)
125+
if args.assets_file:
126+
print(f"Reading assets from file: {args.assets_file}", file=sys.stderr)
127+
with open(args.assets_file, 'r', encoding='utf-8') as f:
128+
assets = json.load(f)
129+
elif args.assets:
130+
# Legacy support for workflows passing raw strings
131+
print(f"Parsing assets from command-line string", file=sys.stderr)
132+
assets = json.loads(args.assets)
119133
except json.JSONDecodeError as exc:
120-
print(f"Invalid JSON for --assets: {exc}", file=sys.stderr)
134+
print(f"Error decoding JSON: {exc}", file=sys.stderr)
135+
return 1
136+
except FileNotFoundError as exc:
137+
print(f"Assets file not found: {args.assets_file}", file=sys.stderr)
121138
return 1
122139

123140
manifest_entries, errors = build_manifest_entries(args.tag, assets, args.owner, args.repo)
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Backfill Manifests
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
limit:
7+
description: "Number of releases to process (Max 250 due to GHA matrix limits)"
8+
required: false
9+
default: "30"
10+
type: string
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
get-all-tags:
21+
runs-on: ubuntu-latest
22+
outputs:
23+
matrix: ${{ steps.set-matrix.outputs.matrix }}
24+
steps:
25+
- name: Fetch release tags
26+
id: set-matrix
27+
env:
28+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29+
LIMIT: ${{ inputs.limit }}
30+
run: |
31+
# Safety Check: Matrix strategy has a hard limit of 256 jobs
32+
if [ "$LIMIT" -gt 250 ]; then
33+
echo "::warning::Limit $LIMIT exceeds GitHub Matrix safety. Capping at 250."
34+
LIMIT=250
35+
fi
36+
37+
echo "Fetching last $LIMIT releases..."
38+
39+
TAGS=$(gh release list \
40+
--limit "$LIMIT" \
41+
--exclude-drafts \
42+
--exclude-pre-releases \
43+
--json tagName \
44+
--jq '[.[] | .tagName]')
45+
46+
if [ -z "$TAGS" ] || [ "$TAGS" == "[]" ]; then
47+
echo "::error::No release tags found!"
48+
exit 1
49+
fi
50+
51+
echo "Found tags count: $(echo $TAGS | jq '. | length')"
52+
echo "matrix=$TAGS" >> $GITHUB_OUTPUT
53+
54+
generate-manifests:
55+
needs: get-all-tags
56+
runs-on: ubuntu-latest
57+
strategy:
58+
fail-fast: false
59+
max-parallel: 10 # Slightly higher parallelization is fine for reads
60+
matrix:
61+
tag: ${{ fromJson(needs.get-all-tags.outputs.matrix) }}
62+
steps:
63+
- name: Checkout repository
64+
uses: actions/checkout@v4
65+
66+
- name: Set up Python 3.13
67+
uses: actions/setup-python@v5
68+
with:
69+
python-version: "3.13"
70+
cache: "poetry"
71+
72+
- name: Install dependencies
73+
run: |
74+
pip install poetry
75+
poetry install --no-interaction --no-ansi
76+
77+
- name: Fetch Assets Data
78+
env:
79+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
80+
run: |
81+
echo "::group::Fetch Assets via GitHub CLI"
82+
# Write JSON directly to file to bypass shell ARG_MAX limits
83+
gh release view "${{ matrix.tag }}" --json assets --jq '.assets' > assets.json
84+
85+
if [ ! -s assets.json ]; then
86+
echo "::error::Assets file is empty for tag ${{ matrix.tag }}"
87+
exit 1
88+
fi
89+
echo "::endgroup::"
90+
91+
- name: Generate Manifest
92+
env:
93+
REPO_NAME: ${{ github.repository }}
94+
REPO_NAME: ${{ env.REPO_NAME }}
95+
OWNER: ${{ github.repository_owner }}
96+
run: |
97+
echo "::group::Run Generation Script"
98+
99+
REPO_NAME="${{ github.repository }}" && REPO_NAME="${REPO_NAME##*/}"
100+
101+
# We use --assets-file now instead of --assets
102+
poetry run python .github/scripts/generate_partial_manifest.py \
103+
--tag "${{ matrix.tag }}" \
104+
--owner "${{ github.repository_owner }}" \
105+
--repo "${REPO_NAME}" \
106+
--assets-file "assets.json" \
107+
> manifest-part-${{ matrix.tag }}.json
108+
109+
cat manifest-part-${{ matrix.tag }}.json
110+
echo "::endgroup::"
111+
112+
- name: Upload Manifest Artifact
113+
uses: actions/upload-artifact@v4
114+
with:
115+
name: manifest-part-${{ matrix.tag }}
116+
path: manifest-part-${{ matrix.tag }}.json
117+
retention-days: 1

tests/test_generate_partial_manifest.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,39 @@ def test_main_outputs_manifest(monkeypatch, capsys):
122122
assert payload[0]["download_url"].endswith("python-3.13.3-linux-22.04-ppc64le.tar.gz")
123123

124124

125+
def test_main_outputs_manifest_from_file(monkeypatch, capsys, tmp_path):
126+
"""Test that main() reads from --assets-file correctly."""
127+
assets = [
128+
{
129+
"name": "python-3.13.3-linux-22.04-ppc64le.tar.gz",
130+
"browser_download_url": "https://github.com/IBM/python-versions-pz/releases/download/3.13.3/python-3.13.3-linux-22.04-ppc64le.tar.gz",
131+
}
132+
]
133+
134+
assets_file = tmp_path / "assets.json"
135+
assets_file.write_text(json.dumps(assets), encoding='utf-8')
136+
137+
args = [
138+
"generate_partial_manifest.py",
139+
"--tag",
140+
"3.13.3",
141+
"--owner",
142+
"IBM",
143+
"--repo",
144+
"python-versions-pz",
145+
"--assets-file",
146+
str(assets_file),
147+
]
148+
monkeypatch.setattr(sys, "argv", args)
149+
150+
exit_code = gpm.main()
151+
assert exit_code == 0
152+
153+
captured = capsys.readouterr()
154+
payload = json.loads(captured.out)
155+
assert payload[0]["download_url"].endswith("python-3.13.3-linux-22.04-ppc64le.tar.gz")
156+
157+
125158
def test_main_rejects_invalid_urls(monkeypatch, capsys):
126159
"""Test that main() rejects invalid URLs and returns error code."""
127160
assets = [

0 commit comments

Comments
 (0)