Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

4. Wrap-up and Challenges

What you built in two hours

The loop: issue, branch, commit, pull request, CI tests, merge, release, Docker image

That is the daily loop of most software teams. The tools change from one company to another; the loop stays the same.

Check yourself

1. What is the difference between Git and GitHub?

Git is a version control program that runs on your computer and records the history of a project. GitHub is a website that hosts Git repositories and adds collaboration tools: pull requests, issues, projects, Actions, packages.

2. You changed three files but want to commit only two. How?

Stage only those two, then commit: git add file1 file2, then git commit -m "...". The staging area exists precisely so that you choose what goes into each snapshot.

3. What does git pull do?

It downloads the new commits from the remote (git fetch), then merges them into your current branch (git merge).

4. Why open a pull request instead of committing to main?

main stays working at all times. A pull request lets others discuss and review the change, and lets CI test it, before it reaches main. It also documents why the change was made.

5. A file contains <<<<<<<, ======= and >>>>>>>. What happened, and what do you do?

A merge conflict: two branches changed the same lines. Edit the file into the version you want, delete the three marker lines, then git add the file and git commit to finish the merge.

6. How do you make a pull request close issue #12 automatically?

Write Closes #12 (or Fixes #12, Resolves #12) in its description. The issue closes when the pull request is merged into the default branch.

7. Milestone or project: what’s the difference?

A milestone groups the issues of one repository toward a goal or a date, usually a release, and shows the percentage done. A project is a board or table that organises work, possibly across several repositories, with custom fields, views and automation.

8. In a workflow, what is the difference between uses: and run:?

uses: runs a published, reusable action (for example actions/checkout@v6). run: runs a shell command on the runner (for example pytest -v).

9. Why does the publish job say needs: test?

So that it starts only after test has succeeded. If the tests fail, nothing is published: a broken version can never reach the registry.

10. Image or container?

An image is a packaged, read-only template: the app plus everything it needs. A container is a running instance of an image. One image can run as many containers as you like, on any machine with Docker.

11. Where does secrets.GITHUB_TOKEN come from?

GitHub creates it automatically at the start of every workflow run, and it expires when the job ends. The permissions: key decides what it may do, for example packages: write. You never create or store it yourself.

Take-home challenges

Each one practises the full loop: open an issue first, then branch, commit, pull request, green CI, merge.

⭐ Getting comfortable

  1. A status badge. Add the pipeline’s badge to the top of your README: ![CI/CD](https://github.com/<you>/calc-app/actions/workflows/ci-cd.yml/badge.svg).

  2. A new operation. Add modulo (the remainder of a / b), test first. Create a milestone v1.1, and release v1.1.0. Check that the new image tag appears in Packages.

  3. Find the next bug. Call /api/power?a=10&b=1000. What happens? Report it with the bug form, then fix it. Hint: Python raises OverflowError; handle it like the division by zero.

⭐⭐ Going further

  1. Test on several Python versions with a matrix: in the test job, add the lines below and use python-version: ${{ matrix.python-version }}. The check names become test (3.11), test (3.12)... so update the ruleset.

        strategy:
          matrix:
            python-version: ["3.11", "3.12", "3.13"]
  2. A linter. Add a step that runs Ruff (pip install ruff, then ruff check .), and fix what it reports.

  3. Team up. Invite a classmate (Settings → Collaborators), set Required approvals to 1 in the ruleset, split the open issues between you, and review each other’s pull requests. Then make a real merge conflict between your two branches, and resolve it.

  4. A pull request template. Add .github/pull_request_template.md with a checklist (“Tests added”, “README updated”, “Closes #”).

⭐⭐⭐ Like a professional

  1. A safer image. Make the container run as a non-root user (useradd and USER in the Dockerfile) and add a HEALTHCHECK.

  2. Continuous deployment. After publish, deploy the image to a cloud service automatically (for example Google Cloud Run, Azure Container Apps, Render or Fly.io). Store its credentials as a repository secret, never in the code.

  3. Automatic updates. Add .github/dependabot.yml so that Dependabot opens pull requests when Flask, pytest or the actions you use publish a new version. Your CI then tells you whether the update is safe.

Keep learning