forked from sloum/spacewalk
Initial commit
This commit is contained in:
commit
8c92ace89a
|
@ -0,0 +1,244 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var swPath, fPath string
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
// Startup & helpers
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
func ifErrExit(e error, msg string) {
|
||||||
|
if e != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: %s\n", msg, e.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func earlyExit(msg string) {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %s\n", msg)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateDataPaths makes sure that the necessary data folders exist
|
||||||
|
// for the user running spacewalk. It builds them if they are not
|
||||||
|
// present and does an error exit if there is an error.
|
||||||
|
func validateDataPaths() {
|
||||||
|
xdgDataHome := os.Getenv("XDG_DATA_HOME")
|
||||||
|
if xdgDataHome == "" {
|
||||||
|
usr, _ := user.Current()
|
||||||
|
xdgDataHome = filepath.Join(usr.HomeDir, "/.local/share")
|
||||||
|
}
|
||||||
|
neededFolders := filepath.Join(xdgDataHome, "spacewalk")
|
||||||
|
neededFolders, _ = filepath.Abs(neededFolders)
|
||||||
|
swPath = neededFolders
|
||||||
|
neededFolders = filepath.Join(neededFolders, "flights")
|
||||||
|
fPath = neededFolders
|
||||||
|
err := os.MkdirAll(neededFolders, 0755)
|
||||||
|
ifErrExit(err, "Error validation data paths")
|
||||||
|
p := filepath.Join(swPath, "flight-manifest")
|
||||||
|
f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
ifErrExit(err, "Could not create flight manifest")
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func displayUsage() {
|
||||||
|
fmt.Println("spacewalk [stuff] [things]")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseArgs is the main entry point into scbm and will run the appropriate functions
|
||||||
|
// based on the arguments passed in at run time.
|
||||||
|
func parseArgs() {
|
||||||
|
a := os.Args
|
||||||
|
if len(a) == 1 {
|
||||||
|
displayUsage()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch a[1] {
|
||||||
|
case "create":
|
||||||
|
if len(a) == 3 {
|
||||||
|
create(a[2])
|
||||||
|
} else {
|
||||||
|
earlyExit("Missing flight name.\n- spacewalk create \033[3mflight\033[0m")
|
||||||
|
}
|
||||||
|
case "update":
|
||||||
|
if len(a) == 3 {
|
||||||
|
update(a[2])
|
||||||
|
} else {
|
||||||
|
earlyExit("Missing flight name.\n- spacewalk update \033[3mflight\033[0m")
|
||||||
|
}
|
||||||
|
case "remove":
|
||||||
|
if len(a) == 3 {
|
||||||
|
remove(a[2])
|
||||||
|
} else {
|
||||||
|
earlyExit("Missing flight name.\n- spacewalk remove \033[3mflight\033[0m")
|
||||||
|
}
|
||||||
|
case "add":
|
||||||
|
if len(a) == 5 {
|
||||||
|
add(a[2], a[3], a[4])
|
||||||
|
} else {
|
||||||
|
earlyExit("Incorrect syntax.\n- spacewalk add \033[3mflight url title\033[0m")
|
||||||
|
}
|
||||||
|
case "delete":
|
||||||
|
if len(a) == 4 {
|
||||||
|
del(a[2], a[3])
|
||||||
|
} else {
|
||||||
|
earlyExit("Incorrect syntax.\n- spacewalk delete \033[3mflight url|title\033[0m")
|
||||||
|
}
|
||||||
|
case "launch":
|
||||||
|
if len(a) == 2 {
|
||||||
|
launchFlights()
|
||||||
|
} else if len(a) == 3 {
|
||||||
|
launchFlight(a[2])
|
||||||
|
} else {
|
||||||
|
earlyExit("Incorrect syntax.\n- spacewalk launch [\033[3mflight\033[0m]")
|
||||||
|
}
|
||||||
|
case "show":
|
||||||
|
if len(a) == 2 {
|
||||||
|
showFlights("")
|
||||||
|
} else if len(a) == 3 {
|
||||||
|
showFlights(a[2])
|
||||||
|
} else {
|
||||||
|
earlyExit("Incorrect syntax.\n- spacewalk show [\033[3mflight\033[0m]")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
earlyExit(fmt.Sprintf("Unknown command %q", a[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLine(prefix string) (string, error) {
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
fmt.Print(prefix)
|
||||||
|
text, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return text[:len(text)-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readManifest() [][]string {
|
||||||
|
p := filepath.Join(swPath, "flight-manifest")
|
||||||
|
f, err := os.Open(p)
|
||||||
|
ifErrExit(err, "Could not open flight manifest")
|
||||||
|
defer f.Close()
|
||||||
|
r := csv.NewReader(f)
|
||||||
|
records, err := r.ReadAll()
|
||||||
|
ifErrExit(err, "Could not read from flight manifest")
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
// Command Execution
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
func create(flight string) {
|
||||||
|
var lp, hp, fp string
|
||||||
|
var err error
|
||||||
|
records := readManifest()
|
||||||
|
for _, r := range records {
|
||||||
|
if r[0] == flight {
|
||||||
|
earlyExit(fmt.Sprintf("There is already a flight with the name %q", flight))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Creating flight: %q\n", flight)
|
||||||
|
|
||||||
|
happy := false
|
||||||
|
for !happy {
|
||||||
|
lp, err = getLine("Enter the launch path (output path including file name): ")
|
||||||
|
ifErrExit(err, "Error reading from stdin")
|
||||||
|
if lp == "" {
|
||||||
|
fmt.Println("Launch path cannot be empty")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hp, err = getLine("Enter the header path, or leave blank for none: ")
|
||||||
|
ifErrExit(err, "Error reading from stdin")
|
||||||
|
if hp == "" {
|
||||||
|
hp = "none"
|
||||||
|
}
|
||||||
|
fp, err = getLine("Enter the footer path, or leave blank for none: ")
|
||||||
|
ifErrExit(err, "Error reading from stdin")
|
||||||
|
if fp == "" {
|
||||||
|
fp = "none"
|
||||||
|
}
|
||||||
|
fmt.Println("Are you happy with the following:")
|
||||||
|
fmt.Printf("Launch path: %s\nHeader path: %s\nFooter path: %s\n", lp, hp, fp)
|
||||||
|
yesNo, err := getLine("Type 'yes' to accept, anything else to redo: ")
|
||||||
|
ifErrExit(err, "Error reading from stdin")
|
||||||
|
|
||||||
|
if strings.ToLower(yesNo) == "yes" {
|
||||||
|
happy = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p := filepath.Join(swPath, "flight-manifest")
|
||||||
|
f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
ifErrExit(err, "Could not open flight manifest")
|
||||||
|
defer f.Close()
|
||||||
|
ln := fmt.Sprintf("%s, %s, %s, %s\n", flight, lp, hp, fp)
|
||||||
|
_, err = f.WriteString(ln)
|
||||||
|
ifErrExit(err, "Unable to save new flight to data file")
|
||||||
|
fpath := filepath.Join(fPath, flight)
|
||||||
|
ff, err := os.OpenFile(fpath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
ifErrExit(err, fmt.Sprintf("Could not create flight log for %s", flight))
|
||||||
|
ff.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(flight string) {
|
||||||
|
fmt.Println(flight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func remove(flight string) {
|
||||||
|
fmt.Println(flight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func add(flight, url, title string) {
|
||||||
|
p := filepath.Join(fPath, "flights", flight)
|
||||||
|
f, err := os.OpenFile(p, os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
ifErrExit(err, fmt.Sprintf("Could not open flight log for %s", flight))
|
||||||
|
defer f.Close()
|
||||||
|
ln := fmt.Sprintf("%s, %s, %s, %s\n", title, url, "nil", "0")
|
||||||
|
_, err = f.WriteString(ln)
|
||||||
|
ifErrExit(err, "Unable to save new capsule to data file")
|
||||||
|
fmt.Printf("Capsule %q added to %s's flight log", title, flight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func del(flight, item string) {
|
||||||
|
fmt.Println(flight, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func launchFlights() {
|
||||||
|
fmt.Println("Hello")
|
||||||
|
}
|
||||||
|
|
||||||
|
func launchFlight(flight string) {
|
||||||
|
fmt.Println(flight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func showFlights(flight string) {
|
||||||
|
records := readManifest()
|
||||||
|
for _, row := range records {
|
||||||
|
if flight != "" && row[0] != flight {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("\033[1mFlight Name:\033[0m %s\n", row[0])
|
||||||
|
fmt.Printf("\033[1mLaunch Path:\033[0m %s\n", row[1])
|
||||||
|
fmt.Printf("\033[1mHeader Path:\033[0m %s\n", row[2])
|
||||||
|
fmt.Printf("\033[1mFooter Path:\033[0m %s\n--\n", row[3])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
validateDataPaths()
|
||||||
|
parseArgs()
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
# spacewalk
|
||||||
|
|
||||||
|
This is intended to be a golang based moku-pona-_like_ program for gemini.
|
||||||
|
|
||||||
|
|
||||||
|
## commands
|
||||||
|
|
||||||
|
### create
|
||||||
|
|
||||||
|
Adds a new flight to the system's spacewalk flight list.
|
||||||
|
|
||||||
|
- spacewalk create _flight_
|
||||||
|
|
||||||
|
### update
|
||||||
|
|
||||||
|
Updates the settings for a flight
|
||||||
|
|
||||||
|
- spacewalk update _flight_
|
||||||
|
|
||||||
|
### remove
|
||||||
|
|
||||||
|
Removes an existing flight.
|
||||||
|
|
||||||
|
- spacewalk remove _flight_
|
||||||
|
|
||||||
|
### add
|
||||||
|
|
||||||
|
Adds a capsule to the a flight's feed. Requires the URL and a display name.
|
||||||
|
|
||||||
|
- spacewalk add flight _url_ _title_
|
||||||
|
|
||||||
|
### delete
|
||||||
|
|
||||||
|
Removes a capsule from the feed. Pass either a url or a display name.
|
||||||
|
|
||||||
|
- spacewalk delete flight _url|title_
|
||||||
|
|
||||||
|
### show
|
||||||
|
|
||||||
|
When called without further arguments, will list the available flights. When called with a flight as an argument will list the capsules for that flight.
|
||||||
|
|
||||||
|
- spacewalk show [flight]
|
||||||
|
|
||||||
|
### launch
|
||||||
|
|
||||||
|
Checks for updates and builds the directory capsule. Takes a flight name to launch just that flight or will launch all flights when not passed a flight name.
|
||||||
|
|
||||||
|
- spacewalk launch [flight]
|
||||||
|
|
||||||
|
|
||||||
|
## files
|
||||||
|
|
||||||
|
### flight config
|
||||||
|
|
||||||
|
Paths: $XDG\_DATA\_HOME/spacewalk/flight-config || ~/.local/share/spacewalk/flight-config
|
||||||
|
Format: csv
|
||||||
|
Structure: flight-name, flight-slug, launch-path, header-path, footer-path
|
||||||
|
|
||||||
|
### flight data
|
||||||
|
|
||||||
|
Paths: $XDG\_DATA\_HOME/spacewalk/flights/{flight-slug} || ~/.local/share/spacewalk/flights/{flight-slug}
|
||||||
|
Format: csv
|
||||||
|
Structure: capsule-name, capsule-url, checksum, timestamp
|
||||||
|
|
||||||
|
|
||||||
|
## glossary
|
||||||
|
|
||||||
|
capsule : a gemini site/page (a gemini URL)
|
||||||
|
flight : a collection of capsules for which spacewalk can generate directory capsules.
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue