Creating a commit with multiple files to Github with JS on the web

私のサイトはentirely staticです。それはHugoで構築され、 HugoでホストされていZeit 。私はセットアップにとても満足していて、インスタントビルドと超高速CDNによるコンテンツ配信に近づいています。また、状態を管理する必要がないので、必要なことはすべて実行できます。

このサイト用のpodcast creatorと、静的にホストされたサイトに新しいコンテンツをすばやく投稿できるようにするためのsimple UIを作成しました。

そう。どうやってやったの?

それは私のGithubリポジトリに対するFirebase認証、コンテンツを編集するためのEditorJS(それはきれいです)、そしてリポジトリにコミットするためのOctokat.jsそして私のHugoビルドを行うためのZeitのGithub統合の組み合わせです。これで、Wordpressのようにデータベースで保護されたCMSでユーザーが投稿を作成するのと同じように、完全に自己管理型の静的CMSを作成できます。

この記事では、インフラストラクチャの一部、つまり複数のファイルをGithubにコミットすることに集中していきます。

コード全体は私のrepoで見ることができます。

Githubに直接コミットする必要があるWeb UIを構築している場合、私が見つけた最高のライブラリはOctokatです。これはCORSと連携し、Github APIのAPIサーフェス全体を処理するようです。

ツリー、ブランチ、その他の部分がどのように機能するのかを理解することになると、Gitは複雑な獣になる可能性があるので、それを簡単にするいくつかの決定を下しました。

  1. heads/master呼ばれるマスターブランチにしかプッシュできheads/master 。 1.私は特定のファイルがどこに格納されるか知っているでしょう(Hugoは私に特定のディレクトリ構造を持たせるように強制します)

それを念頭に置いて、複数のファイルでコミットを作成する一般的なプロセスは次のとおりです。

リポジトリへの参照を取得します。

  1. heads/masterブランチのツリーの先端への参照を取得します。 1.コミットしたいファイルごとにblobを作成してから、 sha ID、パス、モードへの参照を配列に保管します。
  2. heads/masterツリーの先端への参照に追加するすべてのtreeを含む新しいtreeを作成し、新しいshaポインタをこのツリーにshaます。 1.この新しいツリーを指すコミットを作成してから、 heads/masterブランチにプッシュします。

コードはその流れにほぼ従っています。特定の入力に対してパス構造を想定できるので、複雑なUIやファイルの管理を構築する必要はありません。

const createCommit = async (repositoryUrl, filename, data, images, commitMessage, recording) => {
  try {
    const token = localStorage.getItem('accessToken');
    const github = new Octokat({ 'token': token });
    const [user, repoName] = repositoryUrl.split('/');

    if(user === null || repoName === null) {
      alert('Please specifiy a repo');
      return;
    }
    
    const markdownPath = `site/content/${filename}.markdown`.toLowerCase();
    let repo = await github.repos(user, repoName).fetch();
    let main = await repo.git.refs('heads/master').fetch();
    let treeItems = [];

    for(let image of images) {
      let imageGit = await repo.git.blobs.create({ content: image.data, encoding: 'base64' });
      let imagePath = `site/static/images/${image.name}`.toLowerCase();
      treeItems.push({
        path: imagePath,
        sha: imageGit.sha,
        mode: "100644",
        type: "blob"
        });
    }

    if (recording) {
      let audioGit = await repo.git.blobs.create({ content: recording.data, encoding: 'base64' });
      let audioPath = `site/static/audio/${recording.name}.${recording.extension}`.toLowerCase();
      treeItems.push({
        path: audioPath,
        sha: audioGit.sha,
        mode: "100644",
        type: "blob"
        });
    }

    let markdownFile = await repo.git.blobs.create({ content: btoa(jsonEncode(data)), encoding: 'base64' });
    treeItems.push({
      path: markdownPath,
      sha: markdownFile.sha,
      mode: "100644",
      type: "blob"
    });

    let tree = await repo.git.trees.create({
      tree: treeItems,
      base_tree: main.object.sha
    });
  
    let commit = await repo.git.commits.create({
      message: `Created via Web - ${commitMessage}`,
      tree: tree.sha,
      parents: [main.object.sha]});

    main.update({sha: commit.sha})

    logToToast('Posted');
  } catch (err) {
    console.error(err);
    logToToast(err);
  }
}

静的ホスティングで同様のことをしたかどうかを教えてください。私は、完全にサーバーレスのホスティングインフラストラクチャのための現代のフロントエンドを構築できることをとても嬉しく思います。

Zeitはどうですか?

まあ、それはちょっとすべて自動です。 hugoコマンドを実行するためにstatic-builderを使用しstatic-builderたが、それはほとんど問題ありません。 :)

Paul Kinlan

Trying to make the web and developers better.

RSS Github Medium