# 🚀 .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
4. **Deploy TEST environment?** (yes/no)
   - Answer: `_______`

5. **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
8. **Forgejo Runner Setup** (existing/new)
   - Answer: `_______`

9. **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

```yaml
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**

```ini
[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**

```ini
[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

```bash
#!/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**

```bash
#!/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

```markdown
# {{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

```bash
# 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)

```bash
# 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

```bash
git push origin main
```

Check deployment:
```bash
# 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:
```bash
# 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

```bash
{{#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

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

### Restart Services

```bash
{{#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:
```bash
ssh-keygen -t ed25519 -C "deploy@forgejo-ci"
```

Add public key to server:
```bash
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@192.168.1.43
```

Add private key to Forgejo secrets:
```bash
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)

```bash
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