Setting Up Python in a Workflow
Every workflow you write from here on starts with the same two moves: get the code onto the runner, then get an interpreter ready to run it. You already know how to do the first part. This chapter covers the second — turning a bare GitHub-hosted runner into a machine that actually has Python on it.
Why The Runner Doesn’t Already Have The Python You Want
GitHub-hosted runners ship with a handful of Python versions pre-installed, but you can’t rely on “whatever happens to be there” for two reasons. First, the default python3 on the runner image can change between GitHub’s image updates without warning. Second, your project may need a specific version — maybe you’re testing against 3.13 specifically, or you need to guarantee the same version your teammates run locally.
actions/setup-python solves this by downloading and installing the exact version you ask for, then pointing python and pip at it for every step that follows.
Adding setup-python To A Workflow
Here’s the minimum shape:
- name: Setup Python
uses: actions/setup-python@v7
with:
python-version: "3.13"That’s it — one action, one required input. After this step runs, every subsequent run: step in the same job has python and pip available at version 3.13, no matter what the runner image shipped with by default.
Note
setup-python has to run after actions/checkout, but it doesn’t actually depend on your code being present — it just installs an interpreter. The ordering matters more for later steps (installing dependencies, running tests) that assume both the code and the interpreter are ready.
Verifying What Landed On The Runner
Don’t take the version string on faith — print it. A cheap sanity-check step saves you from debugging a mismatch three steps later:
- name: Confirm Python Version
run: python --versionRun this once while you’re setting a workflow up for the first time, then you can leave it in permanently — it’s nearly free and it shows up right in the log, which is useful the day someone changes the python-version input and forgets to check the result.
Pinning Exact Versions Vs Minor Versions
The python-version input accepts more than one format:
| Value | What You Get |
|---|---|
"3.13" | Latest patch release of 3.13 available at run time (e.g. 3.13.7) |
"3.13.2" | That exact patch version, if available |
"3.x" | Latest 3.x release overall |
For most projects, pinning to a minor version ("3.13") is the right default — you get bug fixes and security patches automatically, without your CI silently jumping to Python 4 someday. Pin the exact patch version only when you have a specific reason to freeze it (reproducing a bug tied to a patch release, for example).
Tip
Whatever you choose here, use the same version locally. A workflow that passes on 3.13 in CI but was written and tested against 3.11 on your machine is a trap waiting to spring.
Hands-On: Adding The Workflow
From the previous lesson, you’ve got the project to learn Building Real CI Pipelines checked out locally, with the send_file.py / receive_file.py pair and its tests/ folder. Time to give it a real CI workflow.
Create the workflow file:
mkdir -p .github/workflows
cat > .github/workflows/ci.yml << 'EOF'
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup Python
uses: actions/setup-python@v7
with:
python-version: "3.13"
- name: Confirm Python Version
run: python --version
EOFCommit and push it:
git add .github/workflows/ci.yml
git commit -m "Add CI workflow: checkout and setup Python"
git pushOpen the Actions tab for your repo. You should see the workflow run, and the “Confirm Python Version” step’s log should print something like Python 3.13.7. That confirms the whole chain worked: the runner spun up, checked out your code, installed the interpreter you asked for, and ran a command against it.
This workflow doesn’t do anything useful with your project yet — it just proves the ground floor is solid. The next chapter builds on this exact file, adding the step that installs the project’s real dependency: pytest.