Wie man ein einfaches CMS mit Cloudflare, GitHub Actions und Metalsmith erstellt

Avatar of Jon Paul Uritis
Jon Paul Uritis am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Lass uns ein CMS bauen. Aber anstatt eine Benutzeroberfläche zu erstellen, bekommen wir diese Benutzeroberfläche kostenlos in Form von GitHub selbst! Wir werden GitHub nutzen, um den Inhalt für unseren statischen Website-Generator (es könnte *jeder* statische Website-Generator sein) zu verwalten! Hier ist die Essenz: GitHub wird der Ort sein, an dem wir Dateien verwalten, versionieren und speichern, und auch der Ort, an dem wir Inhalte bearbeiten. Wenn Bearbeitungen stattfinden, werden eine Reihe von Automatisierungen unsere Inhalte testen, verifizieren und schließlich auf Cloudflare bereitstellen.

Den vollständigen Code für das Projekt findest du auf GitHub. Ich betreibe meine eigene Website, jonpauluritis.com, genau auf diese Weise.

Wie sieht der vollständige Stack aus?

Hier ist der Technologie-Stack, mit dem wir in diesem Artikel arbeiten werden

  • Jeder Markdown-Editor (Optional. z.B. Typora.io)
  • Ein statischer Website-Generator (z.B. Metalsmith)
  • Github mit GitHub Actions (CICD und Deployment)
  • Cloudflare Workers

Warum sollte dich dieses Setup interessieren? Dieses Setup ist potenziell der schlankste, schnellste, günstigste (~5 $/Monat) und einfachste Weg, eine Website (oder Jamstack-Site) zu verwalten. Es ist sowohl aus technischer als auch aus Benutzeroberflächen-Sicht fantastisch. Dieses Setup ist so fantastisch, dass ich buchstäblich Aktien von Microsoft und Cloudflare gekauft habe. 

Aber bevor wir anfangen…

Ich werde dich nicht durch die Einrichtung von Konten bei diesen Diensten führen, das kannst du sicher selbst. Hier sind die Konten, die du einrichten musst: 

Ich würde auch Typora für ein fantastisches Markdown-Schreiberlebnis empfehlen, aber Markdown-Editoren sind eine sehr persönliche Sache, also verwende den Editor, der sich für dich richtig anfühlt. 

Projektstruktur

Um dir eine Vorstellung davon zu geben, wohin wir gehen, hier ist die Struktur des abgeschlossenen Projekts

├── build.js
├── .github/workflows
│   ├── deploy.yml
│   └── nodejs.js
├── layouts
│   ├── about.hbs
│   ├── article.hbs
│   ├── index.hbs
│   └── partials
│       └── navigation.hbs
├── package-lock.json
├── package.json
├── public
├── src
│   ├── about.md
│   ├── articles
│   │   ├── post1.md
│   │   └── post2.md
│   └── index.md
├── workers-site
└── wrangler.toml

Schritt 1: Kommandozeilen-Kram

Wechsle in einem Terminalverzeichnis zu dem Ort, an dem du solche Projekte aufbewahrst, und gib Folgendes ein

$ mkdir cms && cd cms && npm init -y

Das erstellt ein neues Verzeichnis, wechselt dorthin und initialisiert die Verwendung von npm.

Das Nächste, was wir tun wollen, ist, auf den Schultern von Giganten zu stehen. Wir werden eine Reihe von npm-Paketen verwenden, die uns bei Dingen helfen, wobei der Kern die Verwendung des statischen Website-Generators Metalsmith ist.

$ npm install --save-dev metalsmith metalsmith-markdown metalsmith-layouts metalsmith-collections metalsmith-permalinks handlebars jstransformer-handlebars

Neben Metalsmith gibt es noch ein paar andere nützliche Dinge. Warum Metalsmith? Lass uns darüber reden.

Schritt 2: Metalsmith

Ich probiere seit 2-3 Jahren statische Website-Generatoren aus und habe immer noch nicht „den einen“ gefunden. All die großen Namen – wie Eleventy, Gatsby, Hugo, Jekyll, Hexo und Vuepress – sind absolut großartig, aber ich komme über die Einfachheit und Erweiterbarkeit von Metalsmith nicht hinweg.

Als Beispiel wird dieser Code tatsächlich eine Website für dich bauen: 

// EXAMPLE... NOT WHAT WE ARE USING FOR THIS TUTORIAL
Metalsmith(__dirname)         
  .source('src')       
  .destination('dest')     
  .use(markdown())             
  .use(layouts())           
  .build((err) => if (err) throw err);

Ziemlich cool, oder?

Der Kürze halber gib dies im Terminal ein und wir erstellen eine Struktur und Dateien zum Starten.

Zuerst die Verzeichnisse erstellen

$ mkdir -p src/articles &&  mkdir -p layouts/partials 

Dann die Build-Datei erstellen

$ touch build.js

Als Nächstes erstellen wir einige Layout-Dateien

$ touch layouts/index.hbs && touch layouts/about.hbs && touch layouts/article.hbs && touch layouts/partials/navigation.hbt

Und schließlich richten wir unsere Inhaltsressourcen ein

$ touch src/index.md && touch src/about.md && touch src/articles/post1.md && touch src/articles/post1.md touch src/articles/post2.md

Der Projektordner sollte ungefähr so aussehen

├── build.js
├── layouts
│   ├── about.hbs
│   ├── article.hbs
│   ├── index.hbs
│   └── partials
│       └── navigation.hbs
├── package-lock.json
├── package.json
└── src
    ├── about.md
    ├── articles
    │   ├── post1.md
    │   └── post2.md
    └── index.md

Schritt 3: Lass uns etwas Code hinzufügen

Um Platz (und Zeit) zu sparen, kannst du die unten stehenden Befehle verwenden, um die Inhalte für unsere fiktive Website zu erstellen. Fühle dich frei, in „articles“ einzutauchen und deine eigenen Blogbeiträge zu erstellen. Der Schlüssel ist, dass die Beiträge einige Metadaten (auch „Front Matter“ genannt) benötigen, um korrekt generiert werden zu können. Die Dateien, die du bearbeiten möchtest, sind index.md, post1.md und post2.md.

Die Metadaten sollten etwa so aussehen: 

---
title: 'Post1'
layout: article.hbs 
---
## Post content here....

Oder, wenn du faul bist wie ich, benutze diese Terminalbefehle, um Mock-Inhalte von GitHub Gists zu deiner Seite hinzuzufügen

$ curl https://gist.githubusercontent.com/jppope/35dd682f962e311241d2f502e3d8fa25/raw/ec9991fb2d5d2c2095ea9d9161f33290e7d9bb9e/index.md > src/index.md
$ curl https://gist.githubusercontent.com/jppope/2f6b3a602a3654b334c4d8df047db846/raw/88d90cec62be6ad0b3ee113ad0e1179dfbbb132b/about.md > src/about.md
$ curl https://gist.githubusercontent.com/jppope/98a31761a9e086604897e115548829c4/raw/6fc1a538e62c237f5de01a926865568926f545e1/post1.md > src/articles/post1.md
$ curl https://gist.githubusercontent.com/jppope/b686802621853a94a8a7695eb2bc4c84/raw/9dc07085d56953a718aeca40a3f71319d14410e7/post2.md > src/articles/post2.md

Als Nächstes erstellen wir unsere Layouts und Teil-Layouts („Partials“). Wir werden Handlebars.js für unsere Vorlagensprache in diesem Tutorial verwenden, aber du kannst jede beliebige Vorlagensprache verwenden. Metalsmith kann mit so ziemlich allen umgehen, und ich habe keine starken Meinungen zu Vorlagensprachen.

Erstelle das Index-Layout

<!DOCTYPE html>
<html lang="en">
  <head>
    <style>
      /* Keeping it simple for the tutorial */
      body {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      .navigation {
        display: flex;
        justify-content: center;
        margin: 2rem 1rem;
      }
      .button {
        margin: 1rem;
        border: solid 1px #ccc;
        border-radius: 4px;        
        padding: 0.5rem 1rem;
        text-decoration: none;
      }
    </style>
  </head>
  <body>
    {{>navigation }}
    <div>
       {{#each articles }}
        <a href="{{path}}"><h3>{{ title }}</h3></a>
        <p>{{ description }}</p>
       {{/each }}
    </div>
  </body>
</html>

Ein paar Anmerkungen: 

  • Unsere „Navigation“ wurde noch nicht definiert, wird aber letztendlich den Bereich ersetzen, in dem {{>navigation }} residiert. 
  • {{#each }} iteriert durch die „Sammlung“ von Artikeln, die Metalsmith während seines Build-Prozesses generiert. 
  • Metalsmith hat *viele* Plugins, die du für Dinge wie Stylesheets, Tags usw. verwenden kannst, aber das ist nicht das Thema dieses Tutorials, also überlassen wir das dir zum Erkunden. 

Erstelle die Über-uns-Seite

Füge Folgendes zu deiner about.hbs-Seite hinzu

<!DOCTYPE html>
<html lang="en">
  <head>
    <style>
      /* Keeping it simple for the tutorial */
      body {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      .navigation {
        display: flex;
        justify-content: center;
        margin: 2rem 1rem;
      }
      .button {
        margin: 1rem;
        border: solid 1px #ccc;
        border-radius: 4px;        
        padding: 0.5rem 1rem;
        text-decoration: none;
      }    
    </style>
  </head>
  <body>
    {{>navigation }}
    <div>
      {{{contents}}}
    </div>
  </body>
</html>

Erstelle das Artikel-Layout

<!DOCTYPE html>
<html lang="en">
  <head>
    <style>
      /* Keeping it simple for the tutorial */
      body {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      .navigation {
        display: flex;
        justify-content: center;
        margin: 2rem 1rem;
      }
      .button {
        margin: 1rem;
        border: solid 1px #ccc;
        border-radius: 4px;        
        padding: 0.5rem 1rem;
        text-decoration: none;
      }
    </style>
  </head>
  <body>
    {{>navigation }}
    <div>
      {{{contents}}}
    </div>
  </body>
</html>

Du hast vielleicht bemerkt, dass dies das exakt gleiche Layout wie die Über-uns-Seite ist. Das ist es. Ich wollte nur abdecken, wie man zusätzliche Seiten hinzufügt, damit du weißt, wie man das macht. Wenn du möchtest, dass diese anders ist, nur zu.

Navigation hinzufügen

Füge Folgendes zur Datei layouts/partials/navigation.hbs hinzu

<div class="navigation">
  <div>
    <a class="button" href="/">Home</a>
    <a class="button" href="/about">About</a>
  </div>
</div>

Sicher, es ist nicht viel... aber das hier soll wirklich kein Metalsmith/SSG-Tutorial sein.  ¯\_(ツ)_¯

Schritt 4: Die Build-Datei

Das Herz und die Seele von Metalsmith ist die Build-Datei. Der Vollständigkeit halber gehe ich sie Zeile für Zeile durch. 

Wir beginnen mit dem Import der Abhängigkeiten

Schneller Hinweis: Metalsmith wurde 2014 erstellt, und das vorherrschende Modulsystem war damals common.js, daher bleibe ich bei require-Statements im Gegensatz zu ES-Modulen. Es ist auch erwähnenswert, dass die meisten anderen Tutorials ebenfalls require-Statements verwenden, sodass das Überspringen eines Build-Schritts mit Babel das Leben hier nur ein wenig weniger komplex macht.

// What we use to glue everything together
const Metalsmith = require('metalsmith');


// compile from markdown (you can use targets as well)
const markdown = require('metalsmith-markdown');


// compiles layouts
const layouts = require('metalsmith-layouts');


// used to build collections of articles
const collections = require('metalsmith-collections');


// permalinks to clean up routes
const permalinks = require('metalsmith-permalinks');


// templating
const handlebars = require('handlebars');


// register the navigation
const fs = require('fs');
handlebars.registerPartial('navigation', fs.readFileSync(__dirname + '/layouts/partials/navigation.hbt').toString());


// NOTE: Uncomment if you want a server for development
// const serve = require('metalsmith-serve');
// const watch = require('metalsmith-watch');

Als Nächstes werden wir Metalsmith einbeziehen und ihm mitteilen, wo er seine Kompilierungsziele finden soll

// Metalsmith
Metalsmith(__dirname)            
  // where your markdown files are
  .source('src')      
  // where you want the compliled files to be rendered
  .destination('public')

Bisher alles gut. Nachdem wir Quelle und Ziel festgelegt haben, werden wir das Markdown-Rendering, das Layout-Rendering einrichten und Metalsmith mitteilen, „Collections“ zu verwenden. Dies ist eine Möglichkeit, Dateien zusammenzufassen. Ein einfaches Beispiel wäre so etwas wie „Blogbeiträge“, aber es könnte wirklich alles sein, zum Beispiel Rezepte, Whiskey-Bewertungen oder was auch immer. Im obigen Beispiel nennen wir die Sammlung „articles“.

 // previous code would go here


  // collections create groups of similar content
  .use(collections({ 
    articles: {
      pattern: 'articles/*.md',
    },
  }))
  // compile from markdown
  .use(markdown())
  // nicer looking links
  .use(permalinks({
    pattern: ':collection/:title'
  }))
  // build layouts using handlebars templates
  // also tell metalsmith where to find the raw input
  .use(layouts({
    engine: 'handlebars',
    directory: './layouts',
    default: 'article.html',
    pattern: ["*/*/*html", "*/*html", "*html"],
    partials: {
      navigation: 'partials/navigation',
    }
  }))


// NOTE: Uncomment if you want a server for development
// .use(serve({
//   port: 8081,
//   verbose: true
// }))
// .use(watch({
//   paths: {
//     "${source}/**/*": true,
//     "layouts/**/*": "**/*",
//   }
// }))

Als Nächstes fügen wir das Markdown-Plugin hinzu, damit wir Markdown für Inhalte verwenden können, die zu HTML kompiliert werden.

Von dort aus verwenden wir das Layouts-Plugin, um unseren rohen Inhalt in das Layout zu packen, das wir im Layout-Ordner definieren. Du kannst mehr über die Einzelheiten auf der offiziellen Plugin-Website erfahren, aber das Ergebnis ist, dass wir {{{contents}}} in einer Vorlage verwenden können und es funktioniert einfach. 

Die letzte Ergänzung zu unserem kleinen Build-Skript wird die Build-Methode sein

// Everything else would be above this
.build(function(err) {
  if (err) {
    console.error(err)
  }
  else {
    console.log('build completed!');
  }
});

Alles zusammen ergibt ein Build-Skript, das so aussehen sollte

const Metalsmith = require('metalsmith');
const markdown = require('metalsmith-markdown');
const layouts = require('metalsmith-layouts');
const collections = require('metalsmith-collections');
const permalinks = require('metalsmith-permalinks');
const handlebars = require('handlebars');
const fs = require('fs');


// Navigation
handlebars.registerPartial('navigation', fs.readFileSync(__dirname + '/layouts/partials/navigation.hbt').toString());


Metalsmith(__dirname)
  .source('src')
  .destination('public')
  .use(collections({
    articles: {
      pattern: 'articles/*.md',
    },
  }))
  .use(markdown())
  .use(permalinks({
    pattern: ':collection/:title'
  }))
  .use(layouts({
    engine: 'handlebars',
    directory: './layouts',
    default: 'article.html',
    pattern: ["*/*/*html", "*/*html", "*html"],
    partials: {
      navigation: 'partials/navigation',
    }
  }))
  .build(function (err) {
    if (err) {
      console.error(err)
    }
    else {
      console.log('build completed!');
    }
  });

Ich bin ein Fan von einfach und sauber und meiner bescheidenen Meinung nach wird es nicht einfacher oder sauberer als ein Metalsmith-Build. Wir müssen nur eine schnelle Aktualisierung der package.json-Datei vornehmen und dann können wir dies ausführen

 "name": "buffaloTraceRoute",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "node build.js",
    "test": "echo \"No Tests Yet!\" "
  },
  "keywords": [],
  "author": "Your Name",
  "license": "ISC",
  "devDependencies": {
    // these should be the current versions
    // also... comments aren't allowed in JSON
  }
}

Wenn du deine Arbeit sehen möchtest, kannst du die Teile der Build-Datei auskommentieren, die es dir erlauben, dein Projekt zu servieren und Dinge wie npm run build auszuführen. Stelle nur sicher, dass du diesen Code vor der Bereitstellung entfernst.

Arbeiten mit Cloudflare

Als Nächstes arbeiten wir mit Cloudflare zusammen, um Zugriff auf ihre Cloudflare Workers zu erhalten. Hier kommt die 5 $/Monat Kosten ins Spiel.

Jetzt fragst du dich vielleicht: „OK, aber warum Cloudflare? Was ist mit etwas Kostenlosem wie GitHub Pages oder Netlify?“ Das ist eine gute Frage. Es gibt viele Möglichkeiten, eine statische Website bereitzustellen, warum also eine Methode einer anderen vorziehen?

Nun, Cloudflare hat ein paar Vorteile…

Geschwindigkeit und Leistung

Einer der größten Gründe für den Umstieg auf einen statischen Website-Generator ist die Verbesserung der Website-Leistung. Die Verwendung von Cloudflare Workers Sites kann deine Leistung *noch weiter* verbessern.

Hier ist ein Diagramm, das Cloudflare im Vergleich zu zwei konkurrierenden Alternativen zeigt

Mit freundlicher Genehmigung von Cloudflare

Der einfache Grund, warum Cloudflare am schnellsten ist: Eine Website wird in über 190 Datenzentren weltweit bereitgestellt. Dies reduziert die Latenz, da die Benutzer die Assets von einem Standort erhalten, der physisch näher an ihnen liegt.

Einfachheit

Zugegebenermaßen kann die anfängliche Konfiguration von Cloudflare Workers etwas knifflig sein, wenn man nicht weiß, wie man Umgebungsvariablen einrichtet. Aber nachdem du die Grundeinstellungen für deinen Computer eingerichtet hast, ist die Bereitstellung auf Cloudflare so einfach wie `wrangler publish` aus dem Verzeichnis der Website. Dieses Tutorial konzentriert sich auf den CI/CD-Aspekt der Bereitstellung auf Cloudflare, der etwas aufwendiger ist, aber immer noch unglaublich einfach im Vergleich zu den meisten anderen Bereitstellungsprozessen. 

(Es lohnt sich, GitHub Pages und Netlify zu erwähnen, die in diesem Bereich ebenfalls hervorragend sind. Die Entwicklererfahrung all dieser drei Unternehmen ist erstaunlich.)

Mehr Leistung für das Geld

Während GitHub Pages und Netlify beide kostenlose Stufen haben, ist deine Nutzung auf 100 GB Bandbreite pro Monat (begrenzt) beschränkt. Versteh mich nicht falsch, das ist ein super großzügiges Limit. Aber danach hast du Pech. GitHub Pages bietet nichts mehr als das und Netlify springt auf 45 $/Monat, was Cloudflare's 5 $/Monat sehr vernünftig macht.

DienstKostenlose Stufe BandbreiteBezahlte Stufe PreisBezahlte Stufe Anfragen / Bandbreite
GitHub Pages100 GBN/AN/A
Netlify100 GB$45~150K / 400 GB
Cloudflare Workers Sitesnone$510 Mio. / unbegrenzt 
Berechnungen gehen von einer durchschnittlichen Website von 3 MB aus. Cloudflare hat zusätzliche Limits für die CPU-Nutzung. GitHub Pages sollte nicht für Websites mit Kreditkartentransaktionen verwendet werden.

Sicher, es gibt keine kostenlose Stufe für Cloudflare, aber 5 $ für 10 Millionen Anfragen sind ein Schnäppchen. Ich wäre auch nachlässig, wenn ich nicht erwähnen würde, dass GitHub Pages im letzten Jahr einige Ausfälle hatte. Das ist für eine Demo-Website in Ordnung, aber für ein Unternehmen wären das schlechte Nachrichten.

Cloudflare bietet viele zusätzliche Funktionen, die es wert sind, kurz erwähnt zu werden: kostenlose SSL-Zertifikate, kostenlose (und einfache) DNS-Weiterleitung, ein benutzerdefinierter Workers Sites-Domainname für deine Projekte (was großartig für Staging ist), unbegrenzte Umgebungen (z.B. Staging) und die Registrierung eines Domainnamens zum Selbstkostenpreis (im Gegensatz zu den Aufschlagpreisen anderer Registrar).

Bereitstellung auf Cloudflare

Cloudflare bietet einige großartige Tutorials, wie du ihr Cloudflare Workers Produkt nutzen kannst. Wir werden hier die wichtigsten Punkte behandeln.

Stelle zuerst sicher, dass die Cloudflare CLI, Wrangler, installiert ist

$ npm i @cloudflare/wrangler -g

Als Nächstes fügen wir Cloudflare Sites zum Projekt hinzu, so:

wrangler init --site cms 

Wenn ich keinen Fehler gemacht und einen Schritt vergessen habe, sollte hier das im Terminal stehen:

⬇️ Installing cargo-generate...
🔧   Creating project called `workers-site`...
✨   Done! New project created /Users/<User>/Code/cms/workers-site
✨  Succesfully scaffolded workers site
✨  Succesfully created a `wrangler.toml`

Es sollte auch einen generierten Ordner im Projekt-Root namens /workers-site sowie eine Konfigurationsdatei namens wrangler.toml geben – hier liegt die Magie.

name = "cms"
type = "webpack"
account_id = ""
workers_dev = true
route = ""
zone_id = ""


[site]
bucket = ""
entry-point = "workers-site"

Du hast vielleicht schon erraten, was als Nächstes kommt… wir müssen der Konfigurationsdatei einige Informationen hinzufügen! Das erste Schlüssel/Wert-Paar, das wir aktualisieren werden, ist die bucket-Eigenschaft.

bucket = "./public"

Als Nächstes müssen wir die Account ID und Zone ID (d. h. die Route für deinen Domainnamen) abrufen. Du findest sie in deinem Cloudflare-Konto ganz unten im Dashboard für deine Domain

Stopp! Bevor du weitermachst, vergiss nicht, auf die Schaltfläche „Get your API token“ zu klicken, um das letzte Konfigurationsteil zu erhalten, das wir benötigen werden. Speichere es auf einem Notizblock oder irgendwo griffbereit, da wir es für den nächsten Abschnitt benötigen. 

Puh! Gut, die nächste Aufgabe ist, die Account ID und Zone ID, die wir gerade abgerufen haben, zur .toml-Datei hinzuzufügen

name = "buffalo-traceroute"
type = "webpack"
account_id = "d7313702f333457f84f3c648e9d652ff" # Fake... use your account_id
workers_dev = true
# route = "example.com/*" 
# zone_id = "805b078ca1294617aead2a1d2a1830b9" # Fake... use your zone_id


[site]
bucket = "./public"
entry-point = "workers-site"
(Again, those IDs are fake.)

Auch hier sind die IDs gefälscht. Möglicherweise wirst du aufgefordert, Anmeldeinformationen auf deinem Computer einzurichten. Wenn das der Fall ist, führe wrangler config im Terminal aus.

GitHub Actions

Das letzte Puzzleteil ist die Konfiguration von GitHub, um automatische Deployments für uns durchzuführen. Nachdem ich mich bereits mit CI/CD-Setups beschäftigt hatte, war ich auf das Schlimmste vorbereitet, aber erstaunlicherweise ist GitHub Actions für diese Art von Setup sehr einfach.

Wie funktioniert das also?

Stelle zuerst sicher, dass dein GitHub-Konto GitHub Actions aktiviert hat. Es befindet sich technisch noch in der Beta, aber ich bin bisher auf keine Probleme gestoßen.

Als Nächstes müssen wir ein Repository in GitHub erstellen und unseren Code hochladen. Beginne damit, zu GitHub zu gehen und ein Repository zu erstellen.

Dieses Tutorial soll nicht die Feinheiten von Git und/oder GitHub abdecken, aber es gibt eine großartige Einführung. Oder kopiere und füge die folgenden Befehle im Stammverzeichnis des Projekts ein

# run commands one after the other
$ git init
$ touch .gitignore && echo 'node_modules' > .gitignore
$ git add .
$ git commit -m 'first commit'
$ git remote add origin https://github.com/{username}/{repo name}
$ git push -u origin master

Das *sollte* das Projekt zu GitHub hinzufügen. Ich sage das mit ein wenig Zögern, aber hier gehen normalerweise alle Dinge schief. Wenn du zum Beispiel zu viele Befehle ins Terminal eingibst, hat GitHub plötzlich eine Störung oder das Terminal kann den Pfad zu Python nicht lokalisieren. Sei vorsichtig!

Angenommen, wir sind an diesem Punkt vorbei, ist unsere nächste Aufgabe, GitHub Actions zu aktivieren und ein Verzeichnis namens .github/workflows im Stammverzeichnis des Projektverzeichnisses zu erstellen. (GitHub kann dies auch automatisch tun, indem es den „node“-Workflow beim Aktivieren von Actions hinzufügt. Zum Zeitpunkt des Schreibens ist das Hinzufügen eines GitHub Actions Workflows Teil der Benutzeroberfläche von GitHub.)

Sobald wir das Verzeichnis im Projekt-Root haben, können wir die letzten beiden Dateien hinzufügen. Jede Datei wird einen anderen Workflow behandeln

  1. Ein Workflow, um zu überprüfen, ob Updates zusammengeführt werden können (d. h. das „CI“ in CI/CD)
  2. Ein Workflow, um Änderungen bereitzustellen, sobald sie in den Master-Branch zusammengeführt wurden (d. h. das „CD“ in CI/CD)
# integration.yml
name: Integration


on:
  pull_request:
    branches: [ master ]


jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [10.x, 12.x]
    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test
      env:
        CI: true

Dies ist ein geradliniger Workflow. So geradlinig, dass ich ihn direkt aus der offiziellen GitHub Actions Dokumentation kopiert und nur geringfügig geändert habe. Lass uns durchgehen, was dort tatsächlich passiert

  1. on: Führe diesen Workflow nur aus, wenn ein Pull-Request für den Master-Branch erstellt wird
  2. jobs: Führe die folgenden Schritte für zwei Node-Umgebungen aus (z. B. Node 10 und Node 12 – Node 12 ist derzeit die empfohlene Version). Dies wird kompilieren, wenn ein Build-Skript definiert ist. Es werden auch Tests ausgeführt, wenn ein Testskript definiert ist.

Die zweite Datei ist unser Bereitstellungsskript und ist etwas aufwendiger.

# deploy.yml
name: Deploy


on:
  push:
    branches:
      - master


jobs:
  deploy:
    runs-on: ubuntu-latest
    name: Deploy
    strategy:
      matrix:
        node-version: [10.x]


    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm install
      - uses: actions/checkout@master
      - name: Build site
        run: "npm run build"
      - name: Publish
        uses: cloudflare/[email protected]
        with:
          apiToken: ${{ secrets.CF_API_TOKEN }}

Wichtig! Erinnerst du dich an das Cloudflare API-Token, das ich ganz am Anfang erwähnt habe? Jetzt ist es Zeit, es zu verwenden. Gehe zu den Projekteinstellungen und füge ein Geheimnis hinzu. Nenne das Geheimnis CF_API_TOKEN und füge das API-Token hinzu.

Lass uns durchgehen, was in diesem Skript passiert

  1. on: Führe die Schritte aus, wenn Code in den Master-Branch zusammengeführt wird
  2. steps: Verwende Node.js, um alle Abhängigkeiten zu installieren, verwende Node.js, um die Website zu bauen, und verwende dann Cloudflare Wrangler, um die Website zu veröffentlichen

Hier ist eine kurze Zusammenfassung, wie das Projekt vor dem Ausführen eines Builds aussehen sollte (ohne node_modules): 

├── build.js
├── dist
│   └── worker.js
├── layouts
│   ├── about.hbs
│   ├── article.hbs
│   ├── index.hbs
│   └── partials
│       └── navigation.hbs
├── package-lock.json
├── package.json
├── public
├── src
│   ├── about.md
│   ├── articles
│   │   ├── post1.md
│   │   └── post2.md
│   └── index.md
├── workers-site
│   ├── index.js
│   ├── package-lock.json
│   ├── package.json
│   └── worker
│       └── script.js
└── wrangler.toml

Ein GitHub-basiertes CMS

Okay, ich habe es bis hierher geschafft… Mir wurde ein CMS versprochen? Wo ist die Datenbank und meine GUI, in die ich mich einlogge und so?

Keine Sorge, du bist am Ziel! GitHub *ist jetzt dein CMS* und so funktioniert es

  1. Schreibe eine Markdown-Datei (mit Front Matter).
  2. Öffne GitHub und gehe zum Projekt-Repository.
  3. Klicke in das Verzeichnis „Articles“ und lade den neuen Artikel hoch. GitHub fragt, ob ein neuer Branch zusammen mit einem Pull-Request erstellt werden soll. Die Antwort ist ja. 
  4. Nachdem die Integration verifiziert wurde, kann der Pull-Request zusammengeführt werden, was die Bereitstellung auslöst.
  5. Lehne dich zurück, entspanne dich und warte 10 Sekunden… der Inhalt wird in 164 Rechenzentren weltweit bereitgestellt.

Glückwunsch! Du hast jetzt ein minimales Git-basiertes CMS, das praktisch jeder nutzen kann. 

Fehlerbehebungshinweise

  • Metalsmith-Layouts können manchmal etwas knifflig sein. Versuche, diese Debug-Zeile vor dem Build-Schritt hinzuzufügen, um nützliche Ausgaben zu erhalten: DEBUG=metalsmith-layouts npm run build
  • Gelegentlich musste ich bei GitHub Actions node_modules in den Commit aufnehmen, damit es bereitgestellt werden konnte… das war seltsam für mich (und keine empfohlene Vorgehensweise), behob aber die Bereitstellung.
  • Wenn du nach einer einfachen Möglichkeit suchst, Assets (Bilder, SVGs usw.) zu verwalten, ist dieses Plugin fantastisch: https://github.com/alexgs/metalsmith-assets-improved (danke an @gofango für den Hinweis auf das Versäumnis!)
  • Bitte lass mich wissen, wenn du auf Probleme stößt, und wir können sie dieser Liste hinzufügen!