feat: Companion agent download in ServerView + Gitea CI pipeline fix
All checks were successful
Test Asgard Runner / test (push) Successful in 3s

Frontend:
- Add Companion Agent card to ServerView (status, download links, setup instructions)
- Shows agent connection status, last heartbeat, license key for copy
- Download buttons for Linux/Windows amd64 from Gitea releases

CI/CD:
- Fix build-companion.yml: replace actions/github-script with Gitea API curl
- Inject version from git tag via ldflags (-X main.version)
- Add VERSION variable to Makefile with ldflags injection
- Change main.go version from const to var for ldflags compatibility

Deployment:
- Add systemd service file (deployment/corrosion-companion.service)
- Add .gitignore for bin/ (binaries should come from CI, not repo)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-21 13:47:09 -05:00
parent d2e7a42536
commit e0f9438dfa
6 changed files with 215 additions and 74 deletions

View File

@@ -26,13 +26,13 @@ jobs:
run: |
cd companion-agent
mkdir -p bin
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/corrosion-companion-linux-amd64 ./cmd/agent
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.version=${{ steps.version.outputs.VERSION }}" -o bin/corrosion-companion-linux-amd64 ./cmd/agent
chmod +x bin/corrosion-companion-linux-amd64
- name: Build Windows AMD64
run: |
cd companion-agent
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o bin/corrosion-companion-windows-amd64.exe ./cmd/agent
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.version=${{ steps.version.outputs.VERSION }}" -o bin/corrosion-companion-windows-amd64.exe ./cmd/agent
- name: Generate checksums
run: |
@@ -42,85 +42,49 @@ jobs:
cat checksums.txt
- name: Create Release
uses: actions/github-script@v6
env:
VERSION: ${{ steps.version.outputs.VERSION }}
with:
script: |
const fs = require('fs').promises;
const path = require('path');
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
VERSION=${{ steps.version.outputs.VERSION }}
REPO="vantzs/corrosion-admin-panel"
API_URL="https://git.corrosionmgmt.com/api/v1"
// Create release
const release = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: process.env.VERSION,
name: `Companion Agent ${process.env.VERSION}`,
body: `## Corrosion Companion Agent ${process.env.VERSION}
# Create release
RELEASE_ID=$(curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${VERSION}\", \"name\": \"Companion Agent ${VERSION}\", \"body\": \"Companion Agent release ${VERSION}\", \"draft\": false, \"prerelease\": false}" \
"${API_URL}/repos/${REPO}/releases" | jq -r '.id')
### Installation
# Upload Linux binary
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @companion-agent/bin/corrosion-companion-linux-amd64 \
"${API_URL}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=corrosion-companion-linux-amd64"
**Linux:**
\`\`\`bash
wget https://git.corrosionmgmt.com/vantzs/corrosion-admin-panel/releases/download/${process.env.VERSION}/corrosion-companion-linux-amd64
chmod +x corrosion-companion-linux-amd64
sudo mv corrosion-companion-linux-amd64 /usr/local/bin/corrosion-companion
\`\`\`
# Upload Windows binary
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @companion-agent/bin/corrosion-companion-windows-amd64.exe \
"${API_URL}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=corrosion-companion-windows-amd64.exe"
**Windows:**
Download \`corrosion-companion-windows-amd64.exe\` and run as administrator.
### Checksums
See \`checksums.txt\` for SHA256 verification.
### What's New
- Built from commit: ${context.sha.substring(0, 7)}
- Go version: 1.21
- Platforms: Linux AMD64, Windows AMD64
`,
draft: false,
prerelease: false
});
console.log(`Created release: ${release.data.html_url}`);
// Upload Linux binary
await github.rest.repos.uploadReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.data.id,
name: 'corrosion-companion-linux-amd64',
data: await fs.readFile('companion-agent/bin/corrosion-companion-linux-amd64')
});
// Upload Windows binary
await github.rest.repos.uploadReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.data.id,
name: 'corrosion-companion-windows-amd64.exe',
data: await fs.readFile('companion-agent/bin/corrosion-companion-windows-amd64.exe')
});
// Upload checksums
await github.rest.repos.uploadReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.data.id,
name: 'checksums.txt',
data: await fs.readFile('companion-agent/bin/checksums.txt')
});
console.log('Uploaded all release assets');
# Upload checksums
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @companion-agent/bin/checksums.txt \
"${API_URL}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=checksums.txt"
- name: Build Summary
run: |
echo "## 🚀 Companion Agent Build Complete" >> $GITHUB_STEP_SUMMARY
echo "## Companion Agent Build Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY
echo "**Commit:** ${GITHUB_SHA:0:7}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Built Artifacts:" >> $GITHUB_STEP_SUMMARY
echo "- Linux AMD64 ($(stat -f%z companion-agent/bin/corrosion-companion-linux-amd64 2>/dev/null || stat -c%s companion-agent/bin/corrosion-companion-linux-amd64) bytes)" >> $GITHUB_STEP_SUMMARY
echo "- Windows AMD64 ($(stat -f%z companion-agent/bin/corrosion-companion-windows-amd64.exe 2>/dev/null || stat -c%s companion-agent/bin/corrosion-companion-windows-amd64.exe) bytes)" >> $GITHUB_STEP_SUMMARY
echo "- SHA256 checksums" >> $GITHUB_STEP_SUMMARY
echo "- Linux AMD64 ($(stat -c%s companion-agent/bin/corrosion-companion-linux-amd64) bytes)" >> $GITHUB_STEP_SUMMARY
echo "- Windows AMD64 ($(stat -c%s companion-agent/bin/corrosion-companion-windows-amd64.exe) bytes)" >> $GITHUB_STEP_SUMMARY
echo "- SHA256 checksums" >> $GITHUB_STEP_SUMMARY

1
companion-agent/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
bin/

View File

@@ -16,8 +16,11 @@ GOTEST=$(GOCMD) test
GOGET=$(GOCMD) get
GOMOD=$(GOCMD) mod
# Version (overridable via: make build-linux VERSION=v1.2.3)
VERSION ?= dev
# Build flags
LDFLAGS=-ldflags "-s -w"
LDFLAGS = -ldflags "-s -w -X main.version=$(VERSION)"
all: clean build

View File

@@ -32,7 +32,7 @@ type Config struct {
LogLevel string `envconfig:"LOG_LEVEL" default:"info"`
}
const version = "1.0.0"
var version = "dev"
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)

View File

@@ -0,0 +1,28 @@
[Unit]
Description=Corrosion Companion Agent
Documentation=https://corrosionmgmt.com
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=rust
Group=rust
WorkingDirectory=/opt/corrosion
ExecStart=/opt/corrosion/corrosion-companion-linux-amd64
Restart=on-failure
RestartSec=5
StartLimitInterval=60
StartLimitBurst=3
# Environment
EnvironmentFile=/opt/corrosion/.env
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/corrosion /home/rust
ProtectHome=read-only
[Install]
WantedBy=multi-user.target

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ref, computed, onMounted } from 'vue'
import { useServerStore } from '@/stores/server'
import { useAuthStore } from '@/stores/auth'
import {
Server,
Wifi,
@@ -10,13 +11,62 @@ import {
RotateCcw,
Save,
Loader2,
Download,
Terminal,
Monitor,
} from 'lucide-vue-next'
const server = useServerStore()
const auth = useAuthStore()
const editMode = ref(false)
const saving = ref(false)
const actionLoading = ref<string | null>(null)
const copied = ref(false)
const isAgentConnected = computed(() =>
server.connection?.connection_type === 'bare_metal' &&
server.connection?.connection_status === 'connected'
)
const agentLastSeen = computed(() => {
const ts = server.connection?.companion_last_seen
if (!ts) return null
return new Date(ts)
})
const agentLastSeenLabel = computed(() => {
const d = agentLastSeen.value
if (!d) return 'Never'
const diff = Math.floor((Date.now() - d.getTime()) / 1000)
if (diff < 60) return `${diff}s ago`
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`
return d.toLocaleDateString()
})
const licenseKey = computed(() => auth.license?.license_key || 'YOUR-LICENSE-KEY')
const linuxCommands = computed(() => `# Download the agent
curl -LO https://git.corrosionmgmt.com/vantzs/corrosion-admin-panel/releases/latest/download/corrosion-companion-linux-amd64
chmod +x corrosion-companion-linux-amd64
# Start with your license key
export LICENSE_ID="${licenseKey.value}"
export NATS_URL="nats://nats.corrosionmgmt.com:4222"
export NATS_TOKEN="<your-nats-token>"
export GAME_SERVER_PATH="/path/to/RustDedicated"
./corrosion-companion-linux-amd64`)
async function copyCommands() {
try {
await navigator.clipboard.writeText(linuxCommands.value)
copied.value = true
setTimeout(() => { copied.value = false }, 2000)
} catch {
// Clipboard API unavailable
}
}
const form = ref({
server_name: '',
@@ -148,6 +198,101 @@ onMounted(async () => {
</div>
</div>
<!-- Companion Agent -->
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
<div class="flex items-center gap-2 mb-5">
<Monitor class="w-4 h-4 text-oxide-400" />
<h2 class="text-sm font-medium text-neutral-400 uppercase tracking-wider">Companion Agent</h2>
</div>
<!-- Agent Status -->
<div class="grid grid-cols-2 lg:grid-cols-3 gap-6 mb-6">
<div>
<p class="text-xs text-neutral-500 mb-1">Agent Status</p>
<div class="flex items-center gap-2">
<span
class="w-2 h-2 rounded-full"
:class="isAgentConnected ? 'bg-green-400' : 'bg-neutral-600'"
/>
<span
class="text-sm font-medium"
:class="isAgentConnected ? 'text-green-400' : 'text-neutral-400'"
>
{{ isAgentConnected ? 'Active' : 'Inactive' }}
</span>
</div>
</div>
<div>
<p class="text-xs text-neutral-500 mb-1">Connection Type</p>
<p class="text-sm font-medium text-neutral-100 uppercase">
{{ server.connection?.connection_type || '\u2014' }}
</p>
</div>
<div>
<p class="text-xs text-neutral-500 mb-1">Last Heartbeat</p>
<p class="text-sm font-medium" :class="agentLastSeen ? 'text-neutral-200' : 'text-neutral-500'">
{{ agentLastSeenLabel }}
</p>
</div>
</div>
<!-- Download Section -->
<div class="mb-6">
<div class="flex items-center gap-2 mb-3">
<Download class="w-3.5 h-3.5 text-neutral-500" />
<p class="text-xs font-medium text-neutral-400 uppercase tracking-wider">Download Companion Agent</p>
</div>
<div class="flex flex-wrap gap-3">
<a
href="https://git.corrosionmgmt.com/vantzs/corrosion-admin-panel/releases/latest/download/corrosion-companion-linux-amd64"
download="corrosion-companion-linux-amd64"
class="flex items-center gap-2 px-4 py-2.5 bg-neutral-800 hover:bg-neutral-700 text-neutral-200 border border-neutral-700 hover:border-neutral-600 rounded-lg text-sm font-medium transition-colors"
>
<Download class="w-4 h-4 text-oxide-400" />
Linux (amd64)
</a>
<a
href="https://git.corrosionmgmt.com/vantzs/corrosion-admin-panel/releases/latest/download/corrosion-companion-windows-amd64.exe"
download="corrosion-companion-windows-amd64.exe"
class="flex items-center gap-2 px-4 py-2.5 bg-neutral-800 hover:bg-neutral-700 text-neutral-200 border border-neutral-700 hover:border-neutral-600 rounded-lg text-sm font-medium transition-colors"
>
<Download class="w-4 h-4 text-oxide-400" />
Windows (amd64)
</a>
</div>
</div>
<!-- Quick Setup Section -->
<div>
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-2">
<Terminal class="w-3.5 h-3.5 text-neutral-500" />
<p class="text-xs font-medium text-neutral-400 uppercase tracking-wider">Quick Setup (Linux)</p>
</div>
<button
@click="copyCommands"
class="flex items-center gap-1.5 px-3 py-1 text-xs font-medium rounded-md transition-colors"
:class="copied
? 'bg-green-600/20 text-green-400 border border-green-600/30'
: 'bg-neutral-800 hover:bg-neutral-700 text-neutral-400 hover:text-neutral-200 border border-neutral-700'"
>
{{ copied ? 'Copied!' : 'Copy' }}
</button>
</div>
<div class="bg-black/50 border border-neutral-800 rounded-lg p-4 font-mono text-sm text-neutral-300 overflow-x-auto">
<p class="text-neutral-500"># Download the agent</p>
<p>curl -LO https://git.corrosionmgmt.com/vantzs/corrosion-admin-panel/releases/latest/download/corrosion-companion-linux-amd64</p>
<p>chmod +x corrosion-companion-linux-amd64</p>
<p class="mt-3 text-neutral-500"># Start with your license key</p>
<p>export LICENSE_ID=<span class="text-oxide-400">"{{ licenseKey }}"</span></p>
<p>export NATS_URL=<span class="text-oxide-400">"nats://nats.corrosionmgmt.com:4222"</span></p>
<p>export NATS_TOKEN=<span class="text-neutral-500">"&lt;your-nats-token&gt;"</span></p>
<p>export GAME_SERVER_PATH=<span class="text-neutral-500">"/path/to/RustDedicated"</span></p>
<p>./corrosion-companion-linux-amd64</p>
</div>
</div>
</div>
<!-- Configuration -->
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
<div class="flex items-center justify-between mb-4">