# tinqs/ci CI toolchain for Tinqs Studio — composite Gitea Actions and the Lambda dispatcher that orchestrates Spot runners. ## Architecture ``` Push → Gitea webhook → Lambda (tinqs-ci-dispatch) → EC2 Spot instance → act_runner → job → self-terminate ``` Runners are ephemeral: one job per instance, self-terminates on completion. Actions are pre-cached on the runner to avoid repeated clones. ## Actions | Action | What it does | |--------|-------------| | `tinqs/ci/checkout@v1` | Clone a repo from tinqs.com (supports sparse checkout, depth control, token auth) | | `tinqs/ci/setup-go@v1` | Install Go (skips if pre-baked in AMI) | | `tinqs/ci/setup-node@v1` | Install Node.js + pnpm (skips if pre-baked) | | `tinqs/ci/setup-aws@v1` | Install AWS CLI + optional ECR login | ```yaml steps: - uses: tinqs/ci/checkout@v1 with: sparse: 'cmd/tstudio' - uses: tinqs/ci/setup-go@v1 - uses: tinqs/ci/setup-aws@v1 with: ecr-login: 'true' ``` ## Dispatcher (Lambda) `orchestrator/dispatch/main.go` — receives Gitea webhooks, evaluates workflow triggers, launches Spot instances with the right label. | Label | Instance | Use | |-------|----------|-----| | `go` | t3.small | Go builds (tstudio, proxy, docgen) | | `docker` | t3.medium | Docker image builds (platform, bot) | | `deploy` | t3.micro | S3 sync, ECS update | | `node` | t3.medium | Frontend builds | | `godot` | t3.medium | Game exports (future) | ## Runner Images Dockerfiles in `images/` — lean, purpose-built. Push to ECR with `images/build-all.sh`. ## Deploying The dispatcher Lambda can't CI itself — deploy manually: ```bash cd orchestrator/dispatch # Build GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bootstrap -ldflags "-s -w" . # Zip (PowerShell on Windows, zip on Mac/Linux) # Windows: powershell -Command "Compress-Archive -Path bootstrap -DestinationPath function.zip -Force" # Mac/Linux: zip -j function.zip bootstrap # Deploy aws lambda update-function-code --region eu-west-1 \ --function-name tinqs-ci-dispatch \ --zip-file fileb://function.zip # Verify aws lambda invoke --region eu-west-1 --function-name tinqs-ci-dispatch \ --payload '{}' /dev/null --query 'StatusCode' ``` **Lambda env vars** (configured in AWS, not in code): | Var | Purpose | |-----|---------| | `GITEA_URL` | `https://tinqs.com` | | `GITEA_TOKEN` | API token for fetching workflows and runner git auth | | `RUNNER_TOKEN` | act_runner registration token | | `RUNNER_AMI` | Pre-baked AMI with Go, Node, Docker, act_runner | | `SUBNET` | VPC subnet for Spot instances | | `SECURITY_GROUP` | SG allowing outbound HTTPS | | `DDB_TABLE` | DynamoDB table for run tracking | | `INSTANCE_PROFILE` | IAM role for runner instances | ## Contributing This repo is private. Only Tinqs team members contribute. ### Adding a new action 1. Create `/action.yml` (composite, shell: bash) 2. Keep it simple — no Node.js runtime, just bash 3. Add to the table in this README 4. Push to main — actions are resolved via `@v1` (main branch) ### Modifying the dispatcher 1. Edit `orchestrator/dispatch/main.go` 2. Test locally: `go build . && echo '{}' | ./dispatch` (dry run) 3. Deploy manually (see Deploying above) 4. Verify: push a change to `tinqs/studio` and check the pipeline ### Runner images 1. Edit `images//Dockerfile` 2. Build: `cd images && ./build-all.sh v1` 3. Requires Docker + ECR login (`aws ecr get-login-password | docker login ...`) ## Monitoring ```bash # Check for zombie runners (should be 0 except during active builds) aws ec2 describe-instances --region eu-west-1 \ --filters "Name=tag:tinqs-ci,Values=true" "Name=instance-state-name,Values=running" \ --query 'Reservations[].Instances[].InstanceId' # Lambda dispatch logs MSYS_NO_PATHCONV=1 aws logs tail '/aws/lambda/tinqs-ci-dispatch' --region eu-west-1 # Build logs (via Gitea API) curl -s "https://tinqs.com/api/v1/repos/tinqs/studio/actions/jobs//logs" \ -H "Authorization: token $TOKEN" ``` Full debug guide: `tinqs/docs/.cursor/skills/ci-pipeline-discipline/SKILL.md`