GitHub Actions workflows
When scaffolding a new Python project, or when adding CI to an existing one:
Setup
- Ask the user: "Will this project be hosted on GitHub?" If no, skip all CI workflow creation entirely.
- Read the repo name from
pyproject.toml[project].name(the name with hyphens is the repo name) - Read
config.tomlfrom this directory. If it exists, extractgithub_ownerandgithub_url_basefor defaults. If not, ask the user for these values and write them toconfig.toml(one-time setup). - Ask the user for the GitHub owner (default:
<github_owner from config>) - Ask the user for the URL base for badge links:
go.<owner>.com/gh(default, e.g.https://<github_url_base from config>)github.com/<owner>(e.g.https://github.com/<github_owner from config>)- Custom — user provides their own
- Ask the user: "Do you want to set up Renovate for automated dependency updates?"
If yes, load the
renovateskill and follow its instructions for GitHub projects. This createsrenovate.jsonand optionally.github/workflows/renovate.yml.
Substitute <owner>, <repo>, <package>, and <base> in all workflow files and badges using the values gathered above.
Workflow files
.github/workflows/lint.yml— always.github/workflows/test.yml— always.github/workflows/pypi.yml— only if the user opts in (ask first).github/workflows/create_draft_release.yml— only if the user opts in (ask first)renovate.json— only if the user opted in to Renovate
Badges
After creating workflow files, add corresponding badges to the top of README.md in a single row. The GitHub Tag badge is always added.
| Badge | Condition | Markdown |
|---|---|---|
| GitHub Tag | always | [](<base>/<repo>/releases) |
| Lint | always | [](<base>/<repo>/actions/workflows/lint.yml) |
| Test | always | [](<base>/<repo>/actions/workflows/test.yml) |
| PyPI | if opted in | [](https://pypi.org/project/<package>) |
| Renovate | if opted in | [](https://docs.renovatebot.com) |
Templates
Each template file lives in assets/. Read the .yml.tmpl with the Read tool, substitute placeholders, then write the result to .github/workflows/<name>.yml:
assets/lint.yml.tmpl→lint.yml(no substitutions)assets/test.yml.tmpl→test.yml(no substitutions)assets/pypi.yml.tmpl→pypi.yml(only if user opted in)assets/create_draft_release.yml.tmpl→create_draft_release.yml(only if user opted in)
Create the directory before writing:
mkdir -p .github/workflows
Action pinning policy
All workflow actions (uses: lines) are pinned to full commit SHAs with a version comment. The pinned version is the latest stable release available for > 30 days, ensuring a vetting window before automated updates are proposed. Renovate's minimumReleaseAge: "30 days" respects this window. To update a pin, find the desired tag's commit SHA via the GitHub API (/repos/{owner}/{repo}/git/ref/tags/{tag}) or the action's release page. Consider setting up Renovate (step 4) to automate these updates.
README update
After creating workflow files, add the following section to README.md so contributors can run CI workflows locally with act.
### Local CI with act
Requires [act](https://github.com/nektos/act) and a modern runner image:
```bash
# One-time setup
act -P ubuntu-latest=catthehacker/ubuntu:act-latest
```
```bash
# Lint and tests (triggered on push)
act -P ubuntu-latest=catthehacker/ubuntu:act-latest -W .github/workflows/lint.yml
act -P ubuntu-latest=catthehacker/ubuntu:act-latest -W .github/workflows/test.yml
# PyPI publish (workflow_dispatch — needs GITHUB_TOKEN)
act workflow_dispatch -P ubuntu-latest=catthehacker/ubuntu:act-latest \
-W .github/workflows/pypi.yml -s GITHUB_TOKEN=<token>
# Create draft release (workflow_dispatch — needs GITHUB_TOKEN)
act workflow_dispatch -W .github/workflows/create_draft_release.yml \
-s GITHUB_TOKEN=<token>
```
Replace `<token>` with a GitHub classic PAT with `repo` scope.
(The block above is 4-space indented. Strip the leading 4 spaces from each line and write the result to README.)