From 25946d3c7ffd46a4ec94241c015bdbda5eb210bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:40:26 +0000 Subject: [PATCH 01/17] Initial plan From 074a0d57b21683c77e9d63a5d9b688e3e2e54d31 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:43:33 +0000 Subject: [PATCH 02/17] Add Azure Static Web Apps deployment infrastructure and workflow Co-authored-by: robpitcher <13648061+robpitcher@users.noreply.github.com> --- .github/workflows/azure-swa-deploy.yml | 72 +++++++++++++++++++ .gitignore | 6 +- README.md | 62 ++++++++++++++++- infra/README.md | 96 ++++++++++++++++++++++++++ infra/bicep.parameters.json | 21 ++++++ infra/main.bicep | 43 ++++++++++++ infra/main.json | 89 ++++++++++++++++++++++++ staticwebapp.config.json | 29 ++++++++ 8 files changed, 415 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/azure-swa-deploy.yml create mode 100644 infra/README.md create mode 100644 infra/bicep.parameters.json create mode 100644 infra/main.bicep create mode 100644 infra/main.json create mode 100644 staticwebapp.config.json diff --git a/.github/workflows/azure-swa-deploy.yml b/.github/workflows/azure-swa-deploy.yml new file mode 100644 index 0000000..1e1e4b0 --- /dev/null +++ b/.github/workflows/azure-swa-deploy.yml @@ -0,0 +1,72 @@ +name: Deploy to Azure Static Web Apps + +on: + push: + branches: + - main + paths: + - 'docs/**' + pull_request: + types: [opened, synchronize, reopened, closed] + branches: + - main + paths: + - 'docs/**' + workflow_dispatch: + +# Sets permissions for GITHUB_TOKEN +permissions: + contents: read + pull-requests: write + +# Allow only one concurrent deployment +concurrency: + group: "azure-swa" + cancel-in-progress: false + +jobs: + build_and_deploy: + # Only run on push or if PR is not closed + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.action != 'closed') + runs-on: ubuntu-latest + name: Build and Deploy + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.x + + - name: Install DocFX + run: dotnet tool update -g docfx + + - name: Build documentation + run: docfx docs/docfx.json + + - name: Deploy to Azure Static Web Apps + id: deploy + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} + repo_token: ${{ secrets.GITHUB_TOKEN }} + action: upload + app_location: "docs/_site" + api_location: "" + output_location: "" + skip_app_build: true + + close_pull_request: + # Only run when PR is closed + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + name: Close Pull Request + steps: + - name: Close Pull Request + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} + action: close diff --git a/.gitignore b/.gitignore index 31873c8..596ccb1 100644 --- a/.gitignore +++ b/.gitignore @@ -53,4 +53,8 @@ CodeCoverage/ TestResult.xml nunit-*.xml -_site/ \ No newline at end of file +_site/ + +# Azure Static Web Apps +.azure/ +swa-cli.config.json \ No newline at end of file diff --git a/README.md b/README.md index 9466436..d8ecd22 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,66 @@ Output goes to `docs/_site/`. ## Publish -This repo includes a GitHub Actions workflow that builds the site and deploys `docs/_site` to GitHub Pages on pushes to `main`. +This repository supports two deployment options: + +### Option 1: GitHub Pages (Default) + +A GitHub Actions workflow automatically builds and deploys `docs/_site` to GitHub Pages on pushes to `main`. + +Workflow: `.github/workflows/publish-site.yml` + +### Option 2: Azure Static Web Apps + +Deploy to Azure Static Web Apps for a production-ready hosting solution with global CDN, custom domains, and automatic SSL certificates. + +#### Quick Setup + +1. **Deploy Infrastructure** + ```bash + # Edit parameters in infra/bicep.parameters.json + # Then deploy: + cd infra + az group create --name rg-docfx-wiki --location eastus2 + az deployment group create \ + --resource-group rg-docfx-wiki \ + --template-file main.bicep \ + --parameters bicep.parameters.json + ``` + +2. **Get Deployment Token** + ```bash + az deployment group show \ + --resource-group rg-docfx-wiki \ + --name main \ + --query properties.outputs.deploymentToken.value \ + --output tsv + ``` + +3. **Add GitHub Secret** + - Go to repository Settings → Secrets and variables → Actions + - Create new secret: `AZURE_STATIC_WEB_APPS_API_TOKEN` + - Paste the deployment token + +4. **Enable Workflow** + + The workflow `.github/workflows/azure-swa-deploy.yml` will automatically deploy on pushes to `main` when documentation changes. + +#### Customization + +- **Infrastructure**: Edit `infra/bicep.parameters.json` to customize: + - `staticWebAppName`: Your unique app name + - `location`: Azure region + - `sku`: Free or Standard tier + - `tags`: Resource organization tags + +- **Runtime Configuration**: Edit `staticwebapp.config.json` for: + - Navigation fallback rules + - Custom routes + - Headers and MIME types + +See [infra/README.md](infra/README.md) for detailed deployment instructions. ## Learn more -- DocFX project: https://github.com/dotnet/docfx \ No newline at end of file +- DocFX project: https://github.com/dotnet/docfx +- Azure Static Web Apps: https://learn.microsoft.com/azure/static-web-apps/ \ No newline at end of file diff --git a/infra/README.md b/infra/README.md new file mode 100644 index 0000000..e35469a --- /dev/null +++ b/infra/README.md @@ -0,0 +1,96 @@ +# Azure Infrastructure + +This directory contains Bicep templates for deploying the DocFX wiki to Azure Static Web Apps. + +## Prerequisites + +- Azure CLI installed and authenticated +- Azure subscription +- Appropriate permissions to create resources + +## Quick Deployment + +### 1. Edit Parameters + +Edit `bicep.parameters.json` to customize: +- `staticWebAppName`: Globally unique name for your Static Web App +- `location`: Azure region (default: eastus2) +- `sku`: Free or Standard tier +- `tags`: Resource tags for organization + +### 2. Create Resource Group + +```bash +az group create --name rg-docfx-wiki --location eastus2 +``` + +### 3. Deploy Bicep Template + +```bash +az deployment group create \ + --resource-group rg-docfx-wiki \ + --template-file main.bicep \ + --parameters bicep.parameters.json +``` + +### 4. Get Deployment Token + +After deployment, retrieve the deployment token: + +```bash +az deployment group show \ + --resource-group rg-docfx-wiki \ + --name main \ + --query properties.outputs.deploymentToken.value \ + --output tsv +``` + +### 5. Add Secret to GitHub + +Add the deployment token as a repository secret: + +1. Go to your GitHub repository +2. Navigate to Settings → Secrets and variables → Actions +3. Click "New repository secret" +4. Name: `AZURE_STATIC_WEB_APPS_API_TOKEN` +5. Value: Paste the deployment token from step 4 +6. Click "Add secret" + +## Available Outputs + +The deployment provides these outputs: + +- `deploymentToken`: Use this for CI/CD authentication +- `defaultHostname`: Your site's URL +- `resourceId`: Azure resource ID for the Static Web App + +## One-Line Deployment Script + +For convenience, you can use this one-liner to deploy and capture outputs: + +```bash +az deployment group create \ + --resource-group rg-docfx-wiki \ + --template-file main.bicep \ + --parameters bicep.parameters.json \ + --query properties.outputs +``` + +## Updating the Deployment + +To update the deployment with new parameters: + +```bash +az deployment group create \ + --resource-group rg-docfx-wiki \ + --template-file main.bicep \ + --parameters bicep.parameters.json +``` + +## Cleanup + +To delete all resources: + +```bash +az group delete --name rg-docfx-wiki --yes --no-wait +``` diff --git a/infra/bicep.parameters.json b/infra/bicep.parameters.json new file mode 100644 index 0000000..78a3623 --- /dev/null +++ b/infra/bicep.parameters.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "staticWebAppName": { + "value": "docfx-wiki-swa" + }, + "location": { + "value": "eastus2" + }, + "sku": { + "value": "Free" + }, + "tags": { + "value": { + "Environment": "Production", + "Project": "DocFX Wiki" + } + } + } +} diff --git a/infra/main.bicep b/infra/main.bicep new file mode 100644 index 0000000..4f8514d --- /dev/null +++ b/infra/main.bicep @@ -0,0 +1,43 @@ +@description('Name of the Azure Static Web App') +param staticWebAppName string + +@description('Location for the Static Web App') +param location string = resourceGroup().location + +@description('SKU for the Static Web App') +@allowed([ + 'Free' + 'Standard' +]) +param sku string = 'Free' + +@description('Tags to apply to resources') +param tags object = {} + +resource staticWebApp 'Microsoft.Web/staticSites@2023-12-01' = { + name: staticWebAppName + location: location + tags: tags + sku: { + name: sku + tier: sku + } + properties: { + repositoryUrl: '' + branch: '' + buildProperties: { + appLocation: '/' + apiLocation: '' + outputLocation: 'docs/_site' + } + } +} + +@description('Deployment token for CI/CD') +output deploymentToken string = staticWebApp.listSecrets().properties.apiKey + +@description('Static Web App default hostname') +output defaultHostname string = staticWebApp.properties.defaultHostname + +@description('Static Web App resource ID') +output resourceId string = staticWebApp.id diff --git a/infra/main.json b/infra/main.json new file mode 100644 index 0000000..72e9623 --- /dev/null +++ b/infra/main.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.39.26.7824", + "templateHash": "8825082805785648693" + } + }, + "parameters": { + "staticWebAppName": { + "type": "string", + "metadata": { + "description": "Name of the Azure Static Web App" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for the Static Web App" + } + }, + "sku": { + "type": "string", + "defaultValue": "Free", + "allowedValues": [ + "Free", + "Standard" + ], + "metadata": { + "description": "SKU for the Static Web App" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags to apply to resources" + } + } + }, + "resources": [ + { + "type": "Microsoft.Web/staticSites", + "apiVersion": "2023-12-01", + "name": "[parameters('staticWebAppName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]", + "tier": "[parameters('sku')]" + }, + "properties": { + "repositoryUrl": "", + "branch": "", + "buildProperties": { + "appLocation": "/", + "apiLocation": "", + "outputLocation": "docs/_site" + } + } + } + ], + "outputs": { + "deploymentToken": { + "type": "string", + "metadata": { + "description": "Deployment token for CI/CD" + }, + "value": "[listSecrets(resourceId('Microsoft.Web/staticSites', parameters('staticWebAppName')), '2023-12-01').properties.apiKey]" + }, + "defaultHostname": { + "type": "string", + "metadata": { + "description": "Static Web App default hostname" + }, + "value": "[reference(resourceId('Microsoft.Web/staticSites', parameters('staticWebAppName')), '2023-12-01').defaultHostname]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Static Web App resource ID" + }, + "value": "[resourceId('Microsoft.Web/staticSites', parameters('staticWebAppName'))]" + } + } +} \ No newline at end of file diff --git a/staticwebapp.config.json b/staticwebapp.config.json new file mode 100644 index 0000000..16e2895 --- /dev/null +++ b/staticwebapp.config.json @@ -0,0 +1,29 @@ +{ + "navigationFallback": { + "rewrite": "/index.html", + "exclude": [ + "/images/*", + "/content/*", + "/styles/*", + "/fonts/*", + "/*.{css,scss,js,png,jpg,jpeg,gif,svg,ico,woff,woff2,ttf,eot}" + ] + }, + "routes": [], + "globalHeaders": { + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + "Expires": "0" + }, + "mimeTypes": { + ".json": "application/json", + ".yml": "text/yaml", + ".yaml": "text/yaml" + }, + "responseOverrides": { + "404": { + "rewrite": "/404.html", + "statusCode": 404 + } + } +} From b252de7bdd5dad756666d39241a1b984914a0ced Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:45:02 +0000 Subject: [PATCH 03/17] Add deployment quickstart guide and automated deployment script Co-authored-by: robpitcher <13648061+robpitcher@users.noreply.github.com> --- DEPLOYMENT.md | 216 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 48 ++++------- deploy-azure.sh | 166 +++++++++++++++++++++++++++++++++++++ 3 files changed, 398 insertions(+), 32 deletions(-) create mode 100644 DEPLOYMENT.md create mode 100755 deploy-azure.sh diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..1dda69c --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,216 @@ +# Azure Static Web Apps Deployment - Quick Start + +This guide walks you through deploying your DocFX wiki to Azure Static Web Apps in minutes. + +## Prerequisites + +- Azure subscription ([free trial available](https://azure.microsoft.com/free/)) +- Azure CLI installed ([install guide](https://learn.microsoft.com/cli/azure/install-azure-cli)) +- GitHub repository access (for setting secrets) + +## Step-by-Step Deployment + +### 1. Clone and Customize + +```bash +# Clone the repository +git clone https://github.com/YOUR-USERNAME/docfx-wiki.git +cd docfx-wiki + +# Edit the parameters file +cd infra +nano bicep.parameters.json # or use your preferred editor +``` + +**Edit these values in `bicep.parameters.json`:** + +```json +{ + "parameters": { + "staticWebAppName": { + "value": "my-unique-wiki-name" // ⚠️ Must be globally unique + }, + "location": { + "value": "eastus2" // Choose your preferred region + }, + "sku": { + "value": "Free" // Free or Standard + } + } +} +``` + +### 2. Login to Azure + +```bash +az login +``` + +### 3. Deploy Infrastructure (One Command!) + +```bash +# Create resource group and deploy in one go +RESOURCE_GROUP="rg-docfx-wiki" +LOCATION="eastus2" + +az group create --name $RESOURCE_GROUP --location $LOCATION && \ +az deployment group create \ + --resource-group $RESOURCE_GROUP \ + --template-file main.bicep \ + --parameters bicep.parameters.json +``` + +✅ **Success!** Your Azure Static Web App is now created. + +### 4. Get Your Deployment Token + +```bash +DEPLOYMENT_TOKEN=$(az deployment group show \ + --resource-group $RESOURCE_GROUP \ + --name main \ + --query properties.outputs.deploymentToken.value \ + --output tsv) + +echo "Your deployment token: $DEPLOYMENT_TOKEN" +``` + +⚠️ **Important:** Copy this token - you'll need it in the next step. + +### 5. Add Secret to GitHub + +1. Go to your GitHub repository +2. Click **Settings** → **Secrets and variables** → **Actions** +3. Click **New repository secret** +4. Set: + - **Name:** `AZURE_STATIC_WEB_APPS_API_TOKEN` + - **Value:** Paste the deployment token from step 4 +5. Click **Add secret** + +### 6. Trigger Deployment + +Option A: Push to main branch +```bash +git add . +git commit -m "Update documentation" +git push origin main +``` + +Option B: Manual trigger +1. Go to **Actions** tab in GitHub +2. Select **Deploy to Azure Static Web Apps** +3. Click **Run workflow** + +### 7. View Your Site + +Get your site URL: +```bash +az staticwebapp show \ + --name $(jq -r '.parameters.staticWebAppName.value' bicep.parameters.json) \ + --resource-group $RESOURCE_GROUP \ + --query defaultHostname \ + --output tsv +``` + +Or find it in the Azure Portal: +- Go to [portal.azure.com](https://portal.azure.com) +- Navigate to your resource group +- Click on your Static Web App +- Copy the URL from the Overview page + +## What Happens Next? + +✨ **Automatic Deployments:** Every time you push changes to the `docs/**` folder on the `main` branch, GitHub Actions will: +1. Build your DocFX site +2. Deploy it to Azure Static Web Apps +3. Make it available at your custom URL + +## Troubleshooting + +### "Static Web App name already exists" +- Change `staticWebAppName` in `bicep.parameters.json` to a unique value +- Re-run the deployment command + +### "Deployment token not working" +- Regenerate the token: + ```bash + az staticwebapp secrets list \ + --name YOUR_APP_NAME \ + --resource-group $RESOURCE_GROUP + ``` +- Update the GitHub secret with the new token + +### "Workflow not triggering" +- Check that the workflow file exists: `.github/workflows/azure-swa-deploy.yml` +- Verify the secret name is exactly: `AZURE_STATIC_WEB_APPS_API_TOKEN` +- Check workflow runs under the **Actions** tab + +### "Site shows 404 or not found" +- Wait 2-3 minutes after first deployment +- Clear your browser cache +- Check the deployment status in GitHub Actions + +## Advanced Configuration + +### Custom Domain +```bash +az staticwebapp hostname set \ + --name YOUR_APP_NAME \ + --resource-group $RESOURCE_GROUP \ + --hostname www.yourdomain.com +``` + +### View Deployment Logs +- Go to GitHub repository → **Actions** tab +- Click on the latest workflow run +- Expand the deployment steps to see logs + +### Upgrade to Standard Tier +Edit `bicep.parameters.json`: +```json +"sku": { + "value": "Standard" +} +``` + +Then redeploy: +```bash +az deployment group create \ + --resource-group $RESOURCE_GROUP \ + --template-file main.bicep \ + --parameters bicep.parameters.json +``` + +## Cost Estimate + +- **Free Tier:** $0/month + - 100 GB bandwidth/month + - 0.5 GB storage + - Custom domains with free SSL + +- **Standard Tier:** ~$9/month + - Unlimited bandwidth + - 10 GB storage + - Additional enterprise features + +[Full pricing details](https://azure.microsoft.com/pricing/details/app-service/static/) + +## Cleanup + +To delete all resources and stop charges: + +```bash +az group delete --name $RESOURCE_GROUP --yes --no-wait +``` + +## Next Steps + +- 📝 [Edit your wiki content](../docs/content/) +- 🎨 [Customize DocFX templates](../docs/docfx.json) +- 🔧 [Configure routing](../staticwebapp.config.json) +- 📊 [Monitor with Application Insights](https://learn.microsoft.com/azure/static-web-apps/monitor) + +## Support + +- 📖 [Azure Static Web Apps Documentation](https://learn.microsoft.com/azure/static-web-apps/) +- 💬 [GitHub Discussions](https://github.com/YOUR-USERNAME/docfx-wiki/discussions) +- 🐛 [Report Issues](https://github.com/YOUR-USERNAME/docfx-wiki/issues) diff --git a/README.md b/README.md index d8ecd22..5a93b1e 100644 --- a/README.md +++ b/README.md @@ -72,37 +72,21 @@ Workflow: `.github/workflows/publish-site.yml` Deploy to Azure Static Web Apps for a production-ready hosting solution with global CDN, custom domains, and automatic SSL certificates. -#### Quick Setup - -1. **Deploy Infrastructure** - ```bash - # Edit parameters in infra/bicep.parameters.json - # Then deploy: - cd infra - az group create --name rg-docfx-wiki --location eastus2 - az deployment group create \ - --resource-group rg-docfx-wiki \ - --template-file main.bicep \ - --parameters bicep.parameters.json - ``` - -2. **Get Deployment Token** - ```bash - az deployment group show \ - --resource-group rg-docfx-wiki \ - --name main \ - --query properties.outputs.deploymentToken.value \ - --output tsv - ``` - -3. **Add GitHub Secret** - - Go to repository Settings → Secrets and variables → Actions - - Create new secret: `AZURE_STATIC_WEB_APPS_API_TOKEN` - - Paste the deployment token - -4. **Enable Workflow** - - The workflow `.github/workflows/azure-swa-deploy.yml` will automatically deploy on pushes to `main` when documentation changes. +#### Quick Deploy (Automated Script) + +```bash +./deploy-azure.sh +``` + +This script will: +- Create the Azure resource group +- Deploy the Static Web App using Bicep +- Retrieve the deployment token +- Display next steps for GitHub setup + +#### Manual Deployment + +See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed step-by-step instructions. #### Customization @@ -117,7 +101,7 @@ Deploy to Azure Static Web Apps for a production-ready hosting solution with glo - Custom routes - Headers and MIME types -See [infra/README.md](infra/README.md) for detailed deployment instructions. +See [infra/README.md](infra/README.md) for infrastructure details. ## Learn more diff --git a/deploy-azure.sh b/deploy-azure.sh new file mode 100755 index 0000000..3fd4bb8 --- /dev/null +++ b/deploy-azure.sh @@ -0,0 +1,166 @@ +#!/bin/bash + +# Azure Static Web Apps Deployment Script +# This script automates the deployment of the DocFX wiki to Azure Static Web Apps + +set -e # Exit on any error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print colored messages +print_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +print_step() { + echo -e "\n${GREEN}==>${NC} $1" +} + +# Check prerequisites +print_step "Checking prerequisites..." + +if ! command -v az &> /dev/null; then + print_error "Azure CLI is not installed. Please install it from: https://learn.microsoft.com/cli/azure/install-azure-cli" + exit 1 +fi + +if ! command -v jq &> /dev/null; then + print_warning "jq is not installed. Some features may not work. Install with: apt-get install jq" +fi + +# Check if logged in to Azure +print_step "Checking Azure login status..." +if ! az account show &> /dev/null; then + print_info "Not logged in to Azure. Logging in..." + az login +else + ACCOUNT=$(az account show --query name -o tsv) + print_info "Already logged in to Azure account: $ACCOUNT" +fi + +# Change to infra directory +cd "$(dirname "$0")/infra" + +# Read parameters from bicep.parameters.json +if [ -f "bicep.parameters.json" ]; then + if command -v jq &> /dev/null; then + APP_NAME=$(jq -r '.parameters.staticWebAppName.value' bicep.parameters.json) + LOCATION=$(jq -r '.parameters.location.value' bicep.parameters.json) + print_info "Using Static Web App name: $APP_NAME" + print_info "Using location: $LOCATION" + else + print_warning "Cannot read parameters without jq. Using defaults." + APP_NAME="docfx-wiki-swa" + LOCATION="eastus2" + fi +else + print_error "bicep.parameters.json not found!" + exit 1 +fi + +# Set resource group name +RESOURCE_GROUP="rg-docfx-wiki" + +# Confirm deployment +print_step "Deployment Summary" +echo "Resource Group: $RESOURCE_GROUP" +echo "Static Web App Name: $APP_NAME" +echo "Location: $LOCATION" +echo "" +read -p "Do you want to proceed with deployment? (y/n) " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_info "Deployment cancelled." + exit 0 +fi + +# Create resource group +print_step "Creating resource group..." +if az group create --name "$RESOURCE_GROUP" --location "$LOCATION" --output none; then + print_info "Resource group created or already exists: $RESOURCE_GROUP" +else + print_error "Failed to create resource group" + exit 1 +fi + +# Deploy Bicep template +print_step "Deploying Azure Static Web App..." +if az deployment group create \ + --resource-group "$RESOURCE_GROUP" \ + --template-file main.bicep \ + --parameters bicep.parameters.json \ + --output none; then + print_info "Deployment successful!" +else + print_error "Deployment failed" + exit 1 +fi + +# Get deployment token +print_step "Retrieving deployment token..." +DEPLOYMENT_TOKEN=$(az deployment group show \ + --resource-group "$RESOURCE_GROUP" \ + --name main \ + --query properties.outputs.deploymentToken.value \ + --output tsv) + +if [ -z "$DEPLOYMENT_TOKEN" ]; then + print_error "Failed to retrieve deployment token" + exit 1 +fi + +# Get site URL +SITE_URL=$(az deployment group show \ + --resource-group "$RESOURCE_GROUP" \ + --name main \ + --query properties.outputs.defaultHostname.value \ + --output tsv) + +# Print success message +print_step "Deployment Complete! 🎉" +echo "" +echo "┌────────────────────────────────────────────────────────────────┐" +echo "│ DEPLOYMENT SUCCESSFUL │" +echo "└────────────────────────────────────────────────────────────────┘" +echo "" +echo "Your site will be available at: https://$SITE_URL" +echo "" +echo "Next steps:" +echo "" +echo "1. Add the deployment token to GitHub:" +echo " - Go to: https://github.com/YOUR-USERNAME/YOUR-REPO/settings/secrets/actions" +echo " - Create new secret: AZURE_STATIC_WEB_APPS_API_TOKEN" +echo " - Value (copy this): " +echo "" +echo " $DEPLOYMENT_TOKEN" +echo "" +echo "2. Push changes to trigger deployment:" +echo " git add ." +echo " git commit -m 'Update documentation'" +echo " git push origin main" +echo "" +echo "3. Monitor deployment:" +echo " - GitHub Actions: https://github.com/YOUR-USERNAME/YOUR-REPO/actions" +echo " - Azure Portal: https://portal.azure.com" +echo "" + +# Optionally copy token to clipboard if available +if command -v xclip &> /dev/null; then + echo "$DEPLOYMENT_TOKEN" | xclip -selection clipboard + print_info "Deployment token copied to clipboard!" +elif command -v pbcopy &> /dev/null; then + echo "$DEPLOYMENT_TOKEN" | pbcopy + print_info "Deployment token copied to clipboard!" +fi From c7b7b2eb99ddf9375bc58ff9ab98dacb3f80db0e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:46:02 +0000 Subject: [PATCH 04/17] Remove build artifact and update gitignore for Bicep outputs Co-authored-by: robpitcher <13648061+robpitcher@users.noreply.github.com> --- .gitignore | 6 +++- infra/main.json | 89 ------------------------------------------------- 2 files changed, 5 insertions(+), 90 deletions(-) delete mode 100644 infra/main.json diff --git a/.gitignore b/.gitignore index 596ccb1..87d7fe4 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,8 @@ _site/ # Azure Static Web Apps .azure/ -swa-cli.config.json \ No newline at end of file +swa-cli.config.json + +# Bicep build artifacts +infra/*.json +!infra/bicep.parameters.json \ No newline at end of file diff --git a/infra/main.json b/infra/main.json deleted file mode 100644 index 72e9623..0000000 --- a/infra/main.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "8825082805785648693" - } - }, - "parameters": { - "staticWebAppName": { - "type": "string", - "metadata": { - "description": "Name of the Azure Static Web App" - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Location for the Static Web App" - } - }, - "sku": { - "type": "string", - "defaultValue": "Free", - "allowedValues": [ - "Free", - "Standard" - ], - "metadata": { - "description": "SKU for the Static Web App" - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "Tags to apply to resources" - } - } - }, - "resources": [ - { - "type": "Microsoft.Web/staticSites", - "apiVersion": "2023-12-01", - "name": "[parameters('staticWebAppName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "[parameters('sku')]", - "tier": "[parameters('sku')]" - }, - "properties": { - "repositoryUrl": "", - "branch": "", - "buildProperties": { - "appLocation": "/", - "apiLocation": "", - "outputLocation": "docs/_site" - } - } - } - ], - "outputs": { - "deploymentToken": { - "type": "string", - "metadata": { - "description": "Deployment token for CI/CD" - }, - "value": "[listSecrets(resourceId('Microsoft.Web/staticSites', parameters('staticWebAppName')), '2023-12-01').properties.apiKey]" - }, - "defaultHostname": { - "type": "string", - "metadata": { - "description": "Static Web App default hostname" - }, - "value": "[reference(resourceId('Microsoft.Web/staticSites', parameters('staticWebAppName')), '2023-12-01').defaultHostname]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Static Web App resource ID" - }, - "value": "[resourceId('Microsoft.Web/staticSites', parameters('staticWebAppName'))]" - } - } -} \ No newline at end of file From 27562d524f6fc334b0dcd21bac26029f1da68b2d Mon Sep 17 00:00:00 2001 From: Rob Pitcher <13648061+robpitcher@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:51:50 -0500 Subject: [PATCH 05/17] Update staticwebapp.config.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- staticwebapp.config.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/staticwebapp.config.json b/staticwebapp.config.json index 16e2895..c753a6d 100644 --- a/staticwebapp.config.json +++ b/staticwebapp.config.json @@ -11,9 +11,7 @@ }, "routes": [], "globalHeaders": { - "Cache-Control": "no-cache, no-store, must-revalidate", - "Pragma": "no-cache", - "Expires": "0" + "Cache-Control": "public, max-age=600, must-revalidate" }, "mimeTypes": { ".json": "application/json", From 28e6682f3611864ff2c45379de243adb69cee65d Mon Sep 17 00:00:00 2001 From: Rob Pitcher <13648061+robpitcher@users.noreply.github.com> Date: Tue, 27 Jan 2026 17:04:06 -0500 Subject: [PATCH 06/17] Update staticwebapp.config.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- staticwebapp.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staticwebapp.config.json b/staticwebapp.config.json index c753a6d..3620e0a 100644 --- a/staticwebapp.config.json +++ b/staticwebapp.config.json @@ -20,7 +20,7 @@ }, "responseOverrides": { "404": { - "rewrite": "/404.html", + "rewrite": "/index.html", "statusCode": 404 } } From 06d43efe2dee17065798cd3d07e3c30a92a6a79d Mon Sep 17 00:00:00 2001 From: Rob Pitcher <13648061+robpitcher@users.noreply.github.com> Date: Tue, 27 Jan 2026 20:08:39 -0500 Subject: [PATCH 07/17] logo --- docs/content/media/sla.jpg | Bin 0 -> 17076 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/content/media/sla.jpg diff --git a/docs/content/media/sla.jpg b/docs/content/media/sla.jpg new file mode 100644 index 0000000000000000000000000000000000000000..84d284196f6d13fd49c57e1a5f37e6704a6dbd89 GIT binary patch literal 17076 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 B4>|w< literal 0 HcmV?d00001 From 1288cc4df71424908913426981ae8fe40f60dfd4 Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 00:51:51 +0000 Subject: [PATCH 08/17] Update introduction.md to enhance warning about adventure racing intensity --- docs/content/introduction.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/introduction.md b/docs/content/introduction.md index e329fe8..317e896 100644 --- a/docs/content/introduction.md +++ b/docs/content/introduction.md @@ -6,8 +6,8 @@ Adventure racing is a thrilling multidisciplinary endurance sport that combines Unlike traditional running or biking races where you follow a marked route, adventure racing requires you to plan your own path. Teams make strategic decisions about which checkpoints to visit and how to connect them, navigating through forests, across rivers, and over mountains while managing limited time and energy. -> [!NOTE] -> Most races allow only map-and-compass navigation, so practicing those skills early makes every other discipline smoother. +> [!WARNING] +> Adventure racing can be intense fun.
From 6f2e6555fe9b470782fbad4d0282ae3230f4c072 Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 01:01:00 +0000 Subject: [PATCH 09/17] Update race-formats.md to add headings for race categories and improve formatting --- docs/content/race-formats.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/content/race-formats.md b/docs/content/race-formats.md index aa93042..e602b78 100644 --- a/docs/content/race-formats.md +++ b/docs/content/race-formats.md @@ -8,6 +8,7 @@ Adventure races are categorized primarily by time, not distance. This page summa Here's what to expect: +# [Sprint](#tab/Sprint) ### Sprint Races (2-8 hours) - **Ideal for**: First-timers and those new to adventure racing - **Format**: Usually completed during daylight hours @@ -16,6 +17,7 @@ Here's what to expect: - **Gear**: Minimal mandatory gear list - **What to expect**: A fun, manageable introduction to the sport with forgiving navigation and moderate physical demands +# [12-Hour](#tab/12-Hour) ### 12-Hour Races - **Ideal for**: Those ready for a bigger challenge after sprint races - **Format**: May extend into nighttime @@ -23,6 +25,7 @@ Here's what to expect: - **Gear**: More comprehensive gear list, including headlamps - **What to expect**: Sustained physical effort, night navigation skills, and better time management needed +# [24-Hour](#tab/24-Hour) ### 24-Hour Races - **Ideal for**: Experienced racers comfortable with extended endurance - **Format**: Racing through both day and night @@ -30,12 +33,13 @@ Here's what to expect: - **Gear**: Full equipment kit for all conditions - **What to expect**: Sleep management decisions, cold-weather preparedness, and advanced navigation strategies +# [Expedition](#tab/expedition) ### Expedition Races (Multi-day) - **Ideal for**: Elite-level teams - **Format**: Can last several days and cover hundreds of miles - **What to expect**: Ultimate test of endurance, navigation, and team dynamics -
+--- ## Checkpoint Navigation Styles From f10c7155250e8bfdd8e2877ae79c9a3b154b70f6 Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 01:14:52 +0000 Subject: [PATCH 10/17] logo --- docs/docfx.json | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docfx.json b/docs/docfx.json index c3a9584..3ac4f99 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -31,6 +31,7 @@ "globalMetadata": { "_appName": "Scrambled Legs & Achin'", "_appTitle": "Scrambled Legs & Achin'", + "_appLogoPath": "content/media/ar-intro.png", "_enableSearch": true, "pdf": false } From 2fd793b6f6b25d3740fb6d4f7f93853a7634e669 Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 01:16:49 +0000 Subject: [PATCH 11/17] Remove unused logo path from global metadata in docfx.json --- docs/content/media/sla.jpg | Bin 17076 -> 0 bytes docs/docfx.json | 1 - 2 files changed, 1 deletion(-) delete mode 100644 docs/content/media/sla.jpg diff --git a/docs/content/media/sla.jpg b/docs/content/media/sla.jpg deleted file mode 100644 index 84d284196f6d13fd49c57e1a5f37e6704a6dbd89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17076 zcmYJa1ymft5-z;B%ifB)A0?E=tcC1oT55D)+W#ODF{w+RRZKtV#Hz@wm`qQWC0!lNLfpdq4u zvj3ss5m8a#;Su3cKUowcl+Rv>D4zxOKR0+Z)c-OlC=gK4(6BJDh_JATXsAf2Xz1ww zJJC_mKgmz}znA|J=qPAtX#c5b=xC__yP=@{>jwbg0R?~pCKToAC?#e$MlO+n&c)m+_@If4rZ28XV2DLKs=Kcx^td`f}(e=0sz!9c@)N`m`r z6a{|Dgoc7b`edOYJ~jMD1ppdIhQTTdqhbP!NzUdHlvLO_aJ8_9MInZ*YU&zXbd8hT zGzenna0@B^w*f%@lnn`l0s@2q^EEDV5zZaM=#2x=UPS0(yTQBz&`j_Yznw+D;M9cT zTO#&(kx4xYc8o(G!kAK2J)GE>3Trqj>7PfvPWR1culm>V+TI_ zj-~}Z{Gu3F@kQ`Ds7?I-{N*}%Dum+){1{K(!6+5BeH5%lTE;~v%f~_dS75G__A6AZ zq*>2@bmKy!H;x*Ijgp4Nv;WteIlN%>&&T+|xOo3*+?*B|!(ZpEZk?ot2SUn3@iTg_bgycRPt)O~6&+KFs#VTS^l{WE&sa0FmyL&~ zr`^l(75u7U8FK6#DVHNnMvF~-*xo-dE9B4_p*iM50$WxVUv-3^vn##k{9*YIiF8uE;hz z?|*3QKwHaMzrxzW%ia82@3@S}3&lnyaIp)FYLJ#7Aq;OftA|F5l%VFGW#kjCDF!i4 z*Z(-y@%D7oTXsVmMf)?OqLr~&l7ODo zLZ~Z4$UhX9%lyn3f_*WuM8*%pfxmNpp+#)a&z%{R(`U2Bo$&P3)I$&O?mnI?RvSlN zg8hZ`cc&2SI7P?Y$j@=eFO%Yu>4MTz{W;vMDiGUjg}lVvXAo0slw+SfV}}X!+o!Mf z<1E65HR(GhWeirNfBAckmsE~5;*aw}TZP%m$P|SkD?$TRP<0Npr$0w(aWK68{~fnv z&Z0zf(CGZ%N^fhm(#Pb{VwYtP=|=~?l7(y9xjvpy@rMB>s2wUW*%(f~N$|Uw^qm0n z{23oc?QeH@Yy}obv(MZ9KxGywn*}piISqj)GV*)BSU%PrT_@CdNArso=N zB7C`1z-o6MH+oL53C6sr)Gd(AXnwWh6NGf)P#iPsVzkNMq>1|hxPsk#l7Km5IPAx= zT<^O%&wSv?jW3g$RY7_(2COabW5tVRHU#cJ0M$3qUUMjF*`3$%T>b&IQ<7#o>jV9U!|r8q8_)-tB(70Xs8fgVRWiqZ2u#5AB+fAVZi9FB5{n^JnEs4$VP0h)ou>ttgF* z1XZqBO2UO(9h0Fs%{#t?cJ&I46;Ba{|6Yd9gTg~yq(d?Y#Sog^%;Qs z&F&b#GLY_*g3kRS`4H8}U@U9{l#d7`tV!+0jv3B7Ayhc%W{v%!$EEoG zo)b}@>O(U8dr^Ccm4}r#Q_+OD3;P>X+9(Z;0M^YGR}FORjBXG<1~AJW`5&Npbcpc# zdkt3raW(1elKT#hU|}0!O0~@i566P=v}BhF?rbSYIrA-K&%uGwbHKoCobcC6Sq5~{ zQs&DVq>RSw(gb`Jze{9+{H!2{sK-|@St4Rg-w*ikl-Fj2R@fsb~9ww$Jq@NGVkC!VIGF;G*5{OlfS`q zX{wnKmd0_CdHbbLP9OeHIz;>Ib3X|+m%!%tE6T!90r;lWzs6YZ%a0(2d|4>kq2<$a zd~~$4R0tnQ7LqQ}@w^uf^Brh7DrPJL0U4uIb#R?|SO8_;du14WhZ8(}do_0-oS99O znZ%7zT-0oOPYg%YOc_va6jh+nF*B+cNelFdg*MMgUT70hO;QJGNkQD?q?8{~$m!m% z^aYw?$1VOxf2s0R(#@VM`4LMCSX_46appDpcqyapkn-`ze9UU>;%|eeRq(-@5U>Rl zBo$k}FfD~L#sEyfbODRSzW!2$(h>La^C5}j)NpcGG>z`_ruZm|BKGjOcrd)wf4&DP z9@6M9v&>mxhgBA>RTc)cmBJa+&S87cE2UG(y5{f+(ueBlNQU2zlsYGjv= zU=jkJkbU2VdTRdxPCW6DKU8LYMqN7tya0rLnF@SB{=n_C9#+4U!nRI6%E5G4vJ@F< zI&a4+L8cI7U{9W$GLO%7%w%)E&r2_DQ*V~A7V2X>MBK_p&_?K38sMD%$qBABV4QG} zz!kY3kG_f02&7Y%j349gf0&gfd83g-qN6Dt^-&we4>K(zNOlEaC^^R#E%N?!Sx)*R z|8SDoG|QkzXK7_6hZV(*wV$f4 z5Xgu0D+XPkL2XwneZ#4@pT^%4`9T$O$4bCZ zDSVH-N51a~2j&bkwWVm|A=h+-czJ~czLr+vV^yaGNfF)igFlY zX-oh*n_D~ls73aXZKs_fxvn2idjlTBh%q>NPNdI41uX}7}lHKLX_4(^(Cq-oce{oY+- z_r&$02N=?ck8u=9;*?cS{{U*OA#CvrA)O*3VoO-b$)eL%B;Ac|6F#v*wn`dOhH&u* zb&_|1eZ!8b_EGx`>F5zu)_1qv_lKL&ivhIH4KMB z!_1(Wsq$MmZR90x@MA<~>o_FG)c9pK*|Z1OOaZ{a z4WF?+p`mlk2h`d3Vh#i606lkV*Z7c+u@3W!Ucb&KK2DpPT>|t(t8tLxSdWJk80D$- zk(>n=Fj+&_c+#zB9RRE%{V?t78|2h{`IFDd`DZ?vg!@}B_}NFgFkj2&H^t$SM$+l1 zFT$b1RGY9p-H{ePEfU1=5opT~8>AQCc!)YpRxJKO9(Mc%^f zPq2_ngA_eKHwQ0VB5u;dbL|^M95!)`1C^7u1-{IN&ap{BjfJv8 zDbpExiV=q2xV?hB(18LS%3ifzah?!FG;(zIaI8r@KmGWQ#K-Z@D&21-;8$m%(dYsk ztPW3L>>zSvY$Ee%gD_5}&)bF@6NU@*7&+h?$t6XaaF{M!Wtd~F|Ei9CpFyTe&tfD( zJbjU3crtdkDX#{}M-kO6Qw@>t)JR*~OBp+0oXY?$=%nK!)e;;r6l)c+x>9rl;pY&x z#M9h&+(?QiJlOE{y8;nq=qegj6-%Nq#mzcQ(3ht?50iE(I;!3abwswsf}S$)hhp2l zEU`$k#$Nj~QX(A9$$LajbvM)NlGr~|ZGW2^ri!JT3G=7`Osi2EsB1}^OOUtCpUAjJ zl_baaYJvH)2IB*`b{xouAQ|}hj3Ej zRtwrym59Wum!K^bvqgcR)KNahsi%L^BPn&wl>G!Agee zlo)5=(PDTlU_8dMhJgGBsJPlY#6eYV1Pv0VrQ1N*l6?)04n?ucteo;j49=OYk5a0d zgN;x$cG$=axHjkiiHbFJ_nSC1uCtNO5d|F0Gnr(J%9&`*mBO~$fU_T>LXN>UfBEY373eLnddU>{!-(%)mz6Rq`AgT;eCY`|I8_whF_~TT% z5=nap6wvV`q=qUjgWBA=g5Acy=lpShnn7y5tB#;b1)CMar#z(|eZRNqW0O2jB|MaL z*NA1SEe4fV=h+*n)Y~hjWC4tg+=ad+UisO$(GM5c+$I|%J(>79`s}{ik4;CwT{$Ut z#4zfxo)c17XSFrdEZfXIg-4$+=v&*$a1eFk4kPdysRW;iWWPpcbKsv2kQv3+;L>rU zLpS^mY+^7xEg{se4z@|&cp2Ii8oMMhYj690_FV8+{3cxnV4hO-{ftK)Vj-?Xb*mK-Jn5owFms3NLodZ2%fxxMgmXz1*g#Nr=J7 z4bylpy+y9;FT{iY!ivy#R%S>dgca!uH!HEADHb>r2skFYSr6wqfH(9q=BncsXhz3| zb!Ja)O!$c#hUw&Xk!F*4_IgY*(^#TLMwPy7CNIYyM_1%gBvSSQ0YlZie*!K}kXnS) zTa|W-$zxe=IUax7RFn3idfHsXZfA5R*FxPRP3dL}MD=(jHRsek^?chQv1w_GF8iUH zGky3*s*SP?#a>y-9X^NiCR9JAOPN>xnBG{0UH1Tj{dSLkVdau=-L|%DD6Iv`!eMAk z3fdQEqBAwdT#$8=4}>~=5^G0K&n`3H=-!_=L<5vB+<6qMV=pt(=y?L$q8D`m2JI_% zjo1g%8iRhL7WIK_7mXP#89$D-mhn;U#=VZrnsgakgYfgbTw?TwmEbfFIckyTB>vuI zhqeaJ(9+gz)KHAB#4?$Bzrw|p=*RvRx48*RqtR@w*wNn?+d!0-Ze@!tzn)4z^jFnB z6UX`CYNNK#o!l~fOQXXyLWP9rN%;lX6Kry-(7_tl?2@i3PFj)%k^bU%4*AQp?M22j zy1m?9RzUFDRDrPs%qh|Q+95Onobc5pssMzaQ}Mq8E8$9V3iENMZOA)iiiMdnQf6Ca zf54!O{mtZ$SEDTZmFBFTDB8G8eNu=IR2GGf%({&T)s3S(&2HQR<6@MaVn{Cku8+-8 zfN-NjjluPEl9))|cd95Q7RrOL@3B{UUe;;-^uu1`tpYa$7KeEax z2WaBsRYxg`jjUcbmMHPf#BoK{hO~4}d`6CXLZOH4KR-=VM1ym?+WBRJ_8`G&RE8%goE2uGrk{r&6vHbYo8YXRwDx=V)j3~fe`r98A;D^s=RU3xlvfBUAwSAY^bS(b zhY;HeM?qWnI)EEJxl!|CX^FqeI-0_j3icGkJz+Yem}qC1IyJ z;!s6F#s2!|7#f%=8b666&uY})X~mfh@Yjt}3C~%}1IQ(GNr|HP#7}C? zS*QZ^iP%CRvx0=Fx0Z&YqSPf#7=lWPLMo=u+BfW$w^E$Lwq4@(UO|~98_A;syYaFI zO)CsgDNG{LXr>+p*T@8lc+>iCBEjD5L3~IQl#~?Qb67Nq+azpPi zgih-D{Ympi*^##3n9$Q(A_W}(1K=pu!vu8UELBT3)Q0gsy;KRQZcc1nv$S!^vQf)W zZ3CC((-j?()gPBXc*~hY_cqOG$e8%3tl`Qj)~X(9jL-jCXp{$J{(4f+34NLpHiEA zoaF(3lpT71lCz&;LL%EB0eU6KegUxOr)12$3&FDbaZ2V~K4wTJ(yNoSMvQ*vr8db? z3YXit1MC7B=hG>r4s2MekL7x7!`<+_?q?5!O*}##lwMH z(Y#q_qvmd01TO`KH zL-Plhz@yvdA-ZfQ&4z(qru`dzTMT=}epfI!On5`&A_jK?m-FNF2s&m1DT)D(8sMR}Pv2l(< z$$8?-nddccxP9sKgN{>2J5&a|I?vhUBDd{Kq4j8gBU#g*x^$F=dWoX`VBI}-G>b;k zm~Q<^*Tb7Gt<&YQCVYC3HrW8gjrD*9cL^0e>?Flyt}`-<%%{j+lm+zB61ol=2au)P zD5s_Y%cbYP4?kT87iHw+_!`%NR|Uo@6i9X_DT=2wjsQhHiU*bUQe8t5pm341#&N~* zf|RUe3E4n_4L{9*Y`+=}=*gO7AE^Nr*>CYzw+9?KoFfO?i2W@O_|aiinlcLIB*jEA zK%GeFG%J3>t2Ea-6MuU0_Hmc3b4)`CU0OKlF+<(?l1`9|1k{wNCK8rQY$T1 z8m(au;^XMWn8g>f()@KRfs&ud3HbZr*Bb6n=vs}a`BgI5PX7uF=CXEoVil#d(1PSI z#KR}mX@ZnKCE&FD!0PrI6*1>5X~Q^zv*o@zxRv(Qly%|$?maih6F^YnV5Czkht1GM zxLiA|wO0#E^|cf3#v{4~GpMCzx2f~na!1p6kF5HhaA{e*)H%0dHvIkS3x6&Zw=mwpi->@y zDTCHHt0L1BE_IYNlF^{OMa>Fy^js*2*3@PoRF5aXB^}`!r3OyN_R&o`&}}D|oToua zp5)I|N(hpP*Uv~j#oNiM*2N7AJZ0=Jz0@n1(-ZTcQ_42{)+i-lT9!T#Tz`lasPB^D zKMdZJjAKc!lC)y4eD30MIb?1yC!fEmnS=HjnP+GoD3Om|&K%=jg)?R)gQm;0*=9a?M4u}3j?%W}B-zfi zHdIhW?P-=Emc1t{WY}VDlk(jdTUKWY;YIL1rF;^5E~O%>|+ zl29YAtM8z}2@C%NgEL5=%kQ%NG3*?%H)c~qtrp~{N*`MVc~2!a(6|O)JQx&DcS}=* z!dIyS6-eH4QR1UTxl{bt`ltlvdkc7)j_#+)3Jl>LLrM^K)eY-If29q`?8i9fJI{}% zS*~2Esc7G?LfRZaj-ky(saI)=50wtMFc+f)u5vR|-j`6be*oE(e}D`^2p2qFNqldt zEGbi08TpiZel|t_+jQZ2%(l)Gkh4&u({EEauA9C{G>FIWA3$4yhb|k!DR%fpjwrdF zngcQ*0F=_VhDvR}L8VWVjhJ$Njy8lSUV=ui&ha+1b+Z;3tB!1%TPJz5NM#L-7h6`cws%CgQ1G%y zakhH--vS!BZs5wmNwo@wts4qUNyWj#$qYD^JDI(NMU~GmDSN+5n#5$oLenj+=70~$ zxe&BEaPb8}E`~2VeqJdEm9)^#+o)2LiJETpl;RcQAk6+|%`C3TghV1wI#-r~)T$=7 z#PKOYddPyStahx8;AHEstg4U$vSG!nZ?n&AjRnn0dbMVyhgODpc!t(VYWZ?(pT!ff^?m-JEv8-7t!l5U zFO8IL8m7r^`j?1J_>S=@q6G(HbL5JFVntmkKY8wR~;v&m@P#*hb?LJ(&U53MI zdW|N-oCNvl6za`=HKnnECFPuAS0z4-E-TqaevyWJDP>CqLm+mV2j_h}0uzl%uRzh; z^{m57p3K2N!1YRt&rErRLX2UTnAYyte2XguzrR*hV5|ex&(B2su&$C0eIKU&+y2SK zjathl1tn=vzcPV$WaZnph{M8vfK1^osDFU+xHi5nIU>0FZK9hQs2r2;!%~s;Esln9 zLVxDpY) zpRq27w;K4Q1(5Z5aSHVOg@@51UnLF>eN&heYFb+J@ruH+xIv8s)6kj_D#$LmvY^y{ z5$i237>5h3!-;XewPXYXJ2hFq14Pfis3=vf<>aY9=zK+e4>IC#OdC=2%15xWmm{ZZ z9-6N<9LybR9i4mEnjw=a;27g3*S~tJo{^Sxo%})4WAlR7ysd&J``LICDJ<|gL6nL( zkht$K_^&f9Myj_=2NZ2^;x%fzK+8wScuJIx#mBj9Lnd1Gdkc}kiAuM|g;DKZA?TJ?05V=K+lz zs{&Lha)GCORL!QpSc(PGESo$C2x6Tv?8ksGlWT;iE2=Y{o=k&d0#E}ZZfZ1uJR33k z40q3?D{4ZzDfwaG2eI3R?b2Q|ZYhHZF?ZlQmB)}2c{oI@J+fnAxNO5O!NGPtMutLN zyzHLtCElB}gzn$z((Udfiz^nuEon`)AWu$c$_&zV+q5FKkyY=< zqC}0`ZV4Igg35z?CE+=W* zx~+D8SPO%BtePP77}Qg{(;Iiyg zoJ71LC$wd4bGrq-;x->goc8M@=TuJ`wbW#46!2_h1$1-OV_h4Hp>e6|3VK*Z2wvmMG^CEIxg-{Z&2-QQ*QUC8A?Zk3egQR`G< zJ5ehZ>ORa#u`u|yKjB!S6|+>i$1`45^|*$&1dDA?c88{?RVg3&_oiP% z3zHDXK%L227#y-122ar&1V}uXE?HZHg z^3uV@bHZ@-MWC>c5qI9?%dk>ld5@!EijgVlt7P#<8r05<3xXCE?GVia)M#Voa7D}a zkfm0FS6TN9ZAAFZuyAnX6~B+HY)zk732?Iyg)2iQef8$x90ygp1ITf!w+R1{Ecy?S zs&8oc<{l#2N7i&ir+He057`ooZ!;~gK&q@QsR|A^F1Gl`3>u1Im<(&fY6K8~t>zVB5@CJt_5zFQ=B z8V>~r!yh$1sVY@pRvDs+4(;&aa}Hb_$f=c@eP@u!5Nj&(WYg$a&!Ko!mcpX7??`3; z7#CCUL?cjel}YpWIhR+u_Kh;5_W^SF(1;E_xSF7OilfOs2>Z9CX+7WOsN^%j!A%0QM!Z6Z8f$7b)>c~Gsp6u7+Fpp1DJ3rg3)o>K$^#sr`IYd}SZGkhZl8J5YDaf*e|#D9+9((#H3Z>nlB z${-RPXIH3xWzZgNDw9af@yNFHpmHKUN5*U%3G#W&T|{xb^1D(k)(BklKajTlaEO5x z`UhazPd$$t+N=<-obV3cr#%u%s!HEzh9-eq8&c<+X0KZ>l&0R`a|6dphQ_`ys>U)f zgc29&UXRfkvyYf3^PGgq2x6*qynd-HH+LIOv!wBQH~G2BrNp2z_)sG#J^QCHS<9Qg z5gL;SCf3$@*g|B~h&c1OKujz;5S#DREK4_XP@%9P*QLb<(^2Zm^!z;zHOX_?Rn!bw ziAc4;Wa(?iB>5CT9otm&0_$8r%GIProz|FS$2u`wvpO`H+@j{$ex+^-6j!(=X_m~2 zg+5qOXc{hf6Q*4hheNaKRm35hzkU#Gc{ra4Iac`(z`8WWSFF!*19bE@8lTfr=v7sN zXf>SC`@NFIFj}<1$yISfO4CO6r~Buahr?ud{{+oLa!42xRZB-lnO40?jlnm%V|LD& z>U&5AubpLzRp|;&LCk|z0_?VQjLo`uIKSE)=_a&!kp0*n+`e{<2jl+9U(Y6wanHto zTy4_5Kq>EE(OzC8w@9!D;ZJsh*;3}}X^6QEF?S=G#W9cDxQh^1(Ea@d@yhJq&Ciq? z9Fyi`X$cbSUUd2FKZA8hExOLXi$fpii+@`ynX7FxoOJIa!&Pxl`~!q5$;Ez@U_PVb zp0%@d2mUTRViL-`_C2((=cgi(?zXMJtD3x+kP3D}N_i9O3n(n35vtUzK+`K~BHZBT zB}X`2i(o&K{1SAKhOL3gS|BgvQkMAB4F|*RSKYkB35jxqzOM6P%QgC3#kYv1<5;a6 z^*&DjL0*#`aK1C4-O9IN%L&U5d<+i+!7QSO1NX_MlsH=K`9C}tamYg@#wx6iKls}3 zDuYJTkKKvr3l1i6btRZ(GJpmjes|c-xF=TrGfnQ2LvsZ zX?I}n`nP`XYcI8oceXg)>@Y8Y0J?(ey+dFULgw_M{d3sh&s}auF*d>|w#At?VTR_{HI@)5ZLa{N@4S z!KQ+A($;1gXAR_eS}_ohHIh>xJZy0g6r(>s`RB$A8QAW?{$|>wYzCjX*viwq?ZZ$UG<I zc{|Ngs8h`z4FXB0riY31q|4Er7QJ`yZ-I%}00h0UA1&GnyZISY5azuU`dw>(MmS^_ zWwrN&2*pu%FObz0D#j^fGzDAM8i*v{#KBySL+#6k6U5g$Sq}NFb6?6=2MPV8ZnHdF zPMciceXh^7fOal&roR?CLEO5E*`u)#8rp^HP;knqiy@vRgsQd~2>Ur`d1cqdDYmi| zDhIi8DG#B`iBj{2DvW*31tYD9WK8ot&NB4p8%kHjcW|a^&@Mv{rXw!=j{g8Po^PhY zvdp)kemBCA86H7ZJIyk-Hn%5037s@^?IZUN2?*r54Nc_5dwj8kS6*P4NhY|qWlFS! zt_pf}@M9?=2hel(YCB{b+YY6+n`j4gBJZj4Q1FvypD!g^Y}`!aiCw8D`3D#*%^yBEBstW^y2UVoW54_^_m#Mm^{KbyZ*dhWa4kO}3;J=i**Tjd{z?=Q?VR(<>oGi= zq3CD?0l6HCVfhGxsel&`De~+`z6V#C6Q_w6*Srhvh-s$JU}dPPYczg0*K4hlp?*m zg{{J(-}_Lku6v<-uSt~j-Mgc$L}*Qe4mASYF&3#X#Y@3bhUJ(ddr3fY{atTtix1P5 zEqDA@@uSkb>71eg0C^)jlGkk_DI*-}i@MQs>pDJc&jGc;Z~qMU5c3AsSuDv7-1ZMI z?}=(LgbBlY%Kbis2layTi{icMyW8A3?)7*mxa7J12Ccq_>u&&ZkL(Olo|q)D$l4;! zg_NoIE1JyXn0+oPh`lwj)x?G4;K$Q>82ByzTl!S;g&D=&VrV>U3FhGPjxKzhs}Y@_ z9u_JE1d@5&_B1SD{2u^!CtS@tJ3Eu;Km&K z4}gH5C>s}C5T+1%VJwVuaHg~w>AEQ`wCjXIZ(x`9C_Ym?5jHMIXWoMa1H%GlgY}Mo zUqDq0h6k~bR>C2NeMh24-HYg;fc|1O6j=f#7um(@HIrX}E-EapUA_j-8L7D~Ms#S| z-`E4OzE!dr=$Y%>Izi=g@`rdAe2z?z>0!#@Y*l$FWv2|@G*$ZOZIoxcuh?OGQ z;v5@Lz_w=qP9bHM3`!QlCoBz!OpqiIqx?)%o~l%yeU5(61a&25Gd`rpEz1C&f=Eym zu3i;GysPIDG=6xsI2X zGhSfY`S~R$gQ4l%3F9>5l&E0oVggtE%P7V@z~<5O$sy3;-cRY~dWz8WA0UF`L*)lJ zA=MqFqZ-cESJ6oO#&wL<`d7E`$bgsf@;!$ZdK4M<-}}R`WINaObXJN=E6do5Z&<+Y z3ef;=ep^bjD- zO=PB%9kMFh^@HpzQt^zM2?xO9igt>b`ENmB3gp%T7&@qDUK<89`&OHL3 zvE)haAnXv#up~ZQXyEaZ`(c(2G4{T@MUuXSxeQiyG1OW$q zkK0jw>8RrHC&JY|@oPk|SO9_8_u)`Z8zm=IoYAdLC$^pa&+uf85b#3iYRK3D*jyH) z_+`p9FG!c;_c)@=}7Aut%LD?4BgXw+(Cb_iPu^9dx>tR zMpNvRm`x#Aq~rbf@qX?Yq(?!x5)AyQ%z1wH!3($FcbE4uOs)}Uik25K!>>tmJ)DEt z-q|k+K(uXgDQm7jF?XJ^k&PYbiZV*WZkwAzW1D{wMJ7}Cd@`%+E`IfxJkqGOCjW7j zuFe^9&c;Adu!XN|rIr2d&|9D0qYtcG*VIa7oq%wQeSpSMOFHxYZU~yTFKRWl4oum+ zW2v@s?PcH~$dz5%mw^t*VvCfaF`w`;%y(UTES`7C@>Qexk)FW_7TPt*$( zsIx%vf*CQAt1bskB80qewjJr)i=cDYsu;rP zxC^-?@J*P;IU76ULbejn;7VQPsdEriISN?!zDh9tJvzZwiysk2q}{+1*KselOBjAYw`**d6?P%t=}RG}_mWLyOR_ydS2K`ClLKxwFt z_ps$B=&6G=8gAAo-Qq~sSDEfq+0jRFjW7zTR%PdeXiqHVoG2KVG?a8E?R%CM)Q=U4 zpYtvpv4sn~gAy`iN%#)K_*}Dye3?-g;&fc_>1>Eixfoa>r*Kj1Y%-{Dn9Edm;4{Bi zT~EU;7uTr_=jEl6UFv&?!fmzvlIt2tUS{!ZODdd=Kd>?m9b36*W{IA;!K{yau?N#_ z*#dN^sdXxvYsu^>OxWWUl+uvD!F==PcDnjNg=?mS5rnGFcyk?mmyS`-b`R(;SKO9g zBwns+&Q^>>T!KaTZ?-lB_IG)ecu8+c%jeTl7fY$acL_dD8VM-0Ggre=WCceVhdQQE zn#f2Fye~J~8!5u*m?xOwH8^*De=ayXU=X_M^OapAMJGvsVn-y#TuL-^gS5)+ncP}S zjppX@tj>47D9yFE*|SfM(Z&Q|9!TFBDm71^$J@3J>MOn4((hpW0}%WJgnfD2d2jm% zIDXtS9Qy}Y*1CC(Y`aURxnKY?y~vc#{W=m?CPc#6~hx0`T+KOsvZoHCBi3-A#USxM7nr^kFAy&e z3N0jh@d)O+E%gMi5`R-%8Kz{b?F3yDYe=mzm6dJ@#U1 zC(X7vI1YmnkC+8ZCS>c1^#yGl>b}2mqHSM~Xpd6z5gf51(CsKc#m^>Rg^><6357`T zsomu<((b%Nfgge#En&!G|MiG?!8Y?q%8q&W;_IT~zk6^vqOUdXU%b)g7hP%&WD15H zWS@q~N$H9Gn#Zjx#7mu?UE)h1vdc;z6bnnd`lU|k{(wCNA=52ay0o6ilWNAzaw>p$ zBn%*fg;l`kz4SQ3IK5#sB!3EL;a9$Cb5H#M_htUtDcvpKf{rhmooXCnKWsl6M_KLG6(48KQ&E@F3_GeLcsxJo-(tfle1 z5KFVP0E^o>7$c2E$4e?(AbGyQC7GP!&4)AvK1VO3yuM4xwXCOfqb;P5h)cCHpX{$` zXpt91!5@NEm2E=D^LmNJzisM^6d816`(=mu0J}=adhZrop#fQ$S!yl$HY4fYF&RGE zKCON<#IEHz))UPQ@xUK``NzNe+CUC7?oJg84iP-La1wux62l>$K$}KjS&$9(l2-R5 zD&D`0^!Q6fL9_OT0N)RKA172d|NY~;AEfs>1|}L)ow^hCp}gDoR6;I^XgRE^JazKa zdXHp!S6^#{7Lhle6`++3rH86YB;%Ts7fjkIg6`O9@)zz7sttQ z$5>Lzkvw27)2GUNJH6MpHZlAk;M5=og#Ig;kJ5k+R2HJS@!eiF3eEHY5VerW;J*Y(W(q()m+mP z?lQ|`F_BtZJMwGH9P(rgjDEIo)m|#nAIe*2zHue54-Z05`_Q4)72epz#p1g0MBXDY zsry!vyW4!8n{oO|pr#Z2lvl~ayQg19r8gaQ(yd-G|OO~X+^WLK<1T$`luP?mw zT1uB%N;u-~3y5ehu|MR_xz=5l6$K=cG2<+=ay6;an!GQj$9AF~i(3}l102E~GDC5{ zA)%8+l1^Az+LH%r=LX#oSy^)Z6&39lS^W7u;LjL6RmoB6;$dnU({5_^or8$)O!tAr zTiiE__eq$kq{`)WB}q2%y6pH4WBAD&CIl^K=_r{v!k*Z-vZLqOV7B{L2}^@mVoEDm zkv)Bd_=8VSk0rcR?ElbipXi>T;u)o+$(~^X>q+nm3oBop>bPiF{4bHXkEBM6FI+5` z-9>lP&)-Hk7r2$m%_Ten5AS1}uJs1we6Y6YQ-4Q*BDegNbr8Q3`;xlvkUFk;ju*sT z7oHE@@ewQN3G)n2L$n51gPb`8i^bRLu1Sc$B(01;d#d+6>6=9CQ1E^f0H5tC$p3iZz6oQ9ezF$+pGH)sOc~=PT0yjyBwGxjbwlSt2V^)M4?!S8+G^yKJt#CzuMCV?K?zukCU1#|Hqn;VE*_#pWL0dgl(~ z;UOvq*|+LLr8)A&^PQ|^>$i=>K01Vo-y!yqny0z5tGtljd-jKD8>o-EYO&(}{z-sZ zypoC7k{Vk3OT)H7`00fAHh*XsUIc750N~clB58iO+^dx6eV>F_M?2!>rzKreKO2*C zZgp+I)!9;!h0;aT6lO*h05#UmEtD$i{Gj+ISX@{CWZukuwQnRt!Ju;`M$cUy9yaU1 z=^lUS@BvitVC*=gTr9|AC42-kV{Vq%Dy8mFU_X0vHNpOcdK2coy|3iHL|L@?xz;ljUDcT_)u5h1g z?V6_@;!qt5KcY&S{@7qPw5~*W((5M85`vQX(JE=>!ocQHv~?q0S99=%Aii4?O5O_H zZ`1+Gx#5u=k|Hq7W#oIqa&LjFv%23!q+BYoIRVh6|B5#fpSN#HiN$(-OY}mdZ%*l5 z5H+BPbJHE-%qF+jeljhySn9kPpJ{x{aV8SCQ?>| z`cOO)3|U;I;AX$9c8f}$v?SoYrT8RJN8;Z(6EFb&C{d1k0iMI6Wu)1~Le zK^{F%M~-ocB92x2N~+`Z!vZ+iD2$9?fOCre@0J}VLQ7~GTQMZD_&5P#PFaZv2_*L? z-}B?Jn0NP&jA_lMaF4S>{|Jl<% B4>|w< diff --git a/docs/docfx.json b/docs/docfx.json index 3ac4f99..c3a9584 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -31,7 +31,6 @@ "globalMetadata": { "_appName": "Scrambled Legs & Achin'", "_appTitle": "Scrambled Legs & Achin'", - "_appLogoPath": "content/media/ar-intro.png", "_enableSearch": true, "pdf": false } From d62f349955309cdbd94e458015c9ec12fc088065 Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 01:34:19 +0000 Subject: [PATCH 12/17] Update devcontainer.json to include Azure CLI and Bicep installation --- .devcontainer/devcontainer.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7c5b934..4b2c4ff 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,8 @@ "ghcr.io/devcontainers/features/git-lfs:1": {}, "ghcr.io/devcontainers/features/node:1": { "version": "20" - } + }, + "ghcr.io/devcontainers/features/azure-cli:1": {} }, "customizations": { "vscode": { @@ -14,12 +15,13 @@ "GitHub.copilot-chat", "ms-dotnettools.csharp", "ms-dotnettools.vscode-dotnet-runtime", - "ms-azuretools.vscode-azure-mcp-server" + "ms-azuretools.vscode-azure-mcp-server", + "ms-azuretools.vscode-bicep" ] } }, - "postCreateCommand": "dotnet tool install -g docfx", + "postCreateCommand": "dotnet tool install -g docfx && az bicep install", "remoteEnv": { "PATH": "${containerEnv:PATH}:${containerEnv:HOME}/.dotnet/tools" } -} +} \ No newline at end of file From 7ccd5cc59f28bba0e9464c79b45ca957c57da6fa Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 01:36:16 +0000 Subject: [PATCH 13/17] Update environment tag in bicep.parameters.json from Production to Dev --- infra/bicep.parameters.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/bicep.parameters.json b/infra/bicep.parameters.json index 78a3623..3f125ec 100644 --- a/infra/bicep.parameters.json +++ b/infra/bicep.parameters.json @@ -13,7 +13,7 @@ }, "tags": { "value": { - "Environment": "Production", + "Environment": "Dev", "Project": "DocFX Wiki" } } From ca604b170c01cb5c390634c757f5627bd6cbbf39 Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 01:37:23 +0000 Subject: [PATCH 14/17] Remove deployment token output from main.bicep --- infra/main.bicep | 3 --- 1 file changed, 3 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 4f8514d..cbc5cd9 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -33,9 +33,6 @@ resource staticWebApp 'Microsoft.Web/staticSites@2023-12-01' = { } } -@description('Deployment token for CI/CD') -output deploymentToken string = staticWebApp.listSecrets().properties.apiKey - @description('Static Web App default hostname') output defaultHostname string = staticWebApp.properties.defaultHostname From 22e8af772412a4ef4da5d37fc797a77068f140f1 Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 01:51:41 +0000 Subject: [PATCH 15/17] Update README.md to enhance deployment options for GitHub Pages and Azure Static Web Apps --- README.md | 79 ++++++++++++++++++++++--------------------------------- 1 file changed, 31 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 5a93b1e..3d0aa32 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,41 @@ -# docfx-wiki +# DocFX Wiki Lightweight wiki-style documentation site built with [DocFX](https://github.com/dotnet/docfx). Author content in Markdown, build a static site, and (optionally) publish to GitHub Pages. -## Quick start +## Publish + +This template supports two deployment options. Pick one: + +### Option 1: GitHub Pages + +- Workflow: `.github/workflows/publish-site.yml` +- Deploys automatically on pushes to `main` that touch `docs/**` + +To enable GitHub Pages for a new repo created from this template: + +1. In GitHub, go to **Settings** → **Pages**. +2. Under **Build and deployment**, set **Source** to **GitHub Actions**. +3. Push a change under `docs/` to `main` (or run the workflow via **Actions**). + +### Option 2: Azure Static Web Apps + +- Workflow: `.github/workflows/azure-swa-deploy.yml` +- Requires a repo secret named `AZURE_STATIC_WEB_APPS_API_TOKEN` + +Quick deploy (automated script): + +```bash +./deploy-azure.sh +``` + +Manual setup: see `DEPLOYMENT.md`. + +## Local Development ### Option A: GitHub Codespaces (recommended) 1. Create a Codespace from this repo. -2. Wait for the dev container to finish provisioning (it installs DocFX). +2. Wait for the dev container to finish provisioning. 3. Start the local site server: ```bash @@ -58,51 +86,6 @@ docfx docs/docfx.json Output goes to `docs/_site/`. -## Publish - -This repository supports two deployment options: - -### Option 1: GitHub Pages (Default) - -A GitHub Actions workflow automatically builds and deploys `docs/_site` to GitHub Pages on pushes to `main`. - -Workflow: `.github/workflows/publish-site.yml` - -### Option 2: Azure Static Web Apps - -Deploy to Azure Static Web Apps for a production-ready hosting solution with global CDN, custom domains, and automatic SSL certificates. - -#### Quick Deploy (Automated Script) - -```bash -./deploy-azure.sh -``` - -This script will: -- Create the Azure resource group -- Deploy the Static Web App using Bicep -- Retrieve the deployment token -- Display next steps for GitHub setup - -#### Manual Deployment - -See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed step-by-step instructions. - -#### Customization - -- **Infrastructure**: Edit `infra/bicep.parameters.json` to customize: - - `staticWebAppName`: Your unique app name - - `location`: Azure region - - `sku`: Free or Standard tier - - `tags`: Resource organization tags - -- **Runtime Configuration**: Edit `staticwebapp.config.json` for: - - Navigation fallback rules - - Custom routes - - Headers and MIME types - -See [infra/README.md](infra/README.md) for infrastructure details. - ## Learn more - DocFX project: https://github.com/dotnet/docfx From 9172a64a50af4906693f791196991542cacee370 Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 02:10:18 +0000 Subject: [PATCH 16/17] Refactor README.md for clarity and enhance deployment instructions for Azure Static Web Apps --- DEPLOYMENT.md | 45 --------------------------------------------- README.md | 9 ++++++--- 2 files changed, 6 insertions(+), 48 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 1dda69c..6ff75ab 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -149,51 +149,6 @@ Or find it in the Azure Portal: - Clear your browser cache - Check the deployment status in GitHub Actions -## Advanced Configuration - -### Custom Domain -```bash -az staticwebapp hostname set \ - --name YOUR_APP_NAME \ - --resource-group $RESOURCE_GROUP \ - --hostname www.yourdomain.com -``` - -### View Deployment Logs -- Go to GitHub repository → **Actions** tab -- Click on the latest workflow run -- Expand the deployment steps to see logs - -### Upgrade to Standard Tier -Edit `bicep.parameters.json`: -```json -"sku": { - "value": "Standard" -} -``` - -Then redeploy: -```bash -az deployment group create \ - --resource-group $RESOURCE_GROUP \ - --template-file main.bicep \ - --parameters bicep.parameters.json -``` - -## Cost Estimate - -- **Free Tier:** $0/month - - 100 GB bandwidth/month - - 0.5 GB storage - - Custom domains with free SSL - -- **Standard Tier:** ~$9/month - - Unlimited bandwidth - - 10 GB storage - - Additional enterprise features - -[Full pricing details](https://azure.microsoft.com/pricing/details/app-service/static/) - ## Cleanup To delete all resources and stop charges: diff --git a/README.md b/README.md index 3d0aa32..7579a51 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ # DocFX Wiki -Lightweight wiki-style documentation site built with [DocFX](https://github.com/dotnet/docfx). Author content in Markdown, build a static site, and (optionally) publish to GitHub Pages. +A proof of concept (PoC) wiki-style documentation site built with [DocFX](https://github.com/dotnet/docfx). It demonstrates a simple, yet feature-rich wiki experience (navigation, search, static hosting) while keeping authoring lightweight with Markdown. + +The focus of this repo is to provide a reusable template so others can quickly start a wiki in their own environment and deploy it with minimal setup. ## Publish -This template supports two deployment options. Pick one: +Click the green `Use this template` button in the top right of this page and choose `create a new repository`, then pick one of the following deployment options: ### Option 1: GitHub Pages @@ -22,11 +24,12 @@ To enable GitHub Pages for a new repo created from this template: - Workflow: `.github/workflows/azure-swa-deploy.yml` - Requires a repo secret named `AZURE_STATIC_WEB_APPS_API_TOKEN` -Quick deploy (automated script): +Quick deploy (automated script) via Codespaces: ```bash ./deploy-azure.sh ``` +or Manual setup: see `DEPLOYMENT.md`. From e8c686323739a993789c6701d744536fe8df49b4 Mon Sep 17 00:00:00 2001 From: robpitcher Date: Wed, 28 Jan 2026 02:42:29 +0000 Subject: [PATCH 17/17] Update README.md to clarify GitHub Pages deployment instructions and enhance Azure Static Web Apps setup steps --- .github/workflows/azure-swa-deploy.yml | 126 ++++++++++++------------- README.md | 16 +++- 2 files changed, 74 insertions(+), 68 deletions(-) diff --git a/.github/workflows/azure-swa-deploy.yml b/.github/workflows/azure-swa-deploy.yml index 1e1e4b0..0ae060b 100644 --- a/.github/workflows/azure-swa-deploy.yml +++ b/.github/workflows/azure-swa-deploy.yml @@ -1,72 +1,72 @@ -name: Deploy to Azure Static Web Apps +# name: Deploy to Azure Static Web Apps -on: - push: - branches: - - main - paths: - - 'docs/**' - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - main - paths: - - 'docs/**' - workflow_dispatch: +# on: +# push: +# branches: +# - main +# paths: +# - 'docs/**' +# pull_request: +# types: [opened, synchronize, reopened, closed] +# branches: +# - main +# paths: +# - 'docs/**' +# workflow_dispatch: -# Sets permissions for GITHUB_TOKEN -permissions: - contents: read - pull-requests: write +# # Sets permissions for GITHUB_TOKEN +# permissions: +# contents: read +# pull-requests: write -# Allow only one concurrent deployment -concurrency: - group: "azure-swa" - cancel-in-progress: false +# # Allow only one concurrent deployment +# concurrency: +# group: "azure-swa" +# cancel-in-progress: false -jobs: - build_and_deploy: - # Only run on push or if PR is not closed - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - name: Build and Deploy - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 1 +# jobs: +# build_and_deploy: +# # Only run on push or if PR is not closed +# if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.action != 'closed') +# runs-on: ubuntu-latest +# name: Build and Deploy +# steps: +# - name: Checkout +# uses: actions/checkout@v4 +# with: +# fetch-depth: 1 - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.x +# - name: Setup .NET +# uses: actions/setup-dotnet@v4 +# with: +# dotnet-version: 8.x - - name: Install DocFX - run: dotnet tool update -g docfx +# - name: Install DocFX +# run: dotnet tool update -g docfx - - name: Build documentation - run: docfx docs/docfx.json +# - name: Build documentation +# run: docfx docs/docfx.json - - name: Deploy to Azure Static Web Apps - id: deploy - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} - repo_token: ${{ secrets.GITHUB_TOKEN }} - action: upload - app_location: "docs/_site" - api_location: "" - output_location: "" - skip_app_build: true +# - name: Deploy to Azure Static Web Apps +# id: deploy +# uses: Azure/static-web-apps-deploy@v1 +# with: +# azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} +# repo_token: ${{ secrets.GITHUB_TOKEN }} +# action: upload +# app_location: "docs/_site" +# api_location: "" +# output_location: "" +# skip_app_build: true - close_pull_request: - # Only run when PR is closed - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Close Pull Request - steps: - - name: Close Pull Request - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} - action: close +# close_pull_request: +# # Only run when PR is closed +# if: github.event_name == 'pull_request' && github.event.action == 'closed' +# runs-on: ubuntu-latest +# name: Close Pull Request +# steps: +# - name: Close Pull Request +# uses: Azure/static-web-apps-deploy@v1 +# with: +# azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} +# action: close diff --git a/README.md b/README.md index 7579a51..84cae24 100644 --- a/README.md +++ b/README.md @@ -11,24 +11,30 @@ Click the green `Use this template` button in the top right of this page and cho ### Option 1: GitHub Pages - Workflow: `.github/workflows/publish-site.yml` -- Deploys automatically on pushes to `main` that touch `docs/**` -To enable GitHub Pages for a new repo created from this template: +Enable GitHub Pages for the new repo created from this template: -1. In GitHub, go to **Settings** → **Pages**. +1. In the repo, go to **Settings** → **Pages**. 2. Under **Build and deployment**, set **Source** to **GitHub Actions**. -3. Push a change under `docs/` to `main` (or run the workflow via **Actions**). +3. Deploy to GitHub Pages by pushing a change under `docs/` to `main` or run the workflow via **Actions**. ### Option 2: Azure Static Web Apps - Workflow: `.github/workflows/azure-swa-deploy.yml` - Requires a repo secret named `AZURE_STATIC_WEB_APPS_API_TOKEN` -Quick deploy (automated script) via Codespaces: +Quick deploy (Codespaces recommended): +1. Edit the `infra/bicep.parameters.json` as desired. +2. Authenticate to Azure using `az login` and select the desired subscription +3. Run the deployment script: ```bash ./deploy-azure.sh ``` +4. Copy the deployment token from the Azure Static Web App in the Azure Portal. +5. Create a repo secret in this repository named `AZURE_STATIC_WEB_APPS_API_TOKEN` with the deployment token as the value. +6. Uncomment `.github/workflows/azure-swa-deploy.yml` and run the workflow via **Actions**. + or Manual setup: see `DEPLOYMENT.md`.