Skip to content

Commit 2e7bbed

Browse files
committed
fix: address review findings and refactor module handling
- Refactor Python tooling to use shared Module dataclass * Created tools/models/module.py with centralized module schema * Added methods: from_dict, parse_modules, owner_repo, to_dict * Migrated scripts to use dataclass: update_module_latest.py, update_module_from_known_good.py, override_known_good_repo.py, known_good_to_workspace_metadata.py, get_module_info.py * Eliminates duplicate parsing logic across tools - Clean up override_known_good_repo.py * Renamed --repo-override to --module-override * Removed backward compatibility for deprecated flags * Extracted write_known_good() function for better reusability * Updated examples to reflect supported formats only - Improve workflow documentation * Clarified ref_int_repo and ref_int_ref semantics in smoke-test workflow * Added detailed usage notes for private fork support * Updated workflow steps to use --module-override flag * Enhanced input descriptions for runner label selection - Remove generated artifact from git * Deleted known_good.updated.json (build output)
1 parent 543c974 commit 2e7bbed

11 files changed

+388
-220
lines changed

.github/workflows/module-integration-build.yml renamed to .github/workflows/reusable_integration-build.yml

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,49 @@
1+
# Module Integration Build Workflow
2+
#
3+
# Summary:
4+
# Builds all modules referenced in known_good.json with Bazel and runs the
5+
# integration test script to validate module interoperability.
6+
#
7+
# What it does:
8+
# - Checks out the reference integration repository
9+
# - Updates score_modules.MODULE.bazel from the provided known_good.json
10+
# - Builds all referenced modules (via scripts/integration_test.sh and Bazel)
11+
# - Runs integration tests
12+
# - Uploads logs from _logs/ as artifact: bazel-build-logs-${{ inputs.config }}
13+
#
14+
# Inputs:
15+
# - known_good (string, required): JSON content used to pin module SHAs.
16+
# - config (string, optional, default: bl-x86_64-linux): Bazel config passed as
17+
# CONFIG to scripts/integration_test.sh.
18+
# - repo_runner_labels (string, optional): Runner label(s). Accepts either a
19+
# single label string (e.g., ubuntu-latest) or a JSON string representing a
20+
# label or an array of labels (e.g., "\"ubuntu-latest\"" or
21+
# "[\"self-hosted\",\"linux\",\"x64\"]").
22+
# - ref_int_repo (string, optional, default: eclipse-score/reference_integration):
23+
# Reference Integration repository to checkout.
24+
# - ref_int_ref (string, optional, default: main): Ref/branch to checkout.
25+
# - REPO_READ_TOKEN (secret, optional): Token for private module access; falls
26+
# back to github.token when not provided.
27+
#
28+
# Runner selection:
29+
# - Priority: `inputs.repo_runner_labels` > `vars.REPO_RUNNER_LABELS` > 'ubuntu-latest'.
30+
# - `repo_runner_labels` can be provided as a single label string or as JSON
31+
# (string or array). When JSON is used, it is parsed via fromJSON().
32+
#
33+
# Usage:
34+
# This workflow is reusable and triggered via workflow_call from other workflows.
35+
# Example:
36+
# jobs:
37+
# integration:
38+
# uses: eclipse-score/reference_integration/.github/workflows/module-integration-build.yml@main
39+
# with:
40+
# known_good: |
41+
# { "modules": [] }
42+
# config: bl-x86_64-linux
43+
# repo_runner_labels: '"ubuntu-latest"'
44+
# secrets:
45+
# REPO_READ_TOKEN: ${{ secrets.REPO_READ_TOKEN }}
46+
#
147
name: Module Integration Build
248

349
on:
@@ -7,10 +53,6 @@ on:
753
description: 'GitHub token with read access to the score modules. Defaults to github.token'
854
required: false
955
inputs:
10-
module_name:
11-
description: 'Name of the module to override (e.g., score_baselibs). If not provided, uses repository URL.'
12-
required: true
13-
type: string
1456
known_good:
1557
description: 'Content of the known_good.json file to use for the integration test.'
1658
required: true
@@ -20,6 +62,10 @@ on:
2062
required: false
2163
type: string
2264
default: 'bl-x86_64-linux'
65+
repo_runner_labels:
66+
description: 'Runner label(s) for the job; single label or JSON string/array.'
67+
required: false
68+
type: string
2369
ref_int_ref:
2470
description: 'Reference Integration repository ref to checkout.'
2571
required: false
@@ -34,7 +80,7 @@ on:
3480
jobs:
3581
integration-test:
3682
name: Integration Test
37-
runs-on: ubuntu-latest
83+
runs-on: ${{ inputs.repo_runner_labels && fromJSON(inputs.repo_runner_labels) || 'ubuntu-latest' }}
3884
steps:
3985
- name: Show disk space before build
4086
run: |
Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,58 @@
1+
# Module Smoke Test Workflow
2+
#
3+
# Summary:
4+
# Orchestrates a fast validation of a single module by producing an
5+
# updated known_good.json (optionally overriding one module to the current PR)
6+
# and then invoking the integration build workflow to compile and test.
7+
#
8+
# What it does:
9+
# - Checks out the reference integration repository
10+
# - Generates known_good.updated.json:
11+
# * If testing an external module PR: overrides `module_name` to the PR SHA
12+
# * If testing this repo: updates modules to latest branches
13+
# - Uploads known_good.updated.json as an artifact
14+
# - Calls the Module Integration Build workflow with a matrix of configs
15+
# - Publishes a summary to the GitHub Step Summary
16+
#
17+
# Inputs:
18+
# - repo_runner_labels (string, required, default: ubuntu-latest): Runner label.
19+
# - module_name (string, required): Module to override (e.g., score_baselibs).
20+
# - ref_int_repo (string, optional, default: eclipse-score/reference_integration):
21+
# The repository that provides the reusable integration workflows and tools.
22+
# Use your private fork/org if you mirror this repo (e.g., my-org/reference_integration).
23+
# Format: owner/repo
24+
# - ref_int_ref (string, required, default: main):
25+
# The ref on ref_int_repo to checkout via actions/checkout — can be a branch
26+
# name, tag, or commit SHA. This ensures the workflow uses the exact version
27+
# of the integration files you intend.
28+
#
29+
# Secrets:
30+
# - REPO_READ_TOKEN (optional): Token for reading private repos; falls back to
31+
# github.token when not provided.
32+
#
33+
# Usage:
34+
# This workflow is reusable and triggered via workflow_call from other workflows.
35+
# Example:
36+
# jobs:
37+
# smoke:
38+
# uses: eclipse-score/reference_integration/.github/workflows/module-smoke-test.yml@main
39+
# with:
40+
# repo_runner_labels: ubuntu-latest
41+
# module_name: score_baselibs
42+
# ref_int_ref: main
43+
# ref_int_repo: eclipse-score/reference_integration
44+
# secrets:
45+
# REPO_READ_TOKEN: ${{ secrets.REPO_READ_TOKEN }}
46+
#
47+
# Notes:
48+
# - Extend the matrix in `integration-test` to cover additional configs.
49+
150
name: Module Smoke Test
251

352
on:
453
workflow_call:
554
inputs:
6-
runner_tag:
55+
repo_runner_labels:
756
description: 'The runner tag to use for the job'
857
required: true
958
type: string
@@ -13,12 +62,12 @@ on:
1362
required: true
1463
type: string
1564
ref_int_ref:
16-
description: 'Reference Integration repository ref to checkout.'
65+
description: 'Ref on ref_int_repo to checkout (branch, tag, or commit SHA).'
1766
required: true
1867
type: string
1968
default: 'main'
2069
ref_int_repo:
21-
description: 'Reference Integration repository to use.'
70+
description: 'Repository providing integration workflows (owner/repo). Supports private forks.'
2271
required: false
2372
type: string
2473
default: 'eclipse-score/reference_integration'
@@ -48,7 +97,7 @@ jobs:
4897
python3 tools/override_known_good_repo.py \
4998
--known known_good.json \
5099
--output known_good.updated.json \
51-
--repo-override ${{ inputs.module_name }}@${{ github.event.repository.clone_url }}@${{ github.sha }}
100+
--module-override ${{ inputs.module_name }}@${{ github.event.repository.clone_url }}@${{ github.sha }}
52101
else
53102
echo "Testing reference integration repository itself - updating to latest commits"
54103
echo "::group::get latest commits from module branches"
@@ -92,13 +141,13 @@ jobs:
92141
# Add more configs here as needed
93142
# - bl-aarch64-linux
94143
# - bl-x86_64-qnx
95-
uses: ./.github/workflows/module-integration-build.yml
144+
uses: ./.github/workflows/reusable_integration-build.yml
96145
secrets:
97146
REPO_READ_TOKEN: ${{ secrets.REPO_READ_TOKEN != '' && secrets.REPO_READ_TOKEN || github.token }}
98147
with:
99-
module_name: ${{ inputs.module_name }}
100148
known_good: ${{ needs.preparation.outputs.known_good_updated }}
101149
config: ${{ matrix.config }}
150+
repo_runner_labels: ${{ inputs.repo_runner_labels }}
102151
ref_int_ref: ${{ inputs.ref_int_ref }}
103152
ref_int_repo: ${{ inputs.ref_int_repo }}
104153

.github/workflows/test_integration.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ on:
2323
# - cron: '30 2 * * *' # Every night at 02:30 UTC on main branch
2424
jobs:
2525
integration_test:
26-
uses: ./.github/workflows/module-smoke-test.yml
26+
uses: ./.github/workflows/reusable_smoke-test.yml
2727
secrets: inherit
2828
with:
2929
runner_tag: 'ubuntu-latest'

known_good.updated.json

Lines changed: 0 additions & 55 deletions
This file was deleted.

tools/get_module_info.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,66 @@
33

44
import json
55
import sys
6-
from typing import Dict, Any
6+
from typing import Optional
77

8+
from models import Module
89

9-
def load_module_data(known_good_file: str, module_name: str) -> Dict[str, Any]:
10+
11+
def load_module(known_good_file: str, module_name: str) -> Optional[Module]:
1012
"""
11-
Load module data from known_good.json.
13+
Load module from known_good.json.
1214
1315
Args:
1416
known_good_file: Path to the known_good.json file
1517
module_name: Name of the module to look up
1618
1719
Returns:
18-
Dictionary with module data, or empty dict if not found
20+
Module instance, or None if not found
1921
"""
2022
try:
2123
with open(known_good_file, 'r') as f:
2224
data = json.load(f)
23-
modules = data.get('modules', {})
24-
return modules.get(module_name, {})
25+
modules_dict = data.get('modules', {})
26+
module_data = modules_dict.get(module_name)
27+
28+
if not module_data:
29+
return None
30+
31+
return Module.from_dict(module_name, module_data)
2532
except Exception as e:
2633
# Log error to stderr for debugging
2734
print(f"Error loading {known_good_file}: {e}", file=sys.stderr)
28-
return {}
35+
return None
2936

3037

31-
def get_module_field(module_data: Dict[str, Any], field: str = 'hash') -> str:
38+
def get_module_field(module: Optional[Module], field: str = 'hash') -> str:
3239
"""
33-
Extract a specific field from module data.
40+
Extract a specific field from module.
3441
3542
Args:
36-
module_data: Dictionary with module information
43+
module: Module instance
3744
field: Field to extract ('hash', 'version', 'repo', or 'all')
3845
3946
Returns:
4047
Requested field value, or 'N/A' if not found
41-
For 'hash': truncated to 8 chars if longer
48+
For 'hash': returns the hash value
4249
For 'all': returns hash/version (prefers hash, falls back to version)
4350
"""
44-
if not module_data:
51+
if not module:
4552
return 'N/A'
4653

4754
if field == 'repo':
48-
repo = module_data.get('repo', 'N/A')
55+
repo = module.repo or 'N/A'
4956
# Remove .git suffix if present
5057
if repo.endswith('.git'):
5158
repo = repo[:-4]
5259
return repo
5360
elif field == 'version':
54-
return module_data.get('version', 'N/A')
61+
return module.version or 'N/A'
5562
elif field == 'hash':
56-
hash_val = module_data.get('hash', 'N/A')
57-
return hash_val
63+
return module.hash or 'N/A'
5864
else: # field == 'all' or default
59-
hash_val = module_data.get('hash', module_data.get('version', 'N/A'))
60-
return hash_val
65+
return module.hash or module.version or 'N/A'
6166

6267

6368
if __name__ == '__main__':
@@ -71,6 +76,6 @@ def get_module_field(module_data: Dict[str, Any], field: str = 'hash') -> str:
7176
module_name = sys.argv[2]
7277
field = sys.argv[3] if len(sys.argv) == 4 else 'all'
7378

74-
module_data = load_module_data(known_good_file, module_name)
75-
result = get_module_field(module_data, field)
79+
module = load_module(known_good_file, module_name)
80+
result = get_module_field(module, field)
7681
print(result)

tools/known_good_to_workspace_metadata.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import json
44
import csv
55

6+
from models import Module
7+
68
MODULES_CSV_HEADER = [
79
"repo_url",
810
"name",
@@ -22,25 +24,24 @@ def main():
2224
with open(args.known_good, "r") as f:
2325
data = json.load(f)
2426

25-
modules = data.get("modules", {})
27+
modules_dict = data.get("modules", {})
28+
29+
# Parse modules using Module dataclass
30+
modules = Module.parse_modules(modules_dict)
2631

2732
gita_metadata = []
28-
for name, info in modules.items():
29-
repo_url = info.get("repo", "")
30-
if not repo_url:
31-
raise RuntimeError("repo must not be empty")
32-
33-
# default branch: main
34-
branch = info.get("branch", "main")
33+
for module in modules:
34+
if not module.repo:
35+
raise RuntimeError(f"Module {module.name}: repo must not be empty")
3536

3637
# if no hash is given, use branch
37-
hash_ = info.get("hash", branch)
38+
hash_value = module.hash if module.hash else module.branch
3839

3940
# workspace_path is not available in known_good.json, default to name of repository
40-
workspace_path = name
41+
workspace_path = module.name
4142

4243
# gita format: {url},{name},{path},{prop['type']},{repo_flags},{branch}
43-
row = [repo_url, name, workspace_path, "", "", hash_]
44+
row = [module.repo, module.name, workspace_path, "", "", hash_value]
4445
gita_metadata.append(row)
4546

4647
with open(args.gita_workspace, "w", newline="") as f:

tools/models/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Models for score reference integration tools."""
2+
3+
from .module import Module
4+
5+
__all__ = ["Module"]

0 commit comments

Comments
 (0)