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
- Add package documentation comments - Use blank identifiers for unused parameters - Add periods to comment sentences for consistency - Fix naked return statement
104 lines
2.6 KiB
Go
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"
|
|
}
|