aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-04-05 20:57:01 +0900
committerOphestra <cat@gensokyo.uk>2026-04-05 23:58:08 +0900
commita69273ab2a7040ba906876e6d7888b4edeb74ebc (patch)
tree0379e49590110d6f4505e0de1671506a46da47b7 /cmd
parent4cd0f57e48c94e7f628446d050027165fdd9e315 (diff)
cmd/dist: replace dist/release.sh
This is much more robust than a shell script. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/dist/comp/_hakurei85
-rw-r--r--cmd/dist/main.go232
2 files changed, 317 insertions, 0 deletions
diff --git a/cmd/dist/comp/_hakurei b/cmd/dist/comp/_hakurei
new file mode 100644
index 00000000..11424612
--- /dev/null
+++ b/cmd/dist/comp/_hakurei
@@ -0,0 +1,85 @@
+#compdef hakurei
+
+_hakurei_run() {
+ __hakurei_files
+ return $?
+}
+
+_hakurei_exec() {
+ _arguments \
+ '--id[Reverse-DNS style Application identifier, leave empty to inherit instance identifier]:id' \
+ '-a[Application identity]: :_numbers' \
+ '-g[Groups inherited by all container processes]: :_groups' \
+ '-d[Container home directory]: :_files -/' \
+ '-u[Passwd user name within sandbox]: :_users' \
+ '--private-runtime[Do not share XDG_RUNTIME_DIR between containers under the same identity]' \
+ '--private-tmpdir[Do not share TMPDIR between containers under the same identity]' \
+ '--wayland[Enable connection to Wayland via security-context-v1]' \
+ '-X[Enable direct connection to X11]' \
+ '--dbus[Enable proxied connection to D-Bus]' \
+ '--pipewire[Enable connection to PipeWire via SecurityContext]' \
+ '--pulse[Enable PulseAudio compatibility daemon]' \
+ '--dbus-config[Path to session bus proxy config file]: :_files -g "*.json"' \
+ '--dbus-system[Path to system bus proxy config file]: :_files -g "*.json"' \
+ '--mpris[Allow owning MPRIS D-Bus path]' \
+ '--dbus-log[Force buffered logging in the D-Bus proxy]'
+}
+
+_hakurei_ps() {
+ _arguments \
+ '--short[List instances only]'
+}
+
+_hakurei_show() {
+ _alternative \
+ 'instances:domains:__hakurei_instances' \
+ 'files:files:__hakurei_files'
+}
+
+__hakurei_files() {
+ _files -g "*.(json|hakurei)"
+ return $?
+}
+
+__hakurei_instances() {
+ local -a out
+ shift -p
+ out=( ${(f)"$(_call_program commands hakurei ps --short 2>&1)"} )
+ if (( $#out == 0 )); then
+ _message "No active instances"
+ else
+ _describe "active instances" out
+ fi
+ return $?
+}
+
+(( $+functions[_hakurei_commands] )) || _hakurei_commands()
+{
+ local -a _hakurei_cmds
+ _hakurei_cmds=(
+ "run:Load and start container from configuration file"
+ "exec:Configure and start a permissive container"
+ "show:Show live or local instance configuration"
+ "ps:List active instances"
+ "version:Display version information"
+ "license:Show full license text"
+ "template:Produce a config template"
+ "help:Show help message"
+ )
+ if (( CURRENT == 1 )); then
+ _describe -t commands 'action' _hakurei_cmds || compadd "$@"
+ else
+ local curcontext="$curcontext"
+ cmd="${${_hakurei_cmds[(r)$words[1]:*]%%:*}}"
+ if (( $+functions[_hakurei_$cmd] )); then
+ _hakurei_$cmd
+ else
+ _message "no more options"
+ fi
+ fi
+}
+
+_arguments -C \
+ '-v[Increase log verbosity]' \
+ '--json[Serialise output in JSON when applicable]' \
+ '*::hakurei command:_hakurei_commands' \ No newline at end of file
diff --git a/cmd/dist/main.go b/cmd/dist/main.go
new file mode 100644
index 00000000..d3555e86
--- /dev/null
+++ b/cmd/dist/main.go
@@ -0,0 +1,232 @@
+//go:build dist
+
+package main
+
+import (
+ "archive/tar"
+ "compress/gzip"
+ "context"
+ "crypto/sha512"
+ _ "embed"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "io/fs"
+ "log"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "runtime"
+)
+
+// getenv looks up an environment variable, and returns fallback if it is unset.
+func getenv(key, fallback string) string {
+ if v, ok := os.LookupEnv(key); ok {
+ return v
+ }
+ return fallback
+}
+
+// mustRun runs a command with the current process's environment and panics
+// on error or non-zero exit code.
+func mustRun(ctx context.Context, name string, arg ...string) {
+ cmd := exec.CommandContext(ctx, name, arg...)
+ cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
+ if err := cmd.Run(); err != nil {
+ panic(err)
+ }
+}
+
+//go:embed comp/_hakurei
+var comp []byte
+
+func main() {
+ fmt.Println()
+ log.SetFlags(0)
+ log.SetPrefix("# ")
+
+ version := getenv("HAKUREI_VERSION", "untagged")
+ prefix := getenv("PREFIX", "/usr")
+ destdir := getenv("DESTDIR", "dist")
+
+ if err := os.MkdirAll(destdir, 0755); err != nil {
+ log.Fatal(err)
+ }
+ s, err := os.MkdirTemp(destdir, ".dist.*")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer func() {
+ var code int
+
+ if err = os.RemoveAll(s); err != nil {
+ code = 1
+ log.Println(err)
+ }
+
+ if r := recover(); r != nil {
+ code = 1
+ log.Println(r)
+ }
+
+ os.Exit(code)
+ }()
+
+ ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
+ defer cancel()
+
+ log.Println("Building hakurei.")
+ mustRun(ctx, "go", "generate", "./...")
+ mustRun(
+ ctx, "go", "build",
+ "-trimpath",
+ "-v", "-o", s,
+ "-ldflags=-s -w "+
+ "-buildid= -linkmode external -extldflags=-static "+
+ "-X hakurei.app/internal/info.buildVersion="+version+" "+
+ "-X hakurei.app/internal/info.hakureiPath="+prefix+"/bin/hakurei "+
+ "-X hakurei.app/internal/info.hsuPath="+prefix+"/bin/hsu "+
+ "-X main.hakureiPath="+prefix+"/bin/hakurei",
+ "./...",
+ )
+ fmt.Println()
+
+ log.Println("Testing Hakurei.")
+ mustRun(
+ ctx, "go", "test",
+ "-ldflags=-buildid= -linkmode external -extldflags=-static",
+ "./...",
+ )
+ fmt.Println()
+
+ log.Println("Creating distribution.")
+ const suffix = ".tar.gz"
+ distName := "hakurei-" + version + "-" + runtime.GOARCH
+ var f *os.File
+ if f, err = os.OpenFile(
+ filepath.Join(s, distName+suffix),
+ os.O_CREATE|os.O_EXCL|os.O_WRONLY,
+ 0644,
+ ); err != nil {
+ panic(err)
+ }
+ defer func() {
+ if f == nil {
+ return
+ }
+ if err = f.Close(); err != nil {
+ log.Println(err)
+ }
+ }()
+
+ h := sha512.New()
+ gw := gzip.NewWriter(io.MultiWriter(f, h))
+ tw := tar.NewWriter(gw)
+
+ mustWriteHeader := func(name string, size int64, mode os.FileMode) {
+ header := tar.Header{
+ Name: filepath.Join(distName, name),
+ Size: size,
+ Mode: int64(mode),
+ Uname: "root",
+ Gname: "root",
+ }
+
+ if mode&os.ModeDir != 0 {
+ header.Typeflag = tar.TypeDir
+ fmt.Printf("%s %s\n", mode, name)
+ } else {
+ header.Typeflag = tar.TypeReg
+ fmt.Printf("%s %s (%d bytes)\n", mode, name, size)
+ }
+
+ if err = tw.WriteHeader(&header); err != nil {
+ panic(err)
+ }
+ }
+
+ mustWriteFile := func(name string, data []byte, mode os.FileMode) {
+ mustWriteHeader(name, int64(len(data)), mode)
+ if mode&os.ModeDir != 0 {
+ return
+ }
+ if _, err = tw.Write(data); err != nil {
+ panic(err)
+ }
+ }
+
+ mustWriteFromPath := func(dst, src string) {
+ var r *os.File
+ if r, err = os.Open(src); err != nil {
+ panic(err)
+ }
+
+ var fi os.FileInfo
+ if fi, err = r.Stat(); err != nil {
+ _ = r.Close()
+ panic(err)
+ }
+
+ mustWriteHeader(dst, fi.Size(), fi.Mode())
+ if _, err = io.Copy(tw, r); err != nil {
+ _ = r.Close()
+ panic(err)
+ } else if err = r.Close(); err != nil {
+ panic(err)
+ }
+ }
+
+ mustWriteFile(".", nil, fs.ModeDir|0755)
+ mustWriteFile("comp/", nil, os.ModeDir|0755)
+ mustWriteFile("comp/_hakurei", comp, 0644)
+ mustWriteFile("install.sh", []byte(`#!/bin/sh -e
+cd "$(dirname -- "$0")" || exit 1
+
+install -vDm0755 "bin/hakurei" "${DESTDIR}`+prefix+`/bin/hakurei"
+install -vDm0755 "bin/sharefs" "${DESTDIR}`+prefix+`/bin/sharefs"
+
+install -vDm4511 "bin/hsu" "${DESTDIR}`+prefix+`/bin/hsu"
+if [ ! -f "${DESTDIR}/etc/hsurc" ]; then
+ install -vDm0400 "hsurc.default" "${DESTDIR}/etc/hsurc"
+fi
+
+install -vDm0644 "comp/_hakurei" "${DESTDIR}`+prefix+`/share/zsh/site-functions/_hakurei"
+`), 0755)
+
+ mustWriteFromPath("README.md", "README.md")
+ mustWriteFile("hsurc.default", []byte("1000 0"), 0400)
+ for _, name := range []string{
+ "hsu",
+ "hakurei",
+ "sharefs",
+ } {
+ mustWriteFromPath(
+ filepath.Join("bin", name),
+ filepath.Join(s, name),
+ )
+ }
+
+ if err = tw.Close(); err != nil {
+ panic(err)
+ } else if err = gw.Close(); err != nil {
+ panic(err)
+ } else if err = f.Close(); err != nil {
+ panic(err)
+ }
+ f = nil
+
+ if err = os.WriteFile(
+ filepath.Join(destdir, distName+suffix+".sha512"),
+ append(hex.AppendEncode(nil, h.Sum(nil)), " "+distName+suffix+"\n"...),
+ 0644,
+ ); err != nil {
+ panic(err)
+ }
+ if err = os.Rename(
+ filepath.Join(s, distName+suffix),
+ filepath.Join(destdir, distName+suffix),
+ ); err != nil {
+ panic(err)
+ }
+}