tech.guitarrapc.cóm

Technical updates

GitHub Appのインストールアクセストークンを使ってPATを排除する

GitHub ActionsでPATを絶対使いたくないマンです。GitHub Appのインストールアクセストークンも使いたくなかったのですが、PATより100倍マシなので最近はこれを使うようにしています。 今回は、GitHub Actionsにおけるアクセストークンを整理し、GitHub Appのインストールアクセストークンを使う方法を説明します。

PATの問題

GitHub ActionsからGitHub APIを叩くときに使うアクセストークンには、3つの選択肢があります。

通常はGITHUB_TOKENを使うのがベストプラクティスですが、これには次の制約があります。

  • 他のリポジトリにアクセスできない
  • PRやPushに用いたとき、ワークフローがトリガーされない
  • ブランチプロティションルールの除外ルールに設定できない
  • Issueに@Copilotをアサインできない2

上記制約はPATで回避できますが、PATには次のような問題があり扱いに困ります。個人リポジトリならともかく、組織リポジトリでPATを使うのは避けたいところです。

  • トークン発行が個人単位3
  • ブランチプロテクションの除外ルールが発行者を対象
  • トークン発行者として処理が実行される4
  • トークンの期限が長く漏洩時のリスクが高い
  • トークンの対象リポジトリ・権限が発行/編集時に固定される

GitHub Appによる解決と使い分け

GitHub AppのインストールアクセストークンはPATの問題を解決します。ただし、CopilotのアサインにはPATが必要なのでそこは注意してください。

PAT GitHub App
トークン発行が個人単位 トークン発行がGitHub App単位で個人に依存しない
ブランチプロテクションの除外ルールが発行者を対象 ブランチプロテクションの除外ルールにGitHub Appを指定できる
トークン発行者として処理が実行される GitHub Appとして処理が実行できる
トークンの期限が長く漏洩時のリスクが高い トークンがアクションの実行事に発行、アクション終了で無効化される
トークンの対象リポジトリ・権限が発行/編集時に固定される トークンの対象リポジトリ・権限をGitHub App、組織承認の2段階で管理できる

GitHub Appの用意とアクセストークン発行処理は面倒ですが、PATを使うよりは遥かにマシです。以上を踏まえて、私はGitHub ActionsでGitHub APIを叩くときは次の基準で使い分けています。

  • 他リポジトリにアクセスしたいならGitHub App
  • ブランチプロテクションルールを回避したいならGitHub App
  • IssueにCopilotをアサインしたいならPAT
  • 上記に該当しないケースはGITHUB_TOKEN
    • GitHub Appを用意できない場合はPAT

フローチャートにすると以下のようになります。

クリックでmermaidを表示

flowchart TD
  GITHUB_TOKEN["GITHUB_TOKEN"]
  GITHUB_APP["GitHub App Installation Access Token"]
  PAT["Personal Access Token (PAT)"]
  START["GitHub操作を行う"]
  A{GitHub Appを用意できる?}

  START -->D{他リポジトリにアクセスする?}
  D -- Yes --> A
  D -- No --> E{ブランチプロテクションルールを回避する?}
  E -- Yes --> A
  E -- No --> F{IssueにCopilotをアサインする?}
  F -- Yes --> PAT
  F -- No --> GITHUB_TOKEN

  A -- Yes --> GITHUB_APP
  A -- No --> PAT

GitHub Actionsにおけるアクセストークンの選択方針フローチャート

それでは、GitHub Appのインストールアクセストークンを使う方法を見てみましょう。

GitHub Appを使う

GitHub Appを使うには、以下の手順を踏みます。

  • GitHub Appを用意する
  • GitHub Appをインストールする
  • GitHub Appの情報をSecretsに登録する
  • GitHub Actionsでインストールアクセストークンを取得する

GitHub Appを用意する

GitHub Appを用意します。Organization単位で作成する場合は、Organization > Settings > Developer settings > GitHub Apps > New GitHub App から作成します。個人で作成する場合は、個人アカウントのSettings > Developer settings > GitHub Apps > New GitHub Appから作成します。

GitHub Appの名前、説明、Homepage URLなどを設定します。Webhookは不要なのでチェックを外します。 Homepage URLはアプリページに表示されるので、localhostやHP、GitHubプロフィールURLなどが安全でしょう。Where can this GitHub App be installed?は、外部に利用させたくないならOnly on this accountOnly on this organizationにします。

設定全般

重要なのがPermissionsです。ここでGitHub Appに与える権限を設定します。必要最低限の権限だけ与えつつ、Actionsで必要になる権限は付与しておく必要があります。たとえば、PRの情報を取得したいならPull requests: Read-onlyが必要ですが、PRの作成や更新をしたいならPull requests: Read and writeが必要です。また、git cloneしたいならContents: Read-onlygit pushしたいならContents: Read and writeが必要です。

権限設定

GitHub Appをインストールする

GitHub Appを対象のリポジトリや組織にインストールします。Organization単位でインストールする場合は、Organizationのオーナー権限が必要です。

インストールすると対象のリポジトリを選択できるので、ここで必要なリポジトリに絞り込むとよいでしょう。全部のリポジトリ、あるいはパブリックリポジトリ全部にしていると影響範囲を把握しづらくなります。

GitHub Actions Appをインストールするとリポジトリアクセスを指定できる

インストール後にGitHub Appの権限を変更した場合、組織のオーナーは変更を承認するか選択できます。

インストールしたアプリを削除もできます。

アプリの削除

GitHub Appの情報をSecretsに登録する

GitHub AppのApp IDPrivate Keyを、Organization SecretsやリポジトリSecretsに登録します。

以下はダミーのGitHub App情報5ですが、ここにあるApp IDを控えます。

GitHub Appの概要

Private KeyはGitHub App作成時点ではまだありません。GitHub App作成後、下にスクロールするとPrivate Keyがあるので、Generate a private keyボタンを押して秘密鍵を生成します。

Private Keyが空とわかる

Private Keyを生成すると自動的にダウンロードされます。秘密鍵の中身をSecretsに登録したらpemファイルは消しましょう。秘密鍵はすぐ消すに限ります。

Private Keyが生成される

ダウンロードされた秘密鍵の例

これらを、GitHub Secretsに登録します。

ACTIONS_BOT_APPID: 上のAPP ID
ACTIONS_BOT_PRIVATE_KEY: 上のPrivate Keyの中身

GitHub Actionsでインストールアクセストークンを取得する

GitHub ActionsのワークフローでGitHub Appのインストールアクセストークンを取得しましょう。幸い、GitHub公式のアクションactions/create-github-app-tokenがあります。以前はサードパーティ製アクションを使う必要がありましたが、公式アクションができたのでこれを使いましょう。

以下はサンプルワークフローです。GitHub Appのトークンを使ったか検証するため、ジョブにはpull requestの権限を与えず、GitHub Appのインストールアクセストークンを取得するときにpull-requestのread権限を与えています。PR一覧が取得できれば成功です。

actions/create-github-app-tokenで取得したトークンは、ステップアウトプットsteps.{id}.outputs.tokenで参照できます。例ではactions/create-github-app-tokenのidをapp-tokenにしているので、steps.app-token.outputs.tokenです。

name: github app token
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  app-token:
    permissions:
      contents: read # no pull request permission
    runs-on: ubuntu-24.04
    timeout-minutes: 3
    steps:
      - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
        id: app-token
        with:
          app-id: ${{ secrets.ACTIONS_BOT_APPID }}
          private-key: ${{ secrets.ACTIONS_BOT_PRIVATE_KEY }}
          permission-pull-requests: read # grant read access to pull requests
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          persist-credentials: false
      - name: List open PRs
        run: gh pr list --state open --limit 5
        env:
          GH_TOKEN: ${{ steps.app-token.outputs.token }} # GitHub App token permission to read pull requests
          GH_REPO: ${{ github.repository }}

ワークフローを実行すると、GitHub Appのインストールアクセストークンが発行され、ghコマンドでPR一覧を取得できます。

ワークフローの実行結果

TIPS: create-GitHub-app-tokenのコツ

GitHub Appのインストールアクセストークンを取得するコツです。

  • permissions-<resource>で必要な権限だけ与える

この時取得できる権限は、GitHub Appへ与えられた権限に限られます。Appに与えられていない権限は取得できません。

# pushが必要ならpermission-contents: writeを与える
- uses: actions/create-github-app-token@v2
  id: app-token
  with:
    app-id: ${{ secrets.ACTIONS_BOT_APPID }}
    private-key: ${{ secrets.ACTIONS_BOT_PRIVATE_KEY }}
    permission-contents: write
  • 他のリポジトリにアクセスする場合はownerrepositoriesを指定する
# ownerとrepositoriesを指定して他リポジトリにアクセスする
- uses: actions/create-github-app-token@v2
  id: app-token
  with:
    app-id: ${{ vars.APP_ID }}
    private-key: ${{ secrets.PRIVATE_KEY }}
    owner: ${{ github.repository_owner }}
    repositories: |
      repo1
      repo2

まとめ

GitHub Appを用いると、ごく限定的なケースを除いてPATを排除できます。たとえマシンユーザーがあったとしても、PATよりはGitHub Appのほうが細かく管理できるので好ましいでしょう。

まずはGITHUB_TOKENでできるか検討して、どうしても無理な場合にGitHub Appを用意するのがおすすめです。 やってみると簡単です。ただ、GitHub App用のアクセストークンを取得するのに1ステップ増えるのは度々面倒に感じます。PATのリスクを軽減できている、と考えて我慢です。

参考


  1. コラボレーターとしてリポジトリにアクセスする場合は、Fine-grained access tokenではなくPersonal access token (classic)を使う必要がある
  2. CopilotのアサインにはCopilotライセンスが必要なため、ライセンスを付与したユーザーのPATが必要です。
  3. 組織単位で発行ができず、個人が退職した場合にトークン管理が困難になる。その割にマシンユーザーも非推奨
  4. ghコマンドでPRを作ると、トークン発行者がPR作成者になるなど
  5. すでに消しているので使おうとしても無駄です。

Trusted Publishingでcrates.ioパッケージ公開をトークンレスにする

crates.io Trusted Publishingが2025年7月11日にアナウンスされ、OpenID Connect (OIDC)を使ってトークンレスでCIからRustパッケージを公開できるようになりました。

今回は、crates.io Trusted Publishingを使ってGitHub ActionsからトークンレスでRustパッケージを公開するメリットと手順を解説します。

はじめに

Trusted Publishingにする動機やメリットについては、NuGetのTrusted Publishingを紹介する記事で解説しています。 creates.ioも従来はPATを用いる必要があったのですが、Trusted Publishingを使うことでOIDCを利用したトークンレスでのパッケージ公開が可能です。 簡単な設定で十分な効果を得られるので、ぜひ利用していきましょう。

設定方法

公式ドキュメントに沿って設定していきましょう。 crates.ioとGitHub Actionsそれぞれで設定します。

crates.ioでTrusted Publishingを設定

crates.ioにログインし、設定したいCrateのページを開きます。 SettingsタブにTrusted PublishingセクションがあるのでAddをクリックします。

Trusted PublishingのAdd

Trusted Publishingの設定画面が表示されるので、以下を入力Saveをクリックします。ワークフローファイル名を入力すると、リポジトリにワークフローファイルが存在するかチェックしてくれて便利です

  • Publisher: GitHubを選択 (GitLandも選択可能)
  • Repository owner: 自動入力される
  • Repository name: GitHubリポジトリ名
  • Workflow filename: パッケージの公開処理をするワークフローファイル名
  • Environment name: GitHub Environmentを使っているなら指定

Trusted Publishingの設定画面

ちなみにcrates.ioへの公開をTrusted Publishingに限定できるので、動作確認できたらチェックを付けておくと個人PATを使う事故を防げます。

trusted publishingに限定する

GitHub Actionsワークフローの設定

GitHub Actionsのワークフローを設定します。コツは3つです。

  • ワークフローファイル名はcrates.ioで指定したWorkflow filenameと一致させる
  • rust-lang/crates-io-auth-actionアクションを実行するジョブにid-token: writeの権限を付与する
  • rust-lang/crates-io-auth-actionアクションを使ってcrates.ioの短命トークン取得する

サンプルのワークフローrelease.yamlは次の通りです。git tagでバージョンタグをプッシュしたときにパッケージを公開します。

name: Publish crates
on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  publish:
    permissions:
      contents: read
      id-token: write # OIDCトークン発行のために必要
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.
      # ビルド
      - run: cargo build --verbose
      # cratesの短命トークンを取得
      - uses: rust-lang/crates-io-auth-action@b7e9a28eded4986ec6b1fa40eeee8f8f165559ec # v1.0.3
        id: auth
      # 取得した短命トークンを使ってcratesを公開
      - run: cargo publish --manifest-path csbindgen/Cargo.toml
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}

タグをプッシュすれば、GitHub ActionsがトリガーされてOIDCを使ってcrates.ioのパッケージが公開/更新されます。

まとめ

他のパッケージサービスと同様に、crates.ioもTrusted Publishingが使えます。トークンレスでパッケージ公開できるので、ぜひ使っていきましょう。

参考

Visual Studio 2026のオプション設定

Visual Studio 2026はオプション設定が刷新されて、VS Codeのようにシームレスに設定できます。

Visual Studio 2022のオプション設定画面 Visual Studio 2026のオプション設定画面
Visual Studio 2022のオプション設定 Visual Studio 2026のオプション設定

今回は、私がいつも設定しているオプション設定の一覧を紹介します。

設定のリセット

Visual Studioはメジャーリリースごとに設定の変化が大きいため、そのたびにデフォルトへリセットしています。 この記事もデフォルトにリセットした上で、変更箇所だけピックアップします。

alt text

Visual Studio 2026から、変更箇所の左にマークがつくようになってわかりやすくなりました。地味ですが、めちゃめちゃいい改善です。本当によい。

オプションの変更箇所

また項目左にある歯車をクリックすると、そのオプションだけリセットできるようになったものも神です。

項目指定でリセット

レイアウト

VS Codeのレイアウトが好きなので、これに寄せています。具体的には、左からソリューションエクスプローラーソースコードエディターGitHub Copilotの順番です。

  • Solution Explorer: 右配置 -> 左配置 (自動ハイド)
  • Git Changes: 右配置 -> 左配置 (自動ハイド)
  • GitHub Copilot: 左配置 -> 右配置 (常時表示)
  • View -> Output: 非表示 -> 下配置
  • View -> ErrorList: 非表示 -> 下配置
  • View -> TaskList: 非表示 -> 下配置
  • View -> Other Windows -> Containers: 非表示 -> 下配置

デフォルトレイアウトは次の通りです。

|---------------------------------------------------------------|
| Tab | Tab2 |                                                  |
|---------------------------------------------------------------|
| Editor                                             | Solution |
|                                                    | Explorer |
|                                                    |          |
|                                                    |          |
|                                                    |          |
|                                                    |          |
|---------------------------------------------------------------|
| Output                                                        |
|                                                               |
|---------------------------------------------------------------|

デフォルトレイアウト

変更後のレイアウトです。VS Codeっぽい。

|--------------------------------------------------------------|
| Solution | Editor                                  | GitHub  |
| Explorer |                                         | Copilot |
|          |                                         |         |
|    OR    |                                         |         |
|          |                                         |         |
|   Tab    |                                         |         |
|   Tab2   |                                         |         |
|--------------------------------------------------------------|
| Output                                                       |
|                                                              |
|--------------------------------------------------------------|

変更後のレイアウト

Tools > Optionsの設定

Environment

Visual Experience

私はWindowsのシステムテーマをダークで利用しています。アプリは基本的にOSのテーマに自動的に同期してほしいので、以下のように設定しています。いろいろな色テーマがあるので、気分で変えるのもいいですね。

  • Color theme: Dark -> Use system setting

Color theme

クリックで色テーマの例を見る

Ice Mint、Mango Paradise、Bubblegumなどの明るいテーマも好きです。

見た目
Ice Mint Ice Mint
Mango Paradise Mango Paradise
Bubblegum Bubblegum
Monnlight Glow Moonlight Glow
Spicy Red Spicy Red

デフォルトONになった気がしますが、Visual Studio 2022からの引継ぎだとoffになっている気がします。サーチは大きくある必要がないので、コンパクトを選んでいます。

  • Use compact menu and search bar: on

Compact menu and search bar

Documents

Unicode保存周りを設定します。特にutf-8 BOMなしで保存すると、日本語コメントなどがGitHub上で文字化け、クローン後も文字化けすることがあるため、BOM付きで保存するようにしています。これは日本語特有な感じがしており、OSSでは英語のみにするならUTF-8 BOMなしが良いです。1

  • Save documents as Unicode when data cannot be saved in codepage: on
  • Automatically save files when Visual Studio is in the background: on
  • Save files with a specific encoding: Japanese (Shift-JIS) - Code page 932 -> Unicode (UTF-8 with signature) - Code page 65001

DocumentsのUnicode設定

タブは左に配置して縦タブ表示しています。VS Codeでは上表示の横タブなのですが、VS Codeより開くタブが多いため、縦タブの方が管理しやすいです。

  • Tabs -> Document Tabs -> Tab layout: Top -> Left

Tab layout

Windows

まだOptionsの統一が完了していないのか、従来の設定画面も残っています。ここも設定しておきましょう。

  • More settings -> Fonts and Colors (下参照)

クリックでFonts and Colorsを開く

フォント設定です。Visual Studioの言語によってですが、日本語の場合MSゴシック系でとても読みにくいため、英語設定のデフォルトであるCascadia Monoに変更しています。言語によってフォントが違うのはいいんですが、デフォルト日本語フォントはそろそろアップデートしてもいいのでは?

structの色はデフォルトがclassに近いので、変化を大きくしています。

  • Font: Cascadia Mono

フォントをCascadia Monoへ

  • Display items -> User Types - Structures: デフォルトをベースにカスタム RGB(81, 114, 156)

User Types - Structureの色

デフォルト

デフォルトの色

カスタム

カスタムの色

Projects and Solutions

ソリューションエクスプローラーで、今開いているファイルが常に選択されるようにしています。VS Codeもそうですが、今はどのIDE/エディタもこの挙動がデフォルトですね。

  • General -> Track Active Item in Solution Explorer: off -> on

ビルドログを自動的に表示するようにしています。

  • General -> Show Output window when build starts: off -> on

Generalの設定

VS2026でリセットすれば.slnxのはずです。ただ、VS2022から引き継ぐと.slnな気がします。.slnxはVisual Studio 2022からプレビューサポートされたのですが、人間が扱える程度に簡素化しておりオススメです。今後は.slnxを使っていきましょう。

  • General -> Default Solution File Format: .slnx

Default Solution File Format

Modernization

.NET Upgrade Assistantを使ってプロジェクトのモダナイズを支援する機能です。VS2022まではVS拡張でしたが、VS2026から標準機能として組み込まれました。

  • Enable legacy Upgrade Assistant: off -> on

.NET Upgrade Assistantの有効化

Text Editor

面白い機能がいくつか追加されています。例えばSelect subword on double clickは、ダブルクリックで「文字列全体」ではなく「文字列の中の単語」を選択します。つまり、SortedSequenceという文字列があったら、offならSortedSequence全体が選択されますが、onならSortedなどダブルリックした箇所の単語だけ選択されます。私はそういう使い方を求めていないのでoffのままです。

Display

ホワイトスペースやゼロ幅文字、括弧の対応カラーリングはコードの可読性向上に寄与するため有効です。特に、Visual Studioはゼロ幅スペースがあってもビルドできちゃうので、妙な攻撃を防ぐ意味でも有効にしています。

  • Show whitespace: off -> on
  • Show zero-width characters: off -> on
  • Enable brace pair colorization: off -> on

テキストのDisplay設定

Advanced

ファイルレベルのCodeLensは有効にしています。

  • Show file level CodeLens indicator: off -> on

まだインサイダーにしかないのですが、Compress blank linesは空行の高さを25%減らすことでコードの可読性を向上させるオプションです。アルファベットとありますが、日本語でも効くので安心して有効にしましょう。

  • Compress blank lines: off -> on
  • Compress lines that do not have any alert indicators: off -> on

Textエディターの詳細設定

2025/12/16時点ではVisual Studio 2026インサイダーにしかないのですが、ミドルクリックでスクロールできるようになるオプションです。ブラウザで自動スクロールさせて長いコードや文章を読む癖があるので、Visual Studioで使えるのうれしいです。

  • Touchpad and mouse wheel scrolling sensivity -> Middle click to scroll: off -> on

Middle click to scroll

目の前に集中して読み下すときに本当に便利です。

中央クリックが効くようになる

GitHub

Copilot Chat

Planningを有効にします。

  • Enable Planning: off -> on

Copilot Codingエージェントを有効にします。

  • Enable Copilot Coding agent (Preview): off -> on

Copilotのカスタム指示を有効にします。

  • Enable custom instructions to be loaded from .github/instructions/*.instructions files: off -> on

alt text

まとめ

リセットしてからの変更項目も減って、かなりデフォルトに寄せつつ使えるようになりました。 VS 2022もリリース当時は早くなった感じがしましたが、こうやって見るとずいぶん遅く感じます。VS 2026は起動、動作ともに軽快なのでこのまま軽いIDEであってほしいです。

参照


  1. Shift-JISのトラブルが妙なところで残ってて悲しい

textlintでAI的な文章になっていないかチェックする

以前、GitHubとはてなブログを連動させる話を書きました。この時からtextlintを使って文章のチェックを行っています。 先日、AIが生成したような文章を避けるためのルールセットが公開されていることを知ったので適用してみました。

textlint-rule-preset-ai-writing

textlintのルールセットに、textlint-ja/textlint-rule-preset-ai-writingがあります。これを使うと、AIがよく使う表現をチェックできます。例えば、絵文字の多用、大げさな表現、不必要に丁寧な表現がある場合、警告になります。

利用するには、npmでインストールしてからtextlintのコンフィグ.textlintrcpreset-ai-writingルールを追加します。

{
  "rules": {
    "preset-ai-writing": true
  }
}

VSCodeのtextlint拡張を入れていると、リアルタイムに文章をチェックしてくれてオススメです。

適用してみた結果

このブログは2012年~2025年まで記事があるのですが、すべてに対してtextlintが効いています。ということで、全記事に対してルールを適用したところ200記事以上でヒットしました。自分の文章の癖が出てて面白い反面、AIじゃなくても冗長な表現をしているようです。私は自分で文章を書いているのですが、読んだ本や記事で絵文字を使って表現するの分かりやすいと思って取り入れた部分が判定引っかかったりしていて、なるほどでした。

私の文章で特に多かった指摘内容は次の通りです。

〇〇の必要がありますのような冗長な義務表現

一番多く引っかかったのがこれです。

私は「〇〇の必要があります」という表現をよく使う癖があるようです。確かに冗長なので必要がありますしますに変えています。 直しながら読み返していたんですが、どの文章でも必要が...という表現は無駄でした。もう使わない。

# 【簡潔性】冗長な義務表現が検出されました。「してください」または「します」への直接的な表現を検討してください。 (ai-writing/ai-tech-writing-guideline)textlint(ai-writing/ai-tech-writing-guideline)

- 操作する必要があります
+ 操作します

必要に応じてのようなあいまいな条件表現

次に多かったのがこれです。

「こうしたほうがいいですよ。でも事情があってやらないケースもあるだろう」と考えたとき、必要に応じて適切にという表現を使いがちです。ただ、このあいまいさは伝えたいことには不要だったので消しました。必要に応じるかどうかは、読者が判断することなのでいちいち書く必要ないんですね。

# 【具体性】曖昧な条件表現が検出されました。どのような状況で必要なのか、具体的な判断基準の明示を検討してください。 (ai-writing/ai-tech-writing-guideline)textlint(ai-writing/ai-tech-writing-guideline)

- 必要に応じてグループ化します。
+ グループ化します。

大幅に完全にのような誇張表現

大量に引っかかりました。いわれてみたらAIって本当に誇張表現が多いのでたしかに。GPT 5.2で性格が導入されたので、シンプルな表現をする性格を選ぶとこういう表現は減っている気がします。

副作用で完全に理解したという表現も引っかかりました。当時のミームを思い返しつつ、素直に消しています。

# 「大幅に」という表現は誇張的である可能性があります。具体的な数値や割合を示すことを検討してください。 (ai-writing/no-ai-hype-expressions)textlint(ai-writing/no-ai-hype-expressions)

- 大幅に所要時間が短縮
+ 所要時間が短縮

- 完全に制御下における
+ 制御下における

リストを絵文字でプレフィックス表現

.NETのクラスライブラリ設計では、DOやAVOIDなどを絵文字で表現しています。これが好きで真似をしていたのですが引っかかりました。確かにAIが作る文章でよく見かけるのでしょうがない。

# リストアイテムでの絵文字「📝」の使用は、読み手によっては機械的な印象を与える場合があります。テキストベースの表現も検討してみてください。 (ai-writing/no-ai-list-formatting)textlint(ai-writing/no-ai-list-formatting)

- * 📝: 機能としてのメモ項目
- * 👍: Wiki機能に対してよいと感じた項目
- * 🙅: 触った感触が微妙な項目

+ * MEMO: 機能としてのメモ項目
+ * GOOD: Wiki機能に対してよいと感じた項目
+ * BAD: 触った感触が微妙な項目

boldな語句だけの行

2,3か所引っかかりました。確かにAIがよく作る文章でも同じ表現をよく見ます。文章を書いててまとめきれず表現に悩んで使ったのですが、文章にするよう変更しました。

# 「**例**」のような太字の情報プレフィックスは機械的な印象を与える可能性があります。より自然な表現を検討してください。 (ai-writing/no-ai-emphasis-patterns)textlint(ai-writing/no-ai-emphasis-patterns)

- **例**
- 以下文章は...
+ 例えば、以下文章...

受動的で抽象的な表現

〇〇が行われていたようように、適当な文章を書いてしまう癖があるようです。確かにやっていた、という表現で十分なので直しました。 日本語むずすぎる。

# 【明確性】受動的で抽象的な表現が検出されました。具体的な動詞を使った能動態への変更を検討してください(例:「実行する」「処理する」)。 (ai-writing/ai-tech-writing-guideline)textlint(ai-writing/ai-tech-writing-guideline)

- が行われていたようです。
+ がありました。

まとめ

警告がなくなるまで粛々と直しました。気づいていなかったので指摘されて嬉しいです。これからもtextlintで自分の文章をチェックして、より良い文章を書けるようにしていきます。

ところで、2023年以降、AIっぽいと感じる記事をたびたび見かけますが、自分の文章はAI使っていないのに変な文章でした。日本語をうまくなりたいです。

参考

GitHub Actionsからgit pushをするパターン

GitHub Actionsからgit pushを実行する機会は少なくありません。例えば自動フォーマッタやドキュメント生成、特定ファイルの中身を更新などをした人は多いんじゃないでしょうか。 今回は、GitHub Actionsでgit pushをするときに私がやっている方法の備忘録です。

はじめに

GitHub Actionsのワークフローは基本的に並列動作します。厳密には制御しえますが、GitHub Actionsとしては並列に動作することを前提に設計されており、直列実行をサポートする機能は限定的と言わざるを得ません。git pushは並列動作と相性が悪く、同一リモートrefに対して複数のワークフローやジョブがgit pushを実行すると、競合が発生して失敗します。

そこで、今回は以下の2つのパターンでの対処法を紹介します。

  1. 同一ワークフローで何度もpushしたくなったら
  2. 複数ワークフローでpushしたくなったら

同一ワークフローで何度もpushしたくなったら

同一ワークフローで何度もgit pushをしたくなる例として、Matrixを使うケースがあります。よくあるのがマルチプラットフォーム向けのビルドです。Linux、Windows、macOS向けにビルドして、それぞれの成果物を同一ブランチにpushしたい、みたいなケースが想像しやすいでしょう。

  • ジョブAでOSごとに並列実行してgit pushを実行

素直にワークフローを書くと以下のようになります。1最初のgit pushは成功しますが、2番目以降のジョブ2git pushが失敗します。これは、1つ目と2つ目のジョブでチェックアウトタイミングはほぼ同一なのに、2つ目以降のgit pushはcheckout時点と比べてリモートが更新されているためです。

# 省略....

jobs:
  build:
    permissions:
      contents: write # pushするのでwriteが必要
    strategy:
      matrix:
        build-os: ["linux_x64", "linux_arm64", "windows_x64", "windows_arm64", "macos_arm64"]
    runs-on: ubuntu-24.04
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          persist-credentials: false
      - name: Build
        run: |
          echo "do build"
          echo "build artifact ${{ runner.os }}/${{ runner.arch }}" >> .publish/binary_${{ runner.os }}/${{ runner.arch }}
      - name: Configure git for push
        run: |
          git remote set-url origin "https://github-actions:${GITHUB_TOKEN}@github.com/${{ github.repository }}"
          git config user.name  "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Commit build output
        run: |
          git switch -c "auto/create_pr" || git switch "auto/create_pr"
          git add .
          git commit -m "[automate] add build artifact for ${{ runner.os }}/${{ runner.arch }}"
          git push origin HEAD:${{ github.ref }} # <-- ここでリモート更新済みのためエラーが出る

この場合、それぞれのジョブでgit pushするのをやめて、代わりにgit pushを実行するジョブを分離して1回にしましょう。修正すると次のようになります。

# 省略....

jobs:
  build:
    permissions:
      contents: read # pushしないのでreadだけ
    strategy:
      matrix:
        build-os: ["linux_x64", "linux_arm64", "windows_x64", "windows_arm64", "macos_arm64"]
    runs-on: ubuntu-24.04
    timeout-minutes: 5
    outputs:
      build-artifacts: ${{ steps.set-artifacts.outputs.build-artifacts }}
    steps:
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          persist-credentials: false
      - name: Build
        run: |
          echo "do build"
          echo "build artifact ${{ runner.os }}/${{ runner.arch }}" >> .publish/binary_${{ runner.os }}/${{ runner.arch }}
      - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
        with:
          name: artifact-${{ runner.os }}/${{ runner.arch }}
          path: .publish/
          retention-days: 1

  # git pushを1回だけ実行するようにジョブをわける
  push:
    needs: [build] # buildジョブの後に実行するように制御
    permissions:
      contents: write # pushするのでwriteが必要
    runs-on: ubuntu-24.04
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          persist-credentials: false
      - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
        with:
          path: artifacts # ダウンロード先があれば指定する
      - name: Configure git for push
        run: |
          git remote set-url origin "https://github-actions:${GITHUB_TOKEN}@github.com/${{ github.repository }}"
          git config user.name  "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Commit build output
        run: |
          git checkout -b "${NEW_BRANCH_NAME}"
          git add .
          git commit -m "[automate] add build artifacts"
          git push -u origin "${NEW_BRANCH_NAME}"
        env:
          NEW_BRANCH_NAME: auto/create_pr

複数ワークフローでpushしたくなったら

同一ブランチに対して異なる処理をする複数のワークフローが存在する場合、この問題が発生します。例えば、ドキュメント生成ワークフローとソースコードフォーマッターワークフローがあって、両方とも同一ブランチにpushしたい、みたいなケースを考えましょう。

  • ワークフローAでDocs/のファイルを更新してpush
  • ワークフローBでSrc/のフォーマッターを実行してpush

このような場合、最初にpushしたワークフローやジョブは成功しますが、後からpushしようとしたワークフローはcheckout時点と比べてリモートが更新されているため、git pushは失敗します。

- name: git push
  shell: bash
  run: |
    git checkout -b "${NEW_BRANCH_NAME}"
    git add .
    git commit -m "[automate] foobar"
    git push -u origin "${NEW_BRANCH_NAME}"

どっちのワークフローが先に実行するか実行順序は保証されていないため、複数ワークフローが同一ブランチにpushする場合はリトライロジックを入れるのがおすすめです。以下の例は、リモートが更新されてgit pushに失敗した場合、git pull --rebaseでリモートの変更を取り込んでから再度pushを試みる例です。これは最大3回までリトライします。

- name: git commit
  run: |
    git checkout -b "${NEW_BRANCH_NAME}"
    git add .
    git commit -m "[automate] foobar"
- name: git push
  shell: bash
  run: |
    max_retries=3
    retry_count=0

    while [ $retry_count -lt $max_retries ]; do
      echo "try 'git push' ($((retry_count + 1))/${max_retries}) ..."
      if git push origin "$NEW_BRANCH_NAME"; then
        echo "Push succeeded."
        exit 0
      fi

      # Push failed, increment retry count
      ((retry_count++))

      # If we haven't exceeded max retries, try to rebase and retry
      if [ $retry_count -lt $max_retries ]; then
        echo "Failed to push, try 'git pull --rebase' to resolve ..."
        if ! git pull origin "$NEW_BRANCH_NAME" --rebase; then
          echo "'git pull --rebase' has problem, you need resolve conflict ..."
          exit 1
        fi
        echo "Rebase succeeded, will retry push."
      fi
    done

    echo "max retry reached, but failed to push."
    exit 1

まとめ

GitHub Actionsからのgit pushはうっかり競合が発生しやすいため、工夫が必要です。今回は2つのパターンでの対処法を紹介しました。 他にもいろいろなやり方がありますが、基本的には以下のポイントを押さえておけば大丈夫でしょう。

  • 同一ワークフロー内で複数ジョブがpushする場合は、pushを1つのジョブにまとめる
  • 複数ワークフローがpushする場合は、リトライロジックを入れる

補足: お約束

ワークフローで使っている、どのパターンでも共通するお約束です。以前にも紹介したGitHub Actions脆弱性を考慮した書き方です。

Permissionsを指定する

Permissionsは原則指定しましょう。git pushをする場合はcontents: writeが必要です。

# NG
# permissionsがない

# OK
permissions:
  contents: write

timeout-minutesを指定する

ジョブのタイムアウトは指定しましょう。デフォルトは6時間ですが、ビルドやgit pushだけなら3-5分もあれば十分なはずです。

# NG
# timeout-minutesがない

# OK
timeout-minutes: 3

SHA指定でactions/checkoutを使う

サードパーティアクションは基本的にSHA指定でバージョンを固定して使いましょう。

# NG
- uses: actions/checkout@v5

# OK
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

actions/checkoutはpersist-credentialsを無効にする

# NG
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

# OK
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
  with:
    persist-credentials: false

この場合、Git push時に認証情報がないため、後述のGITHUB_TOKENを使った認証が必要になります。

GitコンフィグでリモートURLとユーザー情報を設定する

git pushするときに、GitHub Actionsのボットユーザー情報を設定しましょう。お決まりのメアドとユーザー名があります。 また、persist-credentials: falseを指定した場合は、リモートURLに${{ secrets.GITHUB_TOKEN }}を埋め込む必要があります。

# NG
# いきなりgit pushしようとする
# set-urlをしないでpushしようとする

# OK
- name: Configure git for push
  run: |
    git remote set-url origin "https://github-actions:${GITHUB_TOKEN}@github.com/${{ github.repository }}"
    git config user.name  "github-actions[bot]"
    git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

サードパーティのGitアクションの利用は慎重に

git pushを行うサードパーティアクションはいくつかあります。よくあるのはad-m/github-push-actionです。だいたいうまく動くのですが、そのアクションを信頼できるかという問題が常に付きまといます。

ここ最近は、アクション関連の脆弱性がサプライチェインから突かれているケースが多いため、git pushのような万が一が許されない操作に関してはより慎重な判断が必要です。このため、個人的にはgit pushは自前でスクリプトを書くことを意識しています。


  1. ジョブごとにOS(runs-on)を切り替えることが多いでしょうが、Goのようにあるプラットフォームからマルチプラットフォーム向けにビルドできるケースということで。
  2. どのジョブが先に実行されるかは不定です。Matrixでmax-parallel: 1にして直列にしても結局、後続のジョブは失敗します。