diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml new file mode 100644 index 0000000..e3a40ba --- /dev/null +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -0,0 +1,61 @@ +name: Pre-commit Autoupdate + +on: + schedule: + # Run weekly on Monday at 9am UTC + - cron: "0 9 * * 1" + workflow_dispatch: + +permissions: {} + +jobs: + autoupdate: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: true + + - name: Generate GitHub App token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + id: app-token + with: + app-id: ${{ vars.AUTOMATION_APP_ID }} + private-key: ${{ secrets.AUTOMATION_APP_PRIVATE_KEY }} + + - name: Install uv + uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0 + + - name: Run prek autoupdate + working-directory: "{{cookiecutter.project_slug}}" + run: uvx prek autoupdate --freeze --cooldown-days 7 + + - name: Create Pull Request + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + if git diff --quiet; then + echo "No changes to commit" + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + branch="pre-commit-autoupdate" + git checkout -b "$branch" + git add -A + git commit -m "build(deps): update pre-commit hooks" + git push --force origin "$branch" + + if gh pr view "$branch" &>/dev/null; then + echo "PR already exists" + else + gh pr create \ + --title "build(deps): update pre-commit hooks" \ + --body "Automated update of pre-commit hooks using \`prek autoupdate --freeze --cooldown-days 7\`. + + This PR was auto-generated by the pre-commit-autoupdate workflow." + fi diff --git a/cookiecutter.json b/cookiecutter.json index 8d61f41..2a7dda7 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -31,6 +31,7 @@ "_copy_without_render": [ ".github/workflows/docs.yml", ".github/workflows/lint.yml", + ".github/workflows/pre-commit-autoupdate.yml", ".github/workflows/tests.yml", ".github/workflows/zizmor.yml" ], diff --git a/{{cookiecutter.project_slug}}/.github/workflows/pre-commit-autoupdate.yml b/{{cookiecutter.project_slug}}/.github/workflows/pre-commit-autoupdate.yml new file mode 100644 index 0000000..9285a1e --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/workflows/pre-commit-autoupdate.yml @@ -0,0 +1,60 @@ +name: Pre-commit Autoupdate + +on: + schedule: + # Run weekly on Monday at 9am UTC + - cron: "0 9 * * 1" + workflow_dispatch: + +permissions: {} + +jobs: + autoupdate: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: true + + - name: Generate GitHub App token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + id: app-token + with: + app-id: ${{ vars.AUTOMATION_APP_ID }} + private-key: ${{ secrets.AUTOMATION_APP_PRIVATE_KEY }} + + - name: Install uv + uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0 + + - name: Run prek autoupdate + run: uvx prek autoupdate --freeze --cooldown-days 7 + + - name: Create Pull Request + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + if git diff --quiet; then + echo "No changes to commit" + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + branch="pre-commit-autoupdate" + git checkout -b "$branch" + git add -A + git commit -m "build(deps): update pre-commit hooks" + git push --force origin "$branch" + + if gh pr view "$branch" &>/dev/null; then + echo "PR already exists" + else + gh pr create \ + --title "build(deps): update pre-commit hooks" \ + --body "Automated update of pre-commit hooks using \`prek autoupdate --freeze --cooldown-days 7\`. + + This PR was auto-generated by the pre-commit-autoupdate workflow." + fi diff --git a/{{cookiecutter.project_slug}}/.github/workflows/tests.yml b/{{cookiecutter.project_slug}}/.github/workflows/tests.yml index a3bb920..cbd73c8 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/tests.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/tests.yml @@ -13,7 +13,6 @@ jobs: strategy: matrix: python: - - "3.10" - "3.11" - "3.12" - "3.13" diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml index 2defcdc..5f9d827 100644 --- a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml +++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -10,6 +10,26 @@ repos: - id: check-merge-conflict - id: detect-private-key + # Shell script linting + - repo: https://github.com/koalaman/shellcheck-precommit + rev: 99470f5e12208ff0fb17ab81c3c494f7620a1d8d # frozen: v0.11.0 + hooks: + - id: shellcheck + args: [--severity=error] + + # GitHub Actions linting + - repo: https://github.com/rhysd/actionlint + rev: 0933c147c9d6587653d45fdcb4c497c57a65f9af # frozen: v1.7.10 + hooks: + - id: actionlint + + # GitHub Actions security audit + - repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: b546b77c44c466a54a42af5499dcc0dcc1a3193f # frozen: v1.22.0 + hooks: + - id: zizmor + args: [--persona=regular, --min-severity=medium, --min-confidence=medium] + - repo: local hooks: - id: format diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index d287159..b29a0d5 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ "Programming Language :: Python :: 3", ] dependencies = [] -requires-python = ">=3.10" +requires-python = ">=3.11" [build-system] requires = ["uv_build>=0.9.0,<0.10.0"] @@ -41,10 +41,12 @@ lint = [ "interrogate", {%- endif %} ] +audit = ["pip-audit"] dev = [ {include-group = "doc"}, {include-group = "test"}, {include-group = "lint"}, + {include-group = "audit"}, "prek", ] @@ -60,25 +62,35 @@ Issues = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter. Source = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}" [tool.coverage.run] +branch = true # don't attempt code coverage for the CLI entrypoints omit = ["{{ cookiecutter.__project_src_path }}/_cli.py"] +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "if __name__ == .__main__.:", +] + [tool.ty.terminal] error-on-warning = true [tool.ty.environment] -python-version = "3.10" +python-version = "3.11" [tool.ty.src] include = ["src", "test"] [tool.ruff] line-length = 100 -target-version = "py310" +target-version = "py311" +src = ["src"] [tool.ruff.format] line-ending = "lf" quote-style = "double" +docstring-code-format = true [tool.ruff.lint] select = ["ALL"]