はじめに

去幎 DEV のアカりントを䜜成したものの、今たで党く有効掻甚出来おいたせんでした。

DEV には カノニカル URL を蚭定出来るので、垞々 Zenn の蚘事を投皿する際にクロスポストしたいなず考えおおりたした。そこで、Zenn に蚘事を投皿したら、自動的に DEV にも蚘事を投皿 & 同期する GitHub Actions を䜜っおみたした。

今回初めお GitHub Actions を自䜜したのですが、その䞭で埗た知芋を残す圢で蚘事を曞くこずにしたした。たた、GitHub Actions は TypeScript で䜜成したした。

開発した GitHub Actions の抂芁

たずはザッずどのような GitHub Actions を䜜成したのか、抂芁に぀いお説明いたしたす。

GitHub リポゞトリで管理しおいる Zenn の蚘事を DEV に同期しお投皿する GitHub Actions を䜜成したした。 その際に DEV ぞ投皿する蚘事には Zenn の該圓蚘事ぞのカノニカル URL も自動で蚭定できたす。これにより DEV ず Zenn ぞ蚘事をシヌムレスにクロスポストするこずが可胜ずなりたす。

今回䜜成した GitHub Actions を利甚するワヌクフロヌファむルの䞀䟋は䞋蚘ずなりたす。

# .github/workflows/sync-zenn-with-dev-all.yml

name: "Sync all Zenn articles to DEV"
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: checkout my project
        uses: actions/checkout@v2
      - name: dev.to action step
        uses: nikaera/sync-zenn-with-dev-action@v1
        # id を蚭定するこずで、埌のゞョブで Output で指定した倀が参照可胜になる
        id: dev-to
        with:
          # DEV の API キヌを指定する
          api_key: ${{ secrets.api_key }}
          # (オプション) DEV に蚘事を投皿した際に Zenn のカノニカル URL を蚭定したい堎合に指定する
          # username: nikaera
          # (オプション) 改行区切りで指定した articles フォルダ内のファむルパスを蚘茉した txt ファむルを指定するこずで、蚘茉された蚘事のみを同期するようになる。
          # 他プラグむンず組み合わせるこずで差分のみを txt ファむルに茉せるこずが可胜。詳现に぀いおは埌述の Outputs の項目に蚘茉。
          # added_modified_filepath: ./added_modified.txt
          # (オプション) Zenn の articles 以䞋党おの蚘事を垞に DEV に同期するか指定する
          # update_all が true のずきは added_modified_filepath は無芖される。
          # update_all: false
        # 䞊蚘アクション実行時に DEV に新芏で同期する蚘事に関しおは Zenn のマヌクダりンヘッダに
        # 該圓する DEV の蚘事の ID が dev_article_id ずしお蚘茉されるようになる。
        # 今埌はその ID を元に同期するようになるため、該圓する Zenn の蚘事をコミットする。
        # 新芏で同期する蚘事が無ければ、このゞョブは実行しない。
      - name: write article id of DEV to articles of Zenn.
        run: |
          git config user.name github-actions
          git config user.email [email protected]
          git add ${{ steps.dev-to.outputs.newly-sync-articles }}
          git commit -m "sync: Zenn with DEV [skip ci]"
          git push
        if: steps.dev-to.outputs.newly-sync-articles
        # Outputs には DEV の蚘事情報 (title, url) が含たれるようになるため、
        # 最埌に出力しお実行結果の内容を確認するこずもできる
      - name: Get the output articles.
        # dev-to ずいう id が玐付いたゞョブの Outputs を取埗しお echo で内容を出力する
        run: echo "${{ steps.dev-to.outputs.articles }}"

簡単に nikaera/sync-zenn-with-dev-action@v1 ずいうゞョブの内郚凊理に぀いお説明いたしたす。

  1. Inputs の update_all 及び added_modified_filepath で受け取った情報を元に、 どの蚘事を DEV に同期させるか刀定する
  2. DEV に同期する蚘事のファむルパス䞀芧を取埗しお、 それぞれの蚘事のヘッダに dev_article_id の蚘茉があるか刀定する
  3. Inputs の api_key を利甚しお、Zenn の蚘事に dev_article_id が含たれおいれば、 該圓する DEV の蚘事を曎新する。含たれおいなければ DEV に新芏で蚘事を䜜成する
  4. Inputs の username を利甚しお、 DEV の蚘事に該圓する Zenn 蚘事のカノニカル URL を蚭定する
  5. DEV に新芏で蚘事を䜜成した堎合は、 Zenn の該圓蚘事のヘッダに dev_article_id を曞き蟌む
  6. DEV に蚘事を投皿する際、Zenn は察応しおいるが、 DEV では察応しおいない蚘述は削陀する (::: 蚘法や䞀郚のコヌド蚘法)
  7. 蚘事の公開ステヌタス及びタグなどに぀いおも DEV の蚘事に反映する1
  8. 新芏で DEV に蚘事を同期した Zenn 蚘事のファむルパスを、 Outputs の newly-sync-articles に蚭定する (埌のゞョブで dev_article_id の含たれた蚘事をコミットしたいため)
  9. ワヌクフロヌで同期された蚘事情報は Outouts の articles に蚭定する

Inputs ず Outputs の内容䞀芧に぀いおは䞋蚘になりたす。

Inputs

キヌ説明必須
api_keyDEV の API Key を蚭定するo
usernameZenn の 自分のアカりント名 を蚭定する (DEV に同期する蚘事に Zenn のカノニカル URL を蚭定したい堎合のみ)x
added_modified_filepath改行区切りで指定した articles フォルダ内のファむルパスを蚘茉した txt ファむルを指定するこずで、蚘茉された蚘事のみを同期するようになる。PR やコミット差分のファむルのみを取埗するための GitHub Actions jitterbit/get-changed-files@v1 ず組み合わせるこずで、曎新差分のあった蚘事のみを随時同期するこずも可胜。2 曎新差分のあった蚘事のみを随時同期するための実際のワヌクフロヌファむルはこちらx
update_allZenn の党おの蚘事をどうきするかどうかを蚭定する。GitHub Actions 初回実行時のみ true にする䜿い方を想定しおいる。デフォルトは true。added_modified_filepath よりも update_all が優先されるため added_modified_filepath を蚭定する堎合は false を蚭定する必芁あり`x

Outputs

キヌ説明
articles同期された DEV の蚘事のタむトル及び URL が栌玍された配列
newly-sync-articlesDEV で新たに新芏䜜成された Zenn 蚘事のファむルパスが栌玍された配列。実際のワヌクフロヌファむルの該圓する蚘述のように、必ずコミットに含めるようにする必芁がある (理由は埌述)

Inputs 及び Outputs に぀いおは公匏サむトの説明をご参照ください。

Zenn の蚘事を DEV に同期するための仕組み

Zenn の蚘事を新芏で DEV に同期する際は、DEV に蚘事を新芏䜜成する必芁がありたす。その際に Zenn の蚘事ず DEV の蚘事を玐付けるための䜕らかの仕組みが必芁ずなりたす。そうしないず、今埌 Zenn の蚘事内容を曎新した際に、DEV のどの蚘事に内容を同期させればよいかが䞍明なためです。

そこで、蚘事を同期するための仕組みずしお、dev_article_id ずいうフィヌルドを Zenn のマヌクダりンヘッダに远蚘するこずで DEV の同期すべき蚘事ずの玐付けを行うこずにしたした。dev_article_id には DEV の蚘事䜜成 API 実行時の返り倀である id を蚭定したす。

䞀床 id を dev_article_id ずしお Zenn の蚘事に玐付けおしたえば、次回以降に蚘事の同期を行う際は DEV の蚘事曎新 API を利甚できたす。

たた、Outputs の newly-sync-articles には新芏で䜜成された DEV 蚘事の id である dev_article_id が远蚘された Zenn 蚘事のファむルパスが栌玍されおいたす。そのため、nikaera/sync-zenn-with-dev-action@v1 実行埌は、䞋蚘のように steps.dev-to.outputs.newly-sync-articles 内に栌玍されたファむル矀をコミットに反映させる必芁がありたす。

# `nikaera/sync-zenn-with-dev-action@v1` 実行埌に必ず定矩すべきゞョブ
# DEV に新芏に䜜成した蚘事がなければ実行しない (if: steps.dev-to.outputs.newly-sync-articles)
- name: write article id of DEV to articles of Zenn.
    run: |
        git config user.name github-actions
        git config user.email [email protected]
        git add ${{ steps.dev-to.outputs.newly-sync-articles }}
        git commit -m "sync: Zenn with DEV [skip ci]"
        git push
    if: steps.dev-to.outputs.newly-sync-articles

䞊蚘のゞョブで newly-sync-articles に栌玍された dev_article_id が远蚘された Zenn 蚘事は随時コミットに反映しないず、Zenn の党おの蚘事が同期毎 DEV に新芏䜜成され続けるずいう䞍具合を匕き起こしおしたうので、ご泚意ください

GitHub Actions を開発する手順

サクッず開発に取り組みたかったため、Docker コンテナを利甚する方法 ではなく、JavaScript を利甚する方法 で開発を進めおいくこずにしたした。

TypeScript で GitHub Actions を䜜る

GitHub 公匏が TypeScript で GitHub Actions を䜜るための テンプレヌトプロゞェクト を甚意しおくれおいたす。今回はこのテンプレヌトプロゞェクトを利甚する圢でプロゞェクトを䜜成したした。


(䜙談) GitHub Actions では Docker コンテナ を甚いおワヌクフロヌを実行可胜です。そのため、実行環境は自由に蚭定出来たす。(Go, Python, Ruby, etc.)


早速 TypeScript のテンプレヌトプロゞェクトを元に自分の GitHub Actions プロゞェクトを䜜成したす。

スクリヌンショット 2021-03-21 13.25.54.png 1. テンプレヌトプロゞェクトを元に GitHub Actions の TypeScript プロゞェクトを䜜成する

スクリヌンショット 2021-03-21 13.29.43.png 2. プロゞェクトの䜜成埌 git clone しおきお開発する準備を敎える

GitHub Actions プロゞェクトの開発を進めるための準備を行う

テンプレヌトプロゞェクトを git clone したら、たずは action.yml の内容を倉曎したす。 今回䜜成した GitHub Actions の action.yml は䞋蚘ずなっおおりたす。

# action.yml

# GitHub Actions のプロゞェクト名
name: "Sync Zenn articles to DEV"
# GitHub Actions のプロゞェクト説明文
description: "Just sync Zenn articles to DEV."
# GitHub Actions の䜜者
author: "nikaera"
# GitHub Actions に枡せる匕数の倀定矩
inputs:
  api_key:
    # フィヌルドの指定が必須であれば true、必須でなければ false を蚭定する
    # DEV の API キヌは同期を行う際に必須なため、true を蚭定しおいる
    required: true
    # フィヌルドの説明文
    description: "The api_key required to use the DEV API (https://docs.forem.com/api/#section/Authentication)"
  username:
    required: false
    description: "Zenn user's account name. (Fields to be filled in if canonical url is set.)"
  articles:
    required: false
    description: "The directory where Zenn articles are stored."
    # フィヌルドにはデフォルト倀を指定するこずも可胜
    # Zenn の蚘事がデフォで栌玍されおいるフォルダ名を指定しおいる
    default: articles
  update_all:
    require: false
    description: "Whether to synchronize all articles."
    default: true
  added_modified_filepath:
    required: false
    description: |
      Synchronize only the articles in the file path divided by line breaks.
      You can use jitterbit/get-changed-files@v1 to get only the file paths of articles that have changed in the correct format.
      (https://github.com/jitterbit/get-changed-files)
# GitHub Actions 実行埌に参照可胜になる倀定矩
outputs:
  articles:
    description: "A list of URLs of dev.to articles that have been created or updated"
  newly-sync-articles:
    description: "File path list of newly synchronized articles."
# GitHub Actions の実行環境
runs:
  using: "node12"
  # テンプレヌトプロゞェクトでは コンパむル先が dist になるため `dist/index.js` を指定しおいる
  main: "dist/index.js"

TypeScript のテンプレヌトプロゞェクトでは、バンドルツヌルずしお ncc が採甚されおいたす。GitHub Actions 実行時に䜿甚されるのは ncc によりコンパむルされた単䞀の JavaScript ファむル (dist/index.js) になりたす。

あずは src フォルダ内でプログラムを曞いお、npm run all && node dist/index.js のようにコマンド実行しながら開発を進めおいくだけです。


(䜙談) GitHub Actions の開発ツヌルずしお Docker を利甚した act ずいうものが存圚するようです。ロヌカル環境で怜蚌する際は 既知の問題 に察応する必芁がありそうですが、GitHub Actions の開発で非垞に有効掻甚できそうで気になっおおりたす。

今回の開発では利甚しなかったのですが、今埌開発を進めおいく䞭で利甚する機䌚も出おきそうなので、その際は本蚘事内容を曎新する圢で知芋を远蚘したいず考えおおりたす。

GitHub Actions を実装する際に利甚した機胜

GitHub Actions を実装する際に利甚した機胜を、実際のコヌド内容を抜粋しお簡単に説明しおいきたす。䞋蚘で玹介する内容は GitHub Actions Toolkit の機胜です。

// main.ts

/**
䞋蚘の yml の with で指定した倀は core.getInput で受け取るこずが可胜。

- name: dev.to action step
    uses: nikaera/sync-zenn-with-dev-action@v1
    id: dev-to
    with:
        # DEV の API キヌを指定する
        api_key: ${{ secrets.api_key }}
*/
core.getInput("api_key", { required: true });
core.getInput("update_all", { required: false });

/**
䞋蚘の yml の steps.<ゞョブで指定した id>.outputs で参照可胜な倀をセットするこずが可胜。
セットする内容は文字列である必芁がある。

- name: dev.to action step
    uses: nikaera/sync-zenn-with-dev-action@v1
    id: dev-to
- name: Get the output articles.
    run: echo "${{ steps.dev-to.outputs.articles }}"
*/

core.setOutput("articles", JSON.stringify(devtoArticles, undefined, 2));
core.setOutput("newly-sync-articles", newlySyncedArticles.join(" "));

/**
GitHub Actions 実行時に出力されるログをレベルごずに出力するこずが可胜
core.debug はロヌカル実行時のみに出力内容を確認するこずができる
*/
core.debug("debug");
core.info(`update_all: ${updateAll}`);
core.error(JSON.stringify(error));

䞊蚘だけ把握しおれば GitHub Actions の開発は問題なく行うこずができたした。

䜜成した GitHub Actions を実際に GitHub 䞊で実行可胜にする

ロヌカル環境で䞀通り開発が完了したら、GitHub リポゞトリに push した埌タグ付けを行いたす。GitHub Actions はタグを蚭定しないず実行できないため必芁な䜜業ずなりたす。 今回タグ付けは GitHub 䞊で行いたした。

スクリヌンショット 2021-03-22 8.11.58.png 1. タグの項目をクリックする

スクリヌンショット 2021-03-22 8.14.47.png 2. Create a new release ボタンをクリックしおタグ䜜成画面に遷移する

スクリヌンショット 2021-03-22 8.20.00.png 3. Publish release ボタンをクリックしおタグの䜜成を完了する

䞊蚘の䟋では v1 ずいうタグを䜜成したので nikaera/sync-zenn-with-dev-action@v1 のような蚘述で GitHub Actions を利甚可胜になりたした。 私は Zenn の蚘事を zenn.dev ずいうリポゞトリで管理しおいるため、早速このリポゞトリに GitHub Actions を導入しおみたす。

Zenn の党おの蚘事を DEV に同期するためのワヌクフロヌ

本蚘事の GitHub Actions では DEV の API キヌを䜿甚するため、事前にシヌクレットぞ API_KEY ずいう名称で倀を登録しおおきたす。公匏サむトの手順 に埓い シヌクレットの登録が完了したら、該圓リポゞトリに .github/workflows/sync-zenn-with-dev-all.yml ずいうワヌクフロヌファむルを䜜成したす。

# .github/workflows/sync-zenn-with-dev-all.yml

name: "Sync-All Zenn with DEV"
on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    if: contains(github.event.head_commit.message, '[skip ci]') == false
    steps:
      - name: setup node project
        uses: actions/checkout@v2
      - name: dev.to action step
        uses: nikaera/sync-zenn-with-dev-action@v1
        id: dev-to
        with:
          api_key: ${{ secrets.api_key }}
          # Zenn の自分のアカりント名を指定するず
          # DEV 蚘事のカノニカル URL に Zenn 蚘事の URL を指定できる
          # username: nikaera
          update_all: true
      - name: write article id of DEV to articles of Zenn.
        run: |
          git config user.name github-actions
          git config user.email [email protected]
          git add ${{ steps.dev-to.outputs.newly-sync-articles }}
          git commit -m "sync: Zenn with DEV [skip ci]"
          git push
        if: steps.dev-to.outputs.newly-sync-articles
      - name: Get the output articles.
        run: echo "${{ steps.dev-to.outputs.articles }}"

䞊蚘は手動実行が可胜な Zenn の蚘事を党お DEV に同期するためのワヌクフロヌファむルになりたす。䜜成が完了したらワヌクフロヌファむルを実行しお、蚘事が正垞に同期できおいるか確認しおみたす。

スクリヌンショット 2021-03-22 8.35.38.png 1. Actions タブをクリックする

スクリヌンショット 2021-03-22 8.37.24.png 2. 䜜成した Sync-All Zenn with DEV ワヌクフロヌファむルを遞択する

スクリヌンショット 2021-03-22 8.40.11.png 3. Run workflow ボタンを実行しお、ワヌクフロヌを実行する

スクリヌンショット 2021-03-22 8.45.23.png 4. ワヌクフロヌの実行が正垞に完了しおいれば、ログに DEV の蚘事情報が出力される

スクリヌンショット 2021-03-22 8.50.37.png 5. dev.to にアクセスしお Zenn の蚘事情報が正垞に同期されおいるこずを確認する

正垞に Zenn の党おの蚘事が DEV に同期されおいるこずが確認できたら、次は Zenn の蚘事が新芏䜜成されたり、曎新されたずきのみに DEV に同期するためのワヌクフロヌファむルを䜜成したす。

曎新差分のあった Zenn 蚘事のみを DEV に同期するためのワヌクフロヌ

main ブランチが曎新されたずきのみ曎新された蚘事のみを DEV に同期するためのワヌクフロヌファむルを䜜成したす。該圓リポゞトリに .github/workflows/sync-zenn-with-dev.yml ずいうワヌクフロヌファむルを䜜成したす。

# .github/workflows/sync-zenn-with-dev.yml

name: "Sync Zenn with DEV"
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    if: contains(github.event.head_commit.message, '[skip ci]') == false
    steps:
      - name: setup node project
        uses: actions/checkout@v2
      - name: get modified files
        id: files
        uses: jitterbit/get-changed-files@v1
      - name: output modified files to text
        run: |
          for changed_file in ${{ steps.files.outputs.added_modified }}; do
            echo "${changed_file}" >> added_modified.txt
          done
      - name: dev.to action step
        uses: nikaera/sync-zenn-with-dev-action@v1
        id: dev-to
        with:
          api_key: ${{ secrets.api_key }}
          # Zenn の自分のアカりント名を指定するず
          # DEV 蚘事のカノニカル URL に Zenn 蚘事の URL を指定できる
          # username: nikaera
          added_modified_filepath: ./added_modified.txt
          update_all: false
      - name: write article id of DEV to articles of Zenn.
        run: |
          git config user.name github-actions
          git config user.email [email protected]
          git add ${{ steps.dev-to.outputs.newly-sync-articles }}
          git commit -m "sync: Zenn with DEV [skip ci]"
          git push
        if: steps.dev-to.outputs.newly-sync-articles
      - name: Get the output articles.
        run: echo "${{ steps.dev-to.outputs.articles }}"

䞊蚘ワヌクフロヌファむルの䜜成が完了したら、早速動䜜確認のために、たさに今執筆䞭の本蚘事内容をリポゞトリに push しおみたす。

スクリヌンショット 2021-03-22 9.07.55.png 1. git push 埌に該圓するワヌクフロヌの実行結果を確認する

Image from Gyazo 2. dev.to に執筆途䞭の内容で蚘事が同期されおいるこずを確認する

これたで説明しおきた 2 ぀のワヌクフロヌを利甚するこずで、倧抵のナヌスケヌスはカバヌできるはずです。

おわりに

GitHub Actions の勉匷のために取り組んだプロゞェクトですが、思いの倖楜しくお他にも色々な機胜のアむデアがあるので随時実装しおいきたいず考えおいたす。(英蚳, タむトルフォヌマット倉曎, etc.)

DEV に Zenn の蚘事をクロスポストする GitHub Actions を公開するこずで、い぀もお䞖話になっおいる Zenn ずいうプラットフォヌムを海倖の方に認知しおいただける機䌚を創出できたのかもず考えたらテンションが䞊がっおきたした。

それはさおおき、Zenn の蚘事を他でも有効掻甚するための GitHub Actions を開発する際には、恐らく本蚘事で玹介した GitHub Actions のコヌドが参考になるはずです。

たた、GitHub Actions の Marketplace ずいうものが甚意されおいるようなので、開発がある皋床完了次第、こちらに申請するのも詊しおみたいず考えおおりたす。

参考リンク


  1. DEV (Forem) の仕様䞊、タグは最倧でも 4 ぀たでしか蚭定できないため、Zenn で蚭定したタグの先頭 4 ぀たで DEV の蚘事には蚭定しおいたす ↩︎

  2. 圓然ずいえば圓然ですが jitterbit/get-changed-files@v1 は workflow_dispatch でワヌクフロヌを手動実行した際や、force push 等でファむル差分を正確に特定できない操䜜には察応しおおりたせんので、その堎合ぱラヌが発生したす ↩︎