From 95acecb9d7db7939f03e666e55d66cb768cb6009 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 28 Aug 2026 17:57:56 +0100 Subject: [PATCH 1/5] Add actionlint checks for GitHub Actions --- .github/workflows/daily.yml | 19 ++++++---- .github/workflows/mypy_primer.yml | 6 ++-- .github/workflows/stubtest_third_party.yml | 31 ++++++++++------ .github/workflows/tests.yml | 41 +++++++++++++++++----- .pre-commit-config.yaml | 12 +++++++ 5 files changed, 80 insertions(+), 29 deletions(-) diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index ed097604d989..066aa70c1845 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -85,21 +85,28 @@ jobs: shell: bash run: | PACKAGES=$(python tests/get_stubtest_system_requirements.py) + PACKAGE_ARGS=() + while IFS= read -r package; do + PACKAGE_ARGS+=("$package") + done <<< "$PACKAGES" if [ "${{ runner.os }}" = "Linux" ]; then if [ -n "$PACKAGES" ]; then - printf "Installing APT packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n" - sudo apt-get update -q && sudo apt-get install -qy $PACKAGES + printf 'Installing APT packages:\n' + printf ' %s\n' "${PACKAGE_ARGS[@]}" + sudo apt-get update -q && sudo apt-get install -qy "${PACKAGE_ARGS[@]}" fi else if [ "${{ runner.os }}" = "macOS" ] && [ -n "$PACKAGES" ]; then - printf "Installing Homebrew packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n" - brew install -q $PACKAGES + printf 'Installing Homebrew packages:\n' + printf ' %s\n' "${PACKAGE_ARGS[@]}" + brew install -q "${PACKAGE_ARGS[@]}" fi if [ "${{ runner.os }}" = "Windows" ] && [ -n "$PACKAGES" ]; then - printf "Installing Chocolatey packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n" - choco install -y $PACKAGES + printf 'Installing Chocolatey packages:\n' + printf ' %s\n' "${PACKAGE_ARGS[@]}" + choco install -y "${PACKAGE_ARGS[@]}" fi fi - name: Run stubtest diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index 4f7cdfac587c..41f0ca2abd8d 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -41,7 +41,7 @@ jobs: cd typeshed_to_test MYPY_VERSION=$(grep mypy== requirements-tests.txt | cut -d = -f 3) echo "new commit" - git rev-list --format=%s --max-count=1 $GITHUB_SHA + git rev-list --format=%s --max-count=1 "$GITHUB_SHA" git checkout -b upstream_main origin/main echo "base commit" git rev-list --format=%s --max-count=1 upstream_main @@ -50,9 +50,9 @@ jobs: # fail action if exit code isn't zero or one ( mypy_primer \ - --new v${MYPY_VERSION} --old v${MYPY_VERSION} \ + --new "v${MYPY_VERSION}" --old "v${MYPY_VERSION}" \ --custom-typeshed-repo typeshed_to_test \ - --new-typeshed $GITHUB_SHA --old-typeshed upstream_main \ + --new-typeshed "$GITHUB_SHA" --old-typeshed upstream_main \ --num-shards 6 --shard-index ${{ matrix.shard-index }} \ --debug \ --output concise \ diff --git a/.github/workflows/stubtest_third_party.yml b/.github/workflows/stubtest_third_party.yml index e3763ae5de03..13602e169ea7 100644 --- a/.github/workflows/stubtest_third_party.yml +++ b/.github/workflows/stubtest_third_party.yml @@ -56,31 +56,39 @@ jobs: # Use the daily.yml workflow to run stubtest on all third party stubs. function find_stubs { git diff --name-only "origin/${GITHUB_BASE_REF}" HEAD | \ - egrep ^stubs/ | cut -d "/" -f 2 | sort -u | \ - (while read stub; do [ -d "stubs/$stub" ] && echo -n "$stub " || true; done) + grep -E ^stubs/ | cut -d "/" -f 2 | sort -u | \ + (while IFS= read -r stub; do [ -d "stubs/$stub" ] && echo -n "$stub " || true; done) } STUBS=$(find_stubs || echo '') echo "Changed stubs: $STUBS" - echo "STUBS=$STUBS" >> $GITHUB_ENV + echo "STUBS=$STUBS" >> "$GITHUB_ENV" - name: Install required system packages shell: bash run: | if [ -n "$STUBS" ]; then - PACKAGES=$(python tests/get_stubtest_system_requirements.py $STUBS) + read -r -a STUB_ARGS <<< "$STUBS" + PACKAGES=$(python tests/get_stubtest_system_requirements.py "${STUB_ARGS[@]}") + PACKAGE_ARGS=() + while IFS= read -r package; do + PACKAGE_ARGS+=("$package") + done <<< "$PACKAGES" if [ "${{ runner.os }}" = "Linux" ]; then if [ -n "$PACKAGES" ]; then - printf "Installing APT packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n" - sudo apt-get update -q && sudo apt-get install -qy $PACKAGES + printf 'Installing APT packages:\n' + printf ' %s\n' "${PACKAGE_ARGS[@]}" + sudo apt-get update -q && sudo apt-get install -qy "${PACKAGE_ARGS[@]}" fi else if [ "${{ runner.os }}" = "macOS" ] && [ -n "$PACKAGES" ]; then - printf "Installing Homebrew packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n" - brew install -q $PACKAGES + printf 'Installing Homebrew packages:\n' + printf ' %s\n' "${PACKAGE_ARGS[@]}" + brew install -q "${PACKAGE_ARGS[@]}" fi if [ "${{ runner.os }}" = "Windows" ] && [ -n "$PACKAGES" ]; then - printf "Installing Chocolatey packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n" - choco install -y $PACKAGES + printf 'Installing Chocolatey packages:\n' + printf ' %s\n' "${PACKAGE_ARGS[@]}" + choco install -y "${PACKAGE_ARGS[@]}" fi fi fi @@ -89,6 +97,7 @@ jobs: run: | if [ -n "$STUBS" ]; then echo "Testing $STUBS..." + read -r -a STUB_ARGS <<< "$STUBS" if [ "${{ runner.os }}" = "Linux" ]; then PYTHON_EXECUTABLE="xvfb-run python" @@ -96,7 +105,7 @@ jobs: PYTHON_EXECUTABLE="python" fi - $PYTHON_EXECUTABLE tests/stubtest_third_party.py --ci-platforms-only $STUBS + $PYTHON_EXECUTABLE tests/stubtest_third_party.py --ci-platforms-only "${STUB_ARGS[@]}" else echo "Nothing to test" fi diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3f08dea1dc61..756f29bf621d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -64,8 +64,13 @@ jobs: run: | PACKAGES=$(python tests/get_stubtest_system_requirements.py) if [ -n "$PACKAGES" ]; then - printf "Installing APT packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n" - sudo apt-get update -q && sudo apt-get install -qy $PACKAGES + PACKAGE_ARGS=() + while IFS= read -r package; do + PACKAGE_ARGS+=("$package") + done <<< "$PACKAGES" + printf 'Installing APT packages:\n' + printf ' %s\n' "${PACKAGE_ARGS[@]}" + sudo apt-get update -q && sudo apt-get install -qy "${PACKAGE_ARGS[@]}" fi - name: Run mypy_test.py run: python ./tests/mypy_test.py --platform=${{ matrix.platform }} --python-version=${{ matrix.python-version }} @@ -117,7 +122,11 @@ jobs: run: | PACKAGES=$(python tests/get_external_stub_requirements.py) if [ -n "$PACKAGES" ]; then - uv pip install --python-version ${{ matrix.python-version }} $PACKAGES + PACKAGE_ARGS=() + while IFS= read -r package; do + PACKAGE_ARGS+=("$package") + done <<< "$PACKAGES" + uv pip install --python-version ${{ matrix.python-version }} "${PACKAGE_ARGS[@]}" fi # Published stub packages can shadow the checked-in stubs when ty # resolves their relative imports. @@ -152,7 +161,11 @@ jobs: run: | PACKAGES=$(python tests/get_external_stub_requirements.py) if [ -n "$PACKAGES" ]; then - uv pip install --python-version ${{ matrix.python-version }} $PACKAGES + PACKAGE_ARGS=() + while IFS= read -r package; do + PACKAGE_ARGS+=("$package") + done <<< "$PACKAGES" + uv pip install --python-version ${{ matrix.python-version }} "${PACKAGE_ARGS[@]}" fi # Published stub packages can shadow the checked-in stubs when pyrefly # resolves their relative imports. @@ -187,8 +200,13 @@ jobs: run: | PACKAGES=$(python tests/get_stubtest_system_requirements.py) if [ -n "$PACKAGES" ]; then - printf "Installing APT packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n" - sudo apt-get update -q && sudo apt-get install -qy $PACKAGES + PACKAGE_ARGS=() + while IFS= read -r package; do + PACKAGE_ARGS+=("$package") + done <<< "$PACKAGES" + printf 'Installing APT packages:\n' + printf ' %s\n' "${PACKAGE_ARGS[@]}" + sudo apt-get update -q && sudo apt-get install -qy "${PACKAGE_ARGS[@]}" fi - name: Create an isolated venv for testing run: uv venv .venv @@ -196,11 +214,16 @@ jobs: run: | PACKAGES=$(python tests/get_external_stub_requirements.py) if [ -n "$PACKAGES" ]; then - printf "Installing python packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n" - uv pip install --python-version ${{ matrix.python-version }} $PACKAGES + PACKAGE_ARGS=() + while IFS= read -r package; do + PACKAGE_ARGS+=("$package") + done <<< "$PACKAGES" + printf 'Installing python packages:\n' + printf ' %s\n' "${PACKAGE_ARGS[@]}" + uv pip install --python-version ${{ matrix.python-version }} "${PACKAGE_ARGS[@]}" fi - name: Activate the isolated venv for the rest of the job - run: echo "$PWD/.venv/bin" >> $GITHUB_PATH + run: echo "$PWD/.venv/bin" >> "$GITHUB_PATH" - name: List 3rd-party stub dependencies installed run: uv pip freeze - name: Run pyright with basic settings on all the stubs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 47dac83f561b..43bc9fcaebb6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,6 +48,18 @@ repos: rev: 451b56af716f9f0d0c2b816503a3fd0cf8b036fa # frozen: v1.29.0 hooks: - id: zizmor + # `actionlint` hook, for verifying correct syntax in GitHub Actions workflows. + - repo: https://github.com/rhysd/actionlint + rev: 914e7df21a07ef503a81201c76d2b11c789d3fca # frozen: v1.7.12 + hooks: + - id: actionlint + # specifying this means renovate will also update `additional_dependencies` + language: golang + additional_dependencies: + # actionlint has a shellcheck integration which extracts shell scripts in `run:` steps from GitHub Actions + # and checks these with shellcheck. This is arguably its most useful feature, + # but the integration only works if shellcheck is installed + - "github.com/wasilibs/go-shellcheck/cmd/shellcheck@v0.11.1" ci: autofix_commit_msg: "[pre-commit.ci] auto fixes from pre-commit.com hooks" From d33800d8d3168d2da92bd6e39b24aa9148f6a65c Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 28 Aug 2026 18:23:53 +0100 Subject: [PATCH 2/5] Run actionlint in GitHub Actions instead of pre-commit.ci --- .github/workflows/actionlint.yml | 32 ++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 12 ------------ 2 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/actionlint.yml diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 000000000000..ebb944b59daf --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,32 @@ +name: Lint GitHub Actions workflows + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - ".github/workflows/**" + pull_request: + paths: + - ".github/workflows/**" + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + actionlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + - name: Run actionlint + # The official image includes ShellCheck and Pyflakes. + uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 + with: + args: -color diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 43bc9fcaebb6..47dac83f561b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,18 +48,6 @@ repos: rev: 451b56af716f9f0d0c2b816503a3fd0cf8b036fa # frozen: v1.29.0 hooks: - id: zizmor - # `actionlint` hook, for verifying correct syntax in GitHub Actions workflows. - - repo: https://github.com/rhysd/actionlint - rev: 914e7df21a07ef503a81201c76d2b11c789d3fca # frozen: v1.7.12 - hooks: - - id: actionlint - # specifying this means renovate will also update `additional_dependencies` - language: golang - additional_dependencies: - # actionlint has a shellcheck integration which extracts shell scripts in `run:` steps from GitHub Actions - # and checks these with shellcheck. This is arguably its most useful feature, - # but the integration only works if shellcheck is installed - - "github.com/wasilibs/go-shellcheck/cmd/shellcheck@v0.11.1" ci: autofix_commit_msg: "[pre-commit.ci] auto fixes from pre-commit.com hooks" From d7ac441d25868e1215b1dd471055cfc79e47fe59 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 28 Aug 2026 18:33:36 +0100 Subject: [PATCH 3/5] Show actionlint progress in CI --- .github/workflows/actionlint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index ebb944b59daf..e53a98041065 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -29,4 +29,4 @@ jobs: # The official image includes ShellCheck and Pyflakes. uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 with: - args: -color + args: -color -verbose From 0a36c867b8bbec0e5f4f81290d94c6e0003c1677 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 28 Aug 2026 18:34:02 +0100 Subject: [PATCH 4/5] Demonstrate actionlint failure with an unquoted variable --- .github/workflows/actionlint.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index e53a98041065..987ef581463c 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -25,6 +25,8 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: persist-credentials: false + - name: Demonstrate a ShellCheck violation + run: echo $GITHUB_WORKSPACE - name: Run actionlint # The official image includes ShellCheck and Pyflakes. uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 From 6611c6c06b1c69e9b26349e1c306c8833efd3c13 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 28 Aug 2026 18:41:17 +0100 Subject: [PATCH 5/5] Revert "Demonstrate actionlint failure with an unquoted variable" This reverts commit 0a36c867b8bbec0e5f4f81290d94c6e0003c1677. --- .github/workflows/actionlint.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index 987ef581463c..e53a98041065 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -25,8 +25,6 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: persist-credentials: false - - name: Demonstrate a ShellCheck violation - run: echo $GITHUB_WORKSPACE - name: Run actionlint # The official image includes ShellCheck and Pyflakes. uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667