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
79 changes: 72 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,30 +43,95 @@ jobs:
bin/druid_rcon_web_rust
.docker/entrypoint.sh
.docker/druid-install-command.sh
docker:
docker-base:
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
include:
- dockerfile: Dockerfile
tag_suffix: ""
- dockerfile: Dockerfile.steamcmd
tag_suffix: "-steamcmd"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to Artifacts Registry
uses: docker/login-action@v3
with:
registry: artifacts.druid.gg
username: ${{ vars.DRUID_ARTIFACTS_REGISTRY_USERNAME }}
password: ${{ secrets.DRUID_ARTIFACTS_REGISTRY_TOKEN }}
#stable is still pretty bleeding edge at this point
- name: Build and push Docker image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v6
with:
file: Dockerfile
context: .
file: ${{ matrix.dockerfile }}
tags: |
highcard/druidd-base:latest
highcard/druidd-base:stable
highcard/druidd-base:${{ needs.build.outputs.version }}
highcard/druidd-base:${{ needs.build.outputs.version_tag }}
highcard/druid:latest${{ matrix.tag_suffix }}
highcard/druid:stable${{ matrix.tag_suffix }}
highcard/druid:${{ needs.build.outputs.version }}${{ matrix.tag_suffix }}
highcard/druid:${{ needs.build.outputs.version_tag }}${{ matrix.tag_suffix }}
artifacts.druid.gg/druid-team/druid:latest${{ matrix.tag_suffix }}
artifacts.druid.gg/druid-team/druid:stable${{ matrix.tag_suffix }}
artifacts.druid.gg/druid-team/druid:${{ needs.build.outputs.version }}${{ matrix.tag_suffix }}
artifacts.druid.gg/druid-team/druid:${{ needs.build.outputs.version_tag }}${{ matrix.tag_suffix }}
push: true
build-args: |
VERSION=${{ needs.build.outputs.version }}
GIT_COMMIT=${{ github.sha }}
GIT_BRANCH=${{ github.ref_name }}

docker-nix:
runs-on: ubuntu-latest
needs: [build, docker-base]
strategy:
matrix:
include:
- base_tag_suffix: ""
tag_suffix: "-nix"
- base_tag_suffix: "-steamcmd"
tag_suffix: "-nix-steamcmd"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to Artifacts Registry
uses: docker/login-action@v3
with:
registry: artifacts.druid.gg
username: ${{ vars.DRUID_ARTIFACTS_REGISTRY_USERNAME }}
password: ${{ secrets.DRUID_ARTIFACTS_REGISTRY_TOKEN }}
- name: Build and push Nix Docker image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.nix
tags: |
highcard/druid:latest${{ matrix.tag_suffix }}
highcard/druid:stable${{ matrix.tag_suffix }}
highcard/druid:${{ needs.build.outputs.version }}${{ matrix.tag_suffix }}
highcard/druid:${{ needs.build.outputs.version_tag }}${{ matrix.tag_suffix }}
artifacts.druid.gg/druid-team/druid:latest${{ matrix.tag_suffix }}
artifacts.druid.gg/druid-team/druid:stable${{ matrix.tag_suffix }}
artifacts.druid.gg/druid-team/druid:${{ needs.build.outputs.version }}${{ matrix.tag_suffix }}
artifacts.druid.gg/druid-team/druid:${{ needs.build.outputs.version_tag }}${{ matrix.tag_suffix }}
push: true
build-args: |
VERSION=${{ needs.build.outputs.version }}${{ matrix.base_tag_suffix }}
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ RUN make build-plugins
# The binaries are in ./bin/ directory after build

# Second stage: minimal runtime image
FROM debian:bullseye-slim
FROM ubuntu:24.04

RUN apt-get update && apt-get install -y \
ca-certificates \
Expand Down
14 changes: 14 additions & 0 deletions Dockerfile.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ARG VERSION=latest
FROM highcard/druid:${VERSION}
USER root

RUN apt-get update && apt-get install -y \
curl xz-utils \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -m 0755 /nix && chown druid /nix
USER druid

# install Nix package manager
RUN bash -c "sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --no-daemon"
# Make nix available in PATH for all RUN commands
ENV PATH=/home/druid/.nix-profile/bin:/home/druid/.nix-profile/sbin:$PATH
16 changes: 16 additions & 0 deletions Dockerfile.steamcmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ARG VERSION=latest
FROM highcard/druid:${VERSION} AS base
FROM gameservermanagers/steamcmd:ubuntu-24.04

COPY --from=base /usr/bin/druid* /usr/bin/
COPY --from=base /entrypoint.sh /entrypoint.sh

# Set up user with the same UID/GID
ARG UID=1000
ARG GID=1000
RUN groupadd -g $GID -o druid
RUN useradd -m -u $UID -g $GID -o -s /bin/bash druid

USER druid

ENTRYPOINT [ "/entrypoint.sh" ]
6 changes: 5 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ var RunCmd = &cobra.Command{
if err != nil {
return fmt.Errorf("error creating scroll service: %w", err)
}
processLauncher := services.NewProcedureLauncher(client, processManager, services.NewPluginManager(), consoleService, logManager, scrollService)
processLauncher, err := services.NewProcedureLauncher(client, processManager, services.NewPluginManager(), consoleService, logManager, scrollService, dependencyResolution)
if err != nil {
return err
}

queueManager := services.NewQueueManager(scrollService, processLauncher)
snapshotService := snapshotService.NewSnapshotService()
Expand All @@ -65,4 +68,5 @@ var RunCmd = &cobra.Command{

func init() {
RunCmd.Flags().BoolVarP(&ignoreVersionCheck, "ignore-version-check", "", false, "Ignore version check")
RunCmd.Flags().StringVarP(&dependencyResolution, "dependency-resolution", "", "auto", "Dependency resolution strategy. Valid values: auto, nix, external")
}
9 changes: 8 additions & 1 deletion cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var initSnapshotUrl string
var skipArtifactDownload bool
var allowPluginErrors bool
var pprofBind string
var dependencyResolution string

var ServeCommand = &cobra.Command{
Use: "serve",
Expand Down Expand Up @@ -118,7 +119,10 @@ to interact and monitor the Scroll Application`,

logger.Log().Info("Scroll loaded", zap.String("Name", currentScroll.Name), zap.Any("Version", currentScroll.Version), zap.String("AppVersion", currentScroll.AppVersion), zap.Any("Ports", currentScroll.Ports))

processLauncher := services.NewProcedureLauncher(client, processManager, pluginManager, consoleService, logManager, scrollService)
processLauncher, err := services.NewProcedureLauncher(client, processManager, pluginManager, consoleService, logManager, scrollService, dependencyResolution)
if err != nil {
return err
}

queueManager := services.NewQueueManager(scrollService, processLauncher)

Expand Down Expand Up @@ -303,6 +307,9 @@ func init() {
ServeCommand.Flags().BoolVarP(&skipArtifactDownload, "skip-artifact-download", "", false, "Skip downloading the artifact on startup")

ServeCommand.Flags().BoolVarP(&allowPluginErrors, "allow-plugin-errors", "", false, "Ignore plugin errors on startup")

ServeCommand.Flags().StringVarP(&dependencyResolution, "dependency-resolution", "", "auto", "Dependency resolution strategy. Valid values: auto, nix, external")

}

func startup(scrollService *services.ScrollService, snapshotService ports.SnapshotService, processLauncher *services.ProcedureLauncher, queueManager *services.QueueManager, portSerivce *services.PortMonitor, coldStarter *services.ColdStarter, healthHandler *handler.HealthHandler, cwd string, doneChan chan error) {
Expand Down
8 changes: 7 additions & 1 deletion examples/minecraft/.scroll/scroll.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ commands:
start:
needs: [install]
run: restart
dependencies: [jdk17]
procedures:
- mode: exec
data:
- java
- -version
- mode: exec
id: start-process
data:
- java
- -Xmx1024M
Expand All @@ -46,7 +52,7 @@ commands:
procedures:
- mode: stdin
data:
- start.0
- start-process
- stop
install:
run: once
Expand Down
7 changes: 4 additions & 3 deletions internal/core/domain/scroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ type Procedure struct {
} // @name Procedure

type CommandInstructionSet struct {
Procedures []*Procedure `yaml:"procedures" json:"procedures"`
Needs []string `yaml:"needs,omitempty" json:"needs,omitempty"`
Run RunMode `yaml:"run,omitempty" json:"run,omitempty"`
Dependencies []string `yaml:"dependencies,omitempty" json:"dependencies,omitempty"`
Procedures []*Procedure `yaml:"procedures" json:"procedures"`
Needs []string `yaml:"needs,omitempty" json:"needs,omitempty"`
Run RunMode `yaml:"run,omitempty" json:"run,omitempty"`
} // @name CommandInstructionSet

var ErrScrollDoesNotExist = fmt.Errorf("scroll does not exist")
Expand Down
7 changes: 6 additions & 1 deletion internal/core/ports/services_ports.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type ScrollServiceInterface interface {

type ProcedureLauchnerInterface interface {
LaunchPlugins() error
RunProcedure(*domain.Procedure, string) (string, *int, error)
RunProcedure(*domain.Procedure, string, []string) (string, *int, error)
Run(cmd string, runCommandCb func(cmd string) error) error
GetProcedureStatuses() map[string]domain.ScrollLockStatus
}
Expand Down Expand Up @@ -184,3 +184,8 @@ type UiDevServiceInterface interface {
IsWatching() bool
SetCommands(procs map[string]*domain.CommandInstructionSet)
}

type NixDependencyServiceInterface interface {
GetCommand(cmd []string, deps []string) []string
EnsureNixInstalled() error
}
34 changes: 34 additions & 0 deletions internal/core/services/nix_dependency_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package services

import (
"fmt"
"os/exec"
"strings"

"github.com/highcard-dev/daemon/internal/core/ports"
)

type NixDependencyService struct{}

func NewNixDependencyService() *NixDependencyService { return &NixDependencyService{} }

func (s *NixDependencyService) EnsureNixInstalled() error {
if _, err := exec.LookPath("nix-shell"); err != nil {
return fmt.Errorf("nix-shell not found in PATH; install Nix from https://nixos.org/download and ensure 'nix-shell' is available: %w", err)
}
return nil
}

func (s *NixDependencyService) GetCommand(cmd []string, deps []string) []string {
var cmds = []string{"nix-shell", "--pure"}
for _, dep := range deps {
cmds = append(cmds, "-p", dep)
}

cmds = append(cmds, "--command", strings.Join(cmd, " "))

return cmds

}

var _ ports.NixDependencyServiceInterface = (*NixDependencyService)(nil)
27 changes: 27 additions & 0 deletions internal/core/services/nix_dependency_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package services

import (
"testing"
)

func TestEnsureNixInstalled(t *testing.T) {
svc := NewNixDependencyService()
err := svc.EnsureNixInstalled()
// We accept either outcome because CI/dev environment may or may not have nix.
if err != nil {
t.Logf("nix-shell not found (acceptable if Nix not installed): %v", err)
}
}

func TestGetCommandPassthrough(t *testing.T) {
svc := NewNixDependencyService()
in := []string{"echo", "hello"}
deps := []string{"nodejs", "python3"}
out := svc.GetCommand(in, deps)
expected := []string{"nix-shell", "--pure", "-p", "nodejs", "-p", "python3", "--command", "echo hello"}
for i, v := range expected {
if out[i] != v {
t.Errorf("expected out[%d] = %s got %s", i, v, out[i])
}
}
}
Loading
Loading