Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ jobs:
run: make build

- name: Use golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v9
with:
version: v1.64.6
args: --print-issued-lines
version: v2.7.2

- name: Run tests
run: make test
19 changes: 19 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,22 @@ jobs:
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

build-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Log in to the Container registry
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
registry: "https://ghcr.io"
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/prbf2-tools/svctl:latest,ghcr.io/prbf2-tools/svctl:${{ github.ref_name }}
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build stage
FROM golang:1.24.1-alpine AS builder
FROM golang:1.25.5-alpine AS builder

# Install required tools for building
RUN apk add --no-cache git make protoc protobuf-dev
Expand All @@ -23,10 +23,10 @@ COPY main.go main.go
COPY Makefile ./

# Generate code and build the daemon
RUN make build
RUN make build-linux

# Runtime stage
FROM alpine:latest
FROM alpine:3.21

# Install ca-certificates for HTTPS requests and Docker CLI for Docker game servers
RUN apk add ca-certificates docker-cli
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ lint:
grpc:
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
svctl/svctl.proto
svctl/v1/svctl.proto

76 changes: 58 additions & 18 deletions cmd/common.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,45 @@
package cmd

import (
"fmt"
"net"
"os"
"path/filepath"

"github.com/sboon-gg/svctl/internal/server"
"github.com/prbf2-tools/svctl/svctl/v1"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)

const (
defaultSettingsPath = ".svctl"

defaultDaemonHost = "127.0.0.1"
defaultDaemonPort = "50051"
)

type serverOpts struct {
serverPath string
id string
settingsPath string
}

func newServerOpts() *serverOpts {
return &serverOpts{
serverPath: ".",
settingsPath: defaultSettingsPath,
}
}

func (opts *serverOpts) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVarP(&opts.serverPath, "path", "p", opts.serverPath, "Path to server directory")
cmd.Flags().StringVar(&opts.settingsPath, "settings", opts.settingsPath, "Path to settings directory")
func (opts *serverOpts) PreRunE(cmd *cobra.Command, args []string) error {
opts.id = args[0]

return nil
}

func (opts *serverOpts) Path() (string, error) {
if filepath.IsAbs(opts.serverPath) {
return opts.serverPath, nil
}
func (opts *serverOpts) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&opts.settingsPath, "settings", opts.settingsPath, "Path to settings directory")

return concatWithWorkingDir(opts.serverPath)
_ = cmd.MarkFlagDirname("settings")
}

func (opts *serverOpts) SettingsPath() (string, error) {
Expand All @@ -54,16 +59,51 @@ func concatWithWorkingDir(path string) (string, error) {
return filepath.Join(wd, path), nil
}

func (opts *serverOpts) Server() (*server.Server, error) {
path, err := opts.Path()
if err != nil {
return nil, err
type daemonConnOpts struct {
host string
port string
}

func newDaemonConnOpts() *daemonConnOpts {
return &daemonConnOpts{
host: defaultDaemonHost,
port: defaultDaemonPort,
}
}

svctlPath, err := opts.SettingsPath()
func (o *daemonConnOpts) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.host, "host", o.host, "Daemon host")
cmd.Flags().StringVar(&o.port, "port", o.port, "Daemon port")
}

func (o *daemonConnOpts) address() string {
return net.JoinHostPort(o.host, o.port)
}

func (o *daemonConnOpts) Client() (svctl.ServersClient, *grpc.ClientConn, error) {
conn, err := grpc.NewClient(o.address(), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, err
return nil, nil, fmt.Errorf("failed to connect to gRPC server at %s: %w", o.address(), err)
}

return server.Open(path, svctlPath)
client := svctl.NewServersClient(conn)

return client, conn, nil
}

type grpcClientCmdOpts struct {
*daemonConnOpts
*serverOpts
}

func newGrpcClientCmdOpts() *grpcClientCmdOpts {
return &grpcClientCmdOpts{
daemonConnOpts: newDaemonConnOpts(),
serverOpts: newServerOpts(),
}
}

func (o *grpcClientCmdOpts) AddFlags(cmd *cobra.Command) {
o.daemonConnOpts.AddFlags(cmd)
o.serverOpts.AddFlags(cmd)
}
32 changes: 10 additions & 22 deletions cmd/daemon.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
package cmd

import (
"log"
"fmt"
"net"

"github.com/sboon-gg/svctl/internal/api"
"github.com/sboon-gg/svctl/internal/daemon"
"github.com/sboon-gg/svctl/svctl"
"github.com/prbf2-tools/svctl/internal/api"
"github.com/prbf2-tools/svctl/internal/daemon"
"github.com/prbf2-tools/svctl/svctl/v1"
"github.com/spf13/cobra"
"google.golang.org/grpc"
)

const (
defaultDaemonHost = "127.0.0.1"
defaultDaemonPort = "50051"
)

type daemonOpts struct {
host string
port string
*daemonConnOpts
configFile string
}

func newDaemonOpts() *daemonOpts {
return &daemonOpts{
host: defaultDaemonHost,
port: defaultDaemonPort,
daemonConnOpts: newDaemonConnOpts(),
}
}

Expand All @@ -43,8 +36,7 @@ func daemonCmd() *cobra.Command {
}

func (o *daemonOpts) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.host, "host", o.host, "Host to listen on")
cmd.Flags().StringVar(&o.port, "port", o.port, "Port to listen on")
o.daemonConnOpts.AddFlags(cmd)
cmd.Flags().StringVar(&o.configFile, "config", o.configFile, "Path to the daemon config file")
}

Expand All @@ -56,29 +48,25 @@ func (o *daemonOpts) Run(cmd *cobra.Command, args []string) error {

lis, err := net.Listen("tcp", o.address())
if err != nil {
log.Fatalf("failed to listen on address %s: %v", o.address(), err)
return fmt.Errorf("failed to listen on address %s: %w", o.address(), err)
}

s := grpc.NewServer()
svctl.RegisterServersServer(s, api.NewDaemonServer(d))
log.Printf("gRPC server listening at %v", lis.Addr())
cmd.Printf("gRPC server listening at %v", lis.Addr())

go func() {
<-cmd.Context().Done()
s.GracefulStop()
}()

if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
return fmt.Errorf("failed to serve: %w", err)
}

return nil
}

func (o *daemonOpts) address() string {
return net.JoinHostPort(o.host, o.port)
}

func init() {
rootCmd.AddCommand(daemonCmd())
}
Loading