🧑‍🤝‍🧑

Developers BlogをZenn Publicationに移行した話

2023/12/19に公開

こんにちは、Luup SREチームのにわです。
これは Luup Advent Calendar 2023 の 19 日目の記事です。

最初に

LuupのDevelopers BlogはこれまでZennでいう個のアカウントとして書いてきました。
そして、以下のような需要があり、Publicationへ移行することにしました。

  • Developers Blogの記事を個人のアカウントから発信したい
  • 個人アカウントベースでも発信を積極的にしていってほしい
  • 管理権限を冗長化するために複数のアカウントに持たせたい

その中で、GitHub上でのレビューフローを変えずに、仕組みを整えて「Developers BlogをPublicationに移行した話」を今回お送りします。

なお、LuupのPublicationはこちらです。
https://zenn.dev/p/luup_developers

本題

さて、ZennでPublicationをお使いの皆さんは記事のレビューをどのようにされているでしょうか。

  • プレビューしたものを画像にした上で、SlackやTeamsでレビューをしている
  • GitHubで個人個人のアカウントを共有した上で、Pull Requestのレビューをしている

などなどいろいろな方法があると思います。

Luupでは、Publicationを使う前に 「一つのGitHubリポジトリで社内レビュー完了後に、ひとまとまりの個のアカウント上に記事を公開する」 という事を行っていました。

Publication移行に当たってのLuupでの要件としては以下のとおりです。

  • レビューはGitHubのPull Requestでしたい
  • レビューはこれまでの方法とほとんど変えない状態でGitHub上にて運用したい
  • 発信を個人のアカウントに帰属したうえで、Zennの記事を各アカウントから出したい
  • 情報資産として残しつつ、共有したい

発信を個人のアカウントに帰属すると何が良いかについて補足すると、なにかの都合、たとえば退職や参画終了したときなどに、個人のアカウントに執筆者の記事を関連付けて維持できます。

これを実現する良い方法はないか検討したところ、良い方法を考え出せました。
それは、「発信を個人のアカウントに帰属できるように、各個人のZennアカウント用GitHubリポジトリにコピーする」です。これで今までのレビューフローを変えずに実現できそうと気づきました。
Publicationの方針について、業務委託の方も社員の方もいらっしゃり、それぞれに自分のプライバシーについて考えがあるので、Zennを管理する方以外はPublicationの参加を自由としました。

技術面としてはGithub Actionsで以下のようにPRを作るようにしました。

GitHub MarketplaceにあるActionsのthe-autobotを利用しようと思いましたが、一部動かない部分などがあったりしたため、参考にしつつ再構成しました。
許諾事項として、the-autobotはMIT Licenseでつくられており、ソースコードを一部お借りしています。

動きとしては記事執筆者がPRを作成したときのみPRコピーがされます。
その後、設定ファイル(ini)を参照してPRコピー先のリポジトリが決定します。

主要な部分は以下のとおりです。

actions.yml
    - run: ./entrypoint.sh
      shell: bash
      env:
        GIT_CONFIG_USER_EMAIL: ${{ inputs.git_config_user_email }}
        GIT_CONFIG_USER_NAME: ${{ inputs.git_config_user_name }}
        SOURCE_DIR: ${{ inputs.source_dir }}
        SOURCE_DIR_COPY_GLOB: ${{ inputs.source_dir_copy_glob }}
        TARGET_REPO: ${{ inputs.target_repo }}
        TARGET_DIR: ${{ inputs.target_dir }}
        PR_TARGET_REPO_BASE_BRANCH: ${{ inputs.pr_target_repo_base_branch }}
        PR_TARGET_REPO_COMPARE_BRANCH: ${{ inputs.pr_target_repo_compare_branch }}
        PR_TITLE: ${{ inputs.pr_title }}
        PR_LABEL: ${{ inputs.pr_label }}
        PR_DESCRIPTION_TEXT: ${{ inputs.pr_description_text }}
        GH_ACCESS_TOKEN:  ${{ inputs.gh_access_token }}
entrypoint.sh, ini file
entrypoint.sh
#!/bin/bash

set -x -e

DEFAULT_AUTOBOT_TEXT="[autobot] [$(date '+%d-%m-%Y %H:%M:%S')] Automated changes"

# PR base and compare branch cannot be the same
if [ "$PR_TARGET_REPO_BASE_BRANCH" = "$PR_TARGET_REPO_COMPARE_BRANCH" ]
then
  echo "Pull request base branch cannot be the same as the compare branch"
  exit 1
fi

# Default autobot branch
if [ -z "$PR_TARGET_REPO_COMPARE_BRANCH" ]
then
    PR_TARGET_REPO_COMPARE_BRANCH="autobot/$(printf '%s' "$GITHUB_SHA" | cut -c 1-7)/$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
fi

# Default PR title
if [ -z "$PR_TITLE" ]
then
    PR_TITLE="chore: "$DEFAULT_AUTOBOT_TEXT
fi

# Default PR description text
if [ -z "$PR_DESCRIPTION_TEXT" ]
then
    PR_DESCRIPTION_TEXT=$DEFAULT_AUTOBOT_TEXT
fi

echo "Configuring git"
export GITHUB_TOKEN=$GH_ACCESS_TOKEN
git config --global user.email "$GIT_CONFIG_USER_EMAIL"
git config --global user.name "$GIT_CONFIG_USER_NAME"

echo "Cloning target repository"
CLONE_DIR=$(mktemp -d)
git clone "https://$GIT_CONFIG_USER_NAME:$GH_ACCESS_TOKEN@github.com/$TARGET_REPO" "$CLONE_DIR"

echo "Copying contents to target repo"

# Enable/Disable globbing
shopt -s globstar extglob dotglob
cp -R "$SOURCE_DIR"$SOURCE_DIR_COPY_GLOB "$CLONE_DIR/$TARGET_DIR"
shopt -u  globstar extglob dotglob

echo "Creating branch to commit changes"
cd "$CLONE_DIR"
git checkout -b "$PR_TARGET_REPO_COMPARE_BRANCH"

echo "Committing changes"
git add .
if git status | grep -q "Changes to be committed"
then
    gh auth status
    gh label create "$PR_LABEL" -R "$TARGET_REPO" --description "$PR_LABEL" --color EDCB18 --force | echo "status: $?"
    git commit -m "Updates from https://github.com/$GITHUB_REPOSITORY/commit/$GITHUB_SHA"
    # git push -u origin HEAD:"$PR_TARGET_REPO_COMPARE_BRANCH"
    git push -u "https://$GIT_CONFIG_USER_NAME:$GH_ACCESS_TOKEN@github.com/$TARGET_REPO" HEAD:"$PR_TARGET_REPO_COMPARE_BRANCH"
    echo "Creating a pull request"
    gh pr create -t "$PR_TITLE" \
        -b "$PR_DESCRIPTION_TEXT" \
        -R "$TARGET_REPO" \
        -B "$PR_TARGET_REPO_BASE_BRANCH" \
        -H "$PR_TARGET_REPO_COMPARE_BRANCH" \
        -l "$PR_LABEL"
else
    echo "No changes detected"
fi
[hoge-user]
repository=hoge-user/test-zenn-blog
app_installation_id=<GitHub appsのinstallation_id>

苦労したところ

非公開リポジトリに対してもPRをコピーできるようにGitHub Appをインストールしていただくようにしました。
これを実現するにあたり、Github Actionsの構築において、認証情報を取得するのには苦労しました。

どの部分かというと、GitHub Appsがインストールされたリポジトリの認証・認可の方法を私がその時点で理解できていませんでした。
そこでGitHubサポートに連絡してどのようにすればよいか問い合わせました。

以下のようにinstallation IDを付与したうえで、記載すればよいことが分かりました。

- name: Generate token
   id: generate_token
   uses: tibdex/github-app-token@v1
   with:
     app_id: ${{ secrets.APP_ID }}
     private_key: ${{ secrets.APP_PRIVATE_KEY }}
     installation_id: <YOUR_INSTALLATION_ID>

installation IDについてGitHubサポートからの解説です。

To find your installation ID:

  • Go to the settings page for your repository hoge-user/test-zenn-blog
  • Click on GitHub Apps under Integrations in the menu on the left hand side.
  • Click on Configure for the hogehoge-copy-blog-bot app
  • You should see that the URL has the format https://github.com/settings/installations/<YOUR_INSTALLATION_ID>, where <YOUR_INSTALLATION_ID> is an eight digit number. This is the number that you'll need to add to your workflow file.

The reason for this is that, when the app gets a temporary access token, it makes requests with the token in the context of a specific installation. The app's private access token can only perform actions for a specific instance of the app installation; if it tries to create or make a call for a resource in another account or repository, it won't work as it won't be recognised as a valid token by the receiving repository. Adding the installation ID should resolve that.

GitHubサポートで質問に対して丁寧に答えていただいて、誠に感謝です。
I really appreciate GitHub support with a lot of kindness.


GitHub Appsのinstallation IDを知らなかったので、今回利用してみて一時的なアクセストークンを取得するのにすごく良いと思いました。

最後に

さて、「Developers BlogをPublicationに移行した話」をお送りしました。
何かの形で皆さんが利用されているZennにおけるPublication機能のレビューフローの参考になれば幸いです。

Developers Blogについてはほんの一例ですが、Luupの開発環境には今回のようにアイデアベースで改善できるのはもちろんのこと、解決すべき課題も多く、やりがいがあります。
もし興味をお持ちいただけたら、弊社の採用ページから是非お気軽にご連絡ください。
https://recruit.luup.sc/

Luup Developers Blog

Discussion