Skip to content

forge: ship a licence with every build.sh wheel #372

forge: ship a licence with every build.sh wheel

forge: ship a licence with every build.sh wheel #372

Workflow file for this run

name: Build and Publish wheels
on:
push:
pull_request:
workflow_dispatch:
inputs:
archs:
description: |
Architectures (comma-separated):
required: false
default: "android,iOS"
packages:
description: |
Packages (comma-separated) or 'ALL' to select all available recipes:
required: false
default: "pydantic-core:,numpy:,pillow:"
prebuild_recipes:
description: |
Recipes (comma-separated; typically flet-lib*) to build FIRST and seed into
the matrix's dist/ for host-dep pip resolution (e.g. flet-libgdal -> gdal):
required: false
default: ""
python_versions:
description: |
Python versions to fan the matrix across (comma-separated):
required: false
default: "3.12,3.13,3.14"
mobile_test_pythons:
description: |
Python versions (comma-separated) whose recipe mobile tests should actually run.
Versions in `python_versions` that aren't in this list still build wheels but skip the
APK/iOS-simulator test stage. Use "ALL" to run tests for every entry in `python_versions`.
Pass an empty string to build wheels WITHOUT any on-device tests (build-only deploy):
required: false
default: "3.12"
python_build_run_id:
description: |
flet-dev/python-build Actions run-id whose artifacts to use as the
python-build support tree, instead of the pinned date-keyed release tarball:
required: false
default: ""
serious_python_ref:
description: |
Git ref (branch/tag/SHA) of flet-dev/serious-python to pin the
recipe-tester's serious_python (+ platform impls) to, so on-device tests
validate against an UNRELEASED sp fix. Empty = use the published
serious_python. Replaces the old hardcoded override in the pyproject template:
required: false
default: ""
publish:
description: |
Publish the built wheels to pypi.flet.dev at the end of the run.
type: boolean
required: false
default: false
# Reusable-workflow entry point for cross-repo callers.
workflow_call:
inputs:
archs:
type: string
required: false
default: "android,iOS"
packages:
type: string
required: false
default: "lru-dict:,pydantic-core:,numpy:"
prebuild_recipes:
type: string
required: false
default: ""
python_versions:
type: string
required: false
default: "3.12,3.13,3.14"
mobile_test_pythons:
type: string
required: false
default: "3.12"
python_build_run_id:
type: string
required: false
default: ""
serious_python_ref:
type: string
required: false
default: ""
publish:
type: boolean
required: false
default: false
secrets:
GEMFURY_TOKEN:
required: false
# Cancel in-flight runs when a newer event arrives for the same logical branch.
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
env:
DEFAULT_PYTHONS: "3.12,3.13,3.14"
# Used as the fallback recipe set when `packages` input is empty
# (workflow_dispatch/workflow_call) or when no `recipes/**` paths
# changed in the triggering event (push/pull_request).
SMOKE_TEST_PACKAGES: "lru-dict:,pydantic-core:,numpy:"
permissions:
contents: read
jobs:
detect:
runs-on: ubuntu-latest
outputs:
pythons_json: ${{ steps.detect-pythons.outputs.pythons_json }}
canonical_python: ${{ steps.detect-pythons.outputs.canonical_python }}
packages: ${{ steps.detect-packages.outputs.packages }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Get changed recipes
id: changed-recipes
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
with:
files: recipes/**
dir_names: true
dir_names_max_depth: 2
- id: detect-pythons
# Decide which Python versions this run fans out across.
shell: bash
env:
INPUT_PYTHONS: ${{ inputs.python_versions }}
run: |
pythons="${INPUT_PYTHONS:-$DEFAULT_PYTHONS}"
# Hand-built JSON array — no jq dep needed.
json="["
first=true
for py in $(echo "$pythons" | tr ',' ' '); do
if [ "$first" = true ]; then first=false; else json+=","; fi
json+="\"$py\""
done
json+="]"
echo "Detected pythons: $pythons -> $json"
echo "pythons_json=$json" >> "$GITHUB_OUTPUT"
# Canonical leg = first listed Python. build.sh (py3-none) recipes are
# identical on every leg, so they build only on the canonical one.
canonical="$(echo "$pythons" | tr ',' ' ' | awk '{print $1}' | cut -d. -f1-2)"
echo "canonical_python=$canonical" >> "$GITHUB_OUTPUT"
- id: detect-packages
shell: bash
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
INPUT_PACKAGES: ${{ inputs.packages }}
CHANGED_DIRS: ${{ steps.changed-recipes.outputs.all_changed_files }}
run: |
if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" || "$GITHUB_EVENT_NAME" == "workflow_call" ]]; then
# The literal value "ALL" expands to every recipe with a meta.yaml under recipes/.
if [[ "$INPUT_PACKAGES" == "ALL" ]]; then
pkgs=""
for dir in recipes/*/; do
[[ -f "$dir/meta.yaml" ]] || continue
pkg=$(basename "$dir")
pkgs="${pkgs:+$pkgs,}${pkg}:"
done
else
pkgs="${INPUT_PACKAGES:-$SMOKE_TEST_PACKAGES}"
fi
else
pkgs=""
for dir in $CHANGED_DIRS; do
[[ -f "$dir/meta.yaml" ]] || continue # skip deleted recipes
pkgs="${pkgs:+$pkgs,}${dir#recipes/}:"
done
pkgs="${pkgs:-$SMOKE_TEST_PACKAGES}"
fi
echo "Detected packages: $pkgs"
echo "packages=$pkgs" >> "$GITHUB_OUTPUT"
# One child workflow run per Python version.
build:
name: Python ${{ matrix.python_version }}
needs: detect
strategy:
fail-fast: false
matrix:
python_version: ${{ fromJson(needs.detect.outputs.pythons_json) }}
uses: ./.github/workflows/build-wheels-version.yml
with:
python_version: ${{ matrix.python_version }}
canonical_python: ${{ needs.detect.outputs.canonical_python }}
archs: ${{ inputs.archs || 'android,iOS' }}
packages: ${{ needs.detect.outputs.packages }}
prebuild_recipes: ${{ inputs.prebuild_recipes || '' }}
# A non-empty value is honored verbatim (list of test legs, or "ALL"). push/PR
# carry no inputs, so those default to the 3.12 on-device test. On
# workflow_dispatch/workflow_call an EXPLICIT empty string disables all mobile
# tests — mapped to the `none` sentinel so it survives the reusable-workflow
# boundary and never matches a python in the child's gate (`none` works too).
mobile_test_pythons: >-
${{ inputs.mobile_test_pythons != '' && inputs.mobile_test_pythons
|| ((github.event_name == 'push' || github.event_name == 'pull_request') && '3.12')
|| 'none' }}
python_build_run_id: ${{ inputs.python_build_run_id || '' }}
# No `|| default` here: an explicitly-passed empty string must survive so a
# caller can disable the override and test against the published sp.
serious_python_ref: ${{ inputs.serious_python_ref }}
publish: ${{ inputs.publish || false }}
secrets:
GEMFURY_TOKEN: ${{ secrets.GEMFURY_TOKEN }}