From 070eaef72de5d7534359991e9a623e19a94d5c95 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 17:22:37 -0500 Subject: [PATCH 1/7] Update homepage repo to build using natter. --- docker/server/Dockerfile | 10 ++++++++-- docker/server/Makefile | 1 + natter.toml | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 natter.toml diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index 3bca508..62e76b9 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -1,4 +1,10 @@ -FROM alpine:3.18 +FROM harbor.fizz.buzz/private/natter:latest AS builder + +COPY . /source +RUN ls /source/ +RUN natter build --config /source/natter.toml + +FROM alpine:3.18 AS server RUN apk add --no-cache bash nginx RUN addgroup web && adduser -D -G web web && install -d -D -o web -g web -m 700 /srv/http/public @@ -6,6 +12,6 @@ RUN ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/ COPY --chown=web:web docker/server/nginx.conf /srv/http COPY --chown=web:web docker/server/headers.include /srv/http -COPY --chown=web:web static/ /srv/http/public/ +COPY --from=builder --chown=web:web /source/output/ /srv/http/public/ ENTRYPOINT ["/usr/sbin/nginx", "-c", "/srv/http/nginx.conf", "-e", "stderr", "-g", "daemon off;"] diff --git a/docker/server/Makefile b/docker/server/Makefile index 3bac958..539f791 100644 --- a/docker/server/Makefile +++ b/docker/server/Makefile @@ -42,6 +42,7 @@ else endif .PHONY: run +run: build run: ## Launch the docker image > docker run --rm -i -t -p "8080:8080" $(IMAGE_NAME) diff --git a/natter.toml b/natter.toml new file mode 100644 index 0000000..868f4c2 --- /dev/null +++ b/natter.toml @@ -0,0 +1 @@ +site_title = "FizzBuzz Blog" From 3ebe169ee9e201a28f53ad444b7b2ae9b73dfae8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 17:39:46 -0500 Subject: [PATCH 2/7] Add a CI job to build the staging image of the homepage. The staging image will be whatever commit was most recently pushed, deployed instantly to a protected subdomain. --- .../pipeline-build-homepage-staging.yaml | 173 ++++++++++++++++++ .lighthouse/triggers.yaml | 7 + 2 files changed, 180 insertions(+) create mode 100644 .lighthouse/pipeline-build-homepage-staging.yaml diff --git a/.lighthouse/pipeline-build-homepage-staging.yaml b/.lighthouse/pipeline-build-homepage-staging.yaml new file mode 100644 index 0000000..772d435 --- /dev/null +++ b/.lighthouse/pipeline-build-homepage-staging.yaml @@ -0,0 +1,173 @@ +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + name: build-homepage-staging +spec: + pipelineSpec: + params: + - name: image-name + description: The name for the built image + type: string + - name: path-to-image-context + description: The path to the build context + type: string + - name: path-to-dockerfile + description: The path to the Dockerfile + type: string + tasks: + - name: get-time + taskSpec: + metadata: {} + stepTemplate: + image: alpine:3.18 + name: "" + resources: + requests: + cpu: 10m + memory: 600Mi + workingDir: /workspace/source + results: + - name: unix-time + description: The current date in unix timestamp format + steps: + - image: alpine:3.18 + name: get-time-step + script: | + #!/usr/bin/env sh + echo -n "$(date +%s)" | tee $(results.unix-time.path) + - name: report-pending + taskRef: + name: gitea-set-status + runAfter: + - fetch-repository + params: + - name: CONTEXT + value: "$(params.JOB_NAME)" + - name: REPO_FULL_NAME + value: "$(params.REPO_OWNER)/$(params.REPO_NAME)" + - name: GITEA_HOST_URL + value: code.fizz.buzz + - name: SHA + value: "$(tasks.fetch-repository.results.commit)" + - name: DESCRIPTION + value: "Build $(params.JOB_NAME) has started" + - name: STATE + value: pending + - name: TARGET_URL + value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)" + - name: fetch-repository + taskRef: + name: git-clone + workspaces: + - name: output + workspace: git-source + params: + - name: url + value: $(params.REPO_URL) + - name: revision + value: $(params.PULL_BASE_SHA) + - name: deleteExisting + value: "true" + - name: build-image + taskRef: + name: kaniko + params: + - name: IMAGE + value: "$(params.image-name):$(tasks.get-time.results.unix-time)" + - name: CONTEXT + value: $(params.path-to-image-context) + - name: DOCKERFILE + value: $(params.path-to-dockerfile) + - name: BUILDER_IMAGE + value: "gcr.io/kaniko-project/executor:v1.12.1" + - name: EXTRA_ARGS + value: + - "--destination=$(params.image-name)" # Also write the :latest image + - --cache=true + - --cache-copy-layers + - --cache-repo=harbor.fizz.buzz/kanikocache/cache + - --use-new-run # Should result in a speed-up + - --reproducible # To remove timestamps so layer caching works. + - --snapshot-mode=redo + - --skip-unused-stages=true + - --registry-mirror=dockerhub.dockerhub.svc.cluster.local + workspaces: + - name: source + workspace: git-source + - name: dockerconfig + workspace: docker-credentials + runAfter: + - get-time + - report-pending + - fetch-repository + finally: + - name: report-success + when: + - input: "$(tasks.status)" + operator: in + values: ["Succeeded", "Completed"] + taskRef: + name: gitea-set-status + params: + - name: CONTEXT + value: "$(params.JOB_NAME)" + - name: REPO_FULL_NAME + value: "$(params.REPO_OWNER)/$(params.REPO_NAME)" + - name: GITEA_HOST_URL + value: code.fizz.buzz + - name: SHA + value: "$(tasks.fetch-repository.results.commit)" + - name: DESCRIPTION + value: "Build $(params.JOB_NAME) has succeeded" + - name: STATE + value: success + - name: TARGET_URL + value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)" + - name: report-failure + when: + - input: "$(tasks.status)" + operator: in + values: ["Failed"] + taskRef: + name: gitea-set-status + params: + - name: CONTEXT + value: "$(params.JOB_NAME)" + - name: REPO_FULL_NAME + value: "$(params.REPO_OWNER)/$(params.REPO_NAME)" + - name: GITEA_HOST_URL + value: code.fizz.buzz + - name: SHA + value: "$(tasks.fetch-repository.results.commit)" + - name: DESCRIPTION + value: "Build $(params.JOB_NAME) has failed" + - name: STATE + value: failure + - name: TARGET_URL + value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)" + workspaces: + - name: git-source + - name: docker-credentials + workspaces: + - name: git-source + volumeClaimTemplate: + spec: + storageClassName: "nfs-client" + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + subPath: rust-source + - name: docker-credentials + secret: + secretName: harbor-plain + serviceAccountName: build-bot + timeout: 240h0m0s + params: + - name: image-name + value: "harbor.fizz.buzz/private/homepage-staging" + - name: path-to-image-context + value: . + - name: path-to-dockerfile + value: docker/server/Dockerfile diff --git a/.lighthouse/triggers.yaml b/.lighthouse/triggers.yaml index 8277f26..12e1c17 100644 --- a/.lighthouse/triggers.yaml +++ b/.lighthouse/triggers.yaml @@ -28,6 +28,13 @@ spec: storage: 10Gi subPath: homepage-source params: [] + - name: build-homepage-staging + source: "pipeline-build-homepage-staging.yaml" + # Override https-based url from lighthouse events. + clone_uri: "git@code.fizz.buzz:talexander/homepage.git" + skip_branches: + # We already run on every commit, so running when the semver tags get pushed is causing needless double-processing. + - "^v[0-9]+\\.[0-9]+\\.[0-9]+$" - name: build-homepage agent: tekton-pipeline branches: From 3c2cff0cef91642f5965a884f5a5841578ce2914 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 17:49:07 -0500 Subject: [PATCH 3/7] Use resolvers for tasks. --- .../pipeline-build-homepage-staging.yaml | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/.lighthouse/pipeline-build-homepage-staging.yaml b/.lighthouse/pipeline-build-homepage-staging.yaml index 772d435..957fada 100644 --- a/.lighthouse/pipeline-build-homepage-staging.yaml +++ b/.lighthouse/pipeline-build-homepage-staging.yaml @@ -37,7 +37,14 @@ spec: echo -n "$(date +%s)" | tee $(results.unix-time.path) - name: report-pending taskRef: - name: gitea-set-status + resolver: git + params: + - name: url + value: https://github.com/tektoncd/catalog.git + - name: revision + value: df36b3853a5657fd883015cdbf07ad6466918acf + - name: pathInRepo + value: task/gitea-set-status/0.1/gitea-set-status.yaml runAfter: - fetch-repository params: @@ -57,7 +64,14 @@ spec: value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)" - name: fetch-repository taskRef: - name: git-clone + resolver: git + params: + - name: url + value: https://github.com/tektoncd/catalog.git + - name: revision + value: df36b3853a5657fd883015cdbf07ad6466918acf + - name: pathInRepo + value: task/git-clone/0.9/git-clone.yaml workspaces: - name: output workspace: git-source @@ -70,7 +84,14 @@ spec: value: "true" - name: build-image taskRef: - name: kaniko + resolver: git + params: + - name: url + value: https://github.com/tektoncd/catalog.git + - name: revision + value: df36b3853a5657fd883015cdbf07ad6466918acf + - name: pathInRepo + value: task/kaniko/0.6//kaniko.yaml params: - name: IMAGE value: "$(params.image-name):$(tasks.get-time.results.unix-time)" @@ -107,7 +128,14 @@ spec: operator: in values: ["Succeeded", "Completed"] taskRef: - name: gitea-set-status + resolver: git + params: + - name: url + value: https://github.com/tektoncd/catalog.git + - name: revision + value: df36b3853a5657fd883015cdbf07ad6466918acf + - name: pathInRepo + value: task/gitea-set-status/0.1/gitea-set-status.yaml params: - name: CONTEXT value: "$(params.JOB_NAME)" @@ -129,7 +157,14 @@ spec: operator: in values: ["Failed"] taskRef: - name: gitea-set-status + resolver: git + params: + - name: url + value: https://github.com/tektoncd/catalog.git + - name: revision + value: df36b3853a5657fd883015cdbf07ad6466918acf + - name: pathInRepo + value: task/gitea-set-status/0.1/gitea-set-status.yaml params: - name: CONTEXT value: "$(params.JOB_NAME)" From a70809ddbb53adf7b67ca3567f92732b54492f22 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 17:50:56 -0500 Subject: [PATCH 4/7] Add docker ignore file. --- .dockerignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f3b6411 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +**/.git From d5b6b93f719bd66a7ba65c828f432465639a00d9 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 19:00:34 -0500 Subject: [PATCH 5/7] Replace the static html homepage with an org page. --- pages/index.org | 25 +++++++++++++++++++++++++ static/index.html | 34 ---------------------------------- 2 files changed, 25 insertions(+), 34 deletions(-) create mode 100644 pages/index.org delete mode 100644 static/index.html diff --git a/pages/index.org b/pages/index.org new file mode 100644 index 0000000..395ba44 --- /dev/null +++ b/pages/index.org @@ -0,0 +1,25 @@ +#+OPTIONS: html-postamble:nil +#+title: FizzBuzz Blog +#+date: <2023-12-23 Sat> +#+author: Tom Alexander +#+email: +#+language: en +#+select_tags: export +#+exclude_tags: noexport + +My dev blog will appear here as soon as I finish writing articles worthy of publishing. In the mean time, please check out my repos at [[https://code.fizz.buzz/explore/repos][code.fizz.buzz]]. + +Links: +- My personal repos: [[https://code.fizz.buzz/explore/repos][code.fizz.buzz]] +- LinkedIn: https://www.linkedin.com/in/tom-alexander-b6a18216/ +- GitHub: https://github.com/tomalexander +- Resume: https://fizz.buzz/resume.pdf +- PGP Key: https://fizz.buzz/pgp.asc + +* Why is your website the way it is? + +I used to have a developer blog hosted at this domain. I quickly developed an appreciation for the power of org-mode for writing the content of the blog but I grew tired of inconsistent build results. The static site generators at the time would function by calling out to emacs itself to parse the org-mode and export HTML which meant that updates to emacs, my elisp packages, or the static site generator could cause compatibility issues. This often lead to things like escaping issues in old blog posts going unnoticed. + +To solve the issue, and to seize the opportunity to gain more experience in Rust, I decided to write my own static site generator that would not depend on outside tools. So far I have written [[https://code.fizz.buzz/talexander/duster][the template engine]] and I am in the process of writing [[https://code.fizz.buzz/talexander/organic][an org-mode parser]]. When that is done, it should just be a matter of tying those two together with some minor glue to make a static site generator to create the new version of this site. Until that is done, I am using this hastily thrown-together manually-written html file as a placeholder. + +That isn't to say that there are no exciting things hosted on this server, just not at the root domain. For example, this server is running kubernetes that I set up manually following [[https://github.com/kelseyhightower/kubernetes-the-hard-way][kubernetes-the-hard-way]] in a bunch of [[https://man.freebsd.org/cgi/man.cgi?bhyve][bhyve VMs]] that I networked together using [[https://man.freebsd.org/cgi/man.cgi?netgraph(4)][netgraph]]. On it I host my own [[https://www.powerdns.com/][PowerDNS]] server as the authoratative DNS server for fizz.buzz. It is integrated with [[https://cert-manager.io/][cert-manager]] and [[https://github.com/kubernetes-sigs/external-dns][ExternalDNS]] so Ingresses/LoadBalancers on my cluster automatically get valid TLS certificates and update the DNS records. I have a fully open-source self-hosted gitops workflow where a commit to a git repo I'm hosting in [[https://code.fizz.buzz/][gitea]], triggers a [[https://tekton.dev/][tekton pipeline]] through [[https://github.com/jenkins-x/lighthouse][lighthouse]] to build a docker image with [[https://github.com/GoogleContainerTools/kaniko][kaniko]], which gets pushed to my self-hosted [[https://goharbor.io/][harbor]] instance, which then gets deployed to my cluster via [[https://fluxcd.io/][flux]]. The end result is I make a commit to a repo and the result is deployed to my website in minutes. diff --git a/static/index.html b/static/index.html deleted file mode 100644 index 9f95776..0000000 --- a/static/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - FizzBuzz - - -

FizzBuzz Dev Blog.

-

Coming Eventually!

- -
- -

Please check out my repos at code.fizz.buzz.

- -

- Links -
-

-

- -

Why is your website the way it is?

-

I used to have a developer blog hosted at this domain. I quickly developed an appreciation for the power of org-mode for writing the content of the blog but I grew tired of inconsistent build results. The static site generators at the time would function by calling out to emacs itself to parse the org-mode and export HTML which meant that updates to emacs, my elisp packages, or the static site generator could cause compatibility issues. This often lead to things like escaping issues in old blog posts going unnoticed.

- -

To solve the issue, and to seize the opportunity to gain more experience in Rust, I decided to write my own static site generator that would not depend on outside tools. So far I have written the template engine and I am in the process of writing an org-mode parser. When that is done, it should just be a matter of tying those two together with some minor glue to make a static site generator to create the new version of this site. Until that is done, I am using this hastily thrown-together manually-written html file as a placeholder.

- -

That isn't to say that there are no exciting things hosted on this server, just not at the root domain. For example, this server is running kubernetes that I set up manually following kubernetes-the-hard-way in a bunch of bhyve VMs that I networked together using netgraph. On it I host my own PowerDNS server as the authoratative DNS server for fizz.buzz. It is integrated with cert-manager and ExternalDNS so Ingresses/LoadBalancers on my cluster automatically get valid TLS certificates and update the DNS records. I have a fully open-source self-hosted gitops workflow where a commit to a git repo I'm hosting in gitea, triggers a tekton pipeline through lighthouse to build a docker image with kaniko, which gets pushed to my self-hosted harbor instance, which then gets deployed to my cluster via flux. The end result is I make a commit to a repo and the result is deployed to my website in minutes.

- - - From 19cb4dcea8b5a254e97e4f9099d90df488c12c0f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 19:23:10 -0500 Subject: [PATCH 6/7] Remove the title. --- pages/index.org | 1 - 1 file changed, 1 deletion(-) diff --git a/pages/index.org b/pages/index.org index 395ba44..b35af85 100644 --- a/pages/index.org +++ b/pages/index.org @@ -1,5 +1,4 @@ #+OPTIONS: html-postamble:nil -#+title: FizzBuzz Blog #+date: <2023-12-23 Sat> #+author: Tom Alexander #+email: From 8918dd124b08a9e2469cb9720a3677b89f923dd9 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 19:32:38 -0500 Subject: [PATCH 7/7] Add .gitignore file. --- .dockerignore | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.dockerignore b/.dockerignore index f3b6411..0da60a2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ **/.git +**/.gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea1472e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +output/