# 🚀 .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