Skip to content
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
24eec73
docs: add feature spec for etelemetry stack migration & monorepo refa…
satra Mar 22, 2026
3137d74
docs: amend constitution to v2026.03.22.1 (CI failure resolution)
satra Mar 22, 2026
6475c83
docs: clarify spec — data retention policy and dashboard geo visualiz…
satra Mar 22, 2026
db70747
docs: clarify spec — scale, dedup, allowlist, AWS deploy, compact pay…
satra Mar 22, 2026
3c027b2
docs: add implementation plan with research, data model, and contracts
satra Mar 22, 2026
64da793
docs: add task breakdown for etelemetry stack migration (58 tasks)
satra Mar 22, 2026
dc44ded
docs: remediate analysis findings in tasks.md (C1, C2, C3)
satra Mar 22, 2026
77cd835
feat: Phase 1 setup — monorepo structure, pyproject.toml, docs skeleton
satra Mar 22, 2026
2940b0c
feat: Phase 2 foundational — settings, DB, models, Alembic, app facto…
satra Mar 22, 2026
a0334cb
feat: US1 server — geolocation, version checker, usage recorder, routes
satra Mar 22, 2026
149f0f4
fix: ensure backward compatibility with existing etelemetry clients
satra Mar 22, 2026
e482111
feat: US1 client library — errors, config, client, public API, tests
satra Mar 22, 2026
1fb6e80
feat: US2 configurable server URL — param > env > default_url > hardc…
satra Mar 22, 2026
61d3169
feat: US5 deployment — Dockerfile, Compose, nginx, CI/CD workflows
satra Mar 22, 2026
ae40a19
feat: US3 migration tool + US4 aggregation service
satra Mar 22, 2026
327331d
feat: US4 dashboard — routes, templates, map, tests, accessibility
satra Mar 22, 2026
8e8a5bb
docs: Phase 8 polish — IP audit, code review, docs update
satra Mar 22, 2026
653615c
feat: release infrastructure, PyPI publishing, setup guide
satra Mar 22, 2026
6febfed
fix: publish all releases (including pre-releases) to PyPI
satra Mar 22, 2026
6e0d5ad
test: add client↔server E2E integration tests + dev compose
satra Mar 22, 2026
9fed218
docs: add spec for AWS least-privilege idempotent deployment
satra Mar 23, 2026
d632326
docs: plan for AWS least-privilege idempotent deployment
satra Mar 23, 2026
7a06b98
docs: add task breakdown for AWS least-privilege deployment (32 tasks)
satra Mar 23, 2026
05d6a8b
docs: remediate analysis findings in tasks.md (F1, E1, E2)
satra Mar 23, 2026
9a43ec5
feat: AWS least-privilege idempotent deployment (all 31 tasks)
satra Mar 23, 2026
23b2ed4
docs: rewrite external setup guide with clear ordered pathway
satra Mar 23, 2026
78e2956
fix: add GeoIP.conf to gitignore (contains license key)
satra Mar 23, 2026
bc0af4f
feat: fall back to DB-IP Lite when MaxMind GeoLite2 not available
satra Mar 23, 2026
029c175
fix: CI workflow — use uv run instead of uv pip install --system
satra Mar 23, 2026
0d63f40
fix: CI — fetch-depth 0 for hatch-vcs, add setup-uv to infra job
satra Mar 23, 2026
7f9df6e
fix: CI build failures — add README.md, fix server readme, add fallba…
satra Mar 23, 2026
2ab7439
fix: CI validate-infra — use uv run --with pytest for IAM policy tests
satra Mar 23, 2026
8d6d458
fix: CI — use --extra dev to install pytest and ruff via uv run
satra Mar 23, 2026
5f38c6b
fix: lint — auto-fix imports, line-length 100, exclude _version.py
satra Mar 23, 2026
ecd52c0
fix: CI test-server path — --directory server already changes cwd
satra Mar 24, 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
71 changes: 71 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: CI

on:
pull_request:
branches: [main]
push:
branches: [main]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- run: uv pip install --system ruff
- run: ruff check src/ server/src/ tools/ tests/ server/tests/

test-client:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with:
python-version: ${{ matrix.python-version }}
- run: uv pip install --system -e ".[dev]"
- run: pytest tests/ -v --tb=short

test-server:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: etelemetry
POSTGRES_PASSWORD: testpassword
POSTGRES_DB: etelemetry_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgresql+asyncpg://etelemetry:testpassword@localhost:5432/etelemetry_test
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with:
python-version: "3.11"
- run: uv pip install --system -e "./server[dev]"
- run: pytest server/tests/ -v --tb=short -k "not test_migration"

validate-infra:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: opentofu/setup-opentofu@v1
- name: Validate OpenTofu
working-directory: infra
run: |
tofu init -backend=false
tofu validate
tofu fmt -check -recursive
- name: Run IAM policy tests
run: |
pip install pytest
pytest infra/tests/test_iam_policy.py -v
123 changes: 123 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: Deploy

on:
push:
branches: [main]
paths:
- "server/**"
- "src/**"
- "deploy/**"
- "infra/scripts/**"
- ".github/workflows/deploy.yml"

concurrency:
group: deploy-production
cancel-in-progress: false # Queue, don't cancel

permissions:
id-token: write
contents: read

jobs:
deploy:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://et.dandiproject.org
steps:
- uses: actions/checkout@v4

# Authenticate to AWS via OIDC — no access keys needed
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.AWS_DEPLOY_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION || 'us-east-1' }}

# Build and push Docker image to ECR
- name: Login to ECR
id: ecr-login
uses: aws-actions/amazon-ecr-login@v2

- name: Build and push image
env:
ECR_REGISTRY: ${{ steps.ecr-login.outputs.registry }}
ECR_REPOSITORY: etelemetry
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -f deploy/Dockerfile -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest

# Deploy via SSM (no SSH needed)
- name: Get instance ID
id: instance
run: |
INSTANCE_ID=$(aws ec2 describe-instances \
--filters "Name=tag:Name,Values=etelemetry" "Name=instance-state-name,Values=running" \
--query 'Reservations[0].Instances[0].InstanceId' \
--output text)
echo "id=$INSTANCE_ID" >> "$GITHUB_OUTPUT"

- name: Deploy via SSM
env:
ECR_REGISTRY: ${{ steps.ecr-login.outputs.registry }}
INSTANCE_ID: ${{ steps.instance.outputs.id }}
run: |
COMMAND_ID=$(aws ssm send-command \
--instance-ids "$INSTANCE_ID" \
--document-name "AWS-RunShellScript" \
--parameters "commands=[\"bash /opt/etelemetry/infra/scripts/deploy.sh ${{ github.sha }} ${ECR_REGISTRY}/etelemetry ${{ vars.AWS_REGION || 'us-east-1' }}\"]" \
--timeout-seconds 300 \
--query 'Command.CommandId' \
--output text)

echo "SSM Command ID: $COMMAND_ID"

# Poll for completion
for i in $(seq 1 60); do
STATUS=$(aws ssm get-command-invocation \
--command-id "$COMMAND_ID" \
--instance-id "$INSTANCE_ID" \
--query 'Status' \
--output text 2>/dev/null || echo "Pending")

case "$STATUS" in
Success)
echo "Deployment successful!"
aws ssm get-command-invocation \
--command-id "$COMMAND_ID" \
--instance-id "$INSTANCE_ID" \
--query 'StandardOutputContent' \
--output text
exit 0
;;
Failed|TimedOut|Cancelled)
echo "Deployment FAILED with status: $STATUS"
aws ssm get-command-invocation \
--command-id "$COMMAND_ID" \
--instance-id "$INSTANCE_ID" \
--query 'StandardErrorContent' \
--output text
exit 1
;;
*)
sleep 5
;;
esac
done

echo "Deployment timed out waiting for SSM command"
exit 1

# Verify deployment
- name: Health check
run: |
sleep 10
curl -sf https://et.dandiproject.org/ || {
echo "Health check failed!"
exit 1
}
echo "Deployment verified at https://et.dandiproject.org/"
43 changes: 43 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Publish to PyPI

on:
release:
types: [published]

permissions:
id-token: write # Trusted publishing via OIDC

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for hatch-vcs

- uses: astral-sh/setup-uv@v4
with:
python-version: "3.11"

- name: Build package
run: uv build

- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/etelemetry
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
49 changes: 49 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Generate release notes
id: notes
run: |
# Get previous tag
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
NOTES=$(git log --oneline --no-merges)
else
NOTES=$(git log --oneline --no-merges "${PREV_TAG}..HEAD")
fi
echo "notes<<EOF" >> "$GITHUB_OUTPUT"
echo "$NOTES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"

- name: Determine pre-release
id: prerelease
run: |
TAG="${GITHUB_REF#refs/tags/}"
if echo "$TAG" | grep -qE '(alpha|beta|rc|dev|pre)'; then
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
else
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
fi

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
prerelease: ${{ steps.prerelease.outputs.is_prerelease }}
body: |
${{ steps.notes.outputs.notes }}
44 changes: 44 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Python
__pycache__/
*.pyc
*.pyo
*.egg-info/
dist/
build/
.eggs/
*.egg

# Virtual environments
.venv/
venv/

# uv
uv.lock

# Environment / Secrets
.env
.env.*
!.env.example
!deploy/.env.example

# MaxMind GeoIP databases
*.mmdb

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Testing
.coverage
htmlcov/
.pytest_cache/

# Docker
*.log
11 changes: 7 additions & 4 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<!--
Sync Impact Report
==================
Version change: 0.0.0 (template) → 2026.03.22
Version change: 2026.03.22 → 2026.03.22.1
Previous version ratified: 2026-03-22 (initial)
Amendment: Added CI failure resolution requirement to Principle III
Modified principles: N/A (initial population from template)
Added sections:
- Principle I: Isolated Environments
Expand Down Expand Up @@ -67,8 +69,9 @@ parallel workers MUST coordinate to avoid file conflicts.
- Branches MUST be used for features; `main` is protected.
- Before committing, subagents MUST pull latest changes and resolve
conflicts rather than force-pushing.

### IV. Test-Driven with Real Use Cases
- When working with a GitHub repository, tasks MUST ensure that remote
CI passes. If CI fails after a push, the failure MUST be diagnosed
and fixed before proceeding to the next task.

Every specification MUST conclude with real-use-case validation.
Tests MUST exercise actual user workflows, not just unit-level mocks.
Expand Down Expand Up @@ -188,4 +191,4 @@ verify compliance with these principles.
constitution, an amendment MUST be proposed rather than working
around the gap silently.

**Version**: 2026.03.22 | **Ratified**: 2026-03-22 | **Last Amended**: 2026-03-22
**Version**: 2026.03.22.1 | **Ratified**: 2026-03-22 | **Last Amended**: 2026-03-22
30 changes: 30 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# etelemetry Development Guidelines

Auto-generated from all feature plans. Last updated: 2026-03-22

## Active Technologies

- Python 3.11+ + FastAPI, SQLAlchemy 2.0, asyncpg, httpx, geoip2, (001-stack-migration-refactor)

## Project Structure

```text
backend/
frontend/
tests/
```

## Commands

cd src [ONLY COMMANDS FOR ACTIVE TECHNOLOGIES][ONLY COMMANDS FOR ACTIVE TECHNOLOGIES] pytest [ONLY COMMANDS FOR ACTIVE TECHNOLOGIES][ONLY COMMANDS FOR ACTIVE TECHNOLOGIES] ruff check .

## Code Style

Python 3.11+: Follow standard conventions

## Recent Changes

- 001-stack-migration-refactor: Added Python 3.11+ + FastAPI, SQLAlchemy 2.0, asyncpg, httpx, geoip2,

<!-- MANUAL ADDITIONS START -->
<!-- MANUAL ADDITIONS END -->
Loading
Loading