From 4b1efa499525443afb29b04be8cf25f28040fc80 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Tue, 5 Aug 2025 22:47:37 +0100 Subject: [PATCH 01/19] Maintenance action-v2 --- maintenance/action-v2.yml | 187 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 maintenance/action-v2.yml diff --git a/maintenance/action-v2.yml b/maintenance/action-v2.yml new file mode 100644 index 0000000..cc37203 --- /dev/null +++ b/maintenance/action-v2.yml @@ -0,0 +1,187 @@ +name: Maintenance V2 +description: Enable or disable maintenance for a service with centralized templates +inputs: + environment: + description: Name of the app environment + required: true + azure-credentials: + description: 'A JSON string containing service principal credentials e.g. {"client_id": "x", "client_secret": "x", "subscription_id": "x", "tenant_id": "x"}' + required: false + default: '' + azure-client-id: + description: Azure service principal or managed identity client ID when using OIDC + required: false + default: '' + azure-subscription-id: + description: Azure service principal or managed identity subscription ID when using OIDC + required: false + default: '' + azure-tenant-id: + description: Azure service principal or managed identity tenant ID when using OIDC + required: false + default: '' + mode: + description: Maintenance mode to implement, either enable or disable + required: true + docker-repository: + description: Name of the maint app docker repository + required: false + github-token: + description: Default Github token retrieved via secrets.GITHUB_TOKEN or PAT with permission to the repository (Required) + required: false + service-name: + description: Human-readable service name for the maintenance page + required: false + default: 'This service' + maintenance-config-path: + description: Path to service-specific maintenance configuration file + required: false + default: '' + template-ref: + description: Reference (branch/tag/commit) of teacher-services-cloud to use for templates + required: false + default: 'main' + +runs: + using: composite + steps: + - uses: azure/login@v2 + if: inputs.azure-credentials != '' + with: + creds: ${{ inputs.azure-credentials }} + + - uses: azure/login@v2 + if: inputs.azure-credentials == '' + with: + client-id: ${{ inputs.azure-client-id }} + tenant-id: ${{ inputs.azure-tenant-id }} + subscription-id: ${{ inputs.azure-subscription-id }} + + - name: Set ARM and kubelogin environment + uses: DFE-Digital/github-actions/set-kubelogin-environment@master + with: + azure-credentials: ${{ inputs.azure-credentials }} + azure-client-id: ${{ inputs.azure-client-id }} + azure-tenant-id: ${{ inputs.azure-tenant-id }} + azure-subscription-id: ${{ inputs.azure-subscription-id }} + + - name: Fetch maintenance page templates + if: inputs.mode == 'enable' + shell: bash + run: | + echo "Fetching maintenance page templates from teacher-services-cloud..." + + # Create temp directory for building + TEMP_BUILD_DIR=$(mktemp -d) + echo "TEMP_BUILD_DIR=$TEMP_BUILD_DIR" >> $GITHUB_ENV + + # Clone only the templates directory (sparse checkout) + cd $TEMP_BUILD_DIR + git clone --filter=blob:none --sparse https://github.com/DFE-Digital/teacher-services-cloud.git + cd teacher-services-cloud + git sparse-checkout set templates/new_service/maintenance_page + git checkout ${{ inputs.template-ref }} + + # Copy template files to build directory + cp -r templates/new_service/maintenance_page $TEMP_BUILD_DIR/maintenance_build + + echo "Templates fetched successfully" + + - name: Apply service customizations + if: inputs.mode == 'enable' && inputs.maintenance-config-path != '' + shell: bash + run: | + echo "Applying service customizations..." + + # Check if service has local customization file + if [[ -f "${{ inputs.maintenance-config-path }}" ]]; then + echo "Found service configuration at ${{ inputs.maintenance-config-path }}" + + # Copy service config to build directory + cp "${{ inputs.maintenance-config-path }}" $TEMP_BUILD_DIR/maintenance_build/config.json + + # Apply customizations using jq or sed + # This is a simple example - could be enhanced + SERVICE_NAME=$(jq -r '.service_name // "This service"' $TEMP_BUILD_DIR/maintenance_build/config.json) + + # Replace placeholders in HTML + sed -i "s/#SERVICE_PRETTY#/$SERVICE_NAME/g" $TEMP_BUILD_DIR/maintenance_build/html/index.html + + # Apply any custom message if provided + if [[ $(jq -r 'has("maintenance_message")' $TEMP_BUILD_DIR/maintenance_build/config.json) == "true" ]]; then + # This would require more complex HTML manipulation + echo "Custom maintenance message found - would apply here" + fi + else + echo "No service configuration found, using defaults" + # Use the service-name input for basic customization + sed -i "s/#SERVICE_PRETTY#/${{ inputs.service-name }}/g" $TEMP_BUILD_DIR/maintenance_build/html/index.html + fi + + - name: Check for local overrides + if: inputs.mode == 'enable' + shell: bash + run: | + echo "Checking for local maintenance page overrides..." + + # If service has local maintenance_page directory with overrides, copy them + if [[ -d "maintenance_page" ]]; then + echo "Found local maintenance_page directory - applying overrides" + + # Copy local files over template files (allows partial overrides) + # Only copy files that exist, preserving template structure + for file in maintenance_page/*; do + if [[ -f "$file" ]]; then + cp "$file" "$TEMP_BUILD_DIR/maintenance_build/" + echo "Override applied: $(basename $file)" + fi + done + + # Check for local HTML overrides + if [[ -d "maintenance_page/html" ]]; then + cp -r maintenance_page/html/* "$TEMP_BUILD_DIR/maintenance_build/html/" 2>/dev/null || true + echo "HTML overrides applied" + fi + fi + + - name: Build and push docker image + if: inputs.mode == 'enable' + id: build-image + uses: DFE-Digital/github-actions/build-docker-image@master + with: + github-token: ${{ inputs.github-token }} + dockerfile-path: ${{ env.TEMP_BUILD_DIR }}/maintenance_build/Dockerfile + docker-repository: ${{ inputs.docker-repository }} + context: ${{ env.TEMP_BUILD_DIR }}/maintenance_build + + - name: Clean up temp directory + if: inputs.mode == 'enable' + shell: bash + run: | + rm -rf $TEMP_BUILD_DIR + + - name: Enable maintenance mode + if: inputs.mode == 'enable' + shell: bash + run: make ci ${{ inputs.environment }} maintenance-fail-over + env: + MAINTENANCE_IMAGE_TAG: ${{steps.build-image.outputs.tag}} + + - name: Disable maintenance mode + if: inputs.mode == 'disable' + shell: bash + run: make ci ${{ inputs.environment }} disable-maintenance + + - name: Maintenance Summary + if: success() + shell: bash + run: | + NOW=$(TZ=Europe/London date +"%F %R") + echo 'MAINTENANCE PAGE ${{ inputs.mode }}d!' >> $GITHUB_STEP_SUMMARY + echo 'ENV: ${{ inputs.environment }}' >> $GITHUB_STEP_SUMMARY + echo "AT : ${NOW}" >> $GITHUB_STEP_SUMMARY + if [[ "${{ inputs.mode }}" == "enable" ]]; then + TEMP_URLS=$(awk '/name:.*cloud/ {print $2}' ./maintenance_page/manifests/${{ inputs.environment }}/ingress_temp*.yml) + echo 'TEMP URLS:' >> $GITHUB_STEP_SUMMARY + echo "${TEMP_URLS}" >> $GITHUB_STEP_SUMMARY + fi \ No newline at end of file From 773bf4cc2487eaabdf13b9d19db1dec9938d3e94 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 09:05:13 +0100 Subject: [PATCH 02/19] maintenance action v2 --- maintenance/README-v2.md | 111 ++++++++++++++++++++++ maintenance/action-v2.yml | 194 ++++++++++++++++++++++---------------- 2 files changed, 225 insertions(+), 80 deletions(-) create mode 100644 maintenance/README-v2.md diff --git a/maintenance/README-v2.md b/maintenance/README-v2.md new file mode 100644 index 0000000..ecefb61 --- /dev/null +++ b/maintenance/README-v2.md @@ -0,0 +1,111 @@ +# Maintenance GitHub Action V2 + +This action enables or disables maintenance mode for services using centralized templates from the `teacher-services-cloud` repository. + +## Key Features + +- **Centralized Templates**: Uses templates from `teacher-services-cloud` repository +- **Simple Customization**: All customization through action inputs (no config files) +- **Automatic Updates**: Template improvements automatically available to all services +- **Backward Compatible**: Easy migration path from v1 + +## Usage Examples + +### Minimal Configuration +```yaml +- name: Enable maintenance + uses: DFE-Digital/github-actions/maintenance/action-v2@main + with: + environment: production + mode: enable + docker-repository: myservice-maintenance + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} +``` + +### Standard Configuration +```yaml +- name: Enable maintenance + uses: DFE-Digital/github-actions/maintenance/action-v2@main + with: + environment: production + mode: enable + docker-repository: myservice-maintenance + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} + service-name: "Apply for Teacher Training" + maintenance-message: "We're upgrading our systems to serve you better." + contact-email: "support@education.gov.uk" +``` + +### Full Configuration +```yaml +- name: Enable maintenance + uses: DFE-Digital/github-actions/maintenance/action-v2@main + with: + environment: production + mode: enable + docker-repository: myservice-maintenance + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} + service-name: "Apply for Teacher Training" + maintenance-message: "We are performing scheduled maintenance to improve system performance." + contact-email: "support@apply-for-teacher-training.service.gov.uk" + estimated-return-time: "15:00 GMT" + status-page-url: "https://status.education.gov.uk" + template-ref: "v2.1.0" # Pin to specific version +``` + +### Disabling Maintenance +```yaml +- name: Disable maintenance + uses: DFE-Digital/github-actions/maintenance/action-v2@main + with: + environment: production + mode: disable + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} +``` + +## How It Works + +1. **Fetches Templates**: Uses sparse checkout to get maintenance page templates from `teacher-services-cloud` +2. **Applies Customizations**: Replaces placeholders in HTML with your input values +3. **Builds Docker Image**: Creates maintenance page container with customized content +4. **Deploys**: Enables or disables maintenance mode using existing infrastructure + +## Migration from V1 + +### Before (V1) +Each service maintains its own `maintenance_page/` directory with: +- Dockerfile +- nginx.conf +- html/index.html +- All assets and styles + +### After (V2) +Services only need to update their workflow file: + +```yaml +# Change from: +uses: DFE-Digital/github-actions/maintenance@main + +# To: +uses: DFE-Digital/github-actions/maintenance/action-v2@main +with: + service-name: "Your Service Name" + maintenance-message: "Your custom message" + contact-email: "your-support@education.gov.uk" +``` + +Then remove the local `maintenance_page/` directory - it's no longer needed! + +## Template Repository + +Templates are maintained at: +``` +github.com/DFE-Digital/teacher-services-cloud +└── templates/new_service/maintenance_page/ + ├── Dockerfile + ├── nginx.conf + └── html/ + └── index.html +``` + +To update the templates for all services, make changes in the teacher-services-cloud repository. \ No newline at end of file diff --git a/maintenance/action-v2.yml b/maintenance/action-v2.yml index cc37203..7f3e7d5 100644 --- a/maintenance/action-v2.yml +++ b/maintenance/action-v2.yml @@ -1,44 +1,68 @@ name: Maintenance V2 description: Enable or disable maintenance for a service with centralized templates inputs: + # Required inputs environment: description: Name of the app environment required: true + mode: + description: Maintenance mode to implement, either enable or disable + required: true + + # Azure authentication inputs azure-credentials: - description: 'A JSON string containing service principal credentials e.g. {"client_id": "x", "client_secret": "x", "subscription_id": "x", "tenant_id": "x"}' + description: 'JSON string containing service principal credentials' required: false default: '' azure-client-id: - description: Azure service principal or managed identity client ID when using OIDC + description: Azure client ID when using OIDC required: false default: '' azure-subscription-id: - description: Azure service principal or managed identity subscription ID when using OIDC + description: Azure subscription ID when using OIDC required: false default: '' azure-tenant-id: - description: Azure service principal or managed identity tenant ID when using OIDC + description: Azure tenant ID when using OIDC required: false default: '' - mode: - description: Maintenance mode to implement, either enable or disable - required: true + + # Docker configuration docker-repository: description: Name of the maint app docker repository required: false github-token: - description: Default Github token retrieved via secrets.GITHUB_TOKEN or PAT with permission to the repository (Required) + description: GitHub token for repository access required: false + + # Customization inputs (all optional with sensible defaults) service-name: description: Human-readable service name for the maintenance page required: false default: 'This service' - maintenance-config-path: - description: Path to service-specific maintenance configuration file + + maintenance-message: + description: Custom maintenance message to display to users + required: false + default: 'We are currently carrying out essential maintenance. Please try again later.' + + contact-email: + description: Support contact email to display + required: false + default: '' + + estimated-return-time: + description: Estimated time when service will be back (e.g., "15:00" or "3:00 PM") + required: false + default: '' + + status-page-url: + description: URL to external status page for updates required: false default: '' + template-ref: - description: Reference (branch/tag/commit) of teacher-services-cloud to use for templates + description: Branch/tag/commit of teacher-services-cloud repository to use required: false default: 'main' @@ -65,84 +89,72 @@ runs: azure-tenant-id: ${{ inputs.azure-tenant-id }} azure-subscription-id: ${{ inputs.azure-subscription-id }} - - name: Fetch maintenance page templates + - name: Fetch and customize maintenance page templates if: inputs.mode == 'enable' shell: bash run: | echo "Fetching maintenance page templates from teacher-services-cloud..." - # Create temp directory for building - TEMP_BUILD_DIR=$(mktemp -d) - echo "TEMP_BUILD_DIR=$TEMP_BUILD_DIR" >> $GITHUB_ENV + # Remove any existing maintenance_page directory and create fresh + rm -rf maintenance_page + mkdir -p maintenance_page + + # Create temp directory for cloning + TEMP_DIR=$(mktemp -d) # Clone only the templates directory (sparse checkout) - cd $TEMP_BUILD_DIR + cd $TEMP_DIR git clone --filter=blob:none --sparse https://github.com/DFE-Digital/teacher-services-cloud.git cd teacher-services-cloud git sparse-checkout set templates/new_service/maintenance_page git checkout ${{ inputs.template-ref }} - # Copy template files to build directory - cp -r templates/new_service/maintenance_page $TEMP_BUILD_DIR/maintenance_build + # Copy template files to the expected location in workspace + cp -r templates/new_service/maintenance_page/* ${{ github.workspace }}/maintenance_page/ + + # Clean up temp directory + rm -rf $TEMP_DIR echo "Templates fetched successfully" - - - name: Apply service customizations - if: inputs.mode == 'enable' && inputs.maintenance-config-path != '' - shell: bash - run: | + + # Apply customizations echo "Applying service customizations..." + cd ${{ github.workspace }} - # Check if service has local customization file - if [[ -f "${{ inputs.maintenance-config-path }}" ]]; then - echo "Found service configuration at ${{ inputs.maintenance-config-path }}" - - # Copy service config to build directory - cp "${{ inputs.maintenance-config-path }}" $TEMP_BUILD_DIR/maintenance_build/config.json - - # Apply customizations using jq or sed - # This is a simple example - could be enhanced - SERVICE_NAME=$(jq -r '.service_name // "This service"' $TEMP_BUILD_DIR/maintenance_build/config.json) - - # Replace placeholders in HTML - sed -i "s/#SERVICE_PRETTY#/$SERVICE_NAME/g" $TEMP_BUILD_DIR/maintenance_build/html/index.html - - # Apply any custom message if provided - if [[ $(jq -r 'has("maintenance_message")' $TEMP_BUILD_DIR/maintenance_build/config.json) == "true" ]]; then - # This would require more complex HTML manipulation - echo "Custom maintenance message found - would apply here" - fi + # Replace service name placeholder + sed -i "s/#SERVICE_PRETTY#/${{ inputs.service-name }}/g" maintenance_page/html/index.html + + # Replace maintenance message placeholder + ESCAPED_MESSAGE=$(echo "${{ inputs.maintenance-message }}" | sed 's/[[\.*^$()+?{|]/\\&/g') + sed -i "s/#MAINTENANCE_MESSAGE#/${ESCAPED_MESSAGE}/g" maintenance_page/html/index.html + + # Handle estimated return time + if [[ -n "${{ inputs.estimated-return-time }}" ]]; then + RETURN_HTML="

We expect the service to be available again at: ${{ inputs.estimated-return-time }}

" + sed -i "s|#ESTIMATED_RETURN#|${RETURN_HTML}|g" maintenance_page/html/index.html else - echo "No service configuration found, using defaults" - # Use the service-name input for basic customization - sed -i "s/#SERVICE_PRETTY#/${{ inputs.service-name }}/g" $TEMP_BUILD_DIR/maintenance_build/html/index.html + sed -i "s|#ESTIMATED_RETURN#||g" maintenance_page/html/index.html fi - - - name: Check for local overrides - if: inputs.mode == 'enable' - shell: bash - run: | - echo "Checking for local maintenance page overrides..." - # If service has local maintenance_page directory with overrides, copy them - if [[ -d "maintenance_page" ]]; then - echo "Found local maintenance_page directory - applying overrides" - - # Copy local files over template files (allows partial overrides) - # Only copy files that exist, preserving template structure - for file in maintenance_page/*; do - if [[ -f "$file" ]]; then - cp "$file" "$TEMP_BUILD_DIR/maintenance_build/" - echo "Override applied: $(basename $file)" - fi - done - - # Check for local HTML overrides - if [[ -d "maintenance_page/html" ]]; then - cp -r maintenance_page/html/* "$TEMP_BUILD_DIR/maintenance_build/html/" 2>/dev/null || true - echo "HTML overrides applied" - fi + # Handle status page URL + if [[ -n "${{ inputs.status-page-url }}" ]]; then + STATUS_HTML="

For updates, please visit our status page.

" + sed -i "s|#STATUS_PAGE#|${STATUS_HTML}|g" maintenance_page/html/index.html + else + sed -i "s|#STATUS_PAGE#||g" maintenance_page/html/index.html fi + + # Handle contact email + if [[ -n "${{ inputs.contact-email }}" ]]; then + CONTACT_HTML="
  • Email: ${{ inputs.contact-email }}
  • " + CONTACT_HTML="${CONTACT_HTML}
  • We aim to respond within 5 working days, or one working day for more urgent queries
  • " + sed -i "s|#CONTACT_INFO#|${CONTACT_HTML}|g" maintenance_page/html/index.html + else + # If no email provided, remove the entire Get help section + sed -i '/

    Get help<\/h2>/,/<\/ul>/c\' maintenance_page/html/index.html + fi + + echo "Customizations applied successfully" - name: Build and push docker image if: inputs.mode == 'enable' @@ -150,15 +162,16 @@ runs: uses: DFE-Digital/github-actions/build-docker-image@master with: github-token: ${{ inputs.github-token }} - dockerfile-path: ${{ env.TEMP_BUILD_DIR }}/maintenance_build/Dockerfile + dockerfile-path: maintenance_page/Dockerfile docker-repository: ${{ inputs.docker-repository }} - context: ${{ env.TEMP_BUILD_DIR }}/maintenance_build + context: maintenance_page - - name: Clean up temp directory - if: inputs.mode == 'enable' + - name: Clean up maintenance page directory + if: inputs.mode == 'enable' && always() shell: bash run: | - rm -rf $TEMP_BUILD_DIR + rm -rf maintenance_page + echo "Maintenance page directory cleaned up" - name: Enable maintenance mode if: inputs.mode == 'enable' @@ -177,11 +190,32 @@ runs: shell: bash run: | NOW=$(TZ=Europe/London date +"%F %R") - echo 'MAINTENANCE PAGE ${{ inputs.mode }}d!' >> $GITHUB_STEP_SUMMARY - echo 'ENV: ${{ inputs.environment }}' >> $GITHUB_STEP_SUMMARY - echo "AT : ${NOW}" >> $GITHUB_STEP_SUMMARY + echo '## 🚧 Maintenance Page ${{ inputs.mode }}d!' >> $GITHUB_STEP_SUMMARY + echo '' >> $GITHUB_STEP_SUMMARY + echo '| Field | Value |' >> $GITHUB_STEP_SUMMARY + echo '|-------|-------|' >> $GITHUB_STEP_SUMMARY + echo "| **Environment** | ${{ inputs.environment }} |" >> $GITHUB_STEP_SUMMARY + echo "| **Time** | ${NOW} (London) |" >> $GITHUB_STEP_SUMMARY if [[ "${{ inputs.mode }}" == "enable" ]]; then - TEMP_URLS=$(awk '/name:.*cloud/ {print $2}' ./maintenance_page/manifests/${{ inputs.environment }}/ingress_temp*.yml) - echo 'TEMP URLS:' >> $GITHUB_STEP_SUMMARY - echo "${TEMP_URLS}" >> $GITHUB_STEP_SUMMARY + echo "| **Service** | ${{ inputs.service-name }} |" >> $GITHUB_STEP_SUMMARY + echo "| **Message** | ${{ inputs.maintenance-message }} |" >> $GITHUB_STEP_SUMMARY + if [[ -n "${{ inputs.estimated-return-time }}" ]]; then + echo "| **Estimated Return** | ${{ inputs.estimated-return-time }} |" >> $GITHUB_STEP_SUMMARY + fi + if [[ -n "${{ inputs.status-page-url }}" ]]; then + echo "| **Status Page** | [${{ inputs.status-page-url }}](${{ inputs.status-page-url }}) |" >> $GITHUB_STEP_SUMMARY + fi + if [[ -n "${{ inputs.contact-email }}" ]]; then + echo "| **Contact** | ${{ inputs.contact-email }} |" >> $GITHUB_STEP_SUMMARY + fi + echo '' >> $GITHUB_STEP_SUMMARY + + # Check for temp URLs if available + if [[ -f ./maintenance_page/manifests/${{ inputs.environment }}/ingress_temp*.yml ]]; then + echo '### Temporary URLs' >> $GITHUB_STEP_SUMMARY + TEMP_URLS=$(awk '/name:.*cloud/ {print "- " $2}' ./maintenance_page/manifests/${{ inputs.environment }}/ingress_temp*.yml 2>/dev/null || true) + if [[ -n "$TEMP_URLS" ]]; then + echo "$TEMP_URLS" >> $GITHUB_STEP_SUMMARY + fi + fi fi \ No newline at end of file From 980f12aa64af4d4af54701d5e262bc3ea9821d6b Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 11:49:27 +0100 Subject: [PATCH 03/19] debug --- maintenance/action-v2.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/maintenance/action-v2.yml b/maintenance/action-v2.yml index 7f3e7d5..f60dcf0 100644 --- a/maintenance/action-v2.yml +++ b/maintenance/action-v2.yml @@ -94,6 +94,8 @@ runs: shell: bash run: | echo "Fetching maintenance page templates from teacher-services-cloud..." + echo "Current directory: $(pwd)" + echo "GitHub workspace: ${{ github.workspace }}" # Remove any existing maintenance_page directory and create fresh rm -rf maintenance_page @@ -101,6 +103,7 @@ runs: # Create temp directory for cloning TEMP_DIR=$(mktemp -d) + echo "Temp directory: $TEMP_DIR" # Clone only the templates directory (sparse checkout) cd $TEMP_DIR @@ -109,9 +112,17 @@ runs: git sparse-checkout set templates/new_service/maintenance_page git checkout ${{ inputs.template-ref }} + # Debug: Check what files we have + echo "Files in template directory:" + ls -la templates/new_service/maintenance_page/ + # Copy template files to the expected location in workspace cp -r templates/new_service/maintenance_page/* ${{ github.workspace }}/maintenance_page/ + # Debug: Verify files were copied + echo "Files in maintenance_page directory:" + ls -la ${{ github.workspace }}/maintenance_page/ + # Clean up temp directory rm -rf $TEMP_DIR From bfb3edb500d87d42e5609d7e44219413906b3ee8 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 11:55:35 +0100 Subject: [PATCH 04/19] separate directory --- maintenance-v2/action.yml | 244 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 maintenance-v2/action.yml diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml new file mode 100644 index 0000000..5b956d2 --- /dev/null +++ b/maintenance-v2/action.yml @@ -0,0 +1,244 @@ +name: Maintenance V2 +description: Enable or disable maintenance for a service with centralized templates +inputs: + # Required inputs + environment: + description: Name of the app environment + required: true + mode: + description: Maintenance mode to implement, either enable or disable + required: true + + # Azure authentication inputs + azure-credentials: + description: 'JSON string containing service principal credentials' + required: false + default: '' + azure-client-id: + description: Azure client ID when using OIDC + required: false + default: '' + azure-subscription-id: + description: Azure subscription ID when using OIDC + required: false + default: '' + azure-tenant-id: + description: Azure tenant ID when using OIDC + required: false + default: '' + + # Docker configuration + docker-repository: + description: Name of the maint app docker repository + required: false + github-token: + description: GitHub token for repository access + required: false + + # Customization inputs (all optional with sensible defaults) + service-name: + description: Human-readable service name for the maintenance page + required: false + default: 'This service' + + maintenance-message: + description: Custom maintenance message to display to users + required: false + default: 'We are currently carrying out essential maintenance. Please try again later.' + + contact-email: + description: Support contact email to display + required: false + default: '' + + estimated-return-time: + description: Estimated time when service will be back (e.g., "15:00" or "3:00 PM") + required: false + default: '' + + status-page-url: + description: URL to external status page for updates + required: false + default: '' + + template-ref: + description: Branch/tag/commit of teacher-services-cloud repository to use + required: false + default: 'main' + +runs: + using: composite + steps: + - uses: azure/login@v2 + if: inputs.azure-credentials != '' + with: + creds: ${{ inputs.azure-credentials }} + + - uses: azure/login@v2 + if: inputs.azure-credentials == '' + with: + client-id: ${{ inputs.azure-client-id }} + tenant-id: ${{ inputs.azure-tenant-id }} + subscription-id: ${{ inputs.azure-subscription-id }} + + - name: Set ARM and kubelogin environment + uses: DFE-Digital/github-actions/set-kubelogin-environment@master + with: + azure-credentials: ${{ inputs.azure-credentials }} + azure-client-id: ${{ inputs.azure-client-id }} + azure-tenant-id: ${{ inputs.azure-tenant-id }} + azure-subscription-id: ${{ inputs.azure-subscription-id }} + + - name: Fetch and customize maintenance page templates + if: inputs.mode == 'enable' + shell: bash + run: | + echo "Fetching maintenance page templates from teacher-services-cloud..." + echo "Current directory: $(pwd)" + echo "GitHub workspace: ${{ github.workspace }}" + + # Remove any existing maintenance_page directory and create fresh + rm -rf maintenance_page + mkdir -p maintenance_page + + # Create temp directory for cloning + TEMP_DIR=$(mktemp -d) + echo "Temp directory: $TEMP_DIR" + + # Clone only the templates directory (sparse checkout) + cd $TEMP_DIR + git clone --filter=blob:none --sparse https://github.com/DFE-Digital/teacher-services-cloud.git + cd teacher-services-cloud + git sparse-checkout set templates/new_service/maintenance_page + git checkout ${{ inputs.template-ref }} + + # Debug: Check what files we have + echo "Files in template directory:" + ls -la templates/new_service/maintenance_page/ + + # Copy template files to the expected location in workspace + cp -r templates/new_service/maintenance_page/* ${{ github.workspace }}/maintenance_page/ + + # Debug: Verify files were copied + echo "Files in maintenance_page directory:" + ls -la ${{ github.workspace }}/maintenance_page/ + + # Clean up temp directory + rm -rf $TEMP_DIR + + echo "Templates fetched successfully" + + # Apply customizations + echo "Applying service customizations..." + cd ${{ github.workspace }} + + # Replace service name placeholder + sed -i "s/#SERVICE_PRETTY#/${{ inputs.service-name }}/g" maintenance_page/html/index.html + + # Replace maintenance message placeholder + ESCAPED_MESSAGE=$(echo "${{ inputs.maintenance-message }}" | sed 's/[[\.*^$()+?{|]/\\&/g') + sed -i "s/#MAINTENANCE_MESSAGE#/${ESCAPED_MESSAGE}/g" maintenance_page/html/index.html + + # Handle estimated return time + if [[ -n "${{ inputs.estimated-return-time }}" ]]; then + RETURN_HTML="

    We expect the service to be available again at: ${{ inputs.estimated-return-time }}

    " + sed -i "s|#ESTIMATED_RETURN#|${RETURN_HTML}|g" maintenance_page/html/index.html + else + sed -i "s|#ESTIMATED_RETURN#||g" maintenance_page/html/index.html + fi + + # Handle status page URL + if [[ -n "${{ inputs.status-page-url }}" ]]; then + STATUS_HTML="

    For updates, please visit our status page.

    " + sed -i "s|#STATUS_PAGE#|${STATUS_HTML}|g" maintenance_page/html/index.html + else + sed -i "s|#STATUS_PAGE#||g" maintenance_page/html/index.html + fi + + # Handle contact email + if [[ -n "${{ inputs.contact-email }}" ]]; then + CONTACT_HTML="
  • Email: ${{ inputs.contact-email }}
  • " + CONTACT_HTML="${CONTACT_HTML}
  • We aim to respond within 5 working days, or one working day for more urgent queries
  • " + sed -i "s|#CONTACT_INFO#|${CONTACT_HTML}|g" maintenance_page/html/index.html + else + # If no email provided, remove the entire Get help section + sed -i '/

    Get help<\/h2>/,/<\/ul>/c\' maintenance_page/html/index.html + fi + + echo "Customizations applied successfully" + + # Also need to replace placeholders in the manifests + # Get the service name from the docker repository (e.g., "teacher_success" from "ghcr.io/dfe-digital/teacher-success-maintenance") + SERVICE_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's/.*\/\([^-]*\)-maintenance$/\1/' | sed 's/-/_/g') + echo "Service name for manifests: $SERVICE_NAME" + + # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) + sed -i "s/#SERVICE_NAME#/${SERVICE_NAME}/g" maintenance_page/manifests/maintenance/deployment_maintenance.yml.tmpl + sed -i "s|#DOCKER_REPOSITORY#|${{ inputs.docker-repository }}|g" maintenance_page/manifests/maintenance/deployment_maintenance.yml.tmpl + + # Replace placeholders in service manifest + sed -i "s/#SERVICE_NAME#/${SERVICE_NAME}/g" maintenance_page/manifests/maintenance/service_maintenance.yml + + echo "Manifest placeholders replaced" + + - name: Build and push docker image + if: inputs.mode == 'enable' + id: build-image + uses: DFE-Digital/github-actions/build-docker-image@master + with: + github-token: ${{ inputs.github-token }} + dockerfile-path: maintenance_page/Dockerfile + docker-repository: ${{ inputs.docker-repository }} + context: maintenance_page + + # Note: We don't clean up maintenance_page directory here because: + # 1. The scripts directory is needed by the Makefile for failover.sh + # 2. The manifests directory might be needed for checking temp URLs + # The directory will be cleaned up when the workflow ends anyway + + - name: Enable maintenance mode + if: inputs.mode == 'enable' + shell: bash + run: make ci ${{ inputs.environment }} maintenance-fail-over + env: + MAINTENANCE_IMAGE_TAG: ${{steps.build-image.outputs.tag}} + + - name: Disable maintenance mode + if: inputs.mode == 'disable' + shell: bash + run: make ci ${{ inputs.environment }} disable-maintenance + + - name: Maintenance Summary + if: success() + shell: bash + run: | + NOW=$(TZ=Europe/London date +"%F %R") + echo '## 🚧 Maintenance Page ${{ inputs.mode }}d!' >> $GITHUB_STEP_SUMMARY + echo '' >> $GITHUB_STEP_SUMMARY + echo '| Field | Value |' >> $GITHUB_STEP_SUMMARY + echo '|-------|-------|' >> $GITHUB_STEP_SUMMARY + echo "| **Environment** | ${{ inputs.environment }} |" >> $GITHUB_STEP_SUMMARY + echo "| **Time** | ${NOW} (London) |" >> $GITHUB_STEP_SUMMARY + if [[ "${{ inputs.mode }}" == "enable" ]]; then + echo "| **Service** | ${{ inputs.service-name }} |" >> $GITHUB_STEP_SUMMARY + echo "| **Message** | ${{ inputs.maintenance-message }} |" >> $GITHUB_STEP_SUMMARY + if [[ -n "${{ inputs.estimated-return-time }}" ]]; then + echo "| **Estimated Return** | ${{ inputs.estimated-return-time }} |" >> $GITHUB_STEP_SUMMARY + fi + if [[ -n "${{ inputs.status-page-url }}" ]]; then + echo "| **Status Page** | [${{ inputs.status-page-url }}](${{ inputs.status-page-url }}) |" >> $GITHUB_STEP_SUMMARY + fi + if [[ -n "${{ inputs.contact-email }}" ]]; then + echo "| **Contact** | ${{ inputs.contact-email }} |" >> $GITHUB_STEP_SUMMARY + fi + echo '' >> $GITHUB_STEP_SUMMARY + + # Check for temp URLs if available + if [[ -f ./maintenance_page/manifests/${{ inputs.environment }}/ingress_temp*.yml ]]; then + echo '### Temporary URLs' >> $GITHUB_STEP_SUMMARY + TEMP_URLS=$(awk '/name:.*cloud/ {print "- " $2}' ./maintenance_page/manifests/${{ inputs.environment }}/ingress_temp*.yml 2>/dev/null || true) + if [[ -n "$TEMP_URLS" ]]; then + echo "$TEMP_URLS" >> $GITHUB_STEP_SUMMARY + fi + fi + fi \ No newline at end of file From fb401fc7d082627b6a0ed75a31f78f0ab843ae31 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 12:08:38 +0100 Subject: [PATCH 05/19] Fix maintenance-v2 action to replace manifest placeholders - Add SERVICE_NAME extraction from docker repository - Replace placeholders in deployment and service manifests - Keep scripts and manifests for Makefile usage - Don't clean up maintenance_page directory until workflow ends --- maintenance/action-v2.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/maintenance/action-v2.yml b/maintenance/action-v2.yml index f60dcf0..5b956d2 100644 --- a/maintenance/action-v2.yml +++ b/maintenance/action-v2.yml @@ -166,6 +166,20 @@ runs: fi echo "Customizations applied successfully" + + # Also need to replace placeholders in the manifests + # Get the service name from the docker repository (e.g., "teacher_success" from "ghcr.io/dfe-digital/teacher-success-maintenance") + SERVICE_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's/.*\/\([^-]*\)-maintenance$/\1/' | sed 's/-/_/g') + echo "Service name for manifests: $SERVICE_NAME" + + # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) + sed -i "s/#SERVICE_NAME#/${SERVICE_NAME}/g" maintenance_page/manifests/maintenance/deployment_maintenance.yml.tmpl + sed -i "s|#DOCKER_REPOSITORY#|${{ inputs.docker-repository }}|g" maintenance_page/manifests/maintenance/deployment_maintenance.yml.tmpl + + # Replace placeholders in service manifest + sed -i "s/#SERVICE_NAME#/${SERVICE_NAME}/g" maintenance_page/manifests/maintenance/service_maintenance.yml + + echo "Manifest placeholders replaced" - name: Build and push docker image if: inputs.mode == 'enable' @@ -177,12 +191,10 @@ runs: docker-repository: ${{ inputs.docker-repository }} context: maintenance_page - - name: Clean up maintenance page directory - if: inputs.mode == 'enable' && always() - shell: bash - run: | - rm -rf maintenance_page - echo "Maintenance page directory cleaned up" + # Note: We don't clean up maintenance_page directory here because: + # 1. The scripts directory is needed by the Makefile for failover.sh + # 2. The manifests directory might be needed for checking temp URLs + # The directory will be cleaned up when the workflow ends anyway - name: Enable maintenance mode if: inputs.mode == 'enable' From 8effd38159ad357917e6cfc1ae418531d34fc425 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 12:15:02 +0100 Subject: [PATCH 06/19] Fix SERVICE_NAME extraction from docker repository - Extract just the repository name first, then remove -maintenance suffix - This properly handles ghcr.io/dfe-digital/teacher-success-maintenance -> teacher_success --- maintenance-v2/action.yml | 4 +++- maintenance/action-v2.yml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 5b956d2..0611336 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -169,7 +169,9 @@ runs: # Also need to replace placeholders in the manifests # Get the service name from the docker repository (e.g., "teacher_success" from "ghcr.io/dfe-digital/teacher-success-maintenance") - SERVICE_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's/.*\/\([^-]*\)-maintenance$/\1/' | sed 's/-/_/g') + # First extract just the repo name, then get the service part + REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') + SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//' | sed 's/-/_/g') echo "Service name for manifests: $SERVICE_NAME" # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) diff --git a/maintenance/action-v2.yml b/maintenance/action-v2.yml index 5b956d2..0611336 100644 --- a/maintenance/action-v2.yml +++ b/maintenance/action-v2.yml @@ -169,7 +169,9 @@ runs: # Also need to replace placeholders in the manifests # Get the service name from the docker repository (e.g., "teacher_success" from "ghcr.io/dfe-digital/teacher-success-maintenance") - SERVICE_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's/.*\/\([^-]*\)-maintenance$/\1/' | sed 's/-/_/g') + # First extract just the repo name, then get the service part + REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') + SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//' | sed 's/-/_/g') echo "Service name for manifests: $SERVICE_NAME" # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) From 40bc40217e55d0c62855f1b13de3014ce86de76d Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 12:19:57 +0100 Subject: [PATCH 07/19] Fix SERVICE_NAME to use hyphens for Kubernetes compatibility - Keep hyphens in service name (teacher-success not teacher_success) - Kubernetes requires RFC 1123 compliance (lowercase, hyphens, no underscores) --- maintenance-v2/action.yml | 4 ++-- maintenance/action-v2.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 0611336..6a994f6 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -168,10 +168,10 @@ runs: echo "Customizations applied successfully" # Also need to replace placeholders in the manifests - # Get the service name from the docker repository (e.g., "teacher_success" from "ghcr.io/dfe-digital/teacher-success-maintenance") + # Get the service name from the docker repository (e.g., "teacher-success" from "ghcr.io/dfe-digital/teacher-success-maintenance") # First extract just the repo name, then get the service part REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') - SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//' | sed 's/-/_/g') + SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//') echo "Service name for manifests: $SERVICE_NAME" # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) diff --git a/maintenance/action-v2.yml b/maintenance/action-v2.yml index 0611336..6a994f6 100644 --- a/maintenance/action-v2.yml +++ b/maintenance/action-v2.yml @@ -168,10 +168,10 @@ runs: echo "Customizations applied successfully" # Also need to replace placeholders in the manifests - # Get the service name from the docker repository (e.g., "teacher_success" from "ghcr.io/dfe-digital/teacher-success-maintenance") + # Get the service name from the docker repository (e.g., "teacher-success" from "ghcr.io/dfe-digital/teacher-success-maintenance") # First extract just the repo name, then get the service part REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') - SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//' | sed 's/-/_/g') + SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//') echo "Service name for manifests: $SERVICE_NAME" # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) From c87c692e99bc4013defb846fbdd3edefe5e8205c Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 12:23:52 +0100 Subject: [PATCH 08/19] Fix manifest copying to preserve service-specific ingress files - Only copy centralized files (Dockerfile, nginx, html, scripts) - Copy maintenance manifests (deployment/service templates) - Don't copy environment-specific manifests (staging/production) - Services keep their own environment-specific ingress files --- maintenance-v2/action.yml | 13 +++++++++++-- maintenance/action-v2.yml | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 6a994f6..79f9a39 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -116,8 +116,17 @@ runs: echo "Files in template directory:" ls -la templates/new_service/maintenance_page/ - # Copy template files to the expected location in workspace - cp -r templates/new_service/maintenance_page/* ${{ github.workspace }}/maintenance_page/ + # Copy template files to the expected location in workspace + # Copy Docker/nginx/html/scripts from templates + cp templates/new_service/maintenance_page/Dockerfile ${{ github.workspace }}/maintenance_page/ + cp templates/new_service/maintenance_page/nginx.conf ${{ github.workspace }}/maintenance_page/ + cp -r templates/new_service/maintenance_page/html ${{ github.workspace }}/maintenance_page/ + cp -r templates/new_service/maintenance_page/scripts ${{ github.workspace }}/maintenance_page/ + + # Only copy the maintenance manifests directory (contains deployment and service templates) + # Environment-specific manifests (staging/production) should already exist in the service repo + mkdir -p ${{ github.workspace }}/maintenance_page/manifests/ + cp -r templates/new_service/maintenance_page/manifests/maintenance ${{ github.workspace }}/maintenance_page/manifests/ # Debug: Verify files were copied echo "Files in maintenance_page directory:" diff --git a/maintenance/action-v2.yml b/maintenance/action-v2.yml index 6a994f6..79f9a39 100644 --- a/maintenance/action-v2.yml +++ b/maintenance/action-v2.yml @@ -116,8 +116,17 @@ runs: echo "Files in template directory:" ls -la templates/new_service/maintenance_page/ - # Copy template files to the expected location in workspace - cp -r templates/new_service/maintenance_page/* ${{ github.workspace }}/maintenance_page/ + # Copy template files to the expected location in workspace + # Copy Docker/nginx/html/scripts from templates + cp templates/new_service/maintenance_page/Dockerfile ${{ github.workspace }}/maintenance_page/ + cp templates/new_service/maintenance_page/nginx.conf ${{ github.workspace }}/maintenance_page/ + cp -r templates/new_service/maintenance_page/html ${{ github.workspace }}/maintenance_page/ + cp -r templates/new_service/maintenance_page/scripts ${{ github.workspace }}/maintenance_page/ + + # Only copy the maintenance manifests directory (contains deployment and service templates) + # Environment-specific manifests (staging/production) should already exist in the service repo + mkdir -p ${{ github.workspace }}/maintenance_page/manifests/ + cp -r templates/new_service/maintenance_page/manifests/maintenance ${{ github.workspace }}/maintenance_page/manifests/ # Debug: Verify files were copied echo "Files in maintenance_page directory:" From af6d68c1fbb1942e388e929b293f83d522934a17 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 13:39:42 +0100 Subject: [PATCH 09/19] Fix maintenance-v2 action to preserve environment-specific manifests The action was removing the entire maintenance_page directory and only copying the maintenance subdirectory from templates. This caused the failover script to fail because it couldn't find the environment-specific ingress files. Now the action: 1. Preserves any existing environment-specific manifests before cleanup 2. Restores them after fetching templates 3. Provides clear error messages if manifests are missing --- maintenance-v2/action.yml | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 79f9a39..b958c17 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -97,6 +97,18 @@ runs: echo "Current directory: $(pwd)" echo "GitHub workspace: ${{ github.workspace }}" + # Save any existing environment-specific manifests before removing directory + if [ -d "maintenance_page/manifests" ]; then + echo "Preserving existing environment-specific manifests..." + mkdir -p /tmp/preserved_manifests + if [ -d "maintenance_page/manifests/staging" ]; then + cp -r maintenance_page/manifests/staging /tmp/preserved_manifests/ + fi + if [ -d "maintenance_page/manifests/production" ]; then + cp -r maintenance_page/manifests/production /tmp/preserved_manifests/ + fi + fi + # Remove any existing maintenance_page directory and create fresh rm -rf maintenance_page mkdir -p maintenance_page @@ -124,10 +136,25 @@ runs: cp -r templates/new_service/maintenance_page/scripts ${{ github.workspace }}/maintenance_page/ # Only copy the maintenance manifests directory (contains deployment and service templates) - # Environment-specific manifests (staging/production) should already exist in the service repo mkdir -p ${{ github.workspace }}/maintenance_page/manifests/ cp -r templates/new_service/maintenance_page/manifests/maintenance ${{ github.workspace }}/maintenance_page/manifests/ + # Restore preserved environment-specific manifests if they existed + if [ -d "/tmp/preserved_manifests" ]; then + echo "Restoring preserved environment-specific manifests..." + cp -r /tmp/preserved_manifests/* ${{ github.workspace }}/maintenance_page/manifests/ + rm -rf /tmp/preserved_manifests + else + echo "No preserved manifests found, checking if they exist in the repo..." + # If no preserved manifests, check if they exist in the current repo + # (they should be committed to the service repository) + if [ -d "maintenance_page/manifests/staging" ] || [ -d "maintenance_page/manifests/production" ]; then + echo "ERROR: Environment-specific manifests should be in the repository but weren't found" + echo "Please ensure maintenance_page/manifests/staging/ and maintenance_page/manifests/production/ directories exist in your repository" + exit 1 + fi + fi + # Debug: Verify files were copied echo "Files in maintenance_page directory:" ls -la ${{ github.workspace }}/maintenance_page/ From bdf7cc120bed2f20d118bb56520c87310f83287c Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 13:50:36 +0100 Subject: [PATCH 10/19] Simplify maintenance-v2 action to preserve existing manifests and scripts Instead of complex preservation/restoration logic, the action now: 1. Only copies necessary template files (Dockerfile, nginx.conf, html) 2. Leaves existing manifests/staging and manifests/production untouched 3. Leaves existing scripts directory untouched (or copies template as fallback) 4. Only copies the maintenance deployment/service templates This ensures service-specific customizations are preserved. --- maintenance-v2/action.yml | 56 ++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index b958c17..16a9160 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -97,20 +97,7 @@ runs: echo "Current directory: $(pwd)" echo "GitHub workspace: ${{ github.workspace }}" - # Save any existing environment-specific manifests before removing directory - if [ -d "maintenance_page/manifests" ]; then - echo "Preserving existing environment-specific manifests..." - mkdir -p /tmp/preserved_manifests - if [ -d "maintenance_page/manifests/staging" ]; then - cp -r maintenance_page/manifests/staging /tmp/preserved_manifests/ - fi - if [ -d "maintenance_page/manifests/production" ]; then - cp -r maintenance_page/manifests/production /tmp/preserved_manifests/ - fi - fi - - # Remove any existing maintenance_page directory and create fresh - rm -rf maintenance_page + # Create maintenance_page directory if it doesn't exist mkdir -p maintenance_page # Create temp directory for cloning @@ -128,31 +115,34 @@ runs: echo "Files in template directory:" ls -la templates/new_service/maintenance_page/ - # Copy template files to the expected location in workspace - # Copy Docker/nginx/html/scripts from templates + # Copy ONLY the necessary template files (Dockerfile, nginx.conf, html) + # DO NOT touch existing manifests or scripts directories in the service repo cp templates/new_service/maintenance_page/Dockerfile ${{ github.workspace }}/maintenance_page/ cp templates/new_service/maintenance_page/nginx.conf ${{ github.workspace }}/maintenance_page/ + + # Remove existing html directory and copy fresh from template + rm -rf ${{ github.workspace }}/maintenance_page/html cp -r templates/new_service/maintenance_page/html ${{ github.workspace }}/maintenance_page/ - cp -r templates/new_service/maintenance_page/scripts ${{ github.workspace }}/maintenance_page/ - # Only copy the maintenance manifests directory (contains deployment and service templates) - mkdir -p ${{ github.workspace }}/maintenance_page/manifests/ - cp -r templates/new_service/maintenance_page/manifests/maintenance ${{ github.workspace }}/maintenance_page/manifests/ + # Copy the maintenance manifests templates (deployment and service only) + # These go in manifests/maintenance/ directory + mkdir -p ${{ github.workspace }}/maintenance_page/manifests/maintenance + cp -r templates/new_service/maintenance_page/manifests/maintenance/* ${{ github.workspace }}/maintenance_page/manifests/maintenance/ + + # Check if service has the required directories + if [ ! -d "${{ github.workspace }}/maintenance_page/manifests/staging" ] && [ ! -d "${{ github.workspace }}/maintenance_page/manifests/production" ]; then + echo "WARNING: No environment-specific manifests found in maintenance_page/manifests/" + echo "Please ensure your repository contains:" + echo " - maintenance_page/manifests/staging/ (for staging environment)" + echo " - maintenance_page/manifests/production/ (for production environment)" + fi - # Restore preserved environment-specific manifests if they existed - if [ -d "/tmp/preserved_manifests" ]; then - echo "Restoring preserved environment-specific manifests..." - cp -r /tmp/preserved_manifests/* ${{ github.workspace }}/maintenance_page/manifests/ - rm -rf /tmp/preserved_manifests + if [ ! -d "${{ github.workspace }}/maintenance_page/scripts" ]; then + echo "WARNING: No scripts directory found in maintenance_page/" + echo "Using template scripts as fallback..." + cp -r templates/new_service/maintenance_page/scripts ${{ github.workspace }}/maintenance_page/ else - echo "No preserved manifests found, checking if they exist in the repo..." - # If no preserved manifests, check if they exist in the current repo - # (they should be committed to the service repository) - if [ -d "maintenance_page/manifests/staging" ] || [ -d "maintenance_page/manifests/production" ]; then - echo "ERROR: Environment-specific manifests should be in the repository but weren't found" - echo "Please ensure maintenance_page/manifests/staging/ and maintenance_page/manifests/production/ directories exist in your repository" - exit 1 - fi + echo "Using existing service-specific scripts from maintenance_page/scripts/" fi # Debug: Verify files were copied From 38ccd435f380a02a1e412c1d1f2cc5a3ebcb20c8 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 14:10:57 +0100 Subject: [PATCH 11/19] Add service-short parameter to support custom Kubernetes resource names Some services use short names (e.g., 'teach') instead of full names (e.g., 'teacher-success') for their Kubernetes resources. This change: 1. Adds optional service-short input parameter 2. Uses service-short for manifest placeholders if provided 3. Falls back to extracting from docker repository name if not provided This ensures compatibility with existing service naming conventions. --- maintenance-v2/action.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 16a9160..b53dd02 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -41,6 +41,11 @@ inputs: required: false default: 'This service' + service-short: + description: Short service name used in Kubernetes resources (e.g., 'teach' instead of 'teacher-success') + required: false + default: '' + maintenance-message: description: Custom maintenance message to display to users required: false @@ -194,10 +199,17 @@ runs: echo "Customizations applied successfully" # Also need to replace placeholders in the manifests - # Get the service name from the docker repository (e.g., "teacher-success" from "ghcr.io/dfe-digital/teacher-success-maintenance") - # First extract just the repo name, then get the service part - REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') - SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//') + # Use service-short if provided, otherwise extract from docker repository + if [[ -n "${{ inputs.service-short }}" ]]; then + SERVICE_NAME="${{ inputs.service-short }}" + echo "Using provided service-short for manifests: $SERVICE_NAME" + else + # Get the service name from the docker repository (e.g., "teacher-success" from "ghcr.io/dfe-digital/teacher-success-maintenance") + # First extract just the repo name, then get the service part + REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') + SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//') + echo "Extracted service name from docker repository: $SERVICE_NAME" + fi echo "Service name for manifests: $SERVICE_NAME" # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) From c1bd9c4733be8e54d2c1aa5d2d5b5814cd3aa320 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 14:39:29 +0100 Subject: [PATCH 12/19] Don't overwrite existing maintenance manifests The action was always copying maintenance manifests from templates, overwriting any customizations in the service repository. Now it only copies templates if the maintenance directory doesn't already exist. This allows services to customize their deployment and service manifests while still getting the benefit of centralized templates for new services. --- maintenance-v2/action.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index b53dd02..2491eb1 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -130,9 +130,14 @@ runs: cp -r templates/new_service/maintenance_page/html ${{ github.workspace }}/maintenance_page/ # Copy the maintenance manifests templates (deployment and service only) - # These go in manifests/maintenance/ directory - mkdir -p ${{ github.workspace }}/maintenance_page/manifests/maintenance - cp -r templates/new_service/maintenance_page/manifests/maintenance/* ${{ github.workspace }}/maintenance_page/manifests/maintenance/ + # Only copy if they don't already exist in the service repository + if [ ! -d "${{ github.workspace }}/maintenance_page/manifests/maintenance" ]; then + echo "Copying maintenance manifest templates from teacher-services-cloud..." + mkdir -p ${{ github.workspace }}/maintenance_page/manifests/maintenance + cp -r templates/new_service/maintenance_page/manifests/maintenance/* ${{ github.workspace }}/maintenance_page/manifests/maintenance/ + else + echo "Using existing maintenance manifests from service repository" + fi # Check if service has the required directories if [ ! -d "${{ github.workspace }}/maintenance_page/manifests/staging" ] && [ ! -d "${{ github.workspace }}/maintenance_page/manifests/production" ]; then From f050facfb238f415a699b5c54c1f100071d3166d Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 15:13:16 +0100 Subject: [PATCH 13/19] Remove old maintenance v2 files These files were an earlier iteration of the maintenance v2 action. The new implementation is in the maintenance-v2/ directory which is cleaner and more maintainable. --- maintenance/README-v2.md | 111 ----------------- maintenance/action-v2.yml | 255 -------------------------------------- 2 files changed, 366 deletions(-) delete mode 100644 maintenance/README-v2.md delete mode 100644 maintenance/action-v2.yml diff --git a/maintenance/README-v2.md b/maintenance/README-v2.md deleted file mode 100644 index ecefb61..0000000 --- a/maintenance/README-v2.md +++ /dev/null @@ -1,111 +0,0 @@ -# Maintenance GitHub Action V2 - -This action enables or disables maintenance mode for services using centralized templates from the `teacher-services-cloud` repository. - -## Key Features - -- **Centralized Templates**: Uses templates from `teacher-services-cloud` repository -- **Simple Customization**: All customization through action inputs (no config files) -- **Automatic Updates**: Template improvements automatically available to all services -- **Backward Compatible**: Easy migration path from v1 - -## Usage Examples - -### Minimal Configuration -```yaml -- name: Enable maintenance - uses: DFE-Digital/github-actions/maintenance/action-v2@main - with: - environment: production - mode: enable - docker-repository: myservice-maintenance - azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} -``` - -### Standard Configuration -```yaml -- name: Enable maintenance - uses: DFE-Digital/github-actions/maintenance/action-v2@main - with: - environment: production - mode: enable - docker-repository: myservice-maintenance - azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} - service-name: "Apply for Teacher Training" - maintenance-message: "We're upgrading our systems to serve you better." - contact-email: "support@education.gov.uk" -``` - -### Full Configuration -```yaml -- name: Enable maintenance - uses: DFE-Digital/github-actions/maintenance/action-v2@main - with: - environment: production - mode: enable - docker-repository: myservice-maintenance - azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} - service-name: "Apply for Teacher Training" - maintenance-message: "We are performing scheduled maintenance to improve system performance." - contact-email: "support@apply-for-teacher-training.service.gov.uk" - estimated-return-time: "15:00 GMT" - status-page-url: "https://status.education.gov.uk" - template-ref: "v2.1.0" # Pin to specific version -``` - -### Disabling Maintenance -```yaml -- name: Disable maintenance - uses: DFE-Digital/github-actions/maintenance/action-v2@main - with: - environment: production - mode: disable - azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} -``` - -## How It Works - -1. **Fetches Templates**: Uses sparse checkout to get maintenance page templates from `teacher-services-cloud` -2. **Applies Customizations**: Replaces placeholders in HTML with your input values -3. **Builds Docker Image**: Creates maintenance page container with customized content -4. **Deploys**: Enables or disables maintenance mode using existing infrastructure - -## Migration from V1 - -### Before (V1) -Each service maintains its own `maintenance_page/` directory with: -- Dockerfile -- nginx.conf -- html/index.html -- All assets and styles - -### After (V2) -Services only need to update their workflow file: - -```yaml -# Change from: -uses: DFE-Digital/github-actions/maintenance@main - -# To: -uses: DFE-Digital/github-actions/maintenance/action-v2@main -with: - service-name: "Your Service Name" - maintenance-message: "Your custom message" - contact-email: "your-support@education.gov.uk" -``` - -Then remove the local `maintenance_page/` directory - it's no longer needed! - -## Template Repository - -Templates are maintained at: -``` -github.com/DFE-Digital/teacher-services-cloud -└── templates/new_service/maintenance_page/ - ├── Dockerfile - ├── nginx.conf - └── html/ - └── index.html -``` - -To update the templates for all services, make changes in the teacher-services-cloud repository. \ No newline at end of file diff --git a/maintenance/action-v2.yml b/maintenance/action-v2.yml deleted file mode 100644 index 79f9a39..0000000 --- a/maintenance/action-v2.yml +++ /dev/null @@ -1,255 +0,0 @@ -name: Maintenance V2 -description: Enable or disable maintenance for a service with centralized templates -inputs: - # Required inputs - environment: - description: Name of the app environment - required: true - mode: - description: Maintenance mode to implement, either enable or disable - required: true - - # Azure authentication inputs - azure-credentials: - description: 'JSON string containing service principal credentials' - required: false - default: '' - azure-client-id: - description: Azure client ID when using OIDC - required: false - default: '' - azure-subscription-id: - description: Azure subscription ID when using OIDC - required: false - default: '' - azure-tenant-id: - description: Azure tenant ID when using OIDC - required: false - default: '' - - # Docker configuration - docker-repository: - description: Name of the maint app docker repository - required: false - github-token: - description: GitHub token for repository access - required: false - - # Customization inputs (all optional with sensible defaults) - service-name: - description: Human-readable service name for the maintenance page - required: false - default: 'This service' - - maintenance-message: - description: Custom maintenance message to display to users - required: false - default: 'We are currently carrying out essential maintenance. Please try again later.' - - contact-email: - description: Support contact email to display - required: false - default: '' - - estimated-return-time: - description: Estimated time when service will be back (e.g., "15:00" or "3:00 PM") - required: false - default: '' - - status-page-url: - description: URL to external status page for updates - required: false - default: '' - - template-ref: - description: Branch/tag/commit of teacher-services-cloud repository to use - required: false - default: 'main' - -runs: - using: composite - steps: - - uses: azure/login@v2 - if: inputs.azure-credentials != '' - with: - creds: ${{ inputs.azure-credentials }} - - - uses: azure/login@v2 - if: inputs.azure-credentials == '' - with: - client-id: ${{ inputs.azure-client-id }} - tenant-id: ${{ inputs.azure-tenant-id }} - subscription-id: ${{ inputs.azure-subscription-id }} - - - name: Set ARM and kubelogin environment - uses: DFE-Digital/github-actions/set-kubelogin-environment@master - with: - azure-credentials: ${{ inputs.azure-credentials }} - azure-client-id: ${{ inputs.azure-client-id }} - azure-tenant-id: ${{ inputs.azure-tenant-id }} - azure-subscription-id: ${{ inputs.azure-subscription-id }} - - - name: Fetch and customize maintenance page templates - if: inputs.mode == 'enable' - shell: bash - run: | - echo "Fetching maintenance page templates from teacher-services-cloud..." - echo "Current directory: $(pwd)" - echo "GitHub workspace: ${{ github.workspace }}" - - # Remove any existing maintenance_page directory and create fresh - rm -rf maintenance_page - mkdir -p maintenance_page - - # Create temp directory for cloning - TEMP_DIR=$(mktemp -d) - echo "Temp directory: $TEMP_DIR" - - # Clone only the templates directory (sparse checkout) - cd $TEMP_DIR - git clone --filter=blob:none --sparse https://github.com/DFE-Digital/teacher-services-cloud.git - cd teacher-services-cloud - git sparse-checkout set templates/new_service/maintenance_page - git checkout ${{ inputs.template-ref }} - - # Debug: Check what files we have - echo "Files in template directory:" - ls -la templates/new_service/maintenance_page/ - - # Copy template files to the expected location in workspace - # Copy Docker/nginx/html/scripts from templates - cp templates/new_service/maintenance_page/Dockerfile ${{ github.workspace }}/maintenance_page/ - cp templates/new_service/maintenance_page/nginx.conf ${{ github.workspace }}/maintenance_page/ - cp -r templates/new_service/maintenance_page/html ${{ github.workspace }}/maintenance_page/ - cp -r templates/new_service/maintenance_page/scripts ${{ github.workspace }}/maintenance_page/ - - # Only copy the maintenance manifests directory (contains deployment and service templates) - # Environment-specific manifests (staging/production) should already exist in the service repo - mkdir -p ${{ github.workspace }}/maintenance_page/manifests/ - cp -r templates/new_service/maintenance_page/manifests/maintenance ${{ github.workspace }}/maintenance_page/manifests/ - - # Debug: Verify files were copied - echo "Files in maintenance_page directory:" - ls -la ${{ github.workspace }}/maintenance_page/ - - # Clean up temp directory - rm -rf $TEMP_DIR - - echo "Templates fetched successfully" - - # Apply customizations - echo "Applying service customizations..." - cd ${{ github.workspace }} - - # Replace service name placeholder - sed -i "s/#SERVICE_PRETTY#/${{ inputs.service-name }}/g" maintenance_page/html/index.html - - # Replace maintenance message placeholder - ESCAPED_MESSAGE=$(echo "${{ inputs.maintenance-message }}" | sed 's/[[\.*^$()+?{|]/\\&/g') - sed -i "s/#MAINTENANCE_MESSAGE#/${ESCAPED_MESSAGE}/g" maintenance_page/html/index.html - - # Handle estimated return time - if [[ -n "${{ inputs.estimated-return-time }}" ]]; then - RETURN_HTML="

    We expect the service to be available again at: ${{ inputs.estimated-return-time }}

    " - sed -i "s|#ESTIMATED_RETURN#|${RETURN_HTML}|g" maintenance_page/html/index.html - else - sed -i "s|#ESTIMATED_RETURN#||g" maintenance_page/html/index.html - fi - - # Handle status page URL - if [[ -n "${{ inputs.status-page-url }}" ]]; then - STATUS_HTML="

    For updates, please visit our status page.

    " - sed -i "s|#STATUS_PAGE#|${STATUS_HTML}|g" maintenance_page/html/index.html - else - sed -i "s|#STATUS_PAGE#||g" maintenance_page/html/index.html - fi - - # Handle contact email - if [[ -n "${{ inputs.contact-email }}" ]]; then - CONTACT_HTML="
  • Email: ${{ inputs.contact-email }}
  • " - CONTACT_HTML="${CONTACT_HTML}
  • We aim to respond within 5 working days, or one working day for more urgent queries
  • " - sed -i "s|#CONTACT_INFO#|${CONTACT_HTML}|g" maintenance_page/html/index.html - else - # If no email provided, remove the entire Get help section - sed -i '/

    Get help<\/h2>/,/<\/ul>/c\' maintenance_page/html/index.html - fi - - echo "Customizations applied successfully" - - # Also need to replace placeholders in the manifests - # Get the service name from the docker repository (e.g., "teacher-success" from "ghcr.io/dfe-digital/teacher-success-maintenance") - # First extract just the repo name, then get the service part - REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') - SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//') - echo "Service name for manifests: $SERVICE_NAME" - - # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) - sed -i "s/#SERVICE_NAME#/${SERVICE_NAME}/g" maintenance_page/manifests/maintenance/deployment_maintenance.yml.tmpl - sed -i "s|#DOCKER_REPOSITORY#|${{ inputs.docker-repository }}|g" maintenance_page/manifests/maintenance/deployment_maintenance.yml.tmpl - - # Replace placeholders in service manifest - sed -i "s/#SERVICE_NAME#/${SERVICE_NAME}/g" maintenance_page/manifests/maintenance/service_maintenance.yml - - echo "Manifest placeholders replaced" - - - name: Build and push docker image - if: inputs.mode == 'enable' - id: build-image - uses: DFE-Digital/github-actions/build-docker-image@master - with: - github-token: ${{ inputs.github-token }} - dockerfile-path: maintenance_page/Dockerfile - docker-repository: ${{ inputs.docker-repository }} - context: maintenance_page - - # Note: We don't clean up maintenance_page directory here because: - # 1. The scripts directory is needed by the Makefile for failover.sh - # 2. The manifests directory might be needed for checking temp URLs - # The directory will be cleaned up when the workflow ends anyway - - - name: Enable maintenance mode - if: inputs.mode == 'enable' - shell: bash - run: make ci ${{ inputs.environment }} maintenance-fail-over - env: - MAINTENANCE_IMAGE_TAG: ${{steps.build-image.outputs.tag}} - - - name: Disable maintenance mode - if: inputs.mode == 'disable' - shell: bash - run: make ci ${{ inputs.environment }} disable-maintenance - - - name: Maintenance Summary - if: success() - shell: bash - run: | - NOW=$(TZ=Europe/London date +"%F %R") - echo '## 🚧 Maintenance Page ${{ inputs.mode }}d!' >> $GITHUB_STEP_SUMMARY - echo '' >> $GITHUB_STEP_SUMMARY - echo '| Field | Value |' >> $GITHUB_STEP_SUMMARY - echo '|-------|-------|' >> $GITHUB_STEP_SUMMARY - echo "| **Environment** | ${{ inputs.environment }} |" >> $GITHUB_STEP_SUMMARY - echo "| **Time** | ${NOW} (London) |" >> $GITHUB_STEP_SUMMARY - if [[ "${{ inputs.mode }}" == "enable" ]]; then - echo "| **Service** | ${{ inputs.service-name }} |" >> $GITHUB_STEP_SUMMARY - echo "| **Message** | ${{ inputs.maintenance-message }} |" >> $GITHUB_STEP_SUMMARY - if [[ -n "${{ inputs.estimated-return-time }}" ]]; then - echo "| **Estimated Return** | ${{ inputs.estimated-return-time }} |" >> $GITHUB_STEP_SUMMARY - fi - if [[ -n "${{ inputs.status-page-url }}" ]]; then - echo "| **Status Page** | [${{ inputs.status-page-url }}](${{ inputs.status-page-url }}) |" >> $GITHUB_STEP_SUMMARY - fi - if [[ -n "${{ inputs.contact-email }}" ]]; then - echo "| **Contact** | ${{ inputs.contact-email }} |" >> $GITHUB_STEP_SUMMARY - fi - echo '' >> $GITHUB_STEP_SUMMARY - - # Check for temp URLs if available - if [[ -f ./maintenance_page/manifests/${{ inputs.environment }}/ingress_temp*.yml ]]; then - echo '### Temporary URLs' >> $GITHUB_STEP_SUMMARY - TEMP_URLS=$(awk '/name:.*cloud/ {print "- " $2}' ./maintenance_page/manifests/${{ inputs.environment }}/ingress_temp*.yml 2>/dev/null || true) - if [[ -n "$TEMP_URLS" ]]; then - echo "$TEMP_URLS" >> $GITHUB_STEP_SUMMARY - fi - fi - fi \ No newline at end of file From 9c037876867e1660079575a1f84950a214a629f7 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Wed, 6 Aug 2025 22:35:41 +0100 Subject: [PATCH 14/19] rick suggestions --- maintenance-v2/action.yml | 170 +++++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 68 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 2491eb1..8eef16c 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -35,36 +35,11 @@ inputs: description: GitHub token for repository access required: false - # Customization inputs (all optional with sensible defaults) - service-name: - description: Human-readable service name for the maintenance page + # Config file path (optional - will use maint-config.txt if it exists) + config-file: + description: Path to maintenance configuration file required: false - default: 'This service' - - service-short: - description: Short service name used in Kubernetes resources (e.g., 'teach' instead of 'teacher-success') - required: false - default: '' - - maintenance-message: - description: Custom maintenance message to display to users - required: false - default: 'We are currently carrying out essential maintenance. Please try again later.' - - contact-email: - description: Support contact email to display - required: false - default: '' - - estimated-return-time: - description: Estimated time when service will be back (e.g., "15:00" or "3:00 PM") - required: false - default: '' - - status-page-url: - description: URL to external status page for updates - required: false - default: '' + default: 'maint-config.txt' template-ref: description: Branch/tag/commit of teacher-services-cloud repository to use @@ -94,6 +69,41 @@ runs: azure-tenant-id: ${{ inputs.azure-tenant-id }} azure-subscription-id: ${{ inputs.azure-subscription-id }} + - name: Load maintenance configuration + if: inputs.mode == 'enable' + id: load-config + shell: bash + run: | + echo "Loading maintenance configuration..." + + # Set default values + SERVICE_PRETTY="This service" + MAINTENANCE_MESSAGE="We are currently carrying out essential maintenance. Please try again later." + ESTIMATED_RETURN="" + STATUS_PAGE="" + CONTACT_EMAIL="" + + # Check if config file exists and load it + if [[ -f "${{ inputs.config-file }}" ]]; then + echo "Loading configuration from ${{ inputs.config-file }}" + # Source the config file to load variables + source "${{ inputs.config-file }}" + else + echo "No config file found at ${{ inputs.config-file }}, using defaults" + fi + + # Export variables for use in later steps + echo "service-name=${SERVICE_PRETTY}" >> $GITHUB_OUTPUT + echo "maintenance-message=${MAINTENANCE_MESSAGE}" >> $GITHUB_OUTPUT + echo "estimated-return=${ESTIMATED_RETURN}" >> $GITHUB_OUTPUT + echo "status-page=${STATUS_PAGE}" >> $GITHUB_OUTPUT + echo "contact-email=${CONTACT_EMAIL}" >> $GITHUB_OUTPUT + + echo "Configuration loaded:" + echo " Service: ${SERVICE_PRETTY}" + echo " Message: ${MAINTENANCE_MESSAGE}" + echo " Contact: ${CONTACT_EMAIL}" + - name: Fetch and customize maintenance page templates if: inputs.mode == 'enable' shell: bash @@ -168,53 +178,77 @@ runs: echo "Applying service customizations..." cd ${{ github.workspace }} - # Replace service name placeholder - sed -i "s/#SERVICE_PRETTY#/${{ inputs.service-name }}/g" maintenance_page/html/index.html + # Use environment variables from the config file to replace placeholders + # This is safer than trying to pass complex HTML through sed + + # Create a temporary script to do the replacements using environment variables + cat > replace_placeholders.sh << 'SCRIPT_END' + #!/bin/bash + set -e - # Replace maintenance message placeholder - ESCAPED_MESSAGE=$(echo "${{ inputs.maintenance-message }}" | sed 's/[[\.*^$()+?{|]/\\&/g') - sed -i "s/#MAINTENANCE_MESSAGE#/${ESCAPED_MESSAGE}/g" maintenance_page/html/index.html + # Load the config file + if [[ -f "$1" ]]; then + source "$1" + else + # Set defaults if no config file + SERVICE_PRETTY="This service" + MAINTENANCE_MESSAGE="We are currently carrying out essential maintenance. Please try again later." + ESTIMATED_RETURN="" + STATUS_PAGE="" + CONTACT_EMAIL="" + fi + + # Read the HTML file + HTML_FILE="$2" + + # Use temporary files to avoid issues with special characters + # Export variables for perl to use + export SERVICE_PRETTY + export MAINTENANCE_MESSAGE + export ESTIMATED_RETURN + export STATUS_PAGE + export CONTACT_EMAIL - # Handle estimated return time - if [[ -n "${{ inputs.estimated-return-time }}" ]]; then - RETURN_HTML="

    We expect the service to be available again at: ${{ inputs.estimated-return-time }}

    " - sed -i "s|#ESTIMATED_RETURN#|${RETURN_HTML}|g" maintenance_page/html/index.html + # Replace placeholders using perl with environment variables + perl -i -pe 's/#SERVICE_PRETTY#/$ENV{SERVICE_PRETTY}/g' "$HTML_FILE" + perl -i -pe 's/#MAINTENANCE_MESSAGE#/$ENV{MAINTENANCE_MESSAGE}/g' "$HTML_FILE" + + # Handle optional fields + if [[ -n "${ESTIMATED_RETURN}" ]]; then + perl -i -pe 's|#ESTIMATED_RETURN#|$ENV{ESTIMATED_RETURN}|g' "$HTML_FILE" else - sed -i "s|#ESTIMATED_RETURN#||g" maintenance_page/html/index.html + perl -i -pe 's|#ESTIMATED_RETURN#||g' "$HTML_FILE" fi - # Handle status page URL - if [[ -n "${{ inputs.status-page-url }}" ]]; then - STATUS_HTML="

    For updates, please visit our status page.

    " - sed -i "s|#STATUS_PAGE#|${STATUS_HTML}|g" maintenance_page/html/index.html + if [[ -n "${STATUS_PAGE}" ]]; then + perl -i -pe 's|#STATUS_PAGE#|$ENV{STATUS_PAGE}|g' "$HTML_FILE" else - sed -i "s|#STATUS_PAGE#||g" maintenance_page/html/index.html + perl -i -pe 's|#STATUS_PAGE#||g' "$HTML_FILE" fi # Handle contact email - if [[ -n "${{ inputs.contact-email }}" ]]; then - CONTACT_HTML="
  • Email: ${{ inputs.contact-email }}
  • " + if [[ -n "${CONTACT_EMAIL}" ]]; then + CONTACT_HTML="
  • Email: ${CONTACT_EMAIL}
  • " CONTACT_HTML="${CONTACT_HTML}
  • We aim to respond within 5 working days, or one working day for more urgent queries
  • " - sed -i "s|#CONTACT_INFO#|${CONTACT_HTML}|g" maintenance_page/html/index.html + export CONTACT_HTML + perl -i -pe 's|#CONTACT_INFO#|$ENV{CONTACT_HTML}|g' "$HTML_FILE" else - # If no email provided, remove the entire Get help section - sed -i '/

    Get help<\/h2>/,/<\/ul>/c\' maintenance_page/html/index.html + # Remove the entire Get help section if no email + perl -i -pe 'BEGIN{undef $/;} s/

    Get help<\/h2>.*?<\/ul>//smg' "$HTML_FILE" fi + SCRIPT_END + + chmod +x replace_placeholders.sh + ./replace_placeholders.sh "${{ inputs.config-file }}" "maintenance_page/html/index.html" + rm replace_placeholders.sh echo "Customizations applied successfully" # Also need to replace placeholders in the manifests - # Use service-short if provided, otherwise extract from docker repository - if [[ -n "${{ inputs.service-short }}" ]]; then - SERVICE_NAME="${{ inputs.service-short }}" - echo "Using provided service-short for manifests: $SERVICE_NAME" - else - # Get the service name from the docker repository (e.g., "teacher-success" from "ghcr.io/dfe-digital/teacher-success-maintenance") - # First extract just the repo name, then get the service part - REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') - SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//') - echo "Extracted service name from docker repository: $SERVICE_NAME" - fi + # Extract service name from docker repository (e.g., "teacher-success" from "ghcr.io/dfe-digital/teacher-success-maintenance") + REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') + SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//') + echo "Extracted service name from docker repository: $SERVICE_NAME" echo "Service name for manifests: $SERVICE_NAME" # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) @@ -265,16 +299,16 @@ runs: echo "| **Environment** | ${{ inputs.environment }} |" >> $GITHUB_STEP_SUMMARY echo "| **Time** | ${NOW} (London) |" >> $GITHUB_STEP_SUMMARY if [[ "${{ inputs.mode }}" == "enable" ]]; then - echo "| **Service** | ${{ inputs.service-name }} |" >> $GITHUB_STEP_SUMMARY - echo "| **Message** | ${{ inputs.maintenance-message }} |" >> $GITHUB_STEP_SUMMARY - if [[ -n "${{ inputs.estimated-return-time }}" ]]; then - echo "| **Estimated Return** | ${{ inputs.estimated-return-time }} |" >> $GITHUB_STEP_SUMMARY + echo "| **Service** | ${{ steps.load-config.outputs.service-name }} |" >> $GITHUB_STEP_SUMMARY + echo "| **Message** | ${{ steps.load-config.outputs.maintenance-message }} |" >> $GITHUB_STEP_SUMMARY + if [[ -n "${{ steps.load-config.outputs.estimated-return }}" ]]; then + echo "| **Estimated Return** | (See maintenance page) |" >> $GITHUB_STEP_SUMMARY fi - if [[ -n "${{ inputs.status-page-url }}" ]]; then - echo "| **Status Page** | [${{ inputs.status-page-url }}](${{ inputs.status-page-url }}) |" >> $GITHUB_STEP_SUMMARY + if [[ -n "${{ steps.load-config.outputs.status-page }}" ]]; then + echo "| **Status Page** | (See maintenance page) |" >> $GITHUB_STEP_SUMMARY fi - if [[ -n "${{ inputs.contact-email }}" ]]; then - echo "| **Contact** | ${{ inputs.contact-email }} |" >> $GITHUB_STEP_SUMMARY + if [[ -n "${{ steps.load-config.outputs.contact-email }}" ]]; then + echo "| **Contact** | ${{ steps.load-config.outputs.contact-email }} |" >> $GITHUB_STEP_SUMMARY fi echo '' >> $GITHUB_STEP_SUMMARY From 1b6ac14d4a0e8740ebcd53a84d686bfb7d1a48ca Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Thu, 7 Aug 2025 14:49:59 +0100 Subject: [PATCH 15/19] Update maintenance-v2 to fetch templates from GitHub release tar archive --- maintenance-v2/action.yml | 146 +++++++++++++++++++++++++------------- 1 file changed, 97 insertions(+), 49 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 8eef16c..3f10d3d 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -42,9 +42,9 @@ inputs: default: 'maint-config.txt' template-ref: - description: Branch/tag/commit of teacher-services-cloud repository to use + description: Template release version to use (e.g., v1.0.0, v1.0.1) required: false - default: 'main' + default: 'v1.0.0' runs: using: composite @@ -74,9 +74,24 @@ runs: id: load-config shell: bash run: | - echo "Loading maintenance configuration..." + echo "Loading configuration..." + + # Load service configuration from Makefile + if [[ -f "Makefile" ]]; then + echo "Loading service configuration from Makefile..." + SERVICE_NAME=$(grep "^SERVICE_NAME=" Makefile | cut -d'=' -f2 | tr -d ' ') + SERVICE_SHORT=$(grep "^SERVICE_SHORT=" Makefile | cut -d'=' -f2 | tr -d ' ') + echo "Found SERVICE_NAME: ${SERVICE_NAME}" + echo "Found SERVICE_SHORT: ${SERVICE_SHORT}" + else + # Fallback: extract from docker repository + echo "No Makefile found, extracting from docker repository..." + REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') + SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//') + SERVICE_SHORT="${SERVICE_NAME}" + fi - # Set default values + # Set default maintenance values SERVICE_PRETTY="This service" MAINTENANCE_MESSAGE="We are currently carrying out essential maintenance. Please try again later." ESTIMATED_RETURN="" @@ -85,22 +100,33 @@ runs: # Check if config file exists and load it if [[ -f "${{ inputs.config-file }}" ]]; then - echo "Loading configuration from ${{ inputs.config-file }}" + echo "Loading maintenance configuration from ${{ inputs.config-file }}" # Source the config file to load variables source "${{ inputs.config-file }}" else echo "No config file found at ${{ inputs.config-file }}, using defaults" fi + # If SERVICE_PRETTY not set in config, use SERVICE_NAME as a fallback + if [[ -z "${SERVICE_PRETTY}" ]] || [[ "${SERVICE_PRETTY}" == "This service" ]]; then + # Try to make SERVICE_NAME more readable by replacing hyphens with spaces and capitalizing + SERVICE_PRETTY=$(echo "${SERVICE_NAME}" | sed 's/-/ /g' | sed 's/\b\(.\)/\u\1/g') + echo "SERVICE_PRETTY not set in config, using: ${SERVICE_PRETTY}" + fi + # Export variables for use in later steps - echo "service-name=${SERVICE_PRETTY}" >> $GITHUB_OUTPUT + echo "service-name=${SERVICE_NAME}" >> $GITHUB_OUTPUT + echo "service-short=${SERVICE_SHORT}" >> $GITHUB_OUTPUT + echo "service-pretty=${SERVICE_PRETTY}" >> $GITHUB_OUTPUT echo "maintenance-message=${MAINTENANCE_MESSAGE}" >> $GITHUB_OUTPUT echo "estimated-return=${ESTIMATED_RETURN}" >> $GITHUB_OUTPUT echo "status-page=${STATUS_PAGE}" >> $GITHUB_OUTPUT echo "contact-email=${CONTACT_EMAIL}" >> $GITHUB_OUTPUT echo "Configuration loaded:" - echo " Service: ${SERVICE_PRETTY}" + echo " Service Name: ${SERVICE_NAME}" + echo " Service Short: ${SERVICE_SHORT}" + echo " Service Pretty: ${SERVICE_PRETTY}" echo " Message: ${MAINTENANCE_MESSAGE}" echo " Contact: ${CONTACT_EMAIL}" @@ -108,48 +134,73 @@ runs: if: inputs.mode == 'enable' shell: bash run: | - echo "Fetching maintenance page templates from teacher-services-cloud..." + echo "Fetching maintenance page templates from GitHub release..." echo "Current directory: $(pwd)" echo "GitHub workspace: ${{ github.workspace }}" # Create maintenance_page directory if it doesn't exist mkdir -p maintenance_page - # Create temp directory for cloning - TEMP_DIR=$(mktemp -d) - echo "Temp directory: $TEMP_DIR" + # Determine template version to fetch + TEMPLATE_VERSION="${{ inputs.template-ref }}" + if [ -z "${TEMPLATE_VERSION}" ] || [ "${TEMPLATE_VERSION}" == "main" ]; then + # Default to v1.0.0 or latest stable version + TEMPLATE_VERSION="v1.0.0" + fi + + # Construct download URL + ARCHIVE_NAME="maintenance-template-${TEMPLATE_VERSION}.tar.gz" + DOWNLOAD_URL="https://github.com/DFE-Digital/teacher-services-cloud/releases/download/maintenance-template-${TEMPLATE_VERSION}/${ARCHIVE_NAME}" + + echo "Downloading template version: ${TEMPLATE_VERSION}" + echo "Download URL: ${DOWNLOAD_URL}" + + # Download the template archive + curl -L -o "${ARCHIVE_NAME}" "${DOWNLOAD_URL}" + + if [ ! -f "${ARCHIVE_NAME}" ]; then + echo "Error: Failed to download template archive from ${DOWNLOAD_URL}" + echo "Trying alternative URL format..." + + # Try alternative URL format (without 'maintenance-template-' prefix in tag) + DOWNLOAD_URL="https://github.com/DFE-Digital/teacher-services-cloud/releases/download/${TEMPLATE_VERSION}/${ARCHIVE_NAME}" + curl -L -o "${ARCHIVE_NAME}" "${DOWNLOAD_URL}" + + if [ ! -f "${ARCHIVE_NAME}" ]; then + echo "Error: Failed to download template archive" + exit 1 + fi + fi + + echo "Downloaded: ${ARCHIVE_NAME} ($(du -h ${ARCHIVE_NAME} | cut -f1))" + + # Extract the archive + echo "Extracting templates..." + tar -xzf "${ARCHIVE_NAME}" - # Clone only the templates directory (sparse checkout) - cd $TEMP_DIR - git clone --filter=blob:none --sparse https://github.com/DFE-Digital/teacher-services-cloud.git - cd teacher-services-cloud - git sparse-checkout set templates/new_service/maintenance_page - git checkout ${{ inputs.template-ref }} + # The archive should contain a maintenance-template directory + if [ ! -d "maintenance-template" ]; then + echo "Error: Expected maintenance-template directory not found in archive" + exit 1 + fi - # Debug: Check what files we have - echo "Files in template directory:" - ls -la templates/new_service/maintenance_page/ + # Copy template files to maintenance_page directory + echo "Copying template files..." - # Copy ONLY the necessary template files (Dockerfile, nginx.conf, html) - # DO NOT touch existing manifests or scripts directories in the service repo - cp templates/new_service/maintenance_page/Dockerfile ${{ github.workspace }}/maintenance_page/ - cp templates/new_service/maintenance_page/nginx.conf ${{ github.workspace }}/maintenance_page/ + # Copy Dockerfile and nginx.conf + cp maintenance-template/Dockerfile maintenance_page/ + cp maintenance-template/nginx.conf maintenance_page/ # Remove existing html directory and copy fresh from template - rm -rf ${{ github.workspace }}/maintenance_page/html - cp -r templates/new_service/maintenance_page/html ${{ github.workspace }}/maintenance_page/ + rm -rf maintenance_page/html + cp -r maintenance-template/html maintenance_page/ - # Copy the maintenance manifests templates (deployment and service only) - # Only copy if they don't already exist in the service repository + # Check if service has the required directories (manifests and scripts remain in service repo) if [ ! -d "${{ github.workspace }}/maintenance_page/manifests/maintenance" ]; then - echo "Copying maintenance manifest templates from teacher-services-cloud..." - mkdir -p ${{ github.workspace }}/maintenance_page/manifests/maintenance - cp -r templates/new_service/maintenance_page/manifests/maintenance/* ${{ github.workspace }}/maintenance_page/manifests/maintenance/ - else - echo "Using existing maintenance manifests from service repository" + echo "WARNING: No maintenance manifests found in maintenance_page/manifests/maintenance/" + echo "Service repository should contain maintenance-specific manifests" fi - # Check if service has the required directories if [ ! -d "${{ github.workspace }}/maintenance_page/manifests/staging" ] && [ ! -d "${{ github.workspace }}/maintenance_page/manifests/production" ]; then echo "WARNING: No environment-specific manifests found in maintenance_page/manifests/" echo "Please ensure your repository contains:" @@ -158,20 +209,19 @@ runs: fi if [ ! -d "${{ github.workspace }}/maintenance_page/scripts" ]; then - echo "WARNING: No scripts directory found in maintenance_page/" - echo "Using template scripts as fallback..." - cp -r templates/new_service/maintenance_page/scripts ${{ github.workspace }}/maintenance_page/ - else - echo "Using existing service-specific scripts from maintenance_page/scripts/" + echo "ERROR: No scripts directory found in maintenance_page/" + echo "Service repository must contain maintenance_page/scripts/ with deployment scripts" + exit 1 fi + # Clean up + rm -rf maintenance-template + rm -f "${ARCHIVE_NAME}" + # Debug: Verify files were copied echo "Files in maintenance_page directory:" ls -la ${{ github.workspace }}/maintenance_page/ - # Clean up temp directory - rm -rf $TEMP_DIR - echo "Templates fetched successfully" # Apply customizations @@ -244,12 +294,9 @@ runs: echo "Customizations applied successfully" - # Also need to replace placeholders in the manifests - # Extract service name from docker repository (e.g., "teacher-success" from "ghcr.io/dfe-digital/teacher-success-maintenance") - REPO_NAME=$(echo "${{ inputs.docker-repository }}" | sed 's|.*/||') - SERVICE_NAME=$(echo "$REPO_NAME" | sed 's/-maintenance$//') - echo "Extracted service name from docker repository: $SERVICE_NAME" - echo "Service name for manifests: $SERVICE_NAME" + # Use service name from load-config step for manifests + SERVICE_NAME="${{ steps.load-config.outputs.service-name }}" + echo "Using service name for manifests: $SERVICE_NAME" # Replace placeholders in deployment template (but keep #MAINTENANCE_IMAGE_TAG# for the script to replace) sed -i "s/#SERVICE_NAME#/${SERVICE_NAME}/g" maintenance_page/manifests/maintenance/deployment_maintenance.yml.tmpl @@ -299,7 +346,8 @@ runs: echo "| **Environment** | ${{ inputs.environment }} |" >> $GITHUB_STEP_SUMMARY echo "| **Time** | ${NOW} (London) |" >> $GITHUB_STEP_SUMMARY if [[ "${{ inputs.mode }}" == "enable" ]]; then - echo "| **Service** | ${{ steps.load-config.outputs.service-name }} |" >> $GITHUB_STEP_SUMMARY + echo "| **Service** | ${{ steps.load-config.outputs.service-pretty }} |" >> $GITHUB_STEP_SUMMARY + echo "| **Service Name** | ${{ steps.load-config.outputs.service-name }} |" >> $GITHUB_STEP_SUMMARY echo "| **Message** | ${{ steps.load-config.outputs.maintenance-message }} |" >> $GITHUB_STEP_SUMMARY if [[ -n "${{ steps.load-config.outputs.estimated-return }}" ]]; then echo "| **Estimated Return** | (See maintenance page) |" >> $GITHUB_STEP_SUMMARY From 7cf32a6b008f5c95dac613049a965cb0daa49cf1 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Thu, 7 Aug 2025 15:03:07 +0100 Subject: [PATCH 16/19] Fix release tag URL to use 'maintenance-templates' (with s) --- maintenance-v2/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 3f10d3d..f6aa68e 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -150,7 +150,7 @@ runs: # Construct download URL ARCHIVE_NAME="maintenance-template-${TEMPLATE_VERSION}.tar.gz" - DOWNLOAD_URL="https://github.com/DFE-Digital/teacher-services-cloud/releases/download/maintenance-template-${TEMPLATE_VERSION}/${ARCHIVE_NAME}" + DOWNLOAD_URL="https://github.com/DFE-Digital/teacher-services-cloud/releases/download/maintenance-templates-${TEMPLATE_VERSION}/${ARCHIVE_NAME}" echo "Downloading template version: ${TEMPLATE_VERSION}" echo "Download URL: ${DOWNLOAD_URL}" From f55b6cef4bb54e3f5c1918092234d4325e53b431 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Thu, 7 Aug 2025 15:11:42 +0100 Subject: [PATCH 17/19] Fix archive filename to match actual release asset (maintenance-templates.tar.gz) --- maintenance-v2/action.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index f6aa68e..2cb6df2 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -149,7 +149,8 @@ runs: fi # Construct download URL - ARCHIVE_NAME="maintenance-template-${TEMPLATE_VERSION}.tar.gz" + # The archive name in the release is just "maintenance-templates.tar.gz" (without version in filename) + ARCHIVE_NAME="maintenance-templates.tar.gz" DOWNLOAD_URL="https://github.com/DFE-Digital/teacher-services-cloud/releases/download/maintenance-templates-${TEMPLATE_VERSION}/${ARCHIVE_NAME}" echo "Downloading template version: ${TEMPLATE_VERSION}" From eeb8e0f6d404965907fcd50e788d0696b53ef408 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Thu, 7 Aug 2025 15:18:04 +0100 Subject: [PATCH 18/19] Handle both maintenance_page and maintenance-template directory structures in archives --- maintenance-v2/action.yml | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 2cb6df2..0f25a5e 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -179,22 +179,29 @@ runs: echo "Extracting templates..." tar -xzf "${ARCHIVE_NAME}" - # The archive should contain a maintenance-template directory - if [ ! -d "maintenance-template" ]; then - echo "Error: Expected maintenance-template directory not found in archive" + # The archive contains a maintenance_page directory (from the existing release) + # Check if it exists, if not check for maintenance-template (future releases) + if [ -d "maintenance_page" ]; then + echo "Found maintenance_page directory in archive" + TEMPLATE_DIR="maintenance_page" + elif [ -d "maintenance-template" ]; then + echo "Found maintenance-template directory in archive" + TEMPLATE_DIR="maintenance-template" + else + echo "Error: Neither maintenance_page nor maintenance-template directory found in archive" exit 1 fi # Copy template files to maintenance_page directory - echo "Copying template files..." + echo "Copying template files from ${TEMPLATE_DIR}..." # Copy Dockerfile and nginx.conf - cp maintenance-template/Dockerfile maintenance_page/ - cp maintenance-template/nginx.conf maintenance_page/ + cp ${TEMPLATE_DIR}/Dockerfile maintenance_page/ + cp ${TEMPLATE_DIR}/nginx.conf maintenance_page/ # Remove existing html directory and copy fresh from template rm -rf maintenance_page/html - cp -r maintenance-template/html maintenance_page/ + cp -r ${TEMPLATE_DIR}/html maintenance_page/ # Check if service has the required directories (manifests and scripts remain in service repo) if [ ! -d "${{ github.workspace }}/maintenance_page/manifests/maintenance" ]; then @@ -216,7 +223,7 @@ runs: fi # Clean up - rm -rf maintenance-template + rm -rf ${TEMPLATE_DIR} rm -f "${ARCHIVE_NAME}" # Debug: Verify files were copied From ffc6a341e75279f4881d97e5f8669e91ff856163 Mon Sep 17 00:00:00 2001 From: Mohammed Islam Date: Thu, 7 Aug 2025 15:25:59 +0100 Subject: [PATCH 19/19] Add checks to avoid overwriting existing template files --- maintenance-v2/action.yml | 44 +++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/maintenance-v2/action.yml b/maintenance-v2/action.yml index 0f25a5e..c2eac59 100644 --- a/maintenance-v2/action.yml +++ b/maintenance-v2/action.yml @@ -175,33 +175,51 @@ runs: echo "Downloaded: ${ARCHIVE_NAME} ($(du -h ${ARCHIVE_NAME} | cut -f1))" - # Extract the archive + # Extract the archive to a temporary directory echo "Extracting templates..." - tar -xzf "${ARCHIVE_NAME}" + TEMP_DIR="temp_templates_$$" + mkdir -p ${TEMP_DIR} + tar -xzf "${ARCHIVE_NAME}" -C ${TEMP_DIR} # The archive contains a maintenance_page directory (from the existing release) # Check if it exists, if not check for maintenance-template (future releases) - if [ -d "maintenance_page" ]; then + if [ -d "${TEMP_DIR}/maintenance_page" ]; then echo "Found maintenance_page directory in archive" - TEMPLATE_DIR="maintenance_page" - elif [ -d "maintenance-template" ]; then + TEMPLATE_DIR="${TEMP_DIR}/maintenance_page" + elif [ -d "${TEMP_DIR}/maintenance-template" ]; then echo "Found maintenance-template directory in archive" - TEMPLATE_DIR="maintenance-template" + TEMPLATE_DIR="${TEMP_DIR}/maintenance-template" else echo "Error: Neither maintenance_page nor maintenance-template directory found in archive" + ls -la ${TEMP_DIR}/ exit 1 fi # Copy template files to maintenance_page directory echo "Copying template files from ${TEMPLATE_DIR}..." - # Copy Dockerfile and nginx.conf - cp ${TEMPLATE_DIR}/Dockerfile maintenance_page/ - cp ${TEMPLATE_DIR}/nginx.conf maintenance_page/ + # Only copy files if they don't already exist (avoid overwriting) + if [ ! -f "maintenance_page/Dockerfile" ]; then + echo "Copying Dockerfile..." + cp ${TEMPLATE_DIR}/Dockerfile maintenance_page/ + else + echo "Dockerfile already exists, skipping..." + fi - # Remove existing html directory and copy fresh from template - rm -rf maintenance_page/html - cp -r ${TEMPLATE_DIR}/html maintenance_page/ + if [ ! -f "maintenance_page/nginx.conf" ]; then + echo "Copying nginx.conf..." + cp ${TEMPLATE_DIR}/nginx.conf maintenance_page/ + else + echo "nginx.conf already exists, skipping..." + fi + + # Copy html directory from template if it doesn't exist + if [ ! -d "maintenance_page/html" ]; then + echo "Copying html directory..." + cp -r ${TEMPLATE_DIR}/html maintenance_page/ + else + echo "html directory already exists, skipping..." + fi # Check if service has the required directories (manifests and scripts remain in service repo) if [ ! -d "${{ github.workspace }}/maintenance_page/manifests/maintenance" ]; then @@ -223,7 +241,7 @@ runs: fi # Clean up - rm -rf ${TEMPLATE_DIR} + rm -rf ${TEMP_DIR} rm -f "${ARCHIVE_NAME}" # Debug: Verify files were copied