Operations & Deployment Guide - CAMT-CSV Project¶
Overview¶
This document provides comprehensive guidance for building, deploying, monitoring, and maintaining the CAMT-CSV application in production environments.
Build Process¶
1. Local Development Build¶
# Using Makefile (recommended)
make build # Build the application
make all # Lint, test, and build
make install-tools # Install dev tools (golangci-lint, gosec, cyclonedx-gomod)
# Direct commands (for specific cases)
# Clean build
go clean -cache
go mod tidy
go mod verify
# Build for current platform
go build -o bin/camt-csv cmd/camt-csv/main.go
# Build with version information
VERSION=$(git describe --tags --always --dirty)
BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
COMMIT=$(git rev-parse --short HEAD)
go build -ldflags "\
-X main.version=${VERSION} \
-X main.commit=${COMMIT} \
-X main.date=${BUILD_TIME}" \
-o camt-csv .
2. Cross-Platform Builds¶
# Build for multiple platforms
PLATFORMS="darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64"
for platform in $PLATFORMS; do
GOOS=${platform%/*}
GOARCH=${platform#*/}
echo "Building for $GOOS/$GOARCH..."
output="bin/camt-csv-${GOOS}-${GOARCH}"
if [ "$GOOS" = "windows" ]; then
output="${output}.exe"
fi
GOOS=$GOOS GOARCH=$GOARCH go build \
-ldflags "-s -w -X main.version=${VERSION}" \
-o $output .
done
3. Docker Build¶
# See Dockerfile in project root for the full version
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
ARG VERSION=dev
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w -X main.version=${VERSION}" \
-o camt-csv .
FROM alpine:3.21
RUN apk --no-cache add ca-certificates poppler-utils
RUN adduser -D -g '' appuser
WORKDIR /app
COPY --from=builder /app/camt-csv .
COPY --from=builder /app/database ./database
RUN chown -R appuser:appuser /app
USER appuser
ENTRYPOINT ["./camt-csv"]
# Build Docker image
docker build -t camt-csv:latest .
# Multi-platform build
docker buildx build --platform linux/amd64,linux/arm64 -t camt-csv:latest .
Release Process¶
1. Version Management¶
Semantic Versioning: Follow semver (MAJOR.MINOR.PATCH)
- MAJOR: Breaking changes to CLI interface or file formats
- MINOR: New features, new parser support
- PATCH: Bug fixes, performance improvements
2. Release Workflow¶
# 1. Prepare release
git checkout main
git pull origin main
# 2. Update version
VERSION="v1.2.3"
echo $VERSION > VERSION
# 3. Update CHANGELOG.md
# Add release notes, breaking changes, new features
# 4. Commit and tag
git add VERSION CHANGELOG.md
git commit -m "Release $VERSION"
git tag -a $VERSION -m "Release $VERSION"
# 5. Push
git push origin main
git push origin $VERSION
3. Automated Release (GoReleaser + GitHub Actions)¶
Releases are fully automated via GoReleaser v2. When you push a v* tag, the workflow at .github/workflows/goreleaser.yml runs and produces:
- Multi-platform binaries (linux/darwin/windows, amd64/arm64) as tar.gz/zip archives
- Docker images pushed to
ghcr.io/fjacquet/camt-csv(multi-arch manifest for amd64+arm64) - Homebrew formula pushed to
fjacquet/homebrew-tap(requiresTAP_GITHUB_TOKENsecret) - GitHub Release with archives, checksums (SHA-256), and auto-generated changelog
- SLSA provenance — the SLSA workflow (
.github/workflows/go-ossf-slsa3-publish.yml) triggers onrelease: [created]to add provenance attestation and SBOM
Configuration is in .goreleaser.yaml. Version info is injected via ldflags (main.version, main.commit, main.date).
Required secrets:
| Secret | Purpose |
|---|---|
GITHUB_TOKEN |
Provided automatically — used for release creation and GHCR push |
TAP_GITHUB_TOKEN |
PAT with Contents:Write on fjacquet/homebrew-tap — for Homebrew formula push |
To create a release:
# 1. Update CHANGELOG.md
# 2. Commit and tag
git tag -a v2.3.0 -m "Release v2.3.0"
git push origin v2.3.0
# GoReleaser handles the rest
To test locally without publishing:
Deployment Strategies¶
1. Homebrew (macOS / Linux)¶
brew tap fjacquet/homebrew-tap
brew install camt-csv
# Verify
camt-csv --version
# Upgrade
brew upgrade camt-csv
2. Standalone Binary Deployment¶
Download pre-built binaries from GitHub Releases.
# Example: Linux amd64
curl -L https://github.com/fjacquet/camt-csv/releases/latest/download/camt-csv_$(curl -s https://api.github.com/repos/fjacquet/camt-csv/releases/latest | grep tag_name | cut -d'"' -f4 | sed 's/v//')_linux_amd64.tar.gz | tar xz
chmod +x camt-csv
sudo mv camt-csv /usr/local/bin/
# Verify installation
camt-csv --version
3. Container Deployment (GHCR)¶
Multi-arch Docker images (amd64/arm64) are published to GitHub Container Registry on every release.
# Pull the latest image
docker pull ghcr.io/fjacquet/camt-csv:latest
# Run with Docker
docker run --rm \
-v $(pwd)/data:/data \
-e GEMINI_API_KEY=$GEMINI_API_KEY \
ghcr.io/fjacquet/camt-csv:latest camt -i /data/input.xml -o /data/output.csv
# Docker Compose for batch processing
version: '3.8'
services:
camt-csv:
image: ghcr.io/fjacquet/camt-csv:latest
volumes:
- ./input:/input
- ./output:/output
environment:
- GEMINI_API_KEY=${GEMINI_API_KEY}
- LOG_LEVEL=info
command: convert --input /input/statements.xml --output /output/transactions.csv
4. Kubernetes Deployment¶
Use Cases: Large-scale processing, enterprise environments
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: camt-csv-processor
spec:
replicas: 3
selector:
matchLabels:
app: camt-csv
template:
metadata:
labels:
app: camt-csv
spec:
containers:
- name: camt-csv
image: ghcr.io/fjacquet/camt-csv:latest
env:
- name: GEMINI_API_KEY
valueFrom:
secretKeyRef:
name: ai-credentials
key: gemini-api-key
- name: LOG_LEVEL
value: "info"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
Configuration Management¶
1. Environment-Specific Configuration¶
# config/production.yaml
log:
level: "warn"
format: "json"
ai:
enabled: true
requests_per_minute: 60
csv:
delimiter: ","
# config/development.yaml
log:
level: "debug"
format: "text"
ai:
enabled: false
2. Secret Management¶
Environment Variables:
# Production secrets
export GEMINI_API_KEY="$(cat /etc/secrets/gemini-api-key)"
export LOG_LEVEL="warn"
Kubernetes Secrets:
apiVersion: v1
kind: Secret
metadata:
name: ai-credentials
type: Opaque
data:
gemini-api-key: <base64-encoded-key>
Docker Secrets:
# Create secret
echo "your-api-key" | docker secret create gemini_api_key -
# Use in service
docker service create \
--secret gemini_api_key \
--env GEMINI_API_KEY_FILE=/run/secrets/gemini_api_key \
camt-csv:latest
Monitoring & Observability¶
1. Logging Strategy¶
Structured Logging:
log.WithFields(logrus.Fields{
"file": filePath,
"parser": "camt",
"transactions": len(transactions),
"duration": time.Since(start),
}).Info("File processing completed")
Log Aggregation:
# docker-compose.yml with ELK stack
version: '3.8'
services:
camt-csv:
image: ghcr.io/fjacquet/camt-csv:latest
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
environment:
- LOG_FORMAT=json
filebeat:
image: elastic/filebeat:7.15.0
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml
Note: This is a CLI tool without an HTTP server, so there are no Prometheus metrics endpoints or health check endpoints. Monitoring is done through:
- Structured logging (JSON format for machine processing)
- Exit codes (0 for success, non-zero for errors)
- File-based audit logs when needed
- CI/CD pipeline checks (golangci-lint, gosec, tests with coverage via codecov)
- SBOM generation via cyclonedx-gomod
- GitHub Pages documentation at https://fjacquet.github.io/camt-csv/
CI/CD Pipeline uses:
- Go 1.24.2
- golangci-lint for code quality
- gosec for security scanning (with SARIF output)
- cyclonedx-gomod for SBOM generation (CycloneDX format)
- codecov for coverage reporting
Makefile Targets:
make build # Build the application
make test # Run all tests
make test-race # Run tests with race detector
make coverage # Generate HTML coverage report
make lint # Run golangci-lint
make security # Run gosec security scan
make sbom # Generate SBOM
make all # Lint, test, and build
make install-tools # Install dev tools
Performance Optimization¶
1. Resource Management¶
Memory Optimization:
// Stream processing for large files
func processLargeFile(filePath string) error {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
decoder := xml.NewDecoder(file)
for {
token, err := decoder.Token()
if err == io.EOF {
break
}
if err != nil {
return err
}
// Process tokens incrementally
if se, ok := token.(xml.StartElement); ok {
if se.Name.Local == "Ntry" {
var entry Entry
if err := decoder.DecodeElement(&entry, &se); err != nil {
return err
}
processEntry(entry)
}
}
}
return nil
}
CPU Optimization:
// Parallel processing
func processTransactions(transactions []models.Transaction) []models.Transaction {
numWorkers := runtime.NumCPU()
jobs := make(chan models.Transaction, len(transactions))
results := make(chan models.Transaction, len(transactions))
// Start workers
for w := 0; w < numWorkers; w++ {
go worker(jobs, results)
}
// Send jobs
for _, tx := range transactions {
jobs <- tx
}
close(jobs)
// Collect results
processed := make([]models.Transaction, 0, len(transactions))
for i := 0; i < len(transactions); i++ {
processed = append(processed, <-results)
}
return processed
}
2. Caching Strategy¶
// Category cache
type CategoryCache struct {
cache map[string]*models.Category
mutex sync.RWMutex
ttl time.Duration
}
func (c *CategoryCache) Get(key string) (*models.Category, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
category, exists := c.cache[key]
return category, exists
}
Troubleshooting¶
1. Common Issues¶
File Processing Errors:
# Check file format
camt-csv validate --input suspicious_file.xml
# Debug with verbose logging
CAMT_LOG_LEVEL=debug camt-csv convert --input file.xml --output file.csv
# Test with minimal example
camt-csv convert --input samples/minimal.xml --output test.csv
Memory Issues:
# Monitor memory usage
top -p $(pgrep camt-csv)
# Use streaming for large files
camt-csv convert --streaming --input large_file.xml --output output.csv
AI Service Issues:
# Test AI connectivity
curl -H "Authorization: Bearer $GEMINI_API_KEY" \
https://generativelanguage.googleapis.com/v1/models
# Disable AI fallback
CAMT_AI_ENABLED=false camt-csv convert --input file.xml --output file.csv
2. Diagnostic Commands¶
# System information
camt-csv version --verbose
# Configuration dump
camt-csv config --show
# Performance profiling
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
3. Log Analysis¶
Error Patterns:
# Find parsing errors
grep "parse error" /var/log/camt-csv.log | tail -20
# Check AI API issues
grep "ai service" /var/log/camt-csv.log | grep ERROR
# Performance analysis
grep "processing_duration" /var/log/camt-csv.log | \
awk '{print $NF}' | sort -n | tail -10
Backup & Recovery¶
1. Data Backup¶
Configuration Backup:
Database Backup:
2. Disaster Recovery¶
Recovery Procedures:
# Restore from backup
tar -xzf camt-csv-config-20241219.tar.gz -C ~/
# Verify configuration
camt-csv config --validate
# Test functionality
camt-csv convert --input samples/test.xml --output test.csv --dry-run
Security Considerations¶
1. Secure Deployment¶
File Permissions:
# Secure configuration files
chmod 600 ~/.camt-csv/config.yaml
chmod 600 ~/.env
# Secure binary
chmod 755 /usr/local/bin/camt-csv
Network Security:
# Kubernetes NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: camt-csv-policy
spec:
podSelector:
matchLabels:
app: camt-csv
policyTypes:
- Egress
egress:
- to: []
ports:
- protocol: TCP
port: 443 # HTTPS only
2. Security Monitoring¶
Audit Logging:
log.WithFields(logrus.Fields{
"user": os.Getenv("USER"),
"file": filePath,
"operation": "convert",
"timestamp": time.Now(),
}).Info("File processing initiated")
Vulnerability Scanning:
# Scan dependencies
go list -json -m all | nancy sleuth
# Container scanning
docker scan camt-csv:latest
This operations guide provides comprehensive coverage for deploying, monitoring, and maintaining the CAMT-CSV application in production environments while ensuring security, performance, and reliability.