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 that
ls *.sh
Something 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 actionsruns
for JavaScript actionsruns
for composite run steps actionsruns
for Docker actionsbranding
Two parameters that do not appear in the configuration
- 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
- Branding: Action branding is what the GitHub Marketplace looks like
We know that RUNS supports three forms
- Js (for this article)
- Composite: Using Linux commands (although macOS devices theoretically support this), shell scripts
- 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
- So let’s define an option so that the outside world knows that we need this, and that’s reflected in the project
action.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
-
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
- Get all labels
- 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
- Write the README
- Play tag/release
- 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
- Published protocols
- 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