From a4819c61c8bd4b6a0b504155c88871994e18eec8 Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Sat, 29 Jul 2023 01:05:33 +0100 Subject: [PATCH] feat(gts): add background image for GTS Add a background image for the custom CSS for GoToSocial. --- assets/gotosocial/images/background.jpg | 3 + magefiles/clean.go | 31 +++ magefiles/deploy.go | 2 +- magefiles/download.go | 280 ++++++++++++++++++++++++ magefiles/download_forgejo.go | 145 ------------ magefiles/download_gotosocial.go | 113 ---------- magefiles/mage.go | 62 ------ magefiles/{render.go => prepare.go} | 92 ++++++-- templates/gotosocial/Dockerfile.gotmpl | 1 + 9 files changed, 395 insertions(+), 334 deletions(-) create mode 100644 assets/gotosocial/images/background.jpg create mode 100644 magefiles/clean.go create mode 100644 magefiles/download.go delete mode 100644 magefiles/download_forgejo.go delete mode 100644 magefiles/download_gotosocial.go delete mode 100644 magefiles/mage.go rename magefiles/{render.go => prepare.go} (52%) diff --git a/assets/gotosocial/images/background.jpg b/assets/gotosocial/images/background.jpg new file mode 100644 index 0000000..861b225 --- /dev/null +++ b/assets/gotosocial/images/background.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:244c7a1a0b8e76d6b93087a10e0c91bf34a1e6300573cd65a3d08f4f48a697a5 +size 1131790 diff --git a/magefiles/clean.go b/magefiles/clean.go new file mode 100644 index 0000000..f75f510 --- /dev/null +++ b/magefiles/clean.go @@ -0,0 +1,31 @@ +//go:build mage + +package main + +import ( + "os" + + "github.com/magefile/mage/sh" +) + +// 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 +} diff --git a/magefiles/deploy.go b/magefiles/deploy.go index a9532b6..e5e4efd 100644 --- a/magefiles/deploy.go +++ b/magefiles/deploy.go @@ -13,7 +13,7 @@ import ( // Deploy deploys the services to the Flow Platform. func Deploy(name string) error { mg.Deps( - mg.F(Render, name), + mg.F(Prepare, name), ) cfg, err := newConfig(configFile) diff --git a/magefiles/download.go b/magefiles/download.go new file mode 100644 index 0000000..0d24639 --- /dev/null +++ b/magefiles/download.go @@ -0,0 +1,280 @@ +//go:build mage + +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + + "github.com/magefile/mage/sh" +) + +const ( + configFile string = "./config/services.json" + rootBuildDir string = "./build" + templateExtension string = ".gotmpl" + rootTemplatesDir string = "./templates" + rootAssetsDir string = "./assets" +) + +// Download downloads the binaries for a given service. +func Download(name string) error { + cfg, err := newConfig(configFile) + if err != nil { + return fmt.Errorf("unable to load the configuration; %v", err) + } + + switch name { + case "forgejo": + if err := downloadForgejo(cfg.Forgejo.Version); err != nil { + return fmt.Errorf("an error occurred whilst getting the forgejo binary; %w", err) + } + case "gotosocial": + if err := downloadGoToSocial(cfg.GoToSocial.Version); err != nil { + return fmt.Errorf("an error occurred whilst getting the packages for GoToSocial; %w", err) + } + default: + fmt.Printf("'%s' has no files to download.\n", name) + } + + return nil +} + +// Download Forgejo + +type forgejoDownload struct { + Downloads map[string]forgejoFiles `json:"downloads"` +} + +type forgejoFiles struct { + Binary string `json:"binary"` + Signature string `json:"signature"` + Digest string `json:"digest"` +} + +const ( + forgejoBinaryFileFormat string = "forgejo-%s-linux-amd64" + forgejoDigestExtension string = ".sha256" + forgejoSignatureExtension string = ".asc" + forgejoDownloadJson string = "./magefiles/forgejo/download.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(forgejoBinaryFileFormat, version), + ) + signaturePath := binaryPath + forgejoSignatureExtension + digestPath := binaryPath + forgejoDigestExtension + + _, err := os.Stat(binaryPath) + if err == nil { + fmt.Printf("Forgejo %s is already downloaded.\n", version) + return nil + } + + data, err := newForgejoDownloadData() + if err != nil { + return err + } + + downloads := []struct { + url string + path string + }{ + { + url: data.Downloads[version].Binary, + path: binaryPath, + }, + { + url: data.Downloads[version].Signature, + path: signaturePath, + }, + { + url: data.Downloads[version].Digest, + path: digestPath, + }, + } + + for _, v := range downloads { + if err := func() error { + download, err := os.Create(v.path) + if err != nil { + return fmt.Errorf("unable to create %s; %w", v.path, err) + } + defer download.Close() + + client := http.Client{ + CheckRedirect: func(r *http.Request, _ []*http.Request) error { + r.URL.Opaque = r.URL.Path + return nil + }, + } + + resp, err := client.Get(v.url) + if err != nil { + return err + } + defer resp.Body.Close() + + size, err := io.Copy(download, resp.Body) + if err != nil { + return err + } + + fmt.Printf("Downloaded %s with size %d.\n", v.path, size) + + return nil + }(); err != nil { + return err + } + } + + if err = sh.Run("gpg", "--verify", signaturePath, binaryPath); err != nil { + return fmt.Errorf("GPG verification failed; %w", err) + } + + if err := os.Chdir(downloadFolder); err != nil { + return err + } + + if err := sh.Run("sha256sum", "--check", fmt.Sprintf(forgejoBinaryFileFormat+forgejoDigestExtension, version)); err != nil { + return err + } + + if err := os.Chdir("../.."); err != nil { + return err + } + + return nil +} + +func newForgejoDownloadData() (forgejoDownload, error) { + var data forgejoDownload + + f, err := os.Open(forgejoDownloadJson) + if err != nil { + return data, err + } + defer f.Close() + + decoder := json.NewDecoder(f) + + if err = decoder.Decode(&data); err != nil { + return data, err + } + + return data, nil +} + +// Download GTS + +func downloadGoToSocial(version string) error { + downloadFolder := filepath.Join(rootBuildDir, "gotosocial") + + if err := os.MkdirAll(downloadFolder, 0o750); err != nil { + return fmt.Errorf("unable to make %s; %w", downloadFolder, err) + } + + binaryTarUrl := fmt.Sprintf( + "https://github.com/superseriousbusiness/gotosocial/releases/download/v%s/gotosocial_%s_linux_amd64.tar.gz", + version, + version, + ) + binaryTarFilepath := filepath.Join(downloadFolder, fmt.Sprintf("gotosocial_%s_linux_amd64.tar.gz", version)) + + webAssetsTarUrl := fmt.Sprintf( + "https://github.com/superseriousbusiness/gotosocial/releases/download/v%s/gotosocial_%s_web-assets.tar.gz", + version, + version, + ) + webAssetsFilepath := filepath.Join(downloadFolder, fmt.Sprintf("gotosocial_%s_web-assets.tar.gz", version)) + + checksumUrl := fmt.Sprintf( + "https://github.com/superseriousbusiness/gotosocial/releases/download/v%s/checksums.txt", + version, + ) + checksumFilePath := filepath.Join(downloadFolder, fmt.Sprintf("gotosocial_%s_checksums.txt", version)) + + _, err := os.Stat(binaryTarFilepath) + if err == nil { + fmt.Printf("GoToSocial %s is already downloaded.\n", version) + return nil + } + + downloads := []struct { + url string + path string + }{ + { + url: binaryTarUrl, + path: binaryTarFilepath, + }, + { + url: webAssetsTarUrl, + path: webAssetsFilepath, + }, + { + url: checksumUrl, + path: checksumFilePath, + }, + } + + for _, v := range downloads { + if err := func() error { + download, err := os.Create(v.path) + if err != nil { + return fmt.Errorf("unable to create %s; %w", v.path, err) + } + defer download.Close() + + client := http.Client{ + CheckRedirect: func(r *http.Request, _ []*http.Request) error { + r.URL.Opaque = r.URL.Path + return nil + }, + } + + resp, err := client.Get(v.url) + if err != nil { + return err + } + defer resp.Body.Close() + + size, err := io.Copy(download, resp.Body) + if err != nil { + return err + } + + fmt.Printf("Downloaded %s with size %d.\n", v.path, size) + + return nil + }(); err != nil { + return err + } + } + + if err := os.Chdir(downloadFolder); err != nil { + return err + } + + if err := sh.Run("sha256sum", "--check", "--ignore-missing", fmt.Sprintf("gotosocial_%s_checksums.txt", version)); err != nil { + return err + } + + if err := os.Chdir("../.."); err != nil { + return err + } + + return nil +} diff --git a/magefiles/download_forgejo.go b/magefiles/download_forgejo.go deleted file mode 100644 index 2f81b70..0000000 --- a/magefiles/download_forgejo.go +++ /dev/null @@ -1,145 +0,0 @@ -//go:build mage - -package main - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - - "github.com/magefile/mage/sh" -) - -type forgejoDownload struct { - Downloads map[string]forgejoFiles `json:"downloads"` -} - -type forgejoFiles struct { - Binary string `json:"binary"` - Signature string `json:"signature"` - Digest string `json:"digest"` -} - -const ( - forgejoBinaryFileFormat string = "forgejo-%s-linux-amd64" - forgejoDigestExtension string = ".sha256" - forgejoSignatureExtension string = ".asc" - forgejoDownloadJson string = "./magefiles/forgejo/download.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(forgejoBinaryFileFormat, version), - ) - signaturePath := binaryPath + forgejoSignatureExtension - digestPath := binaryPath + forgejoDigestExtension - - _, err := os.Stat(binaryPath) - if err == nil { - fmt.Printf("Forgejo %s is already downloaded.\n", version) - return nil - } - - data, err := newForgejoDownloadData() - if err != nil { - return err - } - - downloads := []struct { - url string - path string - }{ - { - url: data.Downloads[version].Binary, - path: binaryPath, - }, - { - url: data.Downloads[version].Signature, - path: signaturePath, - }, - { - url: data.Downloads[version].Digest, - path: digestPath, - }, - } - - for _, v := range downloads { - if err := func() error { - download, err := os.Create(v.path) - if err != nil { - return fmt.Errorf("unable to create %s; %w", v.path, err) - } - defer download.Close() - - client := http.Client{ - CheckRedirect: func(r *http.Request, _ []*http.Request) error { - r.URL.Opaque = r.URL.Path - return nil - }, - } - - resp, err := client.Get(v.url) - if err != nil { - return err - } - defer resp.Body.Close() - - size, err := io.Copy(download, resp.Body) - if err != nil { - return err - } - - fmt.Printf("Downloaded %s with size %d.\n", v.path, size) - - return nil - }(); err != nil { - return err - } - } - - if err = sh.Run("gpg", "--verify", signaturePath, binaryPath); err != nil { - return fmt.Errorf("GPG verification failed; %w", err) - } - - if err := os.Chdir(downloadFolder); err != nil { - return err - } - - if err := sh.Run("sha256sum", "--check", fmt.Sprintf(forgejoBinaryFileFormat+forgejoDigestExtension, version)); err != nil { - return err - } - - if err := os.Chdir("../.."); err != nil { - return err - } - - return nil -} - -func newForgejoDownloadData() (forgejoDownload, error) { - var data forgejoDownload - - f, err := os.Open(forgejoDownloadJson) - if err != nil { - return data, err - } - defer f.Close() - - decoder := json.NewDecoder(f) - - if err = decoder.Decode(&data); err != nil { - return data, err - } - - return data, nil -} diff --git a/magefiles/download_gotosocial.go b/magefiles/download_gotosocial.go deleted file mode 100644 index f85649a..0000000 --- a/magefiles/download_gotosocial.go +++ /dev/null @@ -1,113 +0,0 @@ -//go:build mage - -package main - -import ( - "fmt" - "io" - "net/http" - "os" - "path/filepath" - - "github.com/magefile/mage/sh" -) - -func downloadGoToSocial(version string) error { - downloadFolder := filepath.Join(rootBuildDir, "gotosocial") - - if err := os.MkdirAll(downloadFolder, 0o750); err != nil { - return fmt.Errorf("unable to make %s; %w", downloadFolder, err) - } - - binaryTarUrl := fmt.Sprintf( - "https://github.com/superseriousbusiness/gotosocial/releases/download/v%s/gotosocial_%s_linux_amd64.tar.gz", - version, - version, - ) - binaryTarFilepath := filepath.Join(downloadFolder, fmt.Sprintf("gotosocial_%s_linux_amd64.tar.gz", version)) - - webAssetsTarUrl := fmt.Sprintf( - "https://github.com/superseriousbusiness/gotosocial/releases/download/v%s/gotosocial_%s_web-assets.tar.gz", - version, - version, - ) - webAssetsFilepath := filepath.Join(downloadFolder, fmt.Sprintf("gotosocial_%s_web-assets.tar.gz", version)) - - checksumUrl := fmt.Sprintf( - "https://github.com/superseriousbusiness/gotosocial/releases/download/v%s/checksums.txt", - version, - ) - checksumFilePath := filepath.Join(downloadFolder, fmt.Sprintf("gotosocial_%s_checksums.txt", version)) - - _, err := os.Stat(binaryTarFilepath) - if err == nil { - fmt.Printf("GoToSocial %s is already downloaded.\n", version) - return nil - } - - downloads := []struct { - url string - path string - }{ - { - url: binaryTarUrl, - path: binaryTarFilepath, - }, - { - url: webAssetsTarUrl, - path: webAssetsFilepath, - }, - { - url: checksumUrl, - path: checksumFilePath, - }, - } - - for _, v := range downloads { - if err := func() error { - download, err := os.Create(v.path) - if err != nil { - return fmt.Errorf("unable to create %s; %w", v.path, err) - } - defer download.Close() - - client := http.Client{ - CheckRedirect: func(r *http.Request, _ []*http.Request) error { - r.URL.Opaque = r.URL.Path - return nil - }, - } - - resp, err := client.Get(v.url) - if err != nil { - return err - } - defer resp.Body.Close() - - size, err := io.Copy(download, resp.Body) - if err != nil { - return err - } - - fmt.Printf("Downloaded %s with size %d.\n", v.path, size) - - return nil - }(); err != nil { - return err - } - } - - if err := os.Chdir(downloadFolder); err != nil { - return err - } - - if err := sh.Run("sha256sum", "--check", "--ignore-missing", fmt.Sprintf("gotosocial_%s_checksums.txt", version)); err != nil { - return err - } - - if err := os.Chdir("../.."); err != nil { - return err - } - - return nil -} diff --git a/magefiles/mage.go b/magefiles/mage.go deleted file mode 100644 index c4584e8..0000000 --- a/magefiles/mage.go +++ /dev/null @@ -1,62 +0,0 @@ -//go: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 -} - -// Download downloads the binaries for a given service. -func Download(name string) error { - cfg, err := newConfig(configFile) - if err != nil { - return fmt.Errorf("unable to load the configuration; %v", err) - } - - switch name { - case "forgejo": - if err := downloadForgejo(cfg.Forgejo.Version); err != nil { - return fmt.Errorf("an error occurred whilst getting the forgejo binary; %w", err) - } - case "gotosocial": - if err := downloadGoToSocial(cfg.GoToSocial.Version); err != nil { - return fmt.Errorf("an error occurred whilst getting the packages for GoToSocial; %w", err) - } - default: - fmt.Printf("'%s' has no files to download.\n", name) - } - - return nil -} diff --git a/magefiles/render.go b/magefiles/prepare.go similarity index 52% rename from magefiles/render.go rename to magefiles/prepare.go index 92f5397..5674588 100644 --- a/magefiles/render.go +++ b/magefiles/prepare.go @@ -4,6 +4,8 @@ package main import ( "fmt" + "io" + "io/fs" "log" "os" "path/filepath" @@ -13,14 +15,14 @@ import ( "github.com/magefile/mage/mg" ) -// Render renders the template files. -func Render(name string) error { +// Prepare prepares the service's build directory. +func Prepare(service string) error { cfg, err := newConfig(configFile) if err != nil { return fmt.Errorf("unable to load the configuration; %v", err) } - if name == "all" { + if service == "all" { objects, err := os.ReadDir(rootTemplatesDir) if err != nil { return fmt.Errorf("unable to read the templates directory; %w", err) @@ -31,30 +33,39 @@ func Render(name string) error { continue } - dirName := o.Name() + service := o.Name() - if dirName != "compose" { + if service != "compose" { mg.Deps( - mg.F(Download, dirName), + mg.F(Download, service), ) } - log.Printf("Rendering templates for %s.\n", dirName) - if err := render(cfg, o.Name()); err != nil { - return fmt.Errorf("unable to render templates for %s; %w", dirName, err) + log.Printf("Rendering templates for %s.\n", service) + if err := render(cfg, service); err != nil { + return fmt.Errorf("unable to render templates for %s; %w", service, err) + } + + log.Printf("Copying assets for %s.\n", service) + if err := copyAssets(service); err != nil { + return fmt.Errorf("unable to copy the assets for %s; %w", service, err) } } } else { - if name != "compose" { + if service != "compose" { mg.Deps( - mg.F(Download, name), - mg.F(Render, "compose"), + mg.F(Download, service), + mg.F(Prepare, "compose"), ) } - if err := render(cfg, name); err != nil { + if err := render(cfg, service); err != nil { return fmt.Errorf("an error occurred whilst rendering the templates; %w", err) } + + if err := copyAssets(service); err != nil { + return fmt.Errorf("unable to copy the assets for %s; %w", service, err) + } } return nil @@ -109,3 +120,58 @@ func render(cfg config, component string) error { return nil } + +func copyAssets(service string) error { + assetsDirName := filepath.Join(rootAssetsDir, service) + if _, err := os.Stat(assetsDirName); err != nil { + log.Printf("There's no assets directory for %s.\n", service) + return nil + } + + buildDirName := filepath.Join(rootBuildDir, service, "assets") + + walkDirFunc := func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if path == assetsDirName { + return nil + } + + buildAssetPath := filepath.Join(buildDirName, strings.TrimPrefix(path, assetsDirName)) + + if d.IsDir() { + if err := os.MkdirAll(buildAssetPath, 0o750); err != nil { + return fmt.Errorf("unable to make %s; %w", path, err) + } + + return nil + } + + source, err := os.Open(path) + if err != nil { + return err + } + defer source.Close() + + dest, err := os.Create(buildAssetPath) + if err != nil { + return err + } + defer dest.Close() + + if _, err := io.Copy(dest, source); err != nil { + return err + } + + return nil + } + + err := filepath.WalkDir(assetsDirName, walkDirFunc) + if err != nil { + return fmt.Errorf("error walking the path '%s'; %w", assetsDirName, err) + } + + return nil +} diff --git a/templates/gotosocial/Dockerfile.gotmpl b/templates/gotosocial/Dockerfile.gotmpl index d165871..e963c68 100644 --- a/templates/gotosocial/Dockerfile.gotmpl +++ b/templates/gotosocial/Dockerfile.gotmpl @@ -17,6 +17,7 @@ RUN --mount=type=bind,source=.,target=/packages \ COPY --chown={{ .GoToSocial.LinuxUID }}:{{ .GoToSocial.LinuxUID }} entrypoint.sh /usr/local/bin/entrypoint COPY --chown={{ .GoToSocial.LinuxUID }}:{{ .GoToSocial.LinuxUID }} config.yaml /flow/gts/config/config.yaml +COPY --chown={{ .GoToSocial.LinuxUID }}:{{ .GoToSocial.LinuxUID }} assets/images/background.jpg /flow/gts/web/assets/background.jpg COPY --chown={{ .GoToSocial.LinuxUID }}:{{ .FlowGID }} traefik_gotosocial.yaml /flow/gts/tmp/traefik_gotosocial.yaml RUN chmod a+x /usr/local/bin/entrypoint