serve

package
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 30, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (

	// Command is the serve command.
	Command = &cobra.Command{
		Use:                "serve",
		Short:              "Start the server",
		Args:               cobra.NoArgs,
		PersistentPreRunE:  cmd.InitBackendContext,
		PersistentPostRunE: cmd.CloseDBContext,
		RunE: func(c *cobra.Command, _ []string) error {
			ctx := c.Context()
			cfg := config.DefaultConfig()
			if cfg.Exist() {
				if err := cfg.ParseFile(); err != nil {
					return fmt.Errorf("parse config file: %w", err)
				}
			} else {
				if err := cfg.WriteConfig(); err != nil {
					return fmt.Errorf("write config file: %w", err)
				}
			}

			if err := cfg.ParseEnv(); err != nil {
				return fmt.Errorf("parse environment variables: %w", err)
			}

			customHooksPath := filepath.Join(cfg.DataPath, "hooks")
			if _, err := os.Stat(customHooksPath); err != nil && os.IsNotExist(err) {
				os.MkdirAll(customHooksPath, os.ModePerm)

				hookPath := filepath.Join(customHooksPath, "update.sample")

				if err := os.WriteFile(hookPath, []byte(updateHookExample), 0o744); err != nil {
					return fmt.Errorf("failed to generate update hook example: %w", err)
				}
			}

			logPath := filepath.Join(cfg.DataPath, "log")
			if _, err := os.Stat(logPath); err != nil && os.IsNotExist(err) {
				os.MkdirAll(logPath, os.ModePerm)
			}

			db := db.FromContext(ctx)
			if err := migrate.Migrate(ctx, db); err != nil {
				return fmt.Errorf("migration error: %w", err)
			}

			s, err := NewServer(ctx)
			if err != nil {
				return fmt.Errorf("start server: %w", err)
			}

			if syncHooks {
				be := backend.FromContext(ctx)
				if err := cmd.InitializeHooks(ctx, cfg, be); err != nil {
					return fmt.Errorf("initialize hooks: %w", err)
				}
			}

			lch := make(chan error, 1)
			done := make(chan os.Signal, 1)
			doneOnce := sync.OnceFunc(func() { close(done) })

			signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

			if testRun, _ := strconv.ParseBool(os.Getenv("SOFT_SERVE_TESTRUN")); testRun {
				h := s.HTTPServer.Server.Handler
				s.HTTPServer.Server.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					if r.URL.Path == "/__stop" && r.Method == http.MethodHead {
						doneOnce()
						return
					}
					h.ServeHTTP(w, r)
				})
			}

			go func() {
				lch <- s.Start()
				doneOnce()
			}()

			for {
				select {
				case err := <-lch:
					if err != nil {
						return fmt.Errorf("server error: %w", err)
					}
				case sig := <-done:
					if sig == syscall.SIGHUP {
						s.logger.Info("received SIGHUP signal, reloading TLS certificates if enabled")
						if err := s.ReloadCertificates(); err != nil {
							s.logger.Error("failed to reload TLS certificates", "err", err)
						}
						continue
					}
				}

				break
			}

			ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
			defer cancel()
			if err := s.Shutdown(ctx); err != nil {
				return err
			}

			return nil
		},
	}
)

Functions

This section is empty.

Types

type CertReloader

type CertReloader struct {
	// contains filtered or unexported fields
}

CertReloader is responsible for reloading TLS certificates when a SIGHUP signal is received.

func NewCertReloader

func NewCertReloader(certPath, keyPath string, logger *log.Logger) (*CertReloader, error)

NewCertReloader creates a new CertReloader that watches for SIGHUP signals.

func (*CertReloader) GetCertificateFunc

func (cr *CertReloader) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error)

GetCertificateFunc returns a function that can be used with tls.Config.GetCertificate.

func (*CertReloader) Reload

func (cr *CertReloader) Reload() error

Reload attempts to reload the certificate and key.

type Server

type Server struct {
	SSHServer   *sshsrv.SSHServer
	GitDaemon   *daemon.GitDaemon
	HTTPServer  *web.HTTPServer
	StatsServer *stats.StatsServer
	CertLoader  *CertReloader
	Cron        *cron.Scheduler
	Config      *config.Config
	Backend     *backend.Backend
	DB          *db.DB
	// contains filtered or unexported fields
}

Server is the Soft Serve server.

func NewServer

func NewServer(ctx context.Context) (*Server, error)

NewServer returns a new *Server configured to serve Soft Serve. The SSH server key-pair will be created if none exists. It expects a context with *backend.Backend, *db.DB, *log.Logger, and *config.Config attached.

func (*Server) Close

func (s *Server) Close() error

Close closes the SSH server.

func (*Server) ReloadCertificates

func (s *Server) ReloadCertificates() error

ReloadCertificates reloads the TLS certificates for the HTTP server.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown lets the server gracefully shutdown.

func (*Server) Start

func (s *Server) Start() error

Start starts the SSH server.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL