├── .github
├── steps
│ ├── 1-step.md
│ ├── 2-step.md
│ ├── 3-step.md
│ └── x-review.md
└── workflows
│ ├── 0-start-exercise.yml
│ ├── 1-step.yml
│ └── 2-step.yml
├── .gitignore
├── LICENSE
└── README.md
/.github/steps/1-step.md:
--------------------------------------------------------------------------------
1 | ## Step 1: (replace-me: STEP-NAME)
2 |
3 | (replace-me: OPTIONAL Brief story or scenario to introduce the step)
4 |
5 | ### 📖 Theory: (replace-me: Theory title)
6 |
7 |
8 |
12 |
13 | (replace-me: Optional theory or background information relevant to this step)
14 |
15 | ### ⌨️ Activity: (replace-me: Activity title)
16 |
17 | 1. (replace-me: First instruction)
18 | 1. (replace-me: Second instruction)
19 | 1. (replace-me: Additional instructions as needed)
20 |
21 |
22 | Having trouble? 🤷
23 |
24 | - (replace-me: Troubleshooting tip or hint)
25 | - (replace-me: Additional troubleshooting tips as needed)
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.github/steps/2-step.md:
--------------------------------------------------------------------------------
1 | ## Step 2: (replace-me: STEP-NAME)
2 |
3 | (replace-me: OPTIONAL Brief story or scenario to introduce the step)
4 |
5 | ### 📖 Theory: (replace-me: Theory title)
6 |
7 |
8 |
12 |
13 | (replace-me: Optional theory or background information relevant to this step)
14 |
15 | ### ⌨️ Activity: (replace-me: Activity title)
16 |
17 | 1. (replace-me: First instruction)
18 | 1. (replace-me: Second instruction)
19 | 1. (replace-me: Additional instructions as needed)
20 |
21 |
22 | Having trouble? 🤷
23 |
24 | - (replace-me: Troubleshooting tip or hint)
25 | - (replace-me: Additional troubleshooting tips as needed)
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.github/steps/3-step.md:
--------------------------------------------------------------------------------
1 | ## Step 3: (replace-me: STEP-NAME)
2 |
3 | (replace-me: OPTIONAL Brief story or scenario to introduce the step)
4 |
5 | ### 📖 Theory: (replace-me: Theory title)
6 |
7 |
8 |
12 |
13 | (replace-me: Optional theory or background information relevant to this step)
14 |
15 | ### ⌨️ Activity: (replace-me: Activity title)
16 |
17 | 1. (replace-me: First instruction)
18 | 1. (replace-me: Second instruction)
19 | 1. (replace-me: Additional instructions as needed)
20 |
21 |
22 | Having trouble? 🤷
23 |
24 | - (replace-me: Troubleshooting tip or hint)
25 | - (replace-me: Additional troubleshooting tips as needed)
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.github/steps/x-review.md:
--------------------------------------------------------------------------------
1 | ## Review
2 |
3 | _Congratulations, you've completed this exercise and learned a lot about (replace-me: feature/product that was taught in this exercise)
4 |
5 |
6 |
7 | Here's a recap of your accomplishments:
8 |
9 | - (replace-me: Accomplishment #1)
10 | - (replace-me: Accomplishment #N)
11 |
12 | ### What's next?
13 |
14 | - (replace-me: Natural follow up Skills exercise - if there is one)
15 | - (replace-me: Documentation link to learn more about the feature)
16 | - (replace-me: Other resources or calls to action)
17 |
--------------------------------------------------------------------------------
/.github/workflows/0-start-exercise.yml:
--------------------------------------------------------------------------------
1 | name: Step 0 # Start Exercise
2 |
3 | on:
4 | workflow_dispatch:
5 | # NOTE: Make sure the repository is a template before enabling this trigger.
6 | # push:
7 | # branches:
8 | # - main
9 |
10 | permissions:
11 | contents: write
12 | actions: write
13 | issues: write
14 |
15 | env:
16 | STEP_1_FILE: ".github/steps/1-step.md"
17 |
18 | jobs:
19 | start_exercise:
20 | if: |
21 | !github.event.repository.is_template
22 | name: Start Exercise
23 | uses: skills/exercise-toolkit/.github/workflows/start-exercise.yml@v0.5.0
24 | with:
25 | exercise-title: "(replace-me: Exercise title)"
26 | intro-message: "(replace-me: Brief one line introduction message for the exercise)"
27 |
28 | post_next_step_content:
29 | name: Post next step content
30 | runs-on: ubuntu-latest
31 | needs: [start_exercise]
32 | env:
33 | ISSUE_URL: ${{ needs.start_exercise.outputs.issue-url }}
34 |
35 | steps:
36 | - name: Checkout
37 | uses: actions/checkout@v4
38 |
39 | - name: Get response templates
40 | uses: actions/checkout@v4
41 | with:
42 | repository: skills/exercise-toolkit
43 | path: exercise-toolkit
44 | ref: v0.5.0
45 |
46 | - name: Build comment - add step content
47 | id: build-comment
48 | uses: skills/action-text-variables@v2
49 | with:
50 | template-file: ${{ env.STEP_1_FILE }}
51 | template-vars: |
52 | login: ${{ github.actor }}
53 | full_repo_name: ${{ github.repository }}
54 |
55 | - name: Create comment - add step content
56 | run: |
57 | gh issue comment "$ISSUE_URL" \
58 | --body "$ISSUE_BODY"
59 | env:
60 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61 | ISSUE_BODY: ${{ steps.build-comment.outputs.updated-text }}
62 |
63 | - name: Create comment - watching for progress
64 | run: |
65 | gh issue comment "$ISSUE_URL" \
66 | --body-file "exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md"
67 | env:
68 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69 |
70 | - name: Enable next step workflow
71 | run: |
72 | gh workflow disable "${{github.workflow}}"
73 | env:
74 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
75 |
--------------------------------------------------------------------------------
/.github/workflows/1-step.yml:
--------------------------------------------------------------------------------
1 | name: Step 1
2 |
3 |
4 |
5 | on:
6 | workflow_dispatch:
7 | # Common event triggers: pull_request, push, issues, issue_comment (feel free to experiment)
8 | # Docs: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
9 | # Example:
10 | # pull_request:
11 | # branches:
12 | # - main
13 |
14 | permissions:
15 | contents: read
16 | actions: write
17 | issues: write
18 |
19 | env:
20 | STEP_2_FILE: ".github/steps/2-step.md"
21 |
22 | jobs:
23 | find_exercise:
24 | name: Find Exercise Issue
25 | uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.5.0
26 |
27 | post_next_step_content:
28 | name: Post next step content
29 | needs: [find_exercise]
30 | runs-on: ubuntu-latest
31 | env:
32 | ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }}
33 |
34 | steps:
35 | - name: Checkout
36 | uses: actions/checkout@v4
37 |
38 | - name: Get response templates
39 | uses: actions/checkout@v4
40 | with:
41 | repository: skills/exercise-toolkit
42 | path: exercise-toolkit
43 | ref: v0.5.0
44 |
45 | - name: Create comment - add step content
46 | run: |
47 | gh issue comment "$ISSUE_URL" \
48 | --body-file "$STEP_2_FILE"
49 | env:
50 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 |
52 | - name: Create comment - watching for progress
53 | run: |
54 | gh issue comment "$ISSUE_URL" \
55 | --body-file exercise-toolkit/markdown-templates/step-feedback/watching-for-progress.md
56 | env:
57 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58 |
59 | - name: Disable current workflow and enable next one
60 | run: |
61 | gh workflow disable "${{github.workflow}}"
62 | gh workflow enable "Step 2"
63 | env:
64 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
65 |
--------------------------------------------------------------------------------
/.github/workflows/2-step.yml:
--------------------------------------------------------------------------------
1 | name: Step 2
2 |
3 |
4 | on:
5 | workflow_dispatch:
6 | # Common event triggers: pull_request, push, issues, issue_comment (feel free to experiment)
7 | # Docs: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
8 | # Example:
9 | # pull_request:
10 | # branches:
11 | # - main
12 | # types:
13 | # - closed
14 |
15 | permissions:
16 | contents: write
17 | actions: write
18 | issues: write
19 |
20 | env:
21 | REVIEW_FILE: ".github/steps/x-review.md"
22 |
23 | jobs:
24 | find_exercise:
25 | name: Find Exercise Issue
26 | uses: skills/exercise-toolkit/.github/workflows/find-exercise-issue.yml@v0.5.0
27 |
28 | # This job is optional. We often call it a "grading job". If the step is not graded, remove it.
29 | check_step_work:
30 | name: Check step work
31 | runs-on: ubuntu-latest
32 | needs: [find_exercise]
33 | env:
34 | ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }}
35 |
36 | steps:
37 | - name: Checkout
38 | uses: actions/checkout@v4
39 |
40 | - name: Get response templates
41 | uses: actions/checkout@v4
42 | with:
43 | repository: skills/exercise-toolkit
44 | path: exercise-toolkit
45 | ref: v0.5.0
46 |
47 | - name: Update comment - checking work
48 | run: |
49 | gh issue comment "$ISSUE_URL" \
50 | --body-file exercise-toolkit/markdown-templates/step-feedback/checking-work.md \
51 | --edit-last
52 | env:
53 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54 |
55 | # replace-me: Example of a grading check. You can add more checks as needed.
56 | - name: Check if README file exists
57 | id: check-file-exists
58 | continue-on-error: true
59 | uses: skills/exercise-toolkit/actions/file-exists@v0.5.0
60 | with:
61 | file: README.md
62 |
63 | # replace-me: Example of a grading check. You can add more checks as needed.
64 | - name: Check for keyphrase in README.md
65 | id: check-for-keyphrase
66 | continue-on-error: true
67 | uses: skills/action-keyphrase-checker@v1
68 | with:
69 | text-file: README.md
70 | keyphrase: Installation guide
71 |
72 | - name: Build message - step results
73 | id: build-message-step-results
74 | uses: skills/action-text-variables@v2
75 | with:
76 | template-file: exercise-toolkit/markdown-templates/step-feedback/step-results-table.md
77 | template-vars: |
78 | step_number: 1
79 | passed: ${{ !contains(steps.*.outcome, 'failure') }}
80 | results_table:
81 | - description: "Checked if README.md file exists"
82 | passed: ${{ steps.check-file-exists.outcome == 'success' }}
83 | - description: "Checked for Installation guide in README.md"
84 | passed: ${{ steps.check-for-keyphrase.outcome == 'success' }}
85 |
86 |
87 | - name: Create comment - step results
88 | run: |
89 | gh issue comment "$ISSUE_URL" \
90 | --body "$COMMENT_BODY" \
91 | --edit-last
92 | env:
93 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
94 | COMMENT_BODY: ${{ steps.build-message-step-results.outputs.updated-text }}
95 |
96 | - name: Fail job if not all checks passed
97 | if: contains(steps.*.outcome, 'failure')
98 | run: exit 1
99 |
100 | - name: Build message - step finished
101 | id: build-message-step-finish
102 | uses: skills/action-text-variables@v2
103 | with:
104 | template-file: exercise-toolkit/markdown-templates/step-feedback/step-finished-prepare-next-step.md
105 | template-vars: |
106 | next_step_number: 2
107 |
108 | - name: Update comment - step finished
109 | run: |
110 | gh issue comment "$ISSUE_URL" \
111 | --body "$ISSUE_BODY"
112 | env:
113 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
114 | ISSUE_BODY: ${{ steps.build-message-step-finish.outputs.updated-text }}
115 |
116 | post_review_content:
117 | name: Post review content
118 | needs: [find_exercise, check_step_work]
119 | runs-on: ubuntu-latest
120 | env:
121 | ISSUE_URL: ${{ needs.find_exercise.outputs.issue-url }}
122 |
123 | steps:
124 | - name: Checkout
125 | uses: actions/checkout@v4
126 |
127 | - name: Create comment - add step content
128 | run: |
129 | gh issue comment "$ISSUE_URL" \
130 | --body-file "$REVIEW_FILE"
131 | env:
132 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
133 |
134 | # The last workflow in the chain of steps should also finish the exercise.
135 | finish_exercise:
136 | name: Finish Exercise
137 | needs: [find_exercise, post_review_content]
138 | uses: skills/exercise-toolkit/.github/workflows/finish-exercise.yml@v0.5.0
139 | with:
140 | issue-url: ${{ needs.find_exercise.outputs.issue-url }}
141 |
142 | disable_workflow:
143 | name: Disable this workflow
144 | needs: [find_exercise, post_review_content]
145 | runs-on: ubuntu-latest
146 |
147 | steps:
148 | - name: Checkout
149 | uses: actions/checkout@v4
150 |
151 | - name: Disable current workflow
152 | run: gh workflow disable "${{github.workflow}}"
153 | env:
154 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
155 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.com
4 | *.class
5 | *.dll
6 | *.exe
7 | *.o
8 | *.so
9 |
10 | # Packages #
11 | ############
12 | # it's better to unpack these files and commit the raw source
13 | # git has its own built in compression methods
14 | *.7z
15 | *.dmg
16 | *.gz
17 | *.iso
18 | *.jar
19 | *.rar
20 | *.tar
21 | *.zip
22 |
23 | # Logs and databases #
24 | ######################
25 | *.log
26 | *.sql
27 | *.sqlite
28 |
29 | # OS generated files #
30 | ######################
31 | .DS_Store
32 | .DS_Store?
33 | ._*
34 | .Spotlight-V100
35 | .Trashes
36 | ehthumbs.db
37 | Thumbs.db
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) GitHub, Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # (replace-me: Exercise title)
2 |
3 | _(replace-me: One-line description of the exercise)_
4 |
5 | ## Welcome
6 |
7 | - **Who is this for**: (replace-me: Target audience description)
8 | - **What you'll learn**: (replace-me: Learning objectives)
9 | - **What you'll build**: (replace-me: Description of what the learner will create)
10 | - **Prerequisites**:
11 | - (replace-me: Prerequisite skill/exercise)
12 | - (replace-me: Other prerequisites)
13 |
14 | - **How long**: This exercise takes less than (replace-me: estimated time) to complete.
15 |
16 | In this exercise, you will:
17 |
18 | 1. (replace-me: Learning objective step #1)
19 | 1. (replace-me: Learning objective step #2)
20 | 1. (replace-me: Learning objective step #N)
21 |
22 |
23 | ### How to start this exercise
24 |
25 | 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**.
26 |
27 |
28 | [](https://github.com/new?template_owner=skills&template_name=exercise-template&owner=%40me&name=skills-&description=Exercise:+Replace+me&visibility=public)
29 |
30 |
31 | Having trouble? 🤷
32 |
33 | When copying the exercise, we recommend the following settings:
34 |
35 | - For owner, choose your personal account or an organization to host the repository.
36 |
37 | - We recommend creating a public repository, since private repositories will use Actions minutes.
38 |
39 | If the exercise isn't ready in 20 seconds, please check the [Actions](../../actions) tab.
40 |
41 | - Check to see if a job is running. Sometimes it simply takes a bit longer.
42 |
43 | - If the page shows a failed job, please submit an issue. Nice, you found a bug! 🐛
44 |
45 |
46 |
47 | ---
48 |
49 | © 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)
50 |
--------------------------------------------------------------------------------