Professional Documents
Culture Documents
json master
push master
action.yml
name description
runs name description
runs
docker node.js
docker
action.yml name
description runs.using runs.image
action-test
github-action-explore-debug
git clone
YOUR_USERNAME
github-action-explore-debug
action.yml
runs
Dockerfile docker
image Dockerfile
Dockerfile docker
alpine
jq
action.yml Dockerfile
FROM alpine
ENTRYPOINT ["/entrypoint.sh"]
alpine jq apk
COPY ENTRYPOINT
docker
entrypoint.sh
entrypoint.sh
action.yml
Dockerfile
entrypoint.sh
entrypoint.sh
#!/bin/sh
chmod +x entrypoint.sh
env
GITHUB_ INPUT_ GITHUB_
INPUT_
cut
entrypoint.sh
env
IFS=$'\n'
for v in `env`; do
PREFIX=$(echo $v| cut -d '_' -f1)
./entrypoint.sh
GITHUB_ INPUT_
GITHUB_DEMO=example ./entrypoint.sh
$ GITHUB_DEMO=example ./entrypoint.sh
Debug information:
GITHUB_DEMO=example
jq
Dockerfile
entrypoint.sh
echo "---"
echo "Event:"
jq . $GITHUB_EVENT_PATH
. jq
input
repository sender
.pull_request
jq
jq
INPUT_
jqSelector INPUT_JQSELECTOR
jq_selector INPUT_JQ_SELECTOR
TitleCase camelCase snake_case
snake_case
entrypoint.sh
jq jq
echo "---"
echo "Event:"
jq $INPUT_JQ_SELECTOR $GITHUB_EVENT_PATH
jq
#!/bin/sh
IFS=$'\n'
for v in `env`; do
PREFIX=$(echo $v| cut -d '_' -f1)
echo "---"
echo "Event:"
jq $INPUT_JQ_SELECTOR $GITHUB_EVENT_PATH
$INPUT_JQ_SELECTOR
jq
action.yml inputs
inputs:
jq_selector:
description: 'The JQ selector to execute'
required: false
default: "."
jq_selector
INPUT_JQ_SELECTOR .
input action.yml
docker
GITHUB_ INPUT_
jq
- name: Output debug
runs: |
echo "Debug information:"
IFS=$'\n'
for v in `env`; do
PREFIX=$(echo $v| cut -d '_' -f1)
echo "---"
echo "Event:"
jq .pull_request $GITHUB_EVENT_PATH
- uses: YOUR_NAME/github-action-explore-debug@master
action-test
action-test
action-test
.github/workflows
bananas.yml
<trigger>-<action>
pull_request-check-redirects
.github/workflows
pull_request
.github/workflows/pull_request-debug.yml
YOUR_USERNAME
name: Debug
on:
pull_request:
types: [opened, synchronize, closed]
jobs:
output-debug:
runs-on: ubuntu-18.04
steps:
- uses: YOUR_USERNAME/github-action-explore-debug@master
with:
jq_selector: ".pull_request"
pull_request
on: push
on: pull_request
opened synchronize labeled locked
closed
action-test
on:
pull_request:
types: [opened, synchronize, closed]
pull_request opened
synchronize closed pull_request
review_requested reopened
master on
on:
push:
branches:
- master
on:
push:
tags:
- 'v1.*'
on:
push:
paths:
- 'test/*'
branches-ignore tags-ignore
on:
push:
branches-ignore:
- 'spike-*'
tags-ignore:
- ‘beta-*’
spike-
beta-
branches tags
branches
on:
push:
branches:
- 'release/*'
- '!release/*-beta'
release
-beta
branches-ignore
branches-ignore
release test
output-debug
ubuntu-18.04 runs-on
ubuntu-16.04 runs-on
ubuntu-20.04
ubuntu-latest ubuntu-18.04 act
ubuntu-20.04
windows-latest
windows-2019
macOS-latest macOS-10.15
<os>-latest
<os>-latest
steps
steps
continue-on-error
uses
run
name
with
env
uses run
uses: YOUR_USERNAME/github-action-explore-debug@master
action.yml
steps:
- uses: YOUR_USERNAME/github-action-explore-debug@master
run
steps:
- run: |
echo "Hello World"
[[ -z "$MY_VAR" ]] && echo "MY_VAR is empty"
uses run
name
uses run
name uses run
with env
inputs
env with
env
AWS_ACCESS_KEY_ID AZURE_SUBSCRIPTION_ID
with INPUT_
env
env with
README.md
README.md
README.md
README.md
YOUR_USERNAME
https://github.com/YOUR_USERNAME/action-test/pull/new/changes
Run actions-book/github-action-explore-debug@master
GITHUB_ INPUT_
pull_request
issue_comment
jq_selector: "."
.pull_request.additions
nektos/act
act
act
act --version
0.2.10,
act .github/workflows
act
act push
act act
runs-on: ubuntu-18.04
act
act -v act
act
| Debug information:
| GITHUB_TOKEN=
| GITHUB_EVENT_PATH=/github/workflow/event.json
| GITHUB_WORKFLOW=Debug
| GITHUB_RUN_ID=1
| GITHUB_RUN_NUMBER=1
| GITHUB_ACTION=0
| GITHUB_REPOSITORY=actions-book/action-test
| INPUT_JQ_SELECTOR=.pull_request
| GITHUB_ACTIONS=true
| GITHUB_WORKSPACE=/github/workspace
| GITHUB_SHA=8a55fc0886ab90ca9b3735b89bc57c63bce5817c
| GITHUB_REF=refs/heads/master
| GITHUB_ACTOR=nektos/act
| GITHUB_EVENT_NAME=pull_request
| ---
| Event:
| null
Event
act -e
event.json
{
"pull_request": {
"example": true
}
}
-e
| Debug information:
| GITHUB_TOKEN=
| GITHUB_EVENT_PATH=/github/workflow/event.json
| GITHUB_WORKFLOW=Debug
| GITHUB_RUN_ID=1
| GITHUB_ACTION=0
| GITHUB_RUN_NUMBER=1
| GITHUB_REPOSITORY=actions-book/action-test
| INPUT_JQ_SELECTOR=.pull_request
| GITHUB_ACTIONS=true
| GITHUB_WORKSPACE=/github/workspace
| GITHUB_SHA=8a55fc0886ab90ca9b3735b89bc57c63bce5817c
| GITHUB_REF=refs/heads/master
| GITHUB_ACTOR=nektos/act
| GITHUB_EVENT_NAME=pull_request
| ---
| Event:
| {
| "example": true
| }
event.json act
pull_request
sender pull_request
mergeable requested_reviewers
pull_request-opened.json
act
| Debug information:
| GITHUB_TOKEN=
| GITHUB_EVENT_PATH=/github/workflow/event.json
| GITHUB_WORKFLOW=Debug
| GITHUB_RUN_ID=1
| GITHUB_RUN_NUMBER=1
| GITHUB_ACTION=0
| GITHUB_REPOSITORY=actions-book/action-test
| INPUT_JQ_SELECTOR=.pull_request
| GITHUB_ACTIONS=true
| GITHUB_WORKSPACE=/github/workspace
| GITHUB_SHA=8a55fc0886ab90ca9b3735b89bc57c63bce5817c
| GITHUB_REF=refs/heads/master
| GITHUB_ACTOR=nektos/act
| GITHUB_EVENT_NAME=pull_request
| ---
| Event:
| {
| "action": "opened",
| "number": 11,
| "pull_request": {
| ...
| "html_url": "https://github.com/mheap/action-test/pull/11",
| "id": 413144686,
| "issue_url": "https://api.github.com/repos/mheap/action-test/issues/11",
| "labels": [],
| "locked": false,
| "maintainer_can_modify": true,
| "merge_commit_sha": null,
| ...
| }
| }
secrets
.github/workflows/pull_request-debug.yml
${{ secrets.SECRET_NAME }}
- uses: mheap/github-action-explore-debug@master
with:
jq_selector: ".pull_request"
example: ${{ secrets.EXAMPLE }}
EXAMPLE
INPUT_EXAMPLE
| Debug information:
| INPUT_EXAMPLE=
act -s
| Debug information:
| INPUT_EXAMPLE=***
act
act
act -e event.json pull_request -s EXAMPLE=Hello
GITHUB_TOKEN GITHUB_TOKEN
GITHUB_TOKEN
GITHUB_TOKEN
GITHUB_TOKEN
v1
- uses: mheap/github-action-explore-debug@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
jq_selector: ".pull_request"
github-action-explore-debug
act
act
act
act
.
README.md
event.json
github-action-explore-debug
Dockerfile
action.yml
entrypoint.sh
pull_request-opened.json
action-test
pull_request-debug.yml
- uses: mheap/github-action-explore-debug@master
- uses: ./github-action-explore-debug
act
pull_request-debug.yml
act
github-action-explore-debug
- uses: ./
github-action-explore-debug
act -W
action-test
action-test
github-action-explore-debug
-W
act
GITHUB_TOKEN
act
docker
Dockerfile
action.yml entrypoint.sh
github-action-pull-request-milestone-bash
action-test
action.yml
docker
name: Pull Request Milestone
description: Congratulate people when they hit a certain number of merged pull requests
runs:
using: docker
image: Dockerfile
docker
Dockerfile alpine
jq event.json curl
bash
FROM alpine
ENTRYPOINT ["/entrypoint.sh"]
entrypoint.sh
#!/bin/bash
chmod +x entrypoint.sh
github-action-pull-request-milestone-bash
YOUR_USERNAME
git init
git add .
git commit -m "Initial Commit"
git remote add origin git@github.com:YOUR_USERNAME/github-action-pull-request-milestone-
bash.git
git push -u origin master
action-test
action-test/.github/workflows/pull_request-merged.yml
uses
name: Pull Request Milestone
on:
pull_request:
types: [closed]
jobs:
milestone:
runs-on: ubuntu-18.04
steps:
- uses: YOUR_USERNAME/github-action-pull-request-milestone-bash@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN
act
act -j
milestone
action-test
act -j milestone pull_request
action.yml
pull_request
GITHUB_EVENT_NAME
jq
.action closed
entrypoint.sh
#!/bin/bash
0
1
1
merged
closed pull_request.merged
entrypoint.sh
entrypoint.sh
event.json
act
pull_request-opened.json
action-test pull_request-closed.json
YOUR_USERNAME
{
"action": "closed",
"pull_request": {
"number": 1,
"merged": true,
"user": {
"login": "YOUR_USERNAME"
}
}
}
act
act -j milestone -e pull_request-closed.json pull_request action-test
act
true false closed opened
pull_request-closed.json act
GITHUB_TOKEN
act
repo.public_repo
Authorization token
merged
closed
GET /repos/:owner/:repo/pulls
state
GITHUB_REPOSITORY
YOUR_USERNAME/action-test
:owner :repo GET
curl
-s curl
-S
-s
per_page
&per_page=100 curl
Link rel="next"
while sed
rel="next:
entrypoint.sh
$PULLS
PULLS=""
URL="https://api.github.com/repos/$GITHUB_REPOSITORY/pulls?state=closed&per_page=100"
while [ "$URL" ]; do
RESP=$(curl -i -Ss -H "Authorization: token $GITHUB_TOKEN" "$URL")
HEADERS=$(echo "$RESP" | sed '/^\r$/q')
URL=$(echo "$HEADERS" | sed -n -E 's/Link:.*<(.*?)>; rel="next".*/\1/p')
PULLS="$PULLS $(echo "$RESP" | sed '1,/^\r$/d')"
done
event.json event.json
merged true
merged_at null
merged
.pull_request.user.login event.json
entrypoint.sh PR_AUTHOR
jq merged_at
null PR_AUTHOR
wc -l
wc
tr
wc -l
jq -c compressed jq
$PR_AUTHOR
with
entrypoint.sh
$MERGED_COUNT
INPUT_MERGED_
entrypoint.sh $COMMENT
COMMENT_VAR="INPUT_MERGED_${MERGED_COUNT}"
COMMENT=${!COMMENT_VAR}
POST /repos/:owner/:repo/issues/:issue_number/comments
body
jq
jq
entrypoint.sh
POSTBODY=$(echo $COMMENT | jq -c -R '. | {"body": .}')
-d
$COMMENT_ADDED
201 Created
POST
POST /repos/:owner/:repo/issues/:issue_number/labels
:owner :repo :issue_number
POST
merge-milestone
merge-milestone:$MERGED_COUNT
{
"labels": ["merge-milestone", "merge-milestone:$MERGED_COUNT"]
}
200 OK
201 Created
# Add labels
LABELS='{"labels":["merge-milestone","merge-milestone:'$MERGED_COUNT'"]}'
LABELS_ADDED=$(curl -i -Ss -H "Authorization: token $GITHUB_TOKEN"
"https://api.github.com/repos/$GITHUB_REPOSITORY/issues/$ISSUE_NUMBER/labels" -d $LABELS)
is:closed label:merge-milestone
INPUT_MERGED_
pull_request-merged.yml
action-test
with
- uses: YOUR_USERNAME/github-action-pull-request-milestone-bash@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
merged_1: "Your first PR! We're glad to have you on board"
merged_2: "Two in a row? Thanks for coming back to us"
merged_5: "5? FIVE!? Do you work here or something? Thanks for all the contributions"
merged_13: "Unlucky for some? Not for us! That's 13 PRs merged, keep it up!"
merged_50: "That's half a century for you. Do the same again and let's see what happens!"
merged_100: "100 merged PRs? You're the best. Raise an issue and we'll send you some
swag"
INPUT_MERGED_50
INPUT_MERGED_ entrypoint.sh
event.json
pull_request-merged.yml
GITHUB_TOKEN PAT
PAT
PAT
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.PAT }}
pull_request-merged.yml action-test
entrypoint.sh
github-action-pull-request-milestone-bash master
#!/bin/bash
PULLS=""
URL="https://api.github.com/repos/$GITHUB_REPOSITORY/pulls?state=closed&per_page=100"
while [ "$URL" ]; do
RESP=$(curl -i -Ss -H "Authorization: token $GITHUB_TOKEN" "$URL")
HEADERS=$(echo "$RESP" | sed '/^\r$/q')
URL=$(echo "$HEADERS" | sed -n -E 's/Link:.*<(.*?)>; rel="next".*/\1/p')
PULLS="$PULLS $(echo "$RESP" | sed '1,/^\r$/d')"
done
COMMENT_VAR="INPUT_MERGED_${MERGED_COUNT}"
COMMENT=${!COMMENT_VAR}
# Add labels
LABELS='{"labels":["merge-milestone","merge-milestone:'$MERGED_COUNT'"]}'
LABELS_ADDED=$(curl -i -Ss -H "Authorization: token $GITHUB_TOKEN"
"https://api.github.com/repos/$GITHUB_REPOSITORY/issues/$ISSUE_NUMBER/labels" -d $LABELS)
Node
docker
node12 docker
checkout
cache setup-* setup-java
@actions
@actions/core
$PATH
// This will read INPUT_INPUTNAME from the environment, and error if it's missing
core.getInput('inputName', { required: true });
// This will set an output that can be used in future workflow steps
// Equivalent to echo "::set-output name=outputKey::outputVal" in Bash
core.setOutput('outputKey', 'outputVal');
// Sets the exit code of the action to 1 (Failure) but does *NOT* stop Action execution
core.setFailed(`Action failed with error ${err}`);
@actions/core
@actions/github
context owner
repo
// You can read these values from context.repo which reads from
// process.env.GITHUB_REPOSITORY
const newIssue = await octokit.issues.createComment({
...context.repo,
issue_number: 123,
body: "Congrats! This is your first pull request"
});
// In fact, it can even automatically populate the issue number by detecting if you're
// in an Issue or a Pull Request and reading the correct key in the event payload
const newIssue = await octokit.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: "Congrats! This is your first pull request"
});
@actions/github
@actions/github
@actions/exec
@actions/exec
@actions/artifact
// Upload
const files = [
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
];
const rootDirectory = '/home/user/files/plz-upload';
@actions/artifact
@actions/cache
if (!cacheKey) {
// Really expensive calculation
await writePrimesToFile('prime-numbers');
}
@actions/glob
await io.mkdirP('my/backup');
await io.cp('app/important/folder', 'my/backup', { recursive: true });
await io.rmRF('app');
@actions/tool-cache
@actions/tool-cache
RUNNER_TOOL_CACHE
setup-* @actions/tool-cache
@actions
github-action-pull-request-milestone-node
action-test
action.yml
docker
Dockerfile alpine
node:alpine
npm ci
FROM node:alpine
COPY package*.json ./
RUN npm ci
COPY . .
ENTRYPOINT ["node", "/index.js"]
index.js
github-action-pull-request-milestone-node
git init
npx gitignore node
git add .
git commit -m "Initial Commit"
git remote add origin git@github.com:YOUR_USERNAME/github-action-pull-request-milestone-
node.git
git push -u origin master
action-test
.github/workflows/pull_request-merged.yml
uses
name: Pull Request Milestone
on:
pull_request:
types: [closed]
jobs:
milestone:
runs-on: ubuntu-18.04
steps:
- uses: YOUR_USERNAME/github-action-pull-request-milestone-node@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
merged_1: "Your first PR! We're glad to have you onboard"
merged_2: "Two in a row? Thanks for coming back to us"
merged_5: "5? FIVE!? Do you work here or something? Thanks for all the
contributions"
merged_13: "Unlucky for some? Not for us! That's 13 PRs merged, keep it up!"
merged_50: "That's half a century for you. Do the same again and let's see what
happens!"
merged_100: "100 merged PRs? You're the best. Raise an issue and we'll send you
some swag"
act
action-test
act -j milestone pull_request
node:alpine
JavaScript npm
npm @actions
github-action-pull-request-milestone-node
npm install @actions/core @actions/github common-tags --save
console.log
index.js
module.exports = action;
module.exports
async
async/await
action
action()
require(process.env.GITHUB_EVENT_PATH)
merged
merged
pull_request.merged
pull_request.closed
setFailed 0
action()
if (!payload.pull_request.merged) {
core.warning("Pull request closed without merge");
return;
}
index.js
package.json package-lock.json
act
pull_request-closed.json action-test
YOUR_USERNAME
{
"action": "closed",
"pull_request": {
"number": 1,
"merged": true,
"user": {
"login": "YOUR_USERNAME"
}
}
}
action-test
act -j milestone -e pull_request-closed.json pull_request
true false
closed opened pull_request-closed.json act
GITHUB_TOKEN
act
repo.public_repo
@actions/github
getOctokit action()
process.env.GITHUB_TOKEN
octokit.pulls.list
state
Link
Link
action()
let pulls = await octokit.paginate(
"GET /repos/:owner/:repo/pulls",
{
...github.context.repo,
state: "closed",
per_page: 100,
},
(response) => response.data
);
octokit.paginate Link
response.data
octokit.pulls.list
@actions/github
...github.context.repo
github.context
owner repo
github.context.repo
...
github.context.repo
github.context.issue
merged
merged_at null
true
pulls
pull_request-closed.json
pull_request.user.login act
act
-s GITHUB_TOKEN act
GITHUB_TOKEN
GITHUB_TOKEN,
merged_X X
@actions/core
github.context
issues
octokit.issues.createComment
owner repo issue_number body
action()
await octokit.issues.createComment({
...github.context.repo,
issue_number: github.context.issue.number,
body: message,
});
console.log(stripIndent`
Added comment:
${message}
`);
octokit.issues.addLabels
await octokit.issues.addLabels({
...github.context.repo,
issue_number: github.context.issue.number,
labels: [`merge-milestone`, `merge-milestone:${pullCount}`],
});
merge-milestone
if (!payload.pull_request.merged) {
core.warning("Pull request closed without merge");
return;
}
await octokit.issues.createComment({
...github.context.repo,
issue_number: github.context.issue.number,
body: message,
});
console.log(stripIndent`
Added comment:
${message}
`);
await octokit.issues.addLabels({
...github.context.repo,
issue_number: github.context.issue.number,
labels: [`merge-milestone`, `merge-milestone:${pullCount}`],
});
}
module.exports = action;
entrypoint.sh index.js
index.js
npm
node12
docker
context
action-guard
action-test
npx actions-toolkit@5 github-action-pull-request-milestone-toolkit
actions-toolkit
branding action.yml
.
Dockerfile
action.yml
index.js
index.test.js
node_modules
package-lock.json
package.json
github-action-pull-request-milestone-toolkit
git init
npx gitignore node
git add .
git commit -m "Initial Commit"
git remote add origin git@github.com:YOUR_USERNAME/github-action-pull-request-milestone-
toolkit.git
git push -u origin master
action-test
.github/workflows/pull_request-merged.yml
YOUR_USERNAME/github-action-pull-request-milestone-node@master
YOUR_USERNAME/github-action-pull-request-milestone-toolkit@master
act action-test
act -j milestone pull_request
act node:slim
Dockerfile
index.js
async Toolkit.run
async/await
action-guard
action-guard event
Toolkit.run action-guard
github-action-pull-request-milestone-toolkit action-guard
action-guard
pull_request
closed merged merged
tools.context
tools.log.warn
return tools.exit.success("We did it!");
if (!payload.pull_request.merged) {
tools.log.warn("Pull request closed without merge");
return;
}
tools.log Signale
debug
info warn error start complete
await success
github.getOctokit()
GITHUB_TOKEN
Octokit
tools.github
Octokit
Context tools.context
github.context
require
octokit.pulls.list
github.Github Octokit
pulls.list
merged_at
Toolkit.run
act
action-test
pull_request-closed.json
Octokit
tools.inputs
// Using @actions/core
const message = core.getInput(`merged_${pullCount}`);
Toolkit.run
github.Github
tools.github octokit tools.context
github.context tools.context.issue
tools.context.repo tools.context.repo
issue_number Issue
tools.log.pending tools.log.complete
Toolkit.run
tools.log.pending(`Adding comment`);
await tools.github.issues.createComment({
...tools.context.issue,
body: message,
});
tools.log.complete(`Added comment: ${message}`);
pending complete
tools.log.pending(`Adding labels`);
const labels = [`merge-milestone`, `merge-milestone:${pullCount}`];
await tools.github.issues.addLabels({
...tools.context.issue,
labels,
});
tools.log.complete(`Added labels:`, labels);
pending complete
if (!payload.pull_request.merged) {
tools.log.warn("Pull request closed without merge");
return;
}
tools.log.pending(`Fetching pulls`);
let pulls = await tools.github.paginate(
tools.github.pulls.list,
{
...tools.context.repo,
state: "closed",
per_page: 100,
},
(response) => response.data
);
tools.log.complete(`Fetched pulls`);
tools.log.pending(`Adding comment`);
await tools.github.issues.createComment({
...tools.context.issue,
body: message,
});
tools.log.complete(`Added comment: ${message}`);
tools.log.pending(`Adding labels`);
const labels = [`merge-milestone`, `merge-milestone:${pullCount}`];
await tools.github.issues.addLabels({
...tools.context.issue,
labels,
});
tools.log.complete(`Added labels:`, labels);
});
act
index.test.js
index.test.js
index.js
jest
pull_request
pull_request closed
pull_request
github-action-pull-request-milestone-toolkit
npm test
warning There are environment variables missing from this runtime, but would be present
on GitHub.
- GITHUB_WORKFLOW
- GITHUB_ACTION
- GITHUB_ACTOR
- GITHUB_REPOSITORY
- GITHUB_EVENT_NAME
- GITHUB_EVENT_PATH
- GITHUB_WORKSPACE
- GITHUB_SHA
index.test.js
describe
process.env.GITHUB_WORKFLOW = "demo-workflow";
process.env.GITHUB_ACTION = "pull-request-milestone";
process.env.GITHUB_ACTOR = "YOUR_USERNAME";
process.env.GITHUB_REPOSITORY = "YOUR_USERNAME/action-test";
process.env.GITHUB_WORKSPACE = "/tmp/github/workspace";
process.env.GITHUB_SHA = "fake-sha-a1c85481edd2ea7d19052874ea3743caa8f1bdf6";
process.env.INPUT_MERGED_3 = "This message is added after 3 PRs are merged";
GITHUB_EVENT_NAME GITHUB_EVENT_PATH
GITHUB_EVENT_NAME GITHUB_EVENT_PATH
beforeEach
beforeEach
index.test.js
describe
it('exits successfully')
jest it test
describe/it
GITHUB_EVENT_NAME
GITHUB_EVENT_PATH mockEvent
GITHUB_EVENT_PATH
issues-created
issue-created.json
issues-created.json
{} index.test.js
expect(action(tools))
index.js async
jest
expect().resolves expect().rejects
expect(action(tools)).rejects
Error toThrow
.toThrow(
new Error("Invalid event. Expected 'pull_request', got 'issues'")
);
PASS ./index.test.js
Pull Request Milestone
exits when triggered by the wrong event (14ms)
issues-created.json
pull_request closed
it
it("fails when triggered by the correct event but the wrong action", () => {
tools = mockEvent("pull_request", "pull_request-opened");
});
pull_request-opened
pull_request-opened.json issues-created.json
{"action":"opened"}
index.test.js
it("fails when triggered by the correct event but the wrong action", () => {
tools = mockEvent("pull_request", "pull_request-opened");
return expect(action(tools)).rejects.toThrow(
new Error(
"Invalid event. Expected 'pull_request.closed', got 'pull_request.opened'"
)
);
});
npm test
pull_request-opened.json
GITHUB_EVENT_PATH
require
process.env.GITHUB_EVENT_NAME = name;
process.env.GITHUB_EVENT_PATH = "/github/workspace/event.json";
jest.mock() beforeEach
require(".") it()
beforeEach(() => {
jest.resetModules();
});
issues-created.json
pull_request-opened.json
mockEvent
// And in "it fails when triggered by the correct event but the wrong action"
// Replace:
tools = mockEvent("pull_request", "pull_request-opened");
// With:
tools = mockEvent("pull_request", { action: "opened" });
npm test
expect(action(tools)).resolves async/await
it async
pull_request
tools.log.warn = jest.fn();
await action(tools);
expect(tools.log.warn).toBeCalledWith("Pull request closed without merge");
jest.fn()
tools.log.warn = jest.fn();
await action(tools);
expect(tools.log.warn).toBeCalledWith("Pull request closed without merge");
});
nock
nock
nock
disableNetConnect
nock
await action(tools);
npm test
tools.log.debug tools.log.info
tools.log.debug = jest.fn();
tools.log.info = jest.fn();
await action(tools);
expect(tools.log.debug).toBeCalledWith("There are 0 Pull Requests");
expect(tools.log.info).toBeCalledWith("No action required");
tools.log.debug = jest.fn();
tools.log.info = jest.fn();
await action(tools);
expect(tools.log.debug).toBeCalledWith("There are 0 Pull Requests");
expect(tools.log.info).toBeCalledWith("No action required");
});
pull_request.user.login
reply
tools.log.debug
tools.log.debug
There are 2 Pull Requests
.reply(200, [
{ merged_at: "2020-04-27T21:21:49Z", user: { login: "example-user" } },
{ merged_at: "2020-04-28T22:53:53Z", user: { login: "non-matching" } },
{ merged_at: "2020-04-29T23:48:22Z", user: { login: "example-user" } },
{ merged_at: null, user: { login: "example-user" } },
{ merged_at: "2020-04-30T00:11:24Z", user: { login: "non-matching" } },
])
/pulls
example-user
nock
.reply(200, [
{ merged_at: "2020-04-27T21:21:49Z", user: { login: "example-user" } },
{ merged_at: "2020-04-28T22:53:53Z", user: { login: "example-user" } },
{ merged_at: "2020-04-29T23:48:22Z", user: { login: "example-user" } },
])
tools = mockEvent("pull_request", {
action: "closed",
pull_request: {
number: 18,
merged: true,
user: { login: "example-user" },
},
});
number
process.env.INPUT_MERGED_3
npm test
nock
getPrMock
POST GET
.post()
nock
200
nock
POST
addCommentMock
expect()
await action(tools);
});
pending complete
debug
tools.log.pending = jest.fn();
npm test -- --collect-coverage
jest
.reply(200) .reply(500)
tools.github.* try/catch
INPUT_MERGED_10
docker node12
docker
docker
docker npm ci
alpine bash
node:slim
node12
uses: YOUR_USERNAME/repo@master
uses: docker://<YOUR_DOCKERHUB_USERNAME>/my-action:latest.
docker
Docker
index.js
peter-evans/create-pull-request
actions/toolkit
using.main
node_modules
npm install npm ci
node_modules
node_modules
dist/index.js vercel/ncc
ncc
index.js
docker node12 docker
node12
docker
node12
mheap/github-action-auto-compile-node@master
name: Auto-Compile
on:
release:
types: [published]
jobs:
compile:
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Automatically build action
uses: mheap/github-action-auto-compile-node@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
github-action-auto-compile-node
npm ci
ncc build
docker
node12
github-action-auto-compile-node
ncc node12
build-and-tag-action
build-and-tag-action
docker
node12
index.js
node12
docker github-action-auto-compile-node
node12
branding
action.yml
branding:
icon: stop-circle
color: red
white yellow blue green orange red purple
gray-dark
airplay zap
filter key package
haya14busa
LICENSE
MIT MIT
LICENSE
LICENSE
LICENSE README.md
README.md
README.md
on
inputs
README.md
# github-action-pull-request-milestone
This action runs whenever a pull request is merged. If merging that pull request helps the
author hit one of our predefined milestones (e.g. 1, 5, 10, 100 PRs merged), then a comment
will be added congratulating them, and a label will be added so that we can find it again
later.
## Usage
This action is intended to run whenever a `pull_request` is `closed`. If any other event
triggers it an error will be returned.
Example workflow:
````yaml
name: Pull Request Milestone
on:
pull_request:
types: [closed]
jobs:
milestone:
runs-on: ubuntu-18.04
steps:
- uses: YOUR_NAME/github-action-pull-request-milestone@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
merged_1: "Your first PR! We're glad to have you onboard"
merged_2: "Two in a row? Thanks for coming back to us"
merged_5: "5? FIVE!? Do you work here or something? Thanks for all the
contributions"
````
## Configuration
This action uses the `merged_` inputs to control if a milestone has been hit or not. If you
would like to add a new comment after 33 pull requests, define the `merged_33` input, e.g.:
````
- uses: YOUR_NAME/github-action-pull-request-milestone@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
merged_33: "Lucky number...33?"
````
If you'd like a weekly summary of all of the milestones hit each week, consider installing
YOUR_NAME/github-action-milestone-summary.
README.md
inputs
LICENSE README.md
action.yml
Draft a release
action.yml README
pull-request-milestone
v1
awesome-actions
awesome-actions
LICENSE
README.md
schedule
merge-milestone
schedule
schedule cron
cron
* * * * *
0 1 * * * cron
cron 0 3 * * 1
action-test
.github/workflows/pull_request-summary.yml
schedule
YOUR_USERNAME/github-action-milestone-summary
GITHUB_TOKEN
since
P1D PT3H
P1Y2M4DT20H44M12.67S
action-test
action-guard
schedule
github-action-milestone-summary
npx actions-toolkit
github-action-pull-request-milestone-toolkit
iso8601-duration
npm
iso8601-duration
index.js
tools.github.paginate
since
updated created
tools.github.paginate
done
earliestDate done
index.js earliestDate
merge-milestone
.filter()
Toolkit.run
.find()
pulls
merge-milestone
merge-milestone:
if (label) {
milestones[label.name] = milestones[label.name] || [];
milestones[label.name].push(pr);
}
}
milestones
{
"merge-milestone:5": [<pr>, <pr>],
"merge-milestone:10": [<pr>],
}
milestones
body title
tools.context.repo
// Create an issue
await tools.github.issues.create({
...tools.context.repo,
title: "Milestone Update",
body,
});
tools.exit.success("Report created");
if (label) {
milestones[label.name] = milestones[label.name] || [];
milestones[label.name].push(pr);
}
}
// Create an issue
await tools.github.issues.create({
...tools.context.repo,
title: "Milestone Update",
body,
});
tools.exit.success("Report created");
});
since action-test
action.yml
act
github-action-milestone-summary
git
git init
npx gitignore node
git add .
git commit -m "Initial Commit"
git remote add origin git@github.com:YOUR_USERNAME/github-action-milestone-summary.git
git push -u origin master
action-test
schedule
act -j schedule
event.json
action.yml
GITHUB_TOKEN
master
master GITHUB_TOKEN
push
PAT
GITHUB_TOKEN
GITHUB_TOKEN
- uses: YOUR_USERNAME/action-name@master
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
repository_dispatch
POST
/repos/{owner}/{repo}/dispatches
client_payload context.payload
curl repository_dispatch
curl
POST
repository_dispatch
workflow_dispatch
inputs
action.yml
master
master
1.2.3 1
2 3
YOUR_USERNAME/action-name@v1 v1
master
@v1 v1.1.0
action-tagger
v1.1.0
@v1
# https://github.com/marketplace/actions/actions-tagger
name: Keep the versions up-to-date
on:
release:
types: [published, edited]
jobs:
actions-tagger:
runs-on: ubuntu-latest
steps:
- uses: Actions-R-Us/actions-tagger@latest
env:
GITHUB_TOKEN: "${{secrets.GITHUB_TOKEN}}"
github-action-auto-compile-node
docker node12
build-and-tag
build-and-tag github-action-auto-compile-node
npm run build package.json
setup
# https://github.com/marketplace/actions/build-and-tag
- uses: JasonEtco/build-and-tag-action@v1
with:
setup: 'npm ci && npm run custom-build'
@v1
@master
@master @v1
@master
pin-github-action
steps:
- uses: actions/checkout@v2
- uses: YOUR_USERNAME/custom-action@master
pin-github-action /path/to/.github/workflows/your-name.yml
steps:
- uses: actions/checkout@db41740e12847bb616a339b75eb9414e711417df # pin@v2
- uses: YOUR_USERNAME/custom-action@73549280c1c566830040d9a01fe9050dae6a3036 # pin@master
pin-github-action
@v2 @master
master
branches push
on:
push:
branches:
- master
push
master
on.push.branches
jobs.<job_id>.steps.if
actions/checkout actions/setup-node
npm test push npm publish master
if Release
if
- uses: action-that/requires-secrets@master
if: github.repository == 'YOUR_USERNAME/repo'
if
- uses: windows/specific-action@master
if: startsWith(matrix.os, 'windows')
- uses: unix/specific-action@master
if: !startsWith(matrix.os, 'windows')
- uses: my/action@master
if: github.event_name == 'pull_request' && github.event.action == 'labeled'
if
steps if
runs-on if
merged
closed
pullRequests
MERGED
query MergedPullRequests {
repository(owner: "YOUR_USERNAME", name: "action-test") {
pullRequests(first: 100, states: MERGED) {
totalCount
pageInfo {
hasNextPage
}
edges {
cursor
}
nodes {
number
author {
login
}
}
}
}
}
Link
pageInfo.hasNextPage
pullRequests(first:100, states: MERGED, after: <cursor>)
edges.cursor
search
query searchRepos {
search(query: "repo:YOUR_USERNAME/action-test type:pr is:merged author:YOUR_USERNAME",
type: ISSUE, first: 100) {
pageInfo {
hasNextPage
}
edges {
cursor
}
nodes {
... on PullRequest {
number
}
}
}
}
ISSUE
Link
octokit.graphql
const query = `
query($searchQuery: String!, $after: String) {
search(query: $searchQuery, type: ISSUE, first: 100, after: $after) {
pageInfo {
hasNextPage
endCursor
}
nodes {
... on PullRequest {
number
}
}
}
}
`;
let prCount = 0;
let after = null;
let results;
do {
results = await octokit.graphql(query, {
searchQuery: "repo:YOUR_USER/action-test type:pr is:merged author:ACTOR_NAME",
after,
});
after = results.search.pageInfo.endCursor;
prCount += results.search.nodes.length;
} while (results.search.pageInfo.hasNextPage);
octokit.paginate do..while
octokit.pulls.list
octokit.pulls.list
mheap/github-action-auto-compile-node@master
docker
node12
action.yml
docker dockerhub
dockerhub
docker build
master
v1.0.4 v1 v1.0
v1.0.4 docker
docker://
Dockerfile
FROM username/action:latest
Dockerfile username/action
dockerhub docker://
docker build
Dockerfile YOUR_USER/action@v1
docker://
docker
docker docker
peter-evans/create-pull-request create-pull-request
docker
inputs
create_pull_request.py @actions/exec
actions/github-script
on:
issues:
types: [opened]
jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v2
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: ' Thanks for reporting!'
})
actions/github-script
actions/github-script
octokit/request-action
request-action
github-script
- uses: octokit/request-action@v2.x
with:
route: POST /repos/:owner/:repo/issues/:issue_number/comments
owner: ${{ github.event.issue.owner }}
repository: ${{ github.event.issue.repository }}
issue_number: ${{ github.event.issue.number }}
body: ' Thanks for reporting!'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
route
github.issues.createComment()
actions/github-script
octokit/request-action
${{ secrets.GITHUB_TOKEN }}
secrets
- uses: YOUR_USERNAME/action
with:
url: https://api.example.com/create-foobit
- uses: YOUR_USERNAME/action
with:
url: https://api.example.com/create-foobit
username: admin
password: password123
username password
secrets
- uses: YOUR_USERNAME/action
with:
url: https://api.example.com/create-foobit
username: ${{ secrets.API_USERNAME }}
password: ${{ secrets.API_PASSWORD }}
Settings->Secrets API_USERNAME
Add secret
repository_dispatch workflow_dispatch
if
- uses: YOUR_USERNAME/action
with:
url: https://api.example.com/create-foobit
username: ${{ secrets.API_USERNAME }}
password: ${{ secrets.API_PASSWORD }}
if: github.repository == 'YOUR_USERNAME/repo'
action/github-script octokit/request
docker
act