Communitygithub.com

ポートフォリオ出荷 — 飛書ベース → GitHub Pages + Actions

飛書の多次元テーブルから GitHub Pages のポートフォリオを生成・公開し、Actions で一時メディアリンクを定期更新。素材が飛書にあるクリエイター向け(ブログ/EC/書き込みバックエンド向けではない)。

ポートフォリオ出荷 — 飛書ベース → GitHub Pages + Actions とは?

Personal websites 手順3(ship):feishu-portfolio-launch。飛書 SSOT → 生成サイト → Pages + 定期リンク更新。

対応✓Claude Code~Codex CLI~Cursor
npx skills add Jane-xiaoer/feishu-portfolio-launch

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Feishu Portfolio Launch

このスキルの目的

一言で言うと:Feishu多次元テーブルのリンクを渡せば、ポートフォリオサイトを構築し、「Feishu一時リンクの有効期限切れ」を自動化します。

デフォルトの成果物:

  • index.html または静的フロントエンドページ
  • api/videos.json
  • api/covers.json
  • api/portfolio.json
  • refresh.py
  • .github/workflows/refresh.yml
  • アニメーション付きフロントエンドインタラクション層(CSS / JS / Canvas / Three.js 可)

組み込みテンプレートリソース:

目標結果:

  • GitHub Pages にサイトをデプロイ
  • Feishuメディアリンクは GitHub Actions で12時間ごとに自動更新
  • ユーザーは自身のPCを常時起動させる必要なし
  • ページは単なる静的レイアウトではなく、最低限のエントランス/スクロール/ホバー/ページ切り替えのアニメーションを備える

適用シーン

✅ 適している:

  • AI動画/画像ポートフォリオ
  • 写真、デザイン、クリエイティブサンプルライブラリ
  • 顧客事例紹介サイト
  • 「コンテンツがFeishu添付ファイルにあり、フロントエンドが表示のみを担当する」すべてのシーン

❌ 適していない:

  • テキストブログ
  • EC、決済、ログインシステム
  • データベース書き込みとバックエンドビジネスロジックが必要なサイト

コア原則

Feishuの一時URLを永続的なリソースと見なさないこと。

Feishu添付ファイルの再生/ダウンロードURLには有効期限があります。正しいアプローチは、ユーザーにローカルPCで定期的なスクリプト実行をさせないことです。代わりに:

  1. Feishu多次元テーブルから最新のレコードを読み取る
  2. Feishu API を呼び出して添付ファイルの file_token を一時URLに変換
  3. api/*.json を生成
  4. GitHub リポジトリにコミット
  5. GitHub Pages に自動再デプロイさせる

つまり:

Feishu Bitable -> refresh.py -> api/*.json -> GitHub Pages


実行フロー

Step 0:環境確認

ローカルに必要なツールが揃っているか確認:

for cmd in node python3 gh git; do
  which "$cmd" >/dev/null 2>&1 && echo "OK  $cmd" || echo "MISS $cmd"
done
lark-cli --version >/dev/null 2>&1 && echo "OK  lark-cli" || echo "MISS lark-cli"
gh auth status >/dev/null 2>&1 && echo "OK  GitHub auth" || echo "MISS GitHub auth"

不足しているツールがあれば、ユーザーにインストールを促す。
gh がログインしていなければ、先にGitHub認証を完了させる。


Step 1:Feishu Base と正しいデータシートを取得

ユーザーにFeishu多次元テーブルのリンクを依頼(例):

https://my.feishu.cn/base/AbCdEfGhIjk

URL から app_token を解析する。

その後、すべてのテーブルをリスト化:

lark-cli api GET "/open-apis/bitable/v1/apps/${APP_TOKEN}/tables"

最初のテーブルをデフォルトで選択しないこと。
多くのBaseには「空テンプレートテーブル」と「実際の作品がある素材テーブル」が混在している。レコードを読み取り、以下を検証する:

  • 実際の添付フィールドがあるテーブルはどれか
  • レコード数が正しいテーブルはどれか
  • フィールド名がユーザーの表示内容であるテーブルはどれか

以下のフィールドを優先的に検索:

  • 動画フィールド:サンプル動画 / 動画 / 添付ファイル
  • カバーフィールド:カバー / カバー画像 / ポスター / 画像
  • タイトルフィールド:内容 / タイトル / 作品名
  • カテゴリフィールド:タイプ / カテゴリ
  • 長さフィールド:長さ
  • ツールフィールド:AIツール / ツール
  • ソートフィールド:順序

テーブルに テキスト / 単一選択 / 日付 / 添付ファイル のような空テンプレートフィールドしかない場合は、テーブルを切り替え、サイトを生成しない。


Step 2:Feishu権限を検証

GitHub Actions で自動更新を実行するには、Feishuアプリが最低限以下の操作をできる必要がある:

  • 多次元テーブルレコードの読み取り
  • 添付ファイルのメディアリンクの読み取り

実行計画では、これら4つのシークレットに明示的に依存する:

  • LARK_APP_ID
  • LARK_APP_SECRET
  • LARK_BASE_TOKEN
  • LARK_TABLE_ID

ユーザーのローカルですでに使用可能なFeishuアプリ設定があれば、それを再利用してもよい。それ以外の場合は、Feishuオープンプラットフォームで権限とキーを補足するようユーザーに明確に指示する。


Step 3:構造化された作品データをエクスポート

file_token のリストだけを抽出しない。
フロントエンドが直接消費できる構造化データを構築する。最低限以下を含む:

  • title
  • categories
  • duration
  • tools
  • order
  • video_token
  • cover_token

推奨出力:

  • api/portfolio.json:完全な作品配列
  • api/videos.json:video_token -> tmp_download_url
  • api/covers.json:cover_token -> tmp_download_url

構造サンプルはリポジトリ内の assets/portfolio.sample.json を参照。

フロントエンドは静的HTML+JSのままでも、portfolio.json から完全にレンダリングしてもよい。


Step 4:フロントエンドページを生成

デフォルトでは静的ページを生成するが、以下を要求:

  • レスポンシブ作品グリッド
  • カテゴリフィルター
  • カードクリックで再生
  • 全画面拡大再生
  • 同時に1つの動画のみ再生可能
  • カバーはFeishuカバーフィールドを優先し、カバーがない場合はポスターなしにフォールバック可能

アニメーションはデフォルトで一緒に提供すること。「開けるだけ」の静的ページにしない:

  • ファーストビューには少なくとも1組のエントランスアニメーション(例:タイトル、説明、統計数字が順にfade-up)
  • ナビゲーションはスクロール後にblur/background/borderの変化を許可
  • カードのホバーには少なくとも軽量なフィードバック(例:浮上、拡大、オーバーレイ、タイトル色変更)
  • ページ切り替えやセクション切り替えにはトランジションが必要。唐突なフラッシュ切り替えは避ける
  • About/連絡先/スキルモジュールなどの情報セクションには、スクロールリビールを優先的に追加
  • ユーザーがより視覚的な表現を希望する場合、Canvas や Three.js の背景アニメーションを追加可能

実装の境界:

  • ネイティブCSS transition / keyframes / IntersectionObserver を優先
  • 継続的な動きのある背景が必要な場合のみ requestAnimationFrame を使用
  • Three.js を使用する場合、モバイルで空白、フリーズ、メインコンテンツの隠蔽が発生しないことを確認
  • アニメーションは動画再生、フィルター、ポップアップ、モバイルスクロール、ファーストビューの可読性に影響を与えないこと
  • デフォルトでフォールバックパスを提供し、スクリプトが失敗してもテキストとコンテンツは表示されること

データバインディングの原則:

  • HTMLに期限切れURLをハードコードしない
  • ページ実行時に /api/videos.json と /api/covers.json を取得
  • または、直接 /api/portfolio.json を取得してカードをレンダリング

ユーザーが既存のサイトを持っている場合、既存のビジュアルスタイルを優先して保持し、データソースと更新メカニズムのみを置き換える。

ユーザーが既存のサイトスタイルを参考にしたいと明確に述べた場合、以下のアニメーションタイプを優先的に再利用:

  • hero コピーのエントランスアニメーション
  • sticky filter/nav のスクロール状態遷移
  • masonry カードのホバーエフェクト
  • ページのフェードイン/アウト切り替え
  • About ページの stagger reveal
  • オプションの Three.js/Canvas 背景アニメーション

Step 5:リポジトリを作成しGitHub Pagesにデプロイ

標準フロー:

gh repo create <repo-name> --public --source=. --remote=origin --push

その後、Pagesのソースを確認:

  • branch: main
  • path: /

確認:

gh api repos/<owner>/<repo>/pages

以下を確認必須:

  • Pages が有効化されている
  • cname が正常(カスタムドメインがある場合)
  • source が main とルートディレクトリを指している

Step 6:自動更新スクリプトを生成

更新スクリプトは以下のルートを採用:

  1. app_id + app_secret で tenant_access_token を取得
  2. Bitable レコードを読み取り
  3. レコードから動画とカバーの添付ファイルトークンを抽出
  4. batch_get_tmp_download_url をバッチ呼び出し
  5. api/videos.json、api/covers.json、api/portfolio.json に書き込み

重要要件:

  • レコードのページング読み取りをサポート
  • バッチリクエストは5トークンずつ
  • トークンリストをスクリプトにハードコードしない
  • 実際のテーブルレコードから動的に生成することを優先
  • 複数のフィールド名候補を許可

推奨ファイル名:

  • refresh.py

スキル組み込みテンプレートを直接コピーすることを優先:

cp ~/.claude/skills/feishu-portfolio-launch/assets/refresh.py ./refresh.py

その後、フィールド候補配列のみを修正し、具体的なトークンやURLをハードコードしない。


Step 7:GitHub Actions Workflow を生成

ローカルの launchd をデフォルトのソリューションとして使用しない。
デフォルトは GitHub Actions でなければならない。

ワークフローの要件:

  • workflow_dispatch をサポート
  • schedule をサポートし、デフォルトで12時間ごとに実行
  • python refresh.py を実行
  • api/videos.json、api/covers.json、api/portfolio.json を自動コミット

標準シークレット:

  • LARK_APP_ID
  • LARK_APP_SECRET
  • LARK_BASE_TOKEN
  • LARK_TABLE_ID

標準ファイル:

  • .github/workflows/refresh.yml

スキル組み込みテンプレートを直接コピーすることを優先:

mkdir -p .github/workflows
cp ~/.claude/skills/feishu-portfolio-launch/assets/refresh.yml .github/workflows/refresh.yml

Step 8:Secrets をリポジトリに設定

現在の環境にこれらの値があり、ユーザーが設定を明確に依頼した場合、直接設定:

gh secret set LARK_APP_ID -R <owner>/<repo>
gh secret set LARK_APP_SECRET -R <owner>/<repo>
gh secret set LARK_BASE_TOKEN -R <owner>/<repo>
gh secret set LARK_TABLE_ID -R <owner>/<repo>

その後、確認:

gh secret list -R <owner>/<repo>

値が不足している場合は、ここで停止し、不足しているものをユーザーに明確に伝える。偽造しない。


Step 9:手動でトリガーして全体のチェーンを検証

設定完了後、すぐに手動でワークフローを1回実行:

gh workflow run refresh.yml -R <owner>/<repo>
gh run list -R <owner>/<repo> --limit 3
gh run watch <run-id> -R <owner>/<repo> --exit-status

さらにオンライン結果を確認:

python3 - <<'PY'
import json, urllib.request, time
url = f'https://<domain-or-pages-url>/api/videos.json?t={int(time.time())}'
with urllib.request.urlopen(url, timeout=20) as r:
    data = json.load(r)
print(data.get('_refreshed_at'))
print(data.get('_count'))
PY

その後、1件の動画をサンプル確認:

  • ステータスコードは 206 または 200 であるべき
  • content-type は video/mp4 または再生可能なメディアタイプであるべき

オンラインの api/videos.json がまだ古い時刻の場合:

  • まずPagesのデプロイが完了しているか確認
  • 次にワークフローが実際に新しいJSONをコミットしたか確認
  • さらにPages source が正しいブランチとディレクトリを指しているか確認

Step 10:ドメインバインディング

ユーザーがカスタムドメインを希望する場合のみ、以下を実行:

  • CNAME ファイル
  • GitHub Pages カスタムドメイン設定
  • DNS 設定ガイド

この部分は GitHub Pages の標準フローに従い、Feishu のロジックと結合する必要はない。


ユーザーへのデフォルト説明

「サイトは開けるが動画が再生できない」という状況に遭遇した場合、優先順位:

  1. api/videos.json がまだ古いかどうか
  2. 中のFeishu URLが有効期限切れかどうか
  3. 自動更新ワークフローが失敗したかどうか
  4. Pagesがまだ再デプロイされていないかどうか

デフォルトでプレイヤーやフロントエンドスタイルを疑わない。

ユーザーが「ページが死んでいる」「ポートフォリオのように息づく感覚が欲しい」と言った場合、デフォルトでアニメーション層を追加する。色やレイアウトだけを変更しない。


成功基準

このスキル完了後、少なくとも以下を満たすこと:

  • サイトが公開されている
  • GitHub Pages にアクセス可能
  • 動画が再生可能
  • リポジトリに自動更新ワークフローが存在する
  • リポジトリに4つの LARK_* secrets が存在する
  • 手動トリガーのワークフローが成功
  • オンラインの api/videos.json の _refreshed_at が新しい時刻

デフォルトでしないこと

  • ローカルの launchd をデフォルトで生成しない
  • ユーザーのMacの常時起動にデフォルトで依存しない
  • すべての file_token をハードコードしない
  • 最初のテーブルだけで続行しない
  • videos.json だけを生成し、カバーや構造化レコードを無視しない

参考実装

検証済みのアプローチが必要な場合、まずスキルリポジトリのテンプレートを直接再利用:

  • assets/refresh.py
  • assets/refresh.yml
  • assets/portfolio.sample.json

実際のプロジェクトでは、最終的にこれらのファイルが生成される:

  • refresh.py
  • .github/workflows/refresh.yml
  • api/videos.json
  • api/covers.json
  • api/portfolio.json

既存のリポジトリパターンを優先的に再利用し、その後ユーザーサイトのスタイルに合わせて適応する。

関連スキル