Compare commits

..

No commits in common. "main" and "gatsby-archive" have entirely different histories.

59 changed files with 12697 additions and 520 deletions

6
.dockerignore Normal file
View File

@ -0,0 +1,6 @@
node_modules/
npm-debug.log
yarn-error.log
public/
.git/
.cache/

34
.editorconfig Normal file
View File

@ -0,0 +1,34 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8
# 4 space indentation
[*.py]
indent_style = space
indent_size = 4
# Tab indentation (no size specified)
[Makefile]
indent_style = tab
# Indentation override for all JS under lib directory
[lib/**.js]
indent_style = space
indent_size = 2
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

23
.eslintrc.json Normal file
View File

@ -0,0 +1,23 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"standard",
"plugin:react/recommended"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "babel-eslint",
"plugins": [
"babel",
"react"
],
"rules": {
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }]
}
}

View File

@ -1,31 +0,0 @@
name: Build website container
on:
push:
branches:
- main
jobs:
build:
name: Build image
runs-on: ubuntu-latest
container: git.namesny.com/cluster/act-runner:v1
env:
IMAGE_NAME: namesny-com
REGISTRY: git.namesny.com
REPO_OWNER: mathis
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: git.namesny.com
username: ${{ gitea.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push
run: |
TODAY=$(date +'%Y-%m-%d')
docker build -t ${REGISTRY}/${REPO_OWNER}/${IMAGE_NAME}:${TODAY} -t ${REGISTRY}/${REPO_OWNER}/${IMAGE_NAME}:latest .
docker push ${REGISTRY}/${REPO_OWNER}/${IMAGE_NAME}:${TODAY}
docker push ${REGISTRY}/${REPO_OWNER}/${IMAGE_NAME}:latest

67
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '16 19 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

81
.gitignore vendored
View File

@ -1,13 +1,72 @@
# Generated files by hugo # Logs
/public/ logs
/resources/_gen/ *.log
/assets/jsconfig.json npm-debug.log*
hugo_stats.json yarn-debug.log*
yarn-error.log*
# Executable may be added to repository # Runtime data
hugo.exe pids
hugo.darwin *.pid
hugo.linux *.seed
*.pid.lock
# Temporary lock file while building # Directory for instrumented libs generated by jscoverage/JSCover
/.hugo_build.lock lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# dotenv environment variable files
.env*
# gatsby files
.cache/
public
# Mac files
.DS_Store
# Yarn
yarn-error.log
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity
# Custom
/content

4
.prettierignore Normal file
View File

@ -0,0 +1,4 @@
.cache
package.json
package-lock.json
public

4
.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"arrowParens": "avoid",
"semi": false
}

View File

@ -1,27 +0,0 @@
FROM golang:1.21-bookworm as build
ARG DART_SASS_VERSION=1.70.0
ARG HUGO_VERSION=0.122.0
WORKDIR /app
# Install Dart Sass
RUN curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz && \
tar -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz && \
cp -r dart-sass/* /usr/local/bin && \
rm -rf dart-sass*
# Install Hugo
RUN curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb && \
apt install -y ./hugo_extended_${HUGO_VERSION}_linux-amd64.deb && \
rm hugo_extended_${HUGO_VERSION}_linux-amd64.deb
COPY . /app
RUN hugo mod get -u && \
hugo --gc --minify
FROM nginx:stable-alpine
COPY --from=build /app/public /usr/share/nginx/html/
COPY ./nginx.conf /etc/nginx/conf.d/default.conf

14
LICENSE Normal file
View File

@ -0,0 +1,14 @@
The BSD Zero Clause License (0BSD)
Copyright (c) 2020 Gatsby Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

8
README.md Normal file
View File

@ -0,0 +1,8 @@
# Personal Website
This is a source code for my personal website. It's implemented using Gatsby.js
## TODO
* Light theme
* Remove Google Fonts CDN

View File

@ -1,6 +0,0 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

View File

@ -1,50 +0,0 @@
baseURL = 'https://namesny.com/'
languageCode = 'en-us'
[module]
[[module.imports]]
path = "github.com/LordMathis/hugo-theme-nightfall"
[menu]
[[menu.header]]
name = "blog"
weight = 0
url = "blog"
[[menu.header]]
name = "about"
weight = 1
url = "about"
[params]
user = "hello"
hostname = "namesny.com"
[params.author]
name = "Matúš Námešný"
[[params.social]]
key = 0
name = "github"
url = "https://github.com/LordMathis"
[[params.social]]
key = 1
name = "gitea"
url = "https://git.namesny.com/Mathis"
[[params.social]]
key = 2
name = "linkedin"
url = "https://www.linkedin.com/in/matus-namesny/"
[[params.social]]
key = 3
name = "mastodon"
url = "https://toot.io/@mathis"
rel = "me"
[[params.social]]
key = 4
name = "email"
url = "mailto:matus@namesny.com"

View File

@ -1,24 +0,0 @@
---
title: 'About Me'
showMetadata: false
---
Hello there.
I am Matúš [ˈmatuːʃ]. I am a Machine Learning Engineer based in Darmstadt, Germany. I focus on MLOps bridging the gap between Data Science, DevOps and Software Engineering. I am interested in broad range of topics including Machine Learning, Cloud, DevOps, Linux and open source.
After graduating in Artificial Intelligence and Natural Language Processing I joined Konica Minolta in Brno, Czechia, where I worked as an R&D Engineer developing a Python application that provides a unified approach for production ML model deployment. I was also deploying ML applications and MLOps tools to the cloud with Docker and Kubernetes which sparked my interest in DevOps and Cloud.
Currently, I work at Telespazio Germany as a Software Engineer. I participated in the development of a Kubeflow based Machine Learning Platform for Spacecraft Operational datasets. Nowadays I am working on a question-answering Chatbot the helps users explain complex documentation.
Here are some interesting projects that I'm working on in my free time:
* [k3s-configs](https://git.namesny.com/Cluster/k3s-configs) - Kustomize manifests for my self-hosted cluster.
* [neural-nets](https://github.com/LordMathis/neural-nets) - Small neural network library written in C from scratch.
* [GitEcho](https://github.com/LordMathis/GitEcho) - Backup tool for git repositories.
* [CUDANet](https://github.com/LordMathis/CUDANet) - Convolutional neural network library with CUDA support.
I also maintain two themes for [Hugo](https://gohugo.io), a static site generator:
* [Nix](https://github.com/LordMathis/hugo-theme-nix)
* [Nightfall](https://github.com/LordMathis/hugo-theme-nightfall)

View File

@ -1,3 +0,0 @@
---
title: "Blog"
---

View File

@ -1,47 +0,0 @@
---
title: "Back to Hugo"
date: "2023-08-13"
tags:
- gatsby
- hugo
---
Over the years, this website was running on WordPress, Hugo, a custom React SPA, server-side rendered React, Gatsby, and now I'm back to Hugo.
<!--more-->
## First Hugo Website
I created my first Hugo website in 2016 while still in University studying Computer Science. I started looking for internships and thought that having a personal website would enhance my appeal. I created the [Nix theme](https://github.com/LordMathis/hugo-theme-nix), a minimalistic theme inspired by Unix and the terminal. Looking back at the code, it's actually not that bad considering I had zero experience with web development. But I think it speaks more about the simplicity of Hugo and Bootstrap rather than my skills.
If I were to rewrite this theme from scratch, I would implement many things differently. However, many people use the theme, so I continue to maintain it.
## Here Comes React
Hugo was working perfectly fine for my needs, but for some reason, I wanted something more. Since I was already learning React and Node.js, I decided to build my website using React. Initially, I started building the website as a standard React app. I created all the components that I thought I might need, and the website quickly grew.
Then I learned about universal (isomorphic) rendering and decided to give it a try. This part of development took me the longest. My challenge with implementing universal rendering was a distinct lack of up-to-date tutorials. They all used outdated versions of Webpack or React Router, or didn't utilize React Router at all. Another issue was that some tutorials were using babel-node in production, which is not recommended.
After finally implementing universal rendering, the next issue to solve was how to deliver content. A simpler approach would be to create a new React component for each blog post and "hard code" the content. However, that's not very React-like. Instead, I implemented a simple API in Express.
There are many more aspects that I didn't mention, things I spent hours working on, only to change my mind after implementation. An honorable mention goes to the CSS and the overall style of the website, which underwent several changes. I also reinvented the wheel multiple times, opting to build features from scratch rather than using existing modules.
So now, I had a new website that was much more complex but had fewer features than my initial Hugo website.
## The Great Gatsby
My React-based website wasn't fully server-side rendered. The only server-rendered element was the page layout, while the content was served via an API and rendered on the client side. Thus, I found myself running a Node.js app at all times for a relatively simple website with minimal visitors and content. Instead of embarking on creating my own server-side renderer, I got sidetracked by a captivating newcomer: Gatsby.
Migrating to Gatsby wasn't overly challenging since I already had all my styles and components ready. I just needed to grasp how to write GraphQL queries to fetch content and assemble a set of Gatsby plugins for website building. The initial implementation of the Gatsby website was enjoyable, but ongoing maintenance proved to be a hassle.
## Move Fast and Break Things
The JavaScript landscape evolves at breakneck speed. Libraries that emerged yesterday risk deprecation tomorrow. Not to mention the ceaseless discovery of vulnerabilities every day. To mitigate this, I established dependabot security alerts for my website's GitHub repository. At one point, dependabot became the primary contributor.
While the detected vulnerabilities didn't directly threaten my server, as the website generated static HTML files and was served by nginx, a chain of vulnerabilities encompassing Gatsby, nginx, and Docker would be necessary for a genuine threat. Nevertheless, uncertainty lingered.
In the end, I opted to return to Hugo. You can still explore the archived React/Gatsby source code [here](https://git.namesny.com/Mathis/namesny-com-gatsby-archive).
## Full Circle
Instead of revisiting my old Nix theme, I chose to create a new theme: [Nightfall](https://github.com/LordMathis/hugo-theme-nightfall), based on the layout of my Gatsby website. Migrating to Hugo was relatively straightforward, thanks to React components. I only needed to replace JSX tags with appropriate HTML tags and integrate Hugo partials. I welcome all contributions to both the Nix and Nightfall themes.

View File

@ -1,24 +0,0 @@
---
title: "Coding style"
draft: true
---
I had a few interviews where I was asked about my coding style. I wasn't sure how to respond. I'm not following dogmatically any one coding principle such as Clean Code or TDD. Coding standards are hard, thats why there are so many books about it. I pick and choose the ideas and try to follow those in my code.
I would be surprised if I follow the same ideas in 5 years. The style is evolving and my opinions change.
Here are ideas that I currently follow:
- never nesting
- early return
- fight abstractions
- grugg brained dev
- don't repeat yourself thrice
Sources:
- https://www.youtube.com/watch?v=tD5NrevFtbU
- https://www.youtube.com/watch?v=CFRhGnuXG-4
- https://grugbrain.dev/
- https://testing.googleblog.com/2023/09/else-nuances.html
- https://www.youtube.com/watch?v=bJQj1uKtnus

View File

@ -1,29 +0,0 @@
---
title: Writing a Convolutional Neural Network library with CUDA Support
draft: true
---
Straightforward project, learned a lot more than I expected.
"Just use cuBLAS, it'll be easier. You don't have to implement custom CUDA kernels.", they said. Actually, noone said that. I just thought that because I didn't do enough research.
Why not combine multiple challenging things into 1 (C++, cmake, CUDA, CNN)
Quickly discovering that without writing custom kernels, you can't really progress
- cuBLAS column major layout, macro
- cmake woes (findCUDA)
- google test
- padding kernel
- column major / row major headache
- removing cuBLAS -> just row major representation
- naive conv2d
- learning 3D memory representation
- optimizing conv2d
- softmax sum reduce
- softmax numerical stability - max reduce
- custom binary weights file - (safetensors - json parser vs csv) values overwritten by header
- tests passing -> implement AlexNet
- AlexNet cmake, opencv
- AlexNet crashing -> add cuda error checking to tests -> test crashing
- compute-sanitizer memecheck

View File

@ -1,88 +0,0 @@
---
title: "Building a Docker Container with Gitea Actions on K3s"
date: "2023-12-28"
tags:
- gitea
- k3s
- cicd
---
Building a Docker image and pushing it to the registry with GitHub Actions is incredibily easy. Since Gitea Actions are designed to be compatible with GitHub Actions, this should be easy, right?
<!--more-->
## Gitea Actions
Gitea Actions is a CI/CD solution tightly coupled with Gitea. They have been available since Gitea 1.19 and are designed to be mostly compatible with GitHub Actions. They are based on the [act](https://github.com/nektos/act), which allows you to run GitHub workflows locally. Gitea has soft forked it to create [act_runner](https://gitea.com/gitea/act_runner).
To use Gitea Actions on you instance, you need to first allow them in `app.ini`. Then create a token and deploy the runner. Once the runner is deployed and registered, you will also need to enable Actions for each repository separately. The Actions runner is a self-contained system - a docker container that, for each job, launches a new container inside which the action steps are run. For a full guide on setting up Actions, check the official [Gitea docs](https://docs.gitea.com/usage/actions/quickstart).
## Building and Pushing Docker Image with GitHub Actions
With GitHub Actions, you can make use of thousands of actions available in [GitHub Marketplace](https://github.com/marketplace?type=actions). If you want to build a Docker image on GitHub, you can just use the official Docker [build-and-push](https://github.com/marketplace/actions/build-and-push-docker-images) action. Just copy one of the examples and you are good to go.
## Building with Gitea
In order to enable Gitea Actions, you nedd to first deploy the Actions runner. I followed the Kubernetes example from [gitea/act_runner](https://gitea.com/gitea/act_runner/src/commit/f17cad1bbe0d4a84308a37fb4a5e64211ada7e8a/examples/kubernetes/rootless-docker.yaml) repository. The deployment is simple. The runner will register itself with your Gitea instance, and after you enable Actions globally and for each repository, you'll be able to try Actions.
The first thing I tried was the same workflow that I used on Github. That didn't work. The first step to fail was docker login action. It complained that it couldn't find the `docker` command. It turns out that the default container image in which the Actions runner runs the commands did not contain docker. I tried manually installing it, but a simpler solution was to just specify a different container by [catthehacker](https://github.com/catthehacker/docker_images), which already has docker preinstalled. After switch to the new container, the logging in worked fine.
The next problem was with setting up the docker buildx action. It couldn't connect to Docker daemon at `unix:///var/run/docker.sock`. After much debugging, trying different things, and searching the internet, I found out that because the docker-in-docker runner container is rootless, the Docker socket is at `unix:///var/run/user/1000/docker.sock` instead. I just needed to change the `DOCKER_HOST` environment variables. I also removed `DOCKER_TLS_VERIFY` and `DOCKER_CERT_PATH` environment variables since they weren't necessary.
Okay, so now, everything should work fine, right? Not so fast. Apparently the Docker buildx action makea some assumtions about the system, which work well in a well-defined environment of GitHub Actions but don't necessarily hold true for self-hosted K3s deployments. It complained that it couldn't mount `sysfs` to `rootfs` at `/sys` due to operation not permitted. The solution was to run docker commands directly instead of using `buildx` action.
The last hurdle was to pass the login secrets to the action. Gitea does not yet support an equivalent to `GITHUB_TOKEN`, so instead, I needed to manually create a token and add it to action secrets as `REGISTRY_TOKEN`.
This is a very condensed summary of many hours spent debugging, searching, and trying to make Gitea Actions build a Docker image on K3s. I've skiped a few different attempts that lead to nowhere, such as using RedHat's Buildah instead of Docker. In the end the actual solution was much simpler than any of my attempts.
## TL;DR
To build a Docker image using Gitea Actions on K3s deploy the dind-rootless Actions runner with these environment variables:
{{< highlight yaml >}}
env:
- name: DOCKER_HOST
value: unix:///var/run/user/1000/docker.sock
- name: GITEA_INSTANCE_URL
value: http://gitea-http.gitea.svc.cluster.local:3000
- name: GITEA_RUNNER_REGISTRATION_TOKEN
valueFrom:
secretKeyRef:
name: runner-secret
key: token
{{< / highlight >}}
Create `release.yaml` file in `.gitea/workflows` folder. For building and pushing the image, use `docker` commands directly. For example:
{{< highlight yaml >}}
name: Build docker container
on:
push:
branches:
- main
jobs:
build:
name: Build image
runs-on: ubuntu-latest
container: ghcr.io/catthehacker/ubuntu:act-latest
env:
IMAGE_NAME: example-image
REGISTRY: example.com
REPO_OWNER: test
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: example.com
username: ${{ gitea.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push
run: |
TODAY=$(date +'%Y-%m-%d')
docker build -t ${REGISTRY}/${REPO_OWNER}/${IMAGE_NAME}:${TODAY} -t ${REGISTRY}/${REPO_OWNER}/${IMAGE_NAME}:latest .
docker push ${REGISTRY}/${REPO_OWNER}/${IMAGE_NAME}:${TODAY}
docker push ${REGISTRY}/${REPO_OWNER}/${IMAGE_NAME}:latest
{{< / highlight >}}

View File

@ -1,84 +0,0 @@
---
title: "Replicating Gitea Docker SSH Passthrough on K3s"
date: "2023-02-12"
tags:
- gitea
- k3s
---
If you are selfhosting Gitea on a single node Kubernetes cluster and want to enable git through SSH while keeping SSH connection to the cluster, this guide is for you.
<!--more-->
**Update**
As of Gitea Helm Chart v9.0.0, Gitea is not a StatefulSet but a Deployment instead. In order to make this work you have to replace pod pod name `gitea-0` with:
{{< highlight bash >}}
$(kubectl get po -n gitea -l app=gitea -o name --no-headers=true)
{{< / highlight >}}
Command substitution does not work in `AuthorizedKeysCommand` so I suggest creating a separate script along the lines of `/usr/local/bin/gitea-shell`
## Background
I am currently in the process of migrating my selfhosted applications from docker-compose to Kubernetes. One of my most used selfhosted app is Gitea. I use it to host my projects, dotfiles and config files where I don't expect any contributions or I simply want to keep it more private. In my docker-compose setup I used SSH Container Passthrough from [Gitea docs](https://docs.gitea.io/en-us/install-with-docker/#SSH-container-passthrough) but when I moved Gitea to k3s I couldn't find any guides on how to achieve the same thing.
I installed Gitea using the official [Helm Chart](https://gitea.com/gitea/helm-chart/). The documentation says this about enabling SSH:
> If you're using ingress and want to use SSH, keep in mind, that ingress is not able to forward SSH Ports. You will need a LoadBalancer like metallb and a setting in your SSH service annotations.
However using this method will route all incoming SSH connections to the Gitea container, essentialy disabling SSH connection to the host. Therefore we need a way to pass SSH connections to user `git` to our Gite container running on Kubernetes and at the same time allow SSH connections to host for some other user(s)
## Kubernetes Setup
Create user git on your host and deploy Gitea to Kubernetes (e.g. using Helm). You don't need to expose port 22 using a service.
First we are going to create a new login shell for user git. Create file `/usr/local/bin/gitea-shell` with content:
{{< highlight bash >}}
#!/bin/sh
/usr/local/bin/kubectl exec -i -n gitea gitea-0 -c gitea -- env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" /bin/sh "$@"
{{< / highlight >}}
Your namespace might be different.
Then run as root (or sudo):
{{< highlight bash >}}
chmod +x /usr/local/bin/gitea-shell
usermod -s /usr/local/bin/gitea-shell git
{{< / highlight >}}
Now everytime the user git logs in (i.e. using git via SSH) `/usr/local/bin/gitea-shell` gets executed which means our original SSH command will be executed in the gitea container.
Finally we need to make sure that the SSH keys we add through Gitea interface allow us to 'login' as git user.
Edit `/etc/SSH/SSHd_config` and add the following:
{{< highlight yaml>}}
Match User git
AuthorizedKeysCommandUser git
AuthorizedKeysCommand /usr/local/bin/kubectl exec -i -n gitea gitea-0 -c gitea -- /usr/local/bin/gitea keys -e git -u %u -t %t -k %k
{{< / highlight >}}
If you are using `AllowUsers` directive don't forget to add user git
## Testing
Open Gitea in web browser and add you SSH key. Then try to SSH into the Gitea container.
{{< highlight bash >}}
ssh git@<your gitea url>
{{< / highlight >}}
You should get a message like this:
> PTY allocation request failed on channel 0
> Hi there, test! You've successfully authenticated with the key named <your ssh key>, but Gitea does not provide shell access.
> If this is unexpected, please log in with password and setup Gitea under another user.
> Connection to <your gitea url> closed.
Now you can create a repo and you should be able to clone and push via SSH.

View File

@ -1,64 +0,0 @@
---
title: "Building My Resume with GitHub Actions"
date: "2023-10-02"
tags:
- cicd
---
Even if you are not actively looking for a new job, it is a good idea to have an up to date resume. I'm using Overleaf's GitHub integration and GitHub Actions to build PDF from my LaTeX resume and release it on GitHub
<!--more-->
## LaTeX
LaTeX is a typesetting system and document preparation tool known for its professional-quality typography and precise formatting capabilities. Widely used in academia, it's popular for creating resumes and academic documents due to its superior handling of complex formatting, mathematical equations, and citations. One of the benefits of using LaTeX is that it separates the content and formatting of a document, which makes it easier to focus on the content without worrying about the layout.
TeXLive is a comprehensive distribution of the LaTeX typesetting system, providing a wide range of packages, fonts, and utilities for users. It includes everything needed to create LaTeX documents, making it a go-to choice for many LaTeX users.
## Compiling PDF on Overleaf
I have been using [Overleaf](https://www.overleaf.com/) to edit, update and build my resume. Overleaf is a cloud-based LaTeX editor with countless templates for resumes, theses, presentations, cover letters, scientific papers and more. Overleaf uses TeXLive distribution, enabling their compile servers to provide a real-time preview of the typeset PDFs. This makes editing LaTeX on Overleaf very convenient since it is not necessary to understand the intricacies of document processing. Overleaf handles the complexities of LaTeX compilation, allowing you to concentrate on creating a standout resume. However, replicating the same results outside of Overleaf's environment requires deeper knowledge of the compilation process.
Overleaf uses [latexmk](https://ctan.org/pkg/latexmk?lang=en) package to automate the compilation. The package uses a system-wide configuration, which can be customized by a user-level configuration on a document-by-document basis. This customization might include adjusting the sequence of commands, specifying which files to include or exclude, defining custom dependencies, or setting up personalized error-handling procedures. You can access the `latexmk` configuration file used by Overleaf to typeset your document by compiling [this project](https://www.overleaf.com/learn/how-to/How_does_Overleaf_compile_my_project%3F#How_to_access_a_copy_of_Overleaf%E2%80%99s_LatexMk_file).
Because the template of my resume is quite simple it was not necessary to get the exact `latexmk` config file or to customize it in any way.
## Compiling and Releasing with GitHub Actions
Leveraging the power of GitHub Actions has streamlined my document compilation and release process significantly. Within the GitHub Actions Marketplace, there's a wealth of workflows tailored for various tasks, including LaTeX compilation. I've opted for [xu-cheng/latex-action](https://github.com/xu-cheng/latex-action) which utilizes the TeXLive environment and `latexmk` compiler, mirroring the setup used on Overleaf. And indeed without any configuration changes, my resume compiled to PDF correctly.
But automation doesn't stop there. To simplify the release procedure, I've incorporated [softprops/action-gh-release](https://github.com/softprops/action-gh-release). This action automates the creation of new tags, and releases, and even handles file attachments seamlessly. I've configured it to generate new releases marked with the current date, effortlessly adding the compiled `resume.pdf`. This automated process not only saves valuable time but also streamlines the release workflow, eliminating the need for manual login to Overleaf, recompilation, and downloading. It ensures that each new version of my resume is promptly available to the public.
You can check the full repository [here](https://github.com/LordMathis/resume). Alternatively here's just the workflow file:
```yaml
name: Release Compiled PDF
on:
push:
branches:
- master
jobs:
build_latex:
runs-on: ubuntu-latest
steps:
- name: Get current date
id: date
run: echo "NOW=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
- name: Set up Git repository
uses: actions/checkout@v3
- name: Compile
uses: xu-cheng/latex-action@v2
with:
root_file: resume.tex
- name: Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.NOW }}
files: ./resume.pdf
```

12
docker/Dockerfile Normal file
View File

@ -0,0 +1,12 @@
FROM node:16-alpine as front
WORKDIR /app
COPY ./ /app/
RUN yarn install
RUN yarn run build
FROM nginx:1.17.8-alpine
RUN rm -rf /usr/share/nginx/html
COPY --from=front /app/public/ /usr/share/nginx/html
COPY ./docker/default.conf /etc/nginx/conf.d/default.conf

21
docker/default.conf Normal file
View File

@ -0,0 +1,21 @@
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
autoindex off;
charset urtf-8;
error_page 404 /404.html;
access_log /var/log/nginx/access.log;
location ~* \.(html)$ {
add_header Cache-Control "no-store";
expires off;
}
rewrite ^([^.\?]*[^/])$ $1/ permanent;
try_files $uri $uri/ $uri/index.html =404;
}

7
gatsby-browser.js Normal file
View File

@ -0,0 +1,7 @@
/**
* Implement Gatsby's Browser APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/browser-apis/
*/
// You can delete this file if you're not using it

72
gatsby-config.js Normal file
View File

@ -0,0 +1,72 @@
module.exports = {
siteMetadata: {
author: `Matúš Námešný`,
user: "hello",
hostname: "namesny.com",
email: "matus@namesny.com",
social: [
{
name: "github",
link: "https://github.com/LordMathis",
},
{
name: "linkedin",
link: "https://www.linkedin.com/in/mat%C3%BA%C5%A1-n%C3%A1me%C5%A1n%C3%BD-3903b6128/",
}
],
},
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/content/pages`,
},
},
{
resolve: `gatsby-transformer-remark`,
options: {
filter: node => node.sourceInstanceName === `pages`,
type: `MarkdownPage`
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/content/posts/`,
},
},
{
resolve: `gatsby-transformer-remark`,
options: {
filter: node => node.sourceInstanceName === `posts`,
excerpt_separator: `<!-- end -->`,
type: `BlogPost`
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/favicon-32x32.png`, // This path is relative to the root of the site.
},
},
`gatsby-plugin-sass`,
],
}

93
gatsby-node.js Normal file
View File

@ -0,0 +1,93 @@
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
const parent = getNode(node.parent)
createNodeField({
node,
name: `slug`,
value: slug,
})
createNodeField({
node,
name: 'collection',
value: parent.sourceInstanceName,
})
}
}
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
// Posts query
const posts = await graphql(
`
{
allMarkdownRemark(filter:{frontmatter: {draft: {ne: true}}, fields: {collection: {eq: "posts"}}}) {
edges {
node {
fields {
slug
}
}
}
}
}
`
)
// Handle errors
if (posts.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
// Create pages for each markdown file.
const blogPostTemplate = path.resolve(`src/templates/blog-post.js`)
posts.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: `/posts${node.fields.slug}`,
component: blogPostTemplate,
context: {
slug: node.fields.slug,
},
})
})
const pages = await graphql(
`
{
allMarkdownRemark(filter: {fields: {collection: {eq: "pages"}}}) {
edges {
node {
fields {
slug
}
}
}
}
}
`
)
// Handle errors
if (pages.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
// Create pages for each markdown file.
const pageTemplate = path.resolve(`src/templates/page.js`)
pages.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.fields.slug,
component: pageTemplate,
context: {
slug: node.fields.slug,
},
})
})
}

7
gatsby-ssr.js Normal file
View File

@ -0,0 +1,7 @@
/**
* Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/ssr-apis/
*/
// You can delete this file if you're not using it

5
go.mod
View File

@ -1,5 +0,0 @@
module git.namesny.com/Mathis/namesny.com
go 1.20
require github.com/LordMathis/hugo-theme-nightfall v0.7.0 // indirect

10
go.sum
View File

@ -1,10 +0,0 @@
github.com/LordMathis/hugo-theme-nightfall v0.0.0-20230212210243-f116fbf0bdbb h1:31cDOpojmeNh9kpnwdPSe+Umzc1UaMc9vFM5N/7e5Yc=
github.com/LordMathis/hugo-theme-nightfall v0.0.0-20230212210243-f116fbf0bdbb/go.mod h1:0tCPxAeg5+tWhv17517Q8Lti/TPh0KNyON/uferEU30=
github.com/LordMathis/hugo-theme-nightfall v0.5.1 h1:xeycc74MTnikZ7tv+V8Lhuu9zrqRpVkaNjqw9eQYVNc=
github.com/LordMathis/hugo-theme-nightfall v0.5.1/go.mod h1:0tCPxAeg5+tWhv17517Q8Lti/TPh0KNyON/uferEU30=
github.com/LordMathis/hugo-theme-nightfall v0.6.0 h1:AmJFH2tQ66ZboBJ44RVwtt37Nhi6Qv29k6I/ouPxxRc=
github.com/LordMathis/hugo-theme-nightfall v0.6.0/go.mod h1:0tCPxAeg5+tWhv17517Q8Lti/TPh0KNyON/uferEU30=
github.com/LordMathis/hugo-theme-nightfall v0.6.1 h1:O9MXJpRv8A6Gxn2xUQHZJ2PVq4ryORlht6d4miUXrvI=
github.com/LordMathis/hugo-theme-nightfall v0.6.1/go.mod h1:0tCPxAeg5+tWhv17517Q8Lti/TPh0KNyON/uferEU30=
github.com/LordMathis/hugo-theme-nightfall v0.7.0 h1:aKN7W6wx1l2alvesLwA4rsoi7w/9wR0A+pKBWysiDCI=
github.com/LordMathis/hugo-theme-nightfall v0.7.0/go.mod h1:0tCPxAeg5+tWhv17517Q8Lti/TPh0KNyON/uferEU30=

View File

@ -1,17 +0,0 @@
server {
listen 80;
listen [::]:80;
server_name localhost;
access_log /var/log/nginx/access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

51
package.json Normal file
View File

@ -0,0 +1,51 @@
{
"name": "namesny.com",
"private": true,
"description": "Personal website",
"version": "2.0.0",
"author": "Matus Namesny <matus@namesny.com>",
"dependencies": {
"@fontsource/fira-mono": "^4.5.0",
"@fontsource/open-sans": "^4.5.2",
"gatsby": "^4.5.4",
"gatsby-image": "^3.11.0",
"gatsby-plugin-manifest": "^4.5.2",
"gatsby-plugin-offline": "^5.5.2",
"gatsby-plugin-react-helmet": "^5.5.0",
"gatsby-plugin-sass": "^5.5.0",
"gatsby-plugin-sharp": "^4.5.2",
"gatsby-source-filesystem": "^4.5.2",
"gatsby-transformer-remark": "^5.25.1",
"gatsby-transformer-sharp": "^4.5.0",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-helmet": "^6.1.0",
"sass": "^1.49.0"
},
"devDependencies": {
"prettier": "^2.5.1"
},
"keywords": [
"gatsby",
"blog",
"portfolio"
],
"license": "0BSD",
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
"start": "npm run develop",
"serve": "gatsby serve",
"clean": "gatsby clean",
"test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/gatsbyjs/gatsby-starter-default"
},
"bugs": {
"url": "https://github.com/gatsbyjs/gatsby/issues"
}
}

21
src/components/blog.js Normal file
View File

@ -0,0 +1,21 @@
import React from "react"
import PostLink from "./post-link"
import {header} from "../styles/blog.module.scss"
const Blog = ({ edges }) => {
const Posts = edges
.map(edge => <PostLink key={edge.node.id} post={edge.node} />)
return (
<div>
<div className={header}>
<h1>Blog</h1>
</div>
<div>
{Posts}
</div>
</div>
)
}
export default Blog

12
src/components/footer.js Normal file
View File

@ -0,0 +1,12 @@
import React from "react"
import { footer, link } from '../styles/footer.module.scss'
const Footer = ({authorName}) => (
<footer className={footer}>
© {new Date().getFullYear()} {authorName}, Built with
{` `}
<a href="https://www.gatsbyjs.org" className={link}>Gatsby</a>
</footer>
)
export default Footer

35
src/components/header.js Normal file
View File

@ -0,0 +1,35 @@
import { Link } from "gatsby"
import PropTypes from "prop-types"
import React from "react"
import { header, headerWrapper, terminal, links } from "../styles/header.module.scss"
const Header = ({ user, hostname }) => (
<header className={headerWrapper}>
<div className={header}>
<div>
<Link to="/" className={terminal}>{user}@{hostname} ~ $</Link>
</div>
<nav className={links}>
<ul>
<li key="about">
<a href='/about'>
<span>~/about</span>
</a>
</li>
<li key="blog">
<a href='/blog'>
<span>~/blog</span>
</a>
</li>
</ul>
</nav>
</div>
</header>
)
Header.propTypes = {
user: PropTypes.string.isRequired,
hostname: PropTypes.string.isRequired,
}
export default Header

32
src/components/image.js Normal file
View File

@ -0,0 +1,32 @@
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"
/*
* This component is built using `gatsby-image` to automatically serve optimized
* images with lazy loading and reduced file sizes. The image is loaded using a
* `useStaticQuery`, which allows us to load the image from directly within this
* component, rather than having to pass the image data down from pages.
*
* For more information, see the docs:
* - `gatsby-image`: https://gatsby.dev/gatsby-image
* - `useStaticQuery`: https://www.gatsbyjs.org/docs/use-static-query/
*/
const Image = () => {
const data = useStaticQuery(graphql`
query {
placeholderImage: file(relativePath: { eq: "gatsby-astronaut.png" }) {
childImageSharp {
fluid(maxWidth: 300) {
...GatsbyImageSharpFluid
}
}
}
}
`)
return <Img fluid={data.placeholderImage.childImageSharp.fluid} />
}
export default Image

19
src/components/index.js Normal file
View File

@ -0,0 +1,19 @@
import { Link } from "gatsby"
import PropTypes from "prop-types"
import React from "react"
import { indexWrapper, header } from "../styles/index.module.scss"
import Social from "./social"
const Index = ({ author, social, email }) => (
<div className={indexWrapper}>
<div>
<h1 className={header}>{ author }</h1>
</div>
<Social social={social} email={email}/>
</div>
)
Index.propTypes = {
}
export default Index

49
src/components/layout.js Normal file
View File

@ -0,0 +1,49 @@
import React from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"
import Header from "./header"
import Footer from "./footer"
import { Helmet } from "react-helmet"
import '../styles/global.scss';
import { main, content, vertical, flexWrapper } from "../styles/layout.module.scss"
const Layout = ({ children, title, verticalLayout}) => {
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
author
user
hostname
}
}
}`)
const classes = verticalLayout ? `${content} ${vertical}` : content
return (
<div className={flexWrapper}>
<Helmet
titleTemplate={`%s | ${data.site.siteMetadata.author}`}>
<html lang="en" amp />
<title>{title}</title>
</Helmet>
<Header
user={data.site.siteMetadata.user}
hostname={data.site.siteMetadata.hostname} />
<div className={classes}>
<main className={main}>{children}</main>
</div>
<Footer authorName={data.site.siteMetadata.author}/>
</div>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
vertical: PropTypes.bool
}
export default Layout

View File

@ -0,0 +1,28 @@
import React from "react"
import { Link } from "gatsby"
import { postTitle, postDate, postHeader, postListItem, postExcerpt } from '../styles/post-link.module.scss'
const PostLink = ({ post }) => {
const date = new Date(post.frontmatter.date)
const options = { year: 'numeric', month: 'long', day: 'numeric' };
const postDateString = date.toLocaleDateString('en', options);
const postUrl = "/posts" + post.fields.slug
return (
<Link to={postUrl}>
<div className={postListItem} role="listitem">
<div className={postHeader} >
<span className={postTitle}>{post.frontmatter.title}</span>
<span className={postDate}>{postDateString}</span>
</div>
<div className={postExcerpt}>
<p>{post.excerpt}</p>
</div>
</div>
</Link>
)
}
export default PostLink

43
src/components/social.js Normal file
View File

@ -0,0 +1,43 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { socialNavbar } from '../styles/social.module.scss'
export default class Social extends Component {
static propTypes = {
social: PropTypes.arrayOf(PropTypes.object),
email: PropTypes.string
}
render () {
let key = 0
const socialLinks = this.props.social.map((val) => {
const link = (
<li key={key}>
<a href={val.link} role="link">
{val.name}
</a>
</li>
)
key += 1
return link
})
socialLinks.push(
<li key={key}>
<a href={`mailto:${this.props.email}`} role="link">
e-mail
</a>
</li>
)
return (
<div className={socialNavbar} role="list">
<ul>
{socialLinks}
</ul>
</div>
)
}
}

View File

Before

Width:  |  Height:  |  Size: 433 B

After

Width:  |  Height:  |  Size: 433 B

View File

Before

Width:  |  Height:  |  Size: 887 B

After

Width:  |  Height:  |  Size: 887 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

10
src/pages/404.js Normal file
View File

@ -0,0 +1,10 @@
import React from "react"
const NotFoundPage = () => (
<Layout title="404: Not found" >
<h1>NOT FOUND</h1>
<p>You just hit a route that doesn&#39;t exist... the sadness.</p>
</Layout>
)
export default NotFoundPage

39
src/pages/blog.js Normal file
View File

@ -0,0 +1,39 @@
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Layout from "../components/layout"
import Blog from "../components/blog"
const IndexPage = () => {
const data = useStaticQuery(graphql`
query {
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date] }
filter: {frontmatter: {draft: {ne: true}}, fields: {collection: {eq: "posts"}}}
) {
edges {
node {
id
excerpt
frontmatter {
date
title
}
fields {
slug
}
}
}
}
}
`)
return (
<Layout title="Blog">
<Blog edges={data.allMarkdownRemark.edges}/>
</Layout>
)
}
export default IndexPage

31
src/pages/index.js Normal file
View File

@ -0,0 +1,31 @@
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Layout from "../components/layout"
import Index from "../components"
const IndexPage = () => {
const data = useStaticQuery(graphql`
query SiteDataQuery {
site {
siteMetadata {
author
email
social {
name
link
}
}
}
}
`)
return (
<Layout title="Home" vertical={true} >
<Index author={data.site.siteMetadata.author} social={data.site.siteMetadata.social} email={data.site.siteMetadata.email}/>
</Layout>
)
}
export default IndexPage

View File

@ -0,0 +1,5 @@
@import "./variables.scss";
.blogPostWrapper {
text-align: left;
}

View File

@ -0,0 +1,5 @@
@import "./variables.scss";
.header {
text-align: left;
}

View File

@ -0,0 +1,11 @@
@import "./variables.scss";
.footer {
padding: 15px;
text-align: center;
background-color: $backgroundDarker;
}
.link {
color: $white;
}

25
src/styles/global.scss Normal file
View File

@ -0,0 +1,25 @@
@import "./variables.scss";
body, html {
height: 100%;
}
body {
font-family: $fontParagraph;
color: $white;
background-color: $backgroundDark;
margin: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
@for $i from 1 through 6 {
h#{$i} {
font-family: $fontHeader;
}
}
@-ms-viewport{
width: device-width;
}

View File

@ -0,0 +1,42 @@
@import "./variables.scss";
.header {
font-family: $fontHeader;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
@media only screen and (min-width: $breakLarge) {
width: $width;
}
}
.headerWrapper {
overflow: auto;
box-sizing: border-box;
background-color: $backgroundDarker;
display: flex;
justify-content: center;
}
.links {
ul {
list-style: none;
padding: 0;
margin: 0;
li {
display: inline;
margin: 5px;
a {
color: $white;
text-decoration: none;
}
}
}
}
.terminal {
color: $white;
text-decoration: none;
}

View File

@ -0,0 +1,9 @@
.indexWrapper {
display: flex;
flex-direction: column;
justify-content: center;
}
.header {
font-size: 3em;
}

View File

@ -0,0 +1,31 @@
@import "./variables.scss";
.content {
text-align: center;
margin: 0 auto;
flex: 1 auto;
padding: 20px;
@media only screen and (min-width: $breakLarge) {
display: flex;
width: $width;
}
}
.vertical {
display: flex;
flex-direction: column;
justify-content: center;
}
.flexWrapper {
display: flex;
flex-direction: column;
justify-content: center;
min-height: 100vh;
}
.main {
width: 100%;
height: 100%;
}

View File

@ -0,0 +1,5 @@
@import "./variables.scss";
.pageWrapper {
text-align: left;
}

View File

@ -0,0 +1,52 @@
@import "./variables.scss";
.postDate {
float: right;
color: $white;
}
.postTitle {
color: $blue;
text-decoration: none;
text-transform: capitalize;
font-family: $fontHeader;
font-size: 1.2em;
float: left;
}
.postHeader {
overflow: hidden;
}
.postsList {
margin-top: 20px;
}
.postListItem {
padding: 20px;
background-color: $black;
margin-bottom: 20px;
}
.postExcerpt {
text-align: initial;
text-decoration: none;
color: $white;
}
.headerContainer {
display: flex;
justify-content: space-between;
}
.noDecoration {
text-decoration: none;
}
a,
a:link,
a:visited,
a:hover,
a:active{
text-decoration: none;
}

View File

@ -0,0 +1,19 @@
@import "./variables.scss";
.socialNavbar {
ul {
list-style: none;
padding: 0;
li {
display: inline;
}
}
a {
color: $white;
text-shadow: $black 0px 0px 2px;
-webkit-font-smoothing: antialiased;
display: inline-block;
margin: 10px;
}
}

18
src/styles/variables.scss Normal file
View File

@ -0,0 +1,18 @@
// Colors
$darkGrey: #323232;
$white: #f8f8ff;
$black: #2f2f2f;
$blue: #0f52bf;
$backgroundDarker: #252627;
$backgroundDark: #292a2d;
//Fonts
@import "~@fontsource/fira-mono/500.css";
@import "~@fontsource/open-sans/index.css"; // Weight 400.
$fontHeader: 'Fira Mono', monospace;
$fontParagraph: 'Open Sans', sans-serif;
// Content
$breakLarge: 992px;
$width: 760px;

View File

@ -0,0 +1,27 @@
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
import { blogPostWrapper } from "../styles/blog-post.module.scss"
export default function BlogPost({ data }) {
const post = data.markdownRemark
return (
<Layout>
<div className={blogPostWrapper}>
<h1>{post.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</div>
</Layout>
)
}
export const query = graphql`
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
}
}
}
`

27
src/templates/page.js Normal file
View File

@ -0,0 +1,27 @@
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
import {pageWrapper} from "../styles/page.module.scss"
export default function Page({ data }) {
const page = data.markdownRemark
return (
<Layout>
<div className={pageWrapper}>
<h1>{page.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: page.html }} />
</div>
</Layout>
)
}
export const query = graphql`
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
}
}
}
`

11609
yarn.lock Normal file

File diff suppressed because it is too large Load Diff