Skip to content

Commit

Permalink
[add] Reusable workflow to parse all apps manifest & build them all
Browse files Browse the repository at this point in the history
  • Loading branch information
lpascal-ledger committed Jan 21, 2025
1 parent fc9426d commit 947ce2e
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 0 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/reusable_build_several.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Building several apps, depending on given inputs

on:
workflow_call:
inputs:
sdk:
description: |
The type of SDK the apps use. Could be `c`, `rust` or `all` (defaults to `all`)
required: false
default: 'all'
type: string
sdk_reference:
description: |
Commit of the SDK to checkout.
If not specified, use the current app-builder SDK versions
required: false
default: 'None'
type: string
devices:
description: |
The list of device(s) the CI will run on. If an app does not implement a device, it will be ignored.
Defaults to the all the devices.
required: false
default: 'all'
type: string

jobs:

call_get_apps_metadata:
name: Rertieve applications metadata
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_get_apps_metadata.yml
with:
sdk: ${{ inputs.sdk }}
devices: ${{ inputs.devices }}

build_applications:
name: Build all selected applications using the reusable workflow
needs: call_get_apps_metadata
strategy:
fail-fast: false
matrix:
app: ${{ fromJSON(needs.call_get_apps_metadata.outputs.apps_metadata) }}
uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1
with:
app_repository: ${{ matrix.app.repository }}
run_for_devices: ${{ matrix.app.devices }}
upload_app_binaries_artifact: ${{ matrix.app.name }} # else will colide with other matrix' jobs
app_branch_name: ${{ matrix.app.default_branch }} # else takes the current repo default branch
builder: ledger-app-builder # needed for Rust applications
43 changes: 43 additions & 0 deletions .github/workflows/reusable_get_apps_metadata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Get apps metadatas

on:
workflow_call:
inputs:
sdk:
description: |
The type of SDK the apps use. Could be `c`, `rust` or `all` (defaults to `all`)
required: false
default: 'all'
type: string
devices:
description: |
The list of device(s) the CI will run on. If an app does not implement a device, it will be ignored.
Defaults to the all the devices.
As GitHub does not support (yet?) array as an input, this field must be filled as a string, with escaped
quotation marks. For instance: "[\"nanos+\", \"nanox\", \"stax\"]"
required: false
default: 'all'
type: string
outputs:
apps_metadata:
description: |
The metadata of all apps matching the given filters. The format will be:
[ { "default_branch": "main", "devices": "[\"nanos\", \"nanos+\", \"nanox\", \"flex\", \"stax\"]", "name": "app-boilerplate, "repository": "LedgerHQ/app-boilerplate }, ... ]
value: ${{ jobs.define_matrix.outputs.apps_config }}

jobs:

define_matrix:
name: Generate applications list for each device according to manifests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
pip install ledgered
- name: Define the list of application to build by device
id: process_devices
run: |
devices=$(python ./script/parse_all_apps.py -s ${{ inputs.sdk }} -d ${{ inputs.devices }} -t ${{ secrets.GITHUB_TOKEN }} -l 10)
echo "apps_config=$devices" >> $GITHUB_OUTPUT
100 changes: 100 additions & 0 deletions scripts/parse_all_apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import json
import logging
from argparse import ArgumentParser
from collections import defaultdict
from dataclasses import asdict, dataclass
from ledgered.github import AppRepository, Condition, GitHubLedgerHQ, GitHubApps, \
NoManifestException

LOGGER_FORMAT = "[%(asctime)s][%(levelname)s] %(name)s - %(message)s"
logging.root.handlers.clear()
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(LOGGER_FORMAT))
logging.root.addHandler(handler)
logging.root.setLevel(logging.INFO)

devices = ["nanos", "nanos+", "nanox", "flex", "stax"]


@dataclass
class AppInfo:
default_branch: str
devices: list[str]
name: str
repository: str

def __init__(self, app: AppRepository, filtered_devices: set[str]):
self.default_branch = app.default_branch
compatible_devices = app.manifest.app.devices
self.devices = '["' + '", "'.join(compatible_devices.intersection(filtered_devices)) + '"]'
self.name = app.name
self.repository = f"LedgerHQ/{app.name}"


def arg_parse():
parser = ArgumentParser()
parser.add_argument("-d", "--devices", nargs="+", required=True, choices=["nanos", "nanosp", "nanos+", "nanox", "flex", "stax", "all"],
help="Devices to filter on. Accepts several successive values (seperated with space). "

Check failure on line 37 in scripts/parse_all_apps.py

View workflow job for this annotation

GitHub Actions / Check misspellings

seperated ==> separated
"Valid values are 'nanos', 'nanosp', 'nanos+', 'nanox', 'stax', 'flex', 'all'.")
parser.add_argument("-l", "--limit", required=False, default=0, type=int,
help="Limit the number of application to parse (testing purpose for instance)")
parser.add_argument("-s", "--sdk", required=False, default="all", type=str, choices=["C", "c", "Rust", "rust", "all"],
help="SDK to filter on. Only apps using the SDK are selected. Defaults to 'all'")
parser.add_argument("-t", "--github_token", required=False, default="", type=str,
help="A GitHub token to avoid GH API limitation")

args = parser.parse_args()

selected_devices = list()
for d in args.devices:
if d.lower() == "nanosp":
d = "nanos+"
if d.lower() in devices:
selected_devices.append(d)
continue
if d.lower() == "all":
selected_devices = devices
break
logging.warning(f"Unkown device target '{d}'. Ignoring.")

Check failure on line 58 in scripts/parse_all_apps.py

View workflow job for this annotation

GitHub Actions / Check misspellings

Unkown ==> Unknown
args.devices = selected_devices

return args


def main():
args = arg_parse()

selected_devices = set(args.devices)

logging.info("Fetching application repositories from GitHub")
if args.github_token:
gh = GitHubLedgerHQ(args.github_token)
else:
gh = GitHubLedgerHQ()
apps = gh.apps.filter(archived=Condition.WITHOUT, private=Condition.WITHOUT)

selected_sdk: list[str]
if args.sdk.lower() == "all":
selected_sdk = ["c", "rust"]
else:
selected_sdk = [args.sdk.lower()]

selected_apps = list()
for index, app in enumerate(apps):
logging.info(f"Managing app '{app.name}'")
try:
if not app.manifest.app.sdk in selected_sdk:
logging.debug(f"Wrong SDK, ignoring this app")
continue
selected_apps.append(asdict(AppInfo(app, selected_devices)))
except NoManifestException:
logging.warning(f"Application '{app.name}' has no manifest! Ignoring.")

if args.limit != 0 and index > args.limit:
break

print(json.dumps(selected_apps))


if __name__ == "__main__":
main()

0 comments on commit 947ce2e

Please sign in to comment.