Github Action - 製作高重複利用的 CI Script(Image) for beta

本文已經不合時宜,請參考 Github Action - 更換成 beta v2 新的格式 (yaml)

本文使用 HCL syntax in GitHub Actions 將會在 2019-09-30 被棄用。(for info)

大家在開發 CI 的時候,一定會遇到需要寫 script,無論是 Shell Script, Python, Makefile,多少都會用到。那麼當你在第二個、第三個專案也需要一樣的流程,那我們就會從舊的專案複製一樣的 script 到新專案。

這個時候,如果發現 script 寫錯了,或是要擴充時。那麼你有數十個專案,都要一一進去每個專案改動這個 CI script,是不是想到就頭痛?

那麼你可以開始嘗試 Github Action,Github Action 非常符合 DRY(Don’t Repeat Yourself) 的原則,只要是 public repo 的 Dockerfile,那麼你就可以使用它,這等於是可以將 CI script 模組化,並重複利用。

你甚至可以在 local 使用,當作是 Jenkins, Ansible 的替代品

Talk Is Cheap. Show Me The Code

Sample code: https://github.com/swaglive/action-demo/tree/master/.github

workflow "Show env" {
on = "push"
resolves = ["debug","hello"]
}

action "debug" {
uses = "docker://alpine:3.10"
args = ["env"]
}

action "hello" {
needs = "debug"
uses = "docker://alpine:3.10"
args = ["echo", "hello"]

env = {
DOCKER_REGISTRY_URL = "docker.pkg.github.com"
IMAGE = "echo-box:latest"
}
secrets = ["DOCKER_USERNAME", "DOCKER_PASSWORD"]
}

如果你還沒有辦法使用 Github Action 的話,那麼你可以先在 Local 安裝 act,在 Local 感受一下 Github Action 的威力。

2019.08.01 Github Action 還是 beta 版,需要額外註冊才能使用

Enable:

Disable:

Act - Local Run Your Github Action Workflow

brew tap nektos/tap && brew install nektos/tap/act

More Configs

Use

https://developer.github.com/actions/managing-workflows/workflow-configuration-options/#using-a-dockerfile-image-in-an-action
uses:

actions/heroku@master
actions/aws/ec2@v2.0.1
./.github/action/my-action
docker://alpine:3.8
docker://gcr.io/cloud-builders/gradle

@ branch, ref, SHA

Event

https://developer.github.com/actions/managing-workflows/workflow-configuration-options/#events-supported-in-workflow-files

push, pull_request, release

Env

https://developer.github.com/actions/creating-github-actions/accessing-the-runtime-environment/#environment-variables

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=8b0f1f43f889
GITHUB_ACTION=debug
GITHUB_ACTOR=RammusXu
GITHUB_BASE_REF=
GITHUB_EVENT_NAME=push
GITHUB_EVENT_PATH=/github/workflow/event.json
GITHUB_HEAD_REF=
GITHUB_REPOSITORY=swaglive/action-demo
GITHUB_SHA=1f96df855e94639a5c430d3564ac31a8f2a492c0
GITHUB_WORKFLOW=Show env
GITHUB_WORKSPACE=/github/workspace
HOME=/github/home
GITHUB_REF=refs/heads/master

Dockerfile Example

https://developer.github.com/actions/creating-github-actions/creating-a-docker-container/#label

Label:

FROM alpine:3.10

LABEL "name"="echo-box"
LABEL "maintainer"="Rammus Xu"
LABEL "version"="1.0.0"

LABEL "com.github.actions.name"="Echo Box"
LABEL "com.github.actions.description"="Print debugging information about the running action"
LABEL "com.github.actions.icon"="package"
LABEL "com.github.actions.color"="green"


COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

Push Docker Image to Github Registry(Package)

Tutorial: https://help.github.com/en/articles/configuring-docker-for-use-with-github-package-registry


Behavior

workflow 可以有多個,但是只能放在 main.workflow

# Show env
workflow "Show env" {
on = "push"
resolves = ["debug","hello"]
}

action "debug" {
uses = "docker://alpine:3.10"
args = ["env"]
}

action "hello" {
needs = "debug"
uses = "docker://alpine:3.10"
args = ["echo", "hello"]
}

# Shared Workspace
workflow "Shared Workspace" {
on = "push"
resolves = ["cat file"]
}

action "create file" {
uses = "docker://alpine:3.10"
args = ["sh", "-c", "echo 'rammus file' > shared-file"]
}

action "cat file" {
needs = "create file"
uses = "docker://alpine:3.10"
args = ["sh", "-c", "cat shared-file"]
}

會 cache docker layer,所以第二次 run 或是相同的 image 會非常快。

# Docker CLI
# https://github.com/actions/docker/tree/master/cli
workflow "Docker CLI" {
on = "push"
resolves = ["docker cli run"]
}

action "docker cli build" {
uses = "actions/docker/cli@master"
args = "build -t echo-box:latest ./echo-box"
}

action "docker cli run" {
needs = ["docker cli build"]
uses = "actions/docker/cli@master"
args = "run echo-box:latest hello world"
}

同一個 Build(workflow) 當中,會共享 workspace,所以 action 1 如果做 docker login,action 2 能使用在同一個 workspace 下的 credential。

## Push Github Package
workflow "Push Github Package" {
on = "push"
resolves = ["docker push"]
}

action "docker login" {
uses = "actions/docker/login@master"
env = {
DOCKER_REGISTRY_URL = "docker.pkg.github.com"
}
secrets = ["DOCKER_USERNAME", "DOCKER_PASSWORD"]
}

action "docker build" {
needs = ["docker login"]
uses = "actions/docker/cli@master"
env = {
IMAGE = "echo-box:latest"
}
args = "build -t docker.pkg.github.com/$GITHUB_REPOSITORY/$IMAGE ./echo-box"
}

action "docker push" {
needs = ["docker build"]
uses = "actions/docker/cli@master"
env = {
IMAGE = "echo-box:latest"
}
args = "push docker.pkg.github.com/$GITHUB_REPOSITORY/$IMAGE"
}

某些 Docker 指令被鎖住 ex. network, image, volume,…

# Docker Info
workflow "Show Docker Info" {
on = "push"
resolves = ["show network","show images"]
}

action "show network" {
uses = "actions/docker/cli@master"
args = "network ls"
}

action "show images" {
uses = "actions/docker/cli@master"
args = "images"
}

Docker daemon 也會 share

可以同時 (parellel) 跑多個 Actions,跟 workflow 上限無關

缺點

  • 不能 manual trigger (re-run)
  • 不能及時看到 Container Log

Limit

Publish Github Action

Tutorial: https://developer.github.com/marketplace/actions/publishing-an-action-in-the-github-marketplace/

實際範例: https://github.com/marketplace/actions/facebook-notify

Reference