デザインとコードの断絶を破壊せよ:Penpot × GitHub Actionsで築く、自律型デザインCI/CDパイプライン
テックリードの仕事とは、ツールを導入することではない。チームの認知負荷を限界まで下げ、開発速度とプロダクト品質を物理的限界まで引き上げる「エコシステム」を構築することだ。
多くの現場ではいまだに、デザインの変更がSlackで手動通知され、エンジニアがFigma(あるいはPenpot)を目視で確認し、Storybookやコードベースを「なんとなく」修正するという不毛なハンドオフ(Handoff)が行われている。これではアジャイル開発の名が泣く。
オープンソースのプロトタイピングツール「Penpot」の真価は、単なるFigmaのオープンソース代替品ではない。「APIファースト」で作られた、コードとデザインを直結させるための最強のミドルウェアになり得る点にある。
今回は、PenpotのWebhooksとGitHub Actionsを完全統合し、デザインの更新をトリガーに差分を検出し、自動でビルド・検証・通知を行う「デザインCI/CDパイプライン」の構築手法を、実戦投入可能なコードとともに全公開する。
—
1. 現場の生産性を爆発させるPenpotハック
CI/CDを組む前に、まずはPenpotを日常的に使い倒すための「プロの隠し技」を共有しておこう。ここを知っているか否かで、デザイナーとエンジニアのコラボレーション速度は3倍変わる。
開発スピードを極限まで高めるキーボードショートカット
Penpotはブラウザ完結型でありながら、モダンなIDE並みのショートカット体系を持っている。
- `Shift + R`: レスポンシブ/グリッドモードの切り替え(CSS Gridのメンタルモデルと完全に一致する)
- `Alt + ⟵/⟶/⟸/⟹` (各方向): 要素を選択した状態でホバーすると、CSSの `margin` / `padding` がピクセル単位で即座に数値表示される。
- `Ctrl/Cmd + G`: コンポーネント化(Reactでいう `export const Component = …` の瞬時生成)。
- `Ctrl/Cmd + Shift + L`: レイアウト(Flexbox)の適用。CSS Flexboxの挙動そのままでコンテナ挙動を制御できる。
チーム開発の秩序を守る「設定の共有化ルール」
Penpotでデザインシステムをコードと同期させる大前提として、トークンの共通化が不可欠だ。
チームスペースの設定では、以下のルールを強制せよ。
1. カラーパレットの命名規則の統一: `primary/500`, `neutral/900` のように、Tailwind CSSやDesign Tokens W3C格式に完全準拠したスラッシュ記法を使うこと。
2. タイポグラフィのトークン化: フォントサイズ、ウェイト、行送りをバラバラに定義させず、必ず「スタイル(Styles)」として登録させる。これがいわゆる `Design Tokens` のJSONとして抽出されるベースになる。
—
2. アーキテクチャ概要:デザインCI/CDの全貌
今回構築するパイプラインのフローは以下の通りだ。
[ Penpot (Design Update) ]
│ (Webhook)
▼
[ GitHub Actions (API Gateway / Receiver) ]
│
├─► 1. Penpot APIから最新のデザイン・トークン JSON をフェッチ
├─► 2. リポジトリ内の既存トークンとの差分(Diff)を検出し、PRを自動作成
└─► 3. Storybook / ビルドの整合性チェック(CI)を走らせる
「デザインが変わったら、自動でコードの型やトークンが更新され、プルリクエストが飛んでくる世界」。これを実現する。
—
3. 実装:GitHub Actionsワークフローと自動化スクリプト
それでは、実際にプロジェクトへ組み込むための具体的なコードを提示する。
① GitHub Actions ワークフロー設定
`.github/workflows/design-sync.yml` を作成し、PenpotからのWebhookを受け取るエンドポイント、または定期実行(あるいはAPI連携トリガー)のパイプラインを定義する。
name: Penpot Design Sync & CI
Webhook経由(repository_dispatch)または手動・定期実行を想定
on:
repository_dispatch:
types: [penpot-design-updated]
workflow_dispatch:
jobs:
sync-and-validate:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ’20’
cache: ‘npm’
- name: Install Dependencies
run: npm ci
- name: Fetch Design Tokens from Penpot
env:
PENPOT_API_URL: ${{ secrets.PENPOT_API_URL }}
PENPOT_API_TOKEN: ${{ secrets.PENPOT_API_TOKEN }}
PENPOT_FILE_ID: ${{ secrets.PENPOT_FILE_ID }}
run: |
# Penpot APIを叩いて最新のデザイントークンをJSONとして抽出するカスタムスクリプトを実行
node ./scripts/fetch-penpot-tokens.js
- name: Check for Design Diffs
id: diff_check
run: |
git diff –quiet src/styles/tokens.json || echo “has_diff=true” >> $GITHUB_OUTPUT
- name: Create Pull Request for Design Updates
if: steps.diff_check.outputs.has_diff == ‘true’
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: “chore(design): sync latest design tokens from Penpot”
title: “🎨 [Auto-Sync] Penpot Design Tokens Updated”
body: |
Penpotでのデザイン変更検知に伴い、デザイントークンを自動更新しました。
差分を確認し、デザインシステムとの乖離がないかレビューしてください。
branch: “automated/penpot-sync”
base: “main”
signoff: true
- name: Run Component Build & Visual Regression Tests
run: |
npm run build
npm run test:ui
② デザイントークン同期スクリプト
PenpotのバックエンドAPI、またはエクスポート機能を叩いてトークンを取得・整形するNode.jsスクリプトのベストプラクティス構成。
`scripts/fetch-penpot-tokens.js` として配置する。
/
- @file fetch-penpot-tokens.js
- @description Penpot APIからデザインデータを取得し、W3C Design Tokensフォーマットに変換するスクリプト
/
const fs = require(‘fs’);
const path = require(‘path’);
const PENPOT_API_URL = process.env.PENPOT_API_URL;
const PENPOT_API_TOKEN = process.env.PENPOT_API_TOKEN;
const PENPOT_FILE_ID = process.env.PENPOT_FILE_ID;
async function fetchPenpotData() {
if (!PENPOT_API_URL || !PENPOT_API_TOKEN || !PENPOT_FILE_ID) {
console.warn(‘⚠️ Penpot API credentials not found. Skipping token sync (Dry-run mode or local).’);
return null;
}
try {
// Penpotの公式APIエンドポイント(セルフホスト環境やCloud環境に応じて調整)
const response = filesEndpoint = `${PENPOT_API_URL}/api/files/${PENPOT_FILE_ID}`;
const res = await fetch(filesEndpoint, {
headers: {
‘Authorization’: `Bearer ${PENPOT_API_TOKEN}`,
‘Content-Type’: ‘application/json’,
},
});
if (!res.ok) {
throw new Error(`Failed to fetch from Penpot API: ${res.statusText}`);
}
const data = await res.json();
return data;
} catch (error) {
console.error(‘❌ Error fetching Penpot tokens:’, error);
process.exit(1);
}
}
function transformTokens(rawData) {
// ここでPenpot固有のレスポンス構造を、プロジェクトで利用するDesign Tokens (JSON) にパース・変換する
// 例として、カラーやタイポグラフィの構造を正規化するロジックをここに記述
if (!rawData) {
// フォールバック用のモック(ローカルテスト用)
return {
version: “1.0.0”,
colors: {
primary: { value: “#3b82f6”, type: “color” },
background: { value: “#ffffff”, type: “color” }
}
};
}
// 実際のPenpotオブジェクトツリーからのパース処理を実装
const normalizedTokens = {
updatedAt: new Date().toISOString(),
colors: {} // パース後のカラー定義
};
return normalizedTokens;
}
async function main() {
console.log(‘🔄 Fetching latest design data from Penpot…’);
const rawData = await fetchPenpotData();
const tokens = transformTokens(rawData);
const outputPath = path.resolve(process.cwd(), ‘src/styles/tokens.json’);
fs.writeFileSync(outputPath, JSON.stringify(tokens, null, 2), ‘utf-8’);
console.log(`✨ Successfully synchronized tokens to ${outputPath}`);
}
main();
—
4. テックリードが仕掛ける「真のエンジニアリング」
この仕組みをチームに導入した瞬間から、開発の質的変化が起きる。
1. 「言った・言わない」の完全な排除: デザインの変更履歴がすべてGitHubのCommit履歴とPRに紐づくため、「いつ、誰が、どのデザインを変更した結果、どのコンポーネントに影響が出たのか」がコードベース側から完全トレース可能になる。
2. 手戻りの事前粉砕: デザイナーがPenpot上でマージンやカラーを勝手に変えたとしても、CIが走った段階でビルドエラーやトークンの型不一致として検知されるため、QAフェーズやリリース直前の「デザイン崩れバグ」が物理的に消滅する。
Penpotを単なる「絵を描くツール」として使っているうちは、チームのポテンシャルの半分も引き出せていない。オープンソースであるPenpotの自由度と、GitHub Actionsの堅牢なCI/CDパイプラインを結合させ、デザインとコードが自律的に同期するエコシステムをあなたのチームにも今すぐ実装せよ。
プロダクトの進化スピードは、こうした足回りの自動化の厚みによってのみ加速する。