aboutsummaryrefslogtreecommitdiff
path: root/forged
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--forged/internal/ansiec/doc.go (renamed from forged/internal/ansiec/ansiec.go)0
-rw-r--r--forged/internal/bare/doc.go (renamed from forged/internal/bare/package.go)0
-rw-r--r--forged/internal/config/config.go34
-rw-r--r--forged/internal/database/database.go4
-rw-r--r--forged/internal/git2c/client.go1
-rw-r--r--forged/internal/git2c/doc.go2
-rw-r--r--forged/internal/hooki/hooki.go45
-rw-r--r--forged/internal/irc/bot.go1
-rw-r--r--forged/internal/irc/doc.go2
-rw-r--r--forged/internal/lmtp/config.go53
-rw-r--r--forged/internal/misc/misc.go13
-rw-r--r--forged/internal/misc/slices.go17
-rw-r--r--forged/internal/misc/usock.go23
-rw-r--r--forged/internal/server/server.go48
-rw-r--r--forged/main.go5
15 files changed, 211 insertions, 37 deletions
diff --git a/forged/internal/ansiec/ansiec.go b/forged/internal/ansiec/doc.go
index 542c564..542c564 100644
--- a/forged/internal/ansiec/ansiec.go
+++ b/forged/internal/ansiec/doc.go
diff --git a/forged/internal/bare/package.go b/forged/internal/bare/doc.go
index 2f12f55..2f12f55 100644
--- a/forged/internal/bare/package.go
+++ b/forged/internal/bare/doc.go
diff --git a/forged/internal/config/config.go b/forged/internal/config/config.go
index d3eb8e1..0166390 100644
--- a/forged/internal/config/config.go
+++ b/forged/internal/config/config.go
@@ -1,8 +1,14 @@
package config
import (
+ "bufio"
+ "log/slog"
+ "os"
+
"go.lindenii.runxiyu.org/forge/forged/internal/database"
+ "go.lindenii.runxiyu.org/forge/forged/internal/hooki"
"go.lindenii.runxiyu.org/forge/forged/internal/irc"
+ "go.lindenii.runxiyu.org/forge/forged/internal/scfg"
)
type Config struct {
@@ -16,11 +22,8 @@ type Config struct {
IdleTimeout uint32 `scfg:"idle_timeout"`
ReverseProxy bool `scfg:"reverse_proxy"`
} `scfg:"http"`
- Hooks struct {
- Socket string `scfg:"socket"`
- Execs string `scfg:"execs"`
- } `scfg:"hooks"`
- LMTP struct {
+ Hooks hooki.Config `scfg:"hooks"`
+ LMTP struct {
Socket string `scfg:"socket"`
Domain string `scfg:"domain"`
MaxSize int64 `scfg:"max_size"`
@@ -42,9 +45,28 @@ type Config struct {
General struct {
Title string `scfg:"title"`
} `scfg:"general"`
- DB database.Config `scfg:"db"`
+ DB database.Config `scfg:"db"`
Pprof struct {
Net string `scfg:"net"`
Addr string `scfg:"addr"`
} `scfg:"pprof"`
}
+
+func Open(path string) (config Config, err error) {
+ var configFile *os.File
+
+ if configFile, err = os.Open(path); err != nil {
+ return config, err
+ }
+ defer configFile.Close()
+
+ decoder := scfg.NewDecoder(bufio.NewReader(configFile))
+ if err = decoder.Decode(&config); err != nil {
+ return config, err
+ }
+ for _, u := range decoder.UnknownDirectives() {
+ slog.Warn("unknown configuration directive", "directive", u)
+ }
+
+ return config, err
+}
diff --git a/forged/internal/database/database.go b/forged/internal/database/database.go
index 6674fe6..f353fe8 100644
--- a/forged/internal/database/database.go
+++ b/forged/internal/database/database.go
@@ -19,8 +19,8 @@ type Database struct {
// Open opens a new database connection pool using the provided connection
// string. It returns a Database instance and an error if any occurs.
// It is run indefinitely in the background.
-func Open(connString string) (Database, error) {
- db, err := pgxpool.New(context.Background(), connString)
+func Open(ctx context.Context, config Config) (Database, error) {
+ db, err := pgxpool.New(ctx, config.Conn)
return Database{db}, err
}
diff --git a/forged/internal/git2c/client.go b/forged/internal/git2c/client.go
index ed9390c..d8dc2ea 100644
--- a/forged/internal/git2c/client.go
+++ b/forged/internal/git2c/client.go
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
-// Package git2c provides routines to interact with the git2d backend daemon.
package git2c
import (
diff --git a/forged/internal/git2c/doc.go b/forged/internal/git2c/doc.go
new file mode 100644
index 0000000..e14dae0
--- /dev/null
+++ b/forged/internal/git2c/doc.go
@@ -0,0 +1,2 @@
+// Package git2c provides routines to interact with the git2d backend daemon.
+package git2c
diff --git a/forged/internal/hooki/hooki.go b/forged/internal/hooki/hooki.go
index ae26846..8e75bae 100644
--- a/forged/internal/hooki/hooki.go
+++ b/forged/internal/hooki/hooki.go
@@ -1,13 +1,26 @@
package hooki
import (
- "go.lindenii.runxiyu.org/forge/forged/internal/cmap"
+ "fmt"
+ "net"
+
"github.com/gliderlabs/ssh"
+ "go.lindenii.runxiyu.org/forge/forged/internal/cmap"
+ "go.lindenii.runxiyu.org/forge/forged/internal/misc"
)
-type Pool cmap.Map[string, hookinfo]
+type Pool struct {
+ hookMap cmap.Map[string, hookInfo]
+ socketPath string
+ executablesPath string
+}
-type hookinfo struct {
+type Config struct {
+ Socket string `scfg:"socket"`
+ Execs string `scfg:"execs"`
+}
+
+type hookInfo struct {
session ssh.Session
pubkey string
directAccess bool
@@ -19,3 +32,29 @@ type hookinfo struct {
repoName string
contribReq string
}
+
+func New(config Config) (pool Pool) {
+ pool.socketPath = config.Socket
+ pool.executablesPath = config.Execs
+ return
+}
+
+func (pool *Pool) Run() error {
+ listener, _, err := misc.ListenUnixSocket(pool.socketPath)
+ if err != nil {
+ return fmt.Errorf("listen unix socket for hooks: %w", err)
+ }
+
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ return fmt.Errorf("accept conn: %w", err)
+ }
+
+ go pool.handleConn(conn)
+ }
+}
+
+func (pool *Pool) handleConn(conn net.Conn) {
+ panic("TODO: handle hook connection")
+}
diff --git a/forged/internal/irc/bot.go b/forged/internal/irc/bot.go
index 1c6d32f..3ebac89 100644
--- a/forged/internal/irc/bot.go
+++ b/forged/internal/irc/bot.go
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
-// Package irc provides basic IRC bot functionality.
package irc
import (
diff --git a/forged/internal/irc/doc.go b/forged/internal/irc/doc.go
new file mode 100644
index 0000000..dcfca82
--- /dev/null
+++ b/forged/internal/irc/doc.go
@@ -0,0 +1,2 @@
+// Package irc provides basic IRC bot functionality.
+package irc
diff --git a/forged/internal/lmtp/config.go b/forged/internal/lmtp/config.go
new file mode 100644
index 0000000..ce22740
--- /dev/null
+++ b/forged/internal/lmtp/config.go
@@ -0,0 +1,53 @@
+package lmtp
+
+import (
+ "fmt"
+ "net"
+
+ "go.lindenii.runxiyu.org/forge/forged/internal/misc"
+)
+
+type Pool struct {
+ socket string
+ domain string
+ maxSize int64
+ writeTimeout uint32
+ readTimeout uint32
+}
+
+type Config struct {
+ Socket string `scfg:"socket"`
+ Domain string `scfg:"domain"`
+ MaxSize int64 `scfg:"max_size"`
+ WriteTimeout uint32 `scfg:"write_timeout"`
+ ReadTimeout uint32 `scfg:"read_timeout"`
+}
+
+func New(config Config) (pool Pool) {
+ pool.socket = config.Socket
+ pool.domain = config.Domain
+ pool.maxSize = config.MaxSize
+ pool.writeTimeout = config.WriteTimeout
+ pool.readTimeout = config.ReadTimeout
+ return pool
+}
+
+func (pool *Pool) Run() error {
+ listener, _, err := misc.ListenUnixSocket(pool.socket)
+ if err != nil {
+ return fmt.Errorf("listen unix socket for LMTP: %w", err)
+ }
+
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ return fmt.Errorf("accept conn: %w", err)
+ }
+
+ go pool.handleConn(conn)
+ }
+}
+
+func (pool *Pool) handleConn(conn net.Conn) {
+ panic("TODO: handle LMTP connection")
+}
diff --git a/forged/internal/misc/misc.go b/forged/internal/misc/misc.go
index 398020a..e9e10ab 100644
--- a/forged/internal/misc/misc.go
+++ b/forged/internal/misc/misc.go
@@ -3,16 +3,3 @@
// Package misc provides miscellaneous functions and other definitions.
package misc
-
-import "strings"
-
-// sliceContainsNewlines returns true if and only if the given slice contains
-// one or more strings that contains newlines.
-func SliceContainsNewlines(s []string) bool {
- for _, v := range s {
- if strings.Contains(v, "\n") {
- return true
- }
- }
- return false
-}
diff --git a/forged/internal/misc/slices.go b/forged/internal/misc/slices.go
new file mode 100644
index 0000000..3ad0211
--- /dev/null
+++ b/forged/internal/misc/slices.go
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
+
+package misc
+
+import "strings"
+
+// sliceContainsNewlines returns true if and only if the given slice contains
+// one or more strings that contains newlines.
+func SliceContainsNewlines(s []string) bool {
+ for _, v := range s {
+ if strings.Contains(v, "\n") {
+ return true
+ }
+ }
+ return false
+}
diff --git a/forged/internal/misc/usock.go b/forged/internal/misc/usock.go
new file mode 100644
index 0000000..357fa43
--- /dev/null
+++ b/forged/internal/misc/usock.go
@@ -0,0 +1,23 @@
+package misc
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "syscall"
+)
+
+func ListenUnixSocket(path string) (listener net.Listener, replaced bool, err error) {
+ listener, err = net.Listen("unix", path)
+ if errors.Is(err, syscall.EADDRINUSE) {
+ replaced = true
+ if unlinkErr := syscall.Unlink(path); unlinkErr != nil {
+ return listener, false, fmt.Errorf("remove existing socket %q: %w", path, unlinkErr)
+ }
+ listener, err = net.Listen("unix", path)
+ }
+ if err != nil {
+ return listener, replaced, fmt.Errorf("listen on unix socket %q: %w", path, err)
+ }
+ return listener, replaced, nil
+}
diff --git a/forged/internal/server/server.go b/forged/internal/server/server.go
index ec875f1..03570c3 100644
--- a/forged/internal/server/server.go
+++ b/forged/internal/server/server.go
@@ -4,29 +4,59 @@ import (
"context"
"fmt"
"html/template"
+ "log"
"go.lindenii.runxiyu.org/forge/forged/internal/config"
"go.lindenii.runxiyu.org/forge/forged/internal/database"
"go.lindenii.runxiyu.org/forge/forged/internal/hooki"
+ "go.lindenii.runxiyu.org/forge/forged/internal/lmtp"
"go.lindenii.runxiyu.org/forge/forged/internal/store"
)
type Server struct {
config config.Config
- database database.Database
- stores *store.Set
- hookis *hooki.Pool
+ database database.Database
+ stores *store.Set
+ hookPool hooki.Pool
+ lmtpPool lmtp.Pool
templates *template.Template
}
-func New(ctx context.Context, config config.Config) (*Server, error) {
- database, err := database.Open(ctx, config.DB)
+func New(ctx context.Context, configPath string) (server *Server, err error) {
+ server = &Server{}
+
+ server.config, err = config.Open(configPath)
if err != nil {
- return nil, fmt.Errorf("open database: %w", err)
+ return server, fmt.Errorf("open config: %w", err)
}
- return &Server{
- database: database,
- }, nil
+ // TODO: Should this belong here, or in Run()?
+ server.database, err = database.Open(ctx, server.config.DB)
+ if err != nil {
+ return server, fmt.Errorf("open database: %w", err)
+ }
+
+ return server, nil
+}
+
+func (s *Server) Run() error {
+ // TODO: Not running git2d because it should be run separately.
+ // This needs to be documented somewhere, hence a TODO here for now.
+
+ go func() {
+ s.hookPool = hooki.New(s.config.Hooks)
+ if err := s.hookPool.Run(); err != nil {
+ log.Fatalf("run hook pool: %v", err)
+ }
+ }()
+
+ go func() {
+ s.lmtpPool = lmtp.New(s.config.LMTP)
+ if err := s.lmtpPool.Run(); err != nil {
+ log.Fatalf("run LMTP pool: %v", err)
+ }
+ }()
+
+ return nil
}
diff --git a/forged/main.go b/forged/main.go
index fde15d1..e9609f3 100644
--- a/forged/main.go
+++ b/forged/main.go
@@ -5,9 +5,10 @@
package main
import (
+ "context"
"flag"
- "go.lindenii.runxiyu.org/forge/forged/internal/unsorted"
+ "go.lindenii.runxiyu.org/forge/forged/internal/server"
)
func main() {
@@ -18,7 +19,7 @@ func main() {
)
flag.Parse()
- s, err := unsorted.NewServer(*configPath)
+ s, err := server.New(context.Background(), *configPath)
if err != nil {
panic(err)
}