Meu site é entirely static . É construído com Hugo e hospedado com Zeit . Estou muito feliz com a configuração, chego perto de versões instantâneas e entrega de conteúdo CDN super rápida e posso fazer todas as coisas que preciso porque não preciso gerenciar nenhum estado.
Eu criei um simple UI para este site e também o meu podcast creator que me permite publicar rapidamente novos conteúdos para o meu site estaticamente hospedado.
Assim. Como eu fiz isso?
É uma combinação do Firebase Auth com o meu Github Repo, EditorJS para criar a edição do conteúdo (é legal) e o Octokat.js para se comprometer com o repo e, em seguida, com a integração do Github do Zeit para fazer o meu hugo build. Com essa configuração, posso ter um CMS estático totalmente auto-hospedado, semelhante a como um usuário pode criar postagens em um CMS com suporte de banco de dados, como o Wordpress.
Neste post, vou me concentrar apenas em uma parte da infraestrutura - cometer vários arquivos para o Github, porque demorei um pouco para trabalhar.
O código inteiro pode ser visto no meu repo .
Se você está construindo uma UI da Web que precisa ser submetida diretamente ao Github, a melhor biblioteca que eu encontrei é Octokat - ela funciona com o CORS e parece lidar com toda a superfície da API da API do Github.
O Git pode ser uma fera complexa quando se trata de entender como a árvore, galhos e outras peças funcionam, então tomei algumas decisões que tornaram isso mais fácil.
- Eu só serei capaz de empurrar para o branch master conhecido como
heads/master
. - Eu saberei onde certos arquivos serão armazenados (Hugo me obriga a ter uma estrutura de diretórios específica)
Com isso em mente, o processo geral para criar uma confirmação com vários arquivos é o seguinte:
Obtenha uma referência ao repo.
- Obtenha uma referência à ponta da árvore na ramificação
heads/master
. - Para cada arquivo que desejamos confirmar, crie um
blob
e, em seguida, armazene as referências ao identificador, caminho e modosha
em uma matriz. - Crie um novo
tree
que contenha todos os blobs para adicionar à referência à dica da árvoreheads/master
e armazene o novo ponteirosha
para essa árvore. - Crie uma confirmação que aponte para essa nova árvore e, em seguida, pressione para a ramificação
heads/master
.
O código praticamente segue esse fluxo. Como posso assumir a estrutura do caminho para determinadas entradas, não preciso criar nenhuma interface do usuário ou gerenciamento complexo para os arquivos.
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);
}
}
Deixe-me saber se você fez algo parecido com hospedagem estática. Estou muito empolgado por poder construir uma interface moderna para o que é uma infraestrutura de hospedagem totalmente sem servidor.
E quanto ao Zeit?
Bem, é meio que tudo automático agora. Eu uso o static-builder
para executar o comando hugo e é basicamente isso. :)