Closed efi-rfauzan closed 2 years ago
Like our "hello world" action, this action will require at least one input:
parameter. We need this parameter so that our JavaScript for this action has access to the output:
from the joke action.
If you recall, in the my-workflow.yml
file, we stated this action would take a specific input named joke:
and we set it's value to the output of the previous action.
- name: create-issue
uses: ./.github/actions/issue-maker
with:
joke: ${{steps.jokes.outputs.joke-output}}
Because of this, we need to define joke:
as one of our inputs:
for this action. Remember when we did this with the first action? It looked a little like this:
inputs:
first-greeting:
description: who you would like to greet in the console
required: true
default: Hubot
Now, we will do something similar so that our action matches what our workflow expects.
💡All of the following steps take place inside of the .github/actions/issue-maker
directory.
We will use the joke output, an issue-title
, and the repo-token
in this portion of the course as inputs:
for our action.
Create a file named action.yml
with the following contents:
name: "I have issues"
description: "consume the output of the previous action and create a new issue in the repository"
inputs:
joke:
description: "This will become the body of the created issue"
issue-title:
description: "Every issue needs a title, it's nice to supply one, even though you could do this dynamically within your code"
default: "a joke for you"
required: true
repo-token:
description: "Token with permissions to do repo things"
runs:
using: "node12"
main: "index.js"
action.yml
filegit add action.yml
git commit -m 'create action.yml'
git push
I will respond when you commit to this branch.
@efi-rfauzan your joke-action stores a value in an output:
parameter. We are going to use that value to create a new issue inside this repository!
Sometimes going from code-to-cloud requires more automation than CI/CD can provide. Actions can be used for this automation and hopefully after you learn how to interact with this repository through writing this action you'll go on to write many more amazing pieces of automation.
Scenario
You run an open source repository that is community driven. Every month you are getting tons of contributions from random developers within the community. Sometimes these contributions are amazing because the developers have read your contributing guidelines. Sometimes the opposite occurs. In both events you would like to thank your community for their contribution and ensure that every contributor is aware that you do in fact have guidelines for contributions. How would you do this?
You can imagine just how much time would be consumed if we sent a human... if we can call developers human in the first place 😉, to respond to all of our projects first time contributors with the same kind of message. Enter GitHub Actions! We can easily automate this process and many more using what you've learned up to this point.
Allow me to show you the final piece and get you started with interacting with your repository through an action.
Our action Unlike the joke action you wrote, this issue maker will have only one JavaScript file.
Let's take a look at what the source code for this action will look like and I'll explain what is happening before having your write it.
Importing packages
The first two lines will import packages from the Actions ToolKit. You'll find yourself using these libraries a lot, so its good to get familiar with them.
const core = require("@actions/core");
const github = require("@actions/github");
Creating the main function
Next we declare an asynchronous function since we are going to be using the HTTP protocol with octokit later.
Wrapping our code in a try/catch block is good practice for error handling, so we will go ahead and do that here. Once that is complete we define a few variables.
async function run() {
try {
} catch (err) {}
}
run();
Getting input for the issue title
Every issue on GitHub needs a title. If you recall we had the issue-title
property set in our action.yml
file for this action, so let's read that in from the workflow file. We do that by calling core.getInput("name of the input")
. In our case the input is named issue-title
so we will use that.
Wait a minute... I know what you're thinking. I said read this property from the workflow file, but we never actually defined it in the workflow. You're right, but why does this work? Do you remember what happens when we give a property a default value and enforce that it is required?
Since the default value can be overwritten we will include it in our code just in case you decide to provide a different title for your joke issues.
async function run() {
try {
const issueTitle = core.getInput("issue-title");
} catch (err) {}
}
run();
Using the other inputs from the metadata Okay, what about the other variables we set up:
jokeBody
token
octokit
The jokeBody
uses the core.getInput()
method but this time that input is going to be defined by the previous action. If you recall, in the workflow we specified joke: ${{steps.jokes.outputs.joke-output}}
to be used with:
this action. This is one way we can pass the output of one action to the another one in the workflow.
We also need to define a token
. This token allows us to interact with the GitHub API, which we will do using octokit/rest.js.
Where does the token
come from?
We specified a repo-token:
parameter for use with:
this action and gave it a value of ${{secrets.GITHUB_TOKEN}}
. I didn't tell you where we got the GITHUB_TOKEN
from so let me do that now.
GitHub sets default environment variables that are available to every step in a workflow run. You also have access to any secrets you have setup in your repository, to include this action specific GITHUB_TOKEN
that is automatically set for you.
You can read more about using the GITHUB_TOKEN
for authentication purposes.
Its also worth taking a look at the different Contexts and expressions that you can use with GitHub Actions.
Adding the octokit client
Long story short, we use the default GITHUB_TOKEN
for authentication with the Octokit client.
async function run() {
try {
const issueTitle = core.getInput("issue-title");
const jokeBody = core.getInput("joke");
const token = core.getInput("repo-token");
const octokit = github.getOctokit(token);
} catch (err) {}
}
run();
Creating an issue in the repository
Next we use that octokit client to create an issue in your repository, which will make the HTTP request to the GitHub API for us. You can learn more about octokit.rest.issues.create()
in the octokit API documentation.
async function run() {
try {
const issueTitle = core.getInput("issue-title");
const jokeBody = core.getInput("joke");
const token = core.getInput("repo-token");
const octokit = github.getOctokit(token);
const newIssue = await octokit.rest.issues.create({
repo: github.context.repo.repo,
owner: github.context.repo.owner,
title: issueTitle,
body: jokeBody
});
} catch (err) {}
}
run()
💡Octokit makes interacting with the GitHub API easy, but if you are writing actions using a different language, or you prefer to not use a library, the same issue can be created with this API endpoint
Let's handle any errors
Lastly, we write the catch
portion of our try/catch block and you use core.setFailed()
method to force our action to fail if something goes wrong, but also produce an error message in the workflow logs.
async function run() {
try {
const issueTitle = core.getInput("issue-title");
const jokeBody = core.getInput("joke");
const token = core.getInput("repo-token");
const octokit = github.getOctokit(token);
const newIssue = await octokit.rest.issues.create({
repo: github.context.repo.repo,
owner: github.context.repo.owner,
title: issueTitle,
body: jokeBody
});
} catch (err) {
core.setFailed(err.message);
}
}
run()
Don't forget to call the function, run()
, on the last line so that your action executes!
I'm counting on you this time! In the previous steps I have guided you heavily on what to type along the way. This time I ask that you look back on the things you've done in this course and pull from the knowledge you already have to accomplish these tasks.
index.js
. Its full path should be: .github/actions/issue-maker/index.js
.core
and github
variablesrun()
issueTitle
, jokeBody
, token
and octokit
variablesissues.create()
octokit method to define your API requestsetFailed()
method from the @actions/core
package to stop your action and log and error if something goes wronggit add index.js
git commit -m 'create index.js'
git push
I'll respond once you push to this branch.
Let's trigger your new workflow! Add a label to this pull request, it can be any label you choose.
After adding a label head over to your Actions tab if you want to watch the workflow.
Once your workflow has completed check your issues tab and you should see a new issue with a hilarious joke as the body!
Continue experimenting with this workflow for as long as you'd like.
Try adding another label and see if you get a new joke!
When you have finished experimenting, merge this pull request. I'll open a new issue containing the next steps once I detect you've closed this.
Great! Go to the final issue.
efi-rfauzan it's time to get ready for the third action 🎉
As with the other actions we wrote, we are going to need to setup a few directories and files.
:keyboard: Activity: Configure your third action
Let's create our final project directory and install all the necessary dependencies. We will take this a step further near the end of this action and we will show you how to avoid needing to check in
node_modules
..github/actions
directory.main
branchaction-three
branch you created for this pull request..github/actions/issue-maker
.issue-maker
folder you just created. The full path should be.github/actions/issue-maker
npm
:I will respond once you have pushed to this branch.