2
0
Files
GitCaddy 587ac42be4
Some checks are pending
CI / build-and-test (push) Waiting to run
Release / build (amd64, linux) (push) Successful in 1m12s
Release / build (amd64, darwin) (push) Successful in 1m16s
Release / build (arm64, darwin) (push) Successful in 1m0s
Release / build (amd64, windows) (push) Successful in 1m13s
Release / build (arm64, linux) (push) Successful in 45s
Release / release (push) Successful in 50s
feat: Rebrand to gitcaddy-runner with upload helper
- Rename binary from act_runner to gitcaddy-runner
- Update all user-facing strings (Gitea → GitCaddy)
- Add gitcaddy-upload helper with automatic retry for large files
- Add upload helper package (internal/pkg/artifact)
- Update Docker image name to marketally/gitcaddy-runner

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-14 07:26:46 +00:00

210 lines
4.4 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package envcheck
import (
"context"
"fmt"
"io"
"net/http"
"sync"
"time"
)
// BandwidthInfo holds network bandwidth test results
type BandwidthInfo struct {
DownloadMbps float64 `json:"download_mbps"`
UploadMbps float64 `json:"upload_mbps,omitempty"`
Latency float64 `json:"latency_ms,omitempty"`
TestedAt time.Time `json:"tested_at"`
}
// BandwidthManager handles periodic bandwidth testing
type BandwidthManager struct {
serverURL string
lastResult *BandwidthInfo
mu sync.RWMutex
testInterval time.Duration
stopChan chan struct{}
}
// NewBandwidthManager creates a new bandwidth manager
func NewBandwidthManager(serverURL string, testInterval time.Duration) *BandwidthManager {
return &BandwidthManager{
serverURL: serverURL,
testInterval: testInterval,
stopChan: make(chan struct{}),
}
}
// Start begins periodic bandwidth testing
func (bm *BandwidthManager) Start(ctx context.Context) {
// Run initial test
bm.RunTest(ctx)
// Start periodic testing
go func() {
ticker := time.NewTicker(bm.testInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
bm.RunTest(ctx)
case <-bm.stopChan:
return
case <-ctx.Done():
return
}
}
}()
}
// Stop stops the periodic testing
func (bm *BandwidthManager) Stop() {
close(bm.stopChan)
}
// RunTest runs a bandwidth test and stores the result
func (bm *BandwidthManager) RunTest(ctx context.Context) *BandwidthInfo {
result := TestBandwidth(ctx, bm.serverURL)
bm.mu.Lock()
bm.lastResult = result
bm.mu.Unlock()
return result
}
// GetLastResult returns the most recent bandwidth test result
func (bm *BandwidthManager) GetLastResult() *BandwidthInfo {
bm.mu.RLock()
defer bm.mu.RUnlock()
return bm.lastResult
}
// TestBandwidth tests network bandwidth to the GitCaddy server
func TestBandwidth(ctx context.Context, serverURL string) *BandwidthInfo {
if serverURL == "" {
return nil
}
info := &BandwidthInfo{
TestedAt: time.Now(),
}
// Test latency first
info.Latency = testLatency(ctx, serverURL)
// Test download speed
info.DownloadMbps = testDownloadSpeed(ctx, serverURL)
return info
}
func testLatency(ctx context.Context, serverURL string) float64 {
client := &http.Client{
Timeout: 10 * time.Second,
}
reqCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(reqCtx, "HEAD", serverURL, nil)
if err != nil {
return 0
}
start := time.Now()
resp, err := client.Do(req)
if err != nil {
return 0
}
resp.Body.Close()
latency := time.Since(start).Seconds() * 1000 // Convert to ms
return float64(int(latency*100)) / 100 // Round to 2 decimals
}
func testDownloadSpeed(ctx context.Context, serverURL string) float64 {
// Try multiple endpoints to accumulate ~1MB of data
endpoints := []string{
"/assets/css/index.css",
"/assets/js/index.js",
"/assets/img/logo.svg",
"/assets/img/logo.png",
"/",
}
client := &http.Client{
Timeout: 30 * time.Second,
}
var totalBytes int64
var totalDuration time.Duration
targetBytes := int64(1024 * 1024) // 1MB target
maxAttempts := 10 // Limit iterations
for attempt := 0; attempt < maxAttempts && totalBytes < targetBytes; attempt++ {
for _, endpoint := range endpoints {
if totalBytes >= targetBytes {
break
}
url := serverURL + endpoint
reqCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
req, err := http.NewRequestWithContext(reqCtx, "GET", url, nil)
if err != nil {
cancel()
continue
}
start := time.Now()
resp, err := client.Do(req)
if err != nil {
cancel()
continue
}
n, _ := io.Copy(io.Discard, resp.Body)
resp.Body.Close()
cancel()
duration := time.Since(start)
if n > 0 {
totalBytes += n
totalDuration += duration
}
}
}
if totalBytes == 0 || totalDuration == 0 {
return 0
}
// Calculate speed in Mbps
seconds := totalDuration.Seconds()
if seconds == 0 {
return 0
}
bytesPerSecond := float64(totalBytes) / seconds
mbps := (bytesPerSecond * 8) / (1024 * 1024)
return float64(int(mbps*100)) / 100
}
// FormatBandwidth formats bandwidth for display
func FormatBandwidth(mbps float64) string {
if mbps == 0 {
return "Unknown"
}
if mbps >= 1000 {
return fmt.Sprintf("%.1f Gbps", mbps/1000)
}
return fmt.Sprintf("%.1f Mbps", mbps)
}