Figma の SVG アイコンと GitHub リポジトリを自動同期する

f:id:tjmschk:20211219231552p:plain この記事は ZOZO Advent Calendar 2021 の 20 日目の記事です。

はじめに

こんにちは、田嶋です。この記事は ZOZO のアドカレの記事2本目です。最近はポケモン自作PCのセットアップに勤しんでいます。フロントエンドエンジニア全然できてません。

前提

今回は FigmaGitHub リポジトリSVG アイコンを自動で同期させ、アイコン管理を楽にしちゃおうという記事です。

タイトルでは自動同期と言ってますが、Figma が変更されたかどうかの検知ではなく cron での定期実行でリポジトリFigma の最新に合わせるすることで、自動同期を実現しています。

こちらの記事が大変参考になりました。GitHub か GitLab かの違いなのでもうほぼ二番煎じです。

inside.pixiv.blog

この記事で述べられている Github Action の figma-action ですが、現在はメンテされておらずアーカイブされていました。一応自分でも使ってみましたが上手く動かないようでした。

方法

実現方法について簡単に説明します。

1. Figma API を叩けるようにする

Figma DevelopersAPI を確認することができます。

Figma のファイルは node と呼ばれる木構造で構成されています。 node は node.typeRECTANGLECOMPONENT など判別することができます。今回使用する Figma ファイルは下図のレイアウトです。今回は Page1 の node に 背景に使っている Rectangle 1 と Icon のコンポーネントが複数あるという構成で、フレームにしていないため Icon は Rectangle の子ではなく兄弟です。

白い四角の上に8つのアイコン
Figma 上に作られたアイコン

2. Node.js で SVG をダウンロードしてくる CLI を作成する

FigmaAPI では子要素以下のファイルを直接ダウンロードする方法がないため、以下の階構成にしました。

  1. Icon を構成している node の id name を取得
  2. id を使用し画像取得用 API を叩き画像URL を取得
  3. fs を使用しローカルに画像をダウンロード

以下がコードです。

require("dotenv").config();
const fetch = require("node-fetch");
const fs = require("fs");

const baseUrl = "https://api.figma.com/v1/";
const figmaToken = process.env.FIGMA_TOKEN;
const fileId = "YhHWg9mZ6GdKkseHAAS1gw";

// Figma ファイルの API から component の ID を取得
const getComponentIds = async () => {
  try {
    const res = await fetch(`${baseUrl}files/${fileId}`, {
      headers: {
        "X-Figma-Token": figmaToken,
      },
    });
    const data = await res.json();
    const { components } = data;
    return components;
  } catch (error) {
    throw error;
  }
};

// Id から画像の URL を取得
const getImageLinks = async (component) => {
  try {
    const componentKeys = Object.keys(component).join();
    const res = await fetch(
      `${baseUrl}images/${fileId}?ids=${componentKeys}&format=svg`,
      {
        headers: {
          "X-Figma-Token": figmaToken,
        },
      }
    );
    const data = await res.json();
    const images = Object.keys(component).map((key) => {
      const { name } = component[key];
      const link = data.images[key];
      return { name, link };
    });

    return images;
  } catch (error) {
    throw error;
  }
};

// ローカルに画像を保存
const downloadImages = async ({ link, name }) => {
  const res = await fetch(link);
  const data = await res.buffer();
  fs.writeFileSync(`../src/${name}.svg`, data);
};

const main = async () => {
  const component = await getComponentIds();
  const images = await getImageLinks(component);
  images.forEach(async (image) => {
    await downloadImages(image);
  });
};

main();

3. Github Actions で定期実行する

あとは Github Action で定期的に上記のコードを実行するだけです。猿でもできますね。(私は二時間くらいここで躓きました。)

説明できることが少ないためコードを載せておきます。

name: Export SVG from Figma
on:
  schedule:
    cron: "0 0 * * *"

jobs:
  actions:
    runs-on: ubuntu-latest
    env:
      FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }}
    steps:
      - name: checkout
        uses: actions/checkout@v2
      - name: node setup
        uses: actions/setup-node@v2
        with:
          node-version: "12"
      - run: cd script; npm install
      - run: cd script; npm run start
      - name: Commit
        run: |
          git config --local user.email "github_actions@example.com"
          git config --local user.name "GitHub Actions"
          git add .
          git commit -m "downloaded from Figma"
      - name: Push changes
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.TOKEN }}`
          branch: master

GitHub Action の schedule は UTC なためこちらじゃ JST の 9:00 に実行されます。あえて 0:00 (UTC) にしたのは起きている時間のほうがバグった時に対応できるからと思たという言い訳です。

まとめ

作成したコードはこちらになります。

github.com

今回は簡単な Figma ファイルで試したためもっと複雑な要求に対応するためにはもう少し改良を加える必要があります。例えば Solid Outline のアイコンはそれぞれ別に管理したいなど。しかし適切な API とドキュメントが提供されているため改良は容易にできそうです。 また curl 芸人ではないため、今回は Node で画像をダウンロードするスクリプトを書きましたが、 Github Actions で完結する方法もあります。

最後に

アドカレ2本目を書くことにあたって実は他に以下の二つのネタを用意していました。

  1. 社内(実質)独自言語の VSCodeシンタックスハイライトをつくる
  2. Figma Branching を使ってみる

しかし 1 はあまりに複雑で簡単にできなさそうだなと諦めたのと、 2 は Beta 版なため Figma の Organization プランでしかつ買うことができず、個人では契約することができなかったので諦めて前から気になっていたこちらをやってみることにしました。

私の所属している部署ではデザインツールは XD が使用されていますがこうしてアドカレで Figma の利便性を訴え布教をしていきたいです。

参考にした資料

no such file or directory, open の理由を知る - Qiita

node-fetch - npm

https://www.figma.com/developers/api

Github Actionで定期的にファイルをリポジトリにコミットさせる – nozograph

GitHub Actions ワークフローで処理を定期実行する方法 | gotohayato.com