├── .github ├── dependabot.yml ├── steps │ ├── -step.txt │ ├── 0-welcome.md │ ├── 1-initialize-javascript-project.md │ ├── 2-configure-your-action.md │ ├── 3-create-metadata-file.md │ ├── 4-create-javascript-files-for-action.md │ ├── 5-add-action-to-workflow-file.md │ ├── 6-trigger-action.md │ └── X-finish.md └── workflows │ ├── 0-welcome.yml │ ├── 1-initialize-javascript-project.yml │ ├── 2-configure-your-action.yml │ ├── 3-create-metadata-file.yml │ ├── 4-create-javascript-files-for-action.yml │ ├── 5-add-action-to-workflow-file.yml │ ├── 6-trigger-action.yml │ └── my-workflow.yml ├── .gitignore ├── LICENSE └── README.md /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | -------------------------------------------------------------------------------- /.github/steps/-step.txt: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /.github/steps/0-welcome.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/steps/1-initialize-javascript-project.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ## Step 1: Initialize a new JavaScript project 10 | 11 | _Welcome to the course :tada:_ 12 | 13 | ### Configuring a workflow 14 | 15 | Actions are enabled on your repository by default, but we still have to tell our repository to use them. We do this by creating a workflow file in our repository. 16 | 17 | A **workflow** file can be thought of as the recipe for automating a task. They house the start to finish instructions, in the form of `jobs` and `steps`, for what should happen based on specific triggers. 18 | 19 | Your repository can contain multiple **workflow** files that carry out a wide variety of tasks. It is important to consider this when deciding on a name for your **workflow**. The name you choose should reflect the tasks being performed. 20 | 21 | _In our case, we will use this one **workflow** file for many things, which leads us to break this convention for teaching purposes._ 22 | 23 | Read more about [workflows](https://docs.github.com/en/actions/writing-workflows/about-workflows) 24 | 25 | ## On to your development environment 26 | 27 | Our JavaScript actions are going to leverage the [GitHub ToolKit](https://github.com/actions/toolkit) for developing GitHub Actions. 28 | 29 | This is an external library that we will install using `npm` which means that you will need [Node.js](https://nodejs.org/) installed. 30 | 31 | We find writing actions to be easier from a local environment vs trying to do everything right here in the repository. Doing these steps locally allows you to use the editor of your choice so that you have all the extensions and snippets you are used to when writing code. 32 | 33 | If you do not have a preferred environment then we suggest following along exactly as you see on the screen, which means you'll need to install [Visual Studio Code](https://code.visualstudio.com/). 34 | 35 | ## Don't forget to set up your workstation 36 | 37 | Most of your work going forward will take place away from your Skills repository, so before continuing with the course ensure you have the following installed on your **local machine**. 38 | 39 | 1. [ ] [Node.js](https://nodejs.org) 40 | 2. [ ] [Visual Studio Code](https://code.visualstudio.com/) or your editor of choice 41 | 3. [ ] [Git](https://git-scm.com/) 42 | 43 | ### :keyboard: Activity 1: Initialize a new JavaScript project 44 | 45 | Once you have the necessary tools installed locally, follow these steps to begin creating your first action. 46 | 47 | 1. Open the **Terminal** (Mac and Linux) or **Command Prompt** (Windows) on your local machine 48 | 2. Clone your Skills repo to your local machine: 49 | ```shell 50 | git clone .git 51 | ``` 52 | 3. Navigate to the folder you just cloned: 53 | ```shell 54 | cd 55 | ``` 56 | 4. We are using branch called `main`. 57 | ```shell 58 | git switch main 59 | ``` 60 | 5. Create a new folder for our actions files: 61 | ```shell 62 | mkdir -p .github/actions/joke-action 63 | ``` 64 | 6. Navigate to the `joke-action` folder you just created: 65 | ```shell 66 | cd .github/actions/joke-action 67 | ``` 68 | 7. Initialize a new project: 69 | ```shell 70 | npm init -y 71 | ``` 72 | 8. Install the **request**, **request-promise** and **@actions/core** dependencies using `npm` from the [GitHub ToolKit](https://github.com/actions/toolkit): 73 | ```shell 74 | npm install --save request request-promise @actions/core 75 | ``` 76 | 9. Commit those newly added files,we will remove the need to upload **node_modules** in a later step: 77 | ```shell 78 | git add . 79 | git commit -m 'add project dependencies' 80 | ``` 81 | 10. Push your changes to your repository: 82 | ```shell 83 | git push 84 | ``` 85 | 11. Wait about 20 seconds then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/en/actions) will automatically update to the next step. 86 | -------------------------------------------------------------------------------- /.github/steps/2-configure-your-action.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Step 2: Configure Your Action 8 | 9 | _Let's keep going! :bike:_ 10 | 11 | ### Excellent! 12 | 13 | Now that we have the custom action pre-requisites, let us create **joke-action** action. 14 | 15 | ### :keyboard: Activity 1: Configure Your Action 16 | 17 | All of the following steps take place inside of the `.github/actions/joke-action` directory. 18 | 19 | We will start with using the parameters that are **required** and later implement some optional parameters as our action evolves. 20 | 21 | 1. Create a new file in: `.github/actions/joke-action/action.yml` 22 | 2. Add the following contents to the `.github/actions/joke-action/action.yml` file: 23 | 24 | ```yaml 25 | name: "my joke action" 26 | 27 | description: "use an external API to retrieve and display a joke" 28 | 29 | runs: 30 | using: "node16" 31 | main: "main.js" 32 | ``` 33 | 34 | 3. Save the `action.yml` file 35 | 4. Commit the changes and push them to the `main` branch: 36 | ```shell 37 | git add action.yml 38 | git commit -m 'create action.yml' 39 | git pull 40 | git push 41 | ``` 42 | 5. Wait about 20 seconds then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/en/actions) will automatically update to the next step. 43 | -------------------------------------------------------------------------------- /.github/steps/3-create-metadata-file.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Step 3: Create the metadata file 8 | 9 | _Nice work configuring your action :smile:_ 10 | 11 | ## Action metadata 12 | 13 | Every GitHub Action that we write needs to be accompanied by a metadata file. This file has a few rules to it, as are indicated below: 14 | 15 | - Filename **must** be `action.yml`. 16 | - Required for both Docker container and JavaScript actions. 17 | - Written in YAML syntax. 18 | 19 | This file defines the following information about your action: 20 | 21 | | Parameter | Description | Required | 22 | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------: | 23 | | Name | The name of your action. Helps visually identify the actions in a job. | :white_check_mark: | 24 | | Description | A summary of what your action does. | :white_check_mark: | 25 | | Inputs | Input parameters allow you to specify data that the action expects to use during runtime. These parameters become environment variables in the runner. | :x: | 26 | | Outputs | Specifies the data that subsequent actions can use later in the workflow after the action that defines these outputs has run. | :x: | 27 | | Runs | The command to run when the action executes. | :white_check_mark: | 28 | | Branding | You can use a color and Feather icon to create a badge to personalize and distinguish your action in GitHub Marketplace. | :x: | 29 | 30 | --- 31 | 32 | Read more about [Action metadata](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/metadata-syntax-for-github-actions) 33 | 34 | ### :keyboard: Activity 1: Create the metadata file 35 | 36 | All of the following steps take place inside of the `.github/actions/joke-action` directory. 37 | 38 | Our action does not require much metadata for it to run correctly. We will not be accepting any inputs, we will however be setting a single output this time. 39 | 40 | 1. Update the action metadata file `.github/actions/joke-action/action.yml` with the following content: 41 | 42 | ```yaml 43 | name: "my joke action" 44 | 45 | description: "use an external API to retrieve and display a joke" 46 | 47 | outputs: 48 | joke-output: 49 | description: The resulting joke from the icanhazdadjokes API 50 | 51 | runs: 52 | using: "node16" 53 | main: "main.js" 54 | ``` 55 | 56 | 2. Save the `action.yml` file 57 | 3. Commit the changes and push them to GitHub: 58 | ```shell 59 | git add action.yml 60 | git pull 61 | git commit -m 'add metadata for the joke action' 62 | git push 63 | ``` 64 | 4. Wait about 20 seconds then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/en/actions) will automatically update to the next step. 65 | -------------------------------------------------------------------------------- /.github/steps/4-create-javascript-files-for-action.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Step 4: Create the JavaScript files for your action 8 | 9 | _Good job adding the metadata file! :dancer:_ 10 | 11 | ## Files 12 | 13 | As you probably know, in JavaScript and other programming languages it is common to break your code into modules so that it is easier to read and maintain going forward. Since JavaScript actions are just programs written in JavaScript that run based on a specific trigger we are able to make our action code modular as well. 14 | 15 | To do so we will create two files. One of them will contain the logic to reach out to an external API and retrieve a joke for us, the other will call that module and print the joke to the actions console for us. We will be extending this functionality in our third and final action. 16 | 17 | ### Fetching a joke 18 | 19 | **Joke API** 20 | 21 | The first file will be `joke.js` and it will fetch our joke for us. We will be using the [icanhazdadjoke API](https://icanhazdadjoke.com/api) for our action. This API does not require any authentication, but it does however that we set a few parameters in the [HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers). We need to point out what those are when we get to the code, however it is outside of the scope of this course to cover HTTP in any depth. 22 | 23 | When we make our request to this API we will get back a JSON Object in the response. That Object looks like this: 24 | 25 | ``` 26 | { 27 | id: '0LuXvkq4Muc', 28 | joke: "I knew I shouldn't steal a mixer from work, but it was a whisk I was willing to take.", 29 | status: 200 30 | } 31 | ``` 32 | 33 | It contains 3 key/value pairs of data that we can use in our own program or service. In our case, we are only interested in the `joke` field. 34 | 35 | **Joke Module** 36 | 37 | We will create a file named `joke.js` and it will reside in the `.github/actions/joke-action` directory. 38 | 39 | The joke module will look like this: 40 | 41 | ```javascript 42 | const request = require("request-promise"); 43 | 44 | const options = { 45 | method: "GET", 46 | uri: "https://icanhazdadjoke.com/", 47 | headers: { 48 | Accept: "application/json", 49 | "User-Agent": "Writing JavaScript action GitHub Skills course.", 50 | }, 51 | json: true, 52 | }; 53 | 54 | async function getJoke() { 55 | const res = await request(options); 56 | return res.joke; 57 | } 58 | 59 | module.exports = getJoke; 60 | ``` 61 | 62 | Need an advanced description of the joke.js source code? 63 | 64 | We first bring in the `request-promise` library that we installed earlier using `npm`. 65 | 66 | Next we define a set of `options` that the `request-promise` library will use when it makes the request. 67 | 68 | Read more about [request-promise](https://github.com/request/request-promise/) 69 | 70 | Inside of the `options` block we add a key named `headers`. This defines the HTTP headers that the **icanhazdadjoke** API expects in each request that comes it's way. 71 | 72 | **icanhazdadjoke** cares the most about the keys, **Accept** and **User-Agent**, so we need to make sure we fill them in. 73 | 74 | Next we define an **asynchronous JavaScript function** to make the request for us, storing the JSON Object that is returned in a variable named `res`. 75 | 76 | Lastly, we `return` the `res.joke` which is only the value associated with the `joke` key of the JSON Object. This value will be random every time our action runs because of how we are interacting with the **icanhazdadjoke** API. 77 | 78 | This file finishes up by exporting the newly created function so that we can use it in our `main.js` file. 79 | 80 | ### Creating the main entry point for your action 81 | 82 | **Main Module** 83 | 84 | We will also create a file named `main.js` that resides inside of the `.github/actions/joke-action` directory. 85 | 86 | That file will look like this: 87 | 88 | ```javascript 89 | const getJoke = require("./joke"); 90 | const core = require("@actions/core"); 91 | 92 | async function run() { 93 | const joke = await getJoke(); 94 | console.log(joke); 95 | core.setOutput("joke-output", joke); 96 | } 97 | 98 | run(); 99 | ``` 100 | 101 | Need an advanced description of the main.js source code? 102 | 103 | Like we did in the `joke.js` file, we are first going to bring in our dependencies. Only this time, our dependencies include something we wrote! To do that we simply use `require()` to point to the location of the file we wish to bring in. 104 | 105 | We also bring in `@actions/core` so that we can set the output of our action. 106 | 107 | Next we write another **asynchronous JavaScript function** that stores the return value of `getJoke()` in a variable called **joke**. 108 | 109 | Then we log the joke to the console. 110 | 111 | Finally we finish the function with by setting the contents of the joke as the value of the `joke-output` output parameter. We will use this output later in the course. 112 | _Don't forget to call the `run()` function._ 113 | 114 | ### :keyboard: Activity 1: Creating the JavaScript files for your new action. 115 | 116 | 1. Create and add the following contents to the `.github/actions/joke-action/joke.js` file: 117 | 118 | ```javascript 119 | const request = require("request-promise"); 120 | 121 | const options = { 122 | method: "GET", 123 | uri: "https://icanhazdadjoke.com/", 124 | headers: { 125 | Accept: "application/json", 126 | "User-Agent": "Writing JavaScript action GitHub Skills course.", 127 | }, 128 | json: true, 129 | }; 130 | 131 | async function getJoke() { 132 | const res = await request(options); 133 | return res.joke; 134 | } 135 | 136 | module.exports = getJoke; 137 | ``` 138 | 139 | 2. Save the `joke.js` file. 140 | 3. Create and add the following contents to the `.github/actions/joke-action/main.js` file: 141 | 142 | ```javascript 143 | const getJoke = require("./joke"); 144 | const core = require("@actions/core"); 145 | 146 | async function run() { 147 | const joke = await getJoke(); 148 | console.log(joke); 149 | core.setOutput("joke-output", joke); 150 | } 151 | 152 | run(); 153 | ``` 154 | 155 | 4. Save the `main.js` file. 156 | 5. Commit the changes to this branch and push them to GitHub: 157 | ```shell 158 | git add joke.js main.js 159 | git commit -m 'creating joke.js and main.js' 160 | git pull 161 | git push 162 | ``` 163 | 6. Wait about 20 seconds then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/en/actions) will automatically update to the next step. 164 | -------------------------------------------------------------------------------- /.github/steps/5-add-action-to-workflow-file.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Step 5: Add your action to the workflow file 8 | 9 | _Great job! :tada:_ 10 | 11 | All of the following steps will add the action to the workflow file that’s already in the repo [`my-workflow.yml` file](/.github/workflows/my-workflow.yml) 12 | 13 | ### :keyboard: Activity 1: Edit the custom action at the bottom of the workflow file. 14 | 15 | ```yaml 16 | - name: ha-ha 17 | uses: ./.github/actions/joke-action 18 | ``` 19 | 20 | Here is what the full file should look like (we’re using issues instead of the pull request event and removing the reference to the hello world action.) 21 | 22 | ```yaml 23 | name: JS Actions 24 | 25 | on: 26 | issues: 27 | types: [labeled] 28 | 29 | jobs: 30 | action: 31 | if: ${{ !github.event.repository.is_template }} 32 | runs-on: ubuntu-latest 33 | 34 | steps: 35 | - uses: actions/checkout@v4 36 | - name: ha-ha 37 | uses: ./.github/actions/joke-action 38 | ``` 39 | 40 | You can make these changes in your repository by opening [`my-workflow.yml`](/.github/workflows/my-workflow.yml) in another browser tab and [editing the file directly](https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files). Make sure to select the `Commit directly to the main branch` option. 41 | 42 | Wait about 20 seconds then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/en/actions) will automatically update to the next step. 43 | -------------------------------------------------------------------------------- /.github/steps/6-trigger-action.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Step 6: Trigger the joke action 8 | 9 | _Great job! :heart:_ 10 | 11 | Everything is all set up and now we are ready to start laughing. You will find you have some joke related labels available to you in this repository. You don't have to use them, any label will trigger our workflow, but the easiest way to follow along would be to use suggested labels. 12 | 13 | ### Trigger a joke 14 | 15 | 1. Open issue #1 in the "Issues tab" 16 | 2. Apply the `first-joke` label to the issue 17 | 3. Wait a few seconds and then apply the `second-joke` label to the issue 18 | 4. Check the `JS Actions` workflow results on the "Actions tab" 19 | 5. Wait about 20 seconds then refresh this page (the one you're following instructions from). [GitHub Actions](https://docs.github.com/en/actions) will automatically update to the next step. 20 | -------------------------------------------------------------------------------- /.github/steps/X-finish.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ## Finish 7 | 8 | celebrate 9 | 10 | ### Congratulations, you've completed this course! 11 | 12 | In this course, you've learned a lot about developing custom actions using JavaScript and Actions Toolkit. 13 | 14 | ## Publishing your actions 15 | 16 | Publishing your actions is a great way to help others in your team and across the GitHub community. Although actions do not need to be published to be consumed, by adding them to the marketplace you make them easier to find. 17 | 18 | Some notable actions you will find on the marketplace are: 19 | 20 | - [Actions for Discord](https://github.com/marketplace/actions/actions-for-discord) 21 | - [GitHub Action for Slack](https://github.com/marketplace/actions/github-action-for-slack) 22 | - [Jekyll action](https://github.com/marketplace/actions/jekyll-action) 23 | - [Run Jest](https://github.com/marketplace/actions/run-jest) 24 | 25 | And that just scratches the surface of the 1600+ and counting actions you will find on the marketplace 26 | 27 | Follow [this guide](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/publishing-actions-in-github-marketplace#publishing-an-action) to learn how to publish your actions to the GitHub Marketplace 28 | 29 | ### What's next? 30 | 31 | - [Take another GitHub Skills course](https://github.com/skills). 32 | - We'd love to hear what you thought of this course in our [discussion board](https://github.com/orgs/skills/discussions/categories/write-javascript-actions). 33 | - [Read the GitHub Getting Started docs](https://docs.github.com/en/get-started). 34 | - To find projects to contribute to, check out [GitHub Explore](https://github.com/explore). 35 | -------------------------------------------------------------------------------- /.github/workflows/0-welcome.yml: -------------------------------------------------------------------------------- 1 | name: Step 0, Welcome 2 | 3 | # This step triggers after the learner creates a new repository from the template. 4 | # This workflow updates from step 0 to step 1. 5 | 6 | # When creating a repository from a template, there is variability in the 7 | # order and timing of events that fire and when workflow triggers are registered. 8 | # Given that, these triggers are purposely broad to ensure this workflow is always triggered. 9 | # The conditions within the on_start job are to ensure it is only fully executed once. 10 | # Reference: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows 11 | on: 12 | push: 13 | branches: 14 | - main 15 | workflow_dispatch: 16 | 17 | permissions: 18 | contents: write 19 | pull-requests: write 20 | issues: write 21 | 22 | jobs: 23 | # Get the current step to only run the main job when the learner is on the same step. 24 | get_current_step: 25 | name: Check current step number 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | - id: get_step 31 | run: | 32 | echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT 33 | outputs: 34 | current_step: ${{ steps.get_step.outputs.current_step }} 35 | 36 | on_start: 37 | name: On start 38 | needs: get_current_step 39 | 40 | # We will only run this action when: 41 | # 1. This repository isn't the template repository. 42 | # 2. The step is currently 0. 43 | # Reference: https://docs.github.com/en/actions/learn-github-actions/contexts 44 | # Reference: https://docs.github.com/en/actions/learn-github-actions/expressions 45 | if: >- 46 | ${{ !github.event.repository.is_template 47 | && needs.get_current_step.outputs.current_step == 0 }} 48 | 49 | # We'll run Ubuntu for performance instead of Mac or Windows. 50 | runs-on: ubuntu-latest 51 | 52 | steps: 53 | # We'll need to check out the repository so that we can edit the README. 54 | - name: Checkout 55 | uses: actions/checkout@v4 56 | with: 57 | fetch-depth: 0 # Let's get all the branches. 58 | 59 | # This is required to establish labels. 60 | # after being created from the template repository. 61 | - name: Prepare labels 62 | run: | 63 | echo "Make sure we are on step 0" 64 | if [ "$(cat .github/steps/-step.txt)" != 0 ] 65 | then 66 | echo "Current step is not 0" 67 | exit 0 68 | fi 69 | echo "Create label 1" 70 | gh label create "first joke" -d "first joke for lab" -f 71 | echo "Create label 2" 72 | gh label create "second joke" -d "second joke for lab" -f 73 | env: 74 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 75 | 76 | # This is required to create issues. 77 | # after being created from the template repository. 78 | - name: Prepare issues 79 | run: | 80 | echo "Make sure we are on step 0" 81 | if [ "$(cat .github/steps/-step.txt)" != 0 ] 82 | then 83 | echo "Current step is not 0" 84 | exit 0 85 | fi 86 | echo "Create issue #1 from file" 87 | gh issue create --title "Create Custom Action" --body "Implement custom action using JavaScript" 88 | env: 89 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 90 | 91 | # Update README from step 0 to step 1. 92 | - name: Update to step 1 93 | uses: skills/action-update-step@v2 94 | with: 95 | token: ${{ secrets.GITHUB_TOKEN }} 96 | from_step: 0 97 | to_step: 1 98 | -------------------------------------------------------------------------------- /.github/workflows/1-initialize-javascript-project.yml: -------------------------------------------------------------------------------- 1 | name: Step 1, Initialize JavaScript Project 2 | 3 | # This step listens for the learner pushing a commit to `main`. 4 | # This workflow updates from step 1 to step 2. 5 | 6 | # This will run every time we push a commit to `main`. 7 | # Reference: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows 8 | 9 | on: 10 | workflow_dispatch: 11 | push: 12 | branches: 13 | - main 14 | 15 | permissions: 16 | # Need `contents: read` to checkout the repository. 17 | # Need `contents: write` to update the step metadata. 18 | contents: write 19 | 20 | jobs: 21 | # Get the current step to only run the main job when the learner is on the same step. 22 | get_current_step: 23 | name: Check current step number 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | - id: get_step 29 | run: | 30 | echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT 31 | outputs: 32 | current_step: ${{ steps.get_step.outputs.current_step }} 33 | 34 | on_commit_contains_required_dependendencies: 35 | name: Check if main branch has the required action dependencies 36 | needs: get_current_step 37 | 38 | # We will only run this action when: 39 | # 1. This repository isn't the template repository. 40 | # 2. The step is currently 1. 41 | # Reference: https://docs.github.com/en/actions/learn-github-actions/contexts 42 | # Reference: https://docs.github.com/en/actions/learn-github-actions/expressions 43 | if: >- 44 | ${{ !github.event.repository.is_template 45 | && needs.get_current_step.outputs.current_step == 1 }} 46 | 47 | # We'll run Ubuntu for performance instead of Mac or Windows. 48 | runs-on: ubuntu-latest 49 | 50 | steps: 51 | - name: Checkout 52 | uses: actions/checkout@v4 53 | with: 54 | fetch-depth: 0 # Let's get all the branches. 55 | 56 | - name: Dump GitHub comment context 57 | id: github_comment_step 58 | env: 59 | COMMENT_CONTEXT: ${{ toJson(github.event) }} 60 | run: echo "$COMMENT_CONTEXT" 61 | 62 | # Check if NPM package.json exists in the expected location. 63 | - name: Check file existence 64 | id: check_files 65 | uses: andstor/file-existence-action@v3 66 | with: 67 | files: ".github/actions/joke-action/package.json" 68 | 69 | # Update README from step 1 to step 2. 70 | - name: Update to step 2 71 | if: steps.check_files.outputs.files_exists == 'true' 72 | uses: skills/action-update-step@v2 73 | with: 74 | token: ${{ secrets.GITHUB_TOKEN }} 75 | from_step: 1 76 | to_step: 2 77 | -------------------------------------------------------------------------------- /.github/workflows/2-configure-your-action.yml: -------------------------------------------------------------------------------- 1 | name: Step 2, Configure Your Action 2 | 3 | # This step listens for the learner pushing a commit to `main`. 4 | # This workflow updates from step 2 to step 3. 5 | 6 | # This will run every time we push a commit to `main`. 7 | # Reference: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows 8 | on: 9 | workflow_dispatch: 10 | push: 11 | branches: 12 | - main 13 | 14 | permissions: 15 | # Need `contents: read` to checkout the repository. 16 | # Need `contents: write` to update the step metadata. 17 | contents: write 18 | 19 | jobs: 20 | # Get the current step to only run the main job when the learner is on the same step. 21 | get_current_step: 22 | name: Check current step number 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | - id: get_step 28 | run: | 29 | echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT 30 | outputs: 31 | current_step: ${{ steps.get_step.outputs.current_step }} 32 | 33 | on_check_action_file_added: 34 | name: Check if the action.yml file was added 35 | needs: get_current_step 36 | 37 | # We will only run this action when: 38 | # 1. This repository isn't the template repository. 39 | # 2. The step is currently 2. 40 | # Reference: https://docs.github.com/en/actions/learn-github-actions/contexts 41 | # Reference: https://docs.github.com/en/actions/learn-github-actions/expressions 42 | if: >- 43 | ${{ !github.event.repository.is_template 44 | && needs.get_current_step.outputs.current_step == 2 }} 45 | 46 | # We'll run Ubuntu for performance instead of Mac or Windows. 47 | runs-on: ubuntu-latest 48 | 49 | steps: 50 | # We'll need to check out the repository so that we can edit the README. 51 | - name: Checkout 52 | uses: actions/checkout@v4 53 | with: 54 | fetch-depth: 0 # Let's get all the branches. 55 | 56 | - name: Dump GitHub comment context 57 | id: github_comment_step 58 | env: 59 | COMMENT_CONTEXT: ${{ toJson(github.event) }} 60 | run: echo "$COMMENT_CONTEXT" 61 | 62 | # Check if NPM package.json exists in the expected location. 63 | - name: Check file existence 64 | id: check_files 65 | uses: andstor/file-existence-action@v3 66 | with: 67 | files: ".github/actions/joke-action/action.yml" 68 | 69 | # Update README from step 2 to step 3. 70 | - name: Update to step 3 71 | if: steps.check_files.outputs.files_exists == 'true' 72 | uses: skills/action-update-step@v2 73 | with: 74 | token: ${{ secrets.GITHUB_TOKEN }} 75 | from_step: 2 76 | to_step: 3 77 | -------------------------------------------------------------------------------- /.github/workflows/3-create-metadata-file.yml: -------------------------------------------------------------------------------- 1 | name: Step 3, Create Metadata File 2 | 3 | # This step listens for the learner pushing a commit to `main`. 4 | # This workflow updates from step 3 to step 4. 5 | 6 | # This will run every time we push a commit to `main`. 7 | # Reference: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows 8 | on: 9 | workflow_dispatch: 10 | push: 11 | branches: [main] 12 | 13 | permissions: 14 | # Need `contents: read` to checkout the repository. 15 | # Need `contents: write` to update the step metadata. 16 | contents: write 17 | 18 | env: 19 | EXPECTED_METADATA: outputs 20 | 21 | jobs: 22 | # Get the current step to only run the main job when the learner is on the same step. 23 | get_current_step: 24 | name: Check current step number 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | - id: get_step 30 | run: | 31 | echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT 32 | outputs: 33 | current_step: ${{ steps.get_step.outputs.current_step }} 34 | 35 | on_action_file_contains_expected_metadata: 36 | name: Check if the action.yml file contains the expected metadata 37 | needs: get_current_step 38 | 39 | # We will only run this action when: 40 | # 1. This repository isn't the template repository. 41 | # 2. The step is currently 3. 42 | # Reference: https://docs.github.com/en/actions/learn-github-actions/contexts 43 | # Reference: https://docs.github.com/en/actions/learn-github-actions/expressions 44 | if: >- 45 | ${{ !github.event.repository.is_template 46 | && needs.get_current_step.outputs.current_step == 3 }} 47 | 48 | # We'll run Ubuntu for performance instead of Mac or Windows. 49 | runs-on: ubuntu-latest 50 | 51 | steps: 52 | # We'll need to check out the repository so that we can edit the README. 53 | - name: Checkout 54 | uses: actions/checkout@v4 55 | with: 56 | fetch-depth: 0 # Let's get all the branches. 57 | 58 | - name: Dump GitHub comment context 59 | id: github_comment_step 60 | env: 61 | COMMENT_CONTEXT: ${{ toJson(github.event) }} 62 | run: echo "$COMMENT_CONTEXT" 63 | 64 | # Check if action.yml file contains metadata. 65 | - name: Read action file contents 66 | id: read_action_yml_file 67 | uses: andstor/file-reader-action@v1 68 | with: 69 | path: ".github/actions/joke-action/action.yml" 70 | 71 | # Update README to from step 3 to step 4. 72 | - name: Update to step 4 73 | if: (contains(steps.read_action_yml_file.outputs.contents, ${{ env.EXPECTED_METADATA }}) 74 | uses: skills/action-update-step@v2 75 | with: 76 | token: ${{ secrets.GITHUB_TOKEN }} 77 | from_step: 3 78 | to_step: 4 79 | -------------------------------------------------------------------------------- /.github/workflows/4-create-javascript-files-for-action.yml: -------------------------------------------------------------------------------- 1 | name: Step 4, Create JavaScript Files For Action 2 | 3 | # This step listens for the learner pushing a commit to `main`. 4 | # This workflow updates from step 4 to step 5. 5 | 6 | # This will run every time we push a commit to `main` 7 | # Reference: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows 8 | on: 9 | workflow_dispatch: 10 | push: 11 | branches: [main] 12 | 13 | permissions: 14 | # Need `contents: read` to checkout the repository. 15 | # Need `contents: write` to update the step metadata. 16 | contents: write 17 | 18 | jobs: 19 | # Get the current step to only run the main job when the learner is on the same step. 20 | get_current_step: 21 | name: Check current step number 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | - id: get_step 27 | run: | 28 | echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT 29 | outputs: 30 | current_step: ${{ steps.get_step.outputs.current_step }} 31 | 32 | on_javascript_files_added: 33 | name: Check if JavaScript files were added 34 | needs: get_current_step 35 | 36 | # We will only run this action when: 37 | # 1. This repository isn't the template repository. 38 | # 2. The step is currently 4. 39 | # Reference: https://docs.github.com/en/actions/learn-github-actions/contexts 40 | # Reference: https://docs.github.com/en/actions/learn-github-actions/expressions 41 | if: >- 42 | ${{ !github.event.repository.is_template 43 | && needs.get_current_step.outputs.current_step == 4 }} 44 | 45 | # We'll run Ubuntu for performance instead of Mac or Windows. 46 | runs-on: ubuntu-latest 47 | 48 | steps: 49 | # We'll need to check out the repository so that we can edit the README. 50 | - name: Checkout 51 | uses: actions/checkout@v4 52 | with: 53 | fetch-depth: 0 # Let's get all the branches. 54 | 55 | - name: Dump GitHub comment context 56 | id: github_comment_step 57 | env: 58 | COMMENT_CONTEXT: ${{ toJson(github.event) }} 59 | run: echo "$COMMENT_CONTEXT" 60 | 61 | # Check if action JavaScript exists in the expected location. 62 | - name: Check file existence 63 | id: check_files 64 | uses: andstor/file-existence-action@v3 65 | with: 66 | files: ".github/actions/joke-action/joke.js, .github/actions/joke-action/main.js" 67 | 68 | # Update README from step 4 to step 5. 69 | - name: Update to step 5 70 | if: steps.check_files.outputs.files_exists == 'true' 71 | uses: skills/action-update-step@v2 72 | with: 73 | token: ${{ secrets.GITHUB_TOKEN }} 74 | from_step: 4 75 | to_step: 5 76 | -------------------------------------------------------------------------------- /.github/workflows/5-add-action-to-workflow-file.yml: -------------------------------------------------------------------------------- 1 | name: Step 5, Add Action To Workflow File 2 | 3 | # This step listens for the learner pushing a commit to `main` 4 | # This workflow updates from step 5 to step 6. 5 | 6 | # This will run every time we push a commit to `main`. 7 | # Reference: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows 8 | on: 9 | workflow_dispatch: 10 | push: 11 | branches: [main] 12 | 13 | permissions: 14 | # Need `contents: read` to checkout the repository. 15 | # Need `contents: write` to update the step metadata. 16 | contents: write 17 | 18 | env: 19 | EXPECTED_METADATA: ha-ha 20 | 21 | jobs: 22 | # Get the current step to only run the main job when the learner is on the same step. 23 | get_current_step: 24 | name: Check current step number 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | - id: get_step 30 | run: | 31 | echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT 32 | outputs: 33 | current_step: ${{ steps.get_step.outputs.current_step }} 34 | 35 | on_workflow_added_with_custom_action: 36 | name: Check if workflow is referencing the custom action 37 | needs: get_current_step 38 | 39 | # We will only run this action when: 40 | # 1. This repository isn't the template repository. 41 | # 2. The step is currently 5. 42 | # Reference: https://docs.github.com/en/actions/learn-github-actions/contexts 43 | # Reference: https://docs.github.com/en/actions/learn-github-actions/expressions 44 | if: >- 45 | ${{ !github.event.repository.is_template 46 | && needs.get_current_step.outputs.current_step == 5 }} 47 | 48 | # We'll run Ubuntu for performance instead of Mac or Windows. 49 | runs-on: ubuntu-latest 50 | 51 | steps: 52 | # We'll need to check out the repository so that we can edit the README. 53 | - name: Checkout 54 | uses: actions/checkout@v4 55 | with: 56 | fetch-depth: 0 # Let's get all the branches. 57 | 58 | - name: Dump GitHub comment context 59 | id: github_comment_step 60 | env: 61 | COMMENT_CONTEXT: ${{ toJson(github.event) }} 62 | run: echo "$COMMENT_CONTEXT" 63 | 64 | # Check if action.yml file contains metadata. 65 | - name: Read action file contents 66 | id: read_action_yml_file 67 | uses: andstor/file-reader-action@v1 68 | with: 69 | path: ".github/workflows/my-workflow.yml" 70 | 71 | # Update README from step 5 to step 6. 72 | - name: Update to step 6 73 | if: (contains(steps.read_action_yml_file.outputs.contents, ${{ env.EXPECTED_METADATA }}) 74 | uses: skills/action-update-step@v2 75 | with: 76 | token: ${{ secrets.GITHUB_TOKEN }} 77 | from_step: 5 78 | to_step: 6 79 | -------------------------------------------------------------------------------- /.github/workflows/6-trigger-action.yml: -------------------------------------------------------------------------------- 1 | name: Step 6, Trigger Your Action 2 | 3 | # This step listens for the learner applying a label to an issue. 4 | # This workflow updates from step 6 to step X. 5 | 6 | # This will run every time we apply a label to an issue. 7 | # Reference: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows 8 | on: 9 | workflow_dispatch: 10 | issues: 11 | types: [labeled] 12 | 13 | permissions: 14 | # Need `contents: read` to checkout the repository. 15 | # Need `contents: write` to update the step metadata. 16 | contents: write 17 | 18 | env: 19 | EXPECTED_METADATA: "second joke" 20 | 21 | jobs: 22 | # Get the current step to only run the main job when the learner is on the same step. 23 | get_current_step: 24 | name: Check current step number 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | - id: get_step 30 | run: | 31 | echo "current_step=$(cat ./.github/steps/-step.txt)" >> $GITHUB_OUTPUT 32 | outputs: 33 | current_step: ${{ steps.get_step.outputs.current_step }} 34 | 35 | on_my_workflow_run: 36 | name: Check if my_workflow has run 37 | needs: get_current_step 38 | 39 | # We will only run this action when: 40 | # 1. This repository isn't the template repository. 41 | # 2. The step is currently 6. 42 | # Reference: https://docs.github.com/en/actions/learn-github-actions/contexts 43 | # Reference: https://docs.github.com/en/actions/learn-github-actions/expressions 44 | if: >- 45 | ${{ !github.event.repository.is_template 46 | && needs.get_current_step.outputs.current_step == 6 }} 47 | 48 | # We'll run Ubuntu for performance instead of Mac or Windows. 49 | runs-on: ubuntu-latest 50 | 51 | steps: 52 | # We'll need to check out the repository so that we can edit the README. 53 | - name: Checkout 54 | uses: actions/checkout@v4 55 | with: 56 | fetch-depth: 0 # Let's get all the branches. 57 | 58 | - name: Dump GitHub comment context 59 | id: github_comment_step 60 | env: 61 | COMMENT_CONTEXT: ${{ toJson(github.event) }} 62 | run: echo "$COMMENT_CONTEXT" 63 | 64 | # Update README from step 6 to step X. 65 | - name: Update to step X 66 | if: contains(github.event, ${{ env.EXPECTED_METADATA }}) 67 | uses: skills/action-update-step@v2 68 | with: 69 | token: ${{ secrets.GITHUB_TOKEN }} 70 | from_step: 6 71 | to_step: X 72 | -------------------------------------------------------------------------------- /.github/workflows/my-workflow.yml: -------------------------------------------------------------------------------- 1 | name: JS Actions 2 | 3 | on: 4 | issues: 5 | types: [labeled] 6 | 7 | jobs: 8 | action: 9 | if: ${{ !github.event.repository.is_template }} 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | -------------------------------------------------------------------------------- /.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 |
2 | 3 | 10 | 11 | # Write JavaScript Actions 12 | 13 | _Write your own GitHub JavaScript Action and automate customized tasks unique to your workflow._ 14 | 15 |
16 | 17 | 22 | 23 | ## Welcome 24 | 25 | Write your own GitHub JavaScript Action and automate customized tasks unique to your workflow. 26 | 27 | - **Who is this for**: Developers, GitHub users, users new to Git, students, managers, and for teams. 28 | - **What you'll learn**: How to consume actions within a workflow file, create custom JavaScript based actions and publish your newly created action to the marketplace. 29 | - **Prerequisites**: Before you start, you should be familiar with GitHub, GitHub Actions, and Continuous Integration with GitHub Actions. 30 | - **How long**: This course takes about 1 to 2 hours to be completed. 31 | 32 | In this course, you will: 33 | 34 | 1. Initialize a JavaScript project 35 | 2. Configure an action 36 | 3. Create a metadata file 37 | 4. Create JavaScript files 38 | 5. Add actions to workflow file 39 | 6. Trigger action 40 | 41 | ### How to start this course 42 | 43 | 53 | 54 | [![start-course](https://user-images.githubusercontent.com/1221423/235727646-4a590299-ffe5-480d-8cd5-8194ea184546.svg)](https://github.com/new?template_owner=skills&template_name=write-javascript-actions&owner=%40me&name=skills-write-javascript-actions&description=My+clone+repository&visibility=public) 55 | 56 | 1. Right-click **Start course** and open the link in a new tab. 57 | 2. In the new tab, most of the prompts will automatically fill in for you. 58 | - For owner, choose your personal account or an organization to host the repository. 59 | - 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). 60 | - Scroll down and click the **Create repository** button at the bottom of the form. 61 | 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. 62 | 63 | 72 | --------------------------------------------------------------------------------