ymadd / writing-javascript-actions

https://lab.github.com/githubtraining/github-actions:-writing-javascript-actions
MIT License
0 stars 0 forks source link

Use outputs #8

Closed ymadd closed 2 years ago

github-learning-lab[bot] commented 2 years ago

ymadd 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.

  1. Open the Terminal (Mac and Linux) or Command Prompt (Windows) on your local machine
  2. navigate to the .github/actions directory.
  3. Checkout the main branch
    git checkout main
  4. Update the contents of your Learning Lab repo to your local machine:
    git pull
  5. Checkout the action-three branch you created for this pull request.
    git checkout action-three
  6. Create a new folder for our actions files. The full path should be .github/actions/issue-maker.
    mkdir issue-maker
  7. Navigate to the issue-maker folder you just created. The full path should be .github/actions/issue-maker
    cd issue-maker
  8. Initialize a new project:
    npm init -y
  9. Install the @actions/core and @actions/github dependencies using npm:
    npm install --save @actions/core @actions/github
  10. Commit those newly added files,we will remove the need to upload node_modules in a later step. Push your changes to GitHub:
    git add .
    git commit -m 'add issue maker dependencies'
    git push

I will respond once you have pushed to this branch.

github-learning-lab[bot] commented 2 years ago

Create and edit the third actions action.yml file

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.

:keyboard: Activity: Create the final metadata file

💡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.

  1. 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"
  2. Save the action.yml file
  3. Commit the changes and push them to GitHub:
    git add action.yml
    git commit -m 'create action.yml'
    git push

I will respond when you commit to this branch.

github-learning-lab[bot] commented 2 years ago

Let's write some JavaScript

@ymadd 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.

About the issue maker 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:

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. Look here to learn more about octokit.issues.create().

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.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.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!

github-learning-lab[bot] commented 2 years ago

:keyboard: Activity: Create the final JavaScript file

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.

  1. Create a file named index.js. Its full path should be: .github/actions/issue-maker/index.js.
  2. Create the core and github variables
  3. Create an asynchronous function named run()
  4. Inside a try/catch block define the issueTitle, jokeBody, token and octokit variables
  5. Use the issues.create() octokit method to define your API request
  6. Add the catch portion of the try/catch block
  7. Use the setFailed() method from the @actions/core package to stop your action and log and error if something goes wrong
  8. Save the file
  9. Commit and push the changes to this branch
    git add index.js
    git commit -m 'create index.js'
    git push

I'll respond once you push to this branch.


View the complete file ```javascript const core = require("@actions/core"); const github = require("@actions/github"); async function run() { try { const issueTitle = core.getInput("issue-title"); const jokeBody = core.getInput("joke"); const token = core.getInput("repo-token"); const octokit = new github.getOctokit(token); const newIssue = await octokit.issues.create({ repo: github.context.repo.repo, owner: github.context.repo.owner, title: issueTitle, body: jokeBody }); } catch (error) { core.setFailed(error.message); } } run(); ```
github-learning-lab[bot] commented 2 years ago

Trigger all the things 🎉

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.

github-learning-lab[bot] commented 2 years ago

Great! Go to the final issue.