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

Mi sitio es entirely static . Está construido con Hugo y alojado con Zeit . Estoy bastante contento con la configuración, obtengo compilaciones instantáneas y entrega de contenido CDN'd súper rápida y puedo hacer todo lo que necesito porque no tengo que administrar ningún estado.

He creado un simple UI para este sitio y también mi podcast creator que me permite publicar rápidamente contenido nuevo en mi sitio alojado estáticamente.

Asi que. Como lo hice

Es una combinación de Firebase Auth contra mi Github Repo, EditorJS para crear editar el contenido (es ordenado) y Octokat.js para comprometerse con el repositorio y luego la integración de Zeit's Github para hacer mi construcción hugo. Con esta configuración, puedo tener un CMS estático totalmente autohospedado, similar a cómo un usuario podría crear publicaciones en un CMS respaldado por una base de datos como Wordpress.

En esta publicación, me voy a centrar en una parte de la infraestructura: enviar varios archivos a Github porque me tomó un poco de tiempo hacer ejercicio.

El código completo se puede ver en mi repo .

Si está creando una interfaz de usuario web que necesita comprometerse directamente con Github, la mejor biblioteca que he encontrado es Octokat: funciona con CORS y parece manejar toda la superficie API de la API de Github.

Git puede ser una bestia compleja cuando se trata de comprender cómo funcionan el árbol, las ramas y otras piezas, por lo que tomé algunas decisiones que lo hicieron más fácil.

  1. Solo podré acceder a la rama maestra conocida como heads/master .
  2. Sabré dónde se almacenarán ciertos archivos (Hugo me obliga a tener una estructura de directorio específica)

Con eso en mente, el proceso general para crear una confirmación con múltiples archivos es el siguiente:

Obtenga una referencia al repositorio.

  1. Obtenga una referencia a la punta del árbol en la rama heads/master .
  2. Para cada archivo que queremos confirmar, cree un blob y luego almacene las referencias al identificador de sha , ruta, modo en una matriz.
  3. Cree un nuevo tree que contenga todos los blobs para agregar a la referencia a la punta del árbol heads/master y almacene el nuevo puntero sha a este árbol.
  4. Cree una confirmación que apunte a este nuevo árbol y luego empuje a la rama heads/master .

El código sigue más o menos ese flujo. Debido a que puedo asumir la estructura de ruta para ciertas entradas, no necesito construir una interfaz de usuario compleja o administración para los archivos.

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);
  }
}

Avíseme si ha hecho algo similar con el alojamiento estático. Estoy muy emocionado de poder construir una interfaz moderna para lo que es una infraestructura de alojamiento completamente sin servidor.

¿Qué hay de Zeit?

Bueno, ahora es algo automático. Uso el static-builder para ejecutar el comando hugo y eso es todo. :)

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.