feat: add GitHub workflows and development tooling

- Add comprehensive CI/CD workflows for testing and releases
- Include issue and pull request templates
- Add GitHub labeler configuration for automated triage
- Include license check and stale issue management
- Add Windsurf rules for development workflow
- Include database directory structure with gitkeep

Provides complete GitHub automation and development tooling
for streamlined contribution and project management.
This commit is contained in:
Jeremie Fraeys 2025-12-04 16:56:25 -05:00
parent c980167041
commit e5dcb347d8
13 changed files with 855 additions and 0 deletions

49
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,49 @@
---
name: Bug Report
about: Create a report to help us improve
title: "[BUG] "
labels: bug
assignees: ''
---
## Describe the Bug
A clear and concise description of what the bug is.
## To Reproduce
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
## Expected Behavior
A clear and concise description of what you expected to happen.
## Actual Behavior
A clear and concise description of what actually happened.
## Environment
- OS: [e.g. macOS 13.0, Ubuntu 22.04]
- Go version: [e.g. 1.21.0]
- Fetch ML version: [e.g. v1.0.0]
- Configuration: [e.g. file-based auth, database auth]
## Configuration
```yaml
# Paste relevant configuration here
auth:
enabled: true
# ...
```
## Logs
```
# Paste relevant logs here
2024-01-01 12:00:00 ERROR: ...
```
## Additional Context
Add any other context about the problem here.
## Possible Solution
If you have ideas on how to fix this, please describe them here.

View file

@ -0,0 +1,34 @@
---
name: Feature Request
about: Suggest an idea for this project
title: "[FEATURE] "
labels: enhancement
assignees: ''
---
## Feature Description
A clear and concise description of what the feature is.
## Problem Statement
What problem does this feature solve? What pain point does it address?
## Proposed Solution
Describe the solution you'd like to see implemented.
## Alternative Solutions
Describe any alternative solutions or features you've considered.
## Use Cases
Describe specific use cases where this feature would be valuable.
## Implementation Details
If you have technical ideas on how this should be implemented, describe them here.
## Mockups/UI (if applicable)
If this involves UI changes, include mockups or screenshots.
## Additional Context
Add any other context, screenshots, or examples about the feature request here.
## Questions
Do you have any questions about how this feature might work?

35
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,35 @@
## Description
Brief description of what this PR changes.
## Type of Change
- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass (if applicable)
- [ ] Manual testing completed
- [ ] Security audit passed
## Checklist
- [ ] Code follows the project's style guidelines
- [ ] Self-review of the code completed
- [ ] Documentation updated if necessary
- [ ] Tests added for new functionality
- [ ] No hardcoded secrets or credentials
- [ ] Error handling implemented appropriately
## Security Considerations
- [ ] No sensitive data in logs
- [ ] Proper input validation
- [ ] Authentication/authorization properly implemented
- [ ] No SQL injection vulnerabilities
- [ ] No XSS vulnerabilities (if applicable)
## Screenshots (if applicable)
Add screenshots to help explain your changes.
## Additional Context
Add any other context about the pull request here.

82
.github/labeler.yml vendored Normal file
View file

@ -0,0 +1,82 @@
# Labeler configuration for automatic PR labeling
# Bug reports
bug:
- "[BUG]"
- "bug:"
- "fixes #"
- "closes #"
# Feature requests
enhancement:
- "[FEATURE]"
- "feat:"
- "feature:"
- "add "
# Documentation
documentation:
- "[DOCS]"
- "docs:"
- "README"
- "documentation"
# Security
security:
- "[SECURITY]"
- "security:"
- "auth"
- "authentication"
- "RBAC"
- "permissions"
# Testing
testing:
- "[TEST]"
- "test:"
- "tests"
- "unit test"
- "integration test"
# CI/CD
ci:
- "[CI]"
- "workflow"
- "github actions"
- "build"
- "deploy"
# Configuration
configuration:
- "[CONFIG]"
- "config"
- "yaml"
- "settings"
# Dependencies
dependencies:
- "[DEPS]"
- "go.mod"
- "dependency"
- "update"
# Performance
performance:
- "[PERF]"
- "performance"
- "optimize"
- "speed"
# Breaking changes
breaking-change:
- "[BREAKING]"
- "breaking"
- "deprecated"
- "remove"
# TUI
tui:
- "[TUI]"
- "cmd/tui"
- "terminal ui"
- "interface"

21
.github/rsync_manifest.json vendored Normal file
View file

@ -0,0 +1,21 @@
{
"version": "v3.2.7",
"base_url": "https://github.com/JMarvi3/rsync-static/releases/download",
"platforms": {
"linux-x86_64": {
"target": "x86_64-linux-musl",
"asset": "rsync-linux-x86_64",
"sha256": "0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5"
},
"macos-x86_64": {
"target": "x86_64-macos",
"asset": "rsync-macos-x86_64",
"sha256": "0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5"
},
"macos-arm64": {
"target": "aarch64-macos",
"asset": "rsync-macos-arm64",
"sha256": "0019dfc4b32d63c1392aa264aed2253c1e0c2fb09216f8e2cc269bbfb8bb49b5"
}
}
}

272
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,272 @@
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
# Concurrency control to prevent multiple runs of the same workflow
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Workflow permissions
permissions:
contents: read
security-events: write
actions: read
packages: write
env:
GO_VERSION: '1.25.0'
ZIG_VERSION: '0.15.2'
jobs:
test:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 30
services:
redis:
image: redis:7
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum', '**/go.mod') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: ${{ env.ZIG_VERSION }}
- name: Cache Zig build
uses: actions/cache@v4
with:
path: |
~/.cache/zig
cli/zig-cache
cli/zig-out
key: ${{ runner.os }}-zig-${{ hashFiles('cli/**') }}
restore-keys: |
${{ runner.os }}-zig-
- name: Install dependencies
run: |
go mod download
sudo apt-get update
sudo apt-get install -y podman redis-tools
- name: Verify dependencies
run: go mod verify
- name: Run tests
run: make test
env:
REDIS_URL: redis://localhost:6379
- name: Test internal/queue package
run: go test -v -race -coverprofile=queue-coverage.out ./internal/queue/...
env:
REDIS_URL: redis://localhost:6379
- name: Run comprehensive tests
run: make test-all
env:
REDIS_URL: redis://localhost:6379
- name: Run linters
run: make lint
- name: Generate coverage report
run: make coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella
build:
name: Build
runs-on: ubuntu-latest
needs: test
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Set up Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: ${{ env.ZIG_VERSION }}
- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum', '**/go.mod') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y podman
- name: Build binaries
run: |
make build
make cli-build
# Build Zig CLI (dev and prod)
cd cli && zig build dev && zig build prod && cd ..
# Note: prod builds use rsync_placeholder wrapper
# For true embedded rsync, add static binary to cli/src/assets/rsync_release.bin
- name: Test binaries
run: |
./bin/user_manager --help
./bin/worker --help
./bin/tui --help
./bin/data_manager --help
# Test Zig CLI
./cli/zig-out/prod/ml --help
# Verify binary size (should be small with placeholder rsync)
ls -lh ./cli/zig-out/prod/ml
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: fetch_ml_binaries
path: |
bin/
cli/zig-out/
dist/
retention-days: 30
test-scripts:
name: Test Scripts
runs-on: ubuntu-latest
needs: test
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y podman redis-tools bats
- name: Test scripts
run: |
# Test script functionality
chmod +x scripts/*.sh
# Test quick start script (dry run)
./scripts/quick_start.sh --help || true
# Test security monitor
./scripts/security-monitor.sh help
# Test auto setup
./scripts/auto_setup.sh help
# Test deployment scripts
./scripts/deploy-secure.sh --help || true
./scripts/deploy-production.sh --help || true
security-scan:
name: Security Scan
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: 'trivy-results.sarif'
- name: Gosec Security Scanner
run: |
go install github.com/securecodewarrior/gosec/v2/cmd/gosec@latest
gosec ./...
docker-build:
name: Docker Build
runs-on: ubuntu-latest
needs: [test, build, test-scripts]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max

50
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,50 @@
name: Documentation
on:
push:
branches: [ main ]
paths: [ 'docs/**', 'README.md' ]
pull_request:
branches: [ main ]
paths: [ 'docs/**', 'README.md' ]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./docs
destination: ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2

22
.github/workflows/label.yml vendored Normal file
View file

@ -0,0 +1,22 @@
name: Label Pull Request
on:
pull_request:
types: [opened, edited, synchronize]
jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Label PR
uses: actions/labeler@v4
with:
configuration-path: .github/labeler.yml
sync-labels: true

51
.github/workflows/license-check.yml vendored Normal file
View file

@ -0,0 +1,51 @@
name: License Check
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
license-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check license headers
run: |
# Check if LICENSE file exists
if [ ! -f "LICENSE" ]; then
echo "LICENSE file is missing"
exit 1
fi
# Check if it's MIT license
if ! grep -q "MIT License" LICENSE; then
echo "License file should be MIT License"
exit 1
fi
echo "License file OK"
- name: Check Go files for license headers
run: |
# Check for license headers in Go files (optional but good practice)
missing_headers=0
for file in $(find . -name "*.go" -not -path "./vendor/*" -not -path "./.git/*"); do
if ! head -10 "$file" | grep -q "Copyright" && ! head -10 "$file" | grep -q "MIT"; then
echo "Missing license header in: $file"
missing_headers=$((missing_headers + 1))
fi
done
if [ $missing_headers -gt 0 ]; then
echo "Found $missing_headers Go files without license headers"
echo "Consider adding license headers to Go files"
# Don't fail the build, just warn
else
echo "All Go files have license headers"
fi

197
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,197 @@
name: Release
on:
push:
tags:
- 'v*' # Trigger on version tags like v1.0.0
permissions:
contents: write
packages: write
env:
GO_VERSION: '1.25.0'
ZIG_VERSION: '0.15.2'
jobs:
prepare_rsync:
name: Prepare rsync metadata
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.manifest.outputs.matrix }}
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Load rsync manifest
id: manifest
run: |
MANIFEST=.github/rsync_manifest.json
MATRIX=$(jq -c '
. as $cfg
| $cfg.platforms
| to_entries
| map({
platform: .key,
target: .value.target,
"rsync-url": ($cfg.base_url + "/" + $cfg.version + "/" + .value.asset),
"rsync-sha256": .value.sha256
})
' "$MANIFEST")
printf 'matrix={"include":%s}\n' "$MATRIX" >> "$GITHUB_OUTPUT"
build-cli:
name: Build CLI - ${{ matrix.platform }}
needs: prepare_rsync
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJson(needs.prepare_rsync.outputs.matrix) }}
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: ${{ env.ZIG_VERSION }}
- name: Download static rsync
run: |
mkdir -p cli/src/assets
wget -O cli/src/assets/rsync_release.bin ${{ matrix.rsync-url }} || \
curl -L -o cli/src/assets/rsync_release.bin ${{ matrix.rsync-url }}
echo "${{ matrix.rsync-sha256 }} cli/src/assets/rsync_release.bin" | sha256sum -c
chmod +x cli/src/assets/rsync_release.bin
ls -lh cli/src/assets/rsync_release.bin
- name: Build CLI
working-directory: cli
run: |
zig build -Dtarget=${{ matrix.target }} -Doptimize=ReleaseSmall
ls -lh zig-out/bin/ml
- name: Strip binary (Linux only)
if: matrix.platform == 'linux-x86_64'
working-directory: cli
run: strip zig-out/bin/ml
- name: Package binary
run: |
mkdir -p dist
cp cli/zig-out/bin/ml dist/ml-${{ matrix.platform }}
cd dist
tar -czf ml-${{ matrix.platform }}.tar.gz ml-${{ matrix.platform }}
sha256sum ml-${{ matrix.platform }}.tar.gz > ml-${{ matrix.platform }}.tar.gz.sha256
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: ml-${{ matrix.platform }}
path: |
dist/ml-${{ matrix.platform }}.tar.gz
dist/ml-${{ matrix.platform }}.tar.gz.sha256
build-go-backends:
name: Build Go Backends
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Build binaries
run: |
make cross-platform
ls -lh dist/
- name: Package binaries
run: |
cd dist
for binary in api-server worker tui data_manager user_manager; do
if [[ -f "${binary}" ]]; then
tar -czf "fetch_ml_${binary}.tar.gz" "${binary}"
sha256sum "fetch_ml_${binary}.tar.gz" > "fetch_ml_${binary}.tar.gz.sha256"
fi
done
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: go-backends
path: |
dist/*.tar.gz
dist/*.sha256
create-release:
name: Create GitHub Release
needs: [build-cli, build-go-backends]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare release assets
run: |
mkdir -p release
# Copy CLI binaries
cp artifacts/ml-*/ml-*.tar.gz* release/
# Copy Go binaries
cp artifacts/go-backends/*.tar.gz* release/
# Generate combined checksums
cd release
sha256sum *.tar.gz > checksums.txt
ls -lh
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: release/*
body: |
## 🚀 Release ${{ github.ref_name }}
### CLI Binaries (Zero Dependencies)
All CLI binaries include embedded static rsync for complete independence.
- **`ml-linux-x86_64.tar.gz`** - Linux x86_64 (fully static, musl)
- **`ml-macos-x86_64.tar.gz`** - macOS Intel
- **`ml-macos-arm64.tar.gz`** - macOS Apple Silicon
### Go Backend Binaries
- **`fetch_ml_api-server.tar.gz`** - API Server
- **`fetch_ml_worker.tar.gz`** - Worker
- **`fetch_ml_tui.tar.gz`** - Terminal UI
- **`fetch_ml_data_manager.tar.gz`** - Data Manager
- **`fetch_ml_user_manager.tar.gz`** - User Manager
### Installation
```bash
# Download and extract
tar -xzf ml-<platform>.tar.gz
# Make executable and move to PATH
chmod +x ml-<platform>
sudo mv ml-<platform> /usr/local/bin/ml
# Verify installation
ml --help
```
### Checksums
SHA256 checksums are provided in `checksums.txt` and individual `.sha256` files.
generate_release_notes: true
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

33
.github/workflows/stale.yml vendored Normal file
View file

@ -0,0 +1,33 @@
name: Mark Stale Issues and PRs
on:
schedule:
- cron: "0 0 * * 1" # Every Monday at midnight
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- name: Mark stale issues and PRs
uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 30
days-before-close: 14
stale-issue-label: "stale"
stale-pr-label: "stale"
stale-issue-message: |
This issue has been automatically marked as stale because it has not had recent activity.
It will be closed if no further activity occurs within 14 days.
Thank you for your contributions!
stale-pr-message: |
This pull request has been automatically marked as stale because it has not had recent activity.
It will be closed if no further activity occurs within 14 days.
Thank you for your contributions!
exempt-issue-labels: "pinned,security,help wanted,good first issue"
exempt-pr-labels: "pinned,security,help wanted,good first issue"

View file

@ -0,0 +1,6 @@
---
trigger: model_decision
description: When a new feature is added, this prompt needs to be run
---
When a significant feature is added make sure that the tests are added as well, change the docs to add details and make sure that the scripts, if needed, are changed. Don't forget to cleanup, you tend to leave a lot of unncessary files and code arround. Do not write loose .md to track task and todo, either add to the code or tell me.

3
db/.gitkeep Normal file
View file

@ -0,0 +1,3 @@
# This directory stores SQLite database files
# Database files are automatically created by the application
# Example: fetch_ml.db