build: automation with Go and Mage

We shall now use Go and Mage to manage the Flow services. The templates
have been converted to Go templates, Mage has replaced Make and the
helper bash scripts have been rewritten in Go.
This commit is contained in:
Dan Anglin 2023-02-12 20:59:55 +00:00
parent 74d1638ce5
commit 3340ddc475
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
29 changed files with 689 additions and 358 deletions

View file

@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
SERVICE=$1
export $(cat ./config/flow-platform-services.cfg | grep DOCKER_HOST | tr -d "\"")
docker compose --project-directory ./build/compose up -d --build "${SERVICE}"

View file

@ -1,15 +0,0 @@
#!/usr/bin/env bash
#
set -o errexit
set -o nounset
set -o pipefail
SERVICE=$1
export $(xargs < ./config/flow-platform-services.cfg)
mkdir -p build/${SERVICE}
for i in $(find "./templates/${SERVICE}" -mindepth 1 -type f); do
file=$(basename ${i})
envsubst < "./templates/${SERVICE}/${file}" > "./build/${SERVICE}/${file}"
done

View file

@ -1,26 +0,0 @@
clean:
@find ./build -mindepth 1 -maxdepth 1 -not -iname *.gitkeep | xargs rm -rf
compose:
bash ./.helpers/render.sh compose
traefik-files: compose
bash ./.helpers/render.sh traefik
traefik: traefik-files
bash ./.helpers/deploy.sh traefik
forgejo-binary:
bash ./.helpers/download-forgejo.sh
forgejo-files: forgejo-binary compose
bash ./.helpers/render.sh forgejo
forgejo: forgejo-files
bash ./.helpers/deploy.sh forgejo
gotosocial-files: compose
bash ./.helpers/render.sh gotosocial
gotosocial: gotosocial-files
bash ./.helpers/deploy.sh gotosocial

2
config

@ -1 +1 @@
Subproject commit 214ea82fd352ea290d41ee42170db34f54a9ef7f Subproject commit b2d4b0b766802f32f70ed2e9c1775476b19b9b6a

5
go.mod Normal file
View file

@ -0,0 +1,5 @@
module flow/services
go 1.19
require github.com/magefile/mage v1.14.0

2
go.sum Normal file
View file

@ -0,0 +1,2 @@
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=

90
magefiles/config.go Normal file
View file

@ -0,0 +1,90 @@
//go:build mage
// +build mage
package main
import (
"encoding/json"
"fmt"
"os"
)
type config struct {
RootDomain string `json:"rootDomain"`
FlowGID int32 `json:"flowGID"`
DockerNetworkSubnet string `json:"dockerNetworkSubnet"`
DockerHost string `json:"dockerHost"`
Traefik traefikConfig `json:"traefik"`
Forgejo forgejoConfig `json:"forgejo"`
GoToSocial gotosocialConfig `json:"gotosocial"`
}
type traefikConfig struct {
Version string `json:"version"`
CheckNewVersion bool `json:"checkNewVersion"`
ExternalSSHPort int32 `json:"externalSSHPort"`
LogLevel string `json:"logLevel"`
SendAnonymousUsage bool `json:"sendAnonymousUsage"`
ContainerIpv4Address string `json:"containerIpv4Address"`
AcmeEmail string `json:"acmeEmail"`
SharedMountPoint string `json:"sharedMountPoint"`
TlsHostDirectory string `json:"tlsHostDirectory"`
TlsContainerDirectory string `json:"tlsContainerDirectory"`
PilotToken string `json:"pilotToken"`
}
type forgejoConfig struct {
Version string `json:"version"`
Name string `json:"name"`
Subdomain string `json:"subdomain"`
ContainerIpv4Address string `json:"containerIpv4Address"`
SshPort int32 `json:"sshPort"`
HttpPort int32 `json:"httpPort"`
RunMode string `json:"runMode"`
LogLevel string `json:"logLevel"`
LinuxUID int32 `json:"linuxUID"`
DataHostDirectory string `json:"dataHostDirectory"`
DataContainerDirectory string `json:"dataContainerDirectory"`
Home string `json:"home"`
Work string `json:"work"`
Custom string `json:"custom"`
AppIni string `json:"appIni"`
Bin string `json:"bin"`
Tmp string `json:"tmp"`
SecretHostDirectory string `json:"secretHostDirectory"`
SecretContainerDirectory string `json:"secretContainerDirectory"`
SecretKey string `json:"secretKey"`
InternalToken string `json:"internalToken"`
LfsJwtSecret string `json:"lfsJwtSecret"`
}
type gotosocialConfig struct {
Version string `json:"version"`
DockerImageDigest string `json:"dockerImageDigest"`
Name string `json:"name"`
LogLevel string `json:"logLevel"`
LinuxUID int32 `json:"linuxUID"`
Subdomain string `json:"subdomain"`
ContainerIpv4Address string `json:"containerIpv4Address"`
Port int32 `json:"port"`
DataHostDirectory string `json:"dataHostDirectory"`
DataContainerDirectory string `json:"dataContainerDirectory"`
}
func newConfig(path string) (config, error) {
var c config
f, err := os.Open(path)
if err != nil {
return c, fmt.Errorf("unable to open the file; %w", err)
}
defer f.Close()
decoder := json.NewDecoder(f)
if err = decoder.Decode(&c); err != nil {
return c, fmt.Errorf("unable to decode JSON data; %w", err)
}
return c, nil
}

View file

@ -0,0 +1,10 @@
{
"1.18.3-1": {
"binary": "https://codeberg.org/attachments/be5952ea-6cfb-4be5-a593-3564c4bd8cc9",
"signature": "https://codeberg.org/attachments/07685af6-ca06-4626-8028-302c83ee041c"
},
"1.18.3-0": {
"binary": "https://codeberg.org/attachments/af34fbfc-d651-41b1-aaff-2b9cc7134051",
"signature": "https://codeberg.org/attachments/f064c1a9-66f7-41a9-be03-4dc5e2298370"
}
}

View file

@ -0,0 +1,126 @@
//go:build mage
// +build mage
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"github.com/magefile/mage/sh"
)
type forgejoDownload map[string]map[string]string
const (
forgejoDownloadFileFormat string = "forgejo-%s-linux-amd64"
forgejoBinariesJson string = "./magefiles/data/forgejo.json"
)
func downloadForgejo(version string) error {
downloadFolder := filepath.Join(rootBuildDir, "forgejo")
if err := os.MkdirAll(downloadFolder, 0o750); err != nil {
return fmt.Errorf("unable to make %s; %w", downloadFolder, err)
}
binaryPath := filepath.Join(
downloadFolder,
fmt.Sprintf(forgejoDownloadFileFormat, version),
)
_, err := os.Stat(binaryPath)
if err == nil {
fmt.Printf("Forgejo %s is already downloaded.\n", version)
return nil
}
m, err := newForgejoDownloadMap()
if err != nil {
return err
}
binary, err := os.Create(binaryPath)
if err != nil {
return err
}
defer binary.Close()
client := http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
r.URL.Opaque = r.URL.Path
return nil
},
}
binaryURL := m[version]["binary"]
resp, err := client.Get(binaryURL)
if err != nil {
return err
}
defer resp.Body.Close()
size, err := io.Copy(binary, resp.Body)
if err != nil {
return err
}
fmt.Printf("Downloaded %s with size %d.\n", binaryPath, size)
signaturePath := binaryPath + ".asc"
signature, err := os.Create(signaturePath)
if err != nil {
return err
}
defer signature.Close()
signatureURL := m[version]["signature"]
sigResp, err := client.Get(signatureURL)
if err != nil {
return err
}
defer sigResp.Body.Close()
size, err = io.Copy(signature, sigResp.Body)
if err != nil {
return nil
}
fmt.Printf("Downloaded %s with size %d.\n", signaturePath, size)
if err = sh.Run(
"gpg",
"--verify",
signaturePath,
binaryPath,
); err != nil {
return fmt.Errorf("GPG verification failed; %w", err)
}
return nil
}
func newForgejoDownloadMap() (forgejoDownload, error) {
m := make(forgejoDownload)
f, err := os.Open(forgejoBinariesJson)
if err != nil {
return nil, err
}
defer f.Close()
decoder := json.NewDecoder(f)
if err = decoder.Decode(&m); err != nil {
return nil, err
}
return m, nil
}

91
magefiles/mage.go Normal file
View file

@ -0,0 +1,91 @@
//go:build mage
// +build mage
package main
import (
"fmt"
"os"
"github.com/magefile/mage/sh"
)
const (
configFile string = "./config/services.json"
rootBuildDir string = "./build"
templateExtension string = ".gotmpl"
rootTemplatesDir string = "./templates"
)
// Clean cleans the workspace.
func Clean() error {
buildDir := "./build"
objects, err := os.ReadDir(buildDir)
if err != nil {
return err
}
for i := range objects {
name := objects[i].Name()
if name != ".gitkeep" {
if err := sh.Rm(buildDir + "/" + name); err != nil {
return err
}
}
}
return nil
}
// Render renders the template files.
func Render(name string) error {
cfg, err := newConfig(configFile)
if err != nil {
return fmt.Errorf("unable to load the configuration; %v", err)
}
if err := render(cfg, name); err != nil {
return fmt.Errorf("an error occurred whilst rendering the templates; %v", err)
}
return nil
}
// Deploy deploys the services to the Flow Platform.
func Deploy(name string) error {
cfg, err := newConfig(configFile)
if err != nil {
return fmt.Errorf("unable to load the configuration; %v", err)
}
os.Setenv("DOCKER_HOST", cfg.DockerHost)
return sh.Run(
"docker",
"compose",
"--project-directory",
rootBuildDir+"/compose",
"up",
"-d",
"--build",
name,
)
}
// DownloadForgejo downloads the Forgejo binary from Codeberg.
func DownloadForgejo() error {
cfg, err := newConfig(configFile)
if err != nil {
return fmt.Errorf("unable to load the configuration; %v", err)
}
version := cfg.Forgejo.Version
if err := downloadForgejo(version); err != nil {
return fmt.Errorf("unable to download Forgejo %s; %w", version, err)
}
return nil
}

62
magefiles/render.go Normal file
View file

@ -0,0 +1,62 @@
//go:build mage
// +build mage
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
)
func render(cfg config, component string) error {
buildDirName := filepath.Join(rootBuildDir, component)
if err := os.MkdirAll(buildDirName, 0o750); err != nil {
return fmt.Errorf("unable to make %s; %w", buildDirName, err)
}
templateDirName := filepath.Join(rootTemplatesDir, component)
files, err := os.ReadDir(templateDirName)
if err != nil {
return fmt.Errorf("unable to read files from %s; %w ", templateDirName, err)
}
for _, f := range files {
err := func() error {
templateFilename := f.Name()
if f.IsDir() || !strings.HasSuffix(templateFilename, templateExtension) {
return nil
}
outputFilename := strings.TrimSuffix(templateFilename, templateExtension)
outputPath := filepath.Join(buildDirName, outputFilename)
file, err := os.Create(outputPath)
if err != nil {
return fmt.Errorf("unable to create the file '%s'; %w", outputPath, err)
}
defer file.Close()
templatePath := filepath.Join(templateDirName, templateFilename)
tmpl, err := template.New(templateFilename).ParseFiles(templatePath)
if err != nil {
return fmt.Errorf("unable to create a new template value from '%s'; %w", templateFilename, err)
}
if err = tmpl.Execute(file, cfg); err != nil {
return fmt.Errorf("unable to render the template to '%s'; %w", outputPath, err)
}
return nil
}()
if err != nil {
return fmt.Errorf("an error occurred whilst rendering the templates for '%s'; %w", component, err)
}
}
return nil
}

View file

@ -1,3 +1,13 @@
{{- define "defaultVolumes" }}
- type: "bind"
source: "/etc/timezone"
target: "/etc/timezone"
read_only: true
- type: "bind"
source: "/etc/localtime"
target: "/etc/localtime"
read_only: true
{{- end -}}
--- ---
version: "3.9" version: "3.9"
@ -7,7 +17,7 @@ networks:
ipam: ipam:
driver: "default" driver: "default"
config: config:
- subnet: "${NETWORK_FORGE_FLOW_SUBNET}" - subnet: "{{ .DockerNetworkSubnet }}"
volumes: volumes:
traefik-shared: traefik-shared:
@ -17,12 +27,12 @@ services:
# -- Traffic flow -- # -- Traffic flow --
traefik: traefik:
container_name: "traffic-flow" container_name: "traffic-flow"
image: localhost/flow/traefik:${TRAEFIK_VERSION} image: "localhost/flow/traefik:{{ .Traefik.Version }}"
build: build:
context: "../traefik" context: "../traefik"
networks: networks:
flow: flow:
ipv4_address: "${TRAEFIK_CONTAINER_IPV4_ADDRESS}" ipv4_address: "{{ .Traefik.ContainerIpv4Address }}"
ports: ports:
- target: 80 - target: 80
published: 80 published: 80
@ -32,81 +42,63 @@ services:
published: 443 published: 443
protocol: "tcp" protocol: "tcp"
mode: "host" mode: "host"
- target: ${TRAEFIK_EXTERNAL_SSH_PORT} - target: {{ .Traefik.ExternalSSHPort }}
published: ${TRAEFIK_EXTERNAL_SSH_PORT} published: {{ .Traefik.ExternalSSHPort }}
protocol: "tcp" protocol: "tcp"
mode: "host" mode: "host"
restart: "always" restart: "always"
volumes: volumes:
{{- template "defaultVolumes" }}
# Shared volume
- type: "volume" - type: "volume"
source: "traefik-shared" source: "traefik-shared"
target: "${TRAEFIK_SHARED_MOUNT_POINT}" target: "{{ .Traefik.SharedMountPoint }}"
- type: "bind"
source: "/etc/timezone"
target: "/etc/timezone"
read_only: true
- type: "bind"
source: "/etc/localtime"
target: "/etc/localtime"
read_only: true
# Traefik TLS volume # Traefik TLS volume
- type: "bind" - type: "bind"
source: "${TRAEFIK_TLS_HOST_DIR}" source: "{{ .Traefik.TlsHostDirectory }}"
target: "${TRAEFIK_TLS_CONTAINER_DIR}" target: "{{ .Traefik.TlsContainerDirectory }}"
# -- Code flow -- # -- Code flow --
forgejo: forgejo:
container_name: "code-flow" container_name: "code-flow"
image: localhost/flow/forgejo:${FORGEJO_VERSION} image: "localhost/flow/forgejo:{{ .Forgejo.Version }}"
build: build:
context: "../forgejo" context: "../forgejo"
expose: expose:
- "${FORGEJO_SSH_PORT}" - "{{ .Forgejo.SshPort }}"
- "${FORGEJO_HTTP_PORT}" - "{{ .Forgejo.HttpPort }}"
networks: networks:
flow: flow:
ipv4_address: "${FORGEJO_CONTAINER_IPV4_ADDRESS}" ipv4_address: "{{ .Forgejo.ContainerIpv4Address }}"
restart: "always" restart: "always"
volumes: volumes:
{{- template "defaultVolumes" }}
# Shared volume
- type: "volume" - type: "volume"
source: "traefik-shared" source: "traefik-shared"
target: "${TRAEFIK_SHARED_MOUNT_POINT}" target: "{{ .Traefik.SharedMountPoint }}"
# Forgejo data volume
- type: "bind" - type: "bind"
source: "/etc/timezone" source: "{{ .Forgejo.DataHostDirectory }}"
target: "/etc/timezone" target: "{{ .Forgejo.DataContainerDirectory }}"
read_only: true
- type: "bind"
source: "/etc/localtime"
target: "/etc/localtime"
read_only: true
# ForgeJo data volume
- type: "bind"
source: "${FORGEJO_DATA_HOST_DIR}"
target: "${FORGEJO_DATA_CONTAINER_DIR}"
# -- Free Flow 2 -- # -- Free Flow 2 --
gotosocial: gotosocial:
container_name: "free-flow" container_name: "free-flow"
image: localhost/flow/gotosocial:${GTS_VERSION} image: "localhost/flow/gotosocial:{{ .GoToSocial.Version }}"
build: build:
context: "../gotosocial" context: "../gotosocial"
expose: expose:
- "${GTS_PORT}" - "{{ .GoToSocial.Port }}"
networks: networks:
flow: flow:
ipv4_address: "${GTS_CONTAINER_IPV4_ADDRESS}" ipv4_address: "{{ .GoToSocial.ContainerIpv4Address }}"
restart: "always" restart: "always"
volumes: volumes:
{{- template "defaultVolumes" }}
# Shared volume
- type: "volume" - type: "volume"
source: "traefik-shared" source: "traefik-shared"
target: "${TRAEFIK_SHARED_MOUNT_POINT}" target: "{{ .Traefik.SharedMountPoint }}"
- type: "bind"
source: "/etc/timezone"
target: "/etc/timezone"
read_only: true
- type: "bind"
source: "/etc/localtime"
target: "/etc/localtime"
read_only: true
# Go To Social data volume # Go To Social data volume
- type: "bind" - type: "bind"
source: "${GTS_DATA_HOST_DIR}" source: "{{ .GoToSocial.DataHostDirectory }}"
target: "${GTS_DATA_CONTAINER_DIR}" target: "{{ .GoToSocial.DataContainerDirectory }}"

View file

@ -1,44 +0,0 @@
# This is a custom made Dockerfile for Gitea which is inspired from
# the official Dockerfile.rootless from https://github.com/go-gitea/gitea/
FROM alpine:3.17
ENV FORGEJO_WORK_DIR=${FORGEJO_WORK_DIR} \
FORGEJO_CUSTOM=${FORGEJO_CUSTOM} \
FORGEJO_APP_INI=${FORGEJO_APP_INI} \
FORGEJO_BIN=${FORGEJO_BIN} \
FORGEJO_VERSION=${FORGEJO_VERSION} \
HOME=${FORGEJO_HOME}
RUN apk update && apk upgrade && apk --no-cache add \
bash \
ca-certificates \
gettext \
git \
curl \
gnupg \
openssh-keygen \
&& addgroup -S -g ${FLOW_GID} flow \
&& adduser -S -H -D -h ${FORGEJO_HOME} -s /bin/bash -u ${FORGEJO_FLOW_UID} -G flow git \
&& mkdir -p ${FORGEJO_DATA_CONTAINER_DIR} ${FORGEJO_TMP} \
&& chown git ${FORGEJO_DATA_CONTAINER_DIR} && chmod 0700 ${FORGEJO_DATA_CONTAINER_DIR} \
&& chown git ${FORGEJO_TMP} && chmod 0700 ${FORGEJO_TMP}
ADD --chown=root:root forgejo-${FORGEJO_VERSION}-linux-amd64 ${FORGEJO_BIN}
ADD app.ini ${FORGEJO_APP_INI}
ADD entrypoint.sh /usr/local/bin/entrypoint.sh
ADD --chown=${FORGEJO_FLOW_UID}:${FLOW_GID} dynamic_git.yaml ${FORGEJO_TMP}/
RUN chown -R ${FORGEJO_FLOW_UID}:${FORGEJO_FLOW_UID} ${FORGEJO_APP_INI} \
&& chmod 0400 ${FORGEJO_APP_INI} \
&& chmod a+x ${FORGEJO_BIN} \
&& chmod a+rx /usr/local/bin/entrypoint.sh
USER ${FORGEJO_FLOW_UID}:${FLOW_GID}
WORKDIR /flow/gitea/data
VOLUME ["/flow/gitea/data"]
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD []

View file

@ -0,0 +1,44 @@
# This is a custom made Dockerfile for Gitea which is inspired from
# the official Dockerfile.rootless from https://github.com/go-gitea/gitea/
FROM alpine:3.17
ENV FORGEJO_WORK_DIR={{ .Forgejo.Work }} \
FORGEJO_CUSTOM={{ .Forgejo.Custom }} \
FORGEJO_APP_INI={{ .Forgejo.AppIni }} \
FORGEJO_BIN={{ .Forgejo.Bin }} \
FORGEJO_VERSION={{ .Forgejo.Version }} \
HOME={{ .Forgejo.Home }}
RUN apk update && apk upgrade && apk --no-cache add \
bash \
ca-certificates \
gettext \
git \
curl \
gnupg \
openssh-keygen \
&& addgroup -S -g {{ .FlowGID }} flow \
&& adduser -S -H -D -h {{ .Forgejo.Home }} -s /bin/bash -u {{ .Forgejo.LinuxUID }} -G flow git \
&& mkdir -p {{ .Forgejo.DataContainerDirectory }} {{ .Forgejo.Tmp }} \
&& chown git {{ .Forgejo.DataContainerDirectory }} && chmod 0700 {{ .Forgejo.DataContainerDirectory }} \
&& chown git {{ .Forgejo.Tmp }} && chmod 0700 {{ .Forgejo.Tmp }}
ADD --chown=root:root forgejo-{{ .Forgejo.Version }}-linux-amd64 {{ .Forgejo.Bin }}
ADD app.ini {{ .Forgejo.AppIni }}
ADD entrypoint.sh /usr/local/bin/entrypoint.sh
ADD --chown={{ .Forgejo.LinuxUID }}:{{ .FlowGID }} dynamic_git.yaml {{ .Forgejo.Tmp }}/
RUN chown -R {{ .Forgejo.LinuxUID }}:{{ .Forgejo.LinuxUID }} {{ .Forgejo.AppIni }} \
&& chmod 0400 {{ .Forgejo.AppIni }} \
&& chmod a+x {{ .Forgejo.Bin }} \
&& chmod a+rx /usr/local/bin/entrypoint.sh
USER {{ .Forgejo.LinuxUID }}:{{ .FlowGID }}
WORKDIR /flow/gitea/data
VOLUME ["/flow/gitea/data"]
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD []

View file

@ -1,112 +0,0 @@
APP_NAME = "${FORGEJO_APP_NAME_01} ${FORGEJO_APP_NAME_02}"
RUN_USER = git
RUN_MODE = ${FORGEJO_RUN_MODE}
[repository]
ROOT = ${FORGEJO_DATA_CONTAINER_DIR}/git/repositories
DEFAULT_BRANCH = main
[repository.local]
LOCAL_COPY_PATH = ${FORGEJO_TMP}/local-repo
[repository.upload]
TEMP_PATH = ${FORGEJO_TMP}/uploads
[repository.signing]
INITIAL_COMMIT = pubkey, twofa
MERGES = pubkey, twofa, basesigned, commitssigned
[ui]
DEFAULT_THEME = forgejo-dark
[server]
APP_DATA_PATH = ${FORGEJO_DATA_CONTAINER_DIR}/git
DOMAIN = ${FORGEJO_DOMAIN}
HTTP_ADDR = ${FORGEJO_CONTAINER_IPV4_ADDRESS}
HTTP_PORT = ${FORGEJO_HTTP_PORT}
ROOT_URL = https://${FORGEJO_DOMAIN}
DISABLE_SSH = false
START_SSH_SERVER = true
SSH_DOMAIN = ${FORGEJO_DOMAIN}
SSH_PORT = ${TRAEFIK_EXTERNAL_SSH_PORT}
SSH_LISTEN_HOST = ${FORGEJO_CONTAINER_IPV4_ADDRESS}
SSH_LISTEN_PORT = ${FORGEJO_SSH_PORT}
BUILTIN_SSH_SERVER_USER = git
LFS_START_SERVER = true
LFS_JWT_SECRET = ${FORGEJO_LFS_JWT_SECRET}
[lfs]
STORAGE_TYPE = local
PATH = ${FORGEJO_DATA_CONTAINER_DIR}/git/lfs
[ssh.minimum_key_sizes]
ED25519 = 256
ECDSA = 256
RSA = 4096
DSA = -1
[database]
DB_TYPE = sqlite3
PATH = ${FORGEJO_DATA_CONTAINER_DIR}/database/gitea.db
HOST = localhost:3306
NAME = gitea
USER = gitea
PASSWD =
[indexer]
ISSUE_INDEXER_PATH = ${FORGEJO_DATA_CONTAINER_DIR}/indexers/issues.bleve
[session]
PROVIDER_CONFIG = ${FORGEJO_DATA_CONTAINER_DIR}/sessions
[queue]
DATADIR = ${FORGEJO_DATA_CONTAINER_DIR}/queues
[admin]
DISABLE_REGULAR_ORG_CREATION = true
DEFAULT_EMAIL_NOTIFICATION = disabled
[security]
INSTALL_LOCK = true
SECRET_KEY = ${FORGEJO_SECRET_KEY}
INTERNAL_TOKEN = ${FORGEJO_INTERNAL_TOKEN}
LOGIN_REMEMBER_DAYS = 1
MIN_PASSWORD_LENGTH = 16
PASSWORD_COMPLEXITY = lower,upper,digit
[service]
DISABLE_REGISTRATION = true
[service.explore]
REQUIRE_SIGNIN_VIEW = false
[picture]
AVATAR_UPLOAD_PATH = ${FORGEJO_DATA_CONTAINER_DIR}/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = ${FORGEJO_DATA_CONTAINER_DIR}/repo-avatars
[attachment]
ENABLED = true
PATH = ${FORGEJO_DATA_CONTAINER_DIR}/attachments
[log]
ROOT_PATH = ${FORGEJO_DATA_CONTAINER_DIR}/log
MODE = console
LEVEL = ${FORGEJO_LOG_LEVEL}
[log.console]
STDERR = false
[i18n]
LANGS = en-US
NAMES = English
[other]
SHOW_FOOTER_BRANDING = true
SHOW_FOOTER_VERSION = false
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
[oauth2]
ENABLE = false
[federation]
ENABLED = true

View file

@ -0,0 +1,112 @@
APP_NAME = "{{ .Forgejo.Name }}"
RUN_USER = git
RUN_MODE = {{ .Forgejo.RunMode }}
[repository]
ROOT = {{ .Forgejo.DataContainerDirectory }}/git/repositories
DEFAULT_BRANCH = main
[repository.local]
LOCAL_COPY_PATH = {{ .Forgejo.Tmp }}/local-repo
[repository.upload]
TEMP_PATH = {{ .Forgejo.Tmp }}/uploads
[repository.signing]
INITIAL_COMMIT = pubkey, twofa
MERGES = pubkey, twofa, basesigned, commitssigned
[ui]
DEFAULT_THEME = forgejo-dark
[server]
APP_DATA_PATH = {{ .Forgejo.DataContainerDirectory }}/git
DOMAIN = {{ .Forgejo.Subdomain }}.{{ .RootDomain }}
HTTP_ADDR = {{ .Forgejo.ContainerIpv4Address }}
HTTP_PORT = {{ .Forgejo.HttpPort }}
ROOT_URL = https://{{ .Forgejo.Subdomain }}.{{ .RootDomain }}
DISABLE_SSH = false
START_SSH_SERVER = true
SSH_DOMAIN = {{ .Forgejo.Subdomain }}.{{ .RootDomain }}
SSH_PORT = {{ .Traefik.ExternalSSHPort }}
SSH_LISTEN_HOST = {{ .Forgejo.ContainerIpv4Address }}
SSH_LISTEN_PORT = {{ .Forgejo.SshPort }}
BUILTIN_SSH_SERVER_USER = git
LFS_START_SERVER = true
LFS_JWT_SECRET = {{ .Forgejo.LfsJwtSecret }}
[lfs]
STORAGE_TYPE = local
PATH = {{ .Forgejo.DataContainerDirectory }}/git/lfs
[ssh.minimum_key_sizes]
ED25519 = 256
ECDSA = 256
RSA = 4096
DSA = -1
[database]
DB_TYPE = sqlite3
PATH = {{ .Forgejo.DataContainerDirectory }}/database/gitea.db
HOST = localhost:3306
NAME = gitea
USER = gitea
PASSWD =
[indexer]
ISSUE_INDEXER_PATH = {{ .Forgejo.DataContainerDirectory }}/indexers/issues.bleve
[session]
PROVIDER_CONFIG = {{ .Forgejo.DataContainerDirectory }}/sessions
[queue]
DATADIR = {{ .Forgejo.DataContainerDirectory }}/queues
[admin]
DISABLE_REGULAR_ORG_CREATION = true
DEFAULT_EMAIL_NOTIFICATION = disabled
[security]
INSTALL_LOCK = true
SECRET_KEY = {{ .Forgejo.SecretKey }}
INTERNAL_TOKEN = {{ .Forgejo.InternalToken }}
LOGIN_REMEMBER_DAYS = 1
MIN_PASSWORD_LENGTH = 16
PASSWORD_COMPLEXITY = lower,upper,digit
[service]
DISABLE_REGISTRATION = true
[service.explore]
REQUIRE_SIGNIN_VIEW = false
[picture]
AVATAR_UPLOAD_PATH = {{ .Forgejo.DataContainerDirectory }}/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = {{ .Forgejo.DataContainerDirectory }}/repo-avatars
[attachment]
ENABLED = true
PATH = {{ .Forgejo.DataContainerDirectory }}/attachments
[log]
ROOT_PATH = {{ .Forgejo.DataContainerDirectory }}/log
MODE = console
LEVEL = {{ .Forgejo.LogLevel }}
[log.console]
STDERR = false
[i18n]
LANGS = en-US
NAMES = English
[other]
SHOW_FOOTER_BRANDING = true
SHOW_FOOTER_VERSION = false
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
[oauth2]
ENABLE = false
[federation]
ENABLED = true

View file

@ -4,7 +4,7 @@ http:
gitea: gitea:
entryPoints: entryPoints:
- "https" - "https"
rule: "Host(`${FORGEJO_DOMAIN}`)" rule: "Host(`{{ .Forgejo.Subdomain }}.{{ .RootDomain }}`)"
service: "git" service: "git"
tls: tls:
certResolver: resolver certResolver: resolver
@ -12,7 +12,7 @@ http:
git: git:
loadBalancer: loadBalancer:
servers: servers:
- url: "http://${FORGEJO_CONTAINER_IPV4_ADDRESS}:${FORGEJO_HTTP_PORT}/" - url: "http://{{ .Forgejo.ContainerIpv4Address }}:{{ .Forgejo.HttpPort }}/"
tcp: tcp:
routers: routers:
@ -25,4 +25,4 @@ tcp:
gitSSH: gitSSH:
loadBalancer: loadBalancer:
servers: servers:
- address: "${FORGEJO_CONTAINER_IPV4_ADDRESS}:${FORGEJO_SSH_PORT}" - address: "{{ .Forgejo.ContainerIpv4Address }}:{{ .Forgejo.SshPort }}"

View file

@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Create the home directory.
if ! [ -d ${FORGEJO_HOME} ]; then
mkdir -p ${FORGEJO_HOME}
chmod 0700 ${FORGEJO_HOME}
fi
# Create the custom directory.
if ! [ -d ${FORGEJO_CUSTOM} ]; then
mkdir -p ${FORGEJO_CUSTOM}
chmod 0500 ${FORGEJO_CUSTOM}
fi
# Move the dynamic Traefik config to the shared volume.
if [ -f /flow/gitea/tmp/dynamic_git.yaml ]; then
mv /flow/gitea/tmp/dynamic_git.yaml ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic/dynamic_git.yaml
fi
if [ $# -gt 0 ]; then
exec "$@"
else
exec ${FORGEJO_BIN} -c ${FORGEJO_APP_INI} web
fi

View file

@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
# Create the home directory.
if ! [ -d {{ .Forgejo.Home }} ]; then
mkdir -p {{ .Forgejo.Home }}
chmod 0700 {{ .Forgejo.Home }}
fi
# Create the custom directory.
if ! [ -d {{ .Forgejo.Custom }} ]; then
mkdir -p {{ .Forgejo.Custom }}
chmod 0500 {{ .Forgejo.Custom }}
fi
# Move the dynamic Traefik config to the shared volume.
if [ -f /flow/gitea/tmp/dynamic_git.yaml ]; then
mv /flow/gitea/tmp/dynamic_git.yaml {{ .Traefik.SharedMountPoint }}/dynamic/dynamic_git.yaml
fi
if [ $# -gt 0 ]; then
exec "$@"
else
exec {{ .Forgejo.Bin }} -c {{ .Forgejo.AppIni }} web
fi

View file

@ -1,19 +0,0 @@
FROM superseriousbusiness/gotosocial:${GTS_VERSION}@${GTS_DOCKER_IMAGE_DIGEST}
USER 0
RUN apk update && apk upgrade && apk add bash \
&& addgroup -S -g ${FLOW_GID} flow \
&& adduser -S -H -D -s /bin/bash -u ${GTS_UID} -G flow gts \
&& chown -R ${GTS_UID}:${GTS_UID} /gotosocial \
&& mkdir -p /flow/gts/tmp /flow/gts/config && chown -R ${GTS_UID}:${GTS_UID} /flow/gts
COPY --chown=${GTS_UID}:${GTS_UID} entrypoint.sh /usr/local/bin/entrypoint.sh
COPY --chown=${GTS_UID}:${GTS_UID} config.yaml /flow/gts/config/config.yaml
COPY --chown=${GTS_UID}:${FLOW_GID} traefik_gotosocial.yaml /flow/gts/tmp/traefik_gotosocial.yaml
RUN chmod a+x /usr/local/bin/entrypoint.sh
USER ${GTS_UID}:${FLOW_GID}
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

View file

@ -0,0 +1,19 @@
FROM superseriousbusiness/gotosocial:{{ .GoToSocial.Version }}@{{ .GoToSocial.DockerImageDigest }}
USER 0
RUN apk update && apk upgrade && apk add bash \
&& addgroup -S -g {{ .FlowGID }} flow \
&& adduser -S -H -D -s /bin/bash -u {{ .GoToSocial.LinuxUID }} -G flow gts \
&& chown -R {{ .GoToSocial.LinuxUID }}:{{ .GoToSocial.LinuxUID }} /gotosocial \
&& mkdir -p /flow/gts/tmp /flow/gts/config && chown -R {{ .GoToSocial.LinuxUID }}:{{ .GoToSocial.LinuxUID }} /flow/gts
COPY --chown={{ .GoToSocial.LinuxUID }}:{{ .GoToSocial.LinuxUID }} entrypoint.sh /usr/local/bin/entrypoint.sh
COPY --chown={{ .GoToSocial.LinuxUID }}:{{ .GoToSocial.LinuxUID }} config.yaml /flow/gts/config/config.yaml
COPY --chown={{ .GoToSocial.LinuxUID }}:{{ .FlowGID }} traefik_gotosocial.yaml /flow/gts/tmp/traefik_gotosocial.yaml
RUN chmod a+x /usr/local/bin/entrypoint.sh
USER {{ .GoToSocial.LinuxUID }}:{{ .FlowGID }}
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

View file

@ -21,7 +21,7 @@
# String. Log level to use throughout the application. Must be lower-case. # String. Log level to use throughout the application. Must be lower-case.
# Options: ["trace","debug","info","warn","error","fatal"] # Options: ["trace","debug","info","warn","error","fatal"]
# Default: "info" # Default: "info"
log-level: "${GTS_LOG_LEVEL}" log-level: "{{ .GoToSocial.LogLevel }}"
# Bool. Log database queries when log-level is set to debug or trace. # Bool. Log database queries when log-level is set to debug or trace.
# This setting produces verbose logs, so it's better to only enable it # This setting produces verbose logs, so it's better to only enable it
@ -33,14 +33,14 @@ log-db-queries: false
# String. Application name to use internally. # String. Application name to use internally.
# Examples: ["My Application","gotosocial"] # Examples: ["My Application","gotosocial"]
# Default: "gotosocial" # Default: "gotosocial"
application-name: "${GTS_APPLICATION_NAME_01} ${GTS_APPLICATION_NAME_02}" application-name: "{{ .GoToSocial.Name }}"
# String. Hostname that this server will be reachable at. Defaults to localhost for local testing, # String. Hostname that this server will be reachable at. Defaults to localhost for local testing,
# but you should *definitely* change this when running for real, or your server won't work at all. # but you should *definitely* change this when running for real, or your server won't work at all.
# DO NOT change this after your server has already run once, or you will break things! # DO NOT change this after your server has already run once, or you will break things!
# Examples: ["gts.example.org","some.server.com"] # Examples: ["gts.example.org","some.server.com"]
# Default: "localhost" # Default: "localhost"
host: "${GTS_HOST}" host: "{{ .GoToSocial.Subdomain }}.{{ .RootDomain }}"
# String. Domain to use when federating profiles. This is useful when you want your server to be at # String. Domain to use when federating profiles. This is useful when you want your server to be at
# eg., "gts.example.org", but you want the domain on accounts to be "example.org" because it looks better # eg., "gts.example.org", but you want the domain on accounts to be "example.org" because it looks better
@ -69,7 +69,7 @@ protocol: "https"
# you have specific networking requirements. # you have specific networking requirements.
# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "[::]", "[2001:db8::fed1]"] # Examples: ["0.0.0.0", "172.128.0.16", "localhost", "[::]", "[2001:db8::fed1]"]
# Default: "0.0.0.0" # Default: "0.0.0.0"
bind-address: "${GTS_CONTAINER_IPV4_ADDRESS}" bind-address: "{{ .GoToSocial.ContainerIpv4Address }}"
# Int. Listen port for the GoToSocial webserver + API. If you're running behind a reverse proxy and/or in a docker, # Int. Listen port for the GoToSocial webserver + API. If you're running behind a reverse proxy and/or in a docker,
# container, just set this to whatever you like (or leave the default), and make sure it's forwarded properly. # container, just set this to whatever you like (or leave the default), and make sure it's forwarded properly.
@ -78,7 +78,7 @@ bind-address: "${GTS_CONTAINER_IPV4_ADDRESS}"
# This *MUST NOT* be the same as the letsencrypt port specified below, unless letsencrypt is turned off. # This *MUST NOT* be the same as the letsencrypt port specified below, unless letsencrypt is turned off.
# Examples: [443, 6666, 8080] # Examples: [443, 6666, 8080]
# Default: 8080 # Default: 8080
port: ${GTS_PORT} port: {{ .GoToSocial.Port }}
# Array of string. CIDRs or IP addresses of proxies that should be trusted when determining real client IP from behind a reverse proxy. # Array of string. CIDRs or IP addresses of proxies that should be trusted when determining real client IP from behind a reverse proxy.
# If you're running inside a Docker container behind Traefik or Nginx, for example, add the subnet of your docker network, # If you're running inside a Docker container behind Traefik or Nginx, for example, add the subnet of your docker network,
@ -86,7 +86,7 @@ port: ${GTS_PORT}
# Example: ["127.0.0.1/32", "172.20.0.1"] # Example: ["127.0.0.1/32", "172.20.0.1"]
# Default: ["127.0.0.1/32"] (localhost) # Default: ["127.0.0.1/32"] (localhost)
trusted-proxies: trusted-proxies:
- "${NETWORK_FORGE_FLOW_SUBNET}" - "{{ .DockerNetworkSubnet }}"
############################ ############################
##### DATABASE CONFIG ###### ##### DATABASE CONFIG ######
@ -112,7 +112,7 @@ db-type: "sqlite"
# #
# Examples: ["localhost","my.db.host","127.0.0.1","192.111.39.110",":memory:", "sqlite.db"] # Examples: ["localhost","my.db.host","127.0.0.1","192.111.39.110",":memory:", "sqlite.db"]
# Default: "" # Default: ""
db-address: "${GTS_DATA_CONTAINER_DIR}/database/gts.db" db-address: "{{ .GoToSocial.DataContainerDirectory }}/database/gts.db"
# Int. Port for database connection. # Int. Port for database connection.
# Examples: [5432, 1234, 6969] # Examples: [5432, 1234, 6969]
@ -306,7 +306,7 @@ storage-backend: "local"
# Only required when running with the local storage backend. # Only required when running with the local storage backend.
# Examples: ["/home/gotosocial/storage", "/opt/gotosocial/datastorage"] # Examples: ["/home/gotosocial/storage", "/opt/gotosocial/datastorage"]
# Default: "/gotosocial/storage" # Default: "/gotosocial/storage"
storage-local-base-path: "${GTS_DATA_CONTAINER_DIR}/storage" storage-local-base-path: "{{ .GoToSocial.DataContainerDirectory }}/storage"
# String. API endpoint of the S3 compatible service. # String. API endpoint of the S3 compatible service.
# Only required when running with the s3 storage backend. # Only required when running with the s3 storage backend.

View file

@ -1,12 +1,14 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -o errexit
set -o nounset
set -o pipefail
# Move the dynamic traefik config to the shared volume # Move the dynamic traefik config to the shared volume
if [ -f /flow/gts/tmp/traefik_gotosocial.yaml ]; then if [ -f /flow/gts/tmp/traefik_gotosocial.yaml ]; then
mv /flow/gts/tmp/traefik_gotosocial.yaml ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic/traefik_gotosocial.yaml mv /flow/gts/tmp/traefik_gotosocial.yaml {{ .Traefik.SharedMountPoint }}/dynamic/traefik_gotosocial.yaml
fi fi
mkdir -p ${GTS_DATA_CONTAINER_DIR}/database mkdir -p {{ .GoToSocial.DataContainerDirectory }}/database
exec /gotosocial/gotosocial --config-path /flow/gts/config/config.yaml server start exec /gotosocial/gotosocial --config-path /flow/gts/config/config.yaml server start

View file

@ -4,7 +4,7 @@ http:
gotosocial: gotosocial:
entryPoints: entryPoints:
- "https" - "https"
rule: "Host(`${GTS_HOST}`)" rule: "Host(`{{ .GoToSocial.Subdomain }}.{{ .RootDomain }}`)"
service: "gotosocial" service: "gotosocial"
tls: tls:
certResolver: resolver certResolver: resolver
@ -12,4 +12,4 @@ http:
gotosocial: gotosocial:
loadBalancer: loadBalancer:
servers: servers:
- url: "http://${GTS_CONTAINER_IPV4_ADDRESS}:${GTS_PORT}/" - url: "http://{{ .GoToSocial.ContainerIpv4Address }}:{{ .GoToSocial.Port }}/"

View file

@ -1,4 +1,4 @@
FROM traefik:${TRAEFIK_VERSION} FROM traefik:{{ .Traefik.Version }}
ADD traefik.yaml /flow/traefik/ ADD traefik.yaml /flow/traefik/

View file

@ -4,5 +4,5 @@ http:
dashboard: dashboard:
entryPoints: entryPoints:
- "https" - "https"
rule: "Host(`${ROOT_DOMAIN}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" rule: "Host(`{{ .RootDomain }}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
service: "api@internal" service: "api@internal"

View file

@ -1,14 +1,15 @@
#!/bin/sh #!/bin/sh
set -e set -e
# Create the dynamic config directory in the shared volume. # Create the dynamic config directory in the shared volume.
mkdir -p ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic mkdir -p {{ .Traefik.SharedMountPoint }}/dynamic
chgrp ${FLOW_GID} ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic chgrp {{ .FlowGID }} {{ .Traefik.SharedMountPoint }}/dynamic
chmod a-rwx,u+rwx,g+rwx ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic chmod a-rwx,u+rwx,g+rwx {{ .Traefik.SharedMountPoint }}/dynamic
# Move the dashboard config to the new directory. # Move the dashboard config to the new directory.
if [ -f /tmp/dynamic_dashboard.yaml ]; then if [ -f /tmp/dynamic_dashboard.yaml ]; then
mv /tmp/dynamic_dashboard.yaml ${TRAEFIK_SHARED_MOUNT_POINT}/dynamic/dynamic_dashboard.yaml mv /tmp/dynamic_dashboard.yaml {{ .Traefik.SharedMountPoint }}/dynamic/dynamic_dashboard.yaml
fi fi
# first arg is `-f` or `--some-option` # first arg is `-f` or `--some-option`

View file

@ -1,36 +0,0 @@
---
global:
checkNewVersion: ${TRAEFIK_CHECK_NEW_VERSION}
sendAnonymousUsage: ${TRAEFIK_SEND_ANONYMOUS_USAGE}
api:
insecure: false
dashboard: true
debug: false
entryPoints:
http:
address: "${TRAEFIK_CONTAINER_IPV4_ADDRESS}:80"
http:
redirections:
entryPoint:
to: "https"
scheme: "https"
permanent: true
https:
address: "${TRAEFIK_CONTAINER_IPV4_ADDRESS}:443"
gitSSH:
address: "${TRAEFIK_CONTAINER_IPV4_ADDRESS}:${TRAEFIK_EXTERNAL_SSH_PORT}"
providers:
file:
watch: true
directory: "${TRAEFIK_SHARED_MOUNT_POINT}/dynamic"
certificatesResolvers:
resolver:
acme:
email: "${TRAEFIK_ACME_EMAIL}"
storage: "${TRAEFIK_TLS_CONTAINER_DIR}/acme.json"
keyType: "RSA4096"
tlsChallenge: {}
log:
level: "${TRAEFIK_LOG_LEVEL}"
pilot:
token: "${TRAEFIK_PILOT_TOKEN}"

View file

@ -0,0 +1,36 @@
---
global:
checkNewVersion: {{ .Traefik.CheckNewVersion }}
sendAnonymousUsage: {{ .Traefik.SendAnonymousUsage }}
api:
insecure: false
dashboard: true
debug: false
entryPoints:
http:
address: "{{ .Traefik.ContainerIpv4Address }}:80"
http:
redirections:
entryPoint:
to: "https"
scheme: "https"
permanent: true
https:
address: "{{ .Traefik.ContainerIpv4Address }}:443"
gitSSH:
address: "{{ .Traefik.ContainerIpv4Address }}:{{ .Traefik.ExternalSSHPort }}"
providers:
file:
watch: true
directory: "{{ .Traefik.SharedMountPoint }}/dynamic"
certificatesResolvers:
resolver:
acme:
email: "{{ .Traefik.AcmeEmail }}"
storage: "{{ .Traefik.TlsContainerDirectory }}/acme.json"
keyType: "RSA4096"
tlsChallenge: {}
log:
level: "{{ .Traefik.LogLevel }}"
pilot:
token: "{{ .Traefik.PilotToken }}"