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

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

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

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

これは、私のGithub Repoに対するFirebase Auth、コンテンツを編集するEditorJS(それはきちんとしている)、およびリポジトリにコミットするOctokat.jsと、Hugoビルドを行うZeitのGithub統合の組み合わせです。この設定により、ユーザーがWordpressのようなCMSを使用したデータベースに投稿を作成する方法と同様に、完全に自己ホスト型の静的CMSを作成できます。

この投稿では、インフラストラクチャの一部に焦点を当てるだけです。Githubに複数のファイルをコミットするのは、少し時間がかかったためです。

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

Githubに直接コミットする必要があるWeb UIを構築している場合、私が見つけた最高のライブラリはOctokatです-CORSで動作し、Github APIのAPI面全体を処理するようです。

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

  1. heads/masterとして知られるmasterブランチにのみプッシュできます。 1.特定のファイルが保存される場所がわかります(Hugoは特定のディレクトリ構造を強制します)

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

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

  1. heads/masterブランチのツリーの先端への参照を取得します。 1.コミットするファイルごとにblobを作成し、 sha識別子、パス、モードへの参照を配列に保存します。 1.すべてのブロブを含む新しいtreeを作成して、 heads/masterツリーの先端への参照に追加し、このツリーへの新しい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はどうですか?

まあ、それは今やちょっと全自動です。私はstatic-builderを使用してhugoコマンドを実行しましたが、これでstatic-builderです。 :)

About Me: Paul Kinlan

I lead the Chrome Developer Relations team at Google.

We want people to have the best experience possible on the web without having to install a native app or produce content in a walled garden.

Our team tries to make it easier for developers to build on the web by supporting every Chrome release, creating great content to support developers on web.dev, contributing to MDN, helping to improve browser compatibility, and some of the best developer tools like Lighthouse, Workbox, Squoosh to name just a few.