Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e0fc7ef
Integrate CI/CD tools (#4)
thesprockee Sep 14, 2025
667215b
Refactor to use nevrcap
thesprockee Oct 26, 2025
84460e8
Fix names in Makefile
thesprockee Oct 26, 2025
64bc97c
Update module references
thesprockee Oct 27, 2025
3a41060
Refactor cmd apps to handle both file formats
thesprockee Oct 28, 2025
8cd79b8
Integrate session events api
thesprockee Oct 29, 2025
40b8d62
Update build/repo files and references to nevrcap
thesprockee Nov 25, 2025
da22b25
Update nakama-common dependency to v1.37.0
thesprockee Nov 27, 2025
95f4cf4
Refactor nevrcap usage to utilize new codecs and processing packages
thesprockee Nov 30, 2025
f4f547b
Enhance Makefile to support building binaries for Windows and Linux
thesprockee Dec 1, 2025
7ebd6bf
Refactor GitHub Actions workflow to build binaries for multiple archi…
thesprockee Dec 1, 2025
57f6170
Consolidate CLI into unified nevr-agent with real-time streaming and …
Copilot Dec 20, 2025
fe86d39
Fix package versions
thesprockee Dec 20, 2025
fcde5a3
Fix JWT error
thesprockee Dec 20, 2025
be07339
Refactor command-line flag handling and configuration loading
thesprockee Dec 22, 2025
3040b08
Refactor configuration files and Docker setup for improved clarity an…
thesprockee Dec 22, 2025
2f78cdc
Add 'bin/' to .gitignore to exclude binary output from version control
thesprockee Dec 22, 2025
4e80c63
Refactor agent to remove events API and streamline event streaming
thesprockee Dec 22, 2025
57745aa
Fix event processor
thesprockee Dec 22, 2025
bdc19b1
Update output format to 'nevrcap' and improve logging message for nev…
thesprockee Dec 22, 2025
cca3fc3
Make the JWT secret optional, update WebSocket endpoint, and improve …
thesprockee Dec 22, 2025
1ae7bd2
Add frame counting and logging for WebSocket message processing
thesprockee Dec 22, 2025
f3a6780
Implement automatic reconnection for WebSocketWriter with exponential…
thesprockee Dec 22, 2025
f71655d
Update sessionEventDatabaseName to use 'nevr_telemetry' in resolvers …
thesprockee Dec 22, 2025
41a67d2
Implement disk buffering for WebSocketWriter to handle frame storage …
thesprockee Dec 22, 2025
4c75c85
Skip storing session frames without events in StoreSessionFrame function
thesprockee Dec 22, 2025
0f1fe21
Add .gitignore and project configuration file for language server setup
thesprockee Dec 22, 2025
436257e
Add StreamHub integration and endpoint for listing active streams
thesprockee Dec 22, 2025
991224e
Enhance match retrieval and storage management
thesprockee Dec 22, 2025
942280f
Add node ID configuration for agent instances
thesprockee Dec 22, 2025
d05e774
feat(converter): add recursive/glob search and round-trip validation
thesprockee Feb 10, 2026
4d415e3
Merge branch 'fix/optimize-streams'
thesprockee Feb 10, 2026
7e3fad2
docs(test): add comprehensive test implementation prompts for converter
thesprockee Feb 10, 2026
dae5f2c
Initial plan
Copilot Feb 10, 2026
a5960cc
Implement Phase 1: Config validation with comprehensive tests
Copilot Feb 10, 2026
73ea282
Add converter test scaffolding and documentation
Copilot Feb 10, 2026
3dee530
Add comprehensive test implementation summary documentation
Copilot Feb 10, 2026
21a84d7
Address code review feedback
Copilot Feb 10, 2026
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
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Go workspace file
go.work
go.work.sum

# Test files
*.nevrcap
*.echoreplay
/benchmark_*
/test_*
/output
20 changes: 20 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# EVR Data Recorder - Docker Compose Environment Variables
# =========================================================
# Copy this file to .env in the same directory and adjust values as needed
# docker-compose will automatically load variables from .env
# DO NOT commit .env to version control - it contains secrets

# Docker Compose Service Configuration
# =====================================

# MongoDB Configuration
MONGODB_PORT=27017
MONGODB_USER=admin
MONGODB_PASSWORD=mongodb_password
MONGODB_DATABASE=nakama

# RabbitMQ Configuration
RABBITMQ_PORT=5672
RABBITMQ_MANAGEMENT_PORT=15672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=rabbitmq_password
77 changes: 77 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# GitHub Copilot Instructions for nevr-agent

## Project Overview

nevr-agent is a unified Go CLI (`agent`) for recording, converting, and replaying EchoVR game session telemetry. Subcommands: `stream`, `serve`, `convert`, `replay`.

## Architecture

```
cmd/agent/ # Cobra CLI commands (main.go, stream.go, etc.)
internal/agent/ # Core writers/pollers implementing FrameWriter interface
internal/api/ # HTTP/WebSocket API server (MongoDB backend, Prometheus metrics)
internal/config/ # Viper-based config with yaml/env/flags hierarchy
internal/amqp/ # RabbitMQ integration
```

**Cross-repo dependencies** (via go.work):
- `nevr-common` → Protobuf definitions (`telemetry.LobbySessionStateFrame`)
- `nevrcap` → Codec implementations (.echoreplay, .nevrcap formats)

## Key Patterns

### CLI Commands (Cobra)
Use local flag variables, NOT `viper.BindPFlags()` - prevents conflicts between subcommands:
```go
func newMyCommand() *cobra.Command {
var myFlag string // LOCAL variable
cmd := &cobra.Command{
Use: "mycommand [flags] <arg>",
RunE: func(cmd *cobra.Command, args []string) error {
return runMyCommand(myFlag, args)
},
}
cmd.Flags().StringVar(&myFlag, "flag", "default", "description")
return cmd
}
```

### FrameWriter Interface
All output destinations implement this (file writers, stream writers, API writers):
```go
type FrameWriter interface {
WriteFrame(*telemetry.LobbySessionStateFrame) error
Close()
IsStopped() bool
}
```

### Configuration Hierarchy
CLI flags > env vars (`EVR_` prefix) > config file > defaults. See `internal/config/config.go`.

### Logging
Use zap structured logging: `logger.Info("msg", zap.String("key", val), zap.Error(err))`

## Build & Test

```bash
make build # Build agent binary (version from git tags)
make test # Run unit tests
make smoke-test # CLI integration tests
make lint # gofmt + go vet
make install-hooks # Pre-commit hook (lint + tests)
```

## Common Tasks

**Add subcommand**: Create `cmd/agent/mycommand.go`, add to `rootCmd.AddCommand()` in main.go
**Add writer**: Implement `FrameWriter` in `internal/agent/writer_mytype.go`
**Add config field**: Update struct in `internal/config/config.go` with `yaml:` and `mapstructure:` tags

## Commit Strategy

Break changes into small, focused commits (config → logic → CLI → tests → docs). PRs are squash-merged.

## Error Handling

Wrap errors with context: `fmt.Errorf("failed to X: %w", err)`. Log at handling point only.
83 changes: 83 additions & 0 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Run and Update Benchmarks

on:
release:
types:
- created

jobs:
bench:
if: github.event.release.draft == true
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.25'

- name: Run benchmarks
id: run_bench
run: |
mkdir -p bench-output
go test -bench=. -benchmem ./... > bench-output/bench.txt || true
cat bench-output/bench.txt

- name: Save benchmark artifact
uses: actions/upload-artifact@v4
with:
name: bench-output
path: bench-output/bench.txt

- name: Get previous tag
id: prev_tag
run: |
# find most recent tag before this release tag (if Git history has tags)
git fetch --tags
echo "RELEASE_TAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
prev=$(git tag --sort=-creatordate | grep -v "${{ github.event.release.tag_name }}" | sed -n '1p' || true)
echo "PREV_TAG=${prev}" >> $GITHUB_ENV
echo "Found previous tag: ${prev}"

- name: Download previous benchmarks (if available)
if: env.PREV_TAG != ''
run: |
echo "No automatic previous benchmark retrieval implemented; proceeding without baseline."

- name: Compare and update BENCHMARKS.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
mkdir -p tools
cat > tools/update_benchmarks.sh <<'SH'
#!/usr/bin/env bash
set -euo pipefail
out=bench-output/bench.txt
md=BENCHMARKS.md

timestamp=$(date -u +"%Y-%m-%d %H:%M:%SZ")
echo "## Benchmark results for ${RELEASE_TAG} - ${timestamp}\n" > /tmp/bench_update.md
echo '```' >> /tmp/bench_update.md
sed -n '1,200p' "$out" >> /tmp/bench_update.md
echo '```' >> /tmp/bench_update.md

if [ -f "$md" ]; then
cat $md >> /tmp/bench_update.md
fi

mv /tmp/bench_update.md $md

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add $md
git commit -m "Update BENCHMARKS.md: add results for ${RELEASE_TAG}" || echo "No changes to commit"
git push origin HEAD:${{ github.head_ref || github.ref }}
SH
chmod +x tools/update_benchmarks.sh
./tools/update_benchmarks.sh

86 changes: 86 additions & 0 deletions .github/workflows/build-and-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Build and Push Container Image

on:
workflow_dispatch:
inputs:
tag:
description: 'Container image tag (default: git short sha)'
required: false
default: ''
push:
description: 'Push to registry'
required: false
default: 'true'
type: choice
options:
- 'true'
- 'false'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

permissions:
contents: read
packages: write

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Container Registry
if: github.event.inputs.push == 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Determine image tag
id: tag
run: |
if [ -z "${{ github.event.inputs.tag }}" ]; then
TAG="${{ github.sha }}"
SHORT_TAG=$(echo $TAG | cut -c1-7)
echo "tag=$SHORT_TAG" >> $GITHUB_OUTPUT
echo "full_tag=$SHORT_TAG" >> $GITHUB_OUTPUT
else
TAG="${{ github.event.inputs.tag }}"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "full_tag=$TAG" >> $GITHUB_OUTPUT
fi
echo "Image will be tagged as: $TAG"

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=ref,event=branch
type=sha,prefix=${{ steps.tag.outputs.tag }}
type=raw,value=${{ steps.tag.outputs.tag }},enable=${{ steps.tag.outputs.tag != '' }}

- name: Build and push image
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event.inputs.push == 'true' }}
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Output image reference
if: github.event.inputs.push == 'true'
run: |
echo "Container image pushed successfully!"
echo "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }}"
66 changes: 66 additions & 0 deletions .github/workflows/build-release-binaries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Build Release Binaries

on:
release:
types: [created]

jobs:
build-and-upload:
if: github.event.release.draft == true && github.event.release.tag_name != ''
runs-on: ubuntu-latest
strategy:
matrix:
goos: [linux, windows]
goarch: [amd64, arm64]
exclude:
# Exclude Windows ARM64 for now (uncomment if needed)
- goos: windows
goarch: arm64

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.23'

- name: Get version
id: version
run: |
VERSION=$(git describe --tags --match 'v[0-9]*' --long --always --dirty --abbrev=7 2>/dev/null || echo v0.0.0-0-g0000000)
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "Building version: $VERSION"

- name: Build binaries
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
VERSION: ${{ steps.version.outputs.VERSION }}
run: |
LDFLAGS="-X main.version=${VERSION} -s -w"

# Extension for Windows
EXT=""
if [ "$GOOS" == "windows" ]; then
EXT=".exe"
fi

# Build the consolidated binary
OUTPUT_NAME="agent_${VERSION}_${GOOS}_${GOARCH}${EXT}"
echo "Building $OUTPUT_NAME"
GOOS=$GOOS GOARCH=$GOARCH go build -ldflags "$LDFLAGS" -o "$OUTPUT_NAME" ./cmd/agent

- name: List built files
run: ls -lah *_${{ steps.version.outputs.VERSION }}_${{ matrix.goos }}_${{ matrix.goarch }}*

- name: Upload binaries to release
uses: softprops/action-gh-release@v1
with:
files: |
*_${{ steps.version.outputs.VERSION }}_${{ matrix.goos }}_${{ matrix.goarch }}*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Loading
Loading