Pull to refresh
69.96
Слёрм
Учебный центр для тех, кто работает в IT

Жизнь действия GitHub Action

Reading time 5 min
Views 5.1K
Original author: Jessie Frazelle

Мне показалось, будет прикольно запилить пост под названием «Жизнь действия GitHub Action». На вводном обучении в Google тебя проводят через «Жизнь запроса», и у меня это был один из самых любимых элементов. Поэтому я использую аналогичный подход для GitHub Action.


Для тех, кто в танке, Actions — это функция GitHub, запущенная на конференции Universe в прошлом году. Хотите в бета-тестеры? Гоу сюда.


Общая идея — это GitHub с поддержкой сценариев, но страдать фигней и разливаться в объяснениях я не стану. Лучше проведу вас через события, которые происходят при запуске действия.


Проблема


Вот типичный рабочий процесс:


  • Создаю пулл-реквест в репозиторий.
  • Пулл-реквест поглощается.
  • Ветка сохраняется до скончания времен, съедая мне ту часть мозга, которая любит чистоту и порядок.


Остающиеся ветви — моя боль, сосредоточимся на ней. Хотя проблема-то общая, поэтому давайте создадим действие для удаления ветвей после поглощения пулл-реквеста.


Многабукаф? Весь код для действия — здесь.


Файл рабочего процесса


Хотите — создавайте действия через пользовательский интерфейс, а хотите — пишите файл рабочего процесса ручками. В этой статье я просто использую файл.


Вот как это выглядит, и я объясню, что все это означает, в комментариях к файлу. Он находится в .github/main.workflow в вашем репозитории.


workflow "on pull request merge, delete the branch" {
  ## On pull_request defines that whenever a pull request event is fired this 
  ## workflow will be run.
  on = "pull_request"

  ## What is the ending action (or set of actions) that we are running. 
  ## Since we can set what actions "need" in our definition of an action,
  ## we only care about the last actions run here.
  resolves = ["branch cleanup"]
}

## This is our action, you can have more than one but we just have this one for 
## our example.
## I named it branch cleanup, and since it is our last action run it matches 
## the name in the resolves section above.
action "branch cleanup" {
  ## Uses defines what we are running, you can point to a repository like below 
  ## OR you can define a docker image.
  uses = "jessfraz/branch-cleanup-action@master"

  ## We need a github token so that when we call the github api from our
  ## scripts in the above repository we can authenticate and have permission 
  ## to delete a branch.
  secrets = ["GITHUB_TOKEN"]
}

Событие


Итак, поскольку статья называется «Жизнь действия», то начнем с того, что за мракобесие творится. Все действия запускаются через событие GitHub. Список поддерживаемых событий — здесь.


Выше мы выбрали событие pull_request. Оно запускается при назначении пулл-реквеста, отмене назначения, пометке, снятии метки, открытии, редактировании, закрытии, повторном открытии, синхронизации, запросе на ревью пулл-реквеста или удалении пулл-реквеста.


Ладно, запустили мы это событие, и…


С пулл-реквестом «что-то» не так...

И тут GitHub такой: «Блинский блин, с пулл-реквестом что-то не так! Фигану-ка я из всех орудий по неполадкам!»


Глядя же на файл рабочего процесса (см.выше), GitHub говорит: «Я сейчас запущу рабочий процесс по поглощению пулл-реквеста, а ветвь удалю».


К чему это ведет? О, «очистка ветви». Давайте я упорядочу действия, необходимые для очистки ветви (в данном случае таковых нет) и запущу их по порядку/параллельно, чтобы прийти к «очистке ветви».


Действие


Здесь GitHub заявляет: «Йоу, народ, мне тут "очистку ветви" надо запустить. Дайте-ка разобраться».


Это возвращает нас к разделу uses нашего файла. Указываем на репозиторий: jessfraz/branch-cleanup-action@master.


В этом репозитории находится Dockerfile. Он определяет среду, в которой будет выполняться наше действие.


Dockerfile


Взглянем на него, а я постараюсь объяснить все объяснить в комментариях.


## FROM defines what Docker image we are starting at. A docker image is a bunch 
## of files combined in a tarball.
## This image is all the files we need for an Alpine OS environment.
FROM alpine:latest

## This label defines our action name, we could have named it butts but
## I decided to be an adult.
LABEL "com.github.actions.name"="Branch Cleanup"
## This label defines the description for our action.
LABEL "com.github.actions.description"="Delete the branch after a pull request has been merged"
## We can pick from a variety of icons for our action.
## The list of icons is here: https://developer.github.com/actions/creating-github-actions/creating-a-docker-container/#supported-feather-icons
LABEL "com.github.actions.icon"="activity"
## This is the color for the action icon that shows up in the UI when it's run.
LABEL "com.github.actions.color"="red"

## These are the packages we are installing. Since I just wrote a shitty bash 
## script for our Action we don't really need all that much. We need bash, 
## CA certificates and curl so we can send a request to the GitHub API
## and jq so I can easily muck with JSON from bash.
RUN apk add --no-cache \
    bash \
    ca-certificates \
    curl \
    jq

## Now I am going to copy my shitty bash script into the image.
COPY cleanup-pr-branch /usr/bin/cleanup-pr-branch

## The cmd for the container defines what arguments should be executed when 
## it is run.
## We are just going to call back to my shitty script.
CMD ["cleanup-pr-branch"]

Сценарий


Ниже приведено содержимое пробного сценария, который я выполняю.


#!/bin/bash
set -e
set -o pipefail

# This is populated by our secret from the Workflow file.
if [[ -z "$GITHUB_TOKEN" ]]; then
    echo "Set the GITHUB_TOKEN env variable."
    exit 1
fi

# This one is populated by GitHub for free :)
if [[ -z "$GITHUB_REPOSITORY" ]]; then
    echo "Set the GITHUB_REPOSITORY env variable."
    exit 1
fi

URI=https://api.github.com
API_VERSION=v3
API_HEADER="Accept: application/vnd.github.${API_VERSION}+json"
AUTH_HEADER="Authorization: token ${GITHUB_TOKEN}"

main(){
    # In every runtime environment for an Action you have the GITHUB_EVENT_PATH 
    # populated. This file holds the JSON data for the event that was triggered.
    # From that we can get the status of the pull request and if it was merged.
    # In this case we only care if it was closed and it was merged.
    action=$(jq --raw-output .action "$GITHUB_EVENT_PATH")
    merged=$(jq --raw-output .pull_request.merged "$GITHUB_EVENT_PATH")

    echo "DEBUG -> action: $action merged: $merged"

    if [[ "$action" == "closed" ]] && [[ "$merged" == "true" ]]; then
        # We only care about the closed event and if it was merged.
        # If so, delete the branch.
        ref=$(jq --raw-output .pull_request.head.ref "$GITHUB_EVENT_PATH")
        owner=$(jq --raw-output .pull_request.head.repo.owner.login "$GITHUB_EVENT_PATH")
        repo=$(jq --raw-output .pull_request.head.repo.name "$GITHUB_EVENT_PATH")
        default_branch=$(
            curl -XGET -sSL \
                -H "${AUTH_HEADER}" \
                -H "${API_HEADER}" \
                "${URI}/repos/${owner}/${repo}" | jq .default_branch
        )

        if [[ "$ref" == "$default_branch" ]]; then
            # Never delete the default branch.
            echo "Will not delete default branch (${default_branch}) for ${owner}/${repo}, exiting."
            exit 0
        fi

        echo "Deleting branch ref $ref for owner ${owner}/${repo}..."
        curl -XDELETE -sSL \
            -H "${AUTH_HEADER}" \
            -H "${API_HEADER}" \
            "${URI}/repos/${owner}/${repo}/git/refs/heads/${ref}"

        echo "Branch delete success!"
    fi
}

main "$@"

Таким образом, на данный момент GitHub выполнил наш сценарий в нашей среде выполнения.
GitHub сообщит статус действия в пользовательский интерфейс, и Вы сможете увидеть его во вкладке «Actions».


Надеюсь, это внесло некоторую ясность о том, как осуществляются процессы в GitHub Actions. Жду не дождусь посмотреть, что выйдет у вас.

Tags:
Hubs:
+15
Comments 2
Comments Comments 2

Articles

Information

Website
slurm.io
Registered
Founded
Employees
51–100 employees
Location
Россия
Representative
Антон Скобин