Commits: 14

Let a developer publish from unclean source repo

It's helpful when developing or debugging the publish.nu script.

index 2d0e5be..039730a 100755
--- a/publish.nu
+++ b/publish.nu
@@ -2,6 +2,7 @@
=def main [
=  --source: path = "."            # path to the source repository
=  --pages: path = "./pages.git"   # path to the Codeberg Pages repository
+  --allow-unclean                 # do not stutter on unclean repo. Useful when hacking on this script.
=] {
=  # A record of information about source and pages repositories gathered as we go.
=  mut spec = {
@@ -9,7 +10,7 @@ def main [
=    pages_path: ($pages | path expand)
=  }
=
-  if (not (git is-clean $spec.source_path)) {
+  if (not ((git is-clean $spec.source_path) or $allow_unclean)) {
=    error make --unspanned {
=      msg: $"The source repository is not clean.",
=      help: $"First commit, then publish. Here are the changes:\n\n (git status --porcelain)"

Fix publish script failing when public/ is removed

After running zola serve the public directory is removed. This is to prevent stale files from lingering there. From git perspective, it leads to non-existing worktree. The solution is to just re-create this directory.

index 039730a..1fcf2c2 100755
--- a/publish.nu
+++ b/publish.nu
@@ -19,6 +19,9 @@ def main [
=
=  $spec.branch = git current-branch $spec.source_path
=
+  # Make sure the worktree exists. Zola has a strange habit of deleting public/
+  git ensure worktree $spec.pages_path
+
=  git clean-repo $spec.pages_path
=  git switch-or-create $spec.pages_path $spec.branch
=
@@ -70,6 +73,12 @@ def "git current-branch" [repo: path]: nothing -> string {
=    git symbolic-ref --short HEAD
=}
=
+def "git ensure worktree" [repo: path] {
+  cd $repo
+  let worktree_path = git config --get core.worktree
+  mkdir $worktree_path
+}
+
=def "git clean-repo" [repo: path] {
=  cd $repo
=  print $"Cleaning ($repo) worktree..."

Fix publish script wrongly detecting remote tracking

The idiomatic Nushell way to suppress error message is to pipe to ignore command. But it has a side effect of supressing the error itself. So in a try - catch block, it swallows the error I want to detect.

For now I opted to classic Unix way of redirecting to /dev/null. It will probably make the script not compatible with Windows. Oh noes!

index 1fcf2c2..ce53715 100755
--- a/publish.nu
+++ b/publish.nu
@@ -133,7 +133,7 @@ def "git has branch" [repo: path, branch: string] {
=# Check if the current branch is tracking a remote.
=def "git is-tracking" [] {
=  try {
-    git rev-parse --abbrev-ref --symbolic-full-name @{u} err>| ignore
+    git rev-parse --abbrev-ref --symbolic-full-name @{u} err> /dev/null
=    true
=  } catch {
=    false

Implement a dry-run mode for publish.nu

It will show the commit message and diff, but won't actually commit.

index ce53715..e661123 100755
--- a/publish.nu
+++ b/publish.nu
@@ -3,6 +3,7 @@ def main [
=  --source: path = "."            # path to the source repository
=  --pages: path = "./pages.git"   # path to the Codeberg Pages repository
=  --allow-unclean                 # do not stutter on unclean repo. Useful when hacking on this script.
+  --dry-run                       # do everything else, but don't commit or push
=] {
=  # A record of information about source and pages repositories gathered as we go.
=  mut spec = {
@@ -47,7 +48,17 @@ Changes since ($spec.last_deployment_time | date to-timezone utc | format date '
=  $spec | table --expand | print
=  input --numchar 1 "Press any key to continue, or ctrl-c to abort."
=
-  $commit_template | git publish $spec.pages_path $spec.branch
+  if ($dry_run) {
+    print "---"
+    print $commit_template
+    print "---"
+    print "☝ this would be the commit message. You would have a chance to edit it."
+    input --numchar 1 "I will show you a diff now. Press any key when ready."
+    cd $spec.pages_path
+    git diff
+  } else {
+    $commit_template | git publish $spec.pages_path $spec.branch
+  }
=}
=
=# Takes commit message as input

Handle /@branch-preview/ prefix in links

The build script will pass the prefix as base_url to zola. All linked assets and hyperlinks should now use this prefix.

index e661123..cc7ecf9 100755
--- a/publish.nu
+++ b/publish.nu
@@ -42,7 +42,11 @@ Changes since ($spec.last_deployment_time | date to-timezone utc | format date '
=($spec.changelog | format changelog)
=  "
=
-  zola build
+  let base_url = match $spec.branch {
+    "main" => "/",
+    _ => $"/@($spec.branch)/"
+  }
+  zola build --base-url $base_url
=
=  print "About to commit and publish. Here is the summary: "
=  $spec | table --expand | print
index 12a7973..fa7c605 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -32,12 +32,12 @@
=    <meta name="description" content="{\{ description \}}" />
=    <meta name="viewport" content="width=device-width, initial-scale=1" />
=
-    <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
-    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
-    <link rel="shortcut icon" href="/favicon.ico" />
-    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
+    <link rel="icon" type="image/png" href="{\{ get_url(path='/favicon-96x96.png') \}}" sizes="96x96" />
+    <link rel="icon" type="image/svg+xml" href="{\{ get_url(path='/favicon.svg') \}}" />
+    <link rel="shortcut icon" href="{\{ get_url(path='/favicon.ico') \}}" />
+    <link rel="apple-touch-icon" sizes="180x180" href="{\{ get_url(path='/apple-touch-icon.png') \}}" />
=    <meta name="apple-mobile-web-app-title" content="{\{ config.title \}}" />
-    <link rel="manifest" href="/site.webmanifest" />
+    <link rel="manifest" href="{\{ get_url(path='/site.webmanifest') \}}" />
=
=    <meta property="og:title" content="{\{ title \}}">
=    <meta property="og:type" content="website" />

Separate services to own section, with emojis 😎

index 435d9d3..a834d7f 100644
--- a/content/_index.md
+++ b/content/_index.md
@@ -10,60 +10,14 @@ Based entirely in Europe, [our team](@/people/_index.md) is on-shore, allowing u
=
=# Our Services
=
-Our primary focus is on helping European Small and Medium-sized Enterprises (SMEs), Non-Governmental Organizations (NGOs), local governments, and educational institutions with implementing free, open-source and European software solutions.
+Our primary focus is on helping European Small and Medium-sized Enterprises (SMEs), Non-Governmental Organizations (NGOs), local governments, and educational institutions with implementing free, open-source and European software solutions. We provide
=
+🌤️ Cloud assistance
=
-## Cloud assistance
+💻 Software Development
=
-Your organisation probably depends on multiple cloud services. Setting up, managing and migrating cloud services can be complicated. Or maybe your company is or wants to self-host some applications? Our team is happy to assist you on projects or provide operational support for your cloud and self-hosted services.
+💬 Consulting
=
-Our technical capabilities include:
+📖 Training and coaching
=
-  * Cloud infrastructure: Kubernetes, Docker, OpenShift, and pretty much any Cloud provider
-  * Infrastructure automation: Terraform, Ansible, GitOps with ArgoCD, GitHub Actions, Jenkins
-  * Service architecture: service mesh, service discovery, containerization, server-less
-  * System reliability:  monitoring (Prometheus, Grafana, Nagios), observability, high availability
-  * Linux, Unix wizardry
-  * Scripting
-
-Reach out to us for any project or operational support in
-
-  * Setting up cloud or self-hosted services, applications
-  * Migrating your cloud or self-hosted services
-  * Administering your cloud or self-hosted services
-  * Trusted Admin Failover as a Service (TAFaaS), if you would like to have a backup plan when your trusted admin is absent unexpectedly
-
-
-## Software Development
-
-Need any customization or extension of FOSS functionalities? Need hard coding skills to set up a (web)application? Want to automate some tasks? Our team has a broad technical skillset, including (but not limited to):
-
-  * HTML, CSS, JavaScript
-  * Python
-  * Rust
-  * Elm
-  * Haskell
-  * Groovy
-  * SQL
-  * Bash
-  * Go
-
-
-## Consulting
-
-We can assist with small and big projects, provide momentary advice or define long term strategies for your organisation. Some examples:
-
-  * Project management
-  * Define migration strategies from concept to technical detail, for example moving to a new collaborative software platform (email, calendar, file sharing, etc.)
-  * Define technical solutions to meet your data security and data privacy requirements
-
-
-## Training and coaching
-
-We can provide training and coaching on different topics. The following list is not exhaustive, don't hesitate to reach out for other needs in the FOSS domain:
-
-  * Web Development
-  * Agile development, Test driven development, Pair prohramming
-  * Relational Databases (RDBMS)
-  * Version management using Git
-  * Operating Systems (Linux, Nix / NixOS)
+[Read more about our services](@/services/_index.md)
new file mode 100644
index 0000000..190f667
--- /dev/null
+++ b/content/services/_index.md
@@ -0,0 +1,63 @@
+---
+title: Services
+---
+
+# Our Services
+
+Our primary focus is on helping European Small and Medium-sized Enterprises (SMEs), Non-Governmental Organizations (NGOs), local governments, and educational institutions with implementing free, open-source and European software solutions.
+
+
+## 🌤 ️Cloud assistance
+
+Your organisation probably depends on multiple cloud services. Setting up, managing and migrating cloud services can be complicated. Or maybe your company is or wants to self-host some applications? Our team is happy to assist you on projects or provide operational support for your cloud and self-hosted services.
+
+Our technical capabilities include:
+
+  * Cloud infrastructure: Kubernetes, Docker, OpenShift, and pretty much any Cloud provider
+  * Infrastructure automation: Terraform, Ansible, GitOps with ArgoCD, GitHub Actions, Jenkins
+  * Service architecture: service mesh, service discovery, containerization, server-less
+  * System reliability:  monitoring (Prometheus, Grafana, Nagios), observability, high availability
+  * Linux, Unix wizardry
+  * Scripting
+
+Reach out to us for any project or operational support in
+
+  * Setting up cloud or self-hosted services, applications
+  * Migrating your cloud or self-hosted services
+  * Administering your cloud or self-hosted services
+  * Trusted Admin Failover as a Service (TAFaaS), if you would like to have a backup plan when your trusted admin is absent unexpectedly
+
+
+## 💻 Software Development
+
+Need any customization or extension of FOSS functionalities? Need hard coding skills to set up a (web)application? Want to automate some tasks? Our team has a broad technical skillset, including (but not limited to):
+
+  * HTML, CSS, JavaScript
+  * Python
+  * Rust
+  * Elm
+  * Haskell
+  * Groovy
+  * SQL
+  * Bash
+  * Go
+
+
+## 💬 Consulting
+
+We can assist with small and big projects, provide momentary advice or define long term strategies for your organisation. Some examples:
+
+  * Project management
+  * Define migration strategies from concept to technical detail, for example moving to a new collaborative software platform (email, calendar, file sharing, etc.)
+  * Define technical solutions to meet your data security and data privacy requirements
+
+
+## 📖 Training and coaching
+
+We can provide training and coaching on different topics. The following list is not exhaustive, don't hesitate to reach out for other needs in the FOSS domain:
+
+  * Web Development
+  * Agile development, Test driven development, Pair prohramming
+  * Relational Databases (RDBMS)
+  * Version management using Git
+  * Operating Systems (Linux, Nix / NixOS)
new file mode 100644
index 0000000..61d3aed
--- /dev/null
+++ b/templates/section.html
@@ -0,0 +1,7 @@
+{# A generic section template #}
+
+{\% extends "base.html" %}
+
+{\%- block main_content -%}
+{\{- section.content | safe -\}}
+{\%- endblock main_content -%}

Fix publish script to handle comma in a commit message

index cc7ecf9..a2da6c8 100755
--- a/publish.nu
+++ b/publish.nu
@@ -159,8 +159,8 @@ def "git is-tracking" [] {
=
=def "git changelog" [repo: path, since: datetime]: nothing -> table {
=  cd $repo
-  git log --pretty=reference --date=iso-strict
-  | parse "{hash} ({title}, {date})"
+  git log --pretty="format:%h\t%s\t%cd" --date=iso-strict
+  | parse "{hash}\t{title}\t{date}"
=  | update date { |row| $row.date | into datetime }
=  | where date > $since
=}

Increase vertical space above the footer

It was unpleasantly narrow.

index fa7c605..3e9f69c 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -96,6 +96,7 @@
=      }
=
=      body > footer {
+        margin-top: 3rem;
=        display: flex;
=        background: var(--dark-color);
=        align-items: center;
@@ -129,7 +130,6 @@
=
=      #logo {
=        height: 3em;
-        width: 3em;
=      }
=
=      p {
@@ -177,7 +177,9 @@
=      /* UTILITY CLASSES */
=
=      .full-bleed-background {
-        margin: 0 calc(-1 * var(--full-bleed-margin));
+        --negative-margin: calc(-1 * var(--full-bleed-margin));
+        margin-left: var(--negative-margin);
+        margin-right: var(--negative-margin);
=        padding: 3rem calc(1rem + var(--full-bleed-margin));
=      }
=

Move the tera context element to the top

Below the footer it was creating an uncanny bright stripe.

index 3e9f69c..2115811 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -291,6 +291,13 @@
=      </p>
=    <![endif]-->
=
+    {\%- if config.mode == "serve" %}
+    <details style="overflow: scroll">
+      <summary>Rendering context</summary>
+      <pre>{\{ __tera_context | escape | safe \}}</pre>
+    </details>
+    {\% endif -%}
+
=    <header>
=      <a href="{\{ get_url(path='@/_index.md') \}}">
=        <img
@@ -347,13 +354,5 @@
=      </ul>
=    </footer>
=
-    {\% if config.mode == "serve" %}
-    <details style="overflow: scroll">
-      <summary>Rendering context</summary>
-      <pre>
-{\{ __tera_context | escape | safe \}}
-      </pre>
-    </details>
-    {\% endif %}
=  </body>
=</html>

Remove obsolete --prompt-font-size variable

It was used in a very confusing way to set the height of main logo. Must have been a relic of some early experiments with the funny prompt.

Now the logo is simply sized in rem units. Visually there should be no change.

index 2115811..27db92b 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -54,7 +54,6 @@
=        --prompts-count: 19;
=        --max-prompt-length: 63;
=        --prompt-height: 1.5em;
-        --prompt-font-size: 2rem;
=        --container-width: 960px;
=        --dark-color: hsl(0, 0%, 10%);
=        --light-color: hsl(0, 0%, 90%);
@@ -76,7 +75,6 @@
=      }
=
=      body > header {
-        font-size: var(--prompt-font-size);
=        color: var(--dark-color);
=        padding: 3rem 1rem;
=        overflow: hidden;
@@ -129,7 +127,7 @@
=      }
=
=      #logo {
-        height: 3em;
+        height: 6rem;
=      }
=
=      p {
@@ -268,7 +266,7 @@
=
=      @media (max-width: 36rem) {
=        #logo {
-          height: 2em;
+          height: 4rem;
=        }
=
=        .title > h1 {

Create an extremely simple main nav element

index 27db92b..5d017ce 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -101,6 +101,13 @@
=        color: var(--light-color);
=      }
=
+      nav > ul {
+        display: flex;
+        justify-content: end;
+        flex-wrap: wrap;
+        gap: 2rem;
+      }
+
=      hgroup h1 {
=        margin: 0;
=        font-weight: 400;
@@ -297,6 +304,13 @@
=    {\% endif -%}
=
=    <header>
+      <nav>
+        <ul>
+          <li><a href="{\{get_url(path='@/_index.md')\}}">Home</a></li>
+          <li><a href="{\{get_url(path='@/people/_index.md')\}}">People</a></li>
+          <li><a href="{\{get_url(path='@/services/_index.md')\}}">Services</a></li>
+        </ul>
+      </nav>
=      <a href="{\{ get_url(path='@/_index.md') \}}">
=        <img
=          id="logo"

Make bullets in main nav look like LEDs

To add a bit of playfulness. Also make the labels stronger.

Under the hood, navigation element is now controlled by site configuration (extra section in config.toml file).

index badacc2..1de2490 100644
--- a/config.toml
+++ b/config.toml
@@ -20,3 +20,15 @@ highlight_code = true
=
=[extra]
=# Put all your custom variables here
+[[extra.navigation]]
+label = "Home"
+class = "green-accent"
+path = "_index.md"
+[[extra.navigation]]
+label = "People"
+class = "red-accent"
+path = "people/_index.md"
+[[extra.navigation]]
+label = "Services"
+class = "blue-accent"
+path = "services/_index.md"
index 5d017ce..15e8068 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -55,14 +55,22 @@
=        --max-prompt-length: 63;
=        --prompt-height: 1.5em;
=        --container-width: 960px;
-        --dark-color: hsl(0, 0%, 10%);
-        --light-color: hsl(0, 0%, 90%);
=        --full-bleed-margin: max(
=          0px,
=          calc(0.5 * (100vw - var(--container-width)))
=        );
+
+
+        /* Colors */
+        --dark-color: hsl(0, 0%, 10%);
+        --light-color: hsl(0, 0%, 90%);
+
+        --red: oklch(36% 80% 29deg);
+        --green: oklch(from var(--red) L C 142deg);
+        --blue: oklch(from var(--red) L C 264deg);
=      }
=
+
=      body {
=        font-family: sans-serif;
=        padding: 0;
@@ -101,11 +109,52 @@
=        color: var(--light-color);
=      }
=
+
+      /* playful colors */
+      .red-accent {
+        --accent-color: var(--red)
+      }
+      .green-accent {
+        --accent-color: var(--green)
+      }
+      .blue-accent {
+        --accent-color: var(--blue)
+      }
+
=      nav > ul {
=        display: flex;
=        justify-content: end;
=        flex-wrap: wrap;
=        gap: 2rem;
+        list-style: none;
+
+        li {
+          --inactive-color: oklch(from var(--accent-color) L 30% H);
+          --active-color: oklch(from var(--accent-color) 63% C H);
+
+          &::before {
+            content: "•";
+
+            color: var(--inactive-color);
+            transition: color 1s, text-shadow 1s;
+          }
+
+          &.active::before {
+            color: var(--active-color);
+            text-shadow: 0 0 4px var(--active-color), 0 0 4px var(--active-color);
+          }
+
+          &:hover::before {
+            color: var(--active-color);
+            text-shadow: 0 0 4px var(--active-color), 0 0 4px var(--active-color);
+          }
+
+          a {
+            font-weight: bold;
+            color: var(--dark-color);
+          }
+        }
+
=      }
=
=      hgroup h1 {
@@ -303,12 +352,28 @@
=    </details>
=    {\% endif -%}
=
+
=    <header>
=      <nav>
=        <ul>
-          <li><a href="{\{get_url(path='@/_index.md')\}}">Home</a></li>
-          <li><a href="{\{get_url(path='@/people/_index.md')\}}">People</a></li>
-          <li><a href="{\{get_url(path='@/services/_index.md')\}}">Services</a></li>
+          {# Code below assumes that every item points to a section.
+
+             If that's no longer the case, the code needs to get smarter.
+             Probably something to do with pattern matching on item.path.
+
+               1. If it ends with _index.md it's a section,
+               2. else if it it ends with .md it's a page,
+               3. else if it starts with / it's a local asset (an image, pdf, etc.),
+               4. else it's an external URL.
+          #}
+          {\%- for item in config.extra.navigation %}
+          {\%- set item_section = get_section(path=item.path) -%}
+          {\%- set is_active = item_section.path == current_path %}
+          <li class="{\{ item.class \}} {\% if is_active %}active{\% endif %}">
+            <a href="{\{ item_section.path \}}">{\{ item.label \}}</a>
+          </li>
+          {\% endfor -%}
+
=        </ul>
=      </nav>
=      <a href="{\{ get_url(path='@/_index.md') \}}">

Make the people accent yellow

Red might look like something is wrong.

I used a yellow color from our logo. Other accents are based on this color too, just with heu rotated in OKLCH space.

index 1de2490..fe8fec3 100644
--- a/config.toml
+++ b/config.toml
@@ -26,7 +26,7 @@ class = "green-accent"
=path = "_index.md"
=[[extra.navigation]]
=label = "People"
-class = "red-accent"
+class = "yellow-accent"
=path = "people/_index.md"
=[[extra.navigation]]
=label = "Services"
index 15e8068..1901c72 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -65,9 +65,10 @@
=        --dark-color: hsl(0, 0%, 10%);
=        --light-color: hsl(0, 0%, 90%);
=
-        --red: oklch(36% 80% 29deg);
-        --green: oklch(from var(--red) L C 142deg);
-        --blue: oklch(from var(--red) L C 264deg);
+        --yellow: oklch(0.8498 0.173865 86.5517); /* same as our logo */
+        --red: oklch(from var(--yellow) L C 29deg);
+        --green: oklch(from var(--yellow) L C 142deg);
+        --blue: oklch(from var(--yellow) L C 264deg);
=      }
=
=
@@ -111,6 +112,9 @@
=
=
=      /* playful colors */
+      .yellow-accent {
+        --accent-color: var(--yellow)
+      }
=      .red-accent {
=        --accent-color: var(--red)
=      }
@@ -129,8 +133,8 @@
=        list-style: none;
=
=        li {
-          --inactive-color: oklch(from var(--accent-color) L 30% H);
-          --active-color: oklch(from var(--accent-color) 63% C H);
+          --inactive-color: oklch(from var(--accent-color) 60% C H);
+          --active-color: oklch(from var(--accent-color) 83% C H);
=
=          &::before {
=            content: "•";

Add more space between nav bullets and labels

They were too close and it looked off.

index 1901c72..6987649 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -140,6 +140,7 @@
=            content: "•";
=
=            color: var(--inactive-color);
+            margin-right: 0.5ch;
=            transition: color 1s, text-shadow 1s;
=          }
=