diff --git a/.github/workflows/auto-merge-on-approval.yml b/.github/workflows/auto-merge-on-approval.yml new file mode 100644 index 0000000..4dc998a --- /dev/null +++ b/.github/workflows/auto-merge-on-approval.yml @@ -0,0 +1,18 @@ +name: Auto-merge on approval +on: + pull_request_review: + types: [submitted] + +jobs: + automerge: + if: ${{ github.event.review.state == 'approved' }} + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + checks: read + steps: + - name: Attempt merge when approved + run: | + echo "Auto-merge on approval disabled." + echo "Please merge the PR manually after required checks and approvals pass." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3db819..2f1c7ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ on: jobs: build: + name: Build and Test runs-on: ubuntu-latest steps: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..0548f00 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,85 @@ +name: Deploy to Azure (on merge to main) + +on: + push: + branches: [main] + +env: + DOTNET_VERSION: "9.0.x" + +jobs: + build-and-deploy: + name: Build, Publish and Deploy + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Restore + run: dotnet restore Aquiis.sln + + - name: Publish + run: dotnet publish ./Aquiis.SimpleStart/Aquiis.SimpleStart.csproj -c Release -o ./publish + + - name: Azure Login (for deployment) + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Deploy with Azure CLI + run: | + az webapp deploy \ + --resource-group ${{ secrets.AZURE_RESOURCE_GROUP }} \ + --name ${{ secrets.AZURE_WEBAPP_NAME }} \ + --src-path ./publish + + - name: Save publish profile to file + run: | + echo "${{ secrets.AZURE_PUBLISH_PROFILE }}" > /tmp/publish_profile.xml + + - name: Extract DB settings and export to GITHUB_ENV + run: | + DB_FILE=$(jq -r '.ApplicationSettings.DatabaseFileName // "app_v0.0.0.db"' Aquiis.SimpleStart/appsettings.json) + PREV_DB_FILE=$(jq -r '.ApplicationSettings.PreviousDatabaseFileName // ""' Aquiis.SimpleStart/appsettings.json) + APP_VERSION=$(jq -r '.ApplicationSettings.Version // ""' Aquiis.SimpleStart/appsettings.json) + SCHEMA_VERSION=$(jq -r '.ApplicationSettings.SchemaVersion // ""' Aquiis.SimpleStart/appsettings.json) + echo "DB_FILE=$DB_FILE" >> "$GITHUB_ENV" + echo "PREV_DB_FILE=$PREV_DB_FILE" >> "$GITHUB_ENV" + echo "APP_VERSION=$APP_VERSION" >> "$GITHUB_ENV" + echo "SCHEMA_VERSION=$SCHEMA_VERSION" >> "$GITHUB_ENV" + echo "DB_PATH=/home/data/$DB_FILE" >> "$GITHUB_ENV" + shell: bash + + - name: Create /home/data on App Service + run: | + chmod +x ./scripts/kudu-create-data-dir.sh + ./scripts/kudu-create-data-dir.sh /tmp/publish_profile.xml ${{ secrets.AZURE_WEBAPP_NAME }} + + - name: Backup existing DB (if any) + run: | + chmod +x ./scripts/kudu-backup-db.sh + ./scripts/kudu-backup-db.sh /tmp/publish_profile.xml ${{ secrets.AZURE_WEBAPP_NAME }} "$DB_PATH" || true + + - name: Azure Login (for App Settings) + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Configure App Settings (set SQLite connection string and app metadata) + run: | + az webapp config appsettings set \ + --resource-group "${{ secrets.AZURE_RESOURCE_GROUP }}" \ + --name "${{ secrets.AZURE_WEBAPP_NAME }}" \ + --settings \ + "ConnectionStrings__DefaultConnection=DataSource=/home/data/$DB_FILE;Cache=Shared" \ + "ApplicationSettings__Version=$APP_VERSION" \ + "ApplicationSettings__DatabaseFileName=$DB_FILE" \ + "ApplicationSettings__PreviousDatabaseFileName=$PREV_DB_FILE" \ + "ApplicationSettings__SchemaVersion=$SCHEMA_VERSION" \ + ASPNETCORE_ENVIRONMENT=Production + shell: bash diff --git a/.gitignore b/.gitignore index aa4f4f9..26c67f3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,10 @@ Data/Backups/** /Data/app* +sp_credentials.json + +publish_profile.xml + # Python virtual environment created per-project .venv/ diff --git a/scripts/kudu-backup-db.sh b/scripts/kudu-backup-db.sh new file mode 100644 index 0000000..8ba2f14 --- /dev/null +++ b/scripts/kudu-backup-db.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail +# Usage: ./kudu-backup-db.sh /path/to/publish_profile.xml APP_NAME [/home/data/app.db] +PUBLISH_PROFILE_FILE=${1:-} +APP_NAME=${2:-} +SRC_DB_PATH=${3:-/home/data/app_v0.0.0.db} +if [ -z "$PUBLISH_PROFILE_FILE" ] || [ -z "$APP_NAME" ]; then + echo "Usage: $0 [src-db-path]" >&2 + exit 2 +fi + +KUDU_USER=$(grep -o 'userName="[^"]*"' "$PUBLISH_PROFILE_FILE" | head -1 | sed -E 's/userName="([^"]+)"/\1/') +KUDU_PWD=$(grep -o 'userPWD="[^"]*"' "$PUBLISH_PROFILE_FILE" | head -1 | sed -E 's/userPWD="([^"]+)"/\1/') + + +TS=$(date +%Y%m%d%H%M%S) +BACKUP_PATH="/home/data/Backups/$(basename $SRC_DB_PATH).$TS" + +API="https://${APP_NAME}.scm.azurewebsites.net/api/command" +CMD="mkdir -p /home/data/Backups && cp '$SRC_DB_PATH' '$BACKUP_PATH' && ls -l /home/data/Backups" + +echo "Backing up $SRC_DB_PATH -> $BACKUP_PATH on ${APP_NAME} via Kudu..." +curl -s -X POST -u "$KUDU_USER:$KUDU_PWD" -H "Content-Type: application/json" -d "{\"command\":\"$CMD\"}" "$API" | jq . + +echo "Backup complete: $BACKUP_PATH" diff --git a/scripts/kudu-cleanup-backups.sh b/scripts/kudu-cleanup-backups.sh new file mode 100644 index 0000000..b541b20 --- /dev/null +++ b/scripts/kudu-cleanup-backups.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail +# Usage: ./kudu-cleanup-backups.sh /path/to/publish_profile.xml APP_NAME RETAIN_DAYS +PUBLISH_PROFILE_FILE=${1:-} +APP_NAME=${2:-} +RETAIN_DAYS=${3:-30} +if [ -z "$PUBLISH_PROFILE_FILE" ] || [ -z "$APP_NAME" ]; then + echo "Usage: $0 [retain-days]" >&2 + exit 2 +fi + +KUDU_USER=$(grep -o 'userName="[^"]*"' "$PUBLISH_PROFILE_FILE" | head -1 | sed -E 's/userName="([^"]+)"/\1/') +KUDU_PWD=$(grep -o 'userPWD="[^"]*"' "$PUBLISH_PROFILE_FILE" | head -1 | sed -E 's/userPWD="([^"]+)"/\1/') + + +API="https://${APP_NAME}.scm.azurewebsites.net/api/command" +CMD="find /home/data/Backups -type f -mtime +$RETAIN_DAYS -print -delete || true && ls -l /home/data/Backups" + +echo "Cleaning up backups older than $RETAIN_DAYS days on ${APP_NAME} via Kudu..." +curl -s -X POST -u "$KUDU_USER:$KUDU_PWD" -H "Content-Type: application/json" -d "{\"command\":\"$CMD\"}" "$API" | jq . + +echo "Cleanup complete." diff --git a/scripts/kudu-create-data-dir.sh b/scripts/kudu-create-data-dir.sh new file mode 100644 index 0000000..4f1f4b4 --- /dev/null +++ b/scripts/kudu-create-data-dir.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail +# Usage: ./kudu-create-data-dir.sh /path/to/publish_profile.xml APP_NAME +PUBLISH_PROFILE_FILE=${1:-} +APP_NAME=${2:-} +if [ -z "$PUBLISH_PROFILE_FILE" ] || [ -z "$APP_NAME" ]; then + echo "Usage: $0 " >&2 + exit 2 +fi + +KUDU_USER=$(grep -o 'userName="[^"]*"' "$PUBLISH_PROFILE_FILE" | head -1 | sed -E 's/userName="([^"]+)"/\1/') +KUDU_PWD=$(grep -o 'userPWD="[^"]*"' "$PUBLISH_PROFILE_FILE" | head -1 | sed -E 's/userPWD="([^"]+)"/\1/') + +API="https://${APP_NAME}.scm.azurewebsites.net/api/command" +CMD='mkdir -p /home/data && chmod 775 /home/data' + +echo "Creating /home/data on ${APP_NAME} via Kudu..." +curl -s -X POST -u "$KUDU_USER:$KUDU_PWD" -H "Content-Type: application/json" -d "{\"command\":\"$CMD\"}" "$API" | jq . + +echo "Done."