appaibuiltest/deploy/AI-DEPLOYMENT-PROMPT.md
Janus C. H. Knudsen b778d91196 fc
2026-01-30 22:56:31 +01:00

18 KiB

🚀 .NET Application Deployment Generator

Purpose: Generate complete deployment configuration for a new .NET application on your Forgejo CI/CD infrastructure.

Infrastructure:

  • Server: 192.168.1.43 (Ubuntu 24.04)
  • Forgejo: 192.168.1.63:3000
  • Deploy User: deploy
  • Reverse Proxy: Caddy
  • Process Manager: systemd
  • Deployment Method: self-contained .NET publish + rsync + SSH

PART 1: INFORMATION COLLECTION

Ask the user the following questions and collect their answers:

Application Configuration

  1. Application Name (e.g., "BlogApp", "ShopAPI", "DashboardApp")

    • Answer: _______
  2. .NET Version (e.g., "9.0.x", "10.0.x")

    • Answer: _______
  3. Executable Name (defaults to Application Name if blank)

    • Answer: _______ (or blank to use app name)

Environment Setup

  1. Deploy TEST environment? (yes/no)

    • Answer: _______
  2. Deploy PROD environment? (yes/no)

    • Answer: _______

Network Configuration (TEST)

Skip if TEST not enabled

6a. TEST Port Number (e.g., 5002, 5010, 5020)

  • Answer: _______

6b. TEST Domain (e.g., "test-blog.jarjarbinks", "staging-shop.jarjarbinks")

  • Answer: _______

Network Configuration (PROD)

Skip if PROD not enabled

7a. PROD Port Number (e.g., 5003, 5011, 5021)

  • Answer: _______

7b. PROD Domain (e.g., "blog.jarjarbinks", "shop.jarjarbinks")

  • Answer: _______

Runner Configuration

  1. Forgejo Runner Setup (existing/new)

    • Answer: _______
  2. Runner Name (if new runner selected, e.g., "blogapp-runner", "shop-runner")

    • Answer: _______ (skip if using existing)

PART 2: VARIABLE MAPPING

Based on user answers, create these variables:

Variable Derivation Example
{{APP_NAME}} Q1 answer BlogApp
{{APP_NAME_LOWER}} Q1 lowercase blogapp
{{DOTNET_VERSION}} Q2 answer 9.0.x
{{EXECUTABLE_NAME}} Q3 answer or Q1 BlogApp
{{INCLUDE_TEST}} Q4 = "yes" true
{{INCLUDE_PROD}} Q5 = "yes" true
{{TEST_PORT}} Q6a answer 5002
{{TEST_DOMAIN}} Q6b answer test-blog.jarjarbinks
{{TEST_FOLDER}} /opt/{{APP_NAME_LOWER}}-test/app /opt/blogapp-test/app
{{TEST_SERVICE}} {{APP_NAME_LOWER}}-test blogapp-test
{{PROD_PORT}} Q7a answer 5003
{{PROD_DOMAIN}} Q7b answer blog.jarjarbinks
{{PROD_FOLDER}} /opt/{{APP_NAME_LOWER}}/app /opt/blogapp/app
{{PROD_SERVICE}} {{APP_NAME_LOWER}} blogapp
{{CREATE_RUNNER}} Q8 = "new" true
{{RUNNER_NAME}} Q9 answer blogapp-runner

PART 3: OUTPUT FOLDER STRUCTURE

Create this folder structure with generated files:

deployment-configs-{{APP_NAME_LOWER}}/
├── README.md
├── .forgejo/
│   └── workflows/
│       └── cicd.yml
├── systemd/
│   ├── {{APP_NAME_LOWER}}-test.service    # if INCLUDE_TEST
│   └── {{APP_NAME_LOWER}}.service         # if INCLUDE_PROD
├── caddy/
│   └── {{APP_NAME_LOWER}}-config.txt
├── scripts/
│   ├── setup-server.sh
│   └── setup-runner.sh                     # if CREATE_RUNNER
└── secrets-checklist.md

PART 4: FILE TEMPLATES

Template 1: .forgejo/workflows/cicd.yml

name: {{APP_NAME}}-cicd

on:
  push:
    branches: [ "main" ]
  workflow_dispatch: {}

jobs:
{{#if INCLUDE_TEST}}
  build_test_deploy_test:
    runs-on: docker
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: "{{DOTNET_VERSION}}"

      - name: Test
        run: dotnet test -c Release

      - name: Publish (self-contained linux-x64)
        run: dotnet publish -c Release -r linux-x64 --self-contained true -o out

      - name: Install deploy tools
        run: |
          apt-get update
          apt-get install -y rsync openssh-client

      - name: Deploy TEST
        env:
          DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
          DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
          DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
        run: |
          set -e
          mkdir -p ~/.ssh
          chmod 700 ~/.ssh
          echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts

          rsync -az --delete out/ "$DEPLOY_USER@$DEPLOY_HOST:{{TEST_FOLDER}}/"
          ssh "$DEPLOY_USER@$DEPLOY_HOST" "chmod +x {{TEST_FOLDER}}/{{EXECUTABLE_NAME}} && sudo systemctl restart {{TEST_SERVICE}}"
{{/if}}

{{#if INCLUDE_PROD}}
  deploy_prod_manual:
    if: ${{ github.event_name == 'workflow_dispatch' }}
    runs-on: docker
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: "{{DOTNET_VERSION}}"

      - name: Publish (self-contained linux-x64)
        run: dotnet publish -c Release -r linux-x64 --self-contained true -o out

      - name: Install deploy tools
        run: |
          apt-get update
          apt-get install -y rsync openssh-client

      - name: Deploy PROD
        env:
          DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
          DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
          DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
        run: |
          set -e
          mkdir -p ~/.ssh
          chmod 700 ~/.ssh
          echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts

          rsync -az --delete out/ "$DEPLOY_USER@$DEPLOY_HOST:{{PROD_FOLDER}}/"
          ssh "$DEPLOY_USER@$DEPLOY_HOST" "chmod +x {{PROD_FOLDER}}/{{EXECUTABLE_NAME}} && sudo systemctl restart {{PROD_SERVICE}}"
{{/if}}

Generation Rule:

  • Remove {{#if INCLUDE_TEST}}...{{/if}} block if TEST not enabled
  • Remove {{#if INCLUDE_PROD}}...{{/if}} block if PROD not enabled
  • Substitute all {{VARIABLES}} with actual values

Template 2: systemd/{{APP_NAME_LOWER}}-test.service

Generate only if INCLUDE_TEST = true

[Unit]
Description={{APP_NAME}} TEST Environment
After=network.target

[Service]
Type=notify
WorkingDirectory={{TEST_FOLDER}}
ExecStart={{TEST_FOLDER}}/{{EXECUTABLE_NAME}}
Environment=ASPNETCORE_URLS=http://127.0.0.1:{{TEST_PORT}}
Environment=ASPNETCORE_ENVIRONMENT=Staging
User=minapp
Group=minapp
Restart=always
RestartSec=10
SyslogIdentifier={{TEST_SERVICE}}

[Install]
WantedBy=multi-user.target

Template 3: systemd/{{APP_NAME_LOWER}}.service

Generate only if INCLUDE_PROD = true

[Unit]
Description={{APP_NAME}} PRODUCTION Environment
After=network.target

[Service]
Type=notify
WorkingDirectory={{PROD_FOLDER}}
ExecStart={{PROD_FOLDER}}/{{EXECUTABLE_NAME}}
Environment=ASPNETCORE_URLS=http://127.0.0.1:{{PROD_PORT}}
Environment=ASPNETCORE_ENVIRONMENT=Production
User=minapp
Group=minapp
Restart=always
RestartSec=10
SyslogIdentifier={{PROD_SERVICE}}

[Install]
WantedBy=multi-user.target

Template 4: caddy/{{APP_NAME_LOWER}}-config.txt

{{#if INCLUDE_TEST}}
# TEST Environment
http://{{TEST_DOMAIN}} {
    reverse_proxy 127.0.0.1:{{TEST_PORT}}
}

{{/if}}
{{#if INCLUDE_PROD}}
# PRODUCTION Environment
http://{{PROD_DOMAIN}} {
    reverse_proxy 127.0.0.1:{{PROD_PORT}}
}
{{/if}}

Note: This content must be manually added to /etc/caddy/Caddyfile on the server.


Template 5: scripts/setup-server.sh

#!/bin/bash
# Setup script for {{APP_NAME}} deployment
# Run this on the webserver (192.168.1.43)

set -e

echo "==================================="
echo "Setting up {{APP_NAME}} on server"
echo "==================================="

{{#if INCLUDE_TEST}}
# Create TEST directories
echo "Creating TEST environment directories..."
sudo mkdir -p {{TEST_FOLDER}}
sudo chown -R minapp:minapp /opt/{{APP_NAME_LOWER}}-test

# Install TEST systemd service
echo "Installing TEST systemd service..."
sudo cp systemd/{{APP_NAME_LOWER}}-test.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable {{TEST_SERVICE}}
echo "✓ TEST service installed: {{TEST_SERVICE}}"

{{/if}}
{{#if INCLUDE_PROD}}
# Create PROD directories
echo "Creating PROD environment directories..."
sudo mkdir -p {{PROD_FOLDER}}
sudo chown -R minapp:minapp /opt/{{APP_NAME_LOWER}}

# Install PROD systemd service
echo "Installing PROD systemd service..."
sudo cp systemd/{{APP_NAME_LOWER}}.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable {{PROD_SERVICE}}
echo "✓ PROD service installed: {{PROD_SERVICE}}"

{{/if}}
# Update Caddy configuration
echo ""
echo "==================================="
echo "MANUAL STEP REQUIRED:"
echo "==================================="
echo "Add the following to /etc/caddy/Caddyfile:"
echo ""
cat caddy/{{APP_NAME_LOWER}}-config.txt
echo ""
echo "Then run: sudo systemctl reload caddy"
echo ""

echo "==================================="
echo "Setup complete!"
echo "==================================="
{{#if INCLUDE_TEST}}
echo "TEST service: {{TEST_SERVICE}}"
echo "TEST folder: {{TEST_FOLDER}}"
echo "TEST URL: http://{{TEST_DOMAIN}}"
{{/if}}
{{#if INCLUDE_PROD}}
echo "PROD service: {{PROD_SERVICE}}"
echo "PROD folder: {{PROD_FOLDER}}"
echo "PROD URL: http://{{PROD_DOMAIN}}"
{{/if}}
echo "==================================="

Template 6: scripts/setup-runner.sh

Generate only if CREATE_RUNNER = true

#!/bin/bash
# Setup Forgejo Runner for {{APP_NAME}}
# Run this on the Forgejo server (192.168.1.63)

set -e

RUNNER_NAME="{{RUNNER_NAME}}"
FORGEJO_URL="http://192.168.1.63:3000"

echo "==================================="
echo "Setting up Forgejo Runner: $RUNNER_NAME"
echo "==================================="

# Create runner directory
mkdir -p ~/forgejo-runners/$RUNNER_NAME
cd ~/forgejo-runners/$RUNNER_NAME

# Create docker-compose.yml
cat > docker-compose.yml <<'EOF'
version: '3'

services:
  runner:
    image: code.forgejo.org/forgejo/runner:latest
    container_name: {{RUNNER_NAME}}
    restart: unless-stopped
    volumes:
      - ./data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DOCKER_HOST=unix:///var/run/docker.sock
EOF

echo "✓ docker-compose.yml created"

# Start the runner
echo "Starting runner container..."
docker-compose up -d

echo "Waiting for runner to start..."
sleep 5

# Register runner
echo ""
echo "==================================="
echo "MANUAL STEP REQUIRED:"
echo "==================================="
echo "1. Go to Forgejo → Repository Settings → Actions → Runners"
echo "2. Click 'Create new Runner'"
echo "3. Copy the registration token"
echo "4. Run the following command:"
echo ""
echo "docker exec -it {{RUNNER_NAME}} forgejo-runner register \\"
echo "  --instance $FORGEJO_URL \\"
echo "  --token YOUR_REGISTRATION_TOKEN \\"
echo "  --name {{RUNNER_NAME}} \\"
echo "  --labels docker:docker://node:20-bookworm"
echo ""
echo "==================================="

Template 7: README.md

# {{APP_NAME}} - Deployment Configuration

**Generated:** $(date)

## Overview

This folder contains all configuration files needed to deploy **{{APP_NAME}}** to your infrastructure:

- **Server:** 192.168.1.43 (Ubuntu 24.04)
- **Forgejo:** 192.168.1.63:3000
- **.NET Version:** {{DOTNET_VERSION}}

## Environments

{{#if INCLUDE_TEST}}
### TEST Environment
- **Port:** {{TEST_PORT}}
- **Domain:** http://{{TEST_DOMAIN}}
- **Folder:** {{TEST_FOLDER}}
- **Service:** {{TEST_SERVICE}}
- **Deploy:** Automatic on `git push` to main
{{/if}}

{{#if INCLUDE_PROD}}
### PROD Environment
- **Port:** {{PROD_PORT}}
- **Domain:** http://{{PROD_DOMAIN}}
- **Folder:** {{PROD_FOLDER}}
- **Service:** {{PROD_SERVICE}}
- **Deploy:** Manual via workflow_dispatch
{{/if}}

## Setup Instructions

### 1. Configure Forgejo Secrets

Go to your repository in Forgejo → Settings → Actions → Secrets

Required secrets (should already exist):
- `DEPLOY_HOST` = `192.168.1.43`
- `DEPLOY_USER` = `deploy`
- `DEPLOY_SSH_KEY` = SSH private key for deploy user

### 2. Copy Workflow to Repository

```bash
# In your repository
mkdir -p .forgejo/workflows
cp .forgejo/workflows/cicd.yml YOUR_REPO/.forgejo/workflows/
cd YOUR_REPO
git add .forgejo/workflows/cicd.yml
git commit -m "Add CI/CD workflow"
git push

3. Setup Server

# Copy files to server
scp -r systemd caddy scripts deploy@192.168.1.43:~/{{APP_NAME_LOWER}}-setup/

# SSH to server
ssh deploy@192.168.1.43

# Run setup script
cd ~/{{APP_NAME_LOWER}}-setup
chmod +x scripts/setup-server.sh
./scripts/setup-server.sh

# Manually add Caddy configuration (as instructed by script)
sudo nano /etc/caddy/Caddyfile
# Add content from caddy/{{APP_NAME_LOWER}}-config.txt
sudo systemctl reload caddy

{{#if CREATE_RUNNER}}

4. Setup Forgejo Runner (Optional)

# Copy script to Forgejo server
scp scripts/setup-runner.sh forgejo@192.168.1.63:~/

# SSH to Forgejo server
ssh forgejo@192.168.1.63

# Run setup script
chmod +x setup-runner.sh
./setup-runner.sh

# Follow instructions to register runner

{{/if}}

Deployment

{{#if INCLUDE_TEST}}

Deploy to TEST

git push origin main

Check deployment:

# On server
sudo systemctl status {{TEST_SERVICE}}
sudo journalctl -u {{TEST_SERVICE}} -n 50

# Test locally
curl http://127.0.0.1:{{TEST_PORT}}

# Test via Caddy
curl http://{{TEST_DOMAIN}}

{{/if}}

{{#if INCLUDE_PROD}}

Deploy to PROD

  1. Go to Forgejo → Repository → Actions
  2. Select the workflow
  3. Click Run workflow
  4. Confirm deployment

Check deployment:

# On server
sudo systemctl status {{PROD_SERVICE}}
sudo journalctl -u {{PROD_SERVICE}} -n 50

# Test locally
curl http://127.0.0.1:{{PROD_PORT}}

# Test via Caddy
curl http://{{PROD_DOMAIN}}

{{/if}}

Troubleshooting

Service Issues

{{#if INCLUDE_TEST}}
# TEST service
sudo systemctl status {{TEST_SERVICE}}
sudo journalctl -u {{TEST_SERVICE}} -n 100 -f
{{/if}}

{{#if INCLUDE_PROD}}
# PROD service
sudo systemctl status {{PROD_SERVICE}}
sudo journalctl -u {{PROD_SERVICE}} -n 100 -f
{{/if}}

Check Ports

ss -lntp | grep {{TEST_PORT}}
ss -lntp | grep {{PROD_PORT}}

Restart Services

{{#if INCLUDE_TEST}}
sudo systemctl restart {{TEST_SERVICE}}
{{/if}}
{{#if INCLUDE_PROD}}
sudo systemctl restart {{PROD_SERVICE}}
{{/if}}
sudo systemctl reload caddy

File Structure

{{TEST_FOLDER}}/          # TEST deployment
{{PROD_FOLDER}}/          # PROD deployment
/etc/systemd/system/{{TEST_SERVICE}}.service
/etc/systemd/system/{{PROD_SERVICE}}.service
/etc/caddy/Caddyfile      # Contains reverse proxy config

---

### Template 8: secrets-checklist.md

```markdown
# Forgejo Secrets Checklist

Repository: **YOUR_REPO_NAME**

Go to: Forgejo → Repository → Settings → Actions → Secrets

## Required Secrets

| Secret Name | Value | Status |
|-------------|-------|--------|
| `DEPLOY_HOST` | `192.168.1.43` | ☐ |
| `DEPLOY_USER` | `deploy` | ☐ |
| `DEPLOY_SSH_KEY` | SSH private key (ED25519) | ☐ |

## Verify Secrets

These secrets should already exist from previous deployments. Verify they are configured:

```bash
# Test SSH connection
ssh deploy@192.168.1.43 "echo Connection successful"

If Secrets Missing

Generate SSH key pair:

ssh-keygen -t ed25519 -C "deploy@forgejo-ci"

Add public key to server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@192.168.1.43

Add private key to Forgejo secrets:

cat ~/.ssh/id_ed25519
# Copy output and paste in Forgejo secret

---

## PART 5: GENERATION INSTRUCTIONS

### Step 1: Create Output Folder

mkdir deployment-configs-{{APP_NAME_LOWER}} cd deployment-configs-{{APP_NAME_LOWER}}


### Step 2: Apply Variable Substitutions

For each template:
1. Replace all `{{VARIABLE}}` with actual values from mapping table
2. Process conditional blocks:
   - Remove `{{#if CONDITION}}...{{/if}}` wrapper and keep content if condition is true
   - Remove entire `{{#if CONDITION}}...{{/if}}` block if condition is false
3. Save to appropriate file in folder structure

### Step 3: Generate Files

Create each file based on conditions:

| File | Condition | Template |
|------|-----------|----------|
| `.forgejo/workflows/cicd.yml` | Always | Template 1 |
| `systemd/{{APP_NAME_LOWER}}-test.service` | INCLUDE_TEST | Template 2 |
| `systemd/{{APP_NAME_LOWER}}.service` | INCLUDE_PROD | Template 3 |
| `caddy/{{APP_NAME_LOWER}}-config.txt` | Always | Template 4 |
| `scripts/setup-server.sh` | Always | Template 5 |
| `scripts/setup-runner.sh` | CREATE_RUNNER | Template 6 |
| `README.md` | Always | Template 7 |
| `secrets-checklist.md` | Always | Template 8 |

### Step 4: Set Execute Permissions

```bash
chmod +x scripts/*.sh

Step 5: Create Archive (Optional)

tar -czf deployment-configs-{{APP_NAME_LOWER}}.tar.gz deployment-configs-{{APP_NAME_LOWER}}/

PART 6: EXAMPLE OUTPUT

For user answers:

  • App Name: BlogApp
  • .NET Version: 9.0.x
  • Executable: BlogApp
  • TEST: yes, port 5002, domain test-blog.jarjarbinks
  • PROD: yes, port 5003, domain blog.jarjarbinks
  • Runner: existing

Generated folder structure:

deployment-configs-blogapp/
├── README.md
├── .forgejo/
│   └── workflows/
│       └── cicd.yml
├── systemd/
│   ├── blogapp-test.service
│   └── blogapp.service
├── caddy/
│   └── blogapp-config.txt
├── scripts/
│   └── setup-server.sh
└── secrets-checklist.md

USAGE

  1. Copy this prompt to your AI assistant
  2. Answer all questions in PART 1
  3. AI will generate all files with substituted values
  4. Review generated files
  5. Follow README.md instructions to deploy

Version: 1.0
Compatible with: .NET 9+, Ubuntu 24.04, Forgejo Actions
Author: Janus