From 8efa89a1ebf5903df583d4c1c6b4ba33a5d89da6 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 13 Jul 2026 14:32:51 +0800 Subject: [PATCH 1/7] Audit permissions and add zizmor. --- .github/actions/app-create/action.yml | 36 +++-- .github/actions/install-briefcase/action.yml | 63 +++++--- .github/dependabot.yml | 4 + .github/workflows/app-build-verify.yml | 160 ++++++++----------- .github/workflows/app-create-verify.yml | 18 ++- .github/workflows/check-pr-template.yml | 5 +- .github/workflows/ci.yml | 23 ++- .github/workflows/coc-update.yml | 48 ++++-- .github/workflows/dependabot-changenote.yml | 25 ++- .github/workflows/docs-build-verify.yml | 20 ++- .github/workflows/new-issue.yml | 5 +- .github/workflows/pr-checklist.yml | 3 + .github/workflows/pre-commit-run.yml | 14 +- .github/workflows/python-package-create.yml | 8 +- .github/workflows/towncrier-run.yml | 18 ++- .pre-commit-config.yaml | 4 + 16 files changed, 279 insertions(+), 175 deletions(-) diff --git a/.github/actions/app-create/action.yml b/.github/actions/app-create/action.yml index 7ba5a2c7..01a2a4ad 100644 --- a/.github/actions/app-create/action.yml +++ b/.github/actions/app-create/action.yml @@ -30,13 +30,18 @@ runs: id: template-override env: GITHUB_TOKEN: ${{ github.token }} + TESTING_PR_BODY: ${{ inputs.testing-pr-body }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO_FULL: ${{ github.repository }} + TEMPLATE_SOURCE: ${{ inputs.briefcase-template-source }} + TEMPLATE_BRANCH_INPUT: ${{ inputs.briefcase-template-branch }} shell: bash run: | # Check Body of PR for template to use # (only PRs will have a value for github.event.pull_request.number) - PR_BODY="${{ inputs.testing-pr-body }}" - if [[ -z "${PR_BODY}" && -n "${{ github.event.pull_request.number }}" ]]; then - PR_BODY=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} --jq '.body') + PR_BODY="${TESTING_PR_BODY}" + if [[ -z "${PR_BODY}" && -n "${PR_NUMBER}" ]]; then + PR_BODY=$(gh api "repos/${REPO_FULL}/pulls/${PR_NUMBER}" --jq '.body') fi printf "::group::Retrieved PR Body\n%s\n::endgroup::\n" "${PR_BODY}" @@ -45,10 +50,10 @@ runs: # If a template is not in the PR, use inputs specified in CI workflow if [ -z "${TEMPLATE_REPO}" ]; then - TEMPLATE_REPO="${{ inputs.briefcase-template-source }}" + TEMPLATE_REPO="${TEMPLATE_SOURCE}" fi if [ -z "${TEMPLATE_REF}" ]; then - TEMPLATE_REF="${{ inputs.briefcase-template-branch }}" + TEMPLATE_REF="${TEMPLATE_BRANCH_INPUT}" fi # Expose template repo and branch via outputs @@ -58,25 +63,30 @@ runs: - name: Create Briefcase Project id: create shell: bash + env: + TEMPLATE_REPO: ${{ steps.template-override.outputs.repo }} + TEMPLATE_REF: ${{ steps.template-override.outputs.ref }} + FRAMEWORK: ${{ inputs.framework }} + ENABLE_AUTOMATION: ${{ inputs.enable-automation }} run: | - if [[ "${{ steps.template-override.outputs.repo }}" != "" ]]; then - TEMPLATE=$(printf -- "--template %q" "${{ steps.template-override.outputs.repo }}") + if [[ "${TEMPLATE_REPO}" != "" ]]; then + TEMPLATE=$(printf -- "--template %q" "${TEMPLATE_REPO}") fi - if [[ "${{ steps.template-override.outputs.ref }}" != "" ]]; then - TEMPLATE_BRANCH=$(printf -- "--template-branch %q" "${{ steps.template-override.outputs.ref }}") + if [[ "${TEMPLATE_REF}" != "" ]]; then + TEMPLATE_BRANCH=$(printf -- "--template-branch %q" "${TEMPLATE_REF}") fi # Map GUI toolkit through case insensitivity - case "$(tr '[:upper:]' '[:lower:]' <<< "${{ inputs.framework }}")" in + case "$(tr '[:upper:]' '[:lower:]' <<< "${FRAMEWORK}")" in toga ) BOOTSTRAP=Toga ;; pyside6 ) BOOTSTRAP=PySide6 ;; pygame ) BOOTSTRAP=Pygame ;; console ) BOOTSTRAP=Console ;; - * ) BOOTSTRAP=${{ inputs.framework }} ;; + * ) BOOTSTRAP="${FRAMEWORK}" ;; esac # If enabled, use the Automation bootstrap - [ "${{ inputs.enable-automation }}" != "false" ] && BOOTSTRAP="${BOOTSTRAP} Automation" + [ "${ENABLE_AUTOMATION}" != "false" ] && BOOTSTRAP="${BOOTSTRAP} Automation" ROOT_DIR="apps" APP_NAME="Verify App" @@ -95,7 +105,7 @@ runs: -Q "bootstrap=${BOOTSTRAP}" echo "Rolled out project to ${APP_PATH}" - echo " > GUI Toolkit: ${{ inputs.framework }}" + echo " > GUI Toolkit: ${FRAMEWORK}" echo " > Bootstrap: ${BOOTSTRAP}" printf "::group::pyproject.toml\n%s\n::endgroup::\n" "$(cat "${APP_DIR}/pyproject.toml")" printf "::group::app.py\n%s\n::endgroup::\n" "$(cat "${APP_DIR}/src/${APP_DIR}/app.py")" diff --git a/.github/actions/install-briefcase/action.yml b/.github/actions/install-briefcase/action.yml index 8de59741..8b17429a 100644 --- a/.github/actions/install-briefcase/action.yml +++ b/.github/actions/install-briefcase/action.yml @@ -36,13 +36,18 @@ runs: id: briefcase-override env: GITHUB_TOKEN: ${{ github.token }} + TESTING_PR_BODY: ${{ inputs.testing-pr-body }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO_FULL: ${{ github.repository }} + BRIEFCASE_URL: ${{ inputs.briefcase-url }} + BRIEFCASE_OVERRIDE_VERSION: ${{ inputs.briefcase-override-version }} shell: bash run: | # Check Body of PR for Briefcase version to use # (only PRs will have a value for github.event.pull_request.number) - PR_BODY="${{ inputs.testing-pr-body }}" - if [[ -z "${PR_BODY}" && -n "${{ github.event.pull_request.number }}" ]]; then - PR_BODY=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} --jq '.body') + PR_BODY="${TESTING_PR_BODY}" + if [[ -z "${PR_BODY}" && -n "${PR_NUMBER}" ]]; then + PR_BODY=$(gh api "repos/${REPO_FULL}/pulls/${PR_NUMBER}" --jq '.body') fi printf "::group::Retrieved PR Body\n%s\n::endgroup::\n" "${PR_BODY}" @@ -51,10 +56,10 @@ runs: # If a version is not in the PR, use inputs specified in CI workflow if [ -z "${BRIEFCASE_REPO}" ]; then - BRIEFCASE_REPO="${{ inputs.briefcase-url }}" + BRIEFCASE_REPO="${BRIEFCASE_URL}" fi if [ -z "${BRIEFCASE_REF}" ]; then - BRIEFCASE_REF="${{ inputs.briefcase-override-version }}" + BRIEFCASE_REF="${BRIEFCASE_OVERRIDE_VERSION}" fi # Expose repo and version via outputs @@ -65,6 +70,17 @@ runs: id: briefcase-derived if: steps.briefcase-override.outputs.ref == '' shell: bash + env: + TESTING_TRIGGER_REF: ${{ inputs.testing-trigger-ref }} + TESTING_PR_REF: ${{ inputs.testing-pr-ref }} + TESTING_REF_NAME: ${{ inputs.testing-ref-name }} + GITHUB_REF: ${{ github.ref }} + GITHUB_BASE_REF: ${{ github.base_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + BRIEFCASE_URL: ${{ inputs.briefcase-url }} + BRIEFCASE_DEFAULT_BRANCH: ${{ inputs.briefcase-default-branch }} run: | # Branch or tag that triggered the workflow. # For example, when github.event is: @@ -72,24 +88,24 @@ runs: # refs/tags/ # release: refs/tags/ # pull_request: refs/pull//merge - TRIGGER_REF="${{ inputs.testing-trigger-ref || github.ref }}" + TRIGGER_REF="${TESTING_TRIGGER_REF:-$GITHUB_REF}" # PR target branch (only available for pull requests) - PR_TARGET_REF="${{ inputs.testing-pr-ref || github.base_ref }}" + PR_TARGET_REF="${TESTING_PR_REF:-$GITHUB_BASE_REF}" # The short ref name of the branch or tag that triggered the workflow # For example: # push: main # release: v0.3.12 # pull_request: 18/merge - REF_NAME="${{ inputs.testing-ref-name || github.ref_name }}" + REF_NAME="${TESTING_REF_NAME:-$GITHUB_REF_NAME}" echo "::group::Workflow Trigger Details" - echo "Event: ${{ github.event_name }}" - echo "Ref: ${{ github.ref }}" - echo "Base Ref: ${{ github.base_ref }}" - echo "Head Ref: ${{ github.head_ref }}" - echo "Ref Name: ${{ github.ref_name }}" + echo "Event: ${GITHUB_EVENT_NAME}" + echo "Ref: ${GITHUB_REF}" + echo "Base Ref: ${GITHUB_BASE_REF}" + echo "Head Ref: ${GITHUB_HEAD_REF}" + echo "Ref Name: ${GITHUB_REF_NAME}" echo "::endgroup::" case "${TRIGGER_REF}" in @@ -106,8 +122,8 @@ runs: # Default to 'main' * ) - echo "::warning::Could not determine Briefcase version from '${TRIGGER_REF}'; defaulting to '${{ inputs.briefcase-default-branch }}'." - TARGET_VERSION="${{ inputs.briefcase-default-branch }}" + echo "::warning::Could not determine Briefcase version from '${TRIGGER_REF}'; defaulting to '${BRIEFCASE_DEFAULT_BRANCH}'." + TARGET_VERSION="${BRIEFCASE_DEFAULT_BRANCH}" ;; esac @@ -115,17 +131,17 @@ runs: echo "Derived Briefcase version: ${TARGET_VERSION}" # Use Briefcase's default branch - if [[ "${TARGET_VERSION}" == "${{ inputs.briefcase-default-branch }}" ]]; then + if [[ "${TARGET_VERSION}" == "${BRIEFCASE_DEFAULT_BRANCH}" ]]; then echo "version=${TARGET_VERSION}" >> ${GITHUB_OUTPUT} # Use version if it is a valid Briefcase tag - elif [[ $(git ls-remote --tags "${{ inputs.briefcase-url }}" "${TARGET_VERSION}") ]]; then + elif [[ $(git ls-remote --tags "${BRIEFCASE_URL}" "${TARGET_VERSION}") ]]; then echo "version=${TARGET_VERSION}" >> ${GITHUB_OUTPUT} # Default to Briefcase's default branch else - echo "::warning::Unknown Briefcase version: '${TARGET_VERSION}'; defaulting to '${{ inputs.briefcase-default-branch }}'." - echo "version=${{ inputs.briefcase-default-branch }}" >> ${GITHUB_OUTPUT} + echo "::warning::Unknown Briefcase version: '${TARGET_VERSION}'; defaulting to '${BRIEFCASE_DEFAULT_BRANCH}'." + echo "version=${BRIEFCASE_DEFAULT_BRANCH}" >> ${GITHUB_OUTPUT} fi - name: Update pip @@ -135,9 +151,14 @@ runs: - name: Install Briefcase id: install shell: bash + env: + OVERRIDE_REPO: ${{ steps.briefcase-override.outputs.repo }} + OVERRIDE_REF: ${{ steps.briefcase-override.outputs.ref }} + DERIVED_VERSION: ${{ steps.briefcase-derived.outputs.version }} + BRIEFCASE_URL: ${{ inputs.briefcase-url }} run: | - REPO="${{ steps.briefcase-override.outputs.repo || inputs.briefcase-url }}" - REF="${{ steps.briefcase-override.outputs.ref || steps.briefcase-derived.outputs.version }}" + REPO="${OVERRIDE_REPO:-$BRIEFCASE_URL}" + REF="${OVERRIDE_REF:-$DERIVED_VERSION}" echo "Installing ${REPO}@${REF}" python -m pip install \ diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d6451878..72160912 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,6 +7,8 @@ updates: interval: "weekly" day: "sunday" time: "20:00" + cooldown: + default-days: 7 - package-ecosystem: "pip" directory: "/" @@ -15,3 +17,5 @@ updates: interval: "weekly" day: "sunday" time: "20:00" + cooldown: + default-days: 7 diff --git a/.github/workflows/app-build-verify.yml b/.github/workflows/app-build-verify.yml index f05f337b..b5905ccd 100644 --- a/.github/workflows/app-build-verify.yml +++ b/.github/workflows/app-build-verify.yml @@ -68,7 +68,6 @@ jobs: timeout-minutes: 30 permissions: contents: read - packages: write env: SDL_AUDIODRIVER: dummy # disable audio for SDL TOGA_SYSTEM_REQUIRES: libcairo2-dev libcanberra-gtk3-module libegl1 libgirepository1.0-dev libgirepository-2.0-dev libthai-dev gir1.2-gtk-3.0 @@ -77,8 +76,14 @@ jobs: - name: Workflow Configuration id: config + env: + RUNNER_OS: ${{ inputs.runner-os }} + REPOSITORY: ${{ inputs.repository }} + FRAMEWORK: ${{ inputs.framework }} + TARGET_PLATFORM: ${{ inputs.target-platform }} + TARGET_FORMAT: ${{ inputs.target-format }} run: | - case "$(tr '[:upper:]' '[:lower:]' <<< '${{ inputs.runner-os }}')" in + case "$(tr '[:upper:]' '[:lower:]' <<< "${RUNNER_OS}")" in ubuntu* ) BRIEFCASE_DIR="$HOME/.cache/briefcase" ;; macos* ) BRIEFCASE_DIR="$HOME/Library/Caches/org.beeware.briefcase" ;; windows* ) BRIEFCASE_DIR="$HOME/AppData/Local/BeeWare/briefcase/Cache" ;; @@ -86,25 +91,27 @@ jobs: esac echo "briefcase-data-dir=${BRIEFCASE_DIR}" | tee -a ${GITHUB_OUTPUT} - CACHE_KEY="${{ inputs.runner-os }}|${{ inputs.repository }}|${{ inputs.framework }}|${{ inputs.target-platform }}" + CACHE_KEY="${RUNNER_OS}|${REPOSITORY}|${FRAMEWORK}|${TARGET_PLATFORM}" # For Linux builds, the cache varies by the output format - if [ "${{ startsWith(inputs.runner-os, 'ubuntu') }}" = "true" ]; then - CACHE_KEY="$CACHE_KEY|${{ inputs.target-format }}" - fi + case "$(tr '[:upper:]' '[:lower:]' <<< "${RUNNER_OS}")" in + ubuntu* ) CACHE_KEY="$CACHE_KEY|${TARGET_FORMAT}" ;; + esac echo "cache-key=$(date +%Y-%m)|${CACHE_KEY}" | tee -a ${GITHUB_OUTPUT} - name: Checkout ${{ inputs.repository }} - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ inputs.repository }} fetch-depth: 0 + persist-credentials: false - name: Checkout ${{ inputs.workflow-repo }}${{ inputs.workflow-repo-ref && format('@{0}', inputs.workflow-repo-ref) || '' }} - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ inputs.workflow-repo }} ref: ${{ inputs.workflow-repo-ref }} path: beeware-.github + persist-credentials: false - name: Prepare Linux if: startsWith(inputs.runner-os, 'ubuntu') @@ -119,11 +126,13 @@ jobs: # https://github.com/actions/runner-images/issues/12541 and # https://github.com/actions/runner-images/issues/12751. if: ${{ startsWith(inputs.runner-os, 'macos') && inputs.xcode-version }} + env: + XCODE_VERSION: ${{ inputs.xcode-version }} run: | - sudo xcode-select --switch /Applications/Xcode_${{ inputs.xcode-version }}.app + sudo xcode-select --switch "/Applications/Xcode_${XCODE_VERSION}.app" - name: Cache Briefcase Tools - uses: actions/cache@v6.1.0 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: key: briefcase-data|${{ steps.config.outputs.cache-key }} path: ${{ steps.config.outputs.briefcase-data-dir }} @@ -133,7 +142,7 @@ jobs: # fall back to the system-provided Python package. Providing "system" as a value # on macOS or Windows will cause an error. if: ${{ !startsWith(inputs.runner-os, 'ubuntu') || inputs.python-version != 'system' }} - uses: actions/setup-python@v6.3.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: # This needs to be kept aligned with the input default value python-version: ${{ inputs.python-version || '3.13' }} @@ -147,7 +156,7 @@ jobs: - name: Get Briefcase Packages # Briefcase will build and package itself in a previous step in its CI if: endsWith(inputs.repository, 'briefcase') - uses: actions/download-artifact@v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: # artifact name is derived in python-package-create.yml pattern: "Packages-briefcase*" @@ -176,9 +185,11 @@ jobs: # other repos will fetch the latest template version during `create` as normal. if: endsWith(inputs.repository, '-template') && inputs.repository != 'beeware/briefcase-template' id: output-format + env: + WORKSPACE: ${{ github.workspace }} run: | # Since the current repo is cloned in to the workspace, use it for the template - echo "template-override=$(printf -- "--config template=%q" "'${{ github.workspace }}'")" | tee -a ${GITHUB_OUTPUT} + echo "template-override=$(printf -- "--config template=%q" "'${WORKSPACE}'")" | tee -a ${GITHUB_OUTPUT} # In the steps below, the builtin functions (i.e. contains, startsWith, etc.) # are used for comparison (instead of ==) because they are case-insensitive. @@ -192,9 +203,10 @@ jobs: env: # SDL cannot always find hardware acceleration on macOS in CI SDL_VIDEODRIVER: ${{ startsWith(inputs.framework, 'pygame') && 'dummy' || '' }} + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | briefcase create macOS app \ - ${{ steps.output-format.outputs.template-override }} + ${TEMPLATE_OVERRIDE} briefcase build macOS app briefcase run macOS app briefcase package macOS app --adhoc-sign @@ -211,9 +223,10 @@ jobs: env: # SDL cannot always find hardware acceleration on macOS in CI SDL_VIDEODRIVER: ${{ startsWith(inputs.framework, 'pygame') && 'dummy' || '' }} + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | briefcase create macOS Xcode \ - ${{ steps.output-format.outputs.template-override }} + ${TEMPLATE_OVERRIDE} briefcase build macOS Xcode briefcase run macOS Xcode briefcase package macOS Xcode --adhoc-sign @@ -227,9 +240,11 @@ jobs: && contains(fromJSON('["", "Windows"]'), inputs.target-platform) && contains(fromJSON('["", "app"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} + env: + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | briefcase create windows app \ - ${{ steps.output-format.outputs.template-override }} + ${TEMPLATE_OVERRIDE} briefcase build windows app briefcase run windows app briefcase package windows app --adhoc-sign @@ -243,9 +258,11 @@ jobs: && contains(fromJSON('["", "Windows"]'), inputs.target-platform) && contains(fromJSON('["", "VisualStudio"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} + env: + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | briefcase create windows VisualStudio \ - ${{ steps.output-format.outputs.template-override }} + ${TEMPLATE_OVERRIDE} briefcase build windows VisualStudio briefcase run windows VisualStudio briefcase package windows VisualStudio --adhoc-sign @@ -260,17 +277,20 @@ jobs: && contains(fromJSON('["", "Linux"]'), inputs.target-platform) && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} + env: + FRAMEWORK: ${{ inputs.framework }} + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | PACKAGES="python3-dev python3-pip build-essential" - case "$(tr '[:upper:]' '[:lower:]' <<< '${{ inputs.framework }}')" in - toga ) PACKAGES="${PACKAGES} ${{ env.TOGA_SYSTEM_REQUIRES }}" ;; - pyside6 ) PACKAGES="${PACKAGES} ${{ env.PYSIDE6_SYSTEM_REQUIRES }}" ;; + case "$(tr '[:upper:]' '[:lower:]' <<< "${FRAMEWORK}")" in + toga ) PACKAGES="${PACKAGES} ${TOGA_SYSTEM_REQUIRES}" ;; + pyside6 ) PACKAGES="${PACKAGES} ${PYSIDE6_SYSTEM_REQUIRES}" ;; esac sudo apt install -y --no-install-recommends ${PACKAGES} briefcase create linux system \ - ${{ steps.output-format.outputs.template-override }} + ${TEMPLATE_OVERRIDE} briefcase build linux system xvfb-run briefcase run linux system briefcase package linux system --adhoc-sign @@ -280,55 +300,6 @@ jobs: # Clean up built artefacts to preserve disk space rm -rf build dist - - name: Log in to GitHub Container Registry - if: > - startsWith(inputs.runner-os, 'ubuntu') - && contains(fromJSON('["", "Linux"]'), inputs.target-platform) - && contains(fromJSON('["", "system", "appimage"]'), inputs.target-format) - uses: docker/login-action@v4.4.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - if: > - startsWith(inputs.runner-os, 'ubuntu') - && contains(fromJSON('["", "Linux"]'), inputs.target-platform) - && contains(fromJSON('["", "system", "appimage"]'), inputs.target-format) - uses: docker/setup-buildx-action@v4.2.0 - - - name: Docker Caching Configuration - id: docker - if: > - startsWith(inputs.runner-os, 'ubuntu') - && contains(fromJSON('["", "Linux"]'), inputs.target-platform) - && contains(fromJSON('["", "system", "appimage"]'), inputs.target-format) - env: - GITHUB_REF_NAME: ${{ github.ref_name }} - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - run: | - PKG_NAME="beeware-ci" - REGISTRY_PKG="ghcr.io/$(cut -d/ -f1 <<< "${{ github.repository }}")/${PKG_NAME}" - CURRENT_REF="$(sed 's|[^a-z0-9_-]|_|g' <<< "${GITHUB_REF_NAME,,}")" - DEFAULT_REF="$(sed 's|[^a-z0-9_-]|_|g' <<< "${DEFAULT_BRANCH,,}")" - - # include all layers, minimize compression time, and ignore package upload issues - CACHE_TO_OPTS="mode=max,compression-level=0,ignore-error=true" - - BUILDX_OPTIONS=( - # attempt to push to BeeWare's package - --Xdocker-build=--cache-to=type=registry,ref=${REGISTRY_PKG}:__PKG_TAG__-${CURRENT_REF},${CACHE_TO_OPTS} - # retrieve the cache using the current branch, falling back to the default branch - --Xdocker-build=--cache-from=type=registry,ref=${REGISTRY_PKG}:__PKG_TAG__-${CURRENT_REF} - --Xdocker-build=--cache-from=type=registry,ref=${REGISTRY_PKG}:__PKG_TAG__-${DEFAULT_REF} - ) - echo "DOCKER_BUILDX_OPTIONS=${BUILDX_OPTIONS[@]}" | tee -a ${GITHUB_ENV} - - # Tag format: app-build------- - # For example: app-build-24-02-toga-3.11-system-debian-trixie-main - echo "tag-base=app-build-$(date +%Y-%m)-${{ inputs.framework }}-${{ inputs.python-version }}" | tee -a ${GITHUB_OUTPUT} - - name: Build Linux System Project (Debian, Dockerized) if: > startsWith(inputs.runner-os, 'ubuntu') @@ -336,11 +307,10 @@ jobs: && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - PKG_TAG: ${{ steps.docker.outputs.tag-base }}-system-debian-trixie + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | briefcase create linux system --target debian:trixie \ - ${{ steps.output-format.outputs.template-override }} \ - ${DOCKER_BUILDX_OPTIONS//__PKG_TAG__/${PKG_TAG}} + ${TEMPLATE_OVERRIDE} briefcase build linux system --target debian:trixie xvfb-run briefcase run linux system --target debian:trixie briefcase package linux system --target debian:trixie --adhoc-sign @@ -357,11 +327,10 @@ jobs: && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - PKG_TAG: ${{ steps.docker.outputs.tag-base }}-system-fedora-41 + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | briefcase create linux system --target fedora:41 \ - ${{ steps.output-format.outputs.template-override }} \ - ${DOCKER_BUILDX_OPTIONS//__PKG_TAG__/${PKG_TAG}} + ${TEMPLATE_OVERRIDE} briefcase build linux system --target fedora:41 xvfb-run briefcase run linux system --target fedora:41 briefcase package linux system --target fedora:41 --adhoc-sign @@ -380,11 +349,10 @@ jobs: && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - PKG_TAG: ${{ steps.docker.outputs.tag-base }}-system-arch + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | briefcase create linux system --target archlinux:latest \ - ${{ steps.output-format.outputs.template-override }} \ - ${DOCKER_BUILDX_OPTIONS//__PKG_TAG__/${PKG_TAG}} + ${TEMPLATE_OVERRIDE} briefcase build linux system --target archlinux:latest xvfb-run briefcase run linux system --target archlinux:latest briefcase package linux system --target archlinux:latest --adhoc-sign @@ -401,11 +369,10 @@ jobs: && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - PKG_TAG: ${{ steps.docker.outputs.tag-base }}-system-opensuse + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | briefcase create linux system --target opensuse/tumbleweed:latest \ - ${{ steps.output-format.outputs.template-override }} \ - ${DOCKER_BUILDX_OPTIONS//__PKG_TAG__/${PKG_TAG}} + ${TEMPLATE_OVERRIDE} briefcase build linux system --target opensuse/tumbleweed:latest xvfb-run briefcase run linux system --target opensuse/tumbleweed:latest briefcase package linux system --target opensuse/tumbleweed:latest --adhoc-sign @@ -484,11 +451,13 @@ jobs: && contains(fromJSON('["", "Linux"]'), inputs.target-platform) && contains(fromJSON('["", "Flatpak"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} + env: + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | sudo apt install -y --no-install-recommends flatpak flatpak-builder elfutils briefcase create linux flatpak \ - ${{ steps.output-format.outputs.template-override }} + ${TEMPLATE_OVERRIDE} briefcase build linux flatpak xvfb-run briefcase run linux flatpak briefcase package linux flatpak --adhoc-sign @@ -505,15 +474,20 @@ jobs: && contains(fromJSON('["", "Gradle"]'), inputs.target-format) && startsWith(inputs.framework, 'toga') working-directory: ${{ steps.create.outputs.project-path }} + env: + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + RUNNER_OS: ${{ inputs.runner-os }} run: | export JAVA_HOME=${JAVA_HOME_17_X64} briefcase create android gradle \ - ${{ steps.output-format.outputs.template-override }} + ${TEMPLATE_OVERRIDE} briefcase build android gradle # Only run Android app on Linux since it's duplicative (and slow) on other platforms - if [ "${{ startsWith(inputs.runner-os, 'ubuntu') }}" = "true" ]; then + # (runner-os values are always canonical lowercase runner labels, e.g. ubuntu-24.04) + case "${RUNNER_OS}" in + ubuntu* ) # allow access to KVM to run the emulator echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \ | sudo tee /etc/udev/rules.d/99-kvm4all.rules @@ -526,7 +500,8 @@ jobs: --Xemulator=-no-window \ --Xemulator=-no-audio \ --Xemulator=-no-boot-anim - fi + ;; + esac briefcase package android gradle --adhoc-sign @@ -540,11 +515,14 @@ jobs: && contains(fromJSON('["", "Xcode"]'), inputs.target-format) && startsWith(inputs.framework, 'toga') working-directory: ${{ steps.create.outputs.project-path }} + env: + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + IOS_SIMULATOR: ${{ inputs.ios-simulator }} run: | briefcase create iOS xcode \ - ${{ steps.output-format.outputs.template-override }} + ${TEMPLATE_OVERRIDE} briefcase build iOS xcode - briefcase run iOS xcode -d "${{ inputs.ios-simulator }}" + briefcase run iOS xcode -d "${IOS_SIMULATOR}" briefcase package iOS xcode --adhoc-sign # Clean up built artefacts to preserve disk space @@ -556,9 +534,11 @@ jobs: && contains(fromJSON('["", "static"]'), inputs.target-format) && startsWith(inputs.framework, 'toga') working-directory: ${{ steps.create.outputs.project-path }} + env: + TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | briefcase create web static \ - ${{ steps.output-format.outputs.template-override }} + ${TEMPLATE_OVERRIDE} briefcase build web static briefcase package web static @@ -566,7 +546,7 @@ jobs: rm -rf build dist - name: Upload Failure Logs - uses: actions/upload-artifact@v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: failure() with: name: build-failure-logs-${{ inputs.runner-os }}-${{ inputs.framework }}-${{ inputs.python-version }}-${{ inputs.target-platform }}-${{ inputs.target-format }} diff --git a/.github/workflows/app-create-verify.yml b/.github/workflows/app-create-verify.yml index f2afd27c..21c17564 100644 --- a/.github/workflows/app-create-verify.yml +++ b/.github/workflows/app-create-verify.yml @@ -38,6 +38,9 @@ defaults: run: shell: bash +permissions: + contents: read + env: FORCE_COLOR: "1" @@ -54,30 +57,33 @@ jobs: echo "system-python-version=${SYSTEM_PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} - name: Checkout ${{ inputs.repository }} - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ inputs.repository }} fetch-depth: 0 + persist-credentials: false - name: Checkout ${{ inputs.workflow-repo }}${{ inputs.workflow-repo-ref && format('@{0}', inputs.workflow-repo-ref) || '' }} - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ inputs.workflow-repo }} ref: ${{ inputs.workflow-repo-ref }} path: beeware-.github + persist-credentials: false - name: Checkout beeware/briefcase-template - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: beeware/briefcase-template path: briefcase-template + persist-credentials: false - name: Set up Python # On Linux, accept a value of "system", which will install a version of # Python matching the system version. We can't use the actual system # install because we need to install dependencies, which would either # require the use of sudo, or extra workarounds for user-space packages. - uses: actions/setup-python@v6.3.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ startsWith(inputs.runner-os, 'ubuntu') && inputs.python-version == 'system' && steps.config.outputs.system-python-version || inputs.python-version || '3.x' }} cache: pip @@ -96,7 +102,7 @@ jobs: - name: Get Briefcase Package # Briefcase will build and package itself in a previous step in its CI if: endsWith(inputs.repository, 'briefcase') - uses: actions/download-artifact@v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: # artifact name is derived in python-package-create.yml name: "Packages-briefcase" @@ -139,7 +145,7 @@ jobs: pprint(toml.load(pyproject_toml)) - name: Upload Failure Logs - uses: actions/upload-artifact@v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: failure() with: name: build-failure-logs-${{ inputs.runner-os }}-${{ inputs.framework }}-${{ inputs.python-version }} diff --git a/.github/workflows/check-pr-template.yml b/.github/workflows/check-pr-template.yml index 9196bacb..eaa1b679 100644 --- a/.github/workflows/check-pr-template.yml +++ b/.github/workflows/check-pr-template.yml @@ -4,7 +4,10 @@ on: pull_request: types: [opened, edited, reopened, synchronize] +permissions: + contents: read + jobs: check-pr-template: name: Check PR template - uses: beeware/.github/.github/workflows/pr-checklist.yml@main + uses: ./.github/workflows/pr-checklist.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c98c60e..e1cb51d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,11 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +# Read-only by default. Called reusable workflows declare any additional +# permissions they require (e.g. towncrier needs pull-requests: write). +permissions: + contents: read + defaults: run: shell: bash @@ -30,7 +35,8 @@ jobs: # inherited secrets are not exposed to forked repos if: ${{ github.event.pull_request.head.repo.owner.login == 'beeware' }} uses: ./.github/workflows/docs-build-verify.yml - secrets: inherit + secrets: + RTD_API_TOKEN: ${{ secrets.RTD_API_TOKEN }} with: project-name: ${{ matrix.name }} project-version: ${{ matrix.version }} @@ -105,10 +111,12 @@ jobs: expected: "main" steps: - name: Checkout beeware/.github - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6.3.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.x" cache: pip @@ -128,11 +136,14 @@ jobs: testing-ref-name: ${{ matrix.testing-ref-name }} - name: Briefcase @ ${{ matrix.test-case }} + env: + EXPECTED: ${{ matrix.expected }} + INSTALLED_VERSION: ${{ steps.briefcase.outputs.installed-version }} run: | - if [[ "${{ matrix.expected }}" != "${{ steps.briefcase.outputs.installed-version }}" ]]; then - echo "installed-version: ${{ steps.briefcase.outputs.installed-version }}" + if [[ "${EXPECTED}" != "${INSTALLED_VERSION}" ]]; then + echo "installed-version: ${INSTALLED_VERSION}" echo "Found Briefcase version v$(briefcase -V)" - echo "Expected Briefcase version ${{ matrix.expected }}" + echo "Expected Briefcase version ${EXPECTED}" exit 1 fi diff --git a/.github/workflows/coc-update.yml b/.github/workflows/coc-update.yml index d9d29295..92d2b47f 100644 --- a/.github/workflows/coc-update.yml +++ b/.github/workflows/coc-update.yml @@ -20,52 +20,68 @@ jobs: commit_hash: ${{ steps.commit_hash.outputs.commit_hash }} steps: - name: Checkout this repository - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Get latest commit hash id: commit_hash run: | commit_hash=$(git rev-parse --short=16 HEAD) - echo "commit_hash=$commit_hash" >> $GITHUB_OUTPUT + echo "commit_hash=$commit_hash" >> "$GITHUB_OUTPUT" - name: Get list of repositories id: repo_list env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - repo_list=$(gh repo list $GITHUB_REPOSITORY_OWNER --no-archived --json nameWithOwner --jq ".[].nameWithOwner" -L 500) - jq_repo_list=$(jq -c -n '$ARGS.positional' --args ${repo_list[@]}) - echo repo_list="$jq_repo_list" >> $GITHUB_OUTPUT + repo_list=$(gh repo list "$GITHUB_REPOSITORY_OWNER" --no-archived --json nameWithOwner --jq ".[].nameWithOwner" -L 500 \ + | jq -R -s -c 'split("\n") | map(select(length > 0))') + echo "repo_list=$repo_list" >> "$GITHUB_OUTPUT" update-coc: runs-on: ubuntu-latest needs: plan-coc-update + # This job performs all cross-repo writes using BRUTUS_PAT_TOKEN, so the + # job's own GITHUB_TOKEN needs no permissions. Deny everything by default. + permissions: {} strategy: matrix: full_repo: ${{ fromJson(needs.plan-coc-update.outputs.repo_list) }} steps: - name: Check if source repository id: check-if-source + env: + FULL_REPO: ${{ matrix.full_repo }} run: | - if [[ "${{ matrix.full_repo }}" == "$GITHUB_REPOSITORY" ]]; then + if [[ "${FULL_REPO}" == "$GITHUB_REPOSITORY" ]]; then echo "Skipping source repository ($GITHUB_REPOSITORY)..." echo "is_source=1" >> "$GITHUB_OUTPUT" exit 0 fi echo "is_source=0" >> "$GITHUB_OUTPUT" - name: Checkout this repo - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: path: sourcerepo + persist-credentials: false - name: Checkout repo to be updated - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ matrix.full_repo }} path: targetrepo - token: ${{ secrets.BRUTUS_PAT_TOKEN }} + # Don't persist the token in .git/config (avoids the artipacked + # credential-leak vector); pushes are authenticated via the gh + # credential helper configured in "Configure git" below. + persist-credentials: false - name: Configure git working-directory: ./targetrepo + env: + GH_TOKEN: ${{ secrets.BRUTUS_PAT_TOKEN }} run: | git config --local user.email "brutus@beeware.org" git config --local user.name "Brutus[bot]" + # Authenticate git via the gh credential helper using BRUTUS_PAT_TOKEN, + # rather than a token persisted by actions/checkout. + gh auth setup-git - name: Replace Code of Conduct working-directory: ./targetrepo run: | @@ -85,9 +101,11 @@ jobs: working-directory: ./targetrepo if: | steps.check-no-change.outputs.changed == '1' + env: + COMMIT_HASH: ${{ needs.plan-coc-update.outputs.commit_hash }} run: | git add CODE_OF_CONDUCT.md - git commit -m "Updated Code of Conduct ($GITHUB_REPOSITORY, ${{ needs.plan-coc-update.outputs.commit_hash }})" + git commit -m "Updated Code of Conduct ($GITHUB_REPOSITORY, ${COMMIT_HASH})" - name: Attempt to push Code of Conduct to main id: push-main if: | @@ -97,7 +115,7 @@ jobs: env: GH_TOKEN: ${{ secrets.BRUTUS_PAT_TOKEN }} run: | - # Attempt to push directly to main + # Attempt to push directly to main. echo "success=1" >> "$GITHUB_OUTPUT" git push || echo "success=0" >> "$GITHUB_OUTPUT" - name: Update Code of Conduct via PR to main using new branch @@ -109,13 +127,15 @@ jobs: working-directory: ./targetrepo env: GH_TOKEN: ${{ secrets.BRUTUS_PAT_TOKEN }} + FULL_REPO: ${{ matrix.full_repo }} + COMMIT_HASH: ${{ needs.plan-coc-update.outputs.commit_hash }} run: | timestamp=$(date +%m%d%Y-%H%M%S) branch_name="coc-update-$timestamp" - git checkout -b $branch_name + git checkout -b "$branch_name" git push --set-upstream origin "$branch_name" gh pr create \ - --repo ${{ matrix.full_repo }} \ + --repo "${FULL_REPO}" \ --title "Update Code of Conduct" \ - --body "Updated via $GITHUB_REPOSITORY (${{ needs.plan-coc-update.outputs.commit_hash }})" + --body "Updated via $GITHUB_REPOSITORY (${COMMIT_HASH})" diff --git a/.github/workflows/dependabot-changenote.yml b/.github/workflows/dependabot-changenote.yml index 83c6c0a8..536d8450 100644 --- a/.github/workflows/dependabot-changenote.yml +++ b/.github/workflows/dependabot-changenote.yml @@ -12,38 +12,49 @@ on: required: true permissions: + contents: read pull-requests: write jobs: changenote: name: Dependabot Change Note - if: github.actor == 'dependabot[bot]' && github.repository != 'beeware/.github' + if: github.event.pusher.name == 'dependabot[bot]' && github.repository != 'beeware/.github' runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Checkout - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 1 - token: ${{ secrets.BRUTUS_PAT_TOKEN }} + # Don't persist the token in .git/config (avoids the artipacked + # credential-leak vector); the push is authenticated explicitly below. + persist-credentials: false - name: Configure git + env: + GH_TOKEN: ${{ secrets.BRUTUS_PAT_TOKEN }} run: | git config --local user.email "$(git log --pretty='%ae' -1)" git config --local user.name "dependabot[bot]" + # Configure git to authenticate via the gh credential helper using + # BRUTUS_PAT_TOKEN, rather than a token persisted by actions/checkout. + gh auth setup-git - name: Commit Change Note env: GITHUB_TOKEN: ${{ github.token }} + HEAD_COMMIT_ID: ${{ github.event.head_commit.id }} + HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + CHANGENOTE_FORMAT: ${{ inputs.changenote-format }} run: | # Retrieve PR number from GitHub API - PR_NUM=$(gh api repos/{owner}/{repo}/commits/${{ github.event.head_commit.id }}/pulls --jq '.[].number') - CHANGENOTE_FILEPATH="./changes/${PR_NUM}.misc.${{ inputs.changenote-format }}" + PR_NUM=$(gh api "repos/{owner}/{repo}/commits/${HEAD_COMMIT_ID}/pulls" --jq '.[].number') + CHANGENOTE_FILEPATH="./changes/${PR_NUM}.misc.${CHANGENOTE_FORMAT}" # Create change note from first line of dependabot commit # Replace "Bump" with "Updated", and add markup to URLs. - NEWS=$(printf "%s" "${{ github.event.head_commit.message }}" | head -n1 | sed -e 's|Bump|Updated|' | sed -e 's|\(https://[^/]*/[^ .]*\)|<\1>|') + NEWS=$(printf "%s" "${HEAD_COMMIT_MESSAGE}" | head -n1 | sed -e 's|Bump|Updated|' | sed -e 's|\(https://[^/]*/[^ .]*\)|<\1>|') printf "%s.\n" "${NEWS}" > "${CHANGENOTE_FILEPATH}" # Commit the change note @@ -52,4 +63,6 @@ jobs: git commit -m "Add changenote. [dependabot skip]" - name: Push + env: + GH_TOKEN: ${{ secrets.BRUTUS_PAT_TOKEN }} run: git push origin diff --git a/.github/workflows/docs-build-verify.yml b/.github/workflows/docs-build-verify.yml index 64dfcd1d..e63c0f8a 100644 --- a/.github/workflows/docs-build-verify.yml +++ b/.github/workflows/docs-build-verify.yml @@ -20,11 +20,18 @@ on: description: "Amount of time to wait for ReadTheDocs to report success; defaults to 10 minutes." default: 10 type: number + secrets: + RTD_API_TOKEN: + description: "ReadTheDocs API token used to query build status." + required: true defaults: run: shell: bash +permissions: + contents: read + jobs: docs: name: Verify RTD Docs @@ -34,15 +41,20 @@ jobs: RTD_API_URL: "https://readthedocs.org/api/v3/projects/${{ inputs.project-name }}/versions/${{ inputs.project-version }}/" steps: - name: Verify RTD Built Docs for ${{ inputs.project-version }} + env: + # Pass values through the environment rather than interpolating them + # directly into the script, to avoid injection and secret leakage. + PROJECT_VERSION: ${{ inputs.project-version }} + RTD_API_TOKEN: ${{ secrets.RTD_API_TOKEN }} run: | - echo "Verifying docs for ${{ inputs.project-version }} were successfully built:" + echo "Verifying docs for ${PROJECT_VERSION} were successfully built:" while true do - RTD_RESPONSE=$(curl -s -H "Authorization: Token ${{ secrets.RTD_API_TOKEN }}" "${RTD_API_URL}") - IS_DOCS_BUILT=$(jq .built <<< ${RTD_RESPONSE}) + RTD_RESPONSE=$(curl -s -H "Authorization: Token ${RTD_API_TOKEN}" "${RTD_API_URL}") + IS_DOCS_BUILT=$(jq .built <<< "${RTD_RESPONSE}") echo "::group::IS_DOCS_BUILT=${IS_DOCS_BUILT}" - jq <<< ${RTD_RESPONSE} + jq <<< "${RTD_RESPONSE}" echo "::endgroup::" if [[ "${IS_DOCS_BUILT}" == "true" ]]; then diff --git a/.github/workflows/new-issue.yml b/.github/workflows/new-issue.yml index e4a99c00..260ae893 100644 --- a/.github/workflows/new-issue.yml +++ b/.github/workflows/new-issue.yml @@ -11,8 +11,11 @@ jobs: add-to-project: name: Add issue to BeeWare project runs-on: ubuntu-latest + # The add-to-project action authenticates with BRUTUS_PAT_TOKEN, so the + # job's own GITHUB_TOKEN needs no permissions. + permissions: {} steps: - - uses: actions/add-to-project@v2.0.0 + - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0 with: project-url: https://github.com/orgs/beeware/projects/1 github-token: ${{ secrets.BRUTUS_PAT_TOKEN }} diff --git a/.github/workflows/pr-checklist.yml b/.github/workflows/pr-checklist.yml index cb5e287f..c3fce390 100644 --- a/.github/workflows/pr-checklist.yml +++ b/.github/workflows/pr-checklist.yml @@ -5,6 +5,9 @@ on: types: [opened, edited, reopened, synchronize] workflow_call: +permissions: + contents: read + jobs: check-pr-template: name: Check PR template diff --git a/.github/workflows/pre-commit-run.yml b/.github/workflows/pre-commit-run.yml index 9fb99758..54e60039 100644 --- a/.github/workflows/pre-commit-run.yml +++ b/.github/workflows/pre-commit-run.yml @@ -28,6 +28,9 @@ defaults: run: shell: bash +permissions: + contents: read + jobs: pre-commit: name: Pre-commit Checks @@ -36,12 +39,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ inputs.repository }} + persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6.3.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ inputs.python-version }} allow-prereleases: true @@ -52,7 +56,7 @@ jobs: .pre-commit-config.yaml - name: Cache pre-commit - uses: actions/cache@v6.1.0 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/pre-commit key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} @@ -61,7 +65,9 @@ jobs: run: python -m pip install -U pip - name: Install pre-commit - run: python -m pip install ${{ inputs.pre-commit-source }} + env: + PRE_COMMIT_SOURCE: ${{ inputs.pre-commit-source }} + run: python -m pip install ${PRE_COMMIT_SOURCE} - name: Run pre-commit id: pre-commit diff --git a/.github/workflows/python-package-create.yml b/.github/workflows/python-package-create.yml index 1dca18a5..6877d2e2 100644 --- a/.github/workflows/python-package-create.yml +++ b/.github/workflows/python-package-create.yml @@ -58,14 +58,15 @@ jobs: echo "differentiator=${DIFFERENTIATOR}" | tee -a ${GITHUB_OUTPUT} - name: Checkout - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ inputs.repository }} fetch-depth: 0 # Fetch all refs so setuptools_scm can generate the correct version number + persist-credentials: false - name: Build Package & Upload Artifact id: package - uses: hynek/build-and-inspect-python-package@v2.18.0 + uses: hynek/build-and-inspect-python-package@d44ca7d91762de7a7d5436ddae667c6da6d1c3df # v2.18.0 with: path: ${{ inputs.build-subdirectory || '.' }} upload-name-suffix: ${{ format('-{0}{1}', steps.config.outputs.repo-name, steps.config.outputs.differentiator) }} @@ -75,6 +76,7 @@ jobs: id: artifact env: BASENAME: ${{ steps.package.outputs.artifact-name }} + DIFFERENTIATOR: ${{ steps.config.outputs.differentiator }} run: | # trim the differentiator to generate the artifact's basename - echo "basename=${BASENAME%${{ steps.config.outputs.differentiator }}}" | tee -a ${GITHUB_OUTPUT} + echo "basename=${BASENAME%${DIFFERENTIATOR}}" | tee -a ${GITHUB_OUTPUT} diff --git a/.github/workflows/towncrier-run.yml b/.github/workflows/towncrier-run.yml index 6ebbb16f..3db88272 100644 --- a/.github/workflows/towncrier-run.yml +++ b/.github/workflows/towncrier-run.yml @@ -25,6 +25,7 @@ on: type: string permissions: + contents: read pull-requests: write defaults: @@ -42,20 +43,22 @@ jobs: steps: - name: Checkout Default Branch - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ inputs.repository }} ref: ${{ github.event.repository.default_branch }} fetch-depth: 0 + persist-credentials: false - name: Checkout Current Branch - uses: actions/checkout@v7 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ inputs.repository }} fetch-depth: 0 + persist-credentials: false - name: Setup Python - uses: actions/setup-python@v6.3.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ inputs.python-version }} allow-prereleases: true @@ -68,7 +71,9 @@ jobs: run: python -m pip install -U pip - name: Install towncrier - run: python -m pip install ${{ inputs.tox-source }} + env: + TOX_SOURCE: ${{ inputs.tox-source }} + run: python -m pip install ${TOX_SOURCE} - name: Run towncrier check id: tox @@ -79,16 +84,17 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} PR_NUM: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} run: | MESSAGE="Thank you for your pull request! Unfortunately, CI checks for this pull request are failing because you haven't included a change note in your contribution. Details on creating a change note can be found in the [BeeWare Contribution Guide](https://beeware.org/contributing/guide/how/change-note/). " - COMMENTS=$(gh api repos/${{ github.repository }}/issues/${PR_NUM}/comments) + COMMENTS=$(gh api "repos/${REPO}/issues/${PR_NUM}/comments") HAS_COMMENT=$(jq -r '.[] | select(.body | contains("")) | .body' <<< "$COMMENTS") if [ -z "$HAS_COMMENT" ]; then - gh api --method POST repos/${{ github.repository }}/issues/${PR_NUM}/comments -f body="$MESSAGE" + gh api --method POST "repos/${REPO}/issues/${PR_NUM}/comments" -f body="$MESSAGE" else echo "Comment already exists." fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8a2ddc93..6b4d2ed3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,3 +18,7 @@ repos: rev: v0.2.0 hooks: - id: rumdl + - repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: v1.26.1 + hooks: + - id: zizmor From ed4d03ba63fb82a5a5e70373aebd087e20709843 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 13 Jul 2026 15:50:08 +0800 Subject: [PATCH 2/7] Simplify docs-build-verify secret handling. --- .github/workflows/ci.yml | 2 +- .github/workflows/docs-build-verify.yml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1cb51d6..d8688254 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: test-docs-build-verify: name: Verify Docs Build needs: pre-commit - # inherited secrets are not exposed to forked repos + # inherited secrets are not exposed to forked repos, so we can't run there if: ${{ github.event.pull_request.head.repo.owner.login == 'beeware' }} uses: ./.github/workflows/docs-build-verify.yml secrets: diff --git a/.github/workflows/docs-build-verify.yml b/.github/workflows/docs-build-verify.yml index e63c0f8a..3040eb72 100644 --- a/.github/workflows/docs-build-verify.yml +++ b/.github/workflows/docs-build-verify.yml @@ -45,12 +45,11 @@ jobs: # Pass values through the environment rather than interpolating them # directly into the script, to avoid injection and secret leakage. PROJECT_VERSION: ${{ inputs.project-version }} - RTD_API_TOKEN: ${{ secrets.RTD_API_TOKEN }} run: | echo "Verifying docs for ${PROJECT_VERSION} were successfully built:" while true do - RTD_RESPONSE=$(curl -s -H "Authorization: Token ${RTD_API_TOKEN}" "${RTD_API_URL}") + RTD_RESPONSE=$(curl -s -H "Authorization: Token ${{ secrets.RTD_API_TOKEN }}" "${RTD_API_URL}") IS_DOCS_BUILT=$(jq .built <<< "${RTD_RESPONSE}") echo "::group::IS_DOCS_BUILT=${IS_DOCS_BUILT}" From de366e6897a47ee4b167db9c08c5366e498cd027 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 13 Jul 2026 15:58:34 +0800 Subject: [PATCH 3/7] Add permissions for packaging test. --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8688254..f84a114d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -168,6 +168,11 @@ jobs: test-package-python: name: Create Package needs: pre-commit + # id-token and attestations permissions are needed for packaging. + permissions: + id-token: write + contents: read + attestations: write uses: ./.github/workflows/python-package-create.yml with: repository: beeware/${{ matrix.repo || matrix.name }} From 1bd18a3603054c73b53ac096b362f9ca392ba313 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 14 Jul 2026 09:41:31 +0800 Subject: [PATCH 4/7] Consistency cleanups. --- .github/actions/app-create/action.yml | 33 +++++------ .github/actions/install-briefcase/action.yml | 61 +++++++++----------- .github/workflows/app-build-verify.yml | 45 ++++++++------- .github/workflows/ci.yml | 6 +- .github/workflows/coc-update.yml | 12 ++-- .github/workflows/dependabot-changenote.yml | 12 ++-- .github/workflows/docs-build-verify.yml | 4 +- .github/workflows/pre-commit-run.yml | 4 +- .github/workflows/python-package-create.yml | 4 +- .github/workflows/towncrier-run.yml | 11 ++-- 10 files changed, 93 insertions(+), 99 deletions(-) diff --git a/.github/actions/app-create/action.yml b/.github/actions/app-create/action.yml index 01a2a4ad..445c883c 100644 --- a/.github/actions/app-create/action.yml +++ b/.github/actions/app-create/action.yml @@ -29,19 +29,18 @@ runs: - name: Briefcase Template Override id: template-override env: + GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} GITHUB_TOKEN: ${{ github.token }} - TESTING_PR_BODY: ${{ inputs.testing-pr-body }} - PR_NUMBER: ${{ github.event.pull_request.number }} - REPO_FULL: ${{ github.repository }} - TEMPLATE_SOURCE: ${{ inputs.briefcase-template-source }} - TEMPLATE_BRANCH_INPUT: ${{ inputs.briefcase-template-branch }} + INPUT_BRIEFCASE_TEMPLATE_BRANCH: ${{ inputs.briefcase-template-branch }} + INPUT_BRIEFCASE_TEMPLATE_SOURCE: ${{ inputs.briefcase-template-source }} + INPUT_TESTING_PR_BODY: ${{ inputs.testing-pr-body }} shell: bash run: | # Check Body of PR for template to use # (only PRs will have a value for github.event.pull_request.number) - PR_BODY="${TESTING_PR_BODY}" - if [[ -z "${PR_BODY}" && -n "${PR_NUMBER}" ]]; then - PR_BODY=$(gh api "repos/${REPO_FULL}/pulls/${PR_NUMBER}" --jq '.body') + PR_BODY="${INPUT_TESTING_PR_BODY}" + if [[ -z "${PR_BODY}" && -n "${GITHUB_PR_NUMBER}" ]]; then + PR_BODY=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${GITHUB_PR_NUMBER}" --jq '.body') fi printf "::group::Retrieved PR Body\n%s\n::endgroup::\n" "${PR_BODY}" @@ -50,10 +49,10 @@ runs: # If a template is not in the PR, use inputs specified in CI workflow if [ -z "${TEMPLATE_REPO}" ]; then - TEMPLATE_REPO="${TEMPLATE_SOURCE}" + TEMPLATE_REPO="${INPUT_BRIEFCASE_TEMPLATE_SOURCE}" fi if [ -z "${TEMPLATE_REF}" ]; then - TEMPLATE_REF="${TEMPLATE_BRANCH_INPUT}" + TEMPLATE_REF="${INPUT_BRIEFCASE_TEMPLATE_BRANCH}" fi # Expose template repo and branch via outputs @@ -64,10 +63,10 @@ runs: id: create shell: bash env: - TEMPLATE_REPO: ${{ steps.template-override.outputs.repo }} + INPUT_ENABLE_AUTOMATION: ${{ inputs.enable-automation }} + INPUT_FRAMEWORK: ${{ inputs.framework }} TEMPLATE_REF: ${{ steps.template-override.outputs.ref }} - FRAMEWORK: ${{ inputs.framework }} - ENABLE_AUTOMATION: ${{ inputs.enable-automation }} + TEMPLATE_REPO: ${{ steps.template-override.outputs.repo }} run: | if [[ "${TEMPLATE_REPO}" != "" ]]; then TEMPLATE=$(printf -- "--template %q" "${TEMPLATE_REPO}") @@ -77,16 +76,16 @@ runs: fi # Map GUI toolkit through case insensitivity - case "$(tr '[:upper:]' '[:lower:]' <<< "${FRAMEWORK}")" in + case "$(tr '[:upper:]' '[:lower:]' <<< "${INPUT_FRAMEWORK}")" in toga ) BOOTSTRAP=Toga ;; pyside6 ) BOOTSTRAP=PySide6 ;; pygame ) BOOTSTRAP=Pygame ;; console ) BOOTSTRAP=Console ;; - * ) BOOTSTRAP="${FRAMEWORK}" ;; + * ) BOOTSTRAP="${INPUT_FRAMEWORK}" ;; esac # If enabled, use the Automation bootstrap - [ "${ENABLE_AUTOMATION}" != "false" ] && BOOTSTRAP="${BOOTSTRAP} Automation" + [ "${INPUT_ENABLE_AUTOMATION}" != "false" ] && BOOTSTRAP="${BOOTSTRAP} Automation" ROOT_DIR="apps" APP_NAME="Verify App" @@ -105,7 +104,7 @@ runs: -Q "bootstrap=${BOOTSTRAP}" echo "Rolled out project to ${APP_PATH}" - echo " > GUI Toolkit: ${FRAMEWORK}" + echo " > GUI Toolkit: ${INPUT_FRAMEWORK}" echo " > Bootstrap: ${BOOTSTRAP}" printf "::group::pyproject.toml\n%s\n::endgroup::\n" "$(cat "${APP_DIR}/pyproject.toml")" printf "::group::app.py\n%s\n::endgroup::\n" "$(cat "${APP_DIR}/src/${APP_DIR}/app.py")" diff --git a/.github/actions/install-briefcase/action.yml b/.github/actions/install-briefcase/action.yml index 8b17429a..69373cc2 100644 --- a/.github/actions/install-briefcase/action.yml +++ b/.github/actions/install-briefcase/action.yml @@ -35,19 +35,19 @@ runs: - name: Use Specified Briefcase Version id: briefcase-override env: + GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} GITHUB_TOKEN: ${{ github.token }} - TESTING_PR_BODY: ${{ inputs.testing-pr-body }} - PR_NUMBER: ${{ github.event.pull_request.number }} - REPO_FULL: ${{ github.repository }} - BRIEFCASE_URL: ${{ inputs.briefcase-url }} - BRIEFCASE_OVERRIDE_VERSION: ${{ inputs.briefcase-override-version }} + INPUT_BRIEFCASE_OVERRIDE_VERSION: ${{ inputs.briefcase-override-version }} + INPUT_BRIEFCASE_URL: ${{ inputs.briefcase-url }} + INPUT_TESTING_PR_BODY: ${{ inputs.testing-pr-body }} shell: bash + run: | # Check Body of PR for Briefcase version to use # (only PRs will have a value for github.event.pull_request.number) - PR_BODY="${TESTING_PR_BODY}" - if [[ -z "${PR_BODY}" && -n "${PR_NUMBER}" ]]; then - PR_BODY=$(gh api "repos/${REPO_FULL}/pulls/${PR_NUMBER}" --jq '.body') + PR_BODY="${INPUT_TESTING_PR_BODY}" + if [[ -z "${PR_BODY}" && -n "${GITHUB_PR_NUMBER}" ]]; then + PR_BODY=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${GITHUB_PR_NUMBER}" --jq '.body') fi printf "::group::Retrieved PR Body\n%s\n::endgroup::\n" "${PR_BODY}" @@ -56,10 +56,10 @@ runs: # If a version is not in the PR, use inputs specified in CI workflow if [ -z "${BRIEFCASE_REPO}" ]; then - BRIEFCASE_REPO="${BRIEFCASE_URL}" + BRIEFCASE_REPO="${INPUT_BRIEFCASE_URL}" fi if [ -z "${BRIEFCASE_REF}" ]; then - BRIEFCASE_REF="${BRIEFCASE_OVERRIDE_VERSION}" + BRIEFCASE_REF="${INPUT_BRIEFCASE_OVERRIDE_VERSION}" fi # Expose repo and version via outputs @@ -71,16 +71,11 @@ runs: if: steps.briefcase-override.outputs.ref == '' shell: bash env: - TESTING_TRIGGER_REF: ${{ inputs.testing-trigger-ref }} - TESTING_PR_REF: ${{ inputs.testing-pr-ref }} - TESTING_REF_NAME: ${{ inputs.testing-ref-name }} - GITHUB_REF: ${{ github.ref }} - GITHUB_BASE_REF: ${{ github.base_ref }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_EVENT_NAME: ${{ github.event_name }} - BRIEFCASE_URL: ${{ inputs.briefcase-url }} - BRIEFCASE_DEFAULT_BRANCH: ${{ inputs.briefcase-default-branch }} + INPUT_BRIEFCASE_DEFAULT_BRANCH: ${{ inputs.briefcase-default-branch }} + INPUT_BRIEFCASE_URL: ${{ inputs.briefcase-url }} + INPUT_TESTING_PR_REF: ${{ inputs.testing-pr-ref }} + INPUT_TESTING_REF_NAME: ${{ inputs.testing-ref-name }} + INPUT_TESTING_TRIGGER_REF: ${{ inputs.testing-trigger-ref }} run: | # Branch or tag that triggered the workflow. # For example, when github.event is: @@ -88,17 +83,17 @@ runs: # refs/tags/ # release: refs/tags/ # pull_request: refs/pull//merge - TRIGGER_REF="${TESTING_TRIGGER_REF:-$GITHUB_REF}" + TRIGGER_REF="${INPUT_TESTING_TRIGGER_REF:-$GITHUB_REF}" # PR target branch (only available for pull requests) - PR_TARGET_REF="${TESTING_PR_REF:-$GITHUB_BASE_REF}" + PR_TARGET_REF="${INPUT_TESTING_PR_REF:-$GITHUB_BASE_REF}" # The short ref name of the branch or tag that triggered the workflow # For example: # push: main # release: v0.3.12 # pull_request: 18/merge - REF_NAME="${TESTING_REF_NAME:-$GITHUB_REF_NAME}" + REF_NAME="${INPUT_TESTING_REF_NAME:-$GITHUB_REF_NAME}" echo "::group::Workflow Trigger Details" echo "Event: ${GITHUB_EVENT_NAME}" @@ -122,8 +117,8 @@ runs: # Default to 'main' * ) - echo "::warning::Could not determine Briefcase version from '${TRIGGER_REF}'; defaulting to '${BRIEFCASE_DEFAULT_BRANCH}'." - TARGET_VERSION="${BRIEFCASE_DEFAULT_BRANCH}" + echo "::warning::Could not determine Briefcase version from '${TRIGGER_REF}'; defaulting to '${INPUT_BRIEFCASE_DEFAULT_BRANCH}'." + TARGET_VERSION="${INPUT_BRIEFCASE_DEFAULT_BRANCH}" ;; esac @@ -131,17 +126,17 @@ runs: echo "Derived Briefcase version: ${TARGET_VERSION}" # Use Briefcase's default branch - if [[ "${TARGET_VERSION}" == "${BRIEFCASE_DEFAULT_BRANCH}" ]]; then + if [[ "${TARGET_VERSION}" == "${INPUT_BRIEFCASE_DEFAULT_BRANCH}" ]]; then echo "version=${TARGET_VERSION}" >> ${GITHUB_OUTPUT} # Use version if it is a valid Briefcase tag - elif [[ $(git ls-remote --tags "${BRIEFCASE_URL}" "${TARGET_VERSION}") ]]; then + elif [[ $(git ls-remote --tags "${INPUT_BRIEFCASE_URL}" "${TARGET_VERSION}") ]]; then echo "version=${TARGET_VERSION}" >> ${GITHUB_OUTPUT} # Default to Briefcase's default branch else - echo "::warning::Unknown Briefcase version: '${TARGET_VERSION}'; defaulting to '${BRIEFCASE_DEFAULT_BRANCH}'." - echo "version=${BRIEFCASE_DEFAULT_BRANCH}" >> ${GITHUB_OUTPUT} + echo "::warning::Unknown Briefcase version: '${TARGET_VERSION}'; defaulting to '${INPUT_BRIEFCASE_DEFAULT_BRANCH}'." + echo "version=${INPUT_BRIEFCASE_DEFAULT_BRANCH}" >> ${GITHUB_OUTPUT} fi - name: Update pip @@ -152,12 +147,12 @@ runs: id: install shell: bash env: - OVERRIDE_REPO: ${{ steps.briefcase-override.outputs.repo }} - OVERRIDE_REF: ${{ steps.briefcase-override.outputs.ref }} DERIVED_VERSION: ${{ steps.briefcase-derived.outputs.version }} - BRIEFCASE_URL: ${{ inputs.briefcase-url }} + INPUT_BRIEFCASE_URL: ${{ inputs.briefcase-url }} + OVERRIDE_REF: ${{ steps.briefcase-override.outputs.ref }} + OVERRIDE_REPO: ${{ steps.briefcase-override.outputs.repo }} run: | - REPO="${OVERRIDE_REPO:-$BRIEFCASE_URL}" + REPO="${OVERRIDE_REPO:-$INPUT_BRIEFCASE_URL}" REF="${OVERRIDE_REF:-$DERIVED_VERSION}" echo "Installing ${REPO}@${REF}" diff --git a/.github/workflows/app-build-verify.yml b/.github/workflows/app-build-verify.yml index b5905ccd..315d4e2c 100644 --- a/.github/workflows/app-build-verify.yml +++ b/.github/workflows/app-build-verify.yml @@ -69,21 +69,21 @@ jobs: permissions: contents: read env: + PYSIDE6_SYSTEM_REQUIRES: libgl1 libqt6dbus6 libqt6gui6t64 libxcb-cursor0 SDL_AUDIODRIVER: dummy # disable audio for SDL TOGA_SYSTEM_REQUIRES: libcairo2-dev libcanberra-gtk3-module libegl1 libgirepository1.0-dev libgirepository-2.0-dev libthai-dev gir1.2-gtk-3.0 - PYSIDE6_SYSTEM_REQUIRES: libgl1 libqt6dbus6 libqt6gui6t64 libxcb-cursor0 steps: - name: Workflow Configuration id: config env: - RUNNER_OS: ${{ inputs.runner-os }} - REPOSITORY: ${{ inputs.repository }} - FRAMEWORK: ${{ inputs.framework }} - TARGET_PLATFORM: ${{ inputs.target-platform }} - TARGET_FORMAT: ${{ inputs.target-format }} + INPUT_FRAMEWORK: ${{ inputs.framework }} + INPUT_REPOSITORY: ${{ inputs.repository }} + INPUT_RUNNER_OS: ${{ inputs.runner-os }} + INPUT_TARGET_FORMAT: ${{ inputs.target-format }} + INPUT_TARGET_PLATFORM: ${{ inputs.target-platform }} run: | - case "$(tr '[:upper:]' '[:lower:]' <<< "${RUNNER_OS}")" in + case "$(tr '[:upper:]' '[:lower:]' <<< "${INPUT_RUNNER_OS}")" in ubuntu* ) BRIEFCASE_DIR="$HOME/.cache/briefcase" ;; macos* ) BRIEFCASE_DIR="$HOME/Library/Caches/org.beeware.briefcase" ;; windows* ) BRIEFCASE_DIR="$HOME/AppData/Local/BeeWare/briefcase/Cache" ;; @@ -91,10 +91,10 @@ jobs: esac echo "briefcase-data-dir=${BRIEFCASE_DIR}" | tee -a ${GITHUB_OUTPUT} - CACHE_KEY="${RUNNER_OS}|${REPOSITORY}|${FRAMEWORK}|${TARGET_PLATFORM}" + CACHE_KEY="${INPUT_RUNNER_OS}|${INPUT_REPOSITORY}|${INPUT_FRAMEWORK}|${INPUT_TARGET_PLATFORM}" # For Linux builds, the cache varies by the output format - case "$(tr '[:upper:]' '[:lower:]' <<< "${RUNNER_OS}")" in - ubuntu* ) CACHE_KEY="$CACHE_KEY|${TARGET_FORMAT}" ;; + case "$(tr '[:upper:]' '[:lower:]' <<< "${INPUT_RUNNER_OS}")" in + ubuntu* ) CACHE_KEY="$CACHE_KEY|${INPUT_TARGET_FORMAT}" ;; esac echo "cache-key=$(date +%Y-%m)|${CACHE_KEY}" | tee -a ${GITHUB_OUTPUT} @@ -127,9 +127,9 @@ jobs: # https://github.com/actions/runner-images/issues/12751. if: ${{ startsWith(inputs.runner-os, 'macos') && inputs.xcode-version }} env: - XCODE_VERSION: ${{ inputs.xcode-version }} + INPUT_XCODE_VERSION: ${{ inputs.xcode-version }} run: | - sudo xcode-select --switch "/Applications/Xcode_${XCODE_VERSION}.app" + sudo xcode-select --switch "/Applications/Xcode_${INPUT_XCODE_VERSION}.app" - name: Cache Briefcase Tools uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 @@ -185,11 +185,8 @@ jobs: # other repos will fetch the latest template version during `create` as normal. if: endsWith(inputs.repository, '-template') && inputs.repository != 'beeware/briefcase-template' id: output-format - env: - WORKSPACE: ${{ github.workspace }} run: | - # Since the current repo is cloned in to the workspace, use it for the template - echo "template-override=$(printf -- "--config template=%q" "'${WORKSPACE}'")" | tee -a ${GITHUB_OUTPUT} + echo "template-override=--config template='${GITHUB_WORKSPACE}'" | tee -a "${GITHUB_OUTPUT}" # In the steps below, the builtin functions (i.e. contains, startsWith, etc.) # are used for comparison (instead of ==) because they are case-insensitive. @@ -205,6 +202,8 @@ jobs: SDL_VIDEODRIVER: ${{ startsWith(inputs.framework, 'pygame') && 'dummy' || '' }} TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | + # TEMPLATE_OVERRIDE is intentionally expanded unquoted so it splits into + # the `--config template='...'` arguments; see the output-format step. briefcase create macOS app \ ${TEMPLATE_OVERRIDE} briefcase build macOS app @@ -278,11 +277,11 @@ jobs: && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - FRAMEWORK: ${{ inputs.framework }} + INPUT_FRAMEWORK: ${{ inputs.framework }} TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} run: | PACKAGES="python3-dev python3-pip build-essential" - case "$(tr '[:upper:]' '[:lower:]' <<< "${FRAMEWORK}")" in + case "$(tr '[:upper:]' '[:lower:]' <<< "${INPUT_FRAMEWORK}")" in toga ) PACKAGES="${PACKAGES} ${TOGA_SYSTEM_REQUIRES}" ;; pyside6 ) PACKAGES="${PACKAGES} ${PYSIDE6_SYSTEM_REQUIRES}" ;; esac @@ -475,8 +474,10 @@ jobs: && startsWith(inputs.framework, 'toga') working-directory: ${{ steps.create.outputs.project-path }} env: + # NB: do not name this RUNNER_OS — that is a built-in GitHub variable + # ("Linux"/"macOS"/"Windows") that would override this value. + INPUT_RUNNER_OS: ${{ inputs.runner-os }} TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} - RUNNER_OS: ${{ inputs.runner-os }} run: | export JAVA_HOME=${JAVA_HOME_17_X64} @@ -486,7 +487,7 @@ jobs: # Only run Android app on Linux since it's duplicative (and slow) on other platforms # (runner-os values are always canonical lowercase runner labels, e.g. ubuntu-24.04) - case "${RUNNER_OS}" in + case "${INPUT_RUNNER_OS}" in ubuntu* ) # allow access to KVM to run the emulator echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \ @@ -516,13 +517,13 @@ jobs: && startsWith(inputs.framework, 'toga') working-directory: ${{ steps.create.outputs.project-path }} env: + INPUT_IOS_SIMULATOR: ${{ inputs.ios-simulator }} TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} - IOS_SIMULATOR: ${{ inputs.ios-simulator }} run: | briefcase create iOS xcode \ ${TEMPLATE_OVERRIDE} briefcase build iOS xcode - briefcase run iOS xcode -d "${IOS_SIMULATOR}" + briefcase run iOS xcode -d "${INPUT_IOS_SIMULATOR}" briefcase package iOS xcode --adhoc-sign # Clean up built artefacts to preserve disk space diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f84a114d..c3f087d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,13 +137,13 @@ jobs: - name: Briefcase @ ${{ matrix.test-case }} env: - EXPECTED: ${{ matrix.expected }} INSTALLED_VERSION: ${{ steps.briefcase.outputs.installed-version }} + MATRIX_EXPECTED: ${{ matrix.expected }} run: | - if [[ "${EXPECTED}" != "${INSTALLED_VERSION}" ]]; then + if [[ "${MATRIX_EXPECTED}" != "${INSTALLED_VERSION}" ]]; then echo "installed-version: ${INSTALLED_VERSION}" echo "Found Briefcase version v$(briefcase -V)" - echo "Expected Briefcase version ${EXPECTED}" + echo "Expected Briefcase version ${MATRIX_EXPECTED}" exit 1 fi diff --git a/.github/workflows/coc-update.yml b/.github/workflows/coc-update.yml index 92d2b47f..6f023e2e 100644 --- a/.github/workflows/coc-update.yml +++ b/.github/workflows/coc-update.yml @@ -50,10 +50,10 @@ jobs: - name: Check if source repository id: check-if-source env: - FULL_REPO: ${{ matrix.full_repo }} + MATRIX_FULL_REPO: ${{ matrix.full_repo }} run: | - if [[ "${FULL_REPO}" == "$GITHUB_REPOSITORY" ]]; then - echo "Skipping source repository ($GITHUB_REPOSITORY)..." + if [[ "${MATRIX_FULL_REPO}" == "${GITHUB_REPOSITORY}" ]]; then + echo "Skipping source repository (${GITHUB_REPOSITORY})..." echo "is_source=1" >> "$GITHUB_OUTPUT" exit 0 fi @@ -126,9 +126,9 @@ jobs: steps.push-main.outputs.success == '0' working-directory: ./targetrepo env: - GH_TOKEN: ${{ secrets.BRUTUS_PAT_TOKEN }} - FULL_REPO: ${{ matrix.full_repo }} COMMIT_HASH: ${{ needs.plan-coc-update.outputs.commit_hash }} + GH_TOKEN: ${{ secrets.BRUTUS_PAT_TOKEN }} + MATRIX_FULL_REPO: ${{ matrix.full_repo }} run: | timestamp=$(date +%m%d%Y-%H%M%S) branch_name="coc-update-$timestamp" @@ -136,6 +136,6 @@ jobs: git push --set-upstream origin "$branch_name" gh pr create \ - --repo "${FULL_REPO}" \ + --repo "${MATRIX_FULL_REPO}" \ --title "Update Code of Conduct" \ --body "Updated via $GITHUB_REPOSITORY (${COMMIT_HASH})" diff --git a/.github/workflows/dependabot-changenote.yml b/.github/workflows/dependabot-changenote.yml index 536d8450..6cf358bc 100644 --- a/.github/workflows/dependabot-changenote.yml +++ b/.github/workflows/dependabot-changenote.yml @@ -43,18 +43,18 @@ jobs: - name: Commit Change Note env: + GITHUB_HEAD_COMMIT_ID: ${{ github.event.head_commit.id }} + GITHUB_HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} GITHUB_TOKEN: ${{ github.token }} - HEAD_COMMIT_ID: ${{ github.event.head_commit.id }} - HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - CHANGENOTE_FORMAT: ${{ inputs.changenote-format }} + INPUT_CHANGENOTE_FORMAT: ${{ inputs.changenote-format }} run: | # Retrieve PR number from GitHub API - PR_NUM=$(gh api "repos/{owner}/{repo}/commits/${HEAD_COMMIT_ID}/pulls" --jq '.[].number') - CHANGENOTE_FILEPATH="./changes/${PR_NUM}.misc.${CHANGENOTE_FORMAT}" + PR_NUM=$(gh api "repos/{owner}/{repo}/commits/${GITHUB_HEAD_COMMIT_ID}/pulls" --jq '.[].number') + CHANGENOTE_FILEPATH="./changes/${PR_NUM}.misc.${INPUT_CHANGENOTE_FORMAT}" # Create change note from first line of dependabot commit # Replace "Bump" with "Updated", and add markup to URLs. - NEWS=$(printf "%s" "${HEAD_COMMIT_MESSAGE}" | head -n1 | sed -e 's|Bump|Updated|' | sed -e 's|\(https://[^/]*/[^ .]*\)|<\1>|') + NEWS=$(printf "%s" "${GITHUB_HEAD_COMMIT_MESSAGE}" | head -n1 | sed -e 's|Bump|Updated|' | sed -e 's|\(https://[^/]*/[^ .]*\)|<\1>|') printf "%s.\n" "${NEWS}" > "${CHANGENOTE_FILEPATH}" # Commit the change note diff --git a/.github/workflows/docs-build-verify.yml b/.github/workflows/docs-build-verify.yml index 3040eb72..5dc9a9e4 100644 --- a/.github/workflows/docs-build-verify.yml +++ b/.github/workflows/docs-build-verify.yml @@ -44,9 +44,9 @@ jobs: env: # Pass values through the environment rather than interpolating them # directly into the script, to avoid injection and secret leakage. - PROJECT_VERSION: ${{ inputs.project-version }} + INPUT_PROJECT_VERSION: ${{ inputs.project-version }} run: | - echo "Verifying docs for ${PROJECT_VERSION} were successfully built:" + echo "Verifying docs for ${INPUT_PROJECT_VERSION} were successfully built:" while true do RTD_RESPONSE=$(curl -s -H "Authorization: Token ${{ secrets.RTD_API_TOKEN }}" "${RTD_API_URL}") diff --git a/.github/workflows/pre-commit-run.yml b/.github/workflows/pre-commit-run.yml index 54e60039..7a08b729 100644 --- a/.github/workflows/pre-commit-run.yml +++ b/.github/workflows/pre-commit-run.yml @@ -66,8 +66,8 @@ jobs: - name: Install pre-commit env: - PRE_COMMIT_SOURCE: ${{ inputs.pre-commit-source }} - run: python -m pip install ${PRE_COMMIT_SOURCE} + INPUT_PRE_COMMIT_SOURCE: ${{ inputs.pre-commit-source }} + run: python -m pip install ${INPUT_PRE_COMMIT_SOURCE} - name: Run pre-commit id: pre-commit diff --git a/.github/workflows/python-package-create.yml b/.github/workflows/python-package-create.yml index 6877d2e2..fc488664 100644 --- a/.github/workflows/python-package-create.yml +++ b/.github/workflows/python-package-create.yml @@ -51,10 +51,10 @@ jobs: - name: Determine Artifact Differentiators id: config env: - REPO: ${{ inputs.repository }} DIFFERENTIATOR: ${{ inputs.build-subdirectory && format('-{0}', inputs.build-subdirectory) || '' }} + INPUT_REPOSITORY: ${{ inputs.repository }} run: | - echo "repo-name=$(basename "${REPO}")" | tee -a ${GITHUB_OUTPUT} + echo "repo-name=$(basename "${INPUT_REPOSITORY}")" | tee -a ${GITHUB_OUTPUT} echo "differentiator=${DIFFERENTIATOR}" | tee -a ${GITHUB_OUTPUT} - name: Checkout diff --git a/.github/workflows/towncrier-run.yml b/.github/workflows/towncrier-run.yml index 3db88272..95710498 100644 --- a/.github/workflows/towncrier-run.yml +++ b/.github/workflows/towncrier-run.yml @@ -72,8 +72,8 @@ jobs: - name: Install towncrier env: - TOX_SOURCE: ${{ inputs.tox-source }} - run: python -m pip install ${TOX_SOURCE} + INPUT_TOX_SOURCE: ${{ inputs.tox-source }} + run: python -m pip install ${INPUT_TOX_SOURCE} - name: Run towncrier check id: tox @@ -82,19 +82,18 @@ jobs: - name: Add PR comment if change note is missing if: always() && github.event_name == 'pull_request' && steps.tox.outcome == 'failure' env: + GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} GITHUB_TOKEN: ${{ github.token }} - PR_NUM: ${{ github.event.pull_request.number }} - REPO: ${{ github.repository }} run: | MESSAGE="Thank you for your pull request! Unfortunately, CI checks for this pull request are failing because you haven't included a change note in your contribution. Details on creating a change note can be found in the [BeeWare Contribution Guide](https://beeware.org/contributing/guide/how/change-note/). " - COMMENTS=$(gh api "repos/${REPO}/issues/${PR_NUM}/comments") + COMMENTS=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${GITHUB_PR_NUMBER}/comments") HAS_COMMENT=$(jq -r '.[] | select(.body | contains("")) | .body' <<< "$COMMENTS") if [ -z "$HAS_COMMENT" ]; then - gh api --method POST "repos/${REPO}/issues/${PR_NUM}/comments" -f body="$MESSAGE" + gh api --method POST "repos/${GITHUB_REPOSITORY}/issues/${GITHUB_PR_NUMBER}/comments" -f body="$MESSAGE" else echo "Comment already exists." fi From 452cd38e44dc5551a9114466952f22dd02811a57 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 14 Jul 2026 10:04:43 +0800 Subject: [PATCH 5/7] Turn template override into a more generic config options. --- .github/workflows/app-build-verify.yml | 73 +++++++++++--------------- 1 file changed, 30 insertions(+), 43 deletions(-) diff --git a/.github/workflows/app-build-verify.yml b/.github/workflows/app-build-verify.yml index 315d4e2c..813951d1 100644 --- a/.github/workflows/app-build-verify.yml +++ b/.github/workflows/app-build-verify.yml @@ -43,6 +43,10 @@ on: description: "The target format for the app, e.g. appimage, xcode, app, etc. Leave blank all supported formats." default: "" type: string + create-options: + description: "Additional options to pass to `briefcase create`." + default: "" + type: string workflow-repo: # Only needed for PRs in other repos wanting to test new workflow changes before they are merged. # These inputs should not be specified by another repo on their main branch. @@ -200,12 +204,9 @@ jobs: env: # SDL cannot always find hardware acceleration on macOS in CI SDL_VIDEODRIVER: ${{ startsWith(inputs.framework, 'pygame') && 'dummy' || '' }} - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - # TEMPLATE_OVERRIDE is intentionally expanded unquoted so it splits into - # the `--config template='...'` arguments; see the output-format step. - briefcase create macOS app \ - ${TEMPLATE_OVERRIDE} + briefcase create macOS app ${CREATE_OPTIONS} briefcase build macOS app briefcase run macOS app briefcase package macOS app --adhoc-sign @@ -222,10 +223,9 @@ jobs: env: # SDL cannot always find hardware acceleration on macOS in CI SDL_VIDEODRIVER: ${{ startsWith(inputs.framework, 'pygame') && 'dummy' || '' }} - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - briefcase create macOS Xcode \ - ${TEMPLATE_OVERRIDE} + briefcase create macOS Xcode ${CREATE_OPTIONS} briefcase build macOS Xcode briefcase run macOS Xcode briefcase package macOS Xcode --adhoc-sign @@ -240,10 +240,9 @@ jobs: && contains(fromJSON('["", "app"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - briefcase create windows app \ - ${TEMPLATE_OVERRIDE} + briefcase create windows app ${CREATE_OPTIONS} briefcase build windows app briefcase run windows app briefcase package windows app --adhoc-sign @@ -258,10 +257,9 @@ jobs: && contains(fromJSON('["", "VisualStudio"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - briefcase create windows VisualStudio \ - ${TEMPLATE_OVERRIDE} + briefcase create windows VisualStudio ${CREATE_OPTIONS} briefcase build windows VisualStudio briefcase run windows VisualStudio briefcase package windows VisualStudio --adhoc-sign @@ -278,7 +276,7 @@ jobs: working-directory: ${{ steps.create.outputs.project-path }} env: INPUT_FRAMEWORK: ${{ inputs.framework }} - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | PACKAGES="python3-dev python3-pip build-essential" case "$(tr '[:upper:]' '[:lower:]' <<< "${INPUT_FRAMEWORK}")" in @@ -288,8 +286,7 @@ jobs: sudo apt install -y --no-install-recommends ${PACKAGES} - briefcase create linux system \ - ${TEMPLATE_OVERRIDE} + briefcase create linux system ${CREATE_OPTIONS} briefcase build linux system xvfb-run briefcase run linux system briefcase package linux system --adhoc-sign @@ -306,10 +303,9 @@ jobs: && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - briefcase create linux system --target debian:trixie \ - ${TEMPLATE_OVERRIDE} + briefcase create linux system --target debian:trixie ${CREATE_OPTIONS} briefcase build linux system --target debian:trixie xvfb-run briefcase run linux system --target debian:trixie briefcase package linux system --target debian:trixie --adhoc-sign @@ -326,10 +322,9 @@ jobs: && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - briefcase create linux system --target fedora:41 \ - ${TEMPLATE_OVERRIDE} + briefcase create linux system --target fedora:41 ${CREATE_OPTIONS} briefcase build linux system --target fedora:41 xvfb-run briefcase run linux system --target fedora:41 briefcase package linux system --target fedora:41 --adhoc-sign @@ -348,10 +343,9 @@ jobs: && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - briefcase create linux system --target archlinux:latest \ - ${TEMPLATE_OVERRIDE} + briefcase create linux system --target archlinux:latest ${CREATE_OPTIONS} briefcase build linux system --target archlinux:latest xvfb-run briefcase run linux system --target archlinux:latest briefcase package linux system --target archlinux:latest --adhoc-sign @@ -368,10 +362,9 @@ jobs: && contains(fromJSON('["", "system"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - briefcase create linux system --target opensuse/tumbleweed:latest \ - ${TEMPLATE_OVERRIDE} + briefcase create linux system --target opensuse/tumbleweed:latest ${CREATE_OPTIONS} briefcase build linux system --target opensuse/tumbleweed:latest xvfb-run briefcase run linux system --target opensuse/tumbleweed:latest briefcase package linux system --target opensuse/tumbleweed:latest --adhoc-sign @@ -451,12 +444,11 @@ jobs: && contains(fromJSON('["", "Flatpak"]'), inputs.target-format) working-directory: ${{ steps.create.outputs.project-path }} env: - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | sudo apt install -y --no-install-recommends flatpak flatpak-builder elfutils - briefcase create linux flatpak \ - ${TEMPLATE_OVERRIDE} + briefcase create linux flatpak ${CREATE_OPTIONS} briefcase build linux flatpak xvfb-run briefcase run linux flatpak briefcase package linux flatpak --adhoc-sign @@ -474,15 +466,12 @@ jobs: && startsWith(inputs.framework, 'toga') working-directory: ${{ steps.create.outputs.project-path }} env: - # NB: do not name this RUNNER_OS — that is a built-in GitHub variable - # ("Linux"/"macOS"/"Windows") that would override this value. INPUT_RUNNER_OS: ${{ inputs.runner-os }} - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | export JAVA_HOME=${JAVA_HOME_17_X64} - briefcase create android gradle \ - ${TEMPLATE_OVERRIDE} + briefcase create android gradle ${CREATE_OPTIONS} briefcase build android gradle # Only run Android app on Linux since it's duplicative (and slow) on other platforms @@ -518,10 +507,9 @@ jobs: working-directory: ${{ steps.create.outputs.project-path }} env: INPUT_IOS_SIMULATOR: ${{ inputs.ios-simulator }} - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - briefcase create iOS xcode \ - ${TEMPLATE_OVERRIDE} + briefcase create iOS xcode ${CREATE_OPTIONS} briefcase build iOS xcode briefcase run iOS xcode -d "${INPUT_IOS_SIMULATOR}" briefcase package iOS xcode --adhoc-sign @@ -536,10 +524,9 @@ jobs: && startsWith(inputs.framework, 'toga') working-directory: ${{ steps.create.outputs.project-path }} env: - TEMPLATE_OVERRIDE: ${{ steps.output-format.outputs.template-override }} + CREATE_OPTIONS: ${{ steps.output-format.outputs.template-override }} ${{ inputs.create-options }} run: | - briefcase create web static \ - ${TEMPLATE_OVERRIDE} + briefcase create web static ${CREATE_OPTIONS} briefcase build web static briefcase package web static From aec49a02be42b22a8cb274bc62cba1c8da002c01 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 14 Jul 2026 10:54:52 +0800 Subject: [PATCH 6/7] Rename CI tasks. --- .github/workflows/ci.yml | 56 ++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3f087d4..13c5a210 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: pre-commit-source: -r requirements.txt test-docs-build-verify: - name: Verify Docs Build + name: Docs Build Verify needs: pre-commit # inherited secrets are not exposed to forked repos, so we can't run there if: ${{ github.event.pull_request.head.repo.owner.login == 'beeware' }} @@ -148,7 +148,7 @@ jobs: fi test-pre-commit-run: - name: Pre-commit + name: Pre-commit Run needs: pre-commit uses: ./.github/workflows/pre-commit-run.yml with: @@ -165,8 +165,8 @@ jobs: - repo: rubicon-objc pre-commit-source: "--group pre-commit" - test-package-python: - name: Create Package + test-python-package-create: + name: Python Package Create needs: pre-commit # id-token and attestations permissions are needed for packaging. permissions: @@ -197,9 +197,9 @@ jobs: repo: toga build-subdir: android - test-verify-projects-briefcase: - name: Verify Project - needs: [ pre-commit, test-package-python ] + test-app-create-verify: + name: App Create Verify + needs: pre-commit uses: ./.github/workflows/app-create-verify.yml with: # Falls back to 3.x in called workflow if undefined @@ -213,9 +213,9 @@ jobs: framework: [ toga, pyside6 ] runner-os: [ macos-15, windows-latest, ubuntu-24.04, ubuntu-24.04-arm ] - test-verify-apps-briefcase: - name: Verify Briefcase - needs: [ pre-commit, test-package-python ] + test-app-build-verify: + name: App Build Verify (Default templates) + needs: pre-commit uses: ./.github/workflows/app-build-verify.yml with: # Falls back to 3.x in called workflow if undefined @@ -229,8 +229,8 @@ jobs: framework: [ toga ] runner-os: [ macos-15-intel, macos-15, windows-latest, ubuntu-24.04, ubuntu-24.04-arm ] - test-verify-apps-briefcase-template: - name: Verify Briefcase Template + test-app-build-verify-template: + name: App Build Verify (Briefcase template) needs: pre-commit uses: ./.github/workflows/app-build-verify.yml with: @@ -244,8 +244,8 @@ jobs: framework: [ toga ] runner-os: [ macos-15, windows-latest, ubuntu-24.04, ubuntu-24.04-arm ] - test-verify-apps-android-templates: - name: Verify Android + test-app-build-verify-android: + name: App Build Verify (Android template) needs: pre-commit uses: ./.github/workflows/app-build-verify.yml with: @@ -260,8 +260,8 @@ jobs: framework: [ toga ] # toga only runner-os: [ macos-15, windows-latest, ubuntu-24.04 ] - test-verify-apps-iOS-templates: - name: Verify iOS + test-app-build-verify-iOS: + name: App Build Verify (iOS template) needs: pre-commit uses: ./.github/workflows/app-build-verify.yml with: @@ -275,8 +275,8 @@ jobs: matrix: framework: [ toga ] # toga only - test-verify-apps-linux-system-templates: - name: Verify Linux + test-app-build-verify-linux-system: + name: App Build Verify (System template) needs: pre-commit uses: ./.github/workflows/app-build-verify.yml with: @@ -294,8 +294,8 @@ jobs: runner-os: [ ubuntu-24.04, ubuntu-24.04-arm ] # see app-build-verify.yml; AppImage testing was disabled sept 2024 -# test-verify-apps-appimage-templates: -# name: Verify AppImage +# test-app-build-verify-linux-appimage: +# name: App Build Verify (AppImage template) # needs: pre-commit # uses: ./.github/workflows/app-build-verify.yml # with: @@ -310,8 +310,8 @@ jobs: # # 2024-07-11 (beeware/briefcase#1908): pyside6 segfaults on AppImage start. # framework: [ toga, pygame, console ] - test-verify-apps-flatpak-templates: - name: Verify Flatpak + test-app-build-verify-linux-flatpak: + name: App Build Verify (Flatpak template) needs: pre-commit uses: ./.github/workflows/app-build-verify.yml with: @@ -332,8 +332,8 @@ jobs: - framework: pyside6 runner-os: ubuntu-24.04-arm - test-verify-apps-macOS-templates: - name: Verify macOS + test-app-build-verify-macOS: + name: App Build Verify (macOS templates) needs: pre-commit uses: ./.github/workflows/app-build-verify.yml with: @@ -348,8 +348,8 @@ jobs: framework: [ toga, pyside6, pygame, console ] format: [ app, Xcode ] - test-verify-apps-web-templates: - name: Verify Web + test-app-build-verify-web: + name: App Build Verify (Web template) needs: pre-commit uses: ./.github/workflows/app-build-verify.yml with: @@ -363,8 +363,8 @@ jobs: matrix: framework: [ toga ] # toga only - test-verify-apps-windows-templates: - name: Verify Windows + test-app-build-verify-windows: + name: App Build Verify (Windows templates) needs: pre-commit uses: ./.github/workflows/app-build-verify.yml with: From 9c2a4c5da6ffd26d8993c615dacc4cb7945f23cd Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 14 Jul 2026 10:57:03 +0800 Subject: [PATCH 7/7] Restore package task dependency. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13c5a210..0543895f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -199,7 +199,7 @@ jobs: test-app-create-verify: name: App Create Verify - needs: pre-commit + needs: [ pre-commit, test-python-package-create ] uses: ./.github/workflows/app-create-verify.yml with: # Falls back to 3.x in called workflow if undefined @@ -215,7 +215,7 @@ jobs: test-app-build-verify: name: App Build Verify (Default templates) - needs: pre-commit + needs: [ pre-commit, test-python-package-create ] uses: ./.github/workflows/app-build-verify.yml with: # Falls back to 3.x in called workflow if undefined