Replace DeployErrors with a better system(?) (fix #42)

This commit is contained in:
Noah 2024-06-11 14:00:32 -05:00
parent 20b955231b
commit a122598484
5 changed files with 71 additions and 99 deletions

View File

@ -1,30 +0,0 @@
// package main is the main package for the LapisDeploy program
package main
import (
"fmt"
)
// DeployError contains important information about an error if something went wrong.
type DeployError struct {
code int
where string
details string
command_output string
}
// SendOverMatrix provides an easy way to send the contents of a DeployError over Matrix
func (deploy_error *DeployError) SendOverMatrix() {
sendMessage(MatrixMessage{text: deploy_error.ToString()})
}
// ToString converts a DeployError into a human readable string.
func (deploy_error *DeployError) ToString() string {
return fmt.Sprintf("%sError in `%s`! (%s) Code: %d",
Circles["red"], deploy_error.where, deploy_error.details, deploy_error.code)
}
// newDeployError creates a DeployError
func newDeployError(code int, where string, details string, command_output string) DeployError {
return DeployError{code: code, where: where, details: details, command_output: command_output}
}

46
main.go
View File

@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
@ -12,7 +13,7 @@ import (
"github.com/akamensky/argparse"
)
// Circle emoji storage.
// Circle emoji
var Circles = map[string]string{
"white": "⚪️ ",
"green": "🟢 ",
@ -21,9 +22,6 @@ var Circles = map[string]string{
"orange": "🟠 ",
}
// Create a configuration struct
var configuration = Configuration{}
// fileExists returns whether the given file or directory exists.
func fileExists(path string) (bool, error) {
_, err := os.Stat(path)
@ -36,51 +34,51 @@ func fileExists(path string) (bool, error) {
return false, err
}
// Create a configuration struct
var configuration = Configuration{}
// handler is in charge of handling requests after the JSON has been parsed
func handler(data map[string]interface{}) {
repository := data["repository"].(map[string]interface{})
log.Default().Printf("Repo: %s", repository["full_name"])
sendMessage(MatrixMessage{text: fmt.Sprintf("%sHandling webhook for `%s`...",
Circles["white"], repository["full_name"])})
sendInfo(fmt.Sprintf("Handling webhook for `%s`...", repository["full_name"]))
repo_name := repository["name"].(string)
site, exists, err := getSite(repo_name)
if err != nil {
deploy_error := newDeployError(1, "handler", fmt.Sprintf("Failed to check if site '%s' exists", repo_name), fmt.Sprint(err))
deploy_error.SendOverMatrix()
sendError(errors.New(fmt.Sprintf("Failed to check if site '%s' exists: %s", repo_name, err)))
return
}
if exists {
sendMessage(MatrixMessage{text: Circles["white"] + "Updating repository..."})
if deploy_error := site.Update(); deploy_error.code != 0 {
deploy_error.SendOverMatrix()
sendInfo("Updating repository...")
if err := site.Update(); err != nil {
sendError(err)
return
}
new_site, exists, err := getSite(site.name)
if err != nil || !exists {
newDeployError(1, "main", "Failed to update site data on a site we just created!", fmt.Sprint(err))
sendError(errors.New("Failed to update site data on a site we just updated: " + repo_name))
}
site = new_site
sendMessage(MatrixMessage{text: Circles["white"] + "Restarting server..."})
if deploy_error := site.Restart(); deploy_error.code != 0 {
deploy_error.SendOverMatrix()
sendInfo("Restarting server...")
if error := site.Restart(); error != nil {
sendError(error)
return
}
} else {
sendMessage(MatrixMessage{text: Circles["white"] + "Cloning repository..."})
if deploy_error := CloneSite(repository["ssh_url"].(string), repo_name); deploy_error.code != 0 {
deploy_error.SendOverMatrix()
sendInfo("Cloning repositiory...")
if error := CloneSite(repository["ssh_url"].(string), repo_name); error != nil {
sendError(error)
return
}
sendMessage(MatrixMessage{text: Circles["white"] + "Starting server..."})
sendInfo("Starting server...")
if site, exists, err = getSite(repo_name); err != nil {
deploy_error := newDeployError(1, "handler",
fmt.Sprintf("Failed to get site '%s' after creation!", repo_name), fmt.Sprint(err))
deploy_error.SendOverMatrix()
sendError(err)
return
}
if deploy_error := site.Start(); deploy_error.code != 0 {
deploy_error.SendOverMatrix()
if error := site.Start(); error != nil {
sendError(err)
return
}
defer sendMessage(MatrixMessage{text: "🚀 Launched for the first time!"})

View File

@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
"log"
"strings"
"time"
@ -29,15 +30,29 @@ type MatrixCommand struct {
description string
}
// Output queue
// Output queue.
var MatrixOut []MatrixMessage
// sendMessage sends a message through Matrix
// sendMessage sends a message through Matrix.
func sendMessage(msg MatrixMessage) {
log.Print(msg.text)
MatrixOut = append(MatrixOut, msg)
}
// sendError sends an error through Matrix.
func sendError(err error) {
sendMessage(MatrixMessage{
text: fmt.Sprintf("%s %s", Circles["red"], err),
})
}
// sendInfo sends a bit of info text through Matrix.
func sendInfo(text string) {
sendMessage(MatrixMessage{
text: fmt.Sprintf("%s %s", Circles["white"], text),
})
}
// initMatrix starts a Matrix bot.
func initMatrix() {
client, err := mautrix.NewClient(configuration.matrix.homeserver, "", "")

View File

@ -16,9 +16,9 @@ func getSitesByString(text string) ([]Site, []string) {
sites := []Site{} // sites holds every site we need
// Determine which sites to get
if text == "all" { // If we need to get all sites, (!restart all)
var err DeployError
var err error
sites, err = getAllSites() // Get all sites
if err.code != 0 { // Check for errors
if err != nil { // Check for errors
errors = append(errors, "Failed to get all sites!")
}
} else { // If we need to get a specific site, (!restart LapisDeploy-TestApp)
@ -53,7 +53,7 @@ func registerChatCommands() {
callback: func(ctx context.Context, evt *event.Event, args []string) string {
text := Circles["white"] + "Getting sites...\n"
sites, err := getAllSites()
if err.code != 0 {
if err != nil {
return text + Circles["red"] + "Failed to get sites!"
}
for _, site := range sites {
@ -82,9 +82,8 @@ func registerChatCommands() {
text += Circles["red"] + error + "\n"
}
for _, site := range sites { // For every site queued to be restarted,
if err := site.Restart(); err.code != 0 { // Attempt to restart it
if err := site.Restart(); err != nil { // Attempt to restart it
text += fmt.Sprintf("%sRestarting '%s'... failed!\n", Circles["red"], site.getName())
text += err.ToString()
} else { // Otherwise, success.
text += fmt.Sprintf("%sRestarting '%s'... success", Circles["green"], site.getName())
}
@ -105,9 +104,8 @@ func registerChatCommands() {
text += Circles["red"] + error + "\n"
}
for _, site := range sites {
if err := site.Start(); err.code != 0 {
if err := site.Start(); err != nil {
text += fmt.Sprintf("%sStarting '%s'... failed!\n", Circles["red"], site.getName())
text += err.ToString()
} else {
text += fmt.Sprintf("%sStarting '%s'... success!\n", Circles["green"], site.getName())
}
@ -128,9 +126,8 @@ func registerChatCommands() {
text += Circles["red"] + error + "\n"
}
for _, site := range sites {
if err := site.Stop(); err.code != 0 {
text += fmt.Sprintf("%sStopping '%s'... failed!", Circles["red"], site.getName())
text += err.ToString()
if err := site.Stop(); err != nil {
text += fmt.Sprintf("%sStopping '%s'... failed: %s", Circles["red"], site.getName(), err)
} else {
text += fmt.Sprintf("%sStopping '%s'... success", Circles["green"], site.getName())
}

58
site.go
View File

@ -98,13 +98,12 @@ func (site *Site) getName() string {
}
// getAllSites gets every site installed.
func getAllSites() ([]Site, DeployError) {
func getAllSites() ([]Site, error) {
sites_dir := configuration.sites_dir
files, err := os.ReadDir(sites_dir)
var sites []Site
if err != nil {
return sites, newDeployError(1, "startAllSites",
"Failed to read sites", fmt.Sprint(err))
return sites, errors.New(fmt.Sprintf("Failed to read sites: %s", err))
}
for _, file := range files {
if !file.IsDir() {
@ -112,30 +111,30 @@ func getAllSites() ([]Site, DeployError) {
}
site, exists, err := getSite(file.Name())
if err != nil || !exists {
return sites, newDeployError(1, "getAllSites", "Failed to read sites", fmt.Sprint(err))
return sites, errors.New(fmt.Sprintf("Failed to read sites: %s", err))
}
sites = append(sites, site)
}
return sites, DeployError{}
return sites, nil
}
// startAllSites attempts to start every site
func startAllSites() DeployError {
func startAllSites() error {
sites, err := getAllSites()
if err.code != 0 {
if err != nil {
return err
}
for _, site := range sites {
err = site.Start()
if err.code != 0 {
if err != nil {
return err
}
}
return DeployError{}
return nil
}
// Start attempts to start a Lapis server in the given repository.
func (site *Site) Start() DeployError {
func (site *Site) Start() error {
log.Printf("Starting Lapis server in %s...", site.getName())
old_cwd, _ := os.Getwd()
@ -144,18 +143,17 @@ func (site *Site) Start() DeployError {
cmd := exec.Command("lapis", "serve")
if err := cmd.Start(); err != nil {
return newDeployError(1, "startSite",
fmt.Sprintf("Failed to start Lapis server in '%s'", site.getName()), "")
return errors.New(fmt.Sprintf("Failed to start Lapis server in '%s': %s", site.getName(), err))
}
go func() {
cmd.Wait()
}()
return DeployError{}
return nil
}
// Stop attempts to stop a Lapis server in the given repository.
func (site *Site) Stop() DeployError {
func (site *Site) Stop() error {
log.Printf("Stopping Lapis server %s...", site.getName())
old_cwd, _ := os.Getwd()
@ -164,17 +162,16 @@ func (site *Site) Stop() DeployError {
cmd := exec.Command("lapis", "term")
if err := cmd.Start(); err != nil {
return newDeployError(1, "stopSite",
fmt.Sprintf("Failed to stop Lapis server in '%s'", site.getName()), "")
return errors.New(fmt.Sprintf("Failed to stop Lapis server in '%s': %s", site.getName(), err))
}
go func() {
cmd.Wait()
}()
return DeployError{}
return nil
}
// Restart attempts to restart the Lapis server in the given repository.
func (site *Site) Restart() DeployError {
func (site *Site) Restart() error {
log.Printf("Restarting Lapis server in %s...", site.getName())
old_cwd, _ := os.Getwd()
@ -183,38 +180,33 @@ func (site *Site) Restart() DeployError {
out, err := exec.Command("lapis", "build").CombinedOutput()
if err != nil {
return newDeployError(1, "restartSite",
fmt.Sprintf("Failed to run 'lapis build' in '%s'", site.getName()), string(out))
return errors.New(fmt.Sprintf("Failed to run 'lapis build' in '%s': %s", site.getName(), err))
}
log.Printf("[lapis build] %s", out)
if !strings.Contains(string(out), "HUP") {
return newDeployError(1, "restartSite",
"Failed to restart Lapis server! (Lapis not running?)", string(out))
return errors.New(fmt.Sprintf("Failed to restart Lapis server! (Lapis not running?): %s", string(out)))
}
return DeployError{}
return nil
}
// Update attempts to pull or clone a repository using the given name and url
func (site *Site) Update() DeployError {
func (site *Site) Update() error {
log.Printf("Pulling down repository %s...", site.getName())
repo, err := git.Open(configuration.sites_dir + "/" + site.name)
if err != nil {
return newDeployError(1, "updateSite",
fmt.Sprintf("Failed to open git repository '%s'", site.name), fmt.Sprint(err))
return errors.New(fmt.Sprintf("Failed to open git repository '%s': %s", site.name, err))
}
if err = repo.Pull(); err != nil {
return newDeployError(1, "updateSite",
fmt.Sprintf("Failed to pull down git repository '%s'", site.getName()), fmt.Sprint(err))
return errors.New(fmt.Sprintf("Failed to pull down git repository '%s': %s", site.getName(), err))
}
return DeployError{}
return nil
}
// CloneSite will clone a site and put it in the configured 'sites_dir'
func CloneSite(url string, name string) DeployError {
func CloneSite(url string, name string) error {
log.Printf("Cloning repository %s...", name)
if err := git.Clone(url, configuration.sites_dir+"/"+name); err != nil {
return newDeployError(1, "cloneSite",
fmt.Sprintf("Failed to pull down repository '%s'", name), fmt.Sprint(err))
return errors.New(fmt.Sprintf("Failed to pull down repository '%s': %s", name, err))
}
return DeployError{}
return nil
}