@@ -26,7 +26,7 @@ of running tests on every code change to ensure that the code is working as
2626expected. GitHub provides a feature called GitHub Actions that allows you to
2727integrate this into your projects.
2828
29- In this lesson we will go over the very basics of how to set up a GitHub Action
29+ In this lesson we will go over the basics of how to set up a GitHub Action
3030to run tests on your code.
3131
3232:::::: prereq
9595 push :
9696
9797# This is a list of jobs that the action will run. In this case, we have only
98- # one job called build .
98+ # one job called test .
9999jobs :
100100
101- build :
101+ # This is the name of the job
102+ test :
102103
103104 # This is the environment that the job will run on. In this case, we are
104105 # using the latest version of Ubuntu, however you can use other operating
@@ -166,77 +167,6 @@ individual stages of the workflow and inspect the terminal output.
166167{alt="Detailed view of a GitHub workflow run"}
167168
168169
169- # # Enable running the tests on a Pull Request
170-
171- The typical use-case for a CI system is to run the tests when a pull request is
172- made to the `main` branch to add a feature or fix a bug. However, there is a
173- subtlety to consider : what if the tests pass on your feature branch, but would
174- fail after merging into `main` due to a divergence between the two branches?
175-
176- To verify this, we can set our workflow to run on both `push` and
177- ` pull_request` by modifying the `tests.yaml` file so that the `on:` sections
178- reads :
179-
180- ` ` ` yaml
181- on:
182- push:
183- pull_request:
184- ` ` `
185-
186- Let's test it out.
187-
188- - From within your `main` branch, add a new file `sandwich.py` containing the
189- following function :
190-
191- ` ` ` python
192- def sandwich():
193- bread = "======"
194- filling = "jam"
195- return "\n ".join([bread, filling, bread])
196- ` ` `
197-
198- - Create a new branch in your repository called `sanwich_test` using `git switch -c sandwich_test`
199- - Add a new file `test_sandwich.py` with the function :
200-
201- ` ` ` python
202- from sandwich import sandwich
203-
204- def test_sandwich():
205- assert sandwich() == "======\n jam\n ======"
206- ` ` `
207-
208- - Push this to your repo with `git push -u origin sandwich_test`, but don't
209- raise a pull request just yet.
210- - Imagine that somebody else modifies the `main` branch before your branch can
211- be merged -- something that happens all too often in open source projets!
212- Return to the `main` branch using `git switch main`, and modify
213- `sandwich.py` :
214-
215- ` ` ` python
216- def sandwich():
217- bread = "======"
218- filling = "cheese"
219- return "\n ".join([bread, filling, bread])
220- ` ` `
221-
222- - Push this change to `main` with `git push`.
223- - Now raise a pull request on your respository to merge `sandwich_test` into
224- ` main` .
225-
226- After raising the pull request, you should see that the GitHub Action runs twice :
227- once for a push, which will pass, and once for a pull request, which will fail :
228-
229- {alt="Example of tests failing on pull requests."}
230-
231- To fix this :
232-
233- - Switch to the `sandwich_test` branch, and call `git merge main`.
234- - Fix the test on the `sandwich_test` branch, ensure the tests pass locally,
235- and commit/push the changes.
236-
237- The tests should now pass for both `push` and `pull_request`.
238-
239-
240170# # Testing across multiple platforms
241171
242172A very useful feature of GitHub Actions is the ability to test over a wider
@@ -250,7 +180,7 @@ We can achieve this by setting `jobs.<job_id>.strategy.matrix` in our workflow:
250180
251181` ` ` yaml
252182jobs:
253- build :
183+ test :
254184 strategy:
255185 matrix:
256186 python_version: ["3.12", "3.13", "3.14"]
@@ -272,13 +202,24 @@ Later in the file, the `setup-python` step should be changed to:
272202By default, all combinations in the matrix will be run in separate jobs. The
273203syntax `${{ matrix.x }}` inserts the text from the `x` list for the given matrix job.
274204
275- - Switch to a new branch `matrix_test` using `git switch -c matrix_test`
276- - Make the changes above to `.github/workflows/tests.yaml`
277- - Commit, push, and raise a pull request to `main`.
205+ :::::::::::::::::::::::::::::::::::: : challenge
206+
207+ # # Upgrade the workflow to run across multiple platforms
208+
209+ - Make the changes above to your workflow file, being careful to get the indentation right!
210+ - Commit the changes and push to GitHub.
211+ - Check the latest jobs in the Actions panel.
212+
213+ ::::::::::::::::::::::: : solution
214+
215+ You should see that a total of 6 jobs have run, and hopefully all will have passed!
278216
279- You should now see that _12_ separate jobs run!
217+ {alt="Completed matrix tests."}
218+
219+ :::::::::::::::::::::::::::::::: :
220+
221+ ::::::::::::::::::::::::::::::::::::::::::::::: :
280222
281- {alt="Completed matrix tests."}
282223
283224This ensures that code that runs on your machine should, in theory, run on many
284225other peoples' machines too. However, it's best to restrict the matrix to the
@@ -298,11 +239,11 @@ can do so with a list of exclusions:
298239 python_version: "3.13"
299240` ` ` `
300241
242+ ## Running on other events
301243
302- ## Other tips
303-
304- You may have wondered why there is a trailing colon when we specify ` push:` or `pull_request:`.
305- The reason is that we can optionally set additional conditions on when they'll run. For example :
244+ You may have wondered why there is a trailing colon when we specify ` push:` at
245+ the top of the file. The reason is that we can optionally set additional
246+ conditions on when CI jobs will run. For example :
306247
307248` ` ` yaml
308249on:
@@ -312,17 +253,72 @@ on:
312253 paths:
313254 - '**.py'
314255 - 'pyproject.toml'
256+ # Only check when somebody raises a push to main.
257+ # (not recommended in general!)
258+ branches: [main]
259+ ` ` `
260+
261+ Doing this can prevent pointless CI jobs from running and save resources.
262+
263+ You can also run on events other than a push. For example :
264+
265+ ` ` ` yaml
266+ on:
267+ push:
268+ paths:
269+ - '**.py'
270+ - 'pyproject.toml'
271+ # Run on code in pull requests.
315272 pull_request:
316273 paths:
317274 - '**.py'
318275 - 'pyproject.toml'
319- # Only check when somebody raises a pull_request to main.
320- branches: [main]
321- # This allows you to run the tests manually if you choose
276+ # This allows you to launch the job manually
322277 workflow_dispatch:
323278` ` `
324279
325- Doing this can prevent pointless CI jobs from running and save resources.
280+ There is an important subtlety to running on `pull_request` versus
281+ `push` :
282+
283+ - ` push` runs directly on the commits you push to GitHub.
284+ - ` pull_request` runs on the code that would result _after_ the pull request
285+ has been merged into its target branch.
286+
287+ In collaborative coding projects, it is entirely possible that `main` will have
288+ diverged from your branch while you were working on it, and tests that pass on
289+ your branch will fail after the merge. For this reason, it's recommended to
290+ always include both `push` and `pull_request` in your testing workflows.
291+
292+ :::::::::::::::::::::::::::::::::::: : challenge
293+
294+ # # Running on pull requests (advanced)
295+
296+ Can you engineer a situation where a CI job passes on `push` but
297+ fails on `pull_request`?
298+
299+ - Write a new function, commit the changes, and push it to your `main`
300+ branch.
301+ - Switch to a new branch `my_branch` with `git switch -c my_branch`,
302+ and write a test for that function.
303+ - Check that the test passes, and commit it.
304+ - Push `my_branch` to GitHub with `git push -u origin my_branch`,
305+ but don't raise a pull request yet.
306+ - Return to your `main` branch, and modify the function being tested.
307+ - Push the changes to `main`.
308+ - Now raise a pull request from `my_branch` into `main`.
309+
310+ ::::::::::::::::::::::: : solution
311+
312+ The code on the new branch will be testing the old implementation,
313+ and should pass. However, following the merge, the test would fail.
314+ This results in the `push` test passing, and the `pull_request` test
315+ failing.
316+
317+ {alt="Example of tests failing on pull requests."}
318+
319+ :::::::::::::::::::::::::::::::: :
320+
321+ ::::::::::::::::::::::::::::::::::::::::::::::: :
326322
327323# # Keypoints
328324
0 commit comments