Skip to content

Installing Dependencies


Your workflow can print a Python version, but it can’t run your tests yet — pytest isn’t installed on the runner by default, and neither is anything else your project needs. This chapter adds the dependency-installation step properly, instead of just bolting on a pip install and moving on.

What CI Pipeline Project Actually Needs

Take stock of the project first. send_file.py and receive_file.py only use Python’s standard library — sockets, no third-party networking package required. The one thing you need that isn’t built in is pytest, to run the test suite in tests/.

That’s a short dependency list, which makes it a good project to learn the pattern on before you hit a real requirements.txt with a dozen packages and version constraints.

Why pip install --upgrade pip Comes First

You’ll see this line at the top of almost every CI dependency step you look at:

python -m pip install --upgrade pip

The runner’s baked-in pip version can lag behind. An older pip is more likely to resolve dependencies incorrectly, miss a newer wheel format, or just behave differently than the pip on your machine. Upgrading it first costs a few seconds and removes a whole category of “works locally, fails in CI” surprises.

Note

Run this as python -m pip install ..., not bare pip install .... Calling pip through python -m guarantees you’re upgrading the pip tied to the Python interpreter setup-python just installed — not some other pip that happens to be first on the runner’s PATH.

Writing A requirements.txt

Even for a single dependency, put it in a file rather than typing it inline in the workflow. It gives you one place to check what a project needs, and it’s what makes the caching in the next section possible at all.

At the root of the project:

requirements.txt
pytest

That’s the whole file. Leaving the version unpinned means CI always installs whatever the latest pytest release is. That’s fine for a learning project, but worth knowing the trade-off: an unpinned dependency can change out from under you between one CI run and the next if a new major version ships with breaking changes. Pinning (pytest==8.4.0, for example) trades that risk for having to bump the version yourself when you want updates. Either is a legitimate choice — just make it on purpose.

Installing From requirements.txt In CI

- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt

Two commands, one step — pip is current, then your actual dependency goes in.

A First Look At Caching

Re-downloading pytest from PyPI on every single push works, but it’s wasted time once your dependency list grows. setup-python has a built-in shortcut for the common case: point it at your dependency file and it’ll cache the downloaded packages between runs.

- name: Setup Python
  uses: actions/setup-python@v7
  with:
    python-version: "3.13"
    cache: "pip"
    cache-dependency-path: requirements.txt

With this in place, a run where requirements.txt hasn’t changed reuses the cached packages instead of hitting PyPI again. This is a convenience wrapper around a more general caching mechanism in GitHub Actions — you’ll get the full picture of how that mechanism actually works, and when you’d reach for it directly instead of this shortcut, later in this section.

Hands-On: Wiring It Into Your Workflow

Add the requirements.txt file:

cat > requirements.txt << 'EOF'
pytest
EOF

Update .github/workflows/ci.yml so the Setup Python step gains the cache inputs, and a new Install dependencies step follows it:

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"
          cache: "pip"
          cache-dependency-path: requirements.txt

      - name: Confirm Python Version
        run: python --version

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

Commit and push:

git add requirements.txt .github/workflows/ci.yml
git commit -m "Install pytest from requirements.txt, enable pip caching"
git push

Check the Actions tab. The first run will show a cache miss (nothing to reuse yet) — push again with no changes (git commit --allow-empty -m "Trigger action") to requirements.txt and the second run’s Setup Python step will report a cache hit which significantly fasten installation when requirements grow bigger. pytest is now sitting on the runner, ready to actually be called — which is exactly what the next two chapters do.

Last updated on