From ec1b0b1a7d94fc4f7342bae375537e6954262b74 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:03:00 +0000 Subject: [PATCH 01/42] Add draft of updated exercise steps --- .github/script/merge-branch.sh | 22 -- .github/steps/-step.txt | 1 - .github/steps/0-welcome.md | 1 - .github/steps/1-add-a-test-workflow.md | 45 ----- .github/steps/1-step.md | 62 ++++++ .github/steps/2-fix-the-test.md | 35 ---- .github/steps/2-step.md | 222 +++++++++++++++++++++ .github/steps/3-step.md | 67 +++++++ .github/steps/3-upload-test-reports.md | 42 ---- .github/steps/4-add-branch-protections.md | 25 --- .github/steps/4-step.md | 59 ++++++ .github/steps/5-merge-your-pull-request.md | 17 -- .github/steps/X-finish.md | 5 - 13 files changed, 410 insertions(+), 193 deletions(-) delete mode 100755 .github/script/merge-branch.sh delete mode 100644 .github/steps/-step.txt delete mode 100644 .github/steps/0-welcome.md delete mode 100644 .github/steps/1-add-a-test-workflow.md create mode 100644 .github/steps/1-step.md delete mode 100644 .github/steps/2-fix-the-test.md create mode 100644 .github/steps/2-step.md create mode 100644 .github/steps/3-step.md delete mode 100644 .github/steps/3-upload-test-reports.md delete mode 100644 .github/steps/4-add-branch-protections.md create mode 100644 .github/steps/4-step.md delete mode 100644 .github/steps/5-merge-your-pull-request.md diff --git a/.github/script/merge-branch.sh b/.github/script/merge-branch.sh deleted file mode 100755 index c54c336..0000000 --- a/.github/script/merge-branch.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -# Make sure this file is executable -# chmod a+x .github/script/merge-branch.sh - -# USAGE: This script is used to merge a branch into another branch - -# BACKGROUND: This operation is required to avoid conflicts between branches. - -# Setup committer identity -git config user.name github-actions[bot] -git config user.email github-actions[bot]@users.noreply.github.com - -# Merge branch -echo "If branch $branch2 exists, merge branch $branch1 into branch $branch2" -if git show-ref --quiet refs/heads/$branch2 -then - git checkout $branch2 - git merge $branch1 - git push origin $branch2 -else - echo "Branch $branch2 does not exist" -fi diff --git a/.github/steps/-step.txt b/.github/steps/-step.txt deleted file mode 100644 index 573541a..0000000 --- a/.github/steps/-step.txt +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/.github/steps/0-welcome.md b/.github/steps/0-welcome.md deleted file mode 100644 index 9ff13a5..0000000 --- a/.github/steps/0-welcome.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/.github/steps/1-add-a-test-workflow.md b/.github/steps/1-add-a-test-workflow.md deleted file mode 100644 index c326a83..0000000 --- a/.github/steps/1-add-a-test-workflow.md +++ /dev/null @@ -1,45 +0,0 @@ - - -## Step 1: Add a test workflow - -_Welcome to "GitHub Actions: Continuous Integration"! :wave:_ - -**What is _continuous integration_?**: [Continuous integration](https://en.wikipedia.org/wiki/Continuous_integration) can help you stick to your team’s quality standards by running tests and reporting the results on GitHub. CI tools run builds and tests, triggered by commits. The quality results post back to GitHub in the pull request. The goal is fewer issues in `main` and faster feedback as you work. - -![An illustration with a left half and a right half. On the left: illustration of how GitHub Actions terms are encapsulated. At the highest level: workflows and event triggers. Inside workflows: jobs and definition of the build environment. Inside jobs: steps. Inside steps: a call to an action. On the right: the evaluated sequence: workflow, job, step, action.](https://user-images.githubusercontent.com/6351798/88589835-f5ce0900-d016-11ea-8c8a-0e7d7907c713.png) - -- **Workflow**: A workflow is a unit of automation from its start to finish, including the definition of what triggers the automation, what environment or other aspects should be taken into account during the automation, and what should happen as a result of the trigger. -- **Job**: A job is a section of the workflow, and is made up of one or more steps. In this section of our workflow, the template defines the steps that make up the `build` job. -- **Step**: A step represents one _effect_ of the automation. A step could be defined as a GitHub Action, or another unit, like printing something to the console. -- **Action**: An action is a piece of automation written in a way that is compatible with workflows. Actions can be written by GitHub, by the open source community, or you can write them yourself! - -To learn more, check out [Workflow syntax for GitHub Actions](https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions) in the GitHub Docs. - -First, let's add a workflow to lint (clean, like a lint roller) our Markdown files in this repository. - -### :keyboard: Activity: Add a test workflow - -1. Open a new browser tab, and work through the following steps in that tab while you read the instructions in this tab. -1. Go to the **Actions tab**. -1. Click **New workflow**. -1. Search for "Simple workflow" and click **Configure**. -1. Name your workflow `ci.yml`. -1. Update the workflow by deleting the last two steps. -1. Add the following step at the end of your workflow: - ```yml - - name: Run markdown lint - run: | - npm install remark-cli remark-preset-lint-consistent - npx remark . --use remark-preset-lint-consistent --frail - ``` - > Even after the code is indented properly in `ci.yml`, you will see a build error in GitHub Actions. We'll fix this in the next step. -1. Click **Commit changes...**, and choose to make a new branch named `ci`. -1. Click **Propose changes**. -1. Click **Create pull request**. -1. Wait about 20 seconds and then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/actions) will automatically update to the next step. diff --git a/.github/steps/1-step.md b/.github/steps/1-step.md new file mode 100644 index 0000000..cc9e950 --- /dev/null +++ b/.github/steps/1-step.md @@ -0,0 +1,62 @@ +## Step 1: Continuous Integration + +GitHub Actions is a great way to automate several of your recurring tasks, saving you time to work on the more challenging and fun problems! + +One of the most common tasks a developer deals with is testing their code. Unfortunately, this is often tedious and things get skipped or simply overlooked. Even more so, we often have to test against many frameworks, operating systems, and other situations, exaggerating the problem. + +Let's learn how to automate this process using workflows in GitHub Actions. + +### What is Continuous Integration? + +[Continuous integration](https://en.wikipedia.org/wiki/Continuous_integration) can help you stick to your team’s quality standards by running tests and reporting the results on GitHub. CI tools run builds and tests, triggered by commits. The quality results post back to GitHub in the pull request. The goal is fewer issues in `main` and faster feedback as you work. + +### ⌨️ Activity: Start our sample Python application + +1. Open a new browser tab, and work through the following steps in that tab while you read the instructions in this tab. + +1. Use the below button to open the **Create Codespace** page in a new tab. Use the default configuration. + + [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/{{full_repo_name}}?quickstart=1) + +1. Confirm the **Repository** field is your copy of the exercise, not the original, then click the green **Create Codespace** button. + + - ✅ Your copy: `/{{full_repo_name}}` + - ❌ Original: `/skills/test-with-actions` + +1. Wait a moment for Visual Studio Code to load in your browser. + +1. In the left navigation, select the **Explorer** tab to show the project files. + +1. Open the `src/calculations.py` and `tests/calculation_tests.py` files. + +1. Take a moment to read these files to become familiar. + +1. Expand VS Code's built-in terminal panel. + + > 💡 **Tip**: The keyboard shortcut is `CTRL` + `J`. + +1. Run the below command to install the required Python libraries and tools to show code coverage. + + ```bash + pip install -r requirements.txt + pip install coverage + ``` + +1. Run the below command to run all unit tests and save coverage information. + + ```bash + coverage run -m unittest discover -s tests -p "*_test.py" + ``` + +1. Run the below command to show a test coverage report. + + ```bash + coverage report + ``` + +1. Add a comment to let Mona know the results of your coverage report. After reviewing, she will provide the next steps. + +```md +@professortocat, I've run my coverage report. +It looks good! What's next? +``` diff --git a/.github/steps/2-fix-the-test.md b/.github/steps/2-fix-the-test.md deleted file mode 100644 index fbe53a9..0000000 --- a/.github/steps/2-fix-the-test.md +++ /dev/null @@ -1,35 +0,0 @@ - - -## Step 2: Fix the test - -_Great job adding the templated workflow! :tada:_ - -Adding that file to this branch is enough for GitHub Actions to begin running CI on your repository. - -When a GitHub Actions workflow is running, you should see some checks in progress, like the screenshot below. - -checks in progress in a merge box - -You can follow along as GitHub Actions runs your job by going to the **Actions** tab or by clicking "Details" in the merge box below. - -When the tests finish, you'll see a red X :x: or a green check mark :heavy_check_mark: in the merge box. At that point, you can access the logs for the build job and its associated steps. - -_By looking at the logs, can you identify which tests failed?_ To find it, go to one of the failed builds and scroll through the log. Look for a section that lists all the unit tests. We're looking for the name of the test with an "x". - -screenshot of a sample build log with the names of the tests blurred out - -If the checks don't appear or if the checks are stuck in progress, there's a few things you can do to try and trigger them: - -- Refresh the page, it's possible the workflow ran and the page just hasn't been updated with that change. -- Try making a commit on this branch. Our workflow is triggered with a `push` event, and committing to this branch will result in a new `push`. -- Edit the workflow file on GitHub and ensure there are no red lines indicating a syntax problem. - -### :keyboard: Activity: Fix the test - -1. Update the contents in the `ci` branch to get the test to pass. You need to look at the logs to see what caused the test to fail. -1. **Commit changes**. -1. Wait about 20 seconds and then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/actions) will automatically update to the next step. diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md new file mode 100644 index 0000000..3ab29ba --- /dev/null +++ b/.github/steps/2-step.md @@ -0,0 +1,222 @@ +## Step 2: Workflow files + +The best way to add automation to your project's repository is with a GitHub Actions Workflow. Let's look at the anatomy of a workflow then create 2 of them. + +### What are the parts of workflow? + +![An illustration with a left half and a right half. On the left: illustration of how GitHub Actions terms are encapsulated. At the highest level: workflows and event triggers. Inside workflows: jobs and definition of the build environment. Inside jobs: steps. Inside steps: a call to an action. On the right: the evaluated sequence: workflow, job, step, action.](https://user-images.githubusercontent.com/6351798/88589835-f5ce0900-d016-11ea-8c8a-0e7d7907c713.png) + +- **Workflow**: A unit of automation from start to finish. It starts when the trigger (`on`) matches an activity in the repository and runs 1 or more jobs. + +- **Jobs**: The workflow's jobs each run in their own isolated environments and can be configured differently. They run in parallel unless configured otherwise or dependencies are set. + +- **Steps**: The steps area is a series of related actions that achieve a job's goal. A step can be a pre-made _Action_ from the Actions Marketplace, a private workflow, or even a custom script. + +- **Action**: Each step is an _Action_, a piece of automation written in a way that is compatible with workflows. Actions can be written by GitHub, by the open source community, or specific to the project. + +In the above `Example Workflow` file, it will start when any commits are pushed to the repository on any branch. It will run 1 job with the name `build`. That job's first step uses a pre-made _Action_ from the `actions` organization named `checkout` that clones the code from the repository into the job's environment. + +You can explore all of the configuration options in the [GitHub Actions Docs](https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions). + +### ⌨️ Activity: Add a workflow to run tests + +1. Open a web browser tab and navigate to this exercise repository. The Codespace is not not needed right now. + +1. In the top navigation, select the **Actions** tab. + +1. In the left navigation, above list of workflows, click the **New workflow** button. + +1. Enter `python` into the search box and click the **Enter** button. + + search box with 'python' value + +1. Find the **Python package** workflow and click the **Configure** button to open a file editor with a pre-made workflow. + + image + +1. Above the file editor, update the file name. + + ```txt + python-tests.yml + ``` + +1. Around line 6, simplify the `on` trigger to only use pull requests. + + ```yml + on: + pull_request: + branches: ["main"] + ``` + +1. Around line 17, update the matrix strategy to use Python versions important to our project. + + ```yml + python-version: ["3.11", "3.12", "3.13"] + ``` + +1. Around line 28, remove the unnecessary `pytest` dependency. + + ```diff + python -m pip install flake8 + ``` + +1. Around line 36, change the test framework and command. + + ```yml + - name: Run tests + run: | + python -m unittest discover -s tests -p "*_test.py" + ``` + +1. Above the editor, on the right, click the **Commit changes...**. + +### ⌨️ Activity: Add a workflow to show test coverage + +1. Switch to the VS Code Codespace. + +1. In the left navigation, select the **Explorer** tab to show the project files. + +1. Expand the `.github/workflows/` folder. + +1. Add a new file with the following name and open it. + + ```txt + python-coverage.yml + ``` + +1. Enter the name and set it to trigger on pull requests targeting the `main` branch. + + ```yml + name: Python Coverage + + on: + pull_request: + branches: + - main + ``` + +1. Add the `python-coverage` job and a first step that gets the repository content. + + ```yml + jobs: + python-coverage: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + ``` + +1. Add steps to install Python and required packages. + + ```yml + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.13 + + - name: Install dependencies + run: | + pip install -r requirements.txt + pip install coverage==7.9 + pip freeze + ``` + +1. Add steps to run the coverage report and save for later use. + + ```yml + - name: Run tests with coverage + id: coverage + run: | + coverage run -m unittest discover -s tests -p "*_test.py" + echo "coverage_report<> $GITHUB_OUTPUT + coverage report >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + ``` + +1. Add steps to generate other formats. + + ```yml + - name: Generate coverage reports + run: | + coverage xml + coverage html + + - name: Upload coverage reports + uses: actions/upload-artifact@v4 + with: + name: coverage-reports + path: | + coverage.xml + htmlcov/ + ``` + +1. Add a final step to share the coverage report as a comment on the pull requests. + + ```yml + - name: Post coverage comment + uses: actions/github-script@v7 + with: + script: | + const artifactsUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + const commentBody = `### Test Coverage Report +
+ Coverage Details + + \`\`\` + ${steps.coverage.outputs.coverage_report} + \`\`\` + +
+ + [📊 Detailed HTML Coverage Report](${artifactsUrl}) + `; + + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: commentBody + }); + ``` + +1. Commit and push the coverage workflow. + +> [!TIP] +> We used custom steps here to show the flexibility of Actions. There are several AWESOME pre-made options from the community on the free [Actions Marketplace](https://github.com/marketplace?type=actions). Consider trying one of them out before you build your own! + +⌨️ Activity: Merge workflows + +1. Create a new pull request. Use the following details. + + - **target:** `main` + - **base:** `add-testing-workflows` + - **title:** `feat: Add testing and coverage workflows` + - **description:** (blank) + +1. Ensure + +1. With the new workflow pushed to GitHub, Mona will review your work and post the next steps. + + + + diff --git a/.github/steps/3-step.md b/.github/steps/3-step.md new file mode 100644 index 0000000..10137c6 --- /dev/null +++ b/.github/steps/3-step.md @@ -0,0 +1,67 @@ +## Step 3: Trigger the workflows + + + + + +As specified in our workflows, they will only run when a pull request is targeting the `main` branch. + +Pull requests have a nice advantage when a workflow is associated with them. The run status and results can be displayed directly in the conversation feed. + +### ⌨️ Activity: Make a code change + +1. Return to the VS Code Codespace. + +1. Create a new branch based on `main` with the following name. + + ```txt + add-failure-test-cases + ``` + +1. Open the `tests/calculations_test.py` file. + +1. Add the following 2 entries. + + ```py + def test_area_of_circle_negative_radius(self): + """Test with a negative radius to raise ValueError.""" + # Arrange + radius = -1 + + # Act & Assert + with self.assertRaises(ValueError): + area_of_circle(radius) + ``` + + ```py + def test_get_nth_fibonacci_negative(self): + """Test with a negative number to raise ValueError.""" + # Arrange + n = -1 + + # Act & Assert + with self.assertRaises(ValueError): + get_nth_fibonacci(n) + ``` + +1. Commit the changes and push them. + +1. Return to the browser and create a pull request. Use the following details. + + - **base:** `main` + - **source:** `add-failure-test-cases` + - **title**: `feat: Add tests to verify failure cases` + +1. After the pull request is started, look near the Merge button to see the workflows running. + + - The workflows will fail, letting us know we have a test to fix. + - The test coverage report will come shortly as a comment. + +1. With the pull request started, Mona should be busy checking your work and preparing the next steps. + +
+Need help? + +- If the checks don't appear or updated, try refreshing the page. It's possible the workflow ran and the page just hasn't been updated with that change. + +
diff --git a/.github/steps/3-upload-test-reports.md b/.github/steps/3-upload-test-reports.md deleted file mode 100644 index d3802d7..0000000 --- a/.github/steps/3-upload-test-reports.md +++ /dev/null @@ -1,42 +0,0 @@ - - -## Step 3: Upload test reports - -_The workflow has finished running! :sparkles:_ - -So what do we do when we need the work product of one job in another? We can use the built-in [artifact storage](https://docs.github.com/actions/advanced-guides/storing-workflow-data-as-artifacts) to save artifacts created from one job to be used in another job within the same workflow. - -To upload artifacts to the artifact storage, we can use an action built by GitHub: [`actions/upload-artifact`](https://github.com/actions/upload-artifact). - -### :keyboard: Activity: Upload test reports - -1. Edit your workflow file. -1. Update the `Run markdown lint` step in your `build` job to use `vfile-reporter-json` and output the results to `remark-lint-report.json`. -1. Add a step to your `build` job that uses the `upload-artifact` action. This step should upload the `remark-lint-report.json` file generated by the updated `Run markdown lint` step. -1. Your new `build` should look like this: - - ```yml - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Run markdown lint - run: | - npm install remark-cli remark-preset-lint-consistent vfile-reporter-json - npx remark . --use remark-preset-lint-consistent --report vfile-reporter-json 2> remark-lint-report.json - - - uses: actions/upload-artifact@v4 - with: - name: remark-lint-report - path: remark-lint-report.json - ``` - -1. Commit your change to this branch. -1. Wait about 20 seconds and then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/actions) will automatically update to the next step. - -Like the upload action to send artifacts to the storage, you can use the download action to download these previously uploaded artifacts from the `build` job: [`actions/download-artifact`](https://github.com/actions/download-artifact). For brevity, we'll skip that step for this course. diff --git a/.github/steps/4-add-branch-protections.md b/.github/steps/4-add-branch-protections.md deleted file mode 100644 index 196199b..0000000 --- a/.github/steps/4-add-branch-protections.md +++ /dev/null @@ -1,25 +0,0 @@ - - -## Step 4: Add branch protections - -_Great job uploading test reports! :partying_face:_ - -Take a look at the merge box, you'll notice you can merge this even though the review process hasn't been met. - -Protected branches ensure that collaborators on your repository cannot make irrevocable changes to branches. Enabling protected branches also allows you to enable other optional checks and requirements, like required status checks and required reviews. - -### :keyboard: Activity: Add branch protections - -1. Go to **Branches** settings. You can navigate to that page manually by selecting the right-most tab in the top of the repository called **Settings** and then clicking **Branches**. -1. Click **Add classic branch protection rule** under "Branch protection rules". -1. Type `main` in **Branch name pattern**. -1. Check **Require a pull request before merging**. -1. Uncheck **Require approvals**. -1. Check **Require status checks to pass before merging**. -1. Check all build and test jobs that you'd like to see in the newly visible gray box. -1. Click **Create**. -1. _Once you turn on branch protection, Actions can no longer push directly to the `main` branch. Wait about 20 seconds and then go to the `ci` branch. [GitHub Actions](https://docs.github.com/actions) will automatically update to the next step on the `ci` branch. You'll need to follow instructions on this branch._ diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md new file mode 100644 index 0000000..b434329 --- /dev/null +++ b/.github/steps/4-step.md @@ -0,0 +1,59 @@ +## Step 4: Enforce workflows + +You may have noticed that the merge button was still active before our tests finished. +Even worse, the tests failed and there was nothing to prevent merging the broken code anyway! 😱 + +Let's fix this to avoid anyone (accidentally) bypassing verification. + +### ⌨️ Activity: Add branch protections + +1. In the top navigation, select the **Settings** tab. + +1. In the left navigation, select **Rules** and choose **Rulesets**. + +1. Click the **New ruleset** and select **New branch ruleset**. Use the following settings: + + - **Ruleset Name:** `Protect main` + - **Enforcement status:** `Active` + - **Target branches:** + - **Include default branch** + - **Include by pattern:** `main` + - **Require status checks to pass**: ☑️ Checked + - `Python Tests` + - `Python Coverage` + + target branch settings + +1. Click **Create**. + +1. Navigate back to the pull request. + +1. The **Merge** is now disabled! Nice! 🥰 + +### Activity: Fix the broken test and merge + +1. In the pull request, find the area near the merge button with the list of the workflows. + +1. Click on the title of the failed workflow. + +1. Scroll through the logs to investigate the root cause of the issue. + + - You will learn that one of the tests checks for minimum Python version. + - This explains why only the jobs using `3.8` and `3.9` failed. + - We know these are good from our own testing, so let's update the test. + +1. Switch to the VS Code Codespace. + +1. Open the `tests/supported_versions_test.py` file. + +1. At around line 17, update the line to change the minimum required minor version. + +```py +self.assertGreaterEqual(minor, 8, "Python minor version must be >= 8") +``` + +1. Commit and push your changes. After a moment, the testing workflows should run again. + +1. Wait a moment for the tests to complete. If they all pass, the merge button should activate! + +1. Click the **Merge** button. Congrats, you are all done! diff --git a/.github/steps/5-merge-your-pull-request.md b/.github/steps/5-merge-your-pull-request.md deleted file mode 100644 index f3c3ebe..0000000 --- a/.github/steps/5-merge-your-pull-request.md +++ /dev/null @@ -1,17 +0,0 @@ - - -## Step 5: Merge your pull request - -_Almost there! :heart:_ - -You can now [merge](https://docs.github.com/get-started/quickstart/github-glossary#merge) your pull request! - -### :keyboard: Activity: Merge your pull request - -1. Go to the **Pull requests** tab. -1. Click **Merge pull request**. -1. _Once you turn on branch protection, Actions can no longer push directly to the `main` branch. Make sure that you're on the `ci` branch in the page you're following instructions from._ Wait about 20 seconds and then refresh the page. [GitHub Actions](https://docs.github.com/actions) will automatically update to the next step. diff --git a/.github/steps/X-finish.md b/.github/steps/X-finish.md index 2c519e5..80f6b56 100644 --- a/.github/steps/X-finish.md +++ b/.github/steps/X-finish.md @@ -1,8 +1,3 @@ - - ## Finish _Congratulations friend, you've completed this course!_ From bcdba47f0ed5b8cb022efcdb9614538b47e9e384 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:03:29 +0000 Subject: [PATCH 02/42] Add draft workflows to match updated steps --- .../{0-welcome.yml => 0-start-exercise.yml} | 0 .github/workflows/1-add-a-test-workflow.yml | 73 --------- .github/workflows/1-step.yml | 91 +++++++++++ .github/workflows/2-fix-the-test.yml | 67 -------- .github/workflows/2-step.yml | 146 ++++++++++++++++++ .github/workflows/3-step.yml | 132 ++++++++++++++++ .github/workflows/3-upload-test-reports.yml | 73 --------- .../workflows/4-add-branch-protections.yml | 73 --------- .github/workflows/4-step.yml | 65 ++++++++ .../workflows/5-merge-your-pull-request.yml | 65 -------- .github/workflows/python-coverage.yml | 72 +++++++++ .github/workflows/python-tests.yml | 33 ++++ 12 files changed, 539 insertions(+), 351 deletions(-) rename .github/workflows/{0-welcome.yml => 0-start-exercise.yml} (100%) delete mode 100644 .github/workflows/1-add-a-test-workflow.yml create mode 100644 .github/workflows/1-step.yml delete mode 100644 .github/workflows/2-fix-the-test.yml create mode 100644 .github/workflows/2-step.yml create mode 100644 .github/workflows/3-step.yml delete mode 100644 .github/workflows/3-upload-test-reports.yml delete mode 100644 .github/workflows/4-add-branch-protections.yml create mode 100644 .github/workflows/4-step.yml delete mode 100644 .github/workflows/5-merge-your-pull-request.yml create mode 100644 .github/workflows/python-coverage.yml create mode 100644 .github/workflows/python-tests.yml diff --git a/.github/workflows/0-welcome.yml b/.github/workflows/0-start-exercise.yml similarity index 100% rename from .github/workflows/0-welcome.yml rename to .github/workflows/0-start-exercise.yml diff --git a/.github/workflows/1-add-a-test-workflow.yml b/.github/workflows/1-add-a-test-workflow.yml deleted file mode 100644 index 4a946c4..0000000 --- a/.github/workflows/1-add-a-test-workflow.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Step 1, Add a test workflow - -# This step triggers after push to the ci.yml file on the ci branch. -# This workflow updates from step 1 to step 2. - -# This will run every time we push to the ci.yml file on the ci branch. -# Reference: https://docs.github.com/actions/learn-github-actions/events-that-trigger-workflows -on: - workflow_dispatch: - push: - branches: - - ci - paths: - - .github/workflows/ci.yml - -# Reference: https://docs.github.com/actions/security-guides/automatic-token-authentication -permissions: - # Need `contents: read` to checkout the repository. - # Need `contents: write` to update the step metadata. - contents: write - -jobs: - # Get the current step to only run the main job when the learner is on the same step. - get_current_step: - name: Check current step number - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: get_step - run: | - echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT - outputs: - current_step: ${{ steps.get_step.outputs.current_step }} - - on_push_ci_file: - name: On push ci file - needs: get_current_step - - # We will only run this action when: - # 1. This repository isn't the template repository. - # 2. The step is currently 1. - # Reference: https://docs.github.com/actions/learn-github-actions/contexts - # Reference: https://docs.github.com/actions/learn-github-actions/expressions - if: >- - ${{ !github.event.repository.is_template - && needs.get_current_step.outputs.current_step == 1 }} - - # We'll run Ubuntu for performance instead of Mac or Windows. - runs-on: ubuntu-latest - - steps: - # We'll need to check out the repository so that we can edit the README. - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Let's get all the branches. - - # Verify the learner added remark-lint. - - name: Verify workflow - uses: skills/action-check-file@v1 - with: - file: .github/workflows/ci.yml - search: remark-preset-lint-consistent - - # In README.md, switch step 1 for step 2. - - name: Update to step 2 - uses: skills/action-update-step@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - from_step: 1 - to_step: 2 - branch_name: ci diff --git a/.github/workflows/1-step.yml b/.github/workflows/1-step.yml new file mode 100644 index 0000000..7e9c83b --- /dev/null +++ b/.github/workflows/1-step.yml @@ -0,0 +1,91 @@ +name: Step 1 + +on: + issue_comment: + types: + - created + +permissions: + contents: read + actions: write + issues: write + +env: + STEP_2_FILE: ".github/steps/2-step.md" + +jobs: + check_keywords: + name: Check issue comment text for required keyword + runs-on: ubuntu-latest + + steps: + - name: Check for professortocat reference + id: check_professortocat + uses: skills/action-keyphrase-checker@v1 + with: + text: ${{ github.event.comment.body }} + keyphrase: "professortocat" + case-sensitive: false + minimum-occurrences: 1 + + - name: Check for alert reference + id: check_alert + uses: skills/action-keyphrase-checker@v1 + with: + text: ${{ github.event.comment.body }} + keyphrase: "alert" + case-sensitive: false + minimum-occurrences: 1 + + find_exercise: + name: Find Exercise Issue + uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.7.0 + + post_next_step_content: + name: Post next step content + needs: [check_keywords, find_exercise] + runs-on: ubuntu-latest + env: + ISSUE_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ needs.find_exercise.outputs.issue-number }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.7.0 + + - name: Create comment - step finished + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: exercise-toolkit/markdown-templates/step-feedback/step-finished-prepare-next-step.md + vars: | + next_step_number: 2 + + - name: Create comment - add step content + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: ${{ env.STEP_2_FILE }} + + - name: Create comment - watching for progress + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md + + - name: Disable current workflow and enable next one + run: | + gh workflow disable "${{github.workflow}}" + gh workflow enable "Step 2" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/2-fix-the-test.yml b/.github/workflows/2-fix-the-test.yml deleted file mode 100644 index d249925..0000000 --- a/.github/workflows/2-fix-the-test.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Step 2, Fix the test - -# This step triggers after we push to README.md on the ci branch. -# This workflow updates from step 2 to step 3. - -# This will run every time we push to README.md on the ci branch. -# Reference: https://docs.github.com/actions/learn-github-actions/events-that-trigger-workflows -on: - workflow_dispatch: - push: - branches: - - ci - paths: - - README.md - - resume.md - -# Reference: https://docs.github.com/actions/security-guides/automatic-token-authentication -permissions: - # Need `contents: read` to checkout the repository. - # Need `contents: write` to update the step metadata. - contents: write - -jobs: - # Get the current step to only run the main job when the learner is on the same step. - get_current_step: - name: Check current step number - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: get_step - run: | - echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT - outputs: - current_step: ${{ steps.get_step.outputs.current_step }} - - on_fix_test: - name: On fix test - needs: get_current_step - - # We will only run this action when: - # 1. This repository isn't the template repository. - # 2. The step is currently 2. - # Reference: https://docs.github.com/actions/learn-github-actions/contexts - # Reference: https://docs.github.com/actions/learn-github-actions/expressions - if: >- - ${{ !github.event.repository.is_template - && needs.get_current_step.outputs.current_step == 2 }} - - # We'll run Ubuntu for performance instead of Mac or Windows. - runs-on: ubuntu-latest - - steps: - # We'll need to check out the repository so that we can edit the README. - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Let's get all the branches. - - # In README.md, switch step 2 for step 3. - - name: Update to step 3 - uses: skills/action-update-step@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - from_step: 2 - to_step: 3 - branch_name: ci diff --git a/.github/workflows/2-step.yml b/.github/workflows/2-step.yml new file mode 100644 index 0000000..935b7b7 --- /dev/null +++ b/.github/workflows/2-step.yml @@ -0,0 +1,146 @@ +name: Step 2 + +on: + push: + branches: + - main + paths: + - .github/workflows/** + +permissions: + contents: read + actions: write + issues: write + +env: + STEP_3_FILE: ".github/steps/3-step.md" + +jobs: + find_exercise: + name: Find Exercise Issue + uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.7.0 + + check_step_work: + name: Check step work + runs-on: ubuntu-latest + needs: [find_exercise] + env: + ISSUE_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ needs.find_exercise.outputs.issue-number }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.7.0 + + - name: Find last comment + id: find-last-comment + uses: peter-evans/find-comment@v3 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + direction: last + + - name: Update comment - checking work + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + comment-id: ${{ steps.find-last-comment.outputs.comment-id }} + file: exercise-toolkit/markdown-templates/step-feedback/checking-work.md + edit-mode: replace + + - name: Check for python tests workflow + id: check-python-tests-workflow + continue-on-error: true + uses: skills/action-keyphrase-checker@v1 + with: + text-file: .github/workflows/python-tests.yml + keyphrase: python -m unittest discover + minimum-occurrences: 3 + case-sensitive: false + + - name: Check for python coverage workflow + id: check-python-coverage-workflow + continue-on-error: true + uses: skills/action-keyphrase-checker@v1 + with: + text-file: .github/workflows/python-coverage.yml + keyphrase: coverage report + minimum-occurrences: 1 + case-sensitive: false + + - name: Update comment - step results + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + comment-id: ${{ steps.find-last-comment.outputs.comment-id }} + edit-mode: replace + file: exercise-toolkit/markdown-templates/step-feedback/step-results-table.md + vars: | + step_number: 3 + results_table: + - description: "Python tests workflow created" + passed: ${{ steps.check-python-tests-workflow.outcome == 'success' }} + - description: "Python coverage workflow created" + passed: ${{ steps.check-python-coverage-workflow.outcome == 'success' }} + + - name: Fail job if not all checks passed + if: contains(steps.*.outcome, 'failure') + run: exit 1 + + post_next_step_content: + name: Post next step content + needs: [find_exercise] + runs-on: ubuntu-latest + env: + ISSUE_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ needs.find_exercise.outputs.issue-number }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.7.0 + + - name: Create comment - step finished + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: exercise-toolkit/markdown-templates/step-feedback/step-finished-prepare-next-step.md + vars: | + next_step_number: 3 + + - name: Create comment - add step content + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: ${{ env.STEP_3_FILE }} + + - name: Create comment - watching for progress + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md + + - name: Disable current workflow and enable next one + run: | + gh workflow disable "${{github.workflow}}" + gh workflow enable "Step 3" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/3-step.yml b/.github/workflows/3-step.yml new file mode 100644 index 0000000..5e882e8 --- /dev/null +++ b/.github/workflows/3-step.yml @@ -0,0 +1,132 @@ +name: Step 3 + +on: + pull_request: + branches: + - main + +permissions: + contents: read + actions: write + issues: write + +env: + STEP_4_FILE: ".github/steps/4-step.md" + +jobs: + find_exercise: + name: Find Exercise Issue + uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.7.0 + + check_step_work: + name: Check step work + runs-on: ubuntu-latest + needs: [find_exercise] + env: + ISSUE_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ needs.find_exercise.outputs.issue-number }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.7.0 + + - name: Find last comment + id: find-last-comment + uses: peter-evans/find-comment@v3 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + direction: last + + - name: Update comment - checking work + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + comment-id: ${{ steps.find-last-comment.outputs.comment-id }} + file: exercise-toolkit/markdown-templates/step-feedback/checking-work.md + edit-mode: replace + + - name: Check failure test cases + id: check-failure-test-cases + continue-on-error: true + uses: skills/action-keyphrase-checker@v1 + with: + text-file: tests/calculations_test.py + keyphrase: self.assertRaises + minimum-occurrences: 1 + case-sensitive: false + + - name: Update comment - step results + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + comment-id: ${{ steps.find-last-comment.outputs.comment-id }} + edit-mode: replace + file: exercise-toolkit/markdown-templates/step-feedback/step-results-table.md + vars: | + step_number: 3 + results_table: + - description: "Failure test cases added" + passed: ${{ steps.check-failure-test-cases.outcome == 'success' }} + + - name: Fail job if not all checks passed + if: contains(steps.*.outcome, 'failure') + run: exit 1 + + post_next_step_content: + name: Post next step content + needs: [find_exercise] + runs-on: ubuntu-latest + env: + ISSUE_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ needs.find_exercise.outputs.issue-number }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.7.0 + + - name: Create comment - step finished + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: exercise-toolkit/markdown-templates/step-feedback/step-finished-prepare-next-step.md + vars: | + next_step_number: 4 + + - name: Create comment - add step content + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: ${{ env.STEP_4_FILE }} + + - name: Create comment - watching for progress + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md + + - name: Disable current workflow and enable next one + run: | + gh workflow disable "${{github.workflow}}" + gh workflow enable "Step 4" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/3-upload-test-reports.yml b/.github/workflows/3-upload-test-reports.yml deleted file mode 100644 index eb064f6..0000000 --- a/.github/workflows/3-upload-test-reports.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Step 3, Upload test reports - -# This step triggers after push to the ci.yml file on the ci branch. -# This workflow updates from step 3 to step 4. - -# This will run every time we push to the ci.yml file on the ci branch. -# Reference: https://docs.github.com/actions/learn-github-actions/events-that-trigger-workflows -on: - workflow_dispatch: - push: - branches: - - ci - paths: - - .github/workflows/ci.yml - -# Reference: https://docs.github.com/actions/security-guides/automatic-token-authentication -permissions: - # Need `contents: read` to checkout the repository. - # Need `contents: write` to update the step metadata. - contents: write - -jobs: - # Get the current step to only run the main job when the learner is on the same step. - get_current_step: - name: Check current step number - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: get_step - run: | - echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT - outputs: - current_step: ${{ steps.get_step.outputs.current_step }} - - on_add_test_reporting: - name: On add test reporting - needs: get_current_step - - # We will only run this action when: - # 1. This repository isn't the template repository. - # 2. The step is currently 3. - # Reference: https://docs.github.com/actions/learn-github-actions/contexts - # Reference: https://docs.github.com/actions/learn-github-actions/expressions - if: >- - ${{ !github.event.repository.is_template - && needs.get_current_step.outputs.current_step == 3 }} - - # We'll run Ubuntu for performance instead of Mac or Windows. - runs-on: ubuntu-latest - - steps: - # We'll need to check out the repository so that we can edit the README. - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Let's get all the branches. - - # Verify the learner added actions/upload-artifact. - - name: Verify workflow - uses: skills/action-check-file@v1 - with: - file: .github/workflows/ci.yml - search: actions/upload-artifact - - # In README.md, switch step 3 for step 4. - - name: Update to step 4 - uses: skills/action-update-step@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - from_step: 3 - to_step: 4 - branch_name: ci diff --git a/.github/workflows/4-add-branch-protections.yml b/.github/workflows/4-add-branch-protections.yml deleted file mode 100644 index 94327bd..0000000 --- a/.github/workflows/4-add-branch-protections.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Step 4, Add branch protections - -# This step triggers after we turn on or edit a branch protection rule. -# This workflow updates from step 4 to step 5. - -# This will run every time we turn on or edit a branch protection rule. -# Reference: https://docs.github.com/actions/learn-github-actions/events-that-trigger-workflows -on: - workflow_dispatch: - branch_protection_rule: - types: - - created - - edited - -# Reference: https://docs.github.com/actions/security-guides/automatic-token-authentication -permissions: - # Need `contents: read` to checkout the repository. - # Need `contents: write` to update the step metadata. - contents: write - -jobs: - # Get the current step to only run the main job when the learner is on the same step. - get_current_step: - name: Check current step number - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: get_step - run: | - echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT - outputs: - current_step: ${{ steps.get_step.outputs.current_step }} - - on_update_branch_protection: - name: On update branch protection - needs: get_current_step - - # We will only run this action when: - # 1. This repository isn't the template repository. - # 2. The step is currently 4. - # Reference: https://docs.github.com/actions/learn-github-actions/contexts - # Reference: https://docs.github.com/actions/learn-github-actions/expressions - if: >- - ${{ !github.event.repository.is_template - && needs.get_current_step.outputs.current_step == 4 }} - - # We'll run Ubuntu for performance instead of Mac or Windows. - runs-on: ubuntu-latest - - steps: - # We'll need to check out the repository so that we can edit the README. - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Let's get all the branches. - ref: ci # Important, as normally `branch_protection_rule` event won't grab other branches - - - name: Merge changes from origin/main into ci - run: ./.github/script/merge-branch.sh - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - branch1: origin/main - branch2: ci - - # In README.md, switch step 4 for step 5. - - name: Update to step 5 - uses: skills/action-update-step@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - from_step: 4 - to_step: 5 - base_branch_name: ci diff --git a/.github/workflows/4-step.yml b/.github/workflows/4-step.yml new file mode 100644 index 0000000..d79ea18 --- /dev/null +++ b/.github/workflows/4-step.yml @@ -0,0 +1,65 @@ +name: Step 4 + +on: + pull_request: + types: + - closed + +permissions: + contents: write + actions: write + issues: write + +env: + REVIEW_FILE: ".github/steps/x-review.md" + +jobs: + find_exercise: + name: Find Exercise Issue + uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.7.0 + + post_review_content: + name: Post review content + needs: [find_exercise] + runs-on: ubuntu-latest + env: + ISSUE_REPOSITORY: ${{ github.repository }} + ISSUE_NUMBER: ${{ needs.find_exercise.outputs.issue-number }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get response templates + uses: actions/checkout@v4 + with: + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.7.0 + + - name: Create comment - step finished - final review next + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: exercise-toolkit/markdown-templates/step-feedback/lesson-review.md + + - name: Create comment - add review content + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: ${{ env.REVIEW_FILE }} + + - name: Disable current workflow + run: gh workflow disable "${{github.workflow}}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + finish_exercise: + name: Finish Exercise + needs: [find_exercise, post_review_content] + uses: skills/exercise-toolkit/.github/workflows/finish-exercise.yml@v0.7.0 + with: + issue-url: ${{ needs.find_exercise.outputs.issue-url }} + exercise-title: "Getting Started with GitHub Copilot" diff --git a/.github/workflows/5-merge-your-pull-request.yml b/.github/workflows/5-merge-your-pull-request.yml deleted file mode 100644 index 6ed9da2..0000000 --- a/.github/workflows/5-merge-your-pull-request.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Step 5, Merge your pull request - -# This step triggers after a pull requst is merged to `main`. -# This workflow updates from step 5 to step X. - -# This will run every time we create push a commit to `main`. -# Reference: https://docs.github.com/actions/learn-github-actions/events-that-trigger-workflows -on: - workflow_dispatch: - push: - branches: - - main - -# Reference: https://docs.github.com/actions/security-guides/automatic-token-authentication -permissions: - # Need `contents: read` to checkout the repository. - # Need `contents: write` to update the step metadata. - contents: write - -jobs: - # Get the current step to only run the main job when the learner is on the same step. - get_current_step: - name: Check current step number - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: get_step - run: | - echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT - outputs: - current_step: ${{ steps.get_step.outputs.current_step }} - - on_merge: - name: On merge - needs: get_current_step - - # We will only run this action when: - # 1. This repository isn't the template repository. - # 2. The step is currently 5. - # Reference: https://docs.github.com/actions/learn-github-actions/contexts - # Reference: https://docs.github.com/actions/learn-github-actions/expressions - if: >- - ${{ !github.event.repository.is_template - && needs.get_current_step.outputs.current_step == 5 }} - - # We'll run Ubuntu for performance instead of Mac or Windows. - runs-on: ubuntu-latest - - steps: - # We'll need to check out the repository so that we can edit the README. - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Let's get all the branches. - - - # In README.md, switch step 5 for step X. - - name: Update to step X - uses: skills/action-update-step@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - from_step: 5 - to_step: X - base_branch_name: ci diff --git a/.github/workflows/python-coverage.yml b/.github/workflows/python-coverage.yml new file mode 100644 index 0000000..aaf1386 --- /dev/null +++ b/.github/workflows/python-coverage.yml @@ -0,0 +1,72 @@ +name: Python Coverage + +on: + pull_request: + branches: + - main + +jobs: + python-coverage: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.13 + + - name: Install dependencies + run: | + pip install -r requirements.txt + pip install coverage==7.9.1 + pip freeze + + - name: Run tests with coverage + id: coverage + run: | + coverage run -m unittest discover -s tests -p "*_test.py" + echo "coverage_report<> $GITHUB_OUTPUT + coverage report >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Generate coverage reports + run: | + coverage xml + coverage html + + - name: Upload coverage reports + uses: actions/upload-artifact@v4 + with: + name: coverage-reports + path: | + coverage.xml + htmlcov/ + + - name: Post coverage comment + uses: actions/github-script@v7 + with: + script: | + const artifactsUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + const commentBody = `### Test Coverage Report +
+ Coverage Details + + \`\`\` + ${steps.coverage.outputs.coverage_report} + \`\`\` + +
+ + [📊 Detailed HTML Coverage Report](${artifactsUrl}) + `; + + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: commentBody + }); diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml new file mode 100644 index 0000000..5126227 --- /dev/null +++ b/.github/workflows/python-tests.yml @@ -0,0 +1,33 @@ +name: Python Tests + +# act workflow_dispatch -W '.github/workflows/python-tests.yml' +on: + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + pip install -r requirements.txt + pip install coverage + pip freeze + + - name: Run tests + run: | + python -m unittest discover -s tests -p "*_test.py" From 5c37606cb2e1a1aa583233e670ac5401f8513e07 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:03:37 +0000 Subject: [PATCH 03/42] remove dependabot config --- .github/dependabot.yml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 8ac6b8c..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "monthly" From 95247bb7baee5396aa6b787108ef5196cc0a1f1a Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:05:45 +0000 Subject: [PATCH 04/42] Add gitignore --- .gitignore | 251 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 211 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index ee5535f..824164f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,43 +1,214 @@ -# Compiled source # -################### -*.com -*.class -*.dll -*.exe -*.o +######## +# PYTHON - https://github.com/github/gitignore/blob/main/Python.gitignore +######## + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions *.so -# Packages # -############ -# it's better to unpack these files and commit the raw source -# git has its own built in compression methods -*.7z -*.dmg -*.gz -*.iso -*.jar -*.rar -*.tar -*.zip - -# Logs and databases # -###################### +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: *.log -*.sql -*.sqlite - -# OS generated files # -###################### -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# Course specific # -################### -node_modules -package.json -package-lock.json +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock +#poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +#pdm.lock +#pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +#pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Cursor +# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to +# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data +# refer to https://docs.cursor.com/context/ignore-files +.cursorignore +.cursorindexingignore + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml \ No newline at end of file From eea5f8874997bb1a7c6497e0e14c057bfd94bbdb Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:06:17 +0000 Subject: [PATCH 05/42] Add sample program --- src/calculations.py | 20 +++++++ tests/calculations_test.py | 89 ++++++++++++++++++++++++++++++++ tests/supported_versions_test.py | 20 +++++++ 3 files changed, 129 insertions(+) create mode 100644 src/calculations.py create mode 100644 tests/calculations_test.py create mode 100644 tests/supported_versions_test.py diff --git a/src/calculations.py b/src/calculations.py new file mode 100644 index 0000000..3416664 --- /dev/null +++ b/src/calculations.py @@ -0,0 +1,20 @@ +def area_of_circle(radius): + """Calculate the area of a circle given its radius.""" + import math + if radius < 0: + raise ValueError("Radius cannot be negative") + return math.pi * radius ** 2 + +def get_nth_fibonacci(n): + """Calculate the nth Fibonacci number.""" + if n < 0: + raise ValueError("n cannot be negative") + elif n == 0: + return 0 + elif n == 1: + return 1 + else: + a, b = 0, 1 + for _ in range(2, n + 1): + a, b = b, a + b + return b diff --git a/tests/calculations_test.py b/tests/calculations_test.py new file mode 100644 index 0000000..940cae8 --- /dev/null +++ b/tests/calculations_test.py @@ -0,0 +1,89 @@ +# System Modules +import sys +import os +import unittest + +# Installed Modules +# - None + +# Project Modules +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src'))) +from calculations import area_of_circle, get_nth_fibonacci + +class TestCalculations(unittest.TestCase): + + def test_area_of_circle_positive_radius(self): + """Test with a positive radius.""" + # Arrange + radius = 1 + + # Act + result = area_of_circle(radius) + + # Assert + self.assertAlmostEqual(result, 3.14159, places=5) + + def test_area_of_circle_zero_radius(self): + """Test with a radius of zero.""" + # Arrange + radius = 0 + + # Act + result = area_of_circle(radius) + + # Assert + self.assertAlmostEqual(result, 0) + + # def test_area_of_circle_negative_radius(self): + # """Test with a negative radius to raise ValueError.""" + # # Arrange + # radius = -1 + + # # Act & Assert + # with self.assertRaises(ValueError): + # area_of_circle(radius) + + def test_get_nth_fibonacci_zero(self): + """Test with n=0.""" + # Arrange + n = 0 + + # Act + result = get_nth_fibonacci(n) + + # Assert + self.assertEqual(result, 0) + + def test_get_nth_fibonacci_one(self): + """Test with n=1.""" + # Arrange + n = 1 + + # Act + result = get_nth_fibonacci(n) + + # Assert + self.assertEqual(result, 1) + + def test_get_nth_fibonacci_ten(self): + """Test with n=10.""" + # Arrange + n = 10 + + # Act + result = get_nth_fibonacci(n) + + # Assert + self.assertEqual(result, 55) + + # def test_get_nth_fibonacci_negative(self): + # """Test with a negative number to raise ValueError.""" + # # Arrange + # n = -1 + + # # Act & Assert + # with self.assertRaises(ValueError): + # get_nth_fibonacci(n) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/supported_versions_test.py b/tests/supported_versions_test.py new file mode 100644 index 0000000..6a4167a --- /dev/null +++ b/tests/supported_versions_test.py @@ -0,0 +1,20 @@ +# System Modules +import sys +import os +import unittest + +# Installed Modules +# - None + +# Project Modules +# - None + +class SupportedVersions(unittest.TestCase): + + def test_python_version(self): + major, minor = sys.version_info[:2] + self.assertGreaterEqual(major, 3, "Python major version must be >= 3") + self.assertGreaterEqual(minor, 10, "Python minor version must be >= 10") + +if __name__ == "__main__": + unittest.main() From f753f33e6a7f542c776d1f8059b1742a5b65687b Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:06:53 +0000 Subject: [PATCH 06/42] Add dummy requirements files --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4b43f7e --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +matplotlib \ No newline at end of file From 9ea6e9ce9ddbca3ec565ff40181637a2e77a35bc Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:07:01 +0000 Subject: [PATCH 07/42] Add devcontainer config --- .devcontainer/devcontainer.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e2cba88 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,14 @@ +{ + "name": "Python 3", + "image": "mcr.microsoft.com/vscode/devcontainers/python:3.13", + "postCreateCommand": "pip install -r requirements.txt && pip install coverage", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.debugpy", + "github.vscode-github-actions" + ] + } + } +} From 2aeb9892ec829c146c7de1d0f9fbf9adf3a129e7 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:07:07 +0000 Subject: [PATCH 08/42] Remove extra file --- resume.md | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 resume.md diff --git a/resume.md b/resume.md deleted file mode 100644 index 45ff2d8..0000000 --- a/resume.md +++ /dev/null @@ -1,28 +0,0 @@ -# GitHub Teacher - -_Charting the knowledge of the Internet, just like Galileo charted the stars._ - -## Experience - -### GitHub Trainer - -Teach all things *Git*, give away all the stickers, ensure world peace. - - - -### Supportocat - -Provide _world class support_ to customers on the GitHub platform - -## Skills - -### Education - -Developed and maintained various conference talks, online training, and in-person trainings covering various topics including Git, GitHub, and Open Source. - -### Leadership - -Managed multiple _asynchronous teams_ in the development, maintenance, and release of various web applications and websites. From 5fdc493b98f56a3b87d93b1e6c5fabb28ac05ffa Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:17:47 +0000 Subject: [PATCH 09/42] Update readme description to match new steps --- README.md | 95 ++++++++++++++++++++----------------------------------- 1 file changed, 34 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 0d3ddb4..cb29d47 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,48 @@ -
+# Test with Actions - +_Protect your code with automated unit tests and coverage reports._ -# Test with Actions +## Welcome -_Create workflows that enable you to use Continuous Integration (CI) for your projects._ +- **Who is this for**: Developers, DevOps Engineers, new GitHub users, Testing Engineers +- **What you'll learn**: What continuous integration is, how to create a workflow with GitHub Actions for testing, and how to enforce it. +- **What you'll build**: We'll enforce test coverage on sample Python code to run tests against several versions of python in parallel and report results to a pull request, protecting our production version. +- **Prerequisites**: + - [Hello GitHub Actions](https://github.com/skills/hello-github-actions) +- **How long**: This course takes less than 60 minutes to complete. -
+In this course, you will: - +1. Open and run tests in an existing sample project +2. Add a testing workflow through the GitHub web interface +3. Add a coverage workflow in the code editor +4. Investigate and fix a failed test +5. Enforce tests to pass before merging code changes -## Welcome +### How to start this exercise -[Continuous integration](https://en.wikipedia.org/wiki/Continuous_integration) can help you stick to your team’s quality standards by running tests and reporting the results on GitHub. CI tools run builds and tests, triggered by commits. The results post back to GitHub in the pull request. The goal is fewer issues in `main` and faster feedback as you work. +Simply copy the exercise to your account, then give your favorite Octocat (Mona) **about 20 seconds** to prepare the first lesson, then **refresh the page**. -- **Who is this for**: Developers, DevOps Engineers, new GitHub users, students, teams. -- **What you'll learn**: What continuous integration is, how to use GitHub Actions for CI, how to create a workflow that runs tests and produces test reports. -- **What you'll build**: We'll use [remark-lint](https://github.com/remarkjs/remark-lint) to check the consistency of Markdown files. -- **Prerequisites**: We assume you've completed [Hello GitHub Actions](https://github.com/skills/hello-github-actions) first. -- **How long**: This course takes less than two hours to complete. +[![](https://img.shields.io/badge/Copy%20Exercise-%E2%86%92-1f883d?style=for-the-badge&logo=github&labelColor=197935)](https://github.com/new?template_owner=skills-dev&template_name=test-with-actions&owner=%40me&name=skills-test-with-actions&description=Protect+your+code+with+automated+testing&visibility=public) -In this course, you will: +
+Having trouble? 🤷
-1. Add a test workflow -2. Fix the test -3. Upload a test report -4. Add branch protections -5. Merge your pull request - -### How to start this course - - - -[![start-course](https://user-images.githubusercontent.com/1221423/235727646-4a590299-ffe5-480d-8cd5-8194ea184546.svg)](https://github.com/new?template_owner=skills&template_name=test-with-actions&owner=%40me&name=skills-test-with-actions&description=My+clone+repository&visibility=public) - -1. Right-click **Start course** and open the link in a new tab. -2. In the new tab, most of the prompts will automatically fill in for you. - - For owner, choose your personal account or an organization to host the repository. - - We recommend creating a public repository, as private repositories will [use Actions minutes](https://docs.github.com/billing/managing-billing-for-github-actions/about-billing-for-github-actions). - - Scroll down and click the **Create repository** button at the bottom of the form. -3. After your new repository is created, wait about 20 seconds, then refresh the page. Follow the step-by-step instructions in the new repository's README. - -
- - +When copying the exercise, we recommend the following settings: ---- +- For owner, choose your personal account or an organization to host the repository. +- We recommend creating a public repository, as private repositories will [use Actions minutes](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions). + +If the exercise isn't ready in 20 seconds: -Get help: [Post in our discussion board](https://github.com/orgs/skills/discussions/categories/test-with-actions) • [Review the GitHub status page](https://www.githubstatus.com/) +1. After your new repository is created, wait about 20 seconds, then refresh the page. +2. Follow the step-by-step instructions in the issue created in your repository. +3. If the page doesn't refresh automatically, please check the [Actions](../../actions) tab. + - Check to see if a job is running. Sometimes it simply takes a bit longer. + - If the page shows a failed job, please submit an issue. Nice, you found a bug! 🐛 -© 2023 GitHub • [Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/code_of_conduct.md) • [MIT License](https://gh.io/mit) +
+ +--- - +© 2025 GitHub • [Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/code_of_conduct.md) • [MIT License](https://gh.io/mit) From 9a96222ebc409e3a53810ca1bdd68dca6c096f43 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:21:45 +0000 Subject: [PATCH 10/42] fix workflow to start exercise --- .github/workflows/0-start-exercise.yml | 92 +++++++++++++------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/.github/workflows/0-start-exercise.yml b/.github/workflows/0-start-exercise.yml index a98f881..b1ca3c8 100644 --- a/.github/workflows/0-start-exercise.yml +++ b/.github/workflows/0-start-exercise.yml @@ -1,64 +1,66 @@ -name: Step 0, Welcome +name: Step 0 -# This step triggers after the learner creates a new repository from the template. -# This workflow updates from step 0 to step 1. - -# This will run every time we create push a commit to `main`. -# Reference: https://docs.github.com/actions/learn-github-actions/events-that-trigger-workflows on: - workflow_dispatch: push: branches: - main -# Reference: https://docs.github.com/actions/security-guides/automatic-token-authentication permissions: - # Need `contents: read` to checkout the repository. - # Need `contents: write` to update the step metadata. contents: write + actions: write + issues: write + +env: + STEP_1_FILE: ".github/steps/1-step.md" jobs: - # Get the current step to only run the main job when the learner is on the same step. - get_current_step: - name: Check current step number + start_exercise: + if: | + !github.event.repository.is_template + name: Start Exercise + uses: skills/exercise-toolkit/.github/workflows/start-exercise.yml@v0.7.0 + with: + exercise-title: "Test with Actions" + intro-message: "Protect your code with automated unit tests and coverage reports." + + post_next_step_content: + name: Post next step content runs-on: ubuntu-latest + needs: [start_exercise] + env: + ISSUE_NUMBER: ${{ needs.start_exercise.outputs.issue-number }} + ISSUE_REPOSITORY: ${{ github.repository }} + steps: - name: Checkout uses: actions/checkout@v4 - - id: get_step - run: | - echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT - outputs: - current_step: ${{ steps.get_step.outputs.current_step }} - - on_start: - name: On start - needs: get_current_step - - # We will only run this action when: - # 1. This repository isn't the template repository. - # 2. The step is currently 0. - # Reference: https://docs.github.com/actions/learn-github-actions/contexts - # Reference: https://docs.github.com/actions/learn-github-actions/expressions - if: >- - ${{ !github.event.repository.is_template - && needs.get_current_step.outputs.current_step == 0 }} - - # We'll run Ubuntu for performance instead of Mac or Windows. - runs-on: ubuntu-latest - steps: - # We'll need to check out the repository so that we can edit the README. - - name: Checkout + - name: Get response templates uses: actions/checkout@v4 with: - fetch-depth: 0 # Let's get all the branches. + repository: skills/exercise-toolkit + path: exercise-toolkit + ref: v0.7.0 - # In README.md, switch step 0 for step 1. - - name: Update to step 1 - uses: skills/action-update-step@v2 + - name: Create comment - add step content + uses: GrantBirki/comment@v2.1.1 with: - token: ${{ secrets.GITHUB_TOKEN }} - from_step: 0 - to_step: 1 - branch_name: ci + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: ${{ env.STEP_1_FILE }} + vars: | + login: ${{ github.actor }} + full_repo_name: ${{ github.repository }} + + - name: Create comment - watching for progress + uses: GrantBirki/comment@v2.1.1 + with: + repository: ${{ env.ISSUE_REPOSITORY }} + issue-number: ${{ env.ISSUE_NUMBER }} + file: exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md + + - name: Enable next step workflow + run: | + gh workflow enable "Step 1" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 9b0508a3fad0114dbecf09c4c2058d1d63049fd0 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:24:14 +0000 Subject: [PATCH 11/42] Rename workflow files to be examples --- .../{python-coverage.yml => python-coverage.yml.example} | 0 .github/workflows/{python-tests.yml => python-tests.yml.example} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{python-coverage.yml => python-coverage.yml.example} (100%) rename .github/workflows/{python-tests.yml => python-tests.yml.example} (100%) diff --git a/.github/workflows/python-coverage.yml b/.github/workflows/python-coverage.yml.example similarity index 100% rename from .github/workflows/python-coverage.yml rename to .github/workflows/python-coverage.yml.example diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml.example similarity index 100% rename from .github/workflows/python-tests.yml rename to .github/workflows/python-tests.yml.example From 8da1936671a4aa481033f31a80d35a5de33361a9 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 7 Jul 2025 14:24:51 +0000 Subject: [PATCH 12/42] Filter trigger for step 2 --- .github/workflows/2-step.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/2-step.yml b/.github/workflows/2-step.yml index 935b7b7..1876540 100644 --- a/.github/workflows/2-step.yml +++ b/.github/workflows/2-step.yml @@ -5,7 +5,8 @@ on: branches: - main paths: - - .github/workflows/** + - .github/workflows/python-tests.yml + - .github/workflows/python-coverage.yml permissions: contents: read From 8ccc306e64a2c6ee831ee7c78a3c9386063293d8 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Tue, 8 Jul 2025 01:16:24 +0000 Subject: [PATCH 13/42] Adjustments from trial run --- .github/steps/1-step.md | 2 +- .github/steps/2-step.md | 159 +++++++++++++----- .github/steps/3-step.md | 6 +- .github/workflows/1-step.yml | 6 +- .github/workflows/2-step.yml | 4 +- .github/workflows/3-step.yml | 2 +- .github/workflows/python-coverage.yml.example | 95 ++++++++--- 7 files changed, 198 insertions(+), 76 deletions(-) diff --git a/.github/steps/1-step.md b/.github/steps/1-step.md index cc9e950..c35b6e2 100644 --- a/.github/steps/1-step.md +++ b/.github/steps/1-step.md @@ -58,5 +58,5 @@ Let's learn how to automate this process using workflows in GitHub Actions. ```md @professortocat, I've run my coverage report. -It looks good! What's next? +It looks good! Over 90%! :) What's next? ``` diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md index 3ab29ba..8383c20 100644 --- a/.github/steps/2-step.md +++ b/.github/steps/2-step.md @@ -20,7 +20,7 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht ### ⌨️ Activity: Add a workflow to run tests -1. Open a web browser tab and navigate to this exercise repository. The Codespace is not not needed right now. +1. Open a web browser tab and navigate to this exercise repository. The Codespace is not needed right now. 1. In the top navigation, select the **Actions** tab. @@ -40,6 +40,12 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht python-tests.yml ``` +1. Around line 4, update the workflow name. + + ```yml + name: Python Tests + ``` + 1. Around line 6, simplify the `on` trigger to only use pull requests. ```yml @@ -51,7 +57,7 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht 1. Around line 17, update the matrix strategy to use Python versions important to our project. ```yml - python-version: ["3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] ``` 1. Around line 28, remove the unnecessary `pytest` dependency. @@ -64,11 +70,11 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht ```yml - name: Run tests - run: | - python -m unittest discover -s tests -p "*_test.py" + run: | + python -m unittest discover -s tests -p "*_test.py" ``` -1. Above the editor, on the right, click the **Commit changes...**. +1. Above the editor, on the right, click the **Commit changes...**. Commit directly to the `main` branch. ### ⌨️ Activity: Add a workflow to show test coverage @@ -93,18 +99,21 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht pull_request: branches: - main + + permissions: + pull-requests: write ``` 1. Add the `python-coverage` job and a first step that gets the repository content. ```yml jobs: - python-coverage: - runs-on: ubuntu-latest + python-coverage: + runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 + steps: + - name: Checkout code + uses: actions/checkout@v4 ``` 1. Add steps to install Python and required packages. @@ -125,63 +134,121 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht 1. Add steps to run the coverage report and save for later use. ```yml - - name: Run tests with coverage - id: coverage + - name: Run tests to generate coverage details run: | coverage run -m unittest discover -s tests -p "*_test.py" - echo "coverage_report<> $GITHUB_OUTPUT + + - name: Create reports in multiple formats + id: coverage-results + run: | + # Output full textual coverage report + echo "text_report<> $GITHUB_OUTPUT coverage report >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - ``` - -1. Add steps to generate other formats. - ```yml - - name: Generate coverage reports - run: | + # Generate XML and HTML reports coverage xml coverage html - - - name: Upload coverage reports - uses: actions/upload-artifact@v4 - with: - name: coverage-reports - path: | - coverage.xml - htmlcov/ ``` -1. Add a final step to share the coverage report as a comment on the pull requests. +1. Parse XML report to get any desired results. ```yml - - name: Post coverage comment + - name: Parse XML report to get overall percentage + id: coverage-summary + continue-on-error: true uses: actions/github-script@v7 with: script: | - const artifactsUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const fs = require('fs'); - const commentBody = `### Test Coverage Report -
- Coverage Details + // Read the XML file + const xmlData = fs.readFileSync('coverage.xml', 'utf8'); - \`\`\` - ${steps.coverage.outputs.coverage_report} - \`\`\` + // Extract line-rate using regex (simpler than XML parsing) + const lineRateMatch = xmlData.match(/line-rate="([0-9.]+)"/); -
+ if (!lineRateMatch) { + throw new Error('Could not find line-rate in coverage.xml'); + } + + // Save line-rate value to an output variable + const lineRate = lineRateMatch[1]; + core.setOutput('line_rate', lineRate); + ``` + +1. Attach coverage reports to workflow run. + + ```yml + - name: Attach coverage reports to workflow run + uses: actions/upload-artifact@v4 + with: + name: coverage-reports + path: | + coverage.xml + htmlcov/ + ``` - [📊 Detailed HTML Coverage Report](${artifactsUrl}) - `; +1. Add a final step to share the coverage report as a comment on the pull requests. - github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: commentBody - }); + ```yml + - name: Post coverage comment + uses: actions/github-script@v7 + with: + script: | + // The workflow run URL, where the reports are attached + const workflowRunUrl = `https://github.com/${process.env.REPO_NAME}/actions/runs/${process.env.RUN_ID}`; + + // Calculate coverage percentage and determine pass/fail + const minimumCoverage = parseFloat(process.env.MINIMUM_COVERAGE).toFixed(2); + const coveragePercentage = parseFloat(process.env.COVERAGE_REPORT_LINE_RATE).toFixed(2); + const isPassingCoverage = coveragePercentage >= minimumCoverage; + const statusIcon = isPassingCoverage ? '✅' : '❌'; + const statusText = isPassingCoverage ? 'PASS' : 'FAIL'; + + // Create the comment + const commentBody = ` + ### ${statusIcon} Test Coverage Report + **Coverage Overall**: \`${coveragePercentage * 100}%\` + **Coverage Required**: \`${minimumCoverage * 100}%\` + +
+ 📋 Detailed Coverage Report +
+ + \`\`\` + ${process.env.COVERAGE_REPORT_TEXT} + \`\`\` + +
+ + --- + + ### 🔗 Links + - [📊 View Run Details](${workflowRunUrl}) + - [📁 Download Coverage Artifacts (XML, HTML)](${workflowRunUrl}#artifacts) + + --- + 🤖 This comment was automatically generated by the Python Coverage workflow + `; + + github.rest.issues.createComment({ + owner: process.env.REPO_NAME.split('/')[0], + repo: process.env.REPO_NAME.split('/')[1], + issue_number: Number(process.env.ISSUE_NUMBER), + body: commentBody + }); + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUN_ID: ${{ github.run_id }} + REPO_NAME: ${{ github.repository }} + ISSUE_NUMBER: ${{ github.event.pull_request.number }} + COVERAGE_REPORT_TEXT: ${{ steps.coverage-results.outputs.text_report }} + COVERAGE_REPORT_LINE_RATE: ${{ steps.coverage-summary.outputs.line_rate }} + MINIMUM_COVERAGE: 0.9 ``` -1. Commit and push the coverage workflow. +1. Commit and push the changes to your `python-coverage.yml` file to the `main` branch. > [!TIP] > We used custom steps here to show the flexibility of Actions. There are several AWESOME pre-made options from the community on the free [Actions Marketplace](https://github.com/marketplace?type=actions). Consider trying one of them out before you build your own! diff --git a/.github/steps/3-step.md b/.github/steps/3-step.md index 10137c6..2313d5b 100644 --- a/.github/steps/3-step.md +++ b/.github/steps/3-step.md @@ -12,7 +12,7 @@ Pull requests have a nice advantage when a workflow is associated with them. The 1. Return to the VS Code Codespace. -1. Create a new branch based on `main` with the following name. +1. Create a new branch based on `main` with the following name and **publish** it to GitHub. ```txt add-failure-test-cases @@ -44,7 +44,7 @@ Pull requests have a nice advantage when a workflow is associated with them. The get_nth_fibonacci(n) ``` -1. Commit the changes and push them. +1. Commit the changes and push them to GitHub. 1. Return to the browser and create a pull request. Use the following details. @@ -52,7 +52,7 @@ Pull requests have a nice advantage when a workflow is associated with them. The - **source:** `add-failure-test-cases` - **title**: `feat: Add tests to verify failure cases` -1. After the pull request is started, look near the Merge button to see the workflows running. +1. After the pull request is created, look near the Merge button to see the workflows running. - The workflows will fail, letting us know we have a test to fix. - The test coverage report will come shortly as a comment. diff --git a/.github/workflows/1-step.yml b/.github/workflows/1-step.yml index 7e9c83b..4e71731 100644 --- a/.github/workflows/1-step.yml +++ b/.github/workflows/1-step.yml @@ -20,7 +20,6 @@ jobs: steps: - name: Check for professortocat reference - id: check_professortocat uses: skills/action-keyphrase-checker@v1 with: text: ${{ github.event.comment.body }} @@ -28,12 +27,11 @@ jobs: case-sensitive: false minimum-occurrences: 1 - - name: Check for alert reference - id: check_alert + - name: Check for coverage reference uses: skills/action-keyphrase-checker@v1 with: text: ${{ github.event.comment.body }} - keyphrase: "alert" + keyphrase: "coverage" case-sensitive: false minimum-occurrences: 1 diff --git a/.github/workflows/2-step.yml b/.github/workflows/2-step.yml index 1876540..4674875 100644 --- a/.github/workflows/2-step.yml +++ b/.github/workflows/2-step.yml @@ -64,7 +64,7 @@ jobs: with: text-file: .github/workflows/python-tests.yml keyphrase: python -m unittest discover - minimum-occurrences: 3 + minimum-occurrences: 1 case-sensitive: false - name: Check for python coverage workflow @@ -99,7 +99,7 @@ jobs: post_next_step_content: name: Post next step content - needs: [find_exercise] + needs: [find_exercise, check_step_work] runs-on: ubuntu-latest env: ISSUE_REPOSITORY: ${{ github.repository }} diff --git a/.github/workflows/3-step.yml b/.github/workflows/3-step.yml index 5e882e8..a37ecf6 100644 --- a/.github/workflows/3-step.yml +++ b/.github/workflows/3-step.yml @@ -84,7 +84,7 @@ jobs: post_next_step_content: name: Post next step content - needs: [find_exercise] + needs: [find_exercise, check_step_work] runs-on: ubuntu-latest env: ISSUE_REPOSITORY: ${{ github.repository }} diff --git a/.github/workflows/python-coverage.yml.example b/.github/workflows/python-coverage.yml.example index aaf1386..203b80f 100644 --- a/.github/workflows/python-coverage.yml.example +++ b/.github/workflows/python-coverage.yml.example @@ -5,6 +5,9 @@ on: branches: - main +permissions: + pull-requests: write + jobs: python-coverage: runs-on: ubuntu-latest @@ -21,23 +24,48 @@ jobs: - name: Install dependencies run: | pip install -r requirements.txt - pip install coverage==7.9.1 + pip install coverage==7.9 pip freeze - - - name: Run tests with coverage - id: coverage + + - name: Run tests to generate coverage details run: | coverage run -m unittest discover -s tests -p "*_test.py" - echo "coverage_report<> $GITHUB_OUTPUT + + - name: Create reports in multiple formats + id: coverage-results + run: | + # Output full textual coverage report + echo "text_report<> $GITHUB_OUTPUT coverage report >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - - name: Generate coverage reports - run: | + # Generate XML and HTML reports coverage xml coverage html - - name: Upload coverage reports + - name: Parse XML report to get overall percentage + id: coverage-summary + # continue-on-error: true + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + + // Read the XML file + const xmlData = fs.readFileSync('coverage.xml', 'utf8'); + + // Extract line-rate using regex (simpler than XML parsing) + const lineRateMatch = xmlData.match(/line-rate="([0-9.]+)"/); + + if (!lineRateMatch) { + throw new Error('Could not find line-rate in coverage.xml'); + } + + // Save line-rate value to an output variable + const lineRate = lineRateMatch[1]; + core.setOutput('line_rate', lineRate); + + - name: Attach coverage reports to workflow run uses: actions/upload-artifact@v4 with: name: coverage-reports @@ -49,24 +77,53 @@ jobs: uses: actions/github-script@v7 with: script: | - const artifactsUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + // The workflow run URL, where the reports are attached + const workflowRunUrl = `https://github.com/${process.env.REPO_NAME}/actions/runs/${process.env.RUN_ID}`; - const commentBody = `### Test Coverage Report -
- Coverage Details + // Calculate coverage percentage and determine pass/fail + const minimumCoverage = parseFloat(process.env.MINIMUM_COVERAGE).toFixed(2); + const coveragePercentage = parseFloat(process.env.COVERAGE_REPORT_LINE_RATE).toFixed(2); + const isPassingCoverage = coveragePercentage >= minimumCoverage; + const statusIcon = isPassingCoverage ? '✅' : '❌'; + const statusText = isPassingCoverage ? 'PASS' : 'FAIL'; + + // Create the comment + const commentBody = ` + ### ${statusIcon} Test Coverage Report + **Coverage Overall**: \`${coveragePercentage * 100}%\` + **Coverage Required**: \`${minimumCoverage * 100}%\` +
+ 📋 Detailed Coverage Report +
+ \`\`\` - ${steps.coverage.outputs.coverage_report} + ${process.env.COVERAGE_REPORT_TEXT} \`\`\` - +
- - [📊 Detailed HTML Coverage Report](${artifactsUrl}) + + --- + + ### 🔗 Links + - [📊 View Run Details](${workflowRunUrl}) + - [📁 Download Coverage Artifacts (XML, HTML)](${workflowRunUrl}#artifacts) + + --- + 🤖 This comment was automatically generated by the Python Coverage workflow `; github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, + owner: process.env.REPO_NAME.split('/')[0], + repo: process.env.REPO_NAME.split('/')[1], + issue_number: Number(process.env.ISSUE_NUMBER), body: commentBody }); + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUN_ID: ${{ github.run_id }} + REPO_NAME: ${{ github.repository }} + ISSUE_NUMBER: ${{ github.event.pull_request.number }} + COVERAGE_REPORT_TEXT: ${{ steps.coverage-results.outputs.text_report }} + COVERAGE_REPORT_LINE_RATE: ${{ steps.coverage-summary.outputs.line_rate }} + MINIMUM_COVERAGE: 0.9 \ No newline at end of file From 89194dc3c2e74c77078eab1d0b49af76ce96780b Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Tue, 8 Jul 2025 02:46:35 +0000 Subject: [PATCH 14/42] Adjustments from trial run --- .github/steps/1-step.md | 4 +- .github/steps/2-step.md | 142 ++++++++++++--------- .github/steps/3-step.md | 6 +- .github/workflows/python-tests.yml.example | 16 ++- 4 files changed, 100 insertions(+), 68 deletions(-) diff --git a/.github/steps/1-step.md b/.github/steps/1-step.md index c35b6e2..e707f0f 100644 --- a/.github/steps/1-step.md +++ b/.github/steps/1-step.md @@ -2,9 +2,9 @@ GitHub Actions is a great way to automate several of your recurring tasks, saving you time to work on the more challenging and fun problems! -One of the most common tasks a developer deals with is testing their code. Unfortunately, this is often tedious and things get skipped or simply overlooked. Even more so, we often have to test against many frameworks, operating systems, and other situations, exaggerating the problem. +One of the most common tasks a developer deals with is testing their code. Unfortunately, this is often tedious and things get skipped or simply overlooked. Even more so, we often need to test against many frameworks, operating systems, and other situations, exaggerating the problem. -Let's learn how to automate this process using workflows in GitHub Actions. +Let's learn how to automate this ever-growing need for testing our code by using workflows in GitHub Actions. ### What is Continuous Integration? diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md index 8383c20..b78a709 100644 --- a/.github/steps/2-step.md +++ b/.github/steps/2-step.md @@ -54,6 +54,13 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht branches: ["main"] ``` +1. Around line 10, rename the job to be more specific. + + ```yml + jobs: + python-tests-by-version: + ``` + 1. Around line 17, update the matrix strategy to use Python versions important to our project. ```yml @@ -74,12 +81,34 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht python -m unittest discover -s tests -p "*_test.py" ``` -1. Above the editor, on the right, click the **Commit changes...**. Commit directly to the `main` branch. +1. At the very end, add another job that will verify all versions passed. Note: this is a job, not a step. + + ```yml + python-tests: + runs-on: ubuntu-latest + needs: [python-tests-by-version] + if: always() + steps: + - name: All pass + if: ${{ !(contains(needs.*.result, 'failure')) }} + run: exit 0 + - name: Any fail + if: ${{ contains(needs.*.result, 'failure') }} + run: exit 1 + ``` + + > **💡 Note:** This is not required, but it simplifies our Ruleset in next step. Rulesets require selecting specific job names. + +1. Above the editor, on the right, click the **Commit changes...** button. Commit directly to the `main` branch. ### ⌨️ Activity: Add a workflow to show test coverage 1. Switch to the VS Code Codespace. +1. Check the status bar for a pending update. Click it to pull your recently committed workflow. + + image + 1. In the left navigation, select the **Explorer** tab to show the project files. 1. Expand the `.github/workflows/` folder. @@ -182,11 +211,11 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht ```yml - name: Attach coverage reports to workflow run uses: actions/upload-artifact@v4 - with: - name: coverage-reports - path: | - coverage.xml - htmlcov/ + with: + name: coverage-reports + path: | + coverage.xml + htmlcov/ ``` 1. Add a final step to share the coverage report as a comment on the pull requests. @@ -194,50 +223,6 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht ```yml - name: Post coverage comment uses: actions/github-script@v7 - with: - script: | - // The workflow run URL, where the reports are attached - const workflowRunUrl = `https://github.com/${process.env.REPO_NAME}/actions/runs/${process.env.RUN_ID}`; - - // Calculate coverage percentage and determine pass/fail - const minimumCoverage = parseFloat(process.env.MINIMUM_COVERAGE).toFixed(2); - const coveragePercentage = parseFloat(process.env.COVERAGE_REPORT_LINE_RATE).toFixed(2); - const isPassingCoverage = coveragePercentage >= minimumCoverage; - const statusIcon = isPassingCoverage ? '✅' : '❌'; - const statusText = isPassingCoverage ? 'PASS' : 'FAIL'; - - // Create the comment - const commentBody = ` - ### ${statusIcon} Test Coverage Report - **Coverage Overall**: \`${coveragePercentage * 100}%\` - **Coverage Required**: \`${minimumCoverage * 100}%\` - -
- 📋 Detailed Coverage Report -
- - \`\`\` - ${process.env.COVERAGE_REPORT_TEXT} - \`\`\` - -
- - --- - - ### 🔗 Links - - [📊 View Run Details](${workflowRunUrl}) - - [📁 Download Coverage Artifacts (XML, HTML)](${workflowRunUrl}#artifacts) - - --- - 🤖 This comment was automatically generated by the Python Coverage workflow - `; - - github.rest.issues.createComment({ - owner: process.env.REPO_NAME.split('/')[0], - repo: process.env.REPO_NAME.split('/')[1], - issue_number: Number(process.env.ISSUE_NUMBER), - body: commentBody - }); env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RUN_ID: ${{ github.run_id }} @@ -246,6 +231,50 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht COVERAGE_REPORT_TEXT: ${{ steps.coverage-results.outputs.text_report }} COVERAGE_REPORT_LINE_RATE: ${{ steps.coverage-summary.outputs.line_rate }} MINIMUM_COVERAGE: 0.9 + with: + script: | + // The workflow run URL, where the reports are attached + const workflowRunUrl = `https://github.com/${process.env.REPO_NAME}/actions/runs/${process.env.RUN_ID}`; + + // Calculate coverage percentage and determine pass/fail + const minimumCoverage = parseFloat(process.env.MINIMUM_COVERAGE).toFixed(2); + const coveragePercentage = parseFloat(process.env.COVERAGE_REPORT_LINE_RATE).toFixed(2); + const isPassingCoverage = coveragePercentage >= minimumCoverage; + const statusIcon = isPassingCoverage ? '✅' : '❌'; + const statusText = isPassingCoverage ? 'PASS' : 'FAIL'; + + // Create the comment + const commentBody = ` + ### ${statusIcon} Test Coverage Report + **Coverage Overall**: \`${coveragePercentage * 100}%\` + **Coverage Required**: \`${minimumCoverage * 100}%\` + +
+ 📋 Detailed Coverage Report +
+ + \`\`\` + ${process.env.COVERAGE_REPORT_TEXT} + \`\`\` + +
+ + --- + + ### 🔗 Links + - [📊 View Run Details](${workflowRunUrl}) + - [📁 Download Coverage Artifacts (XML, HTML)](${workflowRunUrl}#artifacts) + + --- + 🤖 This comment was automatically generated by the Python Coverage workflow + `; + + github.rest.issues.createComment({ + owner: process.env.REPO_NAME.split('/')[0], + repo: process.env.REPO_NAME.split('/')[1], + issue_number: Number(process.env.ISSUE_NUMBER), + body: commentBody + }); ``` 1. Commit and push the changes to your `python-coverage.yml` file to the `main` branch. @@ -253,18 +282,7 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht > [!TIP] > We used custom steps here to show the flexibility of Actions. There are several AWESOME pre-made options from the community on the free [Actions Marketplace](https://github.com/marketplace?type=actions). Consider trying one of them out before you build your own! -⌨️ Activity: Merge workflows - -1. Create a new pull request. Use the following details. - - - **target:** `main` - - **base:** `add-testing-workflows` - - **title:** `feat: Add testing and coverage workflows` - - **description:** (blank) - -1. Ensure - -1. With the new workflow pushed to GitHub, Mona will review your work and post the next steps. +1. With both new workflows pushed to GitHub, Mona will review your work and post the next steps. - - diff --git a/.github/steps/3-step.md b/.github/steps/3-step.md index 5fdd2fe..bb4ac29 100644 --- a/.github/steps/3-step.md +++ b/.github/steps/3-step.md @@ -1,9 +1,5 @@ ## Step 3: Trigger the workflows - - - - As specified in our workflows, they will only run when a pull request is targeting the `main` branch. Pull requests have a nice advantage when a workflow is associated with them. The run status and results can be displayed directly in the conversation feed. diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md index 3c1a0a8..0a119f3 100644 --- a/.github/steps/4-step.md +++ b/.github/steps/4-step.md @@ -5,7 +5,7 @@ Even worse, the some tests failed and there was nothing to prevent merging the b Let's fix this to avoid anyone (accidentally) bypassing verification. -### ⌨️ Activity: Add branch protections +### ⌨️ Activity: Add branch protection 1. In the top navigation, select the **Settings** tab. From 175ba4fdf155763f8d6fbbab11ac4e856723f6f7 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Sun, 13 Jul 2025 08:28:23 +0000 Subject: [PATCH 23/42] Add minimums to example --- .github/workflows/python-coverage.yml.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/python-coverage.yml.example b/.github/workflows/python-coverage.yml.example index 702e9bb..063c012 100644 --- a/.github/workflows/python-coverage.yml.example +++ b/.github/workflows/python-coverage.yml.example @@ -36,3 +36,5 @@ jobs: uses: py-cov-action/python-coverage-comment-action@v3 with: GITHUB_TOKEN: ${{ github.token }} + MINIMUM_GREEN: 90 + MINIMUM_ORANGE: 70 From 245f480e0b0b5d8018048bbeac1d1c9c22aa95ee Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Sun, 13 Jul 2025 09:04:23 +0000 Subject: [PATCH 24/42] tweak python code to pass flake8 --- src/calculations.py | 2 ++ tests/calculations_test.py | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/calculations.py b/src/calculations.py index 85aa6bb..7b38f7e 100644 --- a/src/calculations.py +++ b/src/calculations.py @@ -4,12 +4,14 @@ # Installed Modules # - None + def area_of_circle(radius): """Calculate the area of a circle given its radius.""" if radius < 0: raise ValueError("Radius cannot be negative") return math.pi * radius ** 2 + def get_nth_fibonacci(n): """Calculate the nth Fibonacci number.""" if n < 0: diff --git a/tests/calculations_test.py b/tests/calculations_test.py index 42dc406..2b49d57 100644 --- a/tests/calculations_test.py +++ b/tests/calculations_test.py @@ -3,11 +3,12 @@ import os # Installed Modules -import pytest +# None # Project Modules sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src'))) -from calculations import area_of_circle, get_nth_fibonacci +from calculations import area_of_circle, get_nth_fibonacci # noqa: E402 + def test_area_of_circle_positive_radius(): """Test with a positive radius.""" @@ -20,6 +21,7 @@ def test_area_of_circle_positive_radius(): # Assert assert abs(result - 3.14159) < 1e-5 + def test_area_of_circle_zero_radius(): """Test with a radius of zero.""" # Arrange @@ -31,6 +33,7 @@ def test_area_of_circle_zero_radius(): # Assert assert result == 0 + def test_get_nth_fibonacci_zero(): """Test with n=0.""" # Arrange @@ -42,6 +45,7 @@ def test_get_nth_fibonacci_zero(): # Assert assert result == 0 + def test_get_nth_fibonacci_one(): """Test with n=1.""" # Arrange @@ -53,6 +57,7 @@ def test_get_nth_fibonacci_one(): # Assert assert result == 1 + # def test_get_nth_fibonacci_ten(): # """Test with n=10.""" # # Arrange @@ -63,4 +68,3 @@ def test_get_nth_fibonacci_one(): # # Assert # assert result == 89 - From e67d5c50e8e269183dd970f3ec7808c1c0f9ac00 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Sun, 13 Jul 2025 09:06:47 +0000 Subject: [PATCH 25/42] rename file to match default --- .../{python-tests.yml.example => python-package.yml.example} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{python-tests.yml.example => python-package.yml.example} (100%) diff --git a/.github/workflows/python-tests.yml.example b/.github/workflows/python-package.yml.example similarity index 100% rename from .github/workflows/python-tests.yml.example rename to .github/workflows/python-package.yml.example From 28d9ebcc56b31a9de562c52d4901bb245b2b215e Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Sun, 13 Jul 2025 10:24:44 +0000 Subject: [PATCH 26/42] cleanup from test run --- .github/steps/1-step.md | 6 ------ .github/steps/2-step.md | 14 +------------- .github/steps/3-step.md | 2 +- .github/steps/4-step.md | 21 +++++++++++++-------- .github/workflows/2-step.yml | 6 +++--- .github/workflows/3-step.yml | 13 +++++++------ .github/workflows/4-step.yml | 1 + 7 files changed, 26 insertions(+), 37 deletions(-) diff --git a/.github/steps/1-step.md b/.github/steps/1-step.md index 0c11bc8..b93e75d 100644 --- a/.github/steps/1-step.md +++ b/.github/steps/1-step.md @@ -48,12 +48,6 @@ Let's learn how to automate this ever-growing need for testing our code by using pytest --cov=src --verbose ``` -1. Run the below command to show a test coverage report. - - ```bash - coverage report - ``` - 1. Add a comment to let Mona know the results of your coverage report. After reviewing, she will provide the next steps. ```md diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md index 437d16b..bb290c4 100644 --- a/.github/steps/2-step.md +++ b/.github/steps/2-step.md @@ -34,18 +34,6 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht image -1. Above the file editor, update the file name. - - ```txt - python-tests.yml - ``` - -1. Around line 4, update the workflow name. - - ```yml - name: Python Tests - ``` - 1. Around line 6, simplify the `on` trigger to only use pull requests. ```yml @@ -132,7 +120,7 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht pytest --cov=src ``` -1. Add a final step to uses a pre-made GitHub Action that shares the coverage report as a comment on the pull requests. +1. Add a final step that uses a pre-made GitHub Action to share the coverage report as a comment on the pull request. {% raw %} diff --git a/.github/steps/3-step.md b/.github/steps/3-step.md index bb4ac29..0590409 100644 --- a/.github/steps/3-step.md +++ b/.github/steps/3-step.md @@ -41,7 +41,7 @@ Pull requests have a nice advantage when a workflow is associated with them. The - **source:** `reenable-unit-test` - **title**: `Reenable unit test that was disabled` -1. After the pull request is created, look near the Merge button to see the many workflows running. +1. After the pull request is created, look near the Merge button to see many workflows running. - Our coverage workflow will fail, letting us know we have a test to fix. diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md index 0a119f3..ccccdb2 100644 --- a/.github/steps/4-step.md +++ b/.github/steps/4-step.md @@ -25,17 +25,19 @@ Let's fix this to avoid anyone (accidentally) bypassing verification. target branch settings - required status checks + required status checks 1. Click **Create**. 1. Navigate back to the pull request and refresh the page. -1. The **Merge** button is now disabled! Nice! 🥰 +1. Scroll to the bottom to find failed workflows and the **Merge** button now disabled! Nice! 🥰 - failed tests and disabled merge button + failed tests and disabled merge button -1. In the pull request, find the comment that shares information about the coverage and failed tests. There are 2 issues preventing merging. +1. Click on the `Python Coverage` workflow to view the logs. It will automatically navigate to the failed logs. + +1. After some inspection, there are 2 issues preventing merging. - 1 test is failing. - Coverage is below the 90% requirement. @@ -65,9 +67,9 @@ Let's fix this to avoid anyone (accidentally) bypassing verification. assert result == 55 ``` -1. Commit the change and wait for the updated coverage report. +1. Commit the corrected test and wait for the workflows to run again. - - We will now see that all tests pass, but unfortunately the coverage is still too low. + - This time the tests pass and we receive a detailed coverage report. ## Activity: Fix low test coverage @@ -110,9 +112,12 @@ Let's fix this to avoid anyone (accidentally) bypassing verification.
-1. Commit and push the changes. +1. Commit and push the 2 new tests. + +1. Wait a moment for the workflows to run one final time. -1. Wait a moment for the tests to complete. If they all pass, the merge button should activate! + - The coverage comment will update to 100%. + - The merge button will activate! ### Activity: Merge diff --git a/.github/workflows/2-step.yml b/.github/workflows/2-step.yml index 4674875..743fc43 100644 --- a/.github/workflows/2-step.yml +++ b/.github/workflows/2-step.yml @@ -62,8 +62,8 @@ jobs: continue-on-error: true uses: skills/action-keyphrase-checker@v1 with: - text-file: .github/workflows/python-tests.yml - keyphrase: python -m unittest discover + text-file: .github/workflows/python-package.yml + keyphrase: pytest minimum-occurrences: 1 case-sensitive: false @@ -73,7 +73,7 @@ jobs: uses: skills/action-keyphrase-checker@v1 with: text-file: .github/workflows/python-coverage.yml - keyphrase: coverage report + keyphrase: pytest --cov=src minimum-occurrences: 1 case-sensitive: false diff --git a/.github/workflows/3-step.yml b/.github/workflows/3-step.yml index a37ecf6..c16d6d2 100644 --- a/.github/workflows/3-step.yml +++ b/.github/workflows/3-step.yml @@ -54,14 +54,15 @@ jobs: file: exercise-toolkit/markdown-templates/step-feedback/checking-work.md edit-mode: replace - - name: Check failure test cases - id: check-failure-test-cases + - name: Check test case uncommented + id: check-test-uncommented continue-on-error: true uses: skills/action-keyphrase-checker@v1 with: text-file: tests/calculations_test.py - keyphrase: self.assertRaises - minimum-occurrences: 1 + keyphrase: '# def test_get_nth_fibonacci_ten' + maximum-occurrences: 0 + minimum-occurrences: 0 case-sensitive: false - name: Update comment - step results @@ -75,8 +76,8 @@ jobs: vars: | step_number: 3 results_table: - - description: "Failure test cases added" - passed: ${{ steps.check-failure-test-cases.outcome == 'success' }} + - description: "Test case re-enabled" + passed: ${{ steps.check-test-uncommented.outcome == 'success' }} - name: Fail job if not all checks passed if: contains(steps.*.outcome, 'failure') diff --git a/.github/workflows/4-step.yml b/.github/workflows/4-step.yml index d79ea18..ddc0f4d 100644 --- a/.github/workflows/4-step.yml +++ b/.github/workflows/4-step.yml @@ -63,3 +63,4 @@ jobs: with: issue-url: ${{ needs.find_exercise.outputs.issue-url }} exercise-title: "Getting Started with GitHub Copilot" + update-readme-with-congratulations: false From 8b0dbcc4825d70ed93c760ccee71ec5fdf9d4412 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 01:23:36 +0000 Subject: [PATCH 27/42] Add coverage config file to enable coverage comment action --- .coveragerc | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..ce2f455 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,2 @@ +[run] +relative_files = True \ No newline at end of file From 2dac763a0466d5f02cf81fb00e103619c89bc1e1 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 02:22:40 +0000 Subject: [PATCH 28/42] Fix python-package example to match tutorial --- .github/workflows/python-package.yml.example | 42 +++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/.github/workflows/python-package.yml.example b/.github/workflows/python-package.yml.example index 0f80131..330cd64 100644 --- a/.github/workflows/python-package.yml.example +++ b/.github/workflows/python-package.yml.example @@ -1,31 +1,35 @@ -name: Python Tests +name: Python package on: pull_request: - branches: - - main + branches: [ "main" ] jobs: build: + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11"] steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - pip install -r requirements.txt - pip install pytest - - - name: Run tests - run: | - pytest --verbose + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest --verbose \ No newline at end of file From 08bc2c2aa5db798cf1f4b97cc87e4b2d9a73dd70 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 02:23:21 +0000 Subject: [PATCH 29/42] Configure python-coverage workflow to fail if below limit --- .github/steps/2-step.md | 6 ++++-- .github/workflows/python-coverage.yml.example | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md index bb290c4..9d0e869 100644 --- a/.github/steps/2-step.md +++ b/.github/steps/2-step.md @@ -116,8 +116,7 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht ```yml - name: Run tests and generate coverage details - run: | - pytest --cov=src + run: pytest --cov=src ``` 1. Add a final step that uses a pre-made GitHub Action to share the coverage report as a comment on the pull request. @@ -131,6 +130,9 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht GITHUB_TOKEN: ${{ github.token }} MINIMUM_GREEN: 90 MINIMUM_ORANGE: 70 + + - name: Fail if below threshold + run: coverage report --fail-under=90 ``` {% endraw %} diff --git a/.github/workflows/python-coverage.yml.example b/.github/workflows/python-coverage.yml.example index 063c012..9130568 100644 --- a/.github/workflows/python-coverage.yml.example +++ b/.github/workflows/python-coverage.yml.example @@ -29,8 +29,7 @@ jobs: pip install pytest-cov==6.2.1 - name: Run tests and generate coverage details - run: | - pytest --cov=src + run: pytest --cov=src - name: Coverage comment uses: py-cov-action/python-coverage-comment-action@v3 @@ -38,3 +37,6 @@ jobs: GITHUB_TOKEN: ${{ github.token }} MINIMUM_GREEN: 90 MINIMUM_ORANGE: 70 + + - name: Fail if below threshold + run: coverage report --fail-under=90 From 4d6bee2ec01ec1d4a4558c93cae76cf2ac4da963 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 02:24:27 +0000 Subject: [PATCH 30/42] cleanup --- .github/steps/1-step.md | 11 +++++------ .github/steps/2-step.md | 14 ++++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/steps/1-step.md b/.github/steps/1-step.md index b93e75d..9eb4af3 100644 --- a/.github/steps/1-step.md +++ b/.github/steps/1-step.md @@ -12,8 +12,6 @@ Let's learn how to automate this ever-growing need for testing our code by using ### ⌨️ Activity: Start our sample Python application -1. Open a new browser tab, and work through the following steps in that tab while you read the instructions in this tab. - 1. Use the below button to open the **Create Codespace** page in a new tab. Use the default configuration. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/{{full_repo_name}}?quickstart=1) @@ -50,7 +48,8 @@ Let's learn how to automate this ever-growing need for testing our code by using 1. Add a comment to let Mona know the results of your coverage report. After reviewing, she will provide the next steps. -```md -@professortocat, I've run my coverage report. -It looks good! Over 90%! :) What's next? -``` + ```md + @professortocat, I've run my coverage report. + Seems there is some opportunity to increase the test coverage. 🧐 + What should we do next? + ``` diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md index 9d0e869..940a1b5 100644 --- a/.github/steps/2-step.md +++ b/.github/steps/2-step.md @@ -6,11 +6,11 @@ The best way to add automation to your project's repository is with a GitHub Act ![An illustration with a left half and a right half. On the left: illustration of how GitHub Actions terms are encapsulated. At the highest level: workflows and event triggers. Inside workflows: jobs and definition of the build environment. Inside jobs: steps. Inside steps: a call to an action. On the right: the evaluated sequence: workflow, job, step, action.](https://user-images.githubusercontent.com/6351798/88589835-f5ce0900-d016-11ea-8c8a-0e7d7907c713.png) -- **Workflow**: A unit of automation from start to finish. It starts when the trigger (`on`) matches an activity in the repository and runs 1 or more jobs. +- **Workflow**: A unit of automation from start to finish. It begins when the trigger (`on`) matches an activity in the repository. It consists of 1 or more jobs. - **Jobs**: The workflow's jobs each run in their own isolated environments and can be configured differently. They run in parallel unless configured otherwise or dependencies are set. -- **Steps**: The steps area is a series of related actions that achieve a job's goal. A step can be a pre-made _Action_ from the Actions Marketplace, a private workflow, or even a custom script. +- **Steps**: The steps area is a series of related actions that achieve a job's goal. A step can be a pre-made _Action_ from the Actions Marketplace, a private Action, a local custom script, or even direct code. - **Action**: Each step is an _Action_, a piece of automation written in a way that is compatible with workflows. Actions can be written by GitHub, by the open source community, or specific to the project. @@ -26,6 +26,8 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht 1. In the left navigation, above list of workflows, click the **New workflow** button. + image + 1. Enter `python` into the search box and click the **Enter** button. search box with 'python' value @@ -34,7 +36,7 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht image -1. Around line 6, simplify the `on` trigger to only use pull requests. +1. Around line 6, simplify the `on` trigger. Remove the `push` trigger and keep the `pull_request` trigger. ```yml on: @@ -42,10 +44,10 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht branches: ["main"] ``` -1. Around line 36, change the command to show more details in the logs. +1. Around line 38, change the command to show more details in the logs. ```yml - - name: Run tests + - name: Test with pytest run: | pytest --verbose ``` @@ -140,6 +142,6 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht 1. Commit and push the changes to your `python-coverage.yml` file to the `main` branch. > [!TIP] -> Dis you notice the `uses:` statements? Those pre-made steps on the free [Actions Marketplace](https://github.com/marketplace?type=actions). Consider trying one of them out before you build your own! There are lots of AWESOME creations from the community! +> Dis you notice the `uses:` statements? Those are pre-made steps from the free [Actions Marketplace](https://github.com/marketplace?type=actions). Consider trying one of them out before you create your own custom scripts (to maintain)! There are lots of AWESOME creations from the community! 1. With both new workflows pushed to GitHub, Mona will review your work and post the next steps. From c15f8be9a98172eff785a3e058aea9ecd7d6fea0 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 02:24:45 +0000 Subject: [PATCH 31/42] Add references to example files. --- .github/steps/2-step.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md index 940a1b5..8fee570 100644 --- a/.github/steps/2-step.md +++ b/.github/steps/2-step.md @@ -54,6 +54,15 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht 1. Above the editor, on the right, click the **Commit changes...** button. Commit directly to the `main` branch. +
+Having trouble? 🤷‍♂️ + +Indention of `.yml` files is important. If you are getting syntax errors, that may be the reason. + +Finished workflow file: `.github/workflows/python-package.yml.example` + +
+ ### ⌨️ Activity: Add a workflow to show test coverage 1. Switch to the VS Code Codespace. @@ -145,3 +154,12 @@ You can explore all of the configuration options in the [GitHub Actions Docs](ht > Dis you notice the `uses:` statements? Those are pre-made steps from the free [Actions Marketplace](https://github.com/marketplace?type=actions). Consider trying one of them out before you create your own custom scripts (to maintain)! There are lots of AWESOME creations from the community! 1. With both new workflows pushed to GitHub, Mona will review your work and post the next steps. + +
+Having trouble? 🤷‍♂️ + +Indention of `.yml` files is important. If you are getting syntax errors, that may be the reason. + +Finished workflow file: `.github/workflows/python-coverage.yml.example` + +
From 45ea6eb3323a092cd867de443e832ad66053dc59 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 02:28:05 +0000 Subject: [PATCH 32/42] Cleanup --- .github/steps/3-step.md | 8 ++++---- .github/steps/4-step.md | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/steps/3-step.md b/.github/steps/3-step.md index 0590409..1b24abc 100644 --- a/.github/steps/3-step.md +++ b/.github/steps/3-step.md @@ -4,21 +4,21 @@ As specified in our workflows, they will only run when a pull request is targeti Pull requests have a nice advantage when a workflow is associated with them. The run status and results can be displayed directly in the conversation feed. -### ⌨️ Activity: Make a code change +### ⌨️ Activity: Start a PR and propose a code change 1. Return to the VS Code Codespace. -1. Create a new branch based on `main` with the following name and **publish** it to GitHub. +1. Create a new branch based from `main` with the following name and **publish** it to GitHub. ```txt reenable-unit-test ``` -1. Open the `tests/calculations_test.py` file. +1. Double check that you are on the `reenable-unit-test` branch, then open the `tests/calculations_test.py` file. 1. After investigating the code, we see a commented out test on line 56. Uncomment it to re-enable it. - > Hopefully they didn't disable it to get around testing! 😱 + > Hopefully it wasn't disable to get around testing! 😱 ```py def test_get_nth_fibonacci_ten(): diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md index ccccdb2..a610eb2 100644 --- a/.github/steps/4-step.md +++ b/.github/steps/4-step.md @@ -21,7 +21,9 @@ Let's fix this to avoid anyone (accidentally) bypassing verification. - **Require status checks to pass**: ☑️ Checked - `python-coverage` - > ❕ **Important:** To keep the lesson simple, we are only checking the coverage workflow. +
+ + > 🪧 **Note:** To keep the lesson simple, we are only checking the coverage workflow. Feel free to experiment though! target branch settings From 1d66ffe8d01ac486d21958acf3d757c15b44f68e Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 02:31:36 +0000 Subject: [PATCH 33/42] Make instructions clearer --- .github/steps/3-step.md | 15 +++++++++++++-- .github/steps/4-step.md | 16 ++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/steps/3-step.md b/.github/steps/3-step.md index 1b24abc..c558cc8 100644 --- a/.github/steps/3-step.md +++ b/.github/steps/3-step.md @@ -41,6 +41,17 @@ Pull requests have a nice advantage when a workflow is associated with them. The - **source:** `reenable-unit-test` - **title**: `Reenable unit test that was disabled` +
+
+ Having Trouble: Can't create a pull request? 🤷‍♂️ + + Did you accidentally commit to the `main` branch? Here is a command to undo the last commit and force update the repository on GitHub. Be careful not to go back too many steps though! You don't want to remove your new workflows! + + ```bash + git reset --hard HEAD~1 + git push -f + ``` + 1. After the pull request is created, look near the Merge button to see many workflows running. - Our coverage workflow will fail, letting us know we have a test to fix. @@ -48,8 +59,8 @@ Pull requests have a nice advantage when a workflow is associated with them. The 1. With the pull request started, Mona should be busy checking your work and preparing the next steps.
-Need help? +Having trouble? 🤷‍♂️ -- If the checks don't appear or updated, try refreshing the page. It's possible the workflow ran and the page just hasn't been updated with that change. +- If the checks don't appear updated, try refreshing the page. It's possible the workflow ran and the page just hasn't been updated with the change.
diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md index a610eb2..078f45d 100644 --- a/.github/steps/4-step.md +++ b/.github/steps/4-step.md @@ -37,6 +37,10 @@ Let's fix this to avoid anyone (accidentally) bypassing verification. failed tests and disabled merge button +### Activity: Fix the broken test + +Lets investigate why our testing workflow failed. Is it misconfigured or is some code bac? Maybe there was a reason that test was disabled?! + 1. Click on the `Python Coverage` workflow to view the logs. It will automatically navigate to the failed logs. 1. After some inspection, there are 2 issues preventing merging. @@ -44,8 +48,6 @@ Let's fix this to avoid anyone (accidentally) bypassing verification. - 1 test is failing. - Coverage is below the 90% requirement. -### Activity: Fix broken test - 1. Switch to the VS Code Codespace. 1. Open the `tests/calculations_test.py` file. @@ -69,13 +71,17 @@ Let's fix this to avoid anyone (accidentally) bypassing verification. assert result == 55 ``` -1. Commit the corrected test and wait for the workflows to run again. +1. Commit and push the corrected test code then wait for the workflows to run again. - This time the tests pass and we receive a detailed coverage report. ## Activity: Fix low test coverage -1. Let's ask GitHub Copilot to find missing test cases. +With our test corrected, we are now getting coverage results. +Unfortunately it is below the 90% requirement. +Let's add some more tests to increase coverage. + +1. Let's ask GitHub Copilot to find missing test cases. Alternately, you can expand the manual steps below. > ![Static Badge](https://img.shields.io/badge/-Prompt-text?style=social&logo=github%20copilot) > @@ -121,8 +127,6 @@ Let's fix this to avoid anyone (accidentally) bypassing verification. - The coverage comment will update to 100%. - The merge button will activate! -### Activity: Merge - 1. Click the **Merge** button. Congrats, you are all done! image From 889c24cd5b48fc7881158bcf186125e2b86dc77e Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 02:33:41 +0000 Subject: [PATCH 34/42] Clarify final steps --- .github/steps/4-step.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md index 078f45d..eb3f11a 100644 --- a/.github/steps/4-step.md +++ b/.github/steps/4-step.md @@ -127,6 +127,8 @@ Let's add some more tests to increase coverage. - The coverage comment will update to 100%. - The merge button will activate! -1. Click the **Merge** button. Congrats, you are all done! +1. Click the **Merge** button. image + +1. With full coverage, all tests passing, and the pull request merged, Mona will share a final review. Congrats, you are all done! From d36c9e5c6f3bf28f28da3bb4d998d0db70e9dc13 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 02:45:43 +0000 Subject: [PATCH 35/42] Add note about introduction to repository management --- .github/steps/4-step.md | 3 +++ .github/steps/x-review.md | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md index eb3f11a..b54fbe9 100644 --- a/.github/steps/4-step.md +++ b/.github/steps/4-step.md @@ -37,6 +37,9 @@ Let's fix this to avoid anyone (accidentally) bypassing verification. failed tests and disabled merge button +> [!TIP] +> Interested to learn more ways to prepare your project for collaboration? Check out the [Introduction to Repository Management](https://github.com/skills/introduction-to-repository-management) exercise next! + ### Activity: Fix the broken test Lets investigate why our testing workflow failed. Is it misconfigured or is some code bac? Maybe there was a reason that test was disabled?! diff --git a/.github/steps/x-review.md b/.github/steps/x-review.md index fa58f50..1974505 100644 --- a/.github/steps/x-review.md +++ b/.github/steps/x-review.md @@ -11,7 +11,8 @@ Here's a recap of all the tasks you've accomplished in your repository: ### What's next? -- Get more ideas of what you can do with [awesome actions](https://github.com/sdras/awesome-actions). - Find existing Actions in the the free [Actions Marketplace](https://github.com/marketplace?type=actions). +- Find more automation ideas from the [awesome actions](https://github.com/sdras/awesome-actions) repo. +- Learn more about collaboration with the [Introduction to Repository Management](https://github.com/skills/introduction-to-repository-management) exercise. - Take another [GitHub Skills exercise](https://learn.github.com/skills). - To find projects to contribute to, check out [GitHub Explore](https://github.com/explore). From a45b91c8509702753f26ce6bf62a4aeebc904f1a Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 12:53:28 +0000 Subject: [PATCH 36/42] fix typo that causes activity to be hidden --- .github/steps/2-step.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md index 8fee570..4c89090 100644 --- a/.github/steps/2-step.md +++ b/.github/steps/2-step.md @@ -61,7 +61,7 @@ Indention of `.yml` files is important. If you are getting syntax errors, that m Finished workflow file: `.github/workflows/python-package.yml.example` -
+
### ⌨️ Activity: Add a workflow to show test coverage From 761e197a8277b9540d8d8daa038bfb42b8332134 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 12:56:16 +0000 Subject: [PATCH 37/42] fix typo that breaks details box --- .github/steps/2-step.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md index 4c89090..1d5a817 100644 --- a/.github/steps/2-step.md +++ b/.github/steps/2-step.md @@ -150,11 +150,11 @@ Finished workflow file: `.github/workflows/python-package.yml.example` 1. Commit and push the changes to your `python-coverage.yml` file to the `main` branch. -> [!TIP] -> Dis you notice the `uses:` statements? Those are pre-made steps from the free [Actions Marketplace](https://github.com/marketplace?type=actions). Consider trying one of them out before you create your own custom scripts (to maintain)! There are lots of AWESOME creations from the community! - 1. With both new workflows pushed to GitHub, Mona will review your work and post the next steps. +> [!TIP] +> Did you notice the many `uses:` statements? Those are pre-made steps from the free [Actions Marketplace](https://github.com/marketplace?type=actions). Consider trying one of them out before you create your own custom scripts (to maintain)! There are lots of awesome creations from the community! +
Having trouble? 🤷‍♂️ @@ -162,4 +162,4 @@ Indention of `.yml` files is important. If you are getting syntax errors, that m Finished workflow file: `.github/workflows/python-coverage.yml.example` -
+
From f4290a373b885810a84e0e521d1c8df09db75741 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 12:58:26 +0000 Subject: [PATCH 38/42] fix file name trigger in step 2 --- .github/workflows/2-step.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/2-step.yml b/.github/workflows/2-step.yml index 743fc43..c22a123 100644 --- a/.github/workflows/2-step.yml +++ b/.github/workflows/2-step.yml @@ -5,7 +5,7 @@ on: branches: - main paths: - - .github/workflows/python-tests.yml + - .github/workflows/python-package.yml - .github/workflows/python-coverage.yml permissions: From b39dbb618d4c328cab1020e8a2eeebcf78d81b17 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 13:10:20 +0000 Subject: [PATCH 39/42] fix incorrect step name --- .github/workflows/2-step.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/2-step.yml b/.github/workflows/2-step.yml index c22a123..ec76fdb 100644 --- a/.github/workflows/2-step.yml +++ b/.github/workflows/2-step.yml @@ -57,8 +57,8 @@ jobs: file: exercise-toolkit/markdown-templates/step-feedback/checking-work.md edit-mode: replace - - name: Check for python tests workflow - id: check-python-tests-workflow + - name: Check for python package workflow + id: check-python-package-workflow continue-on-error: true uses: skills/action-keyphrase-checker@v1 with: @@ -88,8 +88,8 @@ jobs: vars: | step_number: 3 results_table: - - description: "Python tests workflow created" - passed: ${{ steps.check-python-tests-workflow.outcome == 'success' }} + - description: "Python package workflow created" + passed: ${{ steps.check-python-package-workflow.outcome == 'success' }} - description: "Python coverage workflow created" passed: ${{ steps.check-python-coverage-workflow.outcome == 'success' }} From 652a565b65ad8e497e0b5212041c023fd90b6b58 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Mon, 14 Jul 2025 13:11:27 +0000 Subject: [PATCH 40/42] fix step number --- .github/workflows/2-step.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/2-step.yml b/.github/workflows/2-step.yml index ec76fdb..841cb32 100644 --- a/.github/workflows/2-step.yml +++ b/.github/workflows/2-step.yml @@ -86,7 +86,7 @@ jobs: edit-mode: replace file: exercise-toolkit/markdown-templates/step-feedback/step-results-table.md vars: | - step_number: 3 + step_number: 2 results_table: - description: "Python package workflow created" passed: ${{ steps.check-python-package-workflow.outcome == 'success' }} From 95c6e86269543254e9b1a244d0760c248b2c9eb5 Mon Sep 17 00:00:00 2001 From: "Christopher W. Blake" Date: Thu, 17 Jul 2025 13:37:33 +0000 Subject: [PATCH 41/42] Adjust installation to use a virtual environment --- .github/steps/1-step.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/steps/1-step.md b/.github/steps/1-step.md index 9eb4af3..ee42003 100644 --- a/.github/steps/1-step.md +++ b/.github/steps/1-step.md @@ -33,9 +33,11 @@ Let's learn how to automate this ever-growing need for testing our code by using > 💡 **Tip**: The keyboard shortcut is `CTRL` + `J`. -1. Run the below command to install the required Python libraries and tools to show code coverage. +1. Run the below command to create a virtual environment, then install the required Python libraries and tools to show code coverage. ```bash + python -m venv .venv/calculations + source .venv/calculations/bin/activate pip install -r requirements.txt pip install pytest coverage pytest-cov ``` From b7cd0da137031fe66b04923e8b84941792d00f1d Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 12 Sep 2025 01:05:14 +1000 Subject: [PATCH 42/42] fix: incorrect assert style in testing example (#1) * include pytest module in test * update docs to use pytest to raise error * fix example in docs --- .github/steps/4-step.md | 8 ++++---- tests/calculations_test.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md index b54fbe9..0e09caf 100644 --- a/.github/steps/4-step.md +++ b/.github/steps/4-step.md @@ -100,24 +100,24 @@ Let's add some more tests to increase coverage. 1. Add the following 2 entries. ```py - def test_area_of_circle_negative_radius(self): + def test_area_of_circle_negative_radius(): """Test with a negative radius to raise ValueError.""" # Arrange radius = -1 # Act & Assert - with self.assertRaises(ValueError): + with pytest.raises(ValueError): area_of_circle(radius) ``` ```py - def test_get_nth_fibonacci_negative(self): + def test_get_nth_fibonacci_negative(): """Test with a negative number to raise ValueError.""" # Arrange n = -1 # Act & Assert - with self.assertRaises(ValueError): + with pytest.raises(ValueError): get_nth_fibonacci(n) ``` diff --git a/tests/calculations_test.py b/tests/calculations_test.py index 2b49d57..7e08b56 100644 --- a/tests/calculations_test.py +++ b/tests/calculations_test.py @@ -3,7 +3,7 @@ import os # Installed Modules -# None +import pytest # Project Modules sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src')))