Github Actionsの使い方メモ

Github Actions をワークフロー内で使う方法についてすぐに見返すための備忘録
Github Action を作るためのメモはこちら => [Github Action の作り方メモ](https://qiita.com/HeRo/items/e2d5e3bc3dbe810f0482)

## ドキュメント

- [GitHub Actions について - GitHub Docs](https://docs.github.com/ja/actions/learn-github-actions)
    - [Workflow syntax for GitHub Actions - GitHub Docs](https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions)
- [Expressions - GitHub Docs](https://docs.github.com/ja/actions/learn-github-actions/expressions)
- [Contexts - GitHub Docs](https://docs.github.com/ja/actions/learn-github-actions/contexts)
- [使用制限、支払い、管理 - GitHub Docs](https://docs.github.com/ja/actions/learn-github-actions/usage-limits-billing-and-administration)
- [Environment variables - GitHub Docs](https://docs.github.com/ja/actions/learn-github-actions/environment-variables)


## ワークフローの定義

リポジトリに次のディレクトリを作成し、その中にYAML形式で定義する。

*.github/workflows/*

YAMLファイルは名前は自由。複数OK。

## ワークフローの構造

ワークフローを定義するYAMLファイルは次のような階層構造を持っている。

```
ワークフロー（YAMLファイル）
  └ jobs:
    └ ジョブ(名前は任意)
       └ steps:
         └ アクション
```

### ワークフロー
- *.github/workflows/* ディレクトリ以下に配置されるYAMLファイルが個別のワークフローとなる。
- 複数のワークフローを設置してもOK。
- ワークフローは並列で実行される。

### ジョブ
- 各ジョブは仮想環境の**新しいインスタンス**で実行される。
- したがって、ジョブ間で環境変数やファイル、セットアップ処理の結果などは共有されない。
- ジョブ間の依存を定義して待ち合わせることができる。
    - データの受け渡しが必要ならアーティファクト経由で。

### Step
- Jobが実行する処理の集合。
- 同じJobのStepは同じ仮想環境で実行されるのでファイルやセットアップ処理は共有できる。
- しかし各ステップは別プロセスなのでステップ内で定義した環境変数は共有できない。
    - `jobs.<job_id>.env`で定義した環境変数は全`Step` で利用できる

### アクション

- ワークフローの最小構成要素。
- 単に`run`でコマンドを実行することもできるし、Githubやサードパーティの公開アクションを利用(`use`)することもできる。
- コンテキストを利用して秘密情報を受け取ったりできる

## 仮想環境

Jobが実行される仮想環境のスペックは次の通り。
そんなにスペックは高くない。

- 2コアCPU
- 7 GBのRAMメモリー
- 14 GBのSSDディスク容量

Publicリポジトリなら無料。
Privateだと Linux仮想環境で *$0.008/min* かかる。

$0.008/min = $0.48/hour = 約52円/hour（$1=108.4円）

10分かかるビルドを実行すると約9円かかる。
Freeアカウントで2,000分/月 無料。

## Permission

リポジトリごとにどの Github Action を利用できるのか？ あるいは、Workflow中でリポジトリの読み書きを許可するかを設定できる。
設定はリポジトリの *Settings/Actions* にある。

### Actions permissions
リポジトリで Github Action を実行できるかどうかを設定する。
次の選択肢から選ぶ。

- *Allow all actions*: すべての Action の実行を許可
- *Disable Actions*: すべての Action の実行禁止
- *Allow local actions only*: リポジトリ内で定義した Action のみ実行を許可
- *Allow select actions*: 実行できる Action を選択する
    - *Allow actions created by GitHub*: Github製 Action のみ実行を許可
    - *Allow Marketplace actions by verified creators*: 認証された作者の Action のみ実行を許可
    - *Allow specified actions*: 設定した任意の Action のみ実行を許可

### Fork pull request workflows
プライベートリポジトリのみで設定できる項目。フォークされたプルリクエストからのワークフローの権限を設定でできる。
*Run workflows from fork pull requests* を有効にすると次の2項目を設定できる。

- *Send write tokens to workflows from fork pull requests*: 書き込める`GITHUB_TOKEN`を利用を許可する
- *Send secrets to workflows from fork pull requests*: すべてのシークレットを利用可能にする

### Workflow permissions
Workflow 内で実行される Action がリポジトリを操作することを許可するかどうかを設定する。
次の2つの選択肢から選び、その選択が `GITHUB_TOKEN` に与えられるデフォルトの権限となる。

- *Read and write permissions*: 読み書きを許可する。
    - ただし、フォークされたリポジトリからのPRでは読み込みのみに制限される
- *Read repository contents permission*: 読み込みのみ許可する

#### ワークフローの permissions 設定

*Read repository contents permission* を設定した場合、リポジトリのコンテンツ(コミットやIssue, PR)に対して書き込むような Action は実行時に権限エラーが発生する。

例えば、Lint系の Action ならば、検査結果でプルリクエストやコミットの状態を更新したり、プルリクエストににコメントを追加したりするものがあるが、それらができなくなる。

このような処理を許可するために Workflow 定義に `permissions` を設定することで許可してやることができる。
設定できる権限の種類は次の通り。

```yaml
permissions:
  actions: read|write|none
  checks: read|write|none
  contents: read|write|none
  deployments: read|write|none
  issues: read|write|none
  packages: read|write|none
  pull-requests: read|write|none
  repository-projects: read|write|none
  security-events: read|write|none
  statuses: read|write|none
```

上記のLint系 Action では `pull-requests` と `statuses` の `write` の許可が必要となるであろう。
*Read and write permissions*を選択した場合にはこれらの項目に一律`write` 権限が与えられるということ。

## コンカレンシー

同時に複数のジョブを実行されないように制限することもできる。
例えば、 Push すると自動テストを実行するワークフローを実行中に次の Push を行うと、実行中のワークフローを中断して、後続の Push に対するワークフロー実行を実施することができる（つまり、実行の意味を失った先行 Push のテストをやめてワークフロー実行に対するコストを節約できる）。

コンカレンシーはワークフローレベルまたはジョブレベルで設定できる。

次の例はプルリクエストに Push した際に実行するワークフローで、同じブランチに対してすでにワークフローが実行中であれば、それをキャンセルして、新たなワークフローを実行する例。

```yaml
name: concurrency sample
on:
    pull_request:
    types: [opened, synchronize]

concurrency: 
    group: ${{ github.workflow }}-${{ github.ref }}
    cancel-in-progress: true
```

- [concurrency](https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions#concurrency)
- [コンカレンシーの使用 - GitHub Docs](https://docs.github.com/ja/actions/using-jobs/using-concurrency)

## コンテナ内でのジョブ実行

ジョブを指定したコンテナ内で実行することもできる。

次の例は `ruby:3.1.2` コンテナでジョブを実行する例。
通常は [`actions/setup-ruby`](https://github.com/actions/setup-ruby) で Ruby の実行環境を用意するが、`ruby:3.1.2` コンテナで実行するのでそれが不要となっている。

```yaml
name: Container job sample 
on:
  push:

jobs:
  container-test-job:
    runs-on: ubuntu-latest
    container:
      image: ruby:3.1.2
    steps:
      - name: show ruby version
        run: ruby -v
```

このワークフローのログを確認すると `show ruby version` の結果は次の様に出力されている。

```
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
```

ジョブを実効するコンテナに対して `volumes` をマウントしたり、 `env` で環境変数を設定したりできる。
また、 `credentials` を設定できるのでプライベートレジストリからイメージを Pull することもできる。

see [コンテナ内でのジョブの実行 \- GitHub Docs](https://docs.github.com/ja/actions/using-jobs/running-jobs-in-a-container)


## サービスコンテナ

ワークフローの処理の中で、Dockerコンテナを起動することができる。
例えば、DB を利用するアプリケーションのテストを実行する場合に MySQL や PostgreSQL をコンテナで建てることができる。

サービスコンテナを立ち上げるには Job 内で `service:` を利用する。

次の例は PostgreSQL を利用する Rails アプリケーションの Rspec を実行する例。

```yaml:.github/workflows/rspec.yml
name: Rspec for Rails with PostgreSQL
on: push
jobs:
  # Label of the container job
  rspec:
    # Containers must run in Linux based operating systems
    runs-on: ubuntu-latest
    services: # サービスコンテナの定義
      postgres:
        image: postgres:15.0-alpine3.16
        ports:
            - 5432:5432
        env:
          POSTGRES_PASSWORD: postgres
        options: >- # 起動後のヘルスチェック
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - name: Check out repository code
        uses: actions/checkout@v3
      - actions/setup-ruby@v1
        with:
            ruby-version: 3.1
      - name: Rspec
        env: 
          # テスト対象の Rails アプリケーションは次の環境変数で DB 接続先を設定できる想定
          - DB_HOST: localhost
          - DB_PORT: 5432
        run: |
            bundle install
            bundle install --jobs 4 --retry 3
            bundle exec rails db:create
            bundle exec rails db:migrate
            bundle exec rspec
```

上記の例ではコンテナを使わずに Rspec を実行しているが、コンテナでジョブを実行する場合、 `DB_HOST: postgres` の様にサービスの名前で接続先を指定できる。
また、 `volumes` をマウントしたり、 `credentials` を設定することもできる。

see [サービスコンテナについて - GitHub Docs](https://docs.github.com/ja/actions/using-containerized-services/about-service-containers)

## ファイルシステム

- Dockerコンテナで実行されるアクションには、 */github*パスの下に静的なディレクトリがある。
- Dockerコンテナで実行されないアクションでは3つのディレクトリが作成される。これらのディレクトリパスは動的に生成されるので一定ではない。各ディレクトリの位置は対応する環境変数で取得する。
 - `home`（`HOME`）： ユーザ認証情報などのユーザ関連データが書き込まれる
 - `workspace`（`GITHUB_WORKSPACE`）：アクションが実行されるワークディレクトリ
 - `workflow`：*workflow/event.json*（`GITHUB_EVENT_PATH`）が書き込まれるディレクトリ

## 公開アクション

Github自身が作成しているActionがリポジトリで公開されている。
サードパーティが作ったものはマーケットプレイスで探せる。

- [GitHub Actions](https://github.com/actions)：Github謹製のアクション
- [GitHub Marketplace: actions to improve your workflow](https://github.com/marketplace?type=actions)：サードパーティ製のアクションのレジストリ

## 環境変数

環境変数を定義して、ワークフロー内で参照できる。
設定できる環境変数は次のスコープを取ることができる。

- Workflow （トップレベルの `env:` で定義）
- Job （`jobs.<job_id>.env`で定義）
- Step (`jobs.<job_id>.steps[*].env`で定義)

下へ行くほどスコープは狭くなるが、上位のスコープで設定された値を上書きできる。

```yaml

env:
  SOME_VAR: 'workflow-scope' # Workflowスコープの環境変数

jobs:
  one_job:
    env:
      SOME_VAR: 'job-scope' # Job スコープの環境変数。このJob内ではworkflowスコープの値を上書きする
    runs-on: ubuntu-latest
    steps:
      env:
        SOME_VAR: 'job-scope' # Stepスコープの環境変数。他のスコープで設定された値を上書きする
      - run: echo ${{ env.SOME_VAR }} # 環境変数の参照
```

### 環境変数を動的に追加する

例えば、アーティファクトのファイル名にモジュールのバージョン番号などを入れたい場合などに動的に環境変数を設定したい場合がある。

`$GITHUB_ENV` に追記することで環境変数を設定できる。
環境変数 GITHUB_ENV はステップ内で実行するシェルスクリプトなどの中でも有効。

```yaml
jobs:
  one_job:
    runs-on: ubuntu-latest
    steps:
      - run: echo "DYN_VAR='value'" >> $GITHUB_ENV # 環境変数 DYN_VAR を追加している
      - run: echo ${{ env.DYN_VAR }} # 環境変数の参照
      - uses: actions/upload-artifact@v3
        with:
          name: artifact-${{ env.DYN_VAR }}  # 環境変数の参照し、アーティファクトのファイル名の一部として利用
          path: artifacts/*
```

## Tips

### 複数のコマンドを `run:` したい

通常のYAMLの文法に従ってマルチラインのテキストとして例えば次の様に書けば良い

```yaml
    steps:
      - name: run multi command
        run: |  # <- ここがミソ
          git config --global user.email "someone@sample.com"
          git config --global user.name "github workflow"
          git add .
          git commit -m 'modify manifests'
          git status
```

### プルリクエストの内容で実行するかしないかを決める

コンテキスト`github`にワークフローに関する情報が色々入っている。

例えば、プルリクエストイベントでトリガーするワークフローだと`github.event.pull_request`にGithub REST APIの`pull_request` 相当のプルリクの情報が格納されている。

例えば次のようにするとWIPラベルの付いているときだけ実行するjobとなる。

```yaml
  jobs:
    <job_name>:
      runs-on: ubuntu-latest
      if: "contains(join(github.event.pull_request.labels.*.name), 'WIP')"
      steps:
    	- run: <COMMAND
    	…以下略
```

条件式の書き方など詳しくはこちら -> [GitHub Actions のコンテキストおよび式の構文](https://help.github.com/ja/articles/contexts-and-expression-syntax-for-github-actions)

### チェックアウトするブランチを指定する

Githubが公開するアクション [actions/checkout](https://github.com/actions/checkout)を利用すれば、ソースコードをチェックアウトできる。

では、チェックアウトしたソースのgit的な状態はどうなっているか？

```yaml
    steps:
      - uses: actions/checkout@v3
      - run: git status
      - run: git branch
```

上記のようなコマンドをワークフロー内で実行した結果は次の通り。

```console
    Run git status
    nothing to commit, working tree clean
    Run git status
    HEAD detached at pull/21/merge
    nothing to commit, working tree clean
    Run git branch
    * (HEAD detached at pull/21/merge)
```

見ての通り、プルリクエストのheadブランチとは異なるブランチとしてチェックアウトされている。

プルリクエストのheadブランチをチェックアウトしたいなら次ように指定する

```yaml
    steps:
      - uses: actions/checkout@v3
        with:
          ref: ${{github.head_ref}}
```
`${{github.head_ref}}` はワークフローを実行するブランチを示す。

:::note warn
`${{github.head_ref}}` はプルリクエストイベント(`pull_request` または `pull_request_target`)でトリガーするワークフローでないと 使えないので注意。
:::

### ワークフロー内でファイルを変更 `commit` → `push` する

[actions/checkout](https://github.com/actions/checkout) を使って取得したファイルを書き換えてGitコミットすることができる。
コミットをするということは、ワークフローが実行されるブランチに変更を加えたいということであり、そのためには Push する必要もある。

前述したように [actions/checkout](https://github.com/actions/checkout) でデフォルトでチェックアウトされるブランチはプルリクエストのブランチとは異なる。そのため、上のようにチェックアウトするブランチを指定する必要がある。

### プルリクエストをマージする

次のGithub製の公開Actionで、Github APIを叩けば、プルリクエストをマージすることもできる。

- [actions/github-script](https://github.com/actions/github-script)

actions/github-script は Node.js版のoctokitを使うためのアクション。
このアクションは `script`というパラメータを取り、その中にJSのコードを記述すれば実行される。
`script`内では`github`という変数に認証済みのoctakitインスタンスが格納されており、ワークフローの情報は`context`オブジェクトに格納されている。

次のワークフロー設定はREADME.mdをちょっと変更して、それをコミットしてpushし、マージする例

```yaml
name: Test
  on:
    pull_request:
      types:
        - opened
        - synchronize
  jobs:
    merge:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v3
          with:
            ref: ${{github.head_ref}}
        - run: |
            date >> README.md
            git add .
        - name: check diff
          shell: bash
          id: changes
          run: |
            echo "::set-output name=count::$(git diff --staged --name-only . |wc -l)"
        - name: git commit and push
          shell: bash
          if: steps.changes.outputs.count > 0
          run: |
            git config --global user.name ${GITHUB_ACTOR}
            git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com"
            git commit -m "[WF] ${{inputs.operation}} article(s)"
            git push
        - name: merge pull request
          if: steps.changes.outputs.count > 0
          uses: actions/github-script@0.4.0
          with:
            github-token: ${{secrets.GITHUB_TOKEN}}
            script: |
              github.pulls.merge({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: context.payload.pull_request.number
              })
```

コミット対象のファイルに変更差分が発生しない場合、commitが失敗する。それを避けるためにステップ *name: check diff* で `git diff` を使って変更されているファイルの数をワークフローコマンド `set-output` を利用して `steps.changes.outputs.count` に保存している。後続のステップはこの値が 1 以上であれば実行するようにしている。
ステップ *name: merge pull request* 以下が actions/github-script を使ってプルリクエストをマージしているところ。

ちなみにプルリクエストイベントでトリガーされるワークフローでの`context`はこんな感じ。
<details>
<summary>`context`の中身を開く</summary>
<pre><code>
{
  "context": {
    "payload": {
      "action": "synchronize",
      "after": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "before": "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
      "number": 65,
      "pull_request": {
        "_links": {
          "comments": {
            "href": "https://api.github.com/repos/<owner>/<repo>/issues/65/comments"
          },
          "commits": {
            "href": "https://api.github.com/repos/<owner>/<repo>/pulls/65/commits"
          },
          "html": {
            "href": "https://github.com/<owner>/<repo>/pull/65"
          },
          "issue": {
            "href": "https://api.github.com/repos/<owner>/<repo>/issues/65"
          },
          "review_comment": {
            "href": "https://api.github.com/repos/<owner>/<repo>/pulls/comments{/number}"
          },
          "review_comments": {
            "href": "https://api.github.com/repos/<owner>/<repo>/pulls/65/comments"
          },
          "self": {
            "href": "https://api.github.com/repos/<owner>/<repo>/pulls/65"
          },
          "statuses": {
            "href": "https://api.github.com/repos/<owner>/<repo>/statuses/79dae33c3eb3dee6990846ed9136649454935bfb"
          }
        },
        "additions": 45,
        "assignee": null,
        "assignees": [],
        "author_association": "OWNER",
        "base": {
          "label": "<owner>:master",
          "ref": "master",
          "repo": {
            "archive_url": "https://api.github.com/repos/<owner>/<repo>/{archive_format}{/ref}",
            "archived": false,
            "assignees_url": "https://api.github.com/repos/<owner>/<repo>/assignees{/user}",
            "blobs_url": "https://api.github.com/repos/<owner>/<repo>/git/blobs{/sha}",
            "branches_url": "https://api.github.com/repos/<owner>/<repo>/branches{/branch}",
            "clone_url": "https://github.com/<owner>/<repo>.git",
            "collaborators_url": "https://api.github.com/repos/<owner>/<repo>/collaborators{/collaborator}",
            "comments_url": "https://api.github.com/repos/<owner>/<repo>/comments{/number}",
            "commits_url": "https://api.github.com/repos/<owner>/<repo>/commits{/sha}",
            "compare_url": "https://api.github.com/repos/<owner>/<repo>/compare/{base}...{head}",
            "contents_url": "https://api.github.com/repos/<owner>/<repo>/contents/{+path}",
            "contributors_url": "https://api.github.com/repos/<owner>/<repo>/contributors",
            "created_at": "2019-12-04T09:14:10Z",
            "default_branch": "master",
            "deployments_url": "https://api.github.com/repos/<owner>/<repo>/deployments",
            "description": null,
            "disabled": false,
            "downloads_url": "https://api.github.com/repos/<owner>/<repo>/downloads",
            "events_url": "https://api.github.com/repos/<owner>/<repo>/events",
            "fork": false,
            "forks": 0,
            "forks_count": 0,
            "forks_url": "https://api.github.com/repos/<owner>/<repo>/forks",
            "full_name": "<owner>/<repo>",
            "git_commits_url": "https://api.github.com/repos/<owner>/<repo>/git/commits{/sha}",
            "git_refs_url": "https://api.github.com/repos/<owner>/<repo>/git/refs{/sha}",
            "git_tags_url": "https://api.github.com/repos/<owner>/<repo>/git/tags{/sha}",
            "git_url": "git://github.com/<owner>/<repo>.git",
            "has_downloads": true,
            "has_issues": true,
            "has_pages": false,
            "has_projects": true,
            "has_wiki": true,
            "homepage": null,
            "hooks_url": "https://api.github.com/repos/<owner>/<repo>/hooks",
            "html_url": "https://github.com/<owner>/<repo>",
            "id": 225825747,
            "issue_comment_url": "https://api.github.com/repos/<owner>/<repo>/issues/comments{/number}",
            "issue_events_url": "https://api.github.com/repos/<owner>/<repo>/issues/events{/number}",
            "issues_url": "https://api.github.com/repos/<owner>/<repo>/issues{/number}",
            "keys_url": "https://api.github.com/repos/<owner>/<repo>/keys{/key_id}",
            "labels_url": "https://api.github.com/repos/<owner>/<repo>/labels{/name}",
            "language": "HTML",
            "languages_url": "https://api.github.com/repos/<owner>/<repo>/languages",
            "license": null,
            "merges_url": "https://api.github.com/repos/<owner>/<repo>/merges",
            "milestones_url": "https://api.github.com/repos/<owner>/<repo>/milestones{/number}",
            "mirror_url": null,
            "name": "<repo>",
            "node_id": "MDEwOlJlcG9zaXRvcnkyMjU4MjU3NDc=",
            "notifications_url": "https://api.github.com/repos/<owner>/<repo>/notifications{?since,all,participating}",
            "open_issues": 60,
            "open_issues_count": 60,
            "owner": {
              "avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
              "events_url": "https://api.github.com/users/<owner>/events{/privacy}",
              "followers_url": "https://api.github.com/users/<owner>/followers",
              "following_url": "https://api.github.com/users/<owner>/following{/other_user}",
              "gists_url": "https://api.github.com/users/<owner>/gists{/gist_id}",
              "gravatar_id": "",
              "html_url": "https://github.com/<owner>",
              "id": 26864070,
              "login": "<owner>",
              "node_id": "MDQ6VXNlcjI2ODY0MDcw",
              "organizations_url": "https://api.github.com/users/<owner>/orgs",
              "received_events_url": "https://api.github.com/users/<owner>/received_events",
              "repos_url": "https://api.github.com/users/<owner>/repos",
              "site_admin": false,
              "starred_url": "https://api.github.com/users/<owner>/starred{/owner}{/repo}",
              "subscriptions_url": "https://api.github.com/users/<owner>/subscriptions",
              "type": "User",
              "url": "https://api.github.com/users/<owner>"
            },
            "private": true,
            "pulls_url": "https://api.github.com/repos/<owner>/<repo>/pulls{/number}",
            "pushed_at": "2020-01-24T04:41:03Z",
            "releases_url": "https://api.github.com/repos/<owner>/<repo>/releases{/id}",
            "size": 16,
            "ssh_url": "git@github.com:<owner>/<repo>.git",
            "stargazers_count": 0,
            "stargazers_url": "https://api.github.com/repos/<owner>/<repo>/stargazers",
            "statuses_url": "https://api.github.com/repos/<owner>/<repo>/statuses/{sha}",
            "subscribers_url": "https://api.github.com/repos/<owner>/<repo>/subscribers",
            "subscription_url": "https://api.github.com/repos/<owner>/<repo>/subscription",
            "svn_url": "https://github.com/<owner>/<repo>",
            "tags_url": "https://api.github.com/repos/<owner>/<repo>/tags",
            "teams_url": "https://api.github.com/repos/<owner>/<repo>/teams",
            "trees_url": "https://api.github.com/repos/<owner>/<repo>/git/trees{/sha}",
            "updated_at": "2020-01-22T03:25:23Z",
            "url": "https://api.github.com/repos/<owner>/<repo>",
            "watchers": 0,
            "watchers_count": 0
          },
          "sha": "26a7c543d54cd86669592979a5ae35210f05bf35",
          "user": {
            "avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
            "events_url": "https://api.github.com/users/<owner>/events{/privacy}",
            "followers_url": "https://api.github.com/users/<owner>/followers",
            "following_url": "https://api.github.com/users/<owner>/following{/other_user}",
            "gists_url": "https://api.github.com/users/<owner>/gists{/gist_id}",
            "gravatar_id": "",
            "html_url": "https://github.com/<owner>",
            "id": 26864070,
            "login": "<owner>",
            "node_id": "MDQ6VXNlcjI2ODY0MDcw",
            "organizations_url": "https://api.github.com/users/<owner>/orgs",
            "received_events_url": "https://api.github.com/users/<owner>/received_events",
            "repos_url": "https://api.github.com/users/<owner>/repos",
            "site_admin": false,
            "starred_url": "https://api.github.com/users/<owner>/starred{/owner}{/repo}",
            "subscriptions_url": "https://api.github.com/users/<owner>/subscriptions",
            "type": "User",
            "url": "https://api.github.com/users/<owner>"
          }
        },
        "body": "\nSource PR: [Can merge]()\n",
        "changed_files": 3,
        "closed_at": null,
        "comments": 0,
        "comments_url": "https://api.github.com/repos/<owner>/<repo>/issues/65/comments",
        "commits": 6,
        "commits_url": "https://api.github.com/repos/<owner>/<repo>/pulls/65/commits",
        "created_at": "2020-01-23T07:07:24Z",
        "deletions": 2,
        "diff_url": "https://github.com/<owner>/<repo>/pull/65.diff",
        "draft": false,
        "head": {
          "label": "<owner>:can_merge",
          "ref": "can_merge",
          "repo": {
            "archive_url": "https://api.github.com/repos/<owner>/<repo>/{archive_format}{/ref}",
            "archived": false,
            "assignees_url": "https://api.github.com/repos/<owner>/<repo>/assignees{/user}",
            "blobs_url": "https://api.github.com/repos/<owner>/<repo>/git/blobs{/sha}",
            "branches_url": "https://api.github.com/repos/<owner>/<repo>/branches{/branch}",
            "clone_url": "https://github.com/<owner>/<repo>.git",
            "collaborators_url": "https://api.github.com/repos/<owner>/<repo>/collaborators{/collaborator}",
            "comments_url": "https://api.github.com/repos/<owner>/<repo>/comments{/number}",
            "commits_url": "https://api.github.com/repos/<owner>/<repo>/commits{/sha}",
            "compare_url": "https://api.github.com/repos/<owner>/<repo>/compare/{base}...{head}",
            "contents_url": "https://api.github.com/repos/<owner>/<repo>/contents/{+path}",
            "contributors_url": "https://api.github.com/repos/<owner>/<repo>/contributors",
            "created_at": "2019-12-04T09:14:10Z",
            "default_branch": "master",
            "deployments_url": "https://api.github.com/repos/<owner>/<repo>/deployments",
            "description": null,
            "disabled": false,
            "downloads_url": "https://api.github.com/repos/<owner>/<repo>/downloads",
            "events_url": "https://api.github.com/repos/<owner>/<repo>/events",
            "fork": false,
            "forks": 0,
            "forks_count": 0,
            "forks_url": "https://api.github.com/repos/<owner>/<repo>/forks",
            "full_name": "<owner>/<repo>",
            "git_commits_url": "https://api.github.com/repos/<owner>/<repo>/git/commits{/sha}",
            "git_refs_url": "https://api.github.com/repos/<owner>/<repo>/git/refs{/sha}",
            "git_tags_url": "https://api.github.com/repos/<owner>/<repo>/git/tags{/sha}",
            "git_url": "git://github.com/<owner>/<repo>.git",
            "has_downloads": true,
            "has_issues": true,
            "has_pages": false,
            "has_projects": true,
            "has_wiki": true,
            "homepage": null,
            "hooks_url": "https://api.github.com/repos/<owner>/<repo>/hooks",
            "html_url": "https://github.com/<owner>/<repo>",
            "id": 225825747,
            "issue_comment_url": "https://api.github.com/repos/<owner>/<repo>/issues/comments{/number}",
            "issue_events_url": "https://api.github.com/repos/<owner>/<repo>/issues/events{/number}",
            "issues_url": "https://api.github.com/repos/<owner>/<repo>/issues{/number}",
            "keys_url": "https://api.github.com/repos/<owner>/<repo>/keys{/key_id}",
            "labels_url": "https://api.github.com/repos/<owner>/<repo>/labels{/name}",
            "language": "HTML",
            "languages_url": "https://api.github.com/repos/<owner>/<repo>/languages",
            "license": null,
            "merges_url": "https://api.github.com/repos/<owner>/<repo>/merges",
            "milestones_url": "https://api.github.com/repos/<owner>/<repo>/milestones{/number}",
            "mirror_url": null,
            "name": "<repo>",
            "node_id": "MDEwOlJlcG9zaXRvcnkyMjU4MjU3NDc=",
            "notifications_url": "https://api.github.com/repos/<owner>/<repo>/notifications{?since,all,participating}",
            "open_issues": 60,
            "open_issues_count": 60,
            "owner": {
              "avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
              "events_url": "https://api.github.com/users/<owner>/events{/privacy}",
              "followers_url": "https://api.github.com/users/<owner>/followers",
              "following_url": "https://api.github.com/users/<owner>/following{/other_user}",
              "gists_url": "https://api.github.com/users/<owner>/gists{/gist_id}",
              "gravatar_id": "",
              "html_url": "https://github.com/<owner>",
              "id": 26864070,
              "login": "<owner>",
              "node_id": "MDQ6VXNlcjI2ODY0MDcw",
              "organizations_url": "https://api.github.com/users/<owner>/orgs",
              "received_events_url": "https://api.github.com/users/<owner>/received_events",
              "repos_url": "https://api.github.com/users/<owner>/repos",
              "site_admin": false,
              "starred_url": "https://api.github.com/users/<owner>/starred{/owner}{/repo}",
              "subscriptions_url": "https://api.github.com/users/<owner>/subscriptions",
              "type": "User",
              "url": "https://api.github.com/users/<owner>"
            },
            "private": true,
            "pulls_url": "https://api.github.com/repos/<owner>/<repo>/pulls{/number}",
            "pushed_at": "2020-01-24T04:41:03Z",
            "releases_url": "https://api.github.com/repos/<owner>/<repo>/releases{/id}",
            "size": 16,
            "ssh_url": "git@github.com:<owner>/<repo>.git",
            "stargazers_count": 0,
            "stargazers_url": "https://api.github.com/repos/<owner>/<repo>/stargazers",
            "statuses_url": "https://api.github.com/repos/<owner>/<repo>/statuses/{sha}",
            "subscribers_url": "https://api.github.com/repos/<owner>/<repo>/subscribers",
            "subscription_url": "https://api.github.com/repos/<owner>/<repo>/subscription",
            "svn_url": "https://github.com/<owner>/<repo>",
            "tags_url": "https://api.github.com/repos/<owner>/<repo>/tags",
            "teams_url": "https://api.github.com/repos/<owner>/<repo>/teams",
            "trees_url": "https://api.github.com/repos/<owner>/<repo>/git/trees{/sha}",
            "updated_at": "2020-01-22T03:25:23Z",
            "url": "https://api.github.com/repos/<owner>/<repo>",
            "watchers": 0,
            "watchers_count": 0
          },
          "sha": "79dae33c3eb3dee6990846ed9136649454935bfb",
          "user": {
            "avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
            "events_url": "https://api.github.com/users/<owner>/events{/privacy}",
            "followers_url": "https://api.github.com/users/<owner>/followers",
            "following_url": "https://api.github.com/users/<owner>/following{/other_user}",
            "gists_url": "https://api.github.com/users/<owner>/gists{/gist_id}",
            "gravatar_id": "",
            "html_url": "https://github.com/<owner>",
            "id": 26864070,
            "login": "<owner>",
            "node_id": "MDQ6VXNlcjI2ODY0MDcw",
            "organizations_url": "https://api.github.com/users/<owner>/orgs",
            "received_events_url": "https://api.github.com/users/<owner>/received_events",
            "repos_url": "https://api.github.com/users/<owner>/repos",
            "site_admin": false,
            "starred_url": "https://api.github.com/users/<owner>/starred{/owner}{/repo}",
            "subscriptions_url": "https://api.github.com/users/<owner>/subscriptions",
            "type": "User",
            "url": "https://api.github.com/users/<owner>"
          }
        },
        "html_url": "https://github.com/<owner>/<repo>/pull/65",
        "id": 366212162,
        "issue_url": "https://api.github.com/repos/<owner>/<repo>/issues/65",
        "labels": [],
        "locked": false,
        "maintainer_can_modify": false,
        "merge_commit_sha": "9999999999999999999999999999999999999999",
        "mergeable": null,
        "mergeable_state": "unknown",
        "merged": false,
        "merged_at": null,
        "merged_by": null,
        "milestone": null,
        "node_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        "number": 65,
        "patch_url": "https://github.com/<owner>/<repo>/pull/65.patch",
        "rebaseable": null,
        "requested_reviewers": [],
        "requested_teams": [],
        "review_comment_url": "https://api.github.com/repos/<owner>/<repo>/pulls/comments{/number}",
        "review_comments": 0,
        "review_comments_url": "https://api.github.com/repos/<owner>/<repo>/pulls/65/comments",
        "state": "open",
        "statuses_url": "https://api.github.com/repos/<owner>/<repo>/statuses/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "title": "Can merge",
        "updated_at": "2020-01-24T04:41:03Z",
        "url": "https://api.github.com/repos/<owner>/<repo>/pulls/65",
        "user": {
          "avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
          "events_url": "https://api.github.com/users/<owner>/events{/privacy}",
          "followers_url": "https://api.github.com/users/<owner>/followers",
          "following_url": "https://api.github.com/users/<owner>/following{/other_user}",
          "gists_url": "https://api.github.com/users/<owner>/gists{/gist_id}",
          "gravatar_id": "",
          "html_url": "https://github.com/<owner>",
          "id": 26864070,
          "login": "<owner>",
          "node_id": "MDQ6VXNlcjI2ODY0MDcw",
          "organizations_url": "https://api.github.com/users/<owner>/orgs",
          "received_events_url": "https://api.github.com/users/<owner>/received_events",
          "repos_url": "https://api.github.com/users/<owner>/repos",
          "site_admin": false,
          "starred_url": "https://api.github.com/users/<owner>/starred{/owner}{/repo}",
          "subscriptions_url": "https://api.github.com/users/<owner>/subscriptions",
          "type": "User",
          "url": "https://api.github.com/users/<owner>"
        }
      },
      "repository": {
        "archive_url": "https://api.github.com/repos/<owner>/<repo>/{archive_format}{/ref}",
        "archived": false,
        "assignees_url": "https://api.github.com/repos/<owner>/<repo>/assignees{/user}",
        "blobs_url": "https://api.github.com/repos/<owner>/<repo>/git/blobs{/sha}",
        "branches_url": "https://api.github.com/repos/<owner>/<repo>/branches{/branch}",
        "clone_url": "https://github.com/<owner>/<repo>.git",
        "collaborators_url": "https://api.github.com/repos/<owner>/<repo>/collaborators{/collaborator}",
        "comments_url": "https://api.github.com/repos/<owner>/<repo>/comments{/number}",
        "commits_url": "https://api.github.com/repos/<owner>/<repo>/commits{/sha}",
        "compare_url": "https://api.github.com/repos/<owner>/<repo>/compare/{base}...{head}",
        "contents_url": "https://api.github.com/repos/<owner>/<repo>/contents/{+path}",
        "contributors_url": "https://api.github.com/repos/<owner>/<repo>/contributors",
        "created_at": "2019-12-04T09:14:10Z",
        "default_branch": "master",
        "deployments_url": "https://api.github.com/repos/<owner>/<repo>/deployments",
        "description": null,
        "disabled": false,
        "downloads_url": "https://api.github.com/repos/<owner>/<repo>/downloads",
        "events_url": "https://api.github.com/repos/<owner>/<repo>/events",
        "fork": false,
        "forks": 0,
        "forks_count": 0,
        "forks_url": "https://api.github.com/repos/<owner>/<repo>/forks",
        "full_name": "<owner>/<repo>",
        "git_commits_url": "https://api.github.com/repos/<owner>/<repo>/git/commits{/sha}",
        "git_refs_url": "https://api.github.com/repos/<owner>/<repo>/git/refs{/sha}",
        "git_tags_url": "https://api.github.com/repos/<owner>/<repo>/git/tags{/sha}",
        "git_url": "git://github.com/<owner>/<repo>.git",
        "has_downloads": true,
        "has_issues": true,
        "has_pages": false,
        "has_projects": true,
        "has_wiki": true,
        "homepage": null,
        "hooks_url": "https://api.github.com/repos/<owner>/<repo>/hooks",
        "html_url": "https://github.com/<owner>/<repo>",
        "id": 225825747,
        "issue_comment_url": "https://api.github.com/repos/<owner>/<repo>/issues/comments{/number}",
        "issue_events_url": "https://api.github.com/repos/<owner>/<repo>/issues/events{/number}",
        "issues_url": "https://api.github.com/repos/<owner>/<repo>/issues{/number}",
        "keys_url": "https://api.github.com/repos/<owner>/<repo>/keys{/key_id}",
        "labels_url": "https://api.github.com/repos/<owner>/<repo>/labels{/name}",
        "language": "HTML",
        "languages_url": "https://api.github.com/repos/<owner>/<repo>/languages",
        "license": null,
        "merges_url": "https://api.github.com/repos/<owner>/<repo>/merges",
        "milestones_url": "https://api.github.com/repos/<owner>/<repo>/milestones{/number}",
        "mirror_url": null,
        "name": "<repo>",
        "node_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        "notifications_url": "https://api.github.com/repos/<owner>/<repo>/notifications{?since,all,participating}",
        "open_issues": 60,
        "open_issues_count": 60,
        "owner": {
          "avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
          "events_url": "https://api.github.com/users/<owner>/events{/privacy}",
          "followers_url": "https://api.github.com/users/<owner>/followers",
          "following_url": "https://api.github.com/users/<owner>/following{/other_user}",
          "gists_url": "https://api.github.com/users/<owner>/gists{/gist_id}",
          "gravatar_id": "",
          "html_url": "https://github.com/<owner>",
          "id": 26864070,
          "login": "<owner>",
          "node_id": "XXXXXXXXXXXXXXXXXXXXXX",
          "organizations_url": "https://api.github.com/users/<owner>/orgs",
          "received_events_url": "https://api.github.com/users/<owner>/received_events",
          "repos_url": "https://api.github.com/users/<owner>/repos",
          "site_admin": false,
          "starred_url": "https://api.github.com/users/<owner>/starred{/owner}{/repo}",
          "subscriptions_url": "https://api.github.com/users/<owner>/subscriptions",
          "type": "User",
          "url": "https://api.github.com/users/<owner>"
        },
        "private": true,
        "pulls_url": "https://api.github.com/repos/<owner>/<repo>/pulls{/number}",
        "pushed_at": "2020-01-24T04:41:03Z",
        "releases_url": "https://api.github.com/repos/<owner>/<repo>/releases{/id}",
        "size": 16,
        "ssh_url": "git@github.com:<owner>/<repo>.git",
        "stargazers_count": 0,
        "stargazers_url": "https://api.github.com/repos/<owner>/<repo>/stargazers",
        "statuses_url": "https://api.github.com/repos/<owner>/<repo>/statuses/{sha}",
        "subscribers_url": "https://api.github.com/repos/<owner>/<repo>/subscribers",
        "subscription_url": "https://api.github.com/repos/<owner>/<repo>/subscription",
        "svn_url": "https://github.com/<owner>/<repo>",
        "tags_url": "https://api.github.com/repos/<owner>/<repo>/tags",
        "teams_url": "https://api.github.com/repos/<owner>/<repo>/teams",
        "trees_url": "https://api.github.com/repos/<owner>/<repo>/git/trees{/sha}",
        "updated_at": "2020-01-22T03:25:23Z",
        "url": "https://api.github.com/repos/<owner>/<repo>",
        "watchers": 0,
        "watchers_count": 0
      },
      "sender": {
        "avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
        "events_url": "https://api.github.com/users/<owner>/events{/privacy}",
        "followers_url": "https://api.github.com/users/<owner>/followers",
        "following_url": "https://api.github.com/users/<owner>/following{/other_user}",
        "gists_url": "https://api.github.com/users/<owner>/gists{/gist_id}",
        "gravatar_id": "",
        "html_url": "https://github.com/<owner>",
        "id": 26864070,
        "login": "<owner>",
        "node_id": "XXXXXXXXXXXXXXXXXXXX",
        "organizations_url": "https://api.github.com/users/<owner>/orgs",
        "received_events_url": "https://api.github.com/users/<owner>/received_events",
        "repos_url": "https://api.github.com/users/<owner>/repos",
        "site_admin": false,
        "starred_url": "https://api.github.com/users/<owner>/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/<owner>/subscriptions",
        "type": "User",
        "url": "https://api.github.com/users/<owner>"
      }
    },
    "eventName": "pull_request",
    "sha": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "ref": "refs/pull/65/merge",
    "workflow": "Test",
    "action": "actionsgithub-script",
    "actor": "<owner>"
  }
}
</code></pre>
</details>

### ワークフローの一部を再利用したい

ワークフローの一部の処理を複数のワークフローで共有したい場合がある。

次の2つの方法がある。

1. Github Actions を作る
1. Reusable workflows を作る

#### Github Actions による再利用

ワークフローの一部を Github Actions として切り出す方法。
別に [GitHub Marketplace · Actions to improve your workflow](https://github.com/marketplace?type=actions)で公開しなくても利用することはできる。
Public リポジトリで定義すると他のリポジトリからも利用できる。
Private リポジトリで定義すると同じリポジトリからしか利用できない。
Job 内のステップとして利用できるので、共通の前処理/後処理を切り出したしたい場合には便利。
ただ、Action として定義をするのがちょっと手間。

参考： [Github Action の作り方メモ - Qiita](https://qiita.com/HeRo/items/e2d5e3bc3dbe810f0482)

#### Reusable workflows による再利用

*workflow_call* イベントでトリガーされるように定義したワークフローを他のワークフローから呼び出す方法。
呼び出される側のワークフローは *called workflow* といい、通常のワークフローの定義をほぼそのまま利用できる。
Public リポジトリで定義された *called workflow* は他のリポジトリのワークフローからも利用可能。
Private リポジトリで定義されたものは同じリポジトリ内からのみ利用可能。このあたりは Github Aciton と同じ。
Reusable workflows は Job として利用することとなる（つまりワークフローの他の処理とは別のJobにせざるを得ない）。したがって、コードの取得や各種言語環境の準備などの初期化処理も含めて *called workflow* を作る必要があるし、*called workflow* の成果物を他の Job で利用するなら output や Ａｒｔｉｆａｃｔ などを利用して受け渡す必要がある。

参考： [Reusing workflows - GitHub Docs](https://docs.github.com/en/actions/using-workflows/reusing-workflows)