mirror of
https://github.com/davegallant/davegallant.github.io.git
synced 2025-08-15 04:30:20 +00:00
1694 lines
62 KiB
HTML
1694 lines
62 KiB
HTML
<!doctype html><html lang=en data-theme=dark><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><link rel=preload as=font type=font/woff2 href=/fonts/roboto-slab-latin-400.woff2 crossorigin=anonymous><link rel=preload as=font type=font/woff2 href=/fonts/roboto-slab-latin-700.woff2 crossorigin=anonymous><link rel=preload as=font type=font/woff2 href=/fonts/fira-code-latin-300.woff2 crossorigin=anonymous><link rel=preload as=font type=font/woff2 href=/fonts/fira-code-latin-400.woff2 crossorigin=anonymous><link rel=preload as=font type=font/woff2 href=/fonts/fira-code-latin-700.woff2 crossorigin=anonymous><meta name=robots content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"><title>Setting up Gitea Actions with Tailscale</title>
|
|
<meta name=description content="In this post I&rsquo;ll go through the process of setting up Gitea Actions and Tailscale, unlocking a simple and secure way to automate workflows.
|
|
"><link rel=canonical href=/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/><meta name=twitter:card content="summary"><meta name=twitter:title content="Setting up Gitea Actions with Tailscale"><meta name=twitter:description content="In this post I’ll go through the process of setting up Gitea Actions and Tailscale, unlocking a simple and secure way to automate workflows."><meta property="og:title" content="Setting up Gitea Actions with Tailscale"><meta property="og:description" content="In this post I’ll go through the process of setting up Gitea Actions and Tailscale, unlocking a simple and secure way to automate workflows."><meta property="og:type" content="article"><meta property="og:url" content="/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/"><meta property="article:section" content="post"><meta property="article:published_time" content="2023-12-10T17:22:11-05:00"><meta property="article:modified_time" content="2024-01-01T23:33:36-05:00"><meta itemprop=name content="Setting up Gitea Actions with Tailscale"><meta itemprop=description content="In this post I’ll go through the process of setting up Gitea Actions and Tailscale, unlocking a simple and secure way to automate workflows."><meta itemprop=datePublished content="2023-12-10T17:22:11-05:00"><meta itemprop=dateModified content="2024-01-01T23:33:36-05:00"><meta itemprop=wordCount content="1049"><meta itemprop=keywords content="gitea,gitea actions,github actions,tailscale,self-hosted,"><style>/*
|
|
Critical CSS for above-the-fold content, delivered inline to increase first
|
|
paint performance
|
|
*/
|
|
|
|
/*! purgecss start ignore */
|
|
|
|
/* Typefaces */
|
|
|
|
/* roboto-slab-100normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Roboto Slab';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 100;
|
|
src:
|
|
local('Roboto Slab Thin '),
|
|
local('Roboto Slab-Thin'),
|
|
url('/fonts/roboto-slab-latin-100.woff2') format('woff2'),
|
|
url('/fonts/roboto-slab-latin-100.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* roboto-slab-200normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Roboto Slab';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 200;
|
|
src:
|
|
local('Roboto Slab Extra Light '),
|
|
local('Roboto Slab-Extra Light'),
|
|
url('/fonts/roboto-slab-latin-200.woff2') format('woff2'),
|
|
url('/fonts/roboto-slab-latin-200.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* roboto-slab-300normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Roboto Slab';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 300;
|
|
src:
|
|
local('Roboto Slab Light '),
|
|
local('Roboto Slab-Light'),
|
|
url('/fonts/roboto-slab-latin-300.woff2') format('woff2'),
|
|
url('/fonts/roboto-slab-latin-300.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* roboto-slab-400normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Roboto Slab';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 400;
|
|
src:
|
|
local('Roboto Slab Regular '),
|
|
local('Roboto Slab-Regular'),
|
|
url('/fonts/roboto-slab-latin-400.woff2') format('woff2'),
|
|
url('/fonts/roboto-slab-latin-400.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* roboto-slab-500normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Roboto Slab';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 500;
|
|
src:
|
|
local('Roboto Slab Medium '),
|
|
local('Roboto Slab-Medium'),
|
|
url('/fonts/roboto-slab-latin-500.woff2') format('woff2'),
|
|
url('/fonts/roboto-slab-latin-500.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* roboto-slab-600normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Roboto Slab';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 600;
|
|
src:
|
|
local('Roboto Slab SemiBold '),
|
|
local('Roboto Slab-SemiBold'),
|
|
url('/fonts/roboto-slab-latin-600.woff2') format('woff2'),
|
|
url('/fonts/roboto-slab-latin-600.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* roboto-slab-700normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Roboto Slab';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 700;
|
|
src:
|
|
local('Roboto Slab Bold '),
|
|
local('Roboto Slab-Bold'),
|
|
url('/fonts/roboto-slab-latin-700.woff2') format('woff2'),
|
|
url('/fonts/roboto-slab-latin-700.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* roboto-slab-800normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Roboto Slab';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 800;
|
|
src:
|
|
local('Roboto Slab ExtraBold '),
|
|
local('Roboto Slab-ExtraBold'),
|
|
url('/fonts/roboto-slab-latin-800.woff2') format('woff2'),
|
|
url('/fonts/roboto-slab-latin-800.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* roboto-slab-900normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Roboto Slab';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 900;
|
|
src:
|
|
local('Roboto Slab Black '),
|
|
local('Roboto Slab-Black'),
|
|
url('/fonts/roboto-slab-latin-900.woff2') format('woff2'),
|
|
url('/fonts/roboto-slab-latin-900.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* fira-code-300normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Fira Code';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 300;
|
|
src:
|
|
local('Fira Code Light '),
|
|
local('Fira Code-Light'),
|
|
url('/fonts/fira-code-latin-300.woff2') format('woff2'),
|
|
url('/fonts/fira-code-latin-300.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* fira-code-400normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Fira Code';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 400;
|
|
src:
|
|
local('Fira Code Regular '),
|
|
local('Fira Code-Regular'),
|
|
url('/fonts/fira-code-latin-400.woff2') format('woff2'),
|
|
url('/fonts/fira-code-latin-400.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* fira-code-500normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Fira Code';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 500;
|
|
src:
|
|
local('Fira Code Medium '),
|
|
local('Fira Code-Medium'),
|
|
url('/fonts/fira-code-latin-500.woff2') format('woff2'),
|
|
url('/fonts/fira-code-latin-500.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* fira-code-600normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Fira Code';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 600;
|
|
src:
|
|
local('Fira Code SemiBold '),
|
|
local('Fira Code-SemiBold'),
|
|
url('/fonts/fira-code-latin-600.woff2') format('woff2'),
|
|
url('/fonts/fira-code-latin-600.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* fira-code-700normal - latin */
|
|
|
|
@font-face {
|
|
font-family: 'Fira Code';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 700;
|
|
src:
|
|
local('Fira Code Bold '),
|
|
local('Fira Code-Bold'),
|
|
url('/fonts/fira-code-latin-700.woff2') format('woff2'),
|
|
url('/fonts/fira-code-latin-700.woff') format('woff'); /* Modern Browsers */
|
|
}
|
|
|
|
/* Normalize */
|
|
|
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
|
|
|
/* Document
|
|
========================================================================== */
|
|
|
|
/**
|
|
* 1. Correct the line height in all browsers.
|
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
|
*/
|
|
|
|
html {
|
|
line-height: 1.15; /* 1 */
|
|
-webkit-text-size-adjust: 100%; /* 2 */
|
|
}
|
|
|
|
/* Sections
|
|
========================================================================== */
|
|
|
|
/**
|
|
* Remove the margin in all browsers.
|
|
*/
|
|
|
|
body {
|
|
margin: 0;
|
|
}
|
|
|
|
/**
|
|
* Render the `main` element consistently in IE.
|
|
*/
|
|
|
|
main {
|
|
display: block;
|
|
}
|
|
|
|
/**
|
|
* Correct the font size and margin on `h1` elements within `section` and
|
|
* `article` contexts in Chrome, Firefox, and Safari.
|
|
*/
|
|
|
|
h1 {
|
|
font-size: 2em;
|
|
margin: 0.67em 0;
|
|
}
|
|
|
|
/* Grouping content
|
|
========================================================================== */
|
|
|
|
/**
|
|
* 1. Add the correct box sizing in Firefox.
|
|
* 2. Show the overflow in Edge and IE.
|
|
*/
|
|
|
|
hr {
|
|
box-sizing: content-box; /* 1 */
|
|
height: 0; /* 1 */
|
|
overflow: visible; /* 2 */
|
|
}
|
|
|
|
/**
|
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
|
* 2. Correct the odd `em` font sizing in all browsers.
|
|
*/
|
|
|
|
pre {
|
|
font-family: monospace, monospace; /* 1 */
|
|
font-size: 1em; /* 2 */
|
|
}
|
|
|
|
/* Text-level semantics
|
|
========================================================================== */
|
|
|
|
/**
|
|
* Remove the gray background on active links in IE 10.
|
|
*/
|
|
|
|
a {
|
|
background-color: transparent;
|
|
}
|
|
|
|
/**
|
|
* 1. Remove the bottom border in Chrome 57-
|
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
|
*/
|
|
|
|
abbr[title] {
|
|
border-bottom: none; /* 1 */
|
|
text-decoration: underline; /* 2 */
|
|
text-decoration: underline dotted; /* 2 */
|
|
}
|
|
|
|
/**
|
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
|
*/
|
|
|
|
b,
|
|
strong {
|
|
font-weight: bolder;
|
|
}
|
|
|
|
/**
|
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
|
* 2. Correct the odd `em` font sizing in all browsers.
|
|
*/
|
|
|
|
code,
|
|
kbd,
|
|
samp {
|
|
font-family: monospace, monospace; /* 1 */
|
|
font-size: 1em; /* 2 */
|
|
}
|
|
|
|
/**
|
|
* Add the correct font size in all browsers.
|
|
*/
|
|
|
|
small {
|
|
font-size: 80%;
|
|
}
|
|
|
|
/**
|
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
|
* all browsers.
|
|
*/
|
|
|
|
sub,
|
|
sup {
|
|
font-size: 75%;
|
|
line-height: 0;
|
|
position: relative;
|
|
vertical-align: baseline;
|
|
}
|
|
|
|
sub {
|
|
bottom: -0.25em;
|
|
}
|
|
|
|
sup {
|
|
top: -0.5em;
|
|
}
|
|
|
|
/* Embedded content
|
|
========================================================================== */
|
|
|
|
/**
|
|
* Remove the border on images inside links in IE 10.
|
|
*/
|
|
|
|
img {
|
|
border-style: none;
|
|
}
|
|
|
|
/* Forms
|
|
========================================================================== */
|
|
|
|
/**
|
|
* 1. Change the font styles in all browsers.
|
|
* 2. Remove the margin in Firefox and Safari.
|
|
*/
|
|
|
|
button,
|
|
input,
|
|
optgroup,
|
|
select,
|
|
textarea {
|
|
font-family: inherit; /* 1 */
|
|
font-size: 100%; /* 1 */
|
|
line-height: 1.15; /* 1 */
|
|
margin: 0; /* 2 */
|
|
}
|
|
|
|
/**
|
|
* Show the overflow in IE.
|
|
* 1. Show the overflow in Edge.
|
|
*/
|
|
|
|
button,
|
|
input { /* 1 */
|
|
overflow: visible;
|
|
}
|
|
|
|
/**
|
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
|
* 1. Remove the inheritance of text transform in Firefox.
|
|
*/
|
|
|
|
button,
|
|
select { /* 1 */
|
|
text-transform: none;
|
|
}
|
|
|
|
/**
|
|
* Correct the inability to style clickable types in iOS and Safari.
|
|
*/
|
|
|
|
button,
|
|
[type="button"],
|
|
[type="reset"],
|
|
[type="submit"] {
|
|
-webkit-appearance: button;
|
|
}
|
|
|
|
/**
|
|
* Remove the inner border and padding in Firefox.
|
|
*/
|
|
|
|
button::-moz-focus-inner,
|
|
[type="button"]::-moz-focus-inner,
|
|
[type="reset"]::-moz-focus-inner,
|
|
[type="submit"]::-moz-focus-inner {
|
|
border-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
/**
|
|
* Restore the focus styles unset by the previous rule.
|
|
*/
|
|
|
|
button:-moz-focusring,
|
|
[type="button"]:-moz-focusring,
|
|
[type="reset"]:-moz-focusring,
|
|
[type="submit"]:-moz-focusring {
|
|
outline: 1px dotted ButtonText;
|
|
}
|
|
|
|
/**
|
|
* Correct the padding in Firefox.
|
|
*/
|
|
|
|
fieldset {
|
|
padding: 0.35em 0.75em 0.625em;
|
|
}
|
|
|
|
/**
|
|
* 1. Correct the text wrapping in Edge and IE.
|
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
|
* 3. Remove the padding so developers are not caught out when they zero out
|
|
* `fieldset` elements in all browsers.
|
|
*/
|
|
|
|
legend {
|
|
box-sizing: border-box; /* 1 */
|
|
color: inherit; /* 2 */
|
|
display: table; /* 1 */
|
|
max-width: 100%; /* 1 */
|
|
padding: 0; /* 3 */
|
|
white-space: normal; /* 1 */
|
|
}
|
|
|
|
/**
|
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
|
*/
|
|
|
|
progress {
|
|
vertical-align: baseline;
|
|
}
|
|
|
|
/**
|
|
* Remove the default vertical scrollbar in IE 10+.
|
|
*/
|
|
|
|
textarea {
|
|
overflow: auto;
|
|
}
|
|
|
|
/**
|
|
* 1. Add the correct box sizing in IE 10.
|
|
* 2. Remove the padding in IE 10.
|
|
*/
|
|
|
|
[type="checkbox"],
|
|
[type="radio"] {
|
|
box-sizing: border-box; /* 1 */
|
|
padding: 0; /* 2 */
|
|
}
|
|
|
|
/**
|
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
|
*/
|
|
|
|
[type="number"]::-webkit-inner-spin-button,
|
|
[type="number"]::-webkit-outer-spin-button {
|
|
height: auto;
|
|
}
|
|
|
|
/**
|
|
* 1. Correct the odd appearance in Chrome and Safari.
|
|
* 2. Correct the outline style in Safari.
|
|
*/
|
|
|
|
[type="search"] {
|
|
-webkit-appearance: textfield; /* 1 */
|
|
outline-offset: -2px; /* 2 */
|
|
}
|
|
|
|
/**
|
|
* Remove the inner padding in Chrome and Safari on macOS.
|
|
*/
|
|
|
|
[type="search"]::-webkit-search-decoration {
|
|
-webkit-appearance: none;
|
|
}
|
|
|
|
/**
|
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
|
* 2. Change font properties to `inherit` in Safari.
|
|
*/
|
|
|
|
::-webkit-file-upload-button {
|
|
-webkit-appearance: button; /* 1 */
|
|
font: inherit; /* 2 */
|
|
}
|
|
|
|
/* Interactive
|
|
========================================================================== */
|
|
|
|
/*
|
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
|
*/
|
|
|
|
details {
|
|
display: block;
|
|
}
|
|
|
|
/*
|
|
* Add the correct display in all browsers.
|
|
*/
|
|
|
|
summary {
|
|
display: list-item;
|
|
}
|
|
|
|
/* Misc
|
|
========================================================================== */
|
|
|
|
/**
|
|
* Add the correct display in IE 10+.
|
|
*/
|
|
|
|
template {
|
|
display: none;
|
|
}
|
|
|
|
/**
|
|
* Add the correct display in IE 10.
|
|
*/
|
|
|
|
[hidden] {
|
|
display: none;
|
|
}
|
|
|
|
/*! purgecss end ignore */
|
|
|
|
/*! CC BY-SA 3.0 License | https://stackoverflow.com/a/36118384/1154965 */
|
|
|
|
@keyframes blink {
|
|
50% {
|
|
opacity: 0;
|
|
}
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* Default license header for non-vendor CSS source code that follows */
|
|
|
|
/*! MIT License | github.com/schnerring/hugo-theme-gruvbox */
|
|
|
|
/*
|
|
Bootstrap 5 breakpoints
|
|
See: https://getbootstrap.com/docs/5.0/layout/breakpoints/#available-breakpoints
|
|
*/
|
|
|
|
@custom-media --sm (min-width: 576px);
|
|
|
|
@custom-media --md (min-width: 768px);
|
|
|
|
@custom-media --lg (min-width: 992px);
|
|
|
|
@custom-media --xl (min-width: 1200px);
|
|
|
|
@custom-media --xxl (min-width: 1400px);
|
|
|
|
:root[data-theme="light"] {
|
|
--bg: var(--bg0);
|
|
--bg0: #fbf1c7;
|
|
--bg0_h: #f9f5d7;
|
|
--bg0_s: #f2e5bc;
|
|
--bg1: #ebdbb2;
|
|
--bg2: #d5c4a1;
|
|
--bg3: #bdae93;
|
|
--bg4: #a89984;
|
|
--fg: var(--fg1);
|
|
--fg0: #282828;
|
|
--fg1: #3c3836;
|
|
--fg2: #504945;
|
|
--fg3: #665c54;
|
|
--fg4: #7c6f64;
|
|
--gray1: var(--fg4);
|
|
--gray2: #928374;
|
|
--red1: #cc241d;
|
|
--red2: #9d0006;
|
|
--green1: #98971a;
|
|
--green2: #797403;
|
|
--yellow1: #d79921;
|
|
--yellow2: #b57614;
|
|
--blue1: #458588;
|
|
--blue2: #076678;
|
|
--purple1: #b16286;
|
|
--purple2: #8f3f71;
|
|
--aqua1: #689d6a;
|
|
--aqua2: #427b58;
|
|
--orange1: #d65d0e;
|
|
--orange2: #af3a03;
|
|
}
|
|
|
|
[data-theme="light"]:root .light--hidden {
|
|
display: none;
|
|
}
|
|
|
|
:root[data-theme="dark"] {
|
|
--bg: var(--bg0);
|
|
--bg0: #282828;
|
|
--bg0_h: #1d2021;
|
|
--bg0_s: #32302f;
|
|
--bg1: #3c3836;
|
|
--bg2: #504945;
|
|
--bg3: #665c54;
|
|
--bg4: #7c6f64;
|
|
--fg: var(--fg1);
|
|
--fg0: #fbf1c7;
|
|
--fg1: #ebdbb2;
|
|
--fg2: #d5c4a1;
|
|
--fg3: #bdae93;
|
|
--fg4: #a89984;
|
|
--gray1: var(--fg4);
|
|
--gray2: #928374;
|
|
--red1: #cc241d;
|
|
--red2: #fb4934;
|
|
--green1: #98971a;
|
|
--green2: #b8bb26;
|
|
--yellow1: #d79921;
|
|
--yellow2: #fabd2f;
|
|
--blue1: #458588;
|
|
--blue2: #83a598;
|
|
--purple1: #b16286;
|
|
--purple2: #d3869b;
|
|
--aqua1: #689d6a;
|
|
--aqua2: #8ec07c;
|
|
--orange1: #d65d0e;
|
|
--orange2: #fe8019;
|
|
}
|
|
|
|
[data-theme="dark"]:root .dark--hidden {
|
|
display: none;
|
|
}
|
|
|
|
:root {
|
|
|
|
--primary: var(--blue1);
|
|
--primary-alt: var(--blue2);
|
|
}
|
|
|
|
:root {
|
|
--font-monospace: "Fira Code", "Lucida Console", Monaco, monospace;
|
|
--font-sans-serif: Verdana, Helvetica, sans-serif;
|
|
--font-serif: "Roboto Slab", Georgia, serif;
|
|
}
|
|
|
|
html {
|
|
font-family: var(--font-serif);
|
|
font-size: 1rem;
|
|
scroll-behavior: smooth;
|
|
}
|
|
|
|
body {
|
|
background: var(--bg);
|
|
color: var(--fg);
|
|
line-height: 1.675;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
strong {
|
|
letter-spacing: 0.35px;
|
|
}
|
|
|
|
a {
|
|
color: inherit;
|
|
text-decoration: none;
|
|
}
|
|
|
|
a.link--external::after {
|
|
/* 2009 = Thin Space */
|
|
content: "\2009↗";
|
|
}
|
|
|
|
img,
|
|
video {
|
|
border: 2px solid var(--bg1);
|
|
height: auto;
|
|
max-width: 100%;
|
|
}
|
|
|
|
figure {
|
|
display: inline-block;
|
|
}
|
|
|
|
figcaption {
|
|
color: var(--fg3);
|
|
font-family: var(--font-serif);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
*::selection {
|
|
color: var(--fg0);
|
|
background: var(--bg4);
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4,
|
|
h5,
|
|
h6 {
|
|
color: var(--fg0);
|
|
font-family: var(--font-monospace);
|
|
font-weight: 300;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
|
font-size: 1em;
|
|
}
|
|
|
|
h2,
|
|
h3,
|
|
h4,
|
|
h5,
|
|
h6 {
|
|
border-bottom: 1px solid var(--bg1);
|
|
}
|
|
|
|
h1,
|
|
h2 {
|
|
font-weight: 400;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 1.875rem;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 1.75rem;
|
|
}
|
|
|
|
h3 {
|
|
font-size: 1.625rem;
|
|
}
|
|
|
|
@media (--md) {
|
|
h1 {
|
|
font-size: 2.375rem;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 2rem;
|
|
}
|
|
|
|
h3 {
|
|
font-size: 1.75rem;
|
|
}
|
|
}
|
|
|
|
h4 {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
h5 {
|
|
font-size: 1.375rem;
|
|
}
|
|
|
|
h6 {
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
table {
|
|
table-layout: fixed;
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
table,
|
|
th,
|
|
td {
|
|
border: 1px solid var(--bg1);
|
|
padding: 0.5rem;
|
|
}
|
|
|
|
hr {
|
|
border: none;
|
|
background: var(--bg1);
|
|
height: 1px;
|
|
margin: 3rem auto;
|
|
width: 80%;
|
|
}
|
|
|
|
mark {
|
|
background: var(--yellow1);
|
|
color: var(--bg0);
|
|
}
|
|
|
|
abbr {
|
|
text-underline-offset: 0.2rem;
|
|
}
|
|
|
|
blockquote,
|
|
code,
|
|
kbd,
|
|
mark,
|
|
pre {
|
|
border-radius: 0.2rem;
|
|
padding: 0 0.2em;
|
|
}
|
|
|
|
pre code {
|
|
padding: 0;
|
|
}
|
|
|
|
blockquote,
|
|
code,
|
|
kbd,
|
|
pre,
|
|
th {
|
|
background: var(--bg1);
|
|
}
|
|
|
|
code,
|
|
kbd,
|
|
pre,
|
|
th {
|
|
font-family: var(--font-monospace);
|
|
}
|
|
|
|
code code,
|
|
kbd code,
|
|
code kbd,
|
|
kbd kbd {
|
|
background: var(--bg2);
|
|
}
|
|
|
|
blockquote,
|
|
pre {
|
|
padding: 1rem;
|
|
}
|
|
|
|
pre {
|
|
/* TODO is !important really needed because of Prism? */
|
|
background: var(--bg1) !important;
|
|
overflow: auto;
|
|
}
|
|
|
|
pre code {
|
|
background: none;
|
|
}
|
|
|
|
blockquote,
|
|
blockquote.twitter-tweet {
|
|
border-left: var(--primary-alt) 5px solid;
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
:is(blockquote,blockquote.twitter-tweet) code {
|
|
background: var(--bg2);
|
|
}
|
|
|
|
:is(blockquote,blockquote.twitter-tweet) p:first-of-type {
|
|
margin-top: 0;
|
|
}
|
|
|
|
:is(blockquote,blockquote.twitter-tweet) p:last-of-type {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
blockquote.twitter-tweet {
|
|
border-color: var(--blue2);
|
|
color: inherit;
|
|
font: inherit;
|
|
font-size: inherit;
|
|
line-height: inherit;
|
|
}
|
|
|
|
blockquote.twitter-tweet a {
|
|
color: var(--blue2);
|
|
}
|
|
|
|
blockquote.twitter-tweet a:hover {
|
|
color: var(--blue1);
|
|
text-decoration: none !important;
|
|
}
|
|
|
|
pre::-webkit-scrollbar {
|
|
height: 0.5rem;
|
|
scrollbar-width: auto;
|
|
}
|
|
|
|
pre::-webkit-scrollbar-track {
|
|
background: var(--bg2);
|
|
border-radius: 0.2rem;
|
|
}
|
|
|
|
pre::-webkit-scrollbar-thumb {
|
|
background: var(--bg4);
|
|
border-radius: 0.2rem;
|
|
}
|
|
|
|
.layout {
|
|
display: grid;
|
|
grid-template-areas:
|
|
"header"
|
|
"main"
|
|
"footer";
|
|
grid-template-rows: auto 1fr auto;
|
|
height: 100vh;
|
|
}
|
|
|
|
main {
|
|
align-items: start;
|
|
display: grid;
|
|
grid-area: main;
|
|
grid-template-areas: "empty content sidebar";
|
|
grid-template-columns: 1fr minmax(0, 650px) 4fr;
|
|
}
|
|
|
|
header {
|
|
background: var(--bg1);
|
|
grid-area: header;
|
|
}
|
|
|
|
footer {
|
|
grid-area: footer;
|
|
}
|
|
|
|
main,
|
|
footer {
|
|
margin: 0.5em 1.1em;
|
|
}
|
|
|
|
.content {
|
|
grid-area: content;
|
|
}
|
|
|
|
.sidebar {
|
|
display: none;
|
|
flex-direction: column;
|
|
grid-area: sidebar;
|
|
margin-top: 3rem;
|
|
position: sticky;
|
|
top: 2rem;
|
|
}
|
|
|
|
@media (--lg) {
|
|
|
|
.sidebar {
|
|
display: flex;
|
|
}
|
|
}
|
|
|
|
header {
|
|
display: grid;
|
|
font-family: var(--font-monospace);
|
|
font-size: 1.125rem;
|
|
grid-template-columns: auto auto 1fr auto;
|
|
grid-template-areas: "heading search nav theme-toggle";
|
|
padding: 0.75rem;
|
|
}
|
|
|
|
.logo {
|
|
color: var(--fg0);
|
|
display: flex;
|
|
font-weight: 700;
|
|
grid-area: heading;
|
|
}
|
|
|
|
.logo:hover .logo__cursor {
|
|
animation: 1s blink infinite;
|
|
opacity: 1;
|
|
}
|
|
|
|
.logo__chevron,
|
|
.logo__cursor {
|
|
margin-left: 0.5rem;
|
|
}
|
|
|
|
.logo__cursor {
|
|
opacity: 0;
|
|
}
|
|
|
|
.logo__text {
|
|
display: none;
|
|
}
|
|
|
|
@media (--md) {
|
|
.logo__text {
|
|
display: block;
|
|
}
|
|
}
|
|
|
|
/*! purgecss start ignore */
|
|
|
|
.search {
|
|
display: flex;
|
|
grid-area: search;
|
|
margin: 0 1rem;
|
|
}
|
|
|
|
#search__text {
|
|
border: 1px solid var(--bg2);
|
|
border-radius: 0.2rem;
|
|
background: var(--bg2);
|
|
caret-color: var(--fg);
|
|
color: var(--fg);
|
|
outline: none;
|
|
padding: 0 0.5rem;
|
|
width: 100%;
|
|
}
|
|
|
|
#search__text:hover {
|
|
border-color: var(--bg3);
|
|
}
|
|
|
|
#search__text:focus {
|
|
border-color: var(--bg4);
|
|
}
|
|
|
|
#search__text::placeholder {
|
|
color: var(--fg3);
|
|
}
|
|
|
|
#search__text[type="search"]::-webkit-search-cancel-button {
|
|
appearance: none;
|
|
}
|
|
|
|
#search__suggestions {
|
|
background: var(--bg);
|
|
border-radius: 0.2rem;
|
|
box-shadow: 0 0.5rem 1rem var(--bg1);
|
|
font-family: var(--font-serif);
|
|
left: 0;
|
|
margin-top: 2rem;
|
|
position: absolute;
|
|
width: 95vw;
|
|
z-index: 1000;
|
|
}
|
|
|
|
@media (--md) {
|
|
.search {
|
|
position: relative;
|
|
}
|
|
|
|
#search__suggestions {
|
|
width: 60vw;
|
|
}
|
|
}
|
|
|
|
.search__suggestions--hidden {
|
|
display: none;
|
|
}
|
|
|
|
.search__suggestion-item {
|
|
border-bottom: 1px dashed var(--bg2);
|
|
display: grid;
|
|
grid-template-columns: 1fr 2fr;
|
|
}
|
|
|
|
.search__suggestion-item:focus,
|
|
.search__suggestion-item:focus-visible,
|
|
.search__suggestion-item:hover {
|
|
background: var(--bg1);
|
|
cursor: pointer;
|
|
outline: none;
|
|
}
|
|
|
|
.search__suggestion-item:last-child {
|
|
border: none;
|
|
}
|
|
|
|
.search__suggestion-title,
|
|
.search__suggestion-description {
|
|
padding: 0 1rem;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.search__suggestion-title {
|
|
font-weight: 700;
|
|
}
|
|
|
|
.search__suggestion-description {
|
|
border-left: 1px solid var(--bg2);
|
|
}
|
|
|
|
.search__no-results {
|
|
padding: 0.75rem;
|
|
}
|
|
|
|
/*! purgecss end ignore */
|
|
|
|
.theme__toggle {
|
|
align-items: center;
|
|
background: none;
|
|
border: none;
|
|
color: var(--yellow1);
|
|
cursor: pointer;
|
|
display: flex;
|
|
grid-area: theme-toggle;
|
|
margin: 0 1rem;
|
|
}
|
|
|
|
.theme__toggle:hover {
|
|
color: var(--yellow2);
|
|
}
|
|
|
|
.theme__toggle svg {
|
|
height: 28px;
|
|
width: 28px;
|
|
}
|
|
|
|
/* TODO: simplify deep nesting */
|
|
|
|
nav#menu {
|
|
align-items: center;
|
|
display: flex;
|
|
grid-area: nav;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
nav#menu .menu__item {
|
|
color: var(--fg);
|
|
}
|
|
|
|
nav#menu .menu__item:hover {
|
|
color: var(--fg3);
|
|
cursor: pointer;
|
|
}
|
|
|
|
nav#menu ul {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
nav#menu ul.menu--horizontal {
|
|
align-items: center;
|
|
display: none;
|
|
}
|
|
|
|
nav#menu ul.menu--horizontal li {
|
|
display: inline-block;
|
|
margin: 0 0.75rem;
|
|
}
|
|
|
|
@media (--md) {
|
|
|
|
nav#menu ul.menu--horizontal {
|
|
display: flex;
|
|
}
|
|
}
|
|
|
|
nav#menu ul.menu--vertical {
|
|
background: var(--fg0);
|
|
bottom: 0;
|
|
margin: 0;
|
|
padding: 3rem;
|
|
position: fixed;
|
|
right: 0;
|
|
top: 0;
|
|
transform: translate(100%, 0);
|
|
transition: transform 0.5s cubic-bezier(0.9, 0, 0.1, 1);
|
|
width: 50%;
|
|
z-index: 10;
|
|
}
|
|
|
|
nav#menu ul.menu--vertical .menu__item {
|
|
color: var(--bg1);
|
|
}
|
|
|
|
nav#menu ul.menu--vertical .menu__item:hover {
|
|
color: var(--bg4);
|
|
}
|
|
|
|
nav#menu .menu__burger {
|
|
display: flex;
|
|
height: 24px;
|
|
width: 24px;
|
|
}
|
|
|
|
nav#menu .menu__burger > * {
|
|
position: absolute;
|
|
}
|
|
|
|
nav#menu .menu__burger svg {
|
|
width: inherit;
|
|
z-index: 20;
|
|
height: inherit;
|
|
}
|
|
|
|
nav#menu .menu__burger svg line {
|
|
transition-duration: 0.5s;
|
|
transition-property: stroke, opacity, transform;
|
|
transition-timing-function: cubic-bezier(0.9, 0, 0.1, 1);
|
|
}
|
|
|
|
nav#menu .menu__burger svg line:nth-of-type(1) {
|
|
transform-origin: center 6px;
|
|
}
|
|
|
|
nav#menu .menu__burger svg line:nth-of-type(2) {
|
|
transform-origin: center 12px;
|
|
}
|
|
|
|
nav#menu .menu__burger svg line:nth-of-type(3) {
|
|
transform-origin: center 18px;
|
|
}
|
|
|
|
nav#menu .menu__burger input {
|
|
height: inherit;
|
|
opacity: 0;
|
|
width: inherit;
|
|
z-index: 30;
|
|
}
|
|
|
|
:is(nav#menu .menu__burger input:checked) ~ ul.menu--vertical {
|
|
transform: none;
|
|
}
|
|
|
|
:is(nav#menu .menu__burger input:checked) ~ svg {
|
|
stroke: var(--bg1);
|
|
}
|
|
|
|
:is(nav#menu .menu__burger input:checked) ~ svg line:nth-of-type(1) {
|
|
transform: translate(0, 6px) rotate(45deg);
|
|
}
|
|
|
|
:is(nav#menu .menu__burger input:checked) ~ svg line:nth-of-type(2) {
|
|
opacity: 0;
|
|
transform: scale(0.2);
|
|
}
|
|
|
|
:is(nav#menu .menu__burger input:checked) ~ svg line:nth-of-type(3) {
|
|
transform: translate(0, -6px) rotate(-45deg);
|
|
}
|
|
|
|
@media (--md) {
|
|
|
|
nav#menu .menu__burger {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.sidebar {
|
|
font-family: var(--font-monospace);
|
|
max-width: 350px;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
padding-left: 2.5rem;
|
|
}
|
|
|
|
.sidebar hr {
|
|
margin: 1.5rem auto;
|
|
}
|
|
|
|
.sidebar svg {
|
|
fill: var(--fg);
|
|
}
|
|
|
|
.sidebar__heading {
|
|
font-size: 1.3rem;
|
|
}
|
|
|
|
aside.toc a {
|
|
color: var(--primary-alt);
|
|
}
|
|
|
|
aside.toc a:hover {
|
|
color: var(--primary);
|
|
}
|
|
|
|
aside.toc ul {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
aside.toc ul ul {
|
|
font-size: 0.9rem;
|
|
margin-left: 0.5rem;
|
|
}
|
|
|
|
aside.toc ul li {
|
|
line-height: 1.1;
|
|
}
|
|
|
|
aside.toc ul li a {
|
|
display: block;
|
|
padding: 0.2rem 0;
|
|
}
|
|
|
|
.jr-basics__image {
|
|
background: var(--bg1);
|
|
border: 2px solid var(--bg2);
|
|
}
|
|
|
|
.jr-basics__summary {
|
|
color: var(--fg3);
|
|
font-family: var(--font-serif);
|
|
margin: 0.75rem 0;
|
|
}
|
|
|
|
.jr-basics__profile a:hover {
|
|
color: var(--fg3);
|
|
}
|
|
|
|
.jr-basics__profile a:hover svg {
|
|
fill: var(--fg3);
|
|
}
|
|
|
|
.tag-cloud {
|
|
line-height: 1.1;
|
|
text-align: justify;
|
|
}
|
|
|
|
.tag-cloud__tag:hover {
|
|
color: var(--fg3);
|
|
}
|
|
|
|
.tag-cloud__tag--active {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.post,
|
|
.content-section {
|
|
border-bottom: 2px dotted var(--bg1);
|
|
padding: 2rem 0;
|
|
}
|
|
|
|
.post figure,
|
|
.post img:not(figure img),
|
|
.post video:not(figure video) {
|
|
margin: 0.5rem 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.post-header,
|
|
.post-content__read-more {
|
|
font-family: var(--font-monospace);
|
|
}
|
|
|
|
.post-meta__author {
|
|
font-weight: 700;
|
|
}
|
|
|
|
.post-content {
|
|
margin: 1.3rem 0;
|
|
}
|
|
|
|
.post-content__read-more {
|
|
color: var(--primary-alt);
|
|
margin-top: 1.3rem;
|
|
}
|
|
|
|
.post-header a, .post-content a {
|
|
color: var(--primary-alt);
|
|
}
|
|
|
|
.post-header a:hover, .post-content a:hover {
|
|
color: var(--primary);
|
|
}
|
|
|
|
.post-tags {
|
|
align-items: center;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.9rem;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.post-tag {
|
|
font-size: 0.9rem;
|
|
line-height: 1;
|
|
}
|
|
|
|
.post-tag::before {
|
|
content: "#";
|
|
}
|
|
|
|
.post-heading__anchor {
|
|
display: none;
|
|
}
|
|
|
|
h1:hover .post-heading__anchor, h2:hover .post-heading__anchor, h3:hover .post-heading__anchor, h4:hover .post-heading__anchor, h5:hover .post-heading__anchor, h6:hover .post-heading__anchor {
|
|
display: inline-block;
|
|
}
|
|
|
|
.jr__item-meta {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.jr__item-meta,
|
|
.jr-basics__image,
|
|
.jr-basics__item,
|
|
.jr-basics__profile-icon,
|
|
.jr-basics__profile-item {
|
|
align-items: center;
|
|
display: flex;
|
|
}
|
|
|
|
.jr-awards__title,
|
|
.jr-certificates__name,
|
|
.jr-education__area,
|
|
.jr-projects__roles,
|
|
.jr-publications__name,
|
|
.jr-volunteer__position,
|
|
.jr-work__position,
|
|
.jr-basics__name {
|
|
font-size: 1.125rem;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.jr-basics__item {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
}
|
|
|
|
.jr-basics__item hr {
|
|
margin: 1.5rem auto;
|
|
}
|
|
|
|
.jr-basics__image {
|
|
border-radius: 50%;
|
|
height: 250px;
|
|
justify-content: center;
|
|
overflow: hidden;
|
|
width: 250px;
|
|
}
|
|
|
|
.jr-basics__name,
|
|
.jr-basics__label,
|
|
.jr-basics__summary {
|
|
margin-top: 0.75rem;
|
|
}
|
|
|
|
.jr-basics__profile svg {
|
|
height: 24px;
|
|
width: 24px;
|
|
}
|
|
|
|
.jr-basics__profile,
|
|
.jr-basics__profile-item {
|
|
display: flex;
|
|
}
|
|
|
|
.jr-basics__profile-item {
|
|
display: flex;
|
|
padding: 0.2rem;
|
|
}
|
|
|
|
.jr-basics__profile--col {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.jr-basics__profile--row {
|
|
flex-wrap: wrap;
|
|
justify-content: space-evenly;
|
|
}
|
|
|
|
.jr-basics__profile-icon {
|
|
padding: 0 0.75rem;
|
|
}
|
|
|
|
.jr__item-meta {
|
|
font-family: var(--font-monospace);
|
|
}
|
|
|
|
.jr__item-meta {
|
|
align-items: start;
|
|
flex-flow: column;
|
|
}
|
|
|
|
@media (--md) {
|
|
.jr__item-meta {
|
|
align-items: center;
|
|
flex-flow: row wrap;
|
|
}
|
|
|
|
.jr__date,
|
|
.jr__date-range,
|
|
.jr-work__location {
|
|
flex-grow: 1;
|
|
text-align: right;
|
|
}
|
|
|
|
.jr-awards__awarder,
|
|
.jr-publications__publisher,
|
|
.jr-education__institution,
|
|
.jr-volunteer__organization {
|
|
flex-basis: 100%;
|
|
}
|
|
}
|
|
|
|
.social-share {
|
|
align-items: center;
|
|
border-top: 2px dotted var(--bg1);
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.9rem;
|
|
margin: 3rem 0;
|
|
padding-top: 3rem;
|
|
}
|
|
|
|
.social-share svg {
|
|
fill: var(--fg);
|
|
width: 24px;
|
|
height: 24px;
|
|
}
|
|
|
|
.social-share svg.icon-tabler {
|
|
fill: none;
|
|
stroke: var(--fg);
|
|
}
|
|
|
|
.social-share__item {
|
|
background: var(--bg1);
|
|
padding: 0.5rem;
|
|
display: flex;
|
|
}
|
|
|
|
/* Sticky social bar left
|
|
|
|
@media (--xl) {
|
|
.social-share {
|
|
align-items: flex-start;
|
|
border-top: none;
|
|
flex-direction: column;
|
|
|
|
position: fixed;
|
|
top: 15%;
|
|
left: 0;
|
|
}
|
|
|
|
.social-share__heading {
|
|
display: none;
|
|
}
|
|
|
|
.social-share__item {
|
|
transition: padding 0.2s ease-in;
|
|
|
|
&:hover {
|
|
padding: 0.5rem 1rem;
|
|
}
|
|
|
|
& svg {
|
|
&:hover {
|
|
fill: var(--fg);
|
|
}
|
|
|
|
&.icon-tabler {
|
|
fill: none;
|
|
|
|
&:hover {
|
|
stroke: var(--fg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*/
|
|
</style><link rel=preload href="/css/non-critical.67cb283542b48f68e77ea69ecc88c660d6cd3c5286d9f66a72c693a6d009b5745c627a5b065dab9c3583e47513e8845d4c7aa57bd4c4847fa4fca61ae5258681.css" as=style onload='this.onload=null,this.rel="stylesheet"' integrity="sha512-Z8soNUK0j2jnfqaezIjGYNbNPFKG2fZqcsaTptAJtXRcYnpbBl2rnDWD5HUT6IRdTHqle9TEhH+k/KYa5SWGgQ=="><link id=prism-dark rel=preload href=/prism-themes/prism-gruvbox-dark.min.54aecc64074623a4f9898544dcbdab9e804f1560ef0b38f4cf8e10fcaaf72264e798cb407c601aca6ecd833ec4eb93d66535581f18d45ba202cf848b70dbc332.css as=style onload='this.onload=null,this.rel="stylesheet"' integrity="sha512-VK7MZAdGI6T5iYVE3L2rnoBPFWDvCzj0z44Q/Kr3ImTnmMtAfGAaym7Ngz7E65PWZTVYHxjUW6ICz4SLcNvDMg=="><link id=prism-light rel=preload href=/prism-themes/prism-gruvbox-light.min.42a221741efe997fcc94187c39d63c555560678789ac9ca856c74a5f0ddb2aa6c50d38b2ffbecc7a99038cbbd2efa99746e862267f781c559e0cfec10b88a5fc.css as=style onload='this.onload=null,this.rel="stylesheet"' integrity="sha512-QqIhdB7+mX/MlBh8OdY8VVVgZ4eJrJyoVsdKXw3bKqbFDTiy/77MepkDjLvS76mXRuhiJn94HFWeDP7BC4il/A==" disabled><noscript><link rel=stylesheet href=/prism-themes/prism-gruvbox-dark.min.54aecc64074623a4f9898544dcbdab9e804f1560ef0b38f4cf8e10fcaaf72264e798cb407c601aca6ecd833ec4eb93d66535581f18d45ba202cf848b70dbc332.css integrity="sha512-VK7MZAdGI6T5iYVE3L2rnoBPFWDvCzj0z44Q/Kr3ImTnmMtAfGAaym7Ngz7E65PWZTVYHxjUW6ICz4SLcNvDMg=="><link rel=stylesheet href="/css/non-critical.67cb283542b48f68e77ea69ecc88c660d6cd3c5286d9f66a72c693a6d009b5745c627a5b065dab9c3583e47513e8845d4c7aa57bd4c4847fa4fca61ae5258681.css" integrity="sha512-Z8soNUK0j2jnfqaezIjGYNbNPFKG2fZqcsaTptAJtXRcYnpbBl2rnDWD5HUT6IRdTHqle9TEhH+k/KYa5SWGgQ=="></noscript><script>(()=>{function n(){if(localStorage&&localStorage.getItem("theme"))return localStorage.getItem("theme");if(window.matchMedia)return window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"}function e(e){document.documentElement.setAttribute("data-theme",e);let t=document.getElementById("prism-dark"),n=document.getElementById("prism-light");t.toggleAttribute("disabled",e==="light"),n.toggleAttribute("disabled",e==="dark"),localStorage.setItem("theme",e)}var t=n();t&&e(t);function s(t){let n=t.currentTarget.classList.contains("light--hidden")?"light":"dark";e(n)}document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(".theme__toggle").forEach(e=>{e.addEventListener("click",s)})})})()</script><link rel=apple-touch-icon sizes=180x180 href=/apple-touch-icon.png><link rel=icon type=image/png sizes=32x32 href=/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/favicon-16x16.png><link rel=manifest href=/site.webmanifest><link rel=mask-icon href=/safari-pinned-tab.svg color=#282828><meta name=msapplication-TileColor content="#282828"><meta name=theme-color content="#282828"></head><body><div class=layout><header><a class=logo href=/><div class=logo__text>davegallant.ca</div><div class=logo__chevron>></div><div class=logo__cursor>█</div></a><div class=search><input id=search__text type=search placeholder=Search... aria-label=Search autocomplete=off><div id=search__suggestions class=search__suggestions--hidden></div></div><nav id=menu><ul class=menu--horizontal><li class=menu__item><a href=/index.xml>RSS</a></li></ul><div class=menu__burger><input class=menu__item type=checkbox aria-label="Open main menu"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-menu-2" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6h16"/><path d="M4 12h16"/><path d="M4 18h16"/></svg><ul class=menu--vertical><li><a class=menu__item href=/index.xml>RSS</a></li></ul></div></nav><button class="theme__toggle light--hidden" aria-label="Toggle light mode">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-sun" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-4 0a4 4 0 108 0 4 4 0 10-8 0"/><path d="M3 12h1m8-9v1m8 8h1m-9 8v1M5.6 5.6l.7.7m12.1-.7-.7.7m0 11.4.7.7m-12.1-.7-.7.7"/></svg></button>
|
|
<button class="theme__toggle dark--hidden" aria-label="Toggle dark mode"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-moon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 3c.132.0.263.0.393.0a7.5 7.5.0 007.92 12.446A9 9 0 1112 2.992z"/></svg></button></header><main><div class=content><article class=post><div class=post-header><h1>Setting up Gitea Actions with Tailscale</h1><div class=post-meta><span>2023-12-10</span><span> (updated: 2024-01-01)</span><div class=post-tags><a class=post-tag href=/tags/gitea>gitea</a><a class=post-tag href=/tags/gitea-actions>gitea‑actions</a><a class=post-tag href=/tags/github-actions>github‑actions</a><a class=post-tag href=/tags/tailscale>tailscale</a><a class=post-tag href=/tags/self-hosted>self‑hosted</a></div></div></div><div class=post-content><p>In this post I’ll go through the process of setting up Gitea Actions and <a href=https://tailscale.com/ class=link--external target=_blank rel=noreferrer>Tailscale</a>, unlocking a simple and secure way to automate workflows.</p><h2 id=what-is-gitea>What is Gitea?<a href=#what-is-gitea class=post-heading__anchor aria-hidden=true>#</a></h2><p><a href=https://about.gitea.com/ class=link--external target=_blank rel=noreferrer>Gitea</a> is a lightweight and fast git server that has much of the same look and feel as github. I have been using it in my homelab to mirror repositories hosted on other platforms such as github and gitlab. These mirrors take advantage of the decentralized nature of git by serving as “backups”. One of the main reasons I hadn’t been using it more often was due to the lack of integrated CI/CD. This is no longer the case.</p><h2 id=gitea-actions>Gitea Actions<a href=#gitea-actions class=post-heading__anchor aria-hidden=true>#</a></h2><p><a href=https://docs.gitea.com/usage/actions/overview class=link--external target=_blank rel=noreferrer>Gitea Actions</a> have made it into the <a href=https://blog.gitea.com/release-of-1.19.0/ class=link--external target=_blank rel=noreferrer>1.19.0 release</a>. This feature had been in an experimental state up until <a href=https://blog.gitea.com/release-of-1.21.0/ class=link--external target=_blank rel=noreferrer>1.21.0</a> and is now enabled by default 🎉.</p><p>So what are they? If you’ve ever used GitHub Actions (and if you’re reading this, I imagine you have), these will look familiar. Gitea Actions essentially enable the ability to run github workflows on gitea. Workflows between gitea and github are not completely interopable, but a lot of the same workflow syntax is already compatible on gitea. You can find a documented list of <a href=https://docs.gitea.com/usage/actions/comparison#unsupported-workflows-syntax class=link--external target=_blank rel=noreferrer>unsupported workflows syntax</a>.</p><p>Actions work by using a <a href=https://gitea.com/gitea/act class=link--external target=_blank rel=noreferrer>custom fork</a> of <a href=https://github.com/nektos/act class=link--external target=_blank rel=noreferrer>nekos/act</a>. Workflows run in a new container for every job. If you specify an action such as ‘actions/checkout@v3’, it defaults to downloading the scripts from github.com. To avoid internet egress, you could always clone the required actions to your local gitea instance.</p><p>Actions (gitea’s implementation) has me excited because it makes spinning up a network-isolated environment for workflow automation incredibly simple.</p><h2 id=integration-with-tailscale>Integration with Tailscale<a href=#integration-with-tailscale class=post-heading__anchor aria-hidden=true>#</a></h2><p>So how does Tailscale help here? Well, more recently I’ve been exposing my self-hosted services through a combination of traefik and the tailscale (through the tailscale-traefik proxy integration described <a href=https://traefik.io/blog/exploring-the-tailscale-traefik-proxy-integration/ class=link--external target=_blank rel=noreferrer>here</a>). This allows for a nice looking dns name (i.e. gitea.my-tailnet-name.ts.net) and automatic tls certificate management. I can also share this tailscale node securely with other tailscale users without configuring any firewall rules on my router.</p><h2 id=deploying-gitea-traefik-and-tailscale>Deploying Gitea, Traefik, and Tailscale<a href=#deploying-gitea-traefik-and-tailscale class=post-heading__anchor aria-hidden=true>#</a></h2><p>In my case, the following is already set up:</p><ul><li><a href=https://docs.docker.com/compose/install/linux/ class=link--external target=_blank rel=noreferrer>docker-compose is installed</a></li><li><a href=https://tailscale.com/kb/1017/install/ class=link--external target=_blank rel=noreferrer>tailscale is installed on the gitea host</a></li><li><a href=https://tailscale.com/kb/1081/magicdns/ class=link--external target=_blank rel=noreferrer>tailscale magic dns is enabled</a></li></ul><p>My preferred approach to deploying code in a homelab environment is with docker compose. I have deployed this in a <a href=https://pve.proxmox.com/wiki/Linux_Container class=link--external target=_blank rel=noreferrer>proxmox lxc container</a> based on debian with a hostname <code>gitea</code>. This could be deployed in any environment and with any hostname (as long you updated the tailscale machine name to your preferred subdomain for magic dns).</p><p>The <code>docker-compose.yaml</code> file looks like:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#f92672>version</span>: <span style=color:#e6db74>"3.7"</span>
|
|
</span></span><span style=display:flex><span><span style=color:#f92672>services</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>gitea</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>image</span>: <span style=color:#ae81ff>gitea/gitea:1.21.1</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>container_name</span>: <span style=color:#ae81ff>gitea</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>environment</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>USER_UID=1000</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>USER_GID=1000</span>
|
|
</span></span><span style=display:flex><span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>GITEA__server__DOMAIN=gitea.my-tailnet-name.ts.net</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>GITEA__server__ROOT_URL=https://gitea.my-tailnet-name.ts.net</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>GITEA__server__HTTP_ADDR=0.0.0.0</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>GITEA__server__LFS_JWT_SECRET=my-secret-jwt</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>restart</span>: <span style=color:#ae81ff>always</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>volumes</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>./data:/data</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>/etc/timezone:/etc/timezone:ro</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>/etc/localtime:/etc/localtime:ro</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>traefik</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>image</span>: <span style=color:#ae81ff>traefik:v3.0.0-beta4</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>container_name</span>: <span style=color:#ae81ff>traefik</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>security_opt</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#66d9ef>no</span>-<span style=color:#ae81ff>new-privileges:true</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>restart</span>: <span style=color:#ae81ff>unless-stopped</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>ports</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>80</span>:<span style=color:#ae81ff>80</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>443</span>:<span style=color:#ae81ff>443</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>volumes</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>./traefik/data/traefik.yaml:/traefik.yaml:ro</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>./traefik/data/dynamic.yaml:/dynamic.yaml:ro</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>/var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock</span>
|
|
</span></span></code></pre></div><p><code>traefik/data/traefik.yaml</code>:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#f92672>entryPoints</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>https</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>address</span>: <span style=color:#e6db74>":443"</span>
|
|
</span></span><span style=display:flex><span><span style=color:#f92672>providers</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>file</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>filename</span>: <span style=color:#ae81ff>dynamic.yaml</span>
|
|
</span></span><span style=display:flex><span><span style=color:#f92672>certificatesResolvers</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>myresolver</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>tailscale</span>: {}
|
|
</span></span><span style=display:flex><span><span style=color:#f92672>log</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>level</span>: <span style=color:#ae81ff>INFO</span>
|
|
</span></span></code></pre></div><p>and finally <code>traefik/data/dynamic/dynamic.yaml</code>:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#f92672>http</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>routers</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>gitea</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>rule</span>: <span style=color:#ae81ff>Host(`gitea.my-tailnet-name.ts.net`)</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>entrypoints</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#e6db74>"https"</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>service</span>: <span style=color:#ae81ff>gitea</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>tls</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>certResolver</span>: <span style=color:#ae81ff>myresolver</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>services</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>gitea</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>loadBalancer</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>servers</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#f92672>url</span>: <span style=color:#e6db74>"http://gitea:3000"</span>
|
|
</span></span></code></pre></div><p>Something to consider is whether or not you want to use ssh with git. One method to get this to work with containers is to use <a href=https://docs.gitea.com/installation/install-with-docker#ssh-container-passthrough class=link--external target=_blank rel=noreferrer>ssh container passthrough</a>. I decided to keep it simple and not use ssh, since communicating over https is perfectly fine for my use case.</p><p>After adding the above configuration, running <code>docker compose up -d</code> should be enough to get an instance up and running. It will be accessible at <a href=https://gitea.my-tailnet-name.ts.net class=link--external target=_blank rel=noreferrer>https://gitea.my-tailnet-name.ts.net</a> from within the tailnet.</p><h2 id=connecting-a-runner>Connecting a Runner<a href=#connecting-a-runner class=post-heading__anchor aria-hidden=true>#</a></h2><p>I installed the runner by <a href=https://docs.gitea.com/usage/actions/quickstart#set-up-runner class=link--external target=_blank rel=noreferrer>following the docs</a>. I opted for installing it on a separate host (another lxc container) as recommended in the docs. I used the systemd unit file to ensure that the runner comes back online after system reboots. I installed tailscale on this gitea runner as well, so that it can have the same “networking privileges” as the main instance.</p><p>After registering this runner and starting the daemon, it appeared in <code>/admin/actions/runners</code>:</p><p><picture><source type=image/webp srcset="/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-runners_hub2705fca9eca2ad49032b5d26e38ba63_36278_300x0_resize_q75_h2_box_3.webp 300w,/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-runners_hub2705fca9eca2ad49032b5d26e38ba63_36278_500x0_resize_q75_h2_box_3.webp 500w,/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-runners_hub2705fca9eca2ad49032b5d26e38ba63_36278_700x0_resize_q75_h2_box_3.webp 700w" sizes="(max-width: 499px) 300px,(max-width: 699px) 500px,700px"><img src=/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-runners.png srcset="/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-runners_hub2705fca9eca2ad49032b5d26e38ba63_36278_300x0_resize_box_3.png 300w,/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-runners_hub2705fca9eca2ad49032b5d26e38ba63_36278_500x0_resize_box_3.png 500w,/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-runners_hub2705fca9eca2ad49032b5d26e38ba63_36278_700x0_resize_box_3.png 700w" sizes="(max-width: 499px) 300px,(max-width: 699px) 500px,700px" alt=image loading=lazy width=972 height=330></picture></p><h2 id=running-a-workflow>Running a workflow<a href=#running-a-workflow class=post-heading__anchor aria-hidden=true>#</a></h2><p>Now it’s time start running some automation. I used the <a href=https://docs.gitea.com/usage/actions/quickstart#use-actions class=link--external target=_blank rel=noreferrer>demo workflow</a> as a starting point to verify that the runner is executing workflows.</p><p>After this, I wanted to make sure that some of my existing workflows could be migrated over.</p><p>The following workflow uses a matrix to run a job for several of my hosts using ansible playbooks that will do various tasks such as patching os updates and updating container images.</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#f92672>name</span>: <span style=color:#ae81ff>Run ansible</span>
|
|
</span></span><span style=display:flex><span><span style=color:#f92672>on</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>push</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>schedule</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#f92672>cron</span>: <span style=color:#e6db74>"0 */12 * * *"</span>
|
|
</span></span><span style=display:flex><span>
|
|
</span></span><span style=display:flex><span><span style=color:#f92672>jobs</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>run-ansible-playbook</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>runs-on</span>: <span style=color:#ae81ff>ubuntu-latest</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>strategy</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>matrix</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>host</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>changedetection</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>homelab</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>invidious</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>jackett</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>ladder</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>miniflux</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>plex</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>qbittorrent</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>tailscale-exit-node</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>uptime-kuma</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>steps</span>:
|
|
</span></span><span style=display:flex><span> - <span style=color:#f92672>name</span>: <span style=color:#ae81ff>Check out repository code</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>uses</span>: <span style=color:#ae81ff>actions/checkout@v4</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#f92672>name</span>: <span style=color:#ae81ff>Install ansible</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>run</span>: |<span style=color:#e6db74>
|
|
</span></span></span><span style=display:flex><span><span style=color:#e6db74> apt update && apt install ansible -y</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#f92672>name</span>: <span style=color:#ae81ff>Run playbook</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>uses</span>: <span style=color:#ae81ff>dawidd6/action-ansible-playbook@v2</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>with</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>playbook</span>: <span style=color:#ae81ff>playbooks/main.yml</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>requirements</span>: <span style=color:#ae81ff>requirements.yml</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>options</span>: |<span style=color:#e6db74>
|
|
</span></span></span><span style=display:flex><span><span style=color:#e6db74> --inventory inventory
|
|
</span></span></span><span style=display:flex><span><span style=color:#e6db74> --limit ${{ matrix.host }}</span>
|
|
</span></span><span style=display:flex><span> - <span style=color:#f92672>name</span>: <span style=color:#ae81ff>Send failure notification</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>uses</span>: <span style=color:#ae81ff>dawidd6/action-send-mail@v3</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>if</span>: <span style=color:#ae81ff>always() && failure()</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>with</span>:
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>server_address</span>: <span style=color:#ae81ff>smtp.gmail.com</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>server_port</span>: <span style=color:#ae81ff>465</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>secure</span>: <span style=color:#66d9ef>true</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>username</span>: <span style=color:#ae81ff>myuser</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>password</span>: <span style=color:#ae81ff>${{ secrets.MAIL_PASSWORD }}</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>subject</span>: <span style=color:#ae81ff>ansible runbook '${{ matrix.host }}' failed</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>to</span>: <span style=color:#ae81ff>me@davegallant.ca</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>from</span>: <span style=color:#ae81ff>RFD Notify</span>
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>body</span>: |<span style=color:#e6db74>
|
|
</span></span></span><span style=display:flex><span><span style=color:#e6db74> ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}</span>
|
|
</span></span></code></pre></div><p>And voilà:</p><video controls poster=/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-runners.png playsinline><source src=/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-workflow.webm type=video/webm></video><p>You may be wondering how the gitea runner is allowed to connect to the other hosts using ansible? Well, the nodes are in the same tailnet and have <a href=https://tailscale.com/tailscale-ssh class=link--external target=_blank rel=noreferrer>tailscale ssh</a> enabled.</p><h2 id=areas-for-improvement>Areas for improvement<a href=#areas-for-improvement class=post-heading__anchor aria-hidden=true>#</a></h2><p>One enhancement that I would like to see is the ability to send notifications on workflow failures. Currently, this <a href=https://github.com/go-gitea/gitea/issues/23725 class=link--external target=_blank rel=noreferrer>doesn’t seem possible</a> without adding logic to each workflow.</p><h2 id=conclusion>Conclusion<a href=#conclusion class=post-heading__anchor aria-hidden=true>#</a></h2><p>Gitea Actions are fast and the resource footprint is minimal. My gitea instance is currently using around 250mb of memory and a small fraction of a single cpu core (and the runner is using a similar amount of resources). This is impressive since many alternatives tend to require substantially more resources. It likely helps that the codebase is largely written in go.</p><p>By combining gitea with the networking marvel that is tailscale, running workflows becomes simple and fun. Whether you are working on a team or working alone, this setup ensures that your workflows are securely accessible from anywhere with an internet connection.</p></div><script type=text/javascript src=https://storage.ko-fi.com/cdn/widget/Widget_2.js></script><script type=text/javascript>kofiwidget2.init("Buy me a coffee","#458588","F1F2S4LWI"),kofiwidget2.draw()</script><section id=comments class=comments><div class='container sep-before'><div class=comments><script>var getTheme=window.localStorage&&window.localStorage.getItem("theme"),getTheme=getTheme??"dark";let theme=getTheme==="dark"?"gruvbox-dark":"github-light",s=document.createElement("script");s.src="https://utteranc.es/client.js",s.setAttribute("repo","davegallant/davegallant.github.io"),s.setAttribute("issue-term","pathname"),s.setAttribute("theme",theme),s.setAttribute("crossorigin","anonymous"),s.setAttribute("async",""),document.querySelector("div.comments").innerHTML="",document.querySelector("div.comments").appendChild(s)</script></div></div></section></article></div><div class=sidebar><aside class=bio><div class="jr__item jr-basics__item"><div class=jr-basics__name>Dave Gallant</div><div class=jr-basics__email>me@davegallant.ca</div><div class=jr-basics__summary>👋 I'm a software tinkerer with a passion for infra, security and self-hosting.</div><hr><div class="jr-basics__profile jr-basics__profile--row"><a href=https://linktr.ee/davegallant target=_blank rel="noreferrer me"><div class=jr-basics__profile-item><div class=jr-basics__profile-icon><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Linktree</title><path d="m13.73635 5.85251 4.00467-4.11665 2.3248 2.3808-4.20064 4.00466h5.9085v3.30473h-5.9365l4.22865 4.10766-2.3248 2.3338L12.0005 12.099l-5.74052 5.76852-2.3248-2.3248 4.22864-4.10766h-5.9375V8.12132h5.9085L3.93417 4.11666l2.3248-2.3808 4.00468 4.11665V0h3.4727zm-3.4727 10.30614h3.4727V24h-3.4727z"/></svg></div></div></a><a href=https://github.com/davegallant target=_blank rel="noreferrer me"><div class=jr-basics__profile-item><div class=jr-basics__profile-icon><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63.0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577.0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93.0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176.0.0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22.0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22.0 1.606-.015 2.896-.015 3.286.0.315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></div></div></a><a href=https://mastodon.social/@davegallant target=_blank rel="noreferrer me"><div class=jr-basics__profile-item><div class=jr-basics__profile-icon><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Mastodon</title><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792.0 11.813.0h-.03c-3.98.0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057.0 00.023-.043v-1.809a.052.052.0 00-.02-.041.053.053.0 00-.046-.01 20.282 20.282.0 01-4.709.545c-2.73.0-3.463-1.284-3.674-1.818a5.593 5.593.0 01-.319-1.433.053.053.0 01.066-.054c1.517.363 3.072.546 4.632.546.376.0.75.0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23.0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112.0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311.0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13.0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg></div></div></a><a href=https://www.linkedin.com/in/dave-gallant target=_blank rel="noreferrer me"><div class=jr-basics__profile-item><div class=jr-basics__profile-icon><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>LinkedIn</title><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853.0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601.0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144.0-2.063-.926-2.063-2.065.0-1.138.92-2.063 2.063-2.063 1.14.0 2.064.925 2.064 2.063.0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225.0H1.771C.792.0.0.774.0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2.0 22.222.0h.003z"/></svg></div></div></a></div></div></aside></div></main><footer><div class=copyright>Dave Gallant</div></footer><script src=/js/main.4be06c129d6a89e60a661c6ac8c8e0434d58fb0fa2f685f85e2c306aca62adc5e77e7c63cb1c8a2cc5794ea42927281cf868514bcdce21ddf23dc3520e6743e7.js integrity="sha512-S+BsEp1qieYKZhxqyMjgQ01Y+w+i9oX4XiwwaspircXnfnxjyxyKLMV5TqQpJygc+GhRS83OId3yPcNSDmdD5w=="></script><script src=/js/flexsearch.6008453bea2c3113a5612f78b88f04db99ba8fb4ce62b8ee2facd2970062f3f2cf949bebc2b610a40366d44598c9a453b7c6d502e4089844ce707f118ae649db.js integrity="sha512-YAhFO+osMROlYS94uI8E25m6j7TOYrjuL6zSlwBi8/LPlJvrwrYQpANm1EWYyaRTt8bVAuQImETOcH8RiuZJ2w=="></script></div></body></html> |