diff --git a/.forgejo/README.md b/.forgejo/README.md new file mode 100644 index 00000000..81b95519 --- /dev/null +++ b/.forgejo/README.md @@ -0,0 +1,96 @@ +# Forgejo Actions Configuration + +This directory contains Forgejo Actions workflows (GitHub Actions-compatible) for CI/CD. + +## Migration from GitLab CI + +The `.forgejo/workflows/build-deploy.yml` file replaces `.gitlab-ci.yml` with equivalent functionality: + +### Key Differences + +| GitLab CI | Forgejo Actions | +|-----------|-----------------| +| `stages:` | `jobs:` (jobs run in dependency order) | +| `tags: [home]` | `runs-on: home` | +| `rules:` | `on:` (trigger conditions) | +| `$CI_COMMIT_SHA` | `${{ github.sha }}` | +| `$CI_REGISTRY_IMAGE` | `${{ secrets.REGISTRY_URL }}/${{ github.repository }}` | +| `workflow.auto_cancel` | `concurrency.cancel-in-progress` | +| Docker-in-Docker service | `docker/build-push-action@v5` | + +### Required Secrets + +Configure these in your Forgejo repository settings (Settings → Secrets): + +1. **`REGISTRY_URL`** - Container registry URL (e.g., `registry.gitlab.com`) +2. **`REGISTRY_USER`** - Container registry username +3. **`REGISTRY_PASSWORD`** - Container registry password/token + +### Required Runner Setup + +The workflow assumes a self-hosted runner with label `home` that has: + +1. **Docker** - For building images +2. **kubectl** - Pre-configured with cluster access + - Context: `towerops/towerops:home-cluster-agent` must exist + - Namespace: `towerops` with deployment named `towerops` + +### Workflow Stages + +#### 1. Build +- Triggers on push to `main` branch +- Checks out code +- Logs into container registry +- Builds Docker image using `k8s/Dockerfile` +- Uses layer caching from `:latest` tag for faster builds +- Pushes two tags: + - `/:` - Specific version + - `/:latest` - Latest build + +#### 2. Deploy +- Depends on successful build +- Uses `kubectl` to update Kubernetes deployment +- Sets image to the newly built commit SHA +- Adds `DEPLOY_TIMESTAMP` environment variable +- Lets Kubernetes handle rollout asynchronously + +### Testing the Workflow + +To test without deploying: + +```bash +# Validate workflow syntax +act --list -W .forgejo/workflows/build-deploy.yml + +# Run build job locally (if act is installed) +act push -W .forgejo/workflows/build-deploy.yml -j build +``` + +### Comparison to GitLab CI + +**Advantages:** +- Uses official Docker actions (more reliable layer caching) +- Better concurrency control (auto-cancels old runs) +- Clearer job dependencies with `needs:` + +**Equivalent Features:** +- Same Docker build process with BUILDKIT and caching +- Same kubectl deployment commands +- Same environment protection (production) +- Same branch filtering (main only) + +### Troubleshooting + +**Build fails with registry authentication:** +- Verify `REGISTRY_USER` and `REGISTRY_PASSWORD` secrets are set +- Check runner has network access to registry + +**Deploy fails with kubectl errors:** +- Verify runner has kubectl installed: `kubectl version --client` +- Check kubeconfig context exists: `kubectl config get-contexts` +- Verify cluster access: `kubectl get deployments -n towerops` + +**Runner not picking up jobs:** +- Verify runner is registered with label `home` +- Check runner logs in Forgejo Actions settings +- Ensure runner is online and connected diff --git a/.forgejo/workflows/build-deploy.yml b/.forgejo/workflows/build-deploy.yml new file mode 100644 index 00000000..978370fb --- /dev/null +++ b/.forgejo/workflows/build-deploy.yml @@ -0,0 +1,86 @@ +name: Build and Deploy + +on: + push: + branches: + - main + +env: + DOCKER_BUILDKIT: 1 + DOCKER_TLS_CERTDIR: "" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: home + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to container registry + uses: docker/login-action@v3 + with: + registry: ${{ secrets.REGISTRY_URL }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Extract metadata + id: meta + run: | + echo "image=${{ secrets.REGISTRY_URL }}/${{ github.repository }}" >> $GITHUB_OUTPUT + echo "sha_tag=${{ secrets.REGISTRY_URL }}/${{ github.repository }}:${{ github.sha }}" >> $GITHUB_OUTPUT + echo "latest_tag=${{ secrets.REGISTRY_URL }}/${{ github.repository }}:latest" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: k8s/Dockerfile + push: true + tags: | + ${{ steps.meta.outputs.sha_tag }} + ${{ steps.meta.outputs.latest_tag }} + cache-from: type=registry,ref=${{ steps.meta.outputs.latest_tag }} + cache-to: type=inline + build-args: | + BUILDKIT_INLINE_CACHE=1 + + deploy: + runs-on: home + needs: build + environment: + name: production + steps: + - name: Set up kubectl + run: | + # Verify kubectl is available + kubectl version --client + + - name: Configure kubectl + run: | + # Configure kubectl context + kubectl config get-contexts + kubectl config use-context towerops/towerops:home-cluster-agent + + - name: Deploy to Kubernetes + run: | + # Set deployment timestamp (ISO 8601 format in UTC) + DEPLOY_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + echo "Deploying at $DEPLOY_TIMESTAMP" + + # Deploy new version (migrations run on app start) + kubectl set image deployment/towerops \ + towerops=${{ secrets.REGISTRY_URL }}/${{ github.repository }}:${{ github.sha }} \ + -n towerops + + kubectl set env deployment/towerops \ + DEPLOY_TIMESTAMP=$DEPLOY_TIMESTAMP \ + -n towerops + + echo "Deployment initiated. Kubernetes will handle rollout asynchronously."