2
0
Files
gitcaddy-runner/internal/pkg/service/service_windows.go
logikonline bf71b55cb7
Some checks failed
CI / build-and-test (push) Failing after 24s
Release / build (amd64, darwin) (push) Failing after 38s
Release / build (amd64, linux) (push) Failing after 40s
Release / build (amd64, windows) (push) Failing after 32s
Release / build (arm64, darwin) (push) Failing after 31s
Release / build (arm64, linux) (push) Failing after 39s
Release / release (push) Has been skipped
style(service): improve comments and fix linter warnings
- Add package documentation comments
- Use blank identifiers for unused parameters
- Add periods to comment sentences for consistency
- Fix naked return statement
2026-01-25 11:44:38 -05:00

104 lines
2.6 KiB
Go

// Copyright 2024 The Gitea Authors and MarketAlly. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build windows
// Package service provides Windows service integration for the runner.
package service
import (
"context"
"os"
"strings"
"time"
log "github.com/sirupsen/logrus"
"golang.org/x/sys/windows/svc"
)
// runnerService implements svc.Handler for Windows service management.
type runnerService struct {
ctx context.Context
cancel context.CancelFunc
}
// Execute is called by the Windows Service Control Manager.
func (s *runnerService) Execute(_ []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
changes <- svc.Status{State: svc.StartPending}
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
log.Info("Windows service started")
loop:
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
// Windows wants two responses for interrogate
time.Sleep(100 * time.Millisecond)
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
log.Info("Windows service stop/shutdown requested")
s.cancel()
break loop
default:
log.Warnf("unexpected control request #%d", c)
}
case <-s.ctx.Done():
break loop
}
}
changes <- svc.Status{State: svc.StopPending}
return false, 0
}
// IsWindowsService returns true if the process is running as a Windows service.
func IsWindowsService() bool {
// Check if we're running interactively
isInteractive, err := svc.IsWindowsService()
if err != nil {
log.WithError(err).Debug("failed to detect if running as Windows service")
return false
}
return isInteractive
}
// RunAsService runs the application as a Windows service.
func RunAsService(serviceName string, run func(ctx context.Context)) error {
ctx, cancel := context.WithCancel(context.Background())
// Start the actual runner in a goroutine
go run(ctx)
// Run the service handler - this blocks until service stops
err := svc.Run(serviceName, &runnerService{ctx: ctx, cancel: cancel})
if err != nil {
log.WithError(err).Error("Windows service run failed")
return err
}
return nil
}
// GetServiceName returns the service name from environment or default.
func GetServiceName() string {
if name := os.Getenv("GITEA_RUNNER_SERVICE_NAME"); name != "" {
return name
}
// Try to detect from executable name
exe, err := os.Executable()
if err == nil {
base := strings.TrimSuffix(exe, ".exe")
if idx := strings.LastIndex(base, string(os.PathSeparator)); idx >= 0 {
return base[idx+1:]
}
return base
}
return "GiteaRunnerSvc"
}