Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/actions/check-state/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: Check Python action state
description: Verify multiline state transfer from an action's main hook to its post hook.
runs:
using: node24
main: main.cjs
post: post.cjs
26 changes: 26 additions & 0 deletions .github/actions/check-state/check_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Verify action state in main/post hooks; local actions do not support pre."""

import sys
from collections.abc import Sequence

import github_action_utils as gha

MAIN_VALUE = "from main\n100% %0A\nhéllo 世界\n"


def main(argv: Sequence[str] | None = None) -> None:
"""Write main state or assert the exact state received by the post hook."""
arguments = list(sys.argv[1:] if argv is None else argv)
match arguments:
case ["post"]:
assert gha.get_state("from_main") == MAIN_VALUE
print("Post hook received exact main state")
case ["main"]:
gha.save_state("from_main", MAIN_VALUE)
print("Main hook saved state")
case _:
raise ValueError("Expected main or post")


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions .github/actions/check-state/main.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require("./state.cjs")("main");
1 change: 1 addition & 0 deletions .github/actions/check-state/post.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require("./state.cjs")("post");
13 changes: 13 additions & 0 deletions .github/actions/check-state/state.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { spawnSync } = require("node:child_process");
const path = require("node:path");

// The runner supplies STATE_* values to each new hook process.
module.exports = (phase) => {
const result = spawnSync(
"python",
[path.join(__dirname, "check_state.py"), phase],
{ stdio: "inherit" },
);
if (result.error) throw result.error;
process.exit(result.status === null ? 1 : result.status);
};
2 changes: 1 addition & 1 deletion .github/dependabot.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: 2
updates:
- package-ecosystem: pip
- package-ecosystem: uv
directory: "/"
schedule:
interval: monthly
Expand Down
16 changes: 13 additions & 3 deletions .github/workflows/changelog-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,22 @@ on:
pull_request:
types: [ opened ]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: write
pull-requests: write

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.head_ref }}

- name: Run Changelog CI
uses: saadmk11/changelog-ci@v1.1.1
uses: saadmk11/changelog-ci@ed0c1a6b31e9b31d1baa74f66261d5d39c1f9705 # v2.0.2
116 changes: 94 additions & 22 deletions .github/workflows/pypi_publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,101 @@ on:
permissions:
contents: read

concurrency:
group: pypi
cancel-in-progress: false

jobs:
deploy:
release:
runs-on: ubuntu-24.04
timeout-minutes: 30
outputs:
version: ${{ steps.release.outputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: '0.13.1'
- name: Resolve and validate an existing published release tag
id: release
env:
GH_TOKEN: ${{ github.token }}
run: uv run --no-project --python 3.11 python scripts/check_distribution.py --github-release

validate:
needs: release
uses: ./.github/workflows/test.yaml

runs-on: ubuntu-latest
showcase:
needs: release
uses: ./.github/workflows/showcase.yaml

build:
needs: [release, validate, showcase]
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
persist-credentials: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: '0.13.1'
enable-cache: true
cache-dependency-glob: uv.lock
- name: Build and verify the exact release distributions
run: |
uv sync --locked
uv build --clear
uv run --locked python scripts/check_distribution.py
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-distributions
path: dist/
if-no-files-found: error

publish:
needs: [release, build]
runs-on: ubuntu-24.04
timeout-minutes: 30
environment: pypi
permissions:
contents: read
id-token: write
steps:
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-distributions
path: dist/
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: '0.13.1'
- name: Publish the checked bytes with Trusted Publishing
run: |
cd dist
sha256sum --check SHA256SUMS
uv publish --trusted-publishing always ./*.whl ./*.tar.gz

verify-release:
needs: [release, publish]
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.10'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build

- name: Build package
run: python -m build

- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
persist-credentials: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: '0.13.1'
- name: Install and verify the published distribution in a clean environment
env:
RELEASE_VERSION: ${{ needs.release.outputs.version }}
run: |
uv venv --python 3.11 /tmp/published
uv pip install --refresh-package github-action-utils --python /tmp/published/bin/python "github-action-utils==$RELEASE_VERSION"
/tmp/published/bin/python scripts/check_distribution.py --published-version "$RELEASE_VERSION"
70 changes: 70 additions & 0 deletions .github/workflows/showcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Feature showcase

on:
pull_request:
workflow_dispatch:
workflow_call:

permissions:
contents: read

jobs:
showcase:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-latest, macos-latest]
defaults:
run:
shell: bash
env:
INPUT_SHOWCASE_TEXT: ' explicit fixture '
INPUT_SHOWCASE_FLAG: 'true'
INPUT_SHOWCASE_LINES: |
first
世界
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.11'
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: '0.13.1'
enable-cache: true
cache-dependency-glob: uv.lock
- name: Install built package into the action Python
run: |
uv build --clear
uv pip install --system dist/*.whl
- name: Summary append, replacement, and removal in a separate fixture
run: python -m scripts.showcase summary
- name: Console, annotations, groups, inputs, and producer records
id: producer
run: python -m scripts.showcase produce
- name: Assert subsequent-step outputs, environment, PATH, and conditional artifacts
if: always()
env:
OUTPUT_VALUE: ${{ steps.producer.outputs.multiline }}
OUTPUT_NUMBER: ${{ steps.producer.outputs.number }}
OUTPUT_JSON: ${{ steps.producer.outputs.json }}
OUTPUT_EMPTY: ${{ steps.producer.outputs.empty }}
OUTPUT_UNICODE: ${{ steps.producer.outputs.unicode }}
ARTIFACTS_ENABLED: ${{ steps.producer.outputs.enabled }}
run: python -m scripts.showcase verify
- name: Main/post action-scoped state
uses: ./.github/actions/check-state
- name: Feature results
if: always()
run: python -m scripts.showcase report
- name: Upload showcase reports
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: showcase-${{ matrix.os }}
path: reports/showcase/
92 changes: 58 additions & 34 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -1,51 +1,75 @@
name: Test

on:
workflow_call:
push:
branches: [ "main" ]
branches: [main]
pull_request:
branches: [ "main" ]
workflow_dispatch:

permissions:
contents: read

jobs:
linting:
runs-on: ubuntu-latest
quality:
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
python-version: "3.10"

- name: Install Dependencies
run: python -m pip install --upgrade setuptools pip virtualenv tox

- name: Check Black, isort, and mypy
run: python -m tox -e black-formatter,isort,mypy
ref: ${{ github.sha }}
persist-credentials: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: '0.13.1'
enable-cache: true
cache-dependency-glob: uv.lock
- run: uv sync --locked
- run: uv run --locked tox run -e lint,type,coverage,package
- name: Lint workflows
run: go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12
- name: Upload coverage reports
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: quality-reports
path: reports/*coverage.*

test:
name: Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11']

runs-on: ubuntu-20.04

os: [ubuntu-24.04, windows-latest, macos-latest]
python-version: ['3.11', '3.12', '3.13', '3.14', '3.15']
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v3

- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install Dependencies
run: python -m pip install --upgrade setuptools pip virtualenv tox
- name: Run Tests
run: tox -e py

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: coverage.xml
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: false
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
version: '0.13.1'
enable-cache: true
cache-dependency-glob: uv.lock
- name: Build and test the non-editable wheel
env:
PYTHON_VERSION: ${{ matrix.python-version }}
run: |
uv build --clear
uv run --locked --python "$PYTHON_VERSION" --only-group tox tox run -e "py${PYTHON_VERSION/./}" --installpkg dist/*.whl -- --cov=github_action_utils --cov-report=xml:reports/matrix-coverage.xml --cov-fail-under=0
- name: Upload coverage report
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: checks-${{ matrix.os }}-${{ matrix.python-version }}
path: reports/matrix-coverage.xml
Loading
Loading