initial template
This commit is contained in:
65
.gitlab-ci.yml
Normal file
65
.gitlab-ci.yml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Default image for jobs - contains Hugo Extended
|
||||||
|
image:
|
||||||
|
name: hugomods/hugo:node
|
||||||
|
entrypoint: [""]
|
||||||
|
|
||||||
|
variables:
|
||||||
|
HUGO_ENV: production
|
||||||
|
# Tells GitLab Runner to initialize and update submodules recursively
|
||||||
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
|
# TODO: the Surfer server base url (ex: "https://www.example.com/")
|
||||||
|
SURFER_SERVER: ""
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build # Added build stage
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
# Job to build the Hugo site
|
||||||
|
build_site:
|
||||||
|
stage: build
|
||||||
|
before_script:
|
||||||
|
- echo "installing NPM packages"
|
||||||
|
- npm install
|
||||||
|
script:
|
||||||
|
- echo "Starting Hugo build..."
|
||||||
|
- hugo version
|
||||||
|
- hugo --minify --gc --cleanDestinationDir # Build the site
|
||||||
|
- echo "Build completed. Public directory contents:"
|
||||||
|
- ls -la public/
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public/ # Pass the 'public' directory to the next stage
|
||||||
|
expire_in: 1 hour # Optional: Set artifact expiry
|
||||||
|
# Define when this job runs (e.g., only on the main branch)
|
||||||
|
# Adjust 'only' or 'rules' as needed for your workflow
|
||||||
|
only:
|
||||||
|
- publish # the script will only run on the branch 'publish'
|
||||||
|
|
||||||
|
# Job to deploy the built site using cloudron-surfer
|
||||||
|
deploy_site:
|
||||||
|
stage: deploy
|
||||||
|
image: node:18 # Use Node.js image for cloudron-surfer
|
||||||
|
needs: # Ensure 'build_site' job completes successfully first
|
||||||
|
- job: build_site
|
||||||
|
artifacts: true # Download artifacts from 'build_site'
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: none # Optimization: Don't fetch repo source code for deploy job
|
||||||
|
before_script:
|
||||||
|
- echo "Installing cloudron-surfer..."
|
||||||
|
- npm install -g cloudron-surfer
|
||||||
|
script:
|
||||||
|
- 'echo "Deploying to: $SURFER_SERVER"'
|
||||||
|
# Verify artifact downloaded correctly
|
||||||
|
- echo "Contents of downloaded artifact:"
|
||||||
|
- ls -la public/ # Artifacts are extracted to the root of the job workspace
|
||||||
|
- echo "Uploading files to server using surfer put..."
|
||||||
|
# TODO: Makesure to add a $SURFER_TOKEN to GitLab under the repo settings > CI/CD > Variables
|
||||||
|
- >-
|
||||||
|
surfer put
|
||||||
|
--token $SURFER_TOKEN
|
||||||
|
--server $SURFER_SERVER
|
||||||
|
public/* /
|
||||||
|
- echo "Deployment completed successfully"
|
||||||
|
only:
|
||||||
|
- publish # Example: Run only on the main branch
|
||||||
|
|
80
README.md
Normal file
80
README.md
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# MedLab Hugo Base Theme
|
||||||
|
|
||||||
|
This is the base theme for MedLab Hugo projects. This theme provides a starting point for creating websites using Hugo, a powerful static site generator.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
- [Getting Started](#getting-started)
|
||||||
|
- [Development](#development)
|
||||||
|
- [Content Management](#content-management)
|
||||||
|
- [GitLab CI/CD](#gitlab-cicd)
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
1. Install Hugo: Follow the instructions on the [Hugo website](https://gohugo.io/getting-started/installing/) to install Hugo on your machine.
|
||||||
|
2. Clone the repository: Clone this repository to your local machine using Git.
|
||||||
|
3. Navigate to the project directory: Open a terminal and navigate to the directory where you cloned the repository.
|
||||||
|
4. Start the Hugo server: Run the following command to start the Hugo server:
|
||||||
|
```bash
|
||||||
|
hugo server
|
||||||
|
```
|
||||||
|
5. Open your web browser: Open your web browser and go to `http://localhost:1313` to view the website.
|
||||||
|
|
||||||
|
## Content Management
|
||||||
|
|
||||||
|
1. Add content: Create new Markdown files in the `content` directory to add content to your website. You can create subdirectories to organize your content.
|
||||||
|
2. Front matter: Each Markdown file should have front matter at the top of the file. The front matter is a YAML block that contains metadata about the page, such as the title, date, and layout. Here is an example of front matter:
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: "My Page Title"
|
||||||
|
date: 2023-10-01
|
||||||
|
layout: "single"
|
||||||
|
---
|
||||||
|
```
|
||||||
|
3. Markdown syntax: Use Markdown syntax to format your content. You can use headings, lists, links, images, and other Markdown features to create your content. For more information on Markdown syntax, see the [Markdown documentation](https://www.markdownguide.org/basic-syntax/).
|
||||||
|
|
||||||
|
## GitLab CI/CD
|
||||||
|
|
||||||
|
This project includes automated CI/CD pipelines that handle building and deploying your Hugo site. The pipeline automatically builds your site when changes are pushed and deploys it to a Surfer instance.
|
||||||
|
|
||||||
|
### Pipeline Overview
|
||||||
|
- **Build Stage**: Compiles Hugo site with optimizations
|
||||||
|
- **Deploy Stage**: Deploys to Surfer using cloudron-surfer
|
||||||
|
|
||||||
|
### Setting up the pipeline
|
||||||
|
|
||||||
|
1. **Create GitLab Project**
|
||||||
|
- For external repositories (like git.medlab.host):
|
||||||
|
- Use GitLab's "Run CI/CD for external repo" option
|
||||||
|
- This creates a mirrored GitLab project
|
||||||
|
|
||||||
|
2. **Configure Repository Mirroring** (for external hosting)
|
||||||
|
- Navigate to: `Settings > Repository > Mirror Settings`
|
||||||
|
- Add your GitLab repository URL
|
||||||
|
- Enable push mirroring
|
||||||
|
|
||||||
|
3. **Set Required Variables**
|
||||||
|
- Go to: `Settings > CI/CD > Variables`
|
||||||
|
- Add these variables:
|
||||||
|
- `SURFER_TOKEN`: Your Surfer access token
|
||||||
|
- Mark as "Masked and hidden"
|
||||||
|
- In the `.gitlab-ci.yml` file, set the following variable:
|
||||||
|
- `SURFER_SERVER`: Your Surfer server URL
|
||||||
|
- Example: "https://www.example.com/"
|
||||||
|
- If you don't want to be visible to the public you can set this variable in GitLab CI/CD settings as well.
|
||||||
|
|
||||||
|
4. **Deployment Process**
|
||||||
|
- Push changes to the `publish` branch to trigger deployment
|
||||||
|
- Pipeline will:
|
||||||
|
1. Build Hugo site (with minification)
|
||||||
|
2. Deploy to Surfer
|
||||||
|
- Monitor progress in GitLab's CI/CD Pipeline view
|
||||||
|
|
||||||
|
### Important Notes
|
||||||
|
- Ensure `.gitlab-ci.yml` exists in your repository root
|
||||||
|
- The pipeline uses `hugomods/hugo:node` for Hugo Extended support
|
||||||
|
- Deployments only trigger on the `publish` branch
|
||||||
|
- Build artifacts are retained for 1 hour
|
||||||
|
|
||||||
|
For more details about Surfer deployment, see: https://docs.cloudron.io/apps/surfer/
|
5
archetypes/default.md
Normal file
5
archetypes/default.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
+++
|
||||||
|
date = '{{ .Date }}'
|
||||||
|
draft = true
|
||||||
|
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||||
|
+++
|
2
assets/css/styles.css
Normal file
2
assets/css/styles.css
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/* This is the main css file, you can import other files to keep things tidy, or just go wild and put it all in here! */
|
||||||
|
/* @import "components/thingy.css"; */
|
1
assets/js/script.js
Normal file
1
assets/js/script.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
// add your javascript here, all javscript code in /assets/js/ will be bundled
|
7
hugo.toml
Normal file
7
hugo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
baseURL = 'https://example.org/'
|
||||||
|
languageCode = 'en-us'
|
||||||
|
title = 'My New Medlab Hugo Site'
|
||||||
|
|
||||||
|
[params]
|
||||||
|
description = "The Medlab Hugo theme is a basic starter theme for Hugo."
|
||||||
|
openGraphImage = "/images/og-default.jpg"
|
77
layouts/_default/baseof.html
Normal file
77
layouts/_default/baseof.html
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ .Site.LanguageCode | default "en" }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
|
{{/* Generate consistent title */}}
|
||||||
|
{{ $title := "" }}
|
||||||
|
{{ if if .IsHome }}
|
||||||
|
{{ $title = .Title }}
|
||||||
|
{{ else }}
|
||||||
|
{{ $title = printf "%s - %s" .Title .Site.Title }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Basic SEO -->
|
||||||
|
<title>{{ $title }}</title>
|
||||||
|
|
||||||
|
{{/* Generate description from summary, description, or default site description */}}
|
||||||
|
{{ $description := "" }}
|
||||||
|
{{ with .Description }}
|
||||||
|
{{ $description = . }}
|
||||||
|
{{ else }}
|
||||||
|
{{ $description = .Site.Params.description }}
|
||||||
|
{{ end }}
|
||||||
|
<meta name="description" content="{{ $description }}" />
|
||||||
|
|
||||||
|
{{/* Generate keywords from topics */}}
|
||||||
|
{{ with .Params.topics }}
|
||||||
|
<meta name="keywords" content="{{ delimit . ", " }}" />
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Open Graph / Facebook -->
|
||||||
|
<meta property="og:site_name" content="{{ .Site.Title }}" />
|
||||||
|
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
|
||||||
|
<meta property="og:url" content="{{ .Permalink }}" />
|
||||||
|
<meta property="og:title" content="{{ $title }}" />
|
||||||
|
<meta property="og:description" content="{{ $description }}" />
|
||||||
|
{{ with .Params.ogImage | default .Site.Params.openGraphImage }}
|
||||||
|
<meta property="og:image" content="{{ . | absURL }}" />
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Twitter -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content="{{ $title }}" />
|
||||||
|
<meta name="twitter:description" content="{{ $description }}" />
|
||||||
|
{{ with .Params.ogImage | default .Site.Params.openGraphImage }}
|
||||||
|
<meta name="twitter:image" content="{{ . | absURL }}" />
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- Canonical URL -->
|
||||||
|
<link rel="canonical" href="{{ .Permalink }}" />
|
||||||
|
|
||||||
|
<!-- CSS Styles -->
|
||||||
|
{{ $style := resources.Get "css/styles.css" }}
|
||||||
|
{{ if hugo.IsProduction }}
|
||||||
|
{{ $style = $style | minify | fingerprint }}
|
||||||
|
<link rel="stylesheet" href="{{ $style.RelPermalink }}" integrity="{{ $style.Data.Integrity }}" crossorigin="anonymous">
|
||||||
|
{{ else }}
|
||||||
|
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
|
||||||
|
{{ end }}
|
||||||
|
</head>
|
||||||
|
<body class="">
|
||||||
|
{{ partial "header.html" . }}
|
||||||
|
|
||||||
|
{{ block "main" . }}{{ end }}
|
||||||
|
|
||||||
|
{{ partial "footer.html" . }}
|
||||||
|
|
||||||
|
{{ $js := resources.Match "js/*.js" | resources.Concat "js/bundle.js" }}
|
||||||
|
{{ if hugo.IsProduction }}
|
||||||
|
{{ $js = $js | minify | fingerprint }}
|
||||||
|
<script src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}" crossorigin="anonymous"></script>
|
||||||
|
{{ else }}
|
||||||
|
<script src="{{ $js.RelPermalink }}"></script>
|
||||||
|
{{ end }}
|
||||||
|
</body>
|
||||||
|
</html>
|
12
layouts/_default/section.html
Normal file
12
layouts/_default/section.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
<ul>
|
||||||
|
{{ range .Pages }}
|
||||||
|
<li>
|
||||||
|
<a href="{{ .RelPermalink }}">
|
||||||
|
{{ .Title }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
{{ end }}
|
9
layouts/_default/single.html
Normal file
9
layouts/_default/single.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<article>
|
||||||
|
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
|
||||||
|
{{ .Content }}
|
||||||
|
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
26
layouts/_default/taxonomy.html
Normal file
26
layouts/_default/taxonomy.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<header class="my-8">
|
||||||
|
<p>{{ .Data.Singular }}</p>
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<ul>
|
||||||
|
{{ range .Data.Pages }}
|
||||||
|
<li>
|
||||||
|
<a href="{{ .RelPermalink }}">
|
||||||
|
{{ .Title }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{ if gt (len (index .Site.Taxonomies .Data.Singular)) 1 }}
|
||||||
|
<section>
|
||||||
|
<h2>Other {{ .Data.Plural }}</h2>
|
||||||
|
<div class="tag-cloud">
|
||||||
|
{{ partial "taxonomy-cloud" (dict "taxonomy" .Data.Singular "Site" .Site "page" .Page) }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
</main>
|
||||||
|
{{ end }}
|
6
layouts/partials/footer.html
Normal file
6
layouts/partials/footer.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<footer>
|
||||||
|
<h1 class="text-2xl font-bold">
|
||||||
|
<a href="/">{{ .Site.Title }}</a>
|
||||||
|
</h1>
|
||||||
|
<p class="text-lg">{{ .Site.Params.footer | markdownify }}</p>
|
||||||
|
</footer>
|
25
layouts/partials/header.html
Normal file
25
layouts/partials/header.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!-- basic header partial in hugo with just home and about -->
|
||||||
|
<header class="flex flex-col mb-4">
|
||||||
|
<div class="flex justify-between items-center p-4">
|
||||||
|
<h1 class="text-2xl font-bold">
|
||||||
|
<a href="/">{{ .Site.Title }}</a>
|
||||||
|
</h1>
|
||||||
|
<nav class="font-iosevka">
|
||||||
|
<ul class="flex gap-4">
|
||||||
|
<li>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/about">About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="wompum-container wompum-container--wide-gap px-4 h-1">
|
||||||
|
<div class="wompum-grid"
|
||||||
|
data-text="{{ .Site.Title }}"
|
||||||
|
data-columns="7"
|
||||||
|
data-rows="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
19
layouts/partials/image.html
Normal file
19
layouts/partials/image.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{{ $width := .width }}
|
||||||
|
{{ $height := default $width .height }}
|
||||||
|
{{ $class := .class }}
|
||||||
|
{{ $resource := .resource }}
|
||||||
|
{{ $alt := .alt }}
|
||||||
|
|
||||||
|
{{ with $resource }}
|
||||||
|
{{ $image := .Fit (printf "%dx%d webp" (int $width) (int $height)) }}
|
||||||
|
{{ $fallback := .Fit (printf "%dx%d" (int $width) (int $height)) }}
|
||||||
|
<picture class="{{ $class }} block">
|
||||||
|
<source srcset="{{ $image.RelPermalink }}" type="image/webp">
|
||||||
|
<img
|
||||||
|
src="{{ $fallback.RelPermalink }}"
|
||||||
|
alt="{{ $alt }}"
|
||||||
|
width="{{ $width }}"
|
||||||
|
height="{{ $height }}"
|
||||||
|
loading="lazy">
|
||||||
|
</picture>
|
||||||
|
{{ end }}
|
40
layouts/partials/related-articles.html
Normal file
40
layouts/partials/related-articles.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{{- $topics := .topics -}}
|
||||||
|
{{- $limit := default 3 .limit -}}
|
||||||
|
{{- $currentPath := .page.RelPermalink -}}
|
||||||
|
|
||||||
|
{{- $related := where (where site.RegularPages "Type" "articles") "RelPermalink" "!=" $currentPath -}}
|
||||||
|
{{- $matchingArticles := slice -}}
|
||||||
|
|
||||||
|
{{/* First try to find articles with matching topics */}}
|
||||||
|
{{- range $related -}}
|
||||||
|
{{- $matches := 0 -}}
|
||||||
|
{{- range .Params.topics -}}
|
||||||
|
{{- if in $topics . -}}
|
||||||
|
{{- $matches = add $matches 1 -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- if gt $matches 0 -}}
|
||||||
|
{{- $matchingArticles = $matchingArticles | append (dict "page" . "matches" $matches) -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{/* If we found matching articles, sort by number of matching topics */}}
|
||||||
|
{{- $finalArticles := slice -}}
|
||||||
|
{{- if gt (len $matchingArticles) 0 -}}
|
||||||
|
{{- $finalArticles = first $limit (sort $matchingArticles "matches" "desc") -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{/* Fallback to showing other articles sorted by date */}}
|
||||||
|
{{- $finalArticles = first $limit (sort $related "Date" "desc") -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- if gt (len $finalArticles) 0 -}}
|
||||||
|
<div class="related-articles flex flex-wrap gap-4">
|
||||||
|
<h2 class="title text-3xl font-bold">Related Articles</h2>
|
||||||
|
<div class="wompum-container wompum-container--no-gap">
|
||||||
|
<div class="wompum-grid" data-text="Related Articles" data-columns="8" data-rows="1"></div>
|
||||||
|
</div>
|
||||||
|
<ul class="flex flex-col gap-4 w-full">
|
||||||
|
{{ partial "article-list" (dict "Pages" $finalArticles) }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{- end -}}
|
10
layouts/partials/taxonomy-cloud.html
Normal file
10
layouts/partials/taxonomy-cloud.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{{ $taxonomy := .taxonomy }}
|
||||||
|
{{ range $term, $pages := index .Site.Taxonomies $taxonomy }}
|
||||||
|
{{ $termPage := $.Site.GetPage (printf "/%s/%s" $taxonomy $term) }}
|
||||||
|
<a href="{{ $termPage.RelPermalink }}"
|
||||||
|
data-size="{{ len $pages }}"
|
||||||
|
class="tag"
|
||||||
|
data-count="{{ len $pages}}">
|
||||||
|
{{ $term }}
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
Reference in New Issue
Block a user