diff --git a/.env.example b/.env.example index 0fababc..510e0d7 100644 --- a/.env.example +++ b/.env.example @@ -142,6 +142,22 @@ TRAEFIK_ACME_EMAIL=admin@example.com TRAEFIK_DASHBOARD_ENABLED=true TRAEFIK_DASHBOARD_PORT=8080 +# ====================== +# Gitea Integration (Coordinator) +# ====================== +# Gitea instance URL +GITEA_URL=https://git.mosaicstack.dev + +# Coordinator bot credentials (see docs/1-getting-started/3-configuration/4-gitea-coordinator.md) +# SECURITY: Store GITEA_BOT_TOKEN in secrets vault, not in version control +GITEA_BOT_USERNAME=mosaic +GITEA_BOT_TOKEN=REPLACE_WITH_COORDINATOR_BOT_API_TOKEN +GITEA_BOT_PASSWORD=REPLACE_WITH_COORDINATOR_BOT_PASSWORD + +# Repository configuration +GITEA_REPO_OWNER=mosaic +GITEA_REPO_NAME=stack + # ====================== # Logging & Debugging # ====================== diff --git a/docs/1-getting-started/3-configuration/4-gitea-coordinator.md b/docs/1-getting-started/3-configuration/4-gitea-coordinator.md new file mode 100644 index 0000000..6fcd894 --- /dev/null +++ b/docs/1-getting-started/3-configuration/4-gitea-coordinator.md @@ -0,0 +1,378 @@ +# Gitea Coordinator Bot Setup + +**Milestone:** M4.1-Coordinator +**Issue:** #156 - Create coordinator bot user in Gitea + +This document describes how to set up the `mosaic` bot user in Gitea for automated coordinator functionality. + +## Overview + +The coordinator bot is a Gitea user account used by the autonomous coordination system to: + +- Assign issues to agent workers +- Comment on issues with task assignments +- Update issue labels and milestones +- Close issues after completion +- Provide audit trail of coordinator actions + +**Bot Account Details:** + +- Username: `mosaic` +- Email: `mosaic@mosaicstack.dev` +- Type: Bot account +- Repository: `mosaic/stack` + +## Prerequisites + +- Gitea instance running at `https://git.mosaicstack.dev` +- Admin access to Gitea +- API access capability + +## Step 1: Create the Bot User + +### Via Gitea Web UI + +1. **Access Gitea Admin Panel** + - Navigate to `https://git.mosaicstack.dev/admin` + - Log in with your admin account + +2. **Create New User** + - Go to **User Accounts** → **Create New User** + - Fill in the following fields: + + | Field | Value | + | --------------------- | -------------------------------------------------- | + | Username | `mosaic` | + | Email | `mosaic@mosaicstack.dev` | + | Password | [Generate random secure password] | + | Send Activation Email | ✅ (checked) | + | Account Type | **Account Type: Organization** (bot-like behavior) | + | Admin | ☐ (unchecked) | + | Restricted Account | ☐ (unchecked) | + | Disable 2FA | ✅ (checked) | + +3. **Review and Create** + - Click **Create User Account** + - Note the generated temporary password if provided + +### Via API + +```bash +#!/bin/bash +# Set variables +GITEA_URL="https://git.mosaicstack.dev" +ADMIN_TOKEN="your-admin-token-here" +BOT_USERNAME="mosaic" +BOT_EMAIL="mosaic@mosaicstack.dev" +BOT_PASSWORD="$(openssl rand -base64 32)" + +# Create user +curl -s -X POST \ + -H "Authorization: token $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/admin/users" \ + -d "{ + \"username\": \"$BOT_USERNAME\", + \"email\": \"$BOT_EMAIL\", + \"password\": \"$BOT_PASSWORD\", + \"must_change_password\": false, + \"send_notify\": false + }" + +# Store password securely (see Step 3) +echo "Bot user created with temporary password: $BOT_PASSWORD" +``` + +## Step 2: Configure Repository Permissions + +The bot user needs **read** and **write** access to the `mosaic/stack` repository. + +### Add Bot as Collaborator + +1. **Navigate to Repository** + - Go to `https://git.mosaicstack.dev/mosaic/stack` + - Go to **Settings** → **Collaborators** + +2. **Add Bot User** + - Search for `mosaic` + - Select **Push** permission (allows pull + push) + - Or use **Admin** if full repository control needed + +### Via API + +```bash +#!/bin/bash +GITEA_URL="https://git.mosaicstack.dev" +REPO_OWNER="mosaic" +REPO_NAME="stack" +BOT_USERNAME="mosaic" +ADMIN_TOKEN="your-admin-token-here" + +# Add collaborator +curl -s -X PUT \ + -H "Authorization: token $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/collaborators/$BOT_USERNAME" \ + -d '{"permission":"push"}' + +echo "Bot added as collaborator with push permission" +``` + +## Step 3: Generate and Store API Token + +The coordinator needs an API token to authenticate Gitea API calls. + +### Generate Token + +1. **Login as Bot User** + - Log in to Gitea with username `mosaic` + - Complete any initial setup (verify email, set password, etc.) + +2. **Create Access Token** + - Go to **Settings** → **Applications** → **Access Tokens** + - Create token with these settings: + + | Setting | Value | + | ---------- | ----------------------------------------------------------- | + | Token Name | `coordinator-api-token` | + | Scopes | `api`, `read:repository`, `write:repository`, `write:issue` | + | Expiration | 90 days (recommended for security) | + +3. **Copy and Store Token** + - Copy the token immediately (it won't be shown again) + - Store securely in your secrets management system + +### Via API (as Admin) + +```bash +#!/bin/bash +GITEA_URL="https://git.mosaicstack.dev" +ADMIN_TOKEN="your-admin-token-here" +BOT_USERNAME="mosaic" + +# Create access token for bot user +curl -s -X POST \ + -H "Authorization: token $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/admin/users/$BOT_USERNAME/tokens" \ + -d '{ + "name": "coordinator-api-token", + "scopes": ["api", "read:repository", "write:repository", "write:issue"] + }' | jq -r '.sha1' +``` + +## Step 4: Store Credentials in Vault + +**For production environments using Vault:** + +Store the bot credentials in your Vault instance at: + +``` +secret-prod/gitea/coordinator/api-token +secret-prod/gitea/coordinator/password +``` + +**Example Vault setup:** + +```bash +#!/bin/bash +VAULT_ADDR="https://vault.example.com" +VAULT_TOKEN="your-vault-token" + +# Store API token +curl -X POST \ + -H "X-Vault-Token: $VAULT_TOKEN" \ + -H "Content-Type: application/json" \ + "$VAULT_ADDR/v1/secret/data/gitea/coordinator/api-token" \ + -d '{"data":{"token":"your-api-token-here"}}' + +# Store password (for recovery/rotation) +curl -X POST \ + -H "X-Vault-Token: $VAULT_TOKEN" \ + -H "Content-Type: application/json" \ + "$VAULT_ADDR/v1/secret/data/gitea/coordinator/password" \ + -d '{"data":{"password":"bot-password-here"}}' +``` + +**For development (non-production):** + +Store in `.env` file (never commit): + +```bash +# .env (NEVER COMMIT) +GITEA_BOT_TOKEN=your-api-token-here +GITEA_BOT_USERNAME=mosaic +GITEA_BOT_PASSWORD=your-bot-password-here +GITEA_URL=https://git.mosaicstack.dev +``` + +Add to `.env.example` (with placeholders): + +```bash +# Gitea Coordinator Bot Configuration +GITEA_URL=https://git.mosaicstack.dev +GITEA_BOT_USERNAME=mosaic +GITEA_BOT_TOKEN=your-coordinator-bot-token-here +GITEA_BOT_PASSWORD=your-coordinator-bot-password-here +``` + +## Step 5: Test Bot Functionality + +### Test 1: API Authentication + +```bash +#!/bin/bash +GITEA_URL="https://git.mosaicstack.dev" +BOT_TOKEN="your-bot-token" + +# Verify token works +curl -s -H "Authorization: token $BOT_TOKEN" \ + "$GITEA_URL/api/v1/user" | jq . + +# Expected output: +# { +# "id": , +# "username": "mosaic", +# "email": "mosaic@mosaicstack.dev", +# ... +# } +``` + +### Test 2: Assign Issue to Bot + +```bash +#!/bin/bash +GITEA_URL="https://git.mosaicstack.dev" +REPO_OWNER="mosaic" +REPO_NAME="stack" +ISSUE_NUMBER="156" +BOT_TOKEN="your-bot-token" + +# Assign issue to bot user +curl -s -X PATCH \ + -H "Authorization: token $BOT_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues/$ISSUE_NUMBER" \ + -d '{"assignees":["mosaic"]}' | jq . + +# Check assignment succeeded +curl -s -H "Authorization: token $BOT_TOKEN" \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues/$ISSUE_NUMBER" | \ + jq '.assignees[] | .username' + +# Expected output: mosaic +``` + +### Test 3: Comment as Bot + +```bash +#!/bin/bash +GITEA_URL="https://git.mosaicstack.dev" +REPO_OWNER="mosaic" +REPO_NAME="stack" +ISSUE_NUMBER="156" +BOT_TOKEN="your-bot-token" + +# Post comment as bot +curl -s -X POST \ + -H "Authorization: token $BOT_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues/$ISSUE_NUMBER/comments" \ + -d '{"body":"Test comment from coordinator bot"}' | jq . + +# Expected: Comment created successfully with bot as author +``` + +### Test 4: Update Labels + +```bash +#!/bin/bash +GITEA_URL="https://git.mosaicstack.dev" +REPO_OWNER="mosaic" +REPO_NAME="stack" +ISSUE_NUMBER="156" +BOT_TOKEN="your-bot-token" + +# Add label via bot +curl -s -X PATCH \ + -H "Authorization: token $BOT_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues/$ISSUE_NUMBER" \ + -d '{"labels":["coordinator","in-progress"]}' | jq . + +# Expected: Labels updated successfully +``` + +## Coordinator Bot Permissions Summary + +| Action | Required Permission | Verified | +| ------------------ | ------------------- | -------- | +| List issues | read:repository | ✅ | +| Read issue details | read:repository | ✅ | +| Assign issue | write:issue | ✅ | +| Comment on issue | write:issue | ✅ | +| Update labels | write:repository | ✅ | +| Close/reopen issue | write:issue | ✅ | +| Read pull requests | read:repository | ✅ | +| Comment on PR | write:issue | ✅ | + +## Troubleshooting + +### Bot User Won't Authenticate + +```bash +# Verify token is valid and not expired +curl -s -H "Authorization: token $GITEA_BOT_TOKEN" \ + "https://git.mosaicstack.dev/api/v1/user" | jq . + +# If 401 Unauthorized: Token is invalid or expired +# - Check token spelling/format +# - Verify token hasn't expired +# - Regenerate token if needed +``` + +### Permission Denied on Issues + +```bash +# Verify bot has repository access +curl -s -H "Authorization: token $GITEA_BOT_TOKEN" \ + "https://git.mosaicstack.dev/api/v1/repos/mosaic/stack" | jq . + +# If 403 Forbidden: +# - Add bot as collaborator with push permissions +# - Check repository settings allow bot access +``` + +### Token Rotation + +To rotate the API token: + +1. Generate new token (see Step 3) +2. Update configuration with new token +3. Test with new token +4. Delete old token in Gitea settings +5. Verify all integrations working with new token + +## Security Best Practices + +1. **Token Rotation** — Rotate tokens every 90 days +2. **Scoped Permissions** — Use minimum required scopes (`api`, `write:issue`) +3. **Restricted Account** — Mark as restricted if Gitea version supports it +4. **Audit Logging** — Monitor bot activity via Gitea audit logs +5. **Disable 2FA** — Disable 2FA for bot accounts (only use API tokens) +6. **Secure Storage** — Never commit credentials to version control +7. **Service Account** — Treat as privileged service account +8. **Regenerate on Compromise** — Immediately regenerate token if compromised + +## Additional Resources + +- [Gitea API Documentation](https://docs.gitea.io/en-us/api-usage/) +- [Gitea Access Tokens](https://docs.gitea.io/en-us/api-usage/#token) +- [Gitea Administration](https://docs.gitea.io/en-us/administration/) +- [Issue #156 - Create coordinator bot user](https://git.mosaicstack.dev/mosaic/stack/issues/156) + +## Related Issues + +- #140 - Coordinator integration architecture +- #157 - Coordinator webhook configuration +- #158 - Coordinator task assignment engine diff --git a/scripts/coordinator/README.md b/scripts/coordinator/README.md new file mode 100644 index 0000000..cc29d7d --- /dev/null +++ b/scripts/coordinator/README.md @@ -0,0 +1,274 @@ +# Coordinator Scripts + +Utility scripts for setting up and managing the autonomous coordinator system in Mosaic Stack. + +## Overview + +The coordinator system automates issue assignment, tracking, and orchestration across AI agents. These scripts help with setup, configuration, and testing. + +## Scripts + +### create-gitea-bot.sh + +Creates the `mosaic` bot user in Gitea for coordinator automation. + +**Prerequisites:** + +- Gitea admin access +- Admin API token with sufficient permissions + +**Usage:** + +```bash +# Set admin token and run +export ADMIN_TOKEN="your-gitea-admin-token" +./scripts/coordinator/create-gitea-bot.sh + +# Or specify variables +ADMIN_TOKEN="token" GITEA_URL="https://gitea.example.com" \ + ./scripts/coordinator/create-gitea-bot.sh +``` + +**What it does:** + +1. Creates `mosaic` bot user account +2. Sets up email: `mosaic@mosaicstack.dev` +3. Adds bot to `mosaic/stack` repository as collaborator +4. Generates API token for coordinator use +5. Tests bot authentication +6. Displays credentials for secure storage + +**Output:** +The script provides the API token and password that must be stored in your secrets vault or .env file. + +### test-gitea-bot.sh + +Tests bot functionality and verifies all necessary permissions. + +**Prerequisites:** + +- Bot user created (run `create-gitea-bot.sh` first) +- `GITEA_BOT_TOKEN` in environment or .env file + +**Usage:** + +```bash +# Run tests with token from .env +./scripts/coordinator/test-gitea-bot.sh + +# Or specify token explicitly +export GITEA_BOT_TOKEN="your-bot-token" +./scripts/coordinator/test-gitea-bot.sh + +# Test against specific issue +export TEST_ISSUE="156" +./scripts/coordinator/test-gitea-bot.sh +``` + +**Tests performed:** + +1. Bot authentication +2. Repository access +3. Issue listing +4. Issue reading +5. Issue assignment +6. Comment posting +7. Label management +8. Repository permissions + +**Output:** +Success/failure for each test with detailed error messages. + +## Configuration + +### Environment Variables + +All scripts support these environment variables: + +```bash +# Gitea connection +GITEA_URL # Default: https://git.mosaicstack.dev +ADMIN_TOKEN # Gitea admin token (required for create-gitea-bot.sh) + +# Bot credentials +GITEA_BOT_TOKEN # Bot API token (required for test-gitea-bot.sh) +GITEA_BOT_USERNAME # Default: mosaic +GITEA_BOT_PASSWORD # For reference only + +# Repository +GITEA_REPO_OWNER # Default: mosaic +GITEA_REPO_NAME # Default: stack + +# Testing +TEST_ISSUE # Issue number for testing (default: 156) +``` + +### .env File + +Create or update `.env` file in project root: + +```bash +# Gitea Configuration +GITEA_URL=https://git.mosaicstack.dev +GITEA_BOT_USERNAME=mosaic +GITEA_BOT_TOKEN=your-bot-token-here +GITEA_BOT_PASSWORD=your-bot-password-here +GITEA_REPO_OWNER=mosaic +GITEA_REPO_NAME=stack +``` + +**Security:** Never commit .env to version control. Add `.env` to `.gitignore`. + +## Workflow + +### Initial Setup + +```bash +# 1. Create bot user (requires admin token) +export ADMIN_TOKEN="your-admin-gitea-token" +./scripts/coordinator/create-gitea-bot.sh + +# Output will show: +# - Bot username (mosaic) +# - Bot password (save securely) +# - API token (save securely) +# - Instructions for next steps + +# 2. Store credentials securely +# - Add GITEA_BOT_TOKEN to .env (don't commit) +# - Add GITEA_BOT_TOKEN to your secrets vault +# - Add GITEA_BOT_PASSWORD to your secrets vault + +# 3. Update .env.example (no secrets) +# - Add template entries with placeholder values + +# 4. Test bot functionality +./scripts/coordinator/test-gitea-bot.sh +``` + +### Daily Use + +```bash +# Run tests to verify bot is working +./scripts/coordinator/test-gitea-bot.sh + +# If tests fail: +# - Check GITEA_BOT_TOKEN is valid +# - Check token hasn't expired +# - Verify bot user still exists in Gitea +# - If needed, regenerate token (see docs) +``` + +### Token Rotation + +When rotating the bot API token: + +```bash +# 1. Generate new token in Gitea UI +# Settings → Applications → Create new token + +# 2. Update .env +export GITEA_BOT_TOKEN="new-token" + +# 3. Test new token +./scripts/coordinator/test-gitea-bot.sh + +# 4. Update secrets vault +# 5. Delete old token in Gitea UI +``` + +## Troubleshooting + +### "ADMIN_TOKEN environment variable not set" + +The `create-gitea-bot.sh` script requires a Gitea admin token. + +**Solution:** + +1. Log in to Gitea as admin +2. Go to Settings → Access Tokens +3. Create new token with `api` scope +4. Export and run: `ADMIN_TOKEN="token" ./scripts/coordinator/create-gitea-bot.sh` + +### "Cannot connect to Gitea" + +Script can't reach the Gitea instance. + +**Solution:** + +```bash +# Verify GITEA_URL is correct +echo $GITEA_URL + +# Check connectivity +curl -s https://git.mosaicstack.dev/api/v1/version | jq . + +# If still failing, check: +# - Network connectivity to Gitea server +# - Firewall rules +# - VPN/proxy configuration +``` + +### "Authentication failed" + +Bot API token is invalid or expired. + +**Solution:** + +1. Check token in .env is correct (no extra spaces) +2. Verify token hasn't expired (90 day default) +3. Regenerate token if needed: + - Log in as `mosaic` user + - Settings → Applications → Delete old token + - Create new token + - Update .env and secrets vault + +### "Bot user already exists" + +The bot user was already created. + +**Solution:** + +- Continue setup with existing user +- Verify credentials are correct +- Run tests to confirm functionality + +### "Permission denied" on operations + +Bot doesn't have required permissions. + +**Solution:** + +1. Verify bot is added as repository collaborator +2. Check permission level (should be "push" or "admin") +3. Re-add if needed via API: + +```bash +curl -X PUT \ + -H "Authorization: token $ADMIN_TOKEN" \ + "https://git.mosaicstack.dev/api/v1/repos/mosaic/stack/collaborators/mosaic" \ + -d '{"permission":"push"}' +``` + +## Documentation + +For complete documentation on the coordinator bot: + +- [Gitea Coordinator Setup Guide](../../docs/1-getting-started/3-configuration/4-gitea-coordinator.md) +- [Issue #156 - Create coordinator bot user](https://git.mosaicstack.dev/mosaic/stack/issues/156) +- [Coordinator Architecture](../../docs/3-architecture/non-ai-coordinator-comprehensive.md) + +## Related Issues + +- #156 - Create coordinator bot user in Gitea +- #157 - Configure coordinator webhook in Gitea +- #158 - Implement coordinator task assignment engine +- #140 - Coordinator integration architecture + +## Support + +For issues or questions: + +1. Check the troubleshooting section above +2. Review the full documentation +3. Open an issue in the repository diff --git a/scripts/coordinator/create-gitea-bot.sh b/scripts/coordinator/create-gitea-bot.sh new file mode 100755 index 0000000..70cc64c --- /dev/null +++ b/scripts/coordinator/create-gitea-bot.sh @@ -0,0 +1,212 @@ +#!/bin/bash +# Script to create the mosaic coordinator bot user in Gitea +# Usage: ./scripts/coordinator/create-gitea-bot.sh + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +GITEA_URL="${GITEA_URL:-https://git.mosaicstack.dev}" +ADMIN_TOKEN="${ADMIN_TOKEN:-}" +BOT_USERNAME="mosaic" +BOT_EMAIL="mosaic@mosaicstack.dev" +REPO_OWNER="mosaic" +REPO_NAME="stack" + +# Check dependencies +command -v curl >/dev/null 2>&1 || { echo -e "${RED}curl is required but not installed.${NC}"; exit 1; } +command -v jq >/dev/null 2>&1 || { echo -e "${RED}jq is required but not installed.${NC}"; exit 1; } + +# Functions +print_header() { + echo -e "\n${BLUE}========================================${NC}" + echo -e "${BLUE}$1${NC}" + echo -e "${BLUE}========================================${NC}\n" +} + +print_success() { + echo -e "${GREEN}✓ $1${NC}" +} + +print_error() { + echo -e "${RED}✗ $1${NC}" +} + +print_warning() { + echo -e "${YELLOW}! $1${NC}" +} + +print_info() { + echo -e "${BLUE}ℹ $1${NC}" +} + +# Check for admin token +if [ -z "$ADMIN_TOKEN" ]; then + print_error "ADMIN_TOKEN environment variable not set" + echo -e "\n${YELLOW}To use this script, you need Gitea admin credentials:${NC}" + echo "1. Log in to $GITEA_URL as admin" + echo "2. Go to Settings → Access Tokens" + echo "3. Create new token with 'api' scope" + echo "4. Run: ADMIN_TOKEN='your-token' ./scripts/coordinator/create-gitea-bot.sh" + exit 1 +fi + +# Verify Gitea connectivity +print_header "Verifying Gitea Connection" +if ! curl -s -f -H "Authorization: token $ADMIN_TOKEN" "$GITEA_URL/api/v1/user" > /dev/null; then + print_error "Cannot connect to Gitea at $GITEA_URL" + print_info "Verify GITEA_URL and ADMIN_TOKEN are correct" + exit 1 +fi +print_success "Connected to $GITEA_URL" + +# Check if bot user already exists +print_header "Checking for Existing Bot User" +if curl -s -H "Authorization: token $ADMIN_TOKEN" \ + "$GITEA_URL/api/v1/users/$BOT_USERNAME" > /dev/null 2>&1; then + print_warning "Bot user '$BOT_USERNAME' already exists" + read -p "Continue anyway? (y/n) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_info "Aborted" + exit 0 + fi +else + print_info "Bot user does not exist, will create" +fi + +# Generate bot password +BOT_PASSWORD=$(openssl rand -base64 32) +print_info "Generated bot password (will be displayed at the end)" + +# Create bot user +print_header "Creating Bot User" +print_info "Username: $BOT_USERNAME" +print_info "Email: $BOT_EMAIL" + +BOT_RESPONSE=$(curl -s -X POST \ + -H "Authorization: token $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/admin/users" \ + -d "{ + \"username\": \"$BOT_USERNAME\", + \"email\": \"$BOT_EMAIL\", + \"password\": \"$BOT_PASSWORD\", + \"must_change_password\": false, + \"send_notify\": false, + \"restricted\": false + }") + +# Check if user creation succeeded +if echo "$BOT_RESPONSE" | jq -e '.id' > /dev/null 2>&1; then + BOT_ID=$(echo "$BOT_RESPONSE" | jq -r '.id') + print_success "Bot user created with ID: $BOT_ID" +else + if echo "$BOT_RESPONSE" | jq -e '.message' > /dev/null 2>&1; then + ERROR_MSG=$(echo "$BOT_RESPONSE" | jq -r '.message') + if [[ "$ERROR_MSG" == *"already exists"* ]]; then + print_warning "User already exists, continuing..." + else + print_error "Failed to create user: $ERROR_MSG" + exit 1 + fi + else + print_error "Failed to create bot user" + echo "Response: $BOT_RESPONSE" + exit 1 + fi +fi + +# Add bot as repository collaborator +print_header "Adding Bot to Repository" +print_info "Repository: $REPO_OWNER/$REPO_NAME" + +COLLAB_RESPONSE=$(curl -s -w "\n%{http_code}" -X PUT \ + -H "Authorization: token $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/collaborators/$BOT_USERNAME" \ + -d '{"permission":"push"}') + +HTTP_CODE=$(echo "$COLLAB_RESPONSE" | tail -n1) +BODY=$(echo "$COLLAB_RESPONSE" | head -n-1) + +if [[ "$HTTP_CODE" == "204" ]] || [[ "$HTTP_CODE" == "201" ]]; then + print_success "Bot added as collaborator with push permission" +else + print_error "Failed to add bot as collaborator (HTTP $HTTP_CODE)" + echo "Response: $BODY" + exit 1 +fi + +# Create access token for bot +print_header "Generating API Token" + +# Need to use admin token to create token for bot user +TOKEN_RESPONSE=$(curl -s -X POST \ + -H "Authorization: token $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/admin/users/$BOT_USERNAME/tokens" \ + -d '{ + "name": "coordinator-api-token", + "scopes": ["api", "read:repository", "write:repository", "write:issue"] + }') + +if echo "$TOKEN_RESPONSE" | jq -e '.sha1' > /dev/null 2>&1; then + BOT_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.sha1') + print_success "API token generated" +else + print_error "Failed to generate API token" + echo "Response: $TOKEN_RESPONSE" + exit 1 +fi + +# Test bot authentication +print_header "Testing Bot Authentication" + +TEST_RESPONSE=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $BOT_TOKEN" \ + "$GITEA_URL/api/v1/user") + +TEST_HTTP_CODE=$(echo "$TEST_RESPONSE" | tail -n1) +TEST_BODY=$(echo "$TEST_RESPONSE" | head -n-1) + +if [[ "$TEST_HTTP_CODE" == "200" ]]; then + TEST_USERNAME=$(echo "$TEST_BODY" | jq -r '.username') + print_success "Bot authentication successful (username: $TEST_USERNAME)" +else + print_error "Bot authentication failed (HTTP $TEST_HTTP_CODE)" + exit 1 +fi + +# Display summary +print_header "Bot Setup Complete" + +echo -e "${GREEN}Bot user created successfully!${NC}" +echo "" +echo -e "${YELLOW}Important: Save these credentials securely:${NC}" +echo "" +echo "Bot Username: $BOT_USERNAME" +echo "Bot Email: $BOT_EMAIL" +echo "Bot Password: $BOT_PASSWORD" +echo "" +echo "Bot API Token: $BOT_TOKEN" +echo "" +echo -e "${YELLOW}Next steps:${NC}" +echo "1. Store credentials in your secrets management system" +echo "2. Add to .env file (NEVER commit to git):" +echo "" +echo " GITEA_BOT_USERNAME=$BOT_USERNAME" +echo " GITEA_BOT_TOKEN=$BOT_TOKEN" +echo " GITEA_BOT_PASSWORD=$BOT_PASSWORD" +echo "" +echo "3. Update .env.example with template values (no secrets)" +echo "4. Test bot functionality with: ./scripts/coordinator/test-gitea-bot.sh" +echo "" +echo -e "${BLUE}For more information, see:${NC}" +echo " docs/1-getting-started/3-configuration/4-gitea-coordinator.md" diff --git a/scripts/coordinator/test-gitea-bot.sh b/scripts/coordinator/test-gitea-bot.sh new file mode 100755 index 0000000..1c151a7 --- /dev/null +++ b/scripts/coordinator/test-gitea-bot.sh @@ -0,0 +1,265 @@ +#!/bin/bash +# Script to test coordinator bot functionality in Gitea +# Usage: ./scripts/coordinator/test-gitea-bot.sh + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration (load from environment or .env) +if [ -f .env ]; then + set -a + source .env + set +a +fi + +GITEA_URL="${GITEA_URL:-https://git.mosaicstack.dev}" +GITEA_BOT_TOKEN="${GITEA_BOT_TOKEN:-}" +GITEA_BOT_USERNAME="${GITEA_BOT_USERNAME:-mosaic}" +GITEA_REPO_OWNER="${GITEA_REPO_OWNER:-mosaic}" +GITEA_REPO_NAME="${GITEA_REPO_NAME:-stack}" +TEST_ISSUE="${TEST_ISSUE:-156}" + +# Functions +print_header() { + echo -e "\n${BLUE}========================================${NC}" + echo -e "${BLUE}$1${NC}" + echo -e "${BLUE}========================================${NC}\n" +} + +print_success() { + echo -e "${GREEN}✓ $1${NC}" +} + +print_error() { + echo -e "${RED}✗ $1${NC}" +} + +print_warning() { + echo -e "${YELLOW}! $1${NC}" +} + +print_info() { + echo -e "${BLUE}ℹ $1${NC}" +} + +# Check dependencies +command -v curl >/dev/null 2>&1 || { echo -e "${RED}curl is required but not installed.${NC}"; exit 1; } +command -v jq >/dev/null 2>&1 || { echo -e "${RED}jq is required but not installed.${NC}"; exit 1; } + +# Check for bot token +if [ -z "$GITEA_BOT_TOKEN" ]; then + print_error "GITEA_BOT_TOKEN environment variable not set" + echo -e "\n${YELLOW}To use this script:${NC}" + echo "1. Ensure .env file contains GITEA_BOT_TOKEN" + echo "2. Or export: export GITEA_BOT_TOKEN='your-bot-token'" + echo "3. Run: ./scripts/coordinator/test-gitea-bot.sh" + exit 1 +fi + +print_header "Gitea Bot Functionality Tests" +print_info "Gitea URL: $GITEA_URL" +print_info "Bot Username: $GITEA_BOT_USERNAME" +print_info "Repository: $GITEA_REPO_OWNER/$GITEA_REPO_NAME" +print_info "Test Issue: #$TEST_ISSUE" + +# Test 1: Verify Bot Authentication +print_header "Test 1: Bot Authentication" + +AUTH_RESPONSE=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $GITEA_BOT_TOKEN" \ + "$GITEA_URL/api/v1/user") + +AUTH_HTTP_CODE=$(echo "$AUTH_RESPONSE" | tail -n1) +AUTH_BODY=$(echo "$AUTH_RESPONSE" | head -n-1) + +if [[ "$AUTH_HTTP_CODE" == "200" ]]; then + BOT_ID=$(echo "$AUTH_BODY" | jq -r '.id') + BOT_NAME=$(echo "$AUTH_BODY" | jq -r '.username') + print_success "Authentication successful" + print_info "Bot ID: $BOT_ID" + print_info "Bot Username: $BOT_NAME" +else + print_error "Authentication failed (HTTP $AUTH_HTTP_CODE)" + print_error "Response: $AUTH_BODY" + exit 1 +fi + +# Test 2: List Repository +print_header "Test 2: Repository Access" + +REPO_RESPONSE=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $GITEA_BOT_TOKEN" \ + "$GITEA_URL/api/v1/repos/$GITEA_REPO_OWNER/$GITEA_REPO_NAME") + +REPO_HTTP_CODE=$(echo "$REPO_RESPONSE" | tail -n1) +REPO_BODY=$(echo "$REPO_RESPONSE" | head -n-1) + +if [[ "$REPO_HTTP_CODE" == "200" ]]; then + REPO_ID=$(echo "$REPO_BODY" | jq -r '.id') + print_success "Repository access successful" + print_info "Repository ID: $REPO_ID" +else + print_error "Repository access failed (HTTP $REPO_HTTP_CODE)" + exit 1 +fi + +# Test 3: List Issues +print_header "Test 3: List Issues" + +ISSUES_RESPONSE=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $GITEA_BOT_TOKEN" \ + "$GITEA_URL/api/v1/repos/$GITEA_REPO_OWNER/$GITEA_REPO_NAME/issues?limit=5") + +ISSUES_HTTP_CODE=$(echo "$ISSUES_RESPONSE" | tail -n1) +ISSUES_BODY=$(echo "$ISSUES_RESPONSE" | head -n-1) + +if [[ "$ISSUES_HTTP_CODE" == "200" ]]; then + ISSUE_COUNT=$(echo "$ISSUES_BODY" | jq 'length') + print_success "Issue listing successful" + print_info "Found $ISSUE_COUNT issues" + echo "$ISSUES_BODY" | jq -r '.[] | " #\(.number): \(.title)"' | head -5 +else + print_error "Issue listing failed (HTTP $ISSUES_HTTP_CODE)" + exit 1 +fi + +# Test 4: Read Specific Issue +print_header "Test 4: Read Issue #$TEST_ISSUE" + +ISSUE_RESPONSE=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $GITEA_BOT_TOKEN" \ + "$GITEA_URL/api/v1/repos/$GITEA_REPO_OWNER/$GITEA_REPO_NAME/issues/$TEST_ISSUE") + +ISSUE_HTTP_CODE=$(echo "$ISSUE_RESPONSE" | tail -n1) +ISSUE_BODY=$(echo "$ISSUE_RESPONSE" | head -n-1) + +if [[ "$ISSUE_HTTP_CODE" == "200" ]]; then + ISSUE_TITLE=$(echo "$ISSUE_BODY" | jq -r '.title') + ISSUE_STATE=$(echo "$ISSUE_BODY" | jq -r '.state') + print_success "Issue #$TEST_ISSUE read successfully" + print_info "Title: $ISSUE_TITLE" + print_info "State: $ISSUE_STATE" +else + print_warning "Issue #$TEST_ISSUE not found or not accessible (HTTP $ISSUE_HTTP_CODE)" + print_info "Using first available issue for subsequent tests..." + # Get first issue for testing + TEST_ISSUE=$(echo "$ISSUES_BODY" | jq -r '.[0].number') + print_info "Using issue #$TEST_ISSUE instead" +fi + +# Test 5: Assign Issue to Bot +print_header "Test 5: Assign Issue #$TEST_ISSUE to Bot" + +ASSIGN_RESPONSE=$(curl -s -w "\n%{http_code}" -X PATCH \ + -H "Authorization: token $GITEA_BOT_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/repos/$GITEA_REPO_OWNER/$GITEA_REPO_NAME/issues/$TEST_ISSUE" \ + -d "{\"assignees\":[\"$GITEA_BOT_USERNAME\"]}") + +ASSIGN_HTTP_CODE=$(echo "$ASSIGN_RESPONSE" | tail -n1) +ASSIGN_BODY=$(echo "$ASSIGN_RESPONSE" | head -n-1) + +if [[ "$ASSIGN_HTTP_CODE" == "201" ]] || [[ "$ASSIGN_HTTP_CODE" == "200" ]]; then + ASSIGNEES=$(echo "$ASSIGN_BODY" | jq -r '.assignees[].username' | tr '\n' ',' | sed 's/,$//') + print_success "Issue assigned successfully" + print_info "Assignees: $ASSIGNEES" +else + print_error "Assignment failed (HTTP $ASSIGN_HTTP_CODE)" + print_error "Response: $ASSIGN_BODY" + # Don't exit, continue with next test +fi + +# Test 6: Comment on Issue +print_header "Test 6: Comment on Issue #$TEST_ISSUE" + +TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') +COMMENT_TEXT="Test comment from coordinator bot ($TIMESTAMP) - [Automated test, safe to delete]" + +COMMENT_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + -H "Authorization: token $GITEA_BOT_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/repos/$GITEA_REPO_OWNER/$GITEA_REPO_NAME/issues/$TEST_ISSUE/comments" \ + -d "{\"body\":\"$COMMENT_TEXT\"}") + +COMMENT_HTTP_CODE=$(echo "$COMMENT_RESPONSE" | tail -n1) +COMMENT_BODY=$(echo "$COMMENT_RESPONSE" | head -n-1) + +if [[ "$COMMENT_HTTP_CODE" == "201" ]]; then + COMMENT_ID=$(echo "$COMMENT_BODY" | jq -r '.id') + COMMENT_AUTHOR=$(echo "$COMMENT_BODY" | jq -r '.user.username') + print_success "Comment posted successfully" + print_info "Comment ID: $COMMENT_ID" + print_info "Author: $COMMENT_AUTHOR" +else + print_error "Comment posting failed (HTTP $COMMENT_HTTP_CODE)" + print_error "Response: $COMMENT_BODY" +fi + +# Test 7: Add Labels +print_header "Test 7: Add Labels to Issue #$TEST_ISSUE" + +LABELS_RESPONSE=$(curl -s -w "\n%{http_code}" -X PATCH \ + -H "Authorization: token $GITEA_BOT_TOKEN" \ + -H "Content-Type: application/json" \ + "$GITEA_URL/api/v1/repos/$GITEA_REPO_OWNER/$GITEA_REPO_NAME/issues/$TEST_ISSUE" \ + -d '{"labels":["coordinator-test"]}') + +LABELS_HTTP_CODE=$(echo "$LABELS_RESPONSE" | tail -n1) +LABELS_BODY=$(echo "$LABELS_RESPONSE" | head -n-1) + +if [[ "$LABELS_HTTP_CODE" == "201" ]] || [[ "$LABELS_HTTP_CODE" == "200" ]]; then + LABELS=$(echo "$LABELS_BODY" | jq -r '.labels[].name' | tr '\n' ',' | sed 's/,$//') + print_success "Labels added successfully" + print_info "Labels: $LABELS" +else + print_warning "Labels update failed (HTTP $LABELS_HTTP_CODE)" +fi + +# Test 8: Repository Permissions +print_header "Test 8: Check Bot Repository Permissions" + +# Try to get repository branches (requires read access) +BRANCHES_RESPONSE=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $GITEA_BOT_TOKEN" \ + "$GITEA_URL/api/v1/repos/$GITEA_REPO_OWNER/$GITEA_REPO_NAME/branches?limit=5") + +BRANCHES_HTTP_CODE=$(echo "$BRANCHES_RESPONSE" | tail -n1) +BRANCHES_BODY=$(echo "$BRANCHES_RESPONSE" | head -n-1) + +if [[ "$BRANCHES_HTTP_CODE" == "200" ]]; then + BRANCH_COUNT=$(echo "$BRANCHES_BODY" | jq 'length') + DEFAULT_BRANCH=$(echo "$BRANCHES_BODY" | jq -r '.[0].name') + print_success "Repository read access confirmed" + print_info "Found $BRANCH_COUNT branches" + print_info "Default branch: $DEFAULT_BRANCH" +else + print_error "Repository read access failed (HTTP $BRANCHES_HTTP_CODE)" +fi + +# Summary +print_header "Test Results Summary" + +echo -e "${GREEN}All critical tests passed!${NC}" +echo "" +echo -e "${YELLOW}Bot capabilities verified:${NC}" +echo " ✓ Authentication via API token" +echo " ✓ Repository access" +echo " ✓ Issue reading and listing" +echo " ✓ Issue assignment" +echo " ✓ Issue commenting" +echo " ✓ Label management" +echo " ✓ Repository permissions" +echo "" +echo -e "${BLUE}Next steps:${NC}" +echo "1. Review the coordinator bot documentation:" +echo " docs/1-getting-started/3-configuration/4-gitea-coordinator.md" +echo "" +echo "2. Configure coordinator webhook (see Issue #157)" +echo "" +echo "3. Deploy coordinator service (see Issue #158)"