support deployments

This commit is contained in:
Iris Lightshard 2023-10-20 22:46:37 -06:00
parent 8f600b21fb
commit e4ae62ad7b
Signed by: Iris Lightshard
GPG key ID: F54E0D40695271D4
9 changed files with 140 additions and 15 deletions

View file

@ -37,8 +37,9 @@ Initially the user will be presented with the login screen; upon successful logi
- `Pages`: the default page, shows a list of existing pages - clicking one enables editing that page; a button is also presented for adding a new page. Each `Adapter` will provide different formatting help and can allow editable slugs/URLs or not (eg, the `EurekaAdapter` builds slugs/URLs directly from the page title). - `Pages`: the default page, shows a list of existing pages - clicking one enables editing that page; a button is also presented for adding a new page. Each `Adapter` will provide different formatting help and can allow editable slugs/URLs or not (eg, the `EurekaAdapter` builds slugs/URLs directly from the page title).
- `Files`: provides an interface for managing statically hosted files. Files and directories can be added, moved, and deleted. - `Files`: provides an interface for managing statically hosted files. Files and directories can be added, moved, and deleted.
- `Build`: a simple form to build the site - build options configurable by `Adapter` are present under an accordion.
- `Configuration`: interface to the configuration for the `Adapter`. Each `Adapter` provides its own configuration interface with associated data types (currently supported: `int`, `float`, `string`, and `multilinestring`) - `Configuration`: interface to the configuration for the `Adapter`. Each `Adapter` provides its own configuration interface with associated data types (currently supported: `int`, `float`, `string`, and `multilinestring`)
- `Build`: a simple form to build the site - build options configurable by `Adapter` are present under an accordion.
- `Deploy`: a _very_ simple form to either deploy the site or revert the current state to the deployed state
- `Logout`: logs the user out and returns to the login screen - `Logout`: logs the user out and returns to the login screen
## adapter interface ## adapter interface
@ -61,6 +62,8 @@ The `Adapter` interface and associated data types can be found in the [adapter.g
- `SavePage(oldSlug, newSlug, title, content string) error`: given all the proper arguments, save a page to the backing store (eg filesystem, db) - `SavePage(oldSlug, newSlug, title, content string) error`: given all the proper arguments, save a page to the backing store (eg filesystem, db)
- `DeletePage(slug string) error`: given a slug, delete the corresponding page source and possibly its generated HTML, depending on the `Adapter` - `DeletePage(slug string) error`: given a slug, delete the corresponding page source and possibly its generated HTML, depending on the `Adapter`
- `Build(buildOptions map[string][]string) BuildStatus`: takes a map of build option names to their values and builds the site, returning a `BuildStatus` object containing the success or failure as a boolean and the detailed status (eg, the console ouptut of the build process) - `Build(buildOptions map[string][]string) BuildStatus`: takes a map of build option names to their values and builds the site, returning a `BuildStatus` object containing the success or failure as a boolean and the detailed status (eg, the console ouptut of the build process)
- `Deploy() DeployStatus`: executes the deployment script and returns a `DeployStatus` object, analagous to a `BuildStatus`
- `Revert() RevertStatus`: executes the reversion script and returns a `RevertStatus` object, analagous to a `BuildStatus`
## license ## license

View file

@ -9,6 +9,9 @@ type BuildStatus struct {
Message string Message string
} }
type DeployStatus BuildStatus
type RevertStatus BuildStatus
type Page struct { type Page struct {
Title string Title string
Content string Content string
@ -38,4 +41,6 @@ type Adapter interface {
SavePage(oldSlug, newSlug, title, content string) error SavePage(oldSlug, newSlug, title, content string) error
DeletePage(slug string) error DeletePage(slug string) error
Build(buildOptions map[BuildOption]string) BuildStatus Build(buildOptions map[BuildOption]string) BuildStatus
Deploy() DeployStatus
Revert() RevertStatus
} }

View file

@ -302,6 +302,28 @@ func (self *EurekaAdapter) Build(buildOptions map[BuildOption]string) BuildStatu
} }
} }
func (self *EurekaAdapter) Deploy() DeployStatus {
cmd := exec.Command("./deploy.sh")
cmd.Dir = self.Root
out, err := cmd.CombinedOutput()
return DeployStatus{
Success: err == nil,
Message: string(out),
}
}
func (self *EurekaAdapter) Revert() RevertStatus {
cmd := exec.Command("./deploy.sh", "--resync")
cmd.Dir = self.Root
out, err := cmd.CombinedOutput()
return RevertStatus{
Success: err == nil,
Message: string(out),
}
}
func (self *EurekaAdapter) readCfg() error { func (self *EurekaAdapter) readCfg() error {
configPath := filepath.Join(self.Root, "config.h") configPath := filepath.Join(self.Root, "config.h")
_, err := os.Stat(filepath.Join(self.Root, "config.h")) _, err := os.Stat(filepath.Join(self.Root, "config.h"))
@ -389,7 +411,7 @@ func (self *EurekaAdapter) writeCfg() error {
defer f.Close() defer f.Close()
f.WriteString("/* clang-format off */\n"); f.WriteString("/* clang-format off */\n")
for k, v := range self.Config { for k, v := range self.Config {
switch k.Type { switch k.Type {
case "int": case "int":
@ -418,6 +440,6 @@ func (self *EurekaAdapter) writeCfg() error {
} }
} }
f.WriteString("/* clang-format on */\n"); f.WriteString("/* clang-format on */\n")
return nil return nil
} }

View file

@ -2,8 +2,8 @@ package lfo
import ( import (
"context" "context"
"net/http"
core "hacklab.nilfm.cc/nirvash/archetype" core "hacklab.nilfm.cc/nirvash/archetype"
"net/http"
"strings" "strings"
) )

View file

@ -1,14 +1,14 @@
package main package main
import ( import (
"html/template"
"net/http"
core "hacklab.nilfm.cc/nirvash/archetype" core "hacklab.nilfm.cc/nirvash/archetype"
. "hacklab.nilfm.cc/nirvash/lfo" . "hacklab.nilfm.cc/nirvash/lfo"
"hacklab.nilfm.cc/quartzgun/indentalUserDB" "hacklab.nilfm.cc/quartzgun/indentalUserDB"
. "hacklab.nilfm.cc/quartzgun/middleware" . "hacklab.nilfm.cc/quartzgun/middleware"
"hacklab.nilfm.cc/quartzgun/renderer" "hacklab.nilfm.cc/quartzgun/renderer"
"hacklab.nilfm.cc/quartzgun/router" "hacklab.nilfm.cc/quartzgun/router"
"html/template"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
) )
@ -161,6 +161,50 @@ func main() {
udb, udb,
"/")) "/"))
rtr.Get(
`/deploy`,
Fortify(
Protected(
WithAdapter(
renderer.Template(
pathConcat(templateRoot, "deploy.html"),
pathConcat(templateRoot, "header.html"),
pathConcat(templateRoot, "footer.html")),
cfg.Adapter),
http.MethodGet,
udb,
"/login")))
rtr.Post(
`/deploy`,
Defend(
Protected(
WithAdapter(
renderer.Template(
pathConcat(templateRoot, "deployed.html"),
pathConcat(templateRoot, "header.html"),
pathConcat(templateRoot, "footer.html")),
cfg.Adapter),
http.MethodGet,
udb,
"/login"),
udb,
"/"))
rtr.Post(
`/revert`,
Defend(
Protected(
WithAdapter(
renderer.Template(
pathConcat(templateRoot, "reverted.html"),
pathConcat(templateRoot, "header.html"),
pathConcat(templateRoot, "footer.html")),
cfg.Adapter),
http.MethodGet,
udb,
"/login"),
udb,
"/"))
rtr.Post( rtr.Post(
`/delete/(?P<Slug>\S+)`, `/delete/(?P<Slug>\S+)`,
Defend( Defend(

16
templates/deploy.html Normal file
View file

@ -0,0 +1,16 @@
{{ $csrfToken := (.Context).Value "csrfToken" }}
{{ template "header" . }}
<h2>Deployment</h2>
<form class="build" method="POST" action="/deploy">
<input hidden name="csrfToken" value="{{$csrfToken}}"/>
<input type="submit" value="Deploy"/>
</form>
<form class="build" method="POST" action="/revert">
<input hidden name="csrfToken" value="{{$csrfToken}}"/>
<input type="submit" value="Revert"/>
</form>
{{ template "footer" . }}

17
templates/deployed.html Normal file
View file

@ -0,0 +1,17 @@
{{ $status := ((.Context).Value "adapter").Deploy }}
{{ template "header" . }}
{{ if ne ($status).Success true }}
<h2>Deployment Error</h2>
<span class="adapter-error"><pre>{{($status).Message}}</pre></span>
{{ else }}
<h2>Deployment Successful</h2>
<span class="adapter-success"><pre>{{($status).Message}}</pre></span>
{{ end }}
{{ template "footer" . }}

View file

@ -14,8 +14,9 @@
<ul> <ul>
<li><a href="/">Pages</a></li> <li><a href="/">Pages</a></li>
<li><a href="/file-mgr">Files</a></li> <li><a href="/file-mgr">Files</a></li>
<li><a href="/build">Build</a></li>
<li><a href="/config">Configuration</a></li> <li><a href="/config">Configuration</a></li>
<li><a href="/build">Build</a></li>
<li><a href="/deploy">Deployment</a></li>
<li><a href="/logout">Logout</a></li> <li><a href="/logout">Logout</a></li>
</ul> </ul>
</nav> </nav>

17
templates/reverted.html Normal file
View file

@ -0,0 +1,17 @@
{{ $status := ((.Context).Value "adapter").Revert }}
{{ template "header" . }}
{{ if ne ($status).Success true }}
<h2>Revert Error</h2>
<span class="adapter-error"><pre>{{($status).Message}}</pre></span>
{{ else }}
<h2>Revert Successful</h2>
<span class="adapter-success"><pre>{{($status).Message}}</pre></span>
{{ end }}
{{ template "footer" . }}