Github Action is a great thing, but when I look at it, many of my friends just use it. In fact, once in a while, you should develop your own action instead of just using it

Introduction to the

Github Actions github actions is a workflow tool designed to help us actively trigger repository actions in certain situations for unit testing /CI/CD, even release, release management tools, etc

The official repository for actions is here: github.com/actions, and the documentation is here

Github’s main language is JS, and it certainly supports TS as well

In addition, if the need for speed is not high, you can also use Docker, but because the docker installation process will take a certain amount of time according to the size of the image, so it may not be suitable for all friends

If you are not interested in this article, refer to the documentation for creating actions

new

Because I don’t like JS, SO I use TS (not a big fan, but better)

To enter thewarehouseAnd then useButton to complete the initialization process.

Here we create a repository whose purpose is to label the issue automatically

The initialized warehouse

A brief introduction to the warehouse, with some documents and notes

  • Action. yml is the configuration file of the action itself (other projects actually read this thing to determine where the entry is), including the configuration of parameters
  • A standard NPM project with entry specified
  • Inside SRC is the main TS code
  • Ts code needs to be compiled to JS to be used
  • Dist is the compilation product. Git version control needs to include all files in dist, otherwise it will run with old code
  • The project itself has its own action, mainly for CI

An introduction to

The development environment

  • Vscode, here I use vscode to edit, please according to your own situation
  • NPM (node), which I manage using NVM

If your node is larger than 12.0, you should theoretically leave it alone

Clone project

git clone https://github.com/CaiJingLong/action_auto_label.git
cd action_auto_label
npm i
Copy the code

Official Support Library

Toolkit contains some of the libraries that Github officially supports, which I will not cover in detail

  • The @Actions /core Actions core library is included by default
  • @actions/exec If you need to perform CLI tools like ls, mkdir, etc., it’s easy to encapsulate procedures and log output and things like that
  • @actions/ioGlob matches files, we all know thatls *.shSomething like this, the * is a glob, not a re
  • @Actions/Github Encapsulates github, which contains actions that operate on Github itself

Since this post is going to work on Github, we’ll add this to the list below

npm i @actions/github

Hello world

Note that it is not recommended to use console.log for logging in TS, so we will use the core.info method instead

As usual, hello world first.

src/main.ts

import * as core from "@actions/core";

async function run() :Promise<void> {
  try {
    core.info(`Hello world`);
  } catch (error) {
    core.setFailed(error.message);
  }
}

run();
Copy the code

.github/workflows/issue.yml

name: "On issue"
on:
  issue:
    types: [opened.reopened.edited]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: . /
Copy the code

NPM run All package. This step is important, otherwise dist will not work. Consider using Git hooks to do this

The push code is followed by a new issue to trigger it

“Issue” reported wrong, saying it is not a valid event name. Ok, we need to change it to “Issues”, we will resubmit it, and then trigger it. Because edited can be triggered here, we modify the content of the issue, and then recommit

name: "On issue"
on:
  issues:
    types: [opened.reopened.edited]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: . /
Copy the code

This time, the action is successfully triggered and Hello World is printed.

Action. Yml configuration

As mentioned earlier, this file is the configuration file (or manifest file, if you like) for the action, with some configuration options

You can configure parameters in actions to be passed in from outside, by default

The default file content is as follows:

name: "Your name here" As the name implies, the name of the action
description: "Provide a description here" Description of action
author: "Your name or organization here" # author/organization /email etc
inputs: # dictionary of arguments
  milliseconds: # change this
    required: true Is this mandatory
    description: "input description here" # parameter description
    default: "default value if applicable" # the default value
runs: # Runtime environment
  using: "node12" Run on node12
  main: "dist/index.js" This is the thing that requires us to compile TS to JS before it can be used
Copy the code

After looking at the contents of the default file, we are going to try to modify it (the documentation is here), and we know from the documentation that there are the following configuration parameters

  • name
  • author
  • description
  • inputs
  • outputs
  • outputs for composite run steps actions
  • runs for JavaScript actions
  • runs for composite run steps actions
  • runs for Docker actions
  • branding

Two parameters that do not appear in the configuration

  1. outputs: Output parameter, because each action is actually unknown to each other, with this, I can do the reductive output, for example, I execute something in actions 1, and put the result of the calculation in this parameter, and then I can use it, can simply be understood as the return value of the action
  2. Branding: Action branding is what the GitHub Marketplace looks like

We know that RUNS supports three forms

  1. Js (for this article)
  2. Composite: Using Linux commands (although macOS devices theoretically support this), shell scripts
  3. Docker: Using Docker environment, the advantages are not to be said, convenient configuration, strong universality, disadvantage is not as fast as JS and Composite, after all, it takes time to load Docker, the larger the image, the slower the speed

Inputs have a point to note: Use the original name when retrieving it in JS code, but if you’re using it in a shell (composite, or some other language, like Docker using C, Java, etc.), INPUT_

in the environment variable

That’s the simple concept, and then we’re going to do it

The environment variable

Github’s default environment variables include, but are not limited to,$HOME,$GITHUB_WORKSPACE, etc

Sensitive information configuration problem

As we all know, in many cases, the project has some secret information that cannot be configured directly in the project, including but not limited to:

  • github token
  • User names and passwords of various accounts
  • The private key information
  • Various websites API key, APP key, secret key and so on

At this point, it takes some skill to configure them and read the official documents in the code

configuration

This step is done in the Github repository setting



So you can see that even though we’re writing it in lower case, we’re actually writing it in upper case, so be careful here

read

Inputs: ${{secrets.

}} inputs: ${{secrets.

} inputs

  1. So let’s define an option so that the outside world knows that we need this, and that’s reflected in the projectaction.yml
name: "Auto label"
description: "Automation generate label for issues."
author: "Caijinglong"
inputs:
  user_name:
    required: true
    description: "User name"
runs:
  using: "node12"
  main: "dist/index.js"
Copy the code
  1. To configure the workflow: making/workflows/issue. Yml

    name: "On issue"
    on:
      issues:
        types: [opened.reopened.edited]
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v2
          - uses: . /
            with:
              user_name: The ${{ secrets.USER_NAME }}
    Copy the code

Under test

import * as core from "@actions/core";

async function run() :Promise<void> {
  try {
    core.info(`Hello world`);
    const username = core.getInput("user_name");
    core.info(`Hello ${username}`);

    core.info(`username === admin : ${username === "admin"}`);
  } catch (error) {
    core.setFailed(error.message);
  }
}

run();
Copy the code

Often push, always want to modify things, very troublesome, simple push script

touch push.sh
chmod +x push.sh
echo "npm run all && git add . && git commit -m 'push with shell' && git push" > push.sh

./push.sh
Copy the code

Then the trigger is to use the open Issue method

And, well, it turns out that the *** here is’ secured ‘, since admin is manually typed, but ‘happens’ to be the same as secret, so it’s encoded together, and then it’s true, even though it’s encoded, But it does not affect the actual running results

In front of the simple entry configuration are completed, the next simple combat

In actual combat

This action project automatically adds an issue label based on the issue title

Use making API

How to use the API, using the @Actions/Github capability

import * as github from '@actions/github'. core.info(`event name = ${github.context.eventName}`)

Copy the code

And that’s the result

Making configuration label

Think steps first

  1. Get all labels
  2. Match the issue title, using the re to get the leading one[]The contents are as follows[bug]Automatically annotate features such as title, bug label, feature/feature request, etc

Core code:

import * as core from "@actions/core";
import * as github from "@actions/github";
import * as Webhooks from "@octokit/webhooks";

export async function run(githubToken: string) :Promise<void> {
  try {
    if(github.context.eventName ! = ="issues") {
      core.info(
        'Currently only issues trigger is supported, your type is${github.context.eventName}`
      );
      return;
    }
    core.info(`The run token = '${githubToken}'`);

    const payload = github.context
      .payload as Webhooks.EventPayloads.WebhookPayloadIssues;

    core.info(`Hello world`);
    const username = core.getInput("user_name");
    core.info(`Hello ${username}`);

    core.info(`username === admin : ${username === "admin"}`);

    core.info(`event name = ${github.context.eventName}`);

    const octokit = github.getOctokit(githubToken);

    const { owner, repo } = github.context.repo;
    const issue_number = payload.issue.number;
    const regex = /\[([^\]]+)\]/g;
    const array = regex.exec(payload.issue.title);

    core.info(
      'trigger issue: owner:${owner}, repo = ${repo}, issue_number = ${issue_number}`
    );

    if (array == null) {
      core.info('No label found, reply');
      await octokit.issues.createComment({
        owner,
        repo,
        issue_number,
        body: 'No label' of type [XXX] was found});return;
    }

    const labelName = array[1];
    core.info('Expected tag name: labelName is =${labelName}`);

    const allLabels = await octokit.issues.listLabelsForRepo({
      owner,
      repo,
    });

    const labelText = allLabels.data
      .map<string> ((data) = > {
        return data.name;
      })
      .join(",");

    core.info('Found a bunch of labels${labelText}`);

    let haveResult = false;

    for (const label of allLabels.data) {
      const labels = [label.name];
      if (labelName.toUpperCase() === label.name.toUpperCase()) {
        core.info("I found the label. Label it.");
        await octokit.issues.addLabels({
          owner,
          repo,
          issue_number,
          labels,
        });
        haveResult = true;
        break; }}if(! haveResult) { core.info('Didn't find the label${labelName}, reply, may be a new problem, now the first short reply '
      );
      await octokit.issues.createComment({
        owner,
        repo,
        issue_number,
        body: 'Not found${labelName}`}); } core.info("run success");
  } catch (error) {
    core.error("The action run error:"); core.error(error); core.setFailed(error.message); }}Copy the code

The configuration file

name: "On issue"
on:
  issues:
    types: [opened.reopened.edited]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: . /
        with:
          user_name: The ${{ secrets.USER_NAME }}
          github-token: The ${{ secrets.GITHUB_TOKEN }}
Copy the code

Take a look after compiling and uploading



After debugging, achieved the desired effect, found on the mark, not on the label


That is to say, after experiencing these, we can simply achieve our purpose. Later, we can expand the function according to the needs. The current flaw is that some functions are not convenient to debug

In practice, for the convenience of unit testing, encapsulation can be more detailed. For example: remove the github token, issue, repo, owner, title, etc., so that the local test is really useful

release

When you’re done, you’re ready to publish your work, which means to make it available on the Action store

Generally speaking, there are three steps

  1. Write the README
  2. Play tag/release
  3. Post to the Action store

Final file style

. ├ ─ ─ LICENSE ├ ─ ─ the README. Md ├ ─ ─ __tests__ / │ └ ─ ─. The main test. The ts ├ ─ ─ action. Yml ├ ─ ─ dist / │ ├ ─ ─ index. The js │ ├ ─ ─ index. The js. The map │ ├ ─ ─ licenses. TXT │ └ ─ ─ sourcemap - register. Js ├ ─ ─ jest. Config. Js ├ ─ ─ lib / │ ├ ─ ─ handle. Js │ ├ ─ ─ the main, js │ └ ─ ─ wait. Js ├ ─ ─ package - lock. Json ├ ─ ─ package. The json ├ ─ ─ push. Sh * ├ ─ ─ SRC / │ ├ ─ ─ the handle. The ts │ ├ ─ ─ main. Ts │ └ ─ ─ wait. Ts └ ─ ─ tsconfig.jsonCopy the code

Write the README

I’m not going to expand on this, but I’m going to copy somebody else’s, and I’m going to do whatever I want

Play tag

Use the github Web release function directly, so that you can complete the tag and release at the same time, generally speaking, the common action is 1 bit long, we directly type a v1.0.0, and then use the user. Usually use xxx@v1

For example, the most commonly used actions/checkout, the latest release is v2.3.2, but you can use @v2 directly

The official note is that tokens such as v1 v1.0.0 commitHash, master are acceptable, but @master is generally not recommended

Release!

Here’s the url. Select your action, the name you defined in action.yml

I need a release, so I’ll use v1.0.0

When you expose the repository, you can see that there is a release Action option

Then, if you’re using it for the first time, there may be two additional steps

  1. Published protocols
  2. You must enable two-step authentication, I use authy, you can use any other github supported tools, the specific process can be baidu

Yml = action.yml = action.yml = action.yml

Afterword.

This article combines github documentation and templates to complete the github action creation, use, and call process repository