Azure PipelineからAzure DevOps Wikiを更新する

5 min read読了の目安(約5100字

まったくもって誰得なのかわからないような記事ですが、Zennに書いて見るネタが欲しかったので書いてみることにしました。

動機

社内のとある活動で経費精算を取りまとめたかったのですが、エンジニアなのにExcelシートってなんか嫌じゃないですか? なので申請時にYAMLを書いてもらってプルリク投げてもらう運用にしたかったのです。

YAMLからMarkdownテーブルへ

こっちはTypescriptでサクッと(TS2.x以降久しぶりに触ったので興味津々で調べていたのでちっともサクッとではなかったのですが)作ったので割愛します。npm start するとYAMLを食ってMarkdownを吐き出してくれるごくごく簡単なものです。

なぜGithubではない?

社内向けなので社内で使っているAzure DevOpsなのです。Azure DevOpsはその名の通りAzureの親戚でMicrosoftが提供しているGithubのようなものです。これがまた意外とスルメのようなサービスで最初はとっつきにくいのですが、MS内で実プロジェクト利用で揉まれまくっていて実に細部までうまくまとめられた仕様で、使いこなせるとGithubよりもかゆいところに手が届く完成度の高いサービスです。

しかし、Githubがマイクロソフトに買収された今、2つがそのうち統合されることは想像に難くない訳でおそらくはGithub側にAzure DevOpsのテイストが入り込んでくるんじゃないかと思われます。(その片鱗はGithub flowにすでに現れていて、Github flowのyaml構文はAzure Pipelinesの構文と非常によく似ています)

で本文

前置きが長くなりました。やりたいことは

  1. PipelineでMarkdownをビルド
  2. そのMarkdownをWikiに反映

なのですが、Azure DevOpsのWikiは実はGitで管理されたMarkdownファイルの集合体です。なので、

  1. PipelineでMarkdownをビルド
  2. WikiのGitをFetchしてくる
  3. そこに1で作ったMarkdownを上書き
  4. git commit
  5. git push

こんな流れになります。

どうやったのよ?

pipeline用のYAML全文をさらします。

trigger:
  - main

pool:
  vmImage: "ubuntu-20.04"

stages:
  - stage: build
    jobs:
      - job: build
        steps:
          - script: docker-compose run markdown
            displayName: create markdown
          - script: >
              mkdir $(Build.ArtifactStagingDirectory)/Markdown &&
              cp ./processor/output/ExpenseReport.md $(Build.ArtifactStagingDirectory)/Markdown &&
              git log --pretty=format:"%s" -n 1 > $(Build.ArtifactStagingDirectory)/Markdown/commit.txt
            displayName: pack artifacts
          - publish: $(Build.ArtifactStagingDirectory)/Markdown
            artifact: Markdown
      - job: publish
        dependsOn: build
        steps:
          - checkout: git://MyPrj/MyPrj.wiki@wikiMaster
            persistCredentials: true
          - script: >
              git config --global user.email "dummy@example.com" &&
              git config --global user.name "dummy" &&
              git checkout wikiMaster
            displayName: initialize git
          - download: current
            artifact: Markdown
          - script: cp $(Pipeline.Workspace)/Markdown/ExpenseReport.md ./経費精算/精算シート.md
            displayName: apply change
          - script: git branch -a
          - script: >
              git add * &&
              git commit -m "`cat $(Pipeline.Workspace)/Markdown/commit.txt`"
            displayName: commit change
          - script: git push origin wikiMaster
            displayName: push to publish

buildジョブは単にdocker-compose経由でnpmを実行しているだけなので解説は割愛します。
ちょっとコツが必要だったのはpublishジョブの方でした。

1. 別のリポジトリからpullしてくる

          - checkout: git://MyPrj/MyPrj.wiki@wikiMaster
            persistCredentials: true

これはcheckoutタスクを利用します。通常はJOBの初期工程で自動的にトリガーされたリポジトリからチェックアウトされるのですが、本件では別のリポジトリ(Wiki用のリポジトリ)を扱いたいので明示的に指定します。

ポイントは

  • チェックアウトするリポジトリの指定
    • 別のプロジェクトやGithubからのチェックアウトだともう少し複雑な書き方になるのですが、同じOrganization内のリポジトリであればgit://プロジェクト名/リポジトリ名で良いです。
    • デフォルトのWikiはプロジェクト名.wikiというリポジトリ名です
    • デフォだとmasterかmainブランチになってしまうので明示的にブランチ名を@で指定します。WikiのデフォルトブランチはwikiMasterです。
  • 初めてパイプラインを実行するときに「こいつリポジトリをいじろうとしているぞ」みたいなINFOが出るのでGUI上からOKする必要があります(キャプチャは撮ってません)
  • persistCredentialsはあとからpushするときに必要になるのでtrueに指定します。

詳細は下記を参照すると良いです。

https://docs.microsoft.com/ja-jp/azure/devops/pipelines/repos/multi-repo-checkout?view=azure-devops#inline-syntax-checkout

2. Gitの設定

          - script: >
              git config --global user.email "dummy@example.com" &&
              git config --global user.name "dummy" &&
              git checkout wikiMaster

2.1 email, name

https://docs.microsoft.com/ja-jp/azure/devops/pipelines/scripts/git-commands?view=azure-devops&tabs=yaml
こちらを見るとあらかた書いてあるのですが、email, nameを設定しておく必要があります。

2.2 ブランチの設定

ここはcheckoutタスクの出力結果と小一時間にらめっこする羽目にあったのですが、checkoutタスクは素直にpullはしておらずfetchしかしていません。そのためそのまま変更をcommitしてpushをしようとすると下記のように怒られます。

error: src refspec wikiMaster does not match any
error: failed to push some refs to 'https://newgyu.visualstudio.com/MyPrj/_git/MyPrj.wiki'

明示的にcheckoutしてwikiMasterブランチ上で変更をcommitした後にpushすることにしました。

Pushに必要なpermission

          - script: git push origin wikiMaster
            displayName: push to publish

最後にpushするためにはそのパイプラインが一定の権限を持っている必要があります。権限不足の場合にはこんなエラーが出ると思います。

! [remote rejected] wikiMaster -> wikiMaster (TF401027: You need the Git 'GenericContribute' permission to perform this action. Details: identity 'Build\12014033-3fcb-4980-b04e-98b51384fd49', scope 'repository'.)
error: failed to push some refs to 'https://newgyu.visualstudio.com/MyPrj/_git/MyPrj.wiki'

GenericContributeってなんじゃいってかんじでしたが、

という設定でうまくいきました。

下記のStackOverFlowを参考にしました。

https://stackoverflow.com/questions/56541458/azure-pipeline-doest-allow-to-git-push-throwing-genericcontribute-permission

ということで

申請yamlの入ったPullRequestがmainブランチにマージされると自動的にWiki上にmarkdownの申請ページが反映されるようになりました。