From 0ac86ffb5b33ec23694e816d98bcb8c25b4184ae Mon Sep 17 00:00:00 2001 From: ada mancini Date: Wed, 4 Feb 2026 16:45:08 -0500 Subject: [PATCH 1/8] feat(wg-easy): add weekly automated chart validation tests Add automated weekly testing workflow to ensure the wg-easy Helm chart remains installable and functional during periods of low development activity. Changes: - Add weekly-test.yaml workflow that runs every Monday at 9:00 AM UTC - Test chart validation, packaging, deployment, and functionality - Create GitHub Issues automatically on test failures - Comment on issues when tests recover to passing state - Reuse stable "weekly-test" channel/customer for efficiency - Update README.md with automated testing documentation Benefits: - Proactive monitoring catches breakages before users encounter them - Automatic notifications via GitHub Issues - Zero maintenance - runs completely automated - Cost efficient with single cluster configuration - Easy debugging with uploaded logs and workflow links --- .github/workflows/wg-easy-weekly-test.yaml | 454 +++++++++++++++++++++ applications/wg-easy/README.md | 21 + 2 files changed, 475 insertions(+) create mode 100644 .github/workflows/wg-easy-weekly-test.yaml diff --git a/.github/workflows/wg-easy-weekly-test.yaml b/.github/workflows/wg-easy-weekly-test.yaml new file mode 100644 index 00000000..07db0241 --- /dev/null +++ b/.github/workflows/wg-easy-weekly-test.yaml @@ -0,0 +1,454 @@ +--- +name: WG-Easy Weekly Test - Verify Chart Installation + +on: + schedule: + # Run every Monday at 9:00 AM UTC (1:00 AM PST / 4:00 AM EST) + - cron: '0 9 * * 1' + workflow_dispatch: + inputs: + notify_on_success: + description: 'Send notification even on success' + required: false + default: 'false' + type: boolean + +concurrency: + group: wg-easy-weekly-test + cancel-in-progress: false + +env: + APP_DIR: applications/wg-easy + REPLICATED_API_TOKEN: ${{ secrets.WG_EASY_REPLICATED_API_TOKEN }} + REPLICATED_APP: ${{ vars.WG_EASY_REPLICATED_APP }} + HELM_VERSION: "3.17.3" + KUBECTL_VERSION: "v1.30.0" + CHANNEL_NAME: "weekly-test" + CUSTOMER_NAME: "weekly-test-customer" + +jobs: + validate-charts: + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Validate charts + uses: ./.github/actions/chart-validate + with: + app-dir: ${{ env.APP_DIR }} + helm-version: ${{ env.HELM_VERSION }} + + - name: Validate Taskfile syntax + run: task --list-all + working-directory: ${{ env.APP_DIR }} + + build-and-package: + runs-on: ubuntu-24.04 + needs: validate-charts + outputs: + release-path: ${{ steps.package.outputs.release-path }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Package charts + id: package + uses: ./.github/actions/chart-package + with: + app-dir: ${{ env.APP_DIR }} + helm-version: ${{ env.HELM_VERSION }} + + - name: Upload release artifacts + uses: actions/upload-artifact@v4 + with: + name: wg-easy-weekly-test-${{ github.run_number }} + path: ${{ steps.package.outputs.release-path }} + retention-days: 7 + + create-resources: + runs-on: ubuntu-24.04 + needs: build-and-package + outputs: + channel-slug: ${{ steps.set-outputs.outputs.channel-slug }} + release-sequence: ${{ steps.set-outputs.outputs.release-sequence }} + customer-id: ${{ steps.set-outputs.outputs.customer-id }} + license-id: ${{ steps.set-outputs.outputs.license-id }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download release artifacts + uses: actions/download-artifact@v4 + with: + name: wg-easy-weekly-test-${{ github.run_number }} + path: ${{ env.APP_DIR }}/release + + - name: Check if channel exists + id: check-channel + run: | + echo "Checking for existing channel: ${{ env.CHANNEL_NAME }}" + + RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \ + "https://api.replicated.com/vendor/v3/apps/${{ env.REPLICATED_APP }}/channels") + + if [ $? -ne 0 ]; then + echo "curl command failed" + echo "channel-exists=false" >> $GITHUB_OUTPUT + exit 0 + fi + + HTTP_CODE=$(echo "$RESPONSE" | tail -n1) + BODY=$(echo "$RESPONSE" | sed '$d') + + if [ "$HTTP_CODE" != "200" ]; then + echo "API request failed with HTTP $HTTP_CODE" + echo "channel-exists=false" >> $GITHUB_OUTPUT + exit 0 + fi + + CHANNEL_ID=$(echo "$BODY" | jq -r --arg name "${{ env.CHANNEL_NAME }}" \ + 'if .channels then .channels[] | select(.name == $name) | .id else empty end' 2>/dev/null | head -1) + + if [ -n "$CHANNEL_ID" ] && [ "$CHANNEL_ID" != "null" ]; then + echo "Found existing channel: $CHANNEL_ID" + echo "channel-exists=true" >> $GITHUB_OUTPUT + echo "channel-id=$CHANNEL_ID" >> $GITHUB_OUTPUT + echo "channel-slug=${{ env.CHANNEL_NAME }}" >> $GITHUB_OUTPUT + else + echo "Channel does not exist" + echo "channel-exists=false" >> $GITHUB_OUTPUT + fi + + - name: Create Replicated release + id: release + uses: replicatedhq/replicated-actions/create-release@v1.19.0 + with: + app-slug: ${{ env.REPLICATED_APP }} + api-token: ${{ env.REPLICATED_API_TOKEN }} + yaml-dir: ${{ env.APP_DIR }}/release + promote-channel: ${{ env.CHANNEL_NAME }} + + - name: Check if customer exists + id: check-customer + run: | + echo "Checking for existing customer: ${{ env.CUSTOMER_NAME }}" + + RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \ + "https://api.replicated.com/vendor/v3/customers") + + if [ $? -ne 0 ]; then + echo "curl command failed" + echo "customer-exists=false" >> $GITHUB_OUTPUT + exit 0 + fi + + HTTP_CODE=$(echo "$RESPONSE" | tail -n1) + BODY=$(echo "$RESPONSE" | sed '$d') + + if [ "$HTTP_CODE" != "200" ]; then + echo "API request failed with HTTP $HTTP_CODE" + echo "customer-exists=false" >> $GITHUB_OUTPUT + exit 0 + fi + + CUSTOMER_DATA=$(echo "$BODY" | jq -r --arg name "${{ env.CUSTOMER_NAME }}" \ + 'if .customers then .customers[] | select(.name == $name) | {id: .id, created: .createdAt} else empty end' 2>/dev/null \ + | jq -s 'sort_by(.created) | reverse | .[0] // empty' 2>/dev/null) + + CUSTOMER_ID=$(echo "$CUSTOMER_DATA" | jq -r '.id // empty' 2>/dev/null) + + if [ -n "$CUSTOMER_ID" ] && [ "$CUSTOMER_ID" != "null" ]; then + echo "Found existing customer: $CUSTOMER_ID" + echo "customer-exists=true" >> $GITHUB_OUTPUT + echo "customer-id=$CUSTOMER_ID" >> $GITHUB_OUTPUT + + LICENSE_RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \ + "https://api.replicated.com/vendor/v3/customer/$CUSTOMER_ID") + + LICENSE_HTTP_CODE=$(echo "$LICENSE_RESPONSE" | tail -n1) + LICENSE_BODY=$(echo "$LICENSE_RESPONSE" | sed '$d') + + if [ "$LICENSE_HTTP_CODE" = "200" ]; then + LICENSE_ID=$(echo "$LICENSE_BODY" | jq -r '.customer.installationId // empty' 2>/dev/null) + echo "license-id=$LICENSE_ID" >> $GITHUB_OUTPUT + else + echo "Failed to get license ID for customer $CUSTOMER_ID" + echo "customer-exists=false" >> $GITHUB_OUTPUT + fi + else + echo "Customer does not exist" + echo "customer-exists=false" >> $GITHUB_OUTPUT + fi + + - name: Create customer + id: create-customer + if: steps.check-customer.outputs.customer-exists == 'false' + uses: replicatedhq/replicated-actions/create-customer@v1.19.0 + with: + app-slug: ${{ env.REPLICATED_APP }} + api-token: ${{ env.REPLICATED_API_TOKEN }} + customer-name: ${{ env.CUSTOMER_NAME }} + channel-slug: ${{ steps.check-channel.outputs.channel-exists == 'true' && steps.check-channel.outputs.channel-slug || steps.release.outputs.channel-slug }} + license-type: dev + + - name: Set consolidated outputs + id: set-outputs + run: | + if [ "${{ steps.check-channel.outputs.channel-exists }}" == "true" ]; then + echo "channel-slug=${{ steps.check-channel.outputs.channel-slug }}" >> $GITHUB_OUTPUT + else + echo "channel-slug=${{ steps.release.outputs.channel-slug }}" >> $GITHUB_OUTPUT + fi + echo "release-sequence=${{ steps.release.outputs.release-sequence }}" >> $GITHUB_OUTPUT + + if [ "${{ steps.check-customer.outputs.customer-exists }}" == "true" ]; then + echo "customer-id=${{ steps.check-customer.outputs.customer-id }}" >> $GITHUB_OUTPUT + echo "license-id=${{ steps.check-customer.outputs.license-id }}" >> $GITHUB_OUTPUT + else + echo "customer-id=${{ steps.create-customer.outputs.customer-id }}" >> $GITHUB_OUTPUT + echo "license-id=${{ steps.create-customer.outputs.license-id }}" >> $GITHUB_OUTPUT + fi + + test-deployment: + runs-on: ubuntu-24.04 + needs: create-resources + strategy: + matrix: + # Test on latest stable Kubernetes version only for weekly runs + include: + - k8s-version: "v1.35" + distribution: "k3s" + nodes: 1 + instance-type: "r1.small" + timeout-minutes: 15 + fail-fast: true + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup tools + uses: ./.github/actions/setup-tools + with: + helm-version: ${{ env.HELM_VERSION }} + install-helmfile: 'true' + + - name: Create test cluster + id: create-cluster + shell: bash + run: | + CLUSTER_NAME="weekly-test-${{ github.run_number }}" + echo "Creating cluster: $CLUSTER_NAME" + + replicated cluster create \ + --name "$CLUSTER_NAME" \ + --distribution "${{ matrix.distribution }}" \ + --version "${{ matrix.k8s-version }}" \ + --disk "50" \ + --instance-type "${{ matrix.instance-type }}" \ + --nodes "${{ matrix.nodes }}" \ + --ttl "4h" + + if [ $? -ne 0 ]; then + echo "Failed to create cluster" + exit 1 + fi + + echo "Waiting for cluster to be running..." + for i in {1..60}; do + STATUS=$(replicated cluster ls --output json | jq -r '.[] | select(.name == "'$CLUSTER_NAME'") | .status' 2>/dev/null) + if [ "$STATUS" = "running" ]; then + echo "Cluster is running!" + break + fi + echo "Cluster status: $STATUS, waiting... (attempt $i/60)" + sleep 10 + done + + if [ "$STATUS" != "running" ]; then + echo "Cluster failed to reach running state" + exit 1 + fi + + replicated cluster kubeconfig --name "$CLUSTER_NAME" --output-path /tmp/kubeconfig + echo "KUBECONFIG=/tmp/kubeconfig" >> $GITHUB_ENV + echo "CLUSTER_NAME=$CLUSTER_NAME" >> $GITHUB_ENV + + CLUSTER_ID=$(replicated cluster ls --output json | jq -r '.[] | select(.name == "'$CLUSTER_NAME'") | .id' 2>/dev/null) + echo "cluster-id=$CLUSTER_ID" >> $GITHUB_OUTPUT + + - name: Setup cluster ports + working-directory: ${{ env.APP_DIR }} + run: task cluster-ports-expose CLUSTER_NAME="${{ env.CLUSTER_NAME }}" + + - name: Validate cluster readiness + run: | + echo "Validating cluster readiness..." + + if ! kubectl cluster-info; then + echo "ERROR: Cluster API server not accessible" + exit 1 + fi + + if ! kubectl wait --for=condition=Ready nodes --all --timeout=300s; then + echo "ERROR: Cluster nodes did not become ready" + kubectl get nodes -o wide + exit 1 + fi + + echo "✅ Cluster is ready" + kubectl get nodes -o wide + + - name: Deploy application + working-directory: ${{ env.APP_DIR }} + run: | + task customer-helm-install \ + CUSTOMER_NAME="${{ env.CUSTOMER_NAME }}" \ + CLUSTER_NAME="${{ env.CLUSTER_NAME }}" \ + CHANNEL_SLUG="${{ needs.create-resources.outputs.channel-slug }}" \ + REPLICATED_LICENSE_ID="${{ needs.create-resources.outputs.license-id }}" + timeout-minutes: ${{ matrix.timeout-minutes }} + + - name: Run application tests + working-directory: ${{ env.APP_DIR }} + run: task test + timeout-minutes: 10 + + - name: Verify deployment health + run: | + echo "Verifying deployment health..." + + # Check pods are running + kubectl get pods -A + + # Verify WG-Easy pod is running + kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=wg-easy --timeout=300s -n default || { + echo "WG-Easy pod failed to become ready" + kubectl describe pods -l app.kubernetes.io/name=wg-easy -n default + kubectl logs -l app.kubernetes.io/name=wg-easy -n default --tail=100 + exit 1 + } + + echo "✅ Application deployed and healthy" + + - name: Cleanup test cluster + if: always() + run: | + if [ -n "${{ env.CLUSTER_NAME }}" ]; then + echo "Cleaning up cluster: ${{ env.CLUSTER_NAME }}" + replicated cluster rm --name "${{ env.CLUSTER_NAME }}" || echo "Cluster cleanup failed or already removed" + fi + + - name: Upload debug logs on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: debug-logs-weekly-test-${{ github.run_number }} + path: | + /tmp/*.log + ~/.replicated/ + + notify-on-failure: + runs-on: ubuntu-24.04 + needs: [validate-charts, build-and-package, create-resources, test-deployment] + if: failure() + steps: + - name: Create GitHub Issue on Failure + uses: actions/github-script@v7 + with: + script: | + const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const issueTitle = `[Weekly Test Failure] WG-Easy chart installation failed - ${new Date().toISOString().split('T')[0]}`; + const issueBody = `## Weekly Test Failure Report + + The weekly automated test for the WG-Easy Helm chart has failed. + + **Workflow Run:** ${runUrl} + **Date:** ${new Date().toISOString()} + **Trigger:** ${context.eventName === 'schedule' ? 'Scheduled weekly test' : 'Manual workflow dispatch'} + + ### Failed Jobs + Check the workflow run for detailed logs and failure information. + + ### Next Steps + 1. Review the workflow logs at the link above + 2. Investigate the root cause of the failure + 3. Fix any issues with the chart or dependencies + 4. Re-run the workflow to verify the fix + + ### Related Files + - Chart: \`applications/wg-easy/charts/wg-easy/\` + - Workflow: \`.github/workflows/wg-easy-weekly-test.yaml\` + + --- + *This issue was automatically created by the weekly test workflow.*`; + + // Check if there's already an open issue for this week + const issues = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + labels: 'automated-test,wg-easy', + per_page: 10 + }); + + const existingIssue = issues.data.find(issue => + issue.title.includes('[Weekly Test Failure]') && + issue.title.includes(new Date().toISOString().split('T')[0]) + ); + + if (existingIssue) { + // Add a comment to existing issue + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: existingIssue.number, + body: `Another test failure occurred in the same day.\n\n**Latest Run:** ${runUrl}` + }); + console.log(`Updated existing issue #${existingIssue.number}`); + } else { + // Create new issue + const issue = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: issueTitle, + body: issueBody, + labels: ['automated-test', 'wg-easy', 'bug'] + }); + console.log(`Created issue #${issue.data.number}`); + } + + notify-on-success: + runs-on: ubuntu-24.04 + needs: [validate-charts, build-and-package, create-resources, test-deployment] + if: success() && (github.event.inputs.notify_on_success == 'true' || github.event_name == 'schedule') + steps: + - name: Comment on latest open issue if exists + uses: actions/github-script@v7 + with: + script: | + const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + // Find the most recent open test failure issue + const issues = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + labels: 'automated-test,wg-easy', + sort: 'created', + direction: 'desc', + per_page: 1 + }); + + if (issues.data.length > 0) { + const issue = issues.data[0]; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + body: `✅ **Weekly test passed!**\n\nThe WG-Easy chart is now installing and working correctly.\n\n**Successful Run:** ${runUrl}\n\nConsider closing this issue if the problem is resolved.` + }); + console.log(`Added success comment to issue #${issue.number}`); + } else { + console.log('No open test failure issues found - weekly test passed successfully!'); + } diff --git a/applications/wg-easy/README.md b/applications/wg-easy/README.md index 83a65a12..038d24c6 100644 --- a/applications/wg-easy/README.md +++ b/applications/wg-easy/README.md @@ -148,6 +148,27 @@ Key components: > **New to Task or Helmfile?** These tools are convenient wrappers around standard Helm and shell commands -- they are not the point of this example. See the [Tooling Guide](docs/tooling-guide.md) for what each tool does, why it was chosen over Make and plain Helm CLI, and how to translate the workflows to your own tooling. +## Automated Testing + +### Weekly Chart Validation + +The repository includes an automated weekly test workflow that runs every Monday at 9:00 AM UTC to ensure the chart remains installable and functional: + +**Workflow:** `.github/workflows/wg-easy-weekly-test.yaml` + +**What it tests:** +- Chart validation and linting +- Chart packaging and release creation +- Deployment to a fresh Kubernetes cluster (latest stable version) +- Application health checks and functionality tests + +**Notifications:** +- Creates a GitHub Issue automatically when tests fail +- Comments on existing issues when tests pass again +- Can be triggered manually via workflow_dispatch + +This provides continuous validation that the chart is always ready to install, even during periods of low development activity. + ## Learn More - [Composable Multi-Chart Walkthrough](../../patterns/composable-multi-chart-walkthrough/README.md) -- end-to-end guided tour of the data flow from chart structure through release assembly From 44abe21446466fe205da7458f1fed6ca8f5ab986 Mon Sep 17 00:00:00 2001 From: ada mancini Date: Wed, 4 Feb 2026 16:54:41 -0500 Subject: [PATCH 2/8] test: add temporary PR trigger for weekly test workflow This allows testing the workflow on the PR before merge. Will be removed after validation. --- .github/workflows/wg-easy-weekly-test.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/wg-easy-weekly-test.yaml b/.github/workflows/wg-easy-weekly-test.yaml index 07db0241..e632018f 100644 --- a/.github/workflows/wg-easy-weekly-test.yaml +++ b/.github/workflows/wg-easy-weekly-test.yaml @@ -12,6 +12,11 @@ on: required: false default: 'false' type: boolean + pull_request: # TEMPORARY: Remove after testing + branches: [main] + paths: + - 'applications/wg-easy/**' + - '.github/workflows/wg-easy-weekly-test.yaml' concurrency: group: wg-easy-weekly-test From e748b59f462775a5ea5659931f5daa0aebcfeeb7 Mon Sep 17 00:00:00 2001 From: ada mancini Date: Wed, 4 Feb 2026 17:13:40 -0500 Subject: [PATCH 3/8] fix: correct namespace in health check from default to wg-easy The wg-easy pod deploys to the wg-easy namespace, not default. Update health check to look in the correct namespace. --- .github/workflows/wg-easy-weekly-test.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wg-easy-weekly-test.yaml b/.github/workflows/wg-easy-weekly-test.yaml index e632018f..ba291057 100644 --- a/.github/workflows/wg-easy-weekly-test.yaml +++ b/.github/workflows/wg-easy-weekly-test.yaml @@ -327,10 +327,10 @@ jobs: kubectl get pods -A # Verify WG-Easy pod is running - kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=wg-easy --timeout=300s -n default || { + kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=wg-easy --timeout=300s -n wg-easy || { echo "WG-Easy pod failed to become ready" - kubectl describe pods -l app.kubernetes.io/name=wg-easy -n default - kubectl logs -l app.kubernetes.io/name=wg-easy -n default --tail=100 + kubectl describe pods -l app.kubernetes.io/name=wg-easy -n wg-easy + kubectl logs -l app.kubernetes.io/name=wg-easy -n wg-easy --tail=100 exit 1 } From 46238e8a9f6d00a30a376672f8da483e52ac80be Mon Sep 17 00:00:00 2001 From: ada mancini Date: Wed, 4 Feb 2026 17:26:17 -0500 Subject: [PATCH 4/8] chore: remove temporary PR trigger after successful testing The workflow has been validated and works correctly. Removing the temporary pull_request trigger before merge. --- .github/workflows/wg-easy-weekly-test.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/wg-easy-weekly-test.yaml b/.github/workflows/wg-easy-weekly-test.yaml index ba291057..f46e7f64 100644 --- a/.github/workflows/wg-easy-weekly-test.yaml +++ b/.github/workflows/wg-easy-weekly-test.yaml @@ -12,11 +12,6 @@ on: required: false default: 'false' type: boolean - pull_request: # TEMPORARY: Remove after testing - branches: [main] - paths: - - 'applications/wg-easy/**' - - '.github/workflows/wg-easy-weekly-test.yaml' concurrency: group: wg-easy-weekly-test From 094bee9de1cd164316cddf526eff56e2833072a9 Mon Sep 17 00:00:00 2001 From: ada mancini Date: Wed, 4 Feb 2026 17:37:00 -0500 Subject: [PATCH 5/8] feat(wg-easy): add automated README status updates from test results Add automatic test status tracking to the wg-easy README: Changes: - Add test status section to README with markers for automation - Add update-readme-status job to weekly test workflow - Automatically updates status table with test results - Includes status, timestamp, Kubernetes version, and workflow link - Commits changes directly to main on scheduled runs only Benefits: - Users can see test status at a glance in README - Status is always current (updated weekly) - Provides direct link to latest test run - No manual status updates needed --- .github/workflows/wg-easy-weekly-test.yaml | 83 ++++++++++++++++++++++ applications/wg-easy/README.md | 10 +++ 2 files changed, 93 insertions(+) diff --git a/.github/workflows/wg-easy-weekly-test.yaml b/.github/workflows/wg-easy-weekly-test.yaml index f46e7f64..bca5a025 100644 --- a/.github/workflows/wg-easy-weekly-test.yaml +++ b/.github/workflows/wg-easy-weekly-test.yaml @@ -452,3 +452,86 @@ jobs: } else { console.log('No open test failure issues found - weekly test passed successfully!'); } + + update-readme-status: + runs-on: ubuntu-24.04 + needs: [validate-charts, build-and-package, create-resources, test-deployment] + if: always() && github.event_name == 'schedule' + permissions: + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Update README with test results + run: | + README_PATH="applications/wg-easy/README.md" + + # Determine test status + if [ "${{ needs.test-deployment.result }}" == "success" ]; then + STATUS="✅ Passing" + EMOJI="✅" + else + STATUS="❌ Failed" + EMOJI="❌" + fi + + # Get current date + TEST_DATE=$(date -u +"%Y-%m-%d %H:%M UTC") + + # Get workflow run URL + RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + # Create updated status table + cat > /tmp/status_section.md < + ## Test Status + + | Component | Status | Last Tested | Kubernetes Version | Details | + |-----------|--------|-------------|-------------------|---------| + | Chart Installation | ${STATUS} | ${TEST_DATE} | v1.35 | [View Run](${RUN_URL}) | + + *Status automatically updated by [weekly test workflow](${RUN_URL})* + + EOF + + # Replace the section in README + sed -i.bak '//,//d' "$README_PATH" + + # Find the position after the header and insert new status + sed -i.bak '4r /tmp/status_section.md' "$README_PATH" + + # Clean up backup file + rm -f "${README_PATH}.bak" + + echo "Updated README with test status: ${STATUS}" + cat "$README_PATH" | head -20 + + - name: Check for changes + id: check_changes + run: | + if git diff --quiet applications/wg-easy/README.md; then + echo "changed=false" >> $GITHUB_OUTPUT + echo "No changes to commit" + else + echo "changed=true" >> $GITHUB_OUTPUT + echo "Changes detected" + fi + + - name: Commit and push changes + if: steps.check_changes.outputs.changed == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git add applications/wg-easy/README.md + git commit -m "chore(wg-easy): update test status in README [skip ci] + + Automated test status update from weekly test workflow. + + Status: ${{ needs.test-deployment.result }} + Run: ${{ github.run_id }}" + + git push diff --git a/applications/wg-easy/README.md b/applications/wg-easy/README.md index 038d24c6..2f0e8996 100644 --- a/applications/wg-easy/README.md +++ b/applications/wg-easy/README.md @@ -4,6 +4,16 @@ This project demonstrates a **composable multi-chart workflow** for packaging an The target scenario: multiple teams, multiple product verticals, one Replicated release. + +## Test Status + +| Component | Status | Last Tested | Kubernetes Version | +|-----------|--------|-------------|-------------------| +| Chart Installation | ⏳ Pending | Never | v1.35 | + +*Status automatically updated by weekly test workflow* + + ## The Problem with Monolithic Releases In a monolithic Replicated application, all components share a single set of release artifacts: From 21df9838d8dbe29bd10f7d11f4320f61adee9a98 Mon Sep 17 00:00:00 2001 From: ada mancini Date: Wed, 1 Apr 2026 13:24:48 -0400 Subject: [PATCH 6/8] refactor(wg-easy): reduce duplication by making pr-validation a reusable workflow Add workflow_call support to wg-easy-pr-validation.yaml with inputs for channel-name, customer-name, and cleanup-cluster. The weekly test workflow now calls the PR validation workflow directly instead of duplicating all the validate/package/create-resources/deploy logic. Weekly test workflow shrinks from ~537 lines to ~198 lines. The weekly test now also benefits from the same 3-version k8s matrix as PR validation, providing broader coverage. --- .github/workflows/wg-easy-pr-validation.yaml | 48 ++- .github/workflows/wg-easy-weekly-test.yaml | 369 +------------------ 2 files changed, 57 insertions(+), 360 deletions(-) diff --git a/.github/workflows/wg-easy-pr-validation.yaml b/.github/workflows/wg-easy-pr-validation.yaml index f01cf04f..45c2c1a6 100644 --- a/.github/workflows/wg-easy-pr-validation.yaml +++ b/.github/workflows/wg-easy-pr-validation.yaml @@ -13,6 +13,27 @@ on: description: 'Run in test mode' required: false default: 'true' + workflow_call: + inputs: + channel-name: + description: 'Channel name override (defaults to branch-derived name)' + type: string + default: '' + customer-name: + description: 'Customer name override (defaults to branch-derived name)' + type: string + default: '' + cleanup-cluster: + description: 'Delete test clusters after completion' + type: boolean + default: false + secrets: + WG_EASY_REPLICATED_API_TOKEN: + required: true + outputs: + test-result: + description: 'Result of the test-deployment job' + value: ${{ jobs.test-deployment.result }} concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -36,12 +57,19 @@ jobs: - name: Set branch and channel variables id: vars run: | - # Branch name preserves original case for resource naming (clusters, customers) - BRANCH_NAME="${{ github.head_ref || github.ref_name }}" - # Channel name is normalized to lowercase with hyphens for Replicated channels - CHANNEL_NAME=$(echo "$BRANCH_NAME" | tr '[:upper:]' '[:lower:]' | tr '/' '-') - # Customer name uses normalized branch name for idempotent resource creation - CUSTOMER_NAME="${CHANNEL_NAME}" + # Use input overrides when called via workflow_call (e.g. from the weekly test workflow) + if [ -n "${{ inputs.channel-name }}" ]; then + CHANNEL_NAME="${{ inputs.channel-name }}" + BRANCH_NAME="$CHANNEL_NAME" + CUSTOMER_NAME="${{ inputs.customer-name || inputs.channel-name }}" + else + # Branch name preserves original case for resource naming (clusters, customers) + BRANCH_NAME="${{ github.head_ref || github.ref_name }}" + # Channel name is normalized to lowercase with hyphens for Replicated channels + CHANNEL_NAME=$(echo "$BRANCH_NAME" | tr '[:upper:]' '[:lower:]' | tr '/' '-') + # Customer name uses normalized branch name for idempotent resource creation + CUSTOMER_NAME="${CHANNEL_NAME}" + fi echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT echo "channel-name=$CHANNEL_NAME" >> $GITHUB_OUTPUT echo "customer-name=$CUSTOMER_NAME" >> $GITHUB_OUTPUT @@ -747,6 +775,14 @@ jobs: echo "Started: $(date -u)" echo "Status: Complete" + - name: Cleanup test cluster + if: always() && inputs.cleanup-cluster + run: | + K8S_VERSION_NORMALIZED=$(echo "${{ matrix.k8s-version }}" | tr '.' '-') + CLUSTER_NAME="${{ needs.setup.outputs.channel-name }}-$K8S_VERSION_NORMALIZED-${{ matrix.distribution }}-${{ github.run_number }}" + echo "Removing cluster: $CLUSTER_NAME" + replicated cluster rm --name "$CLUSTER_NAME" || echo "Cluster cleanup failed or already removed" + - name: Upload debug logs if: failure() uses: actions/upload-artifact@v4 diff --git a/.github/workflows/wg-easy-weekly-test.yaml b/.github/workflows/wg-easy-weekly-test.yaml index bca5a025..a5a9e848 100644 --- a/.github/workflows/wg-easy-weekly-test.yaml +++ b/.github/workflows/wg-easy-weekly-test.yaml @@ -17,340 +17,19 @@ concurrency: group: wg-easy-weekly-test cancel-in-progress: false -env: - APP_DIR: applications/wg-easy - REPLICATED_API_TOKEN: ${{ secrets.WG_EASY_REPLICATED_API_TOKEN }} - REPLICATED_APP: ${{ vars.WG_EASY_REPLICATED_APP }} - HELM_VERSION: "3.17.3" - KUBECTL_VERSION: "v1.30.0" - CHANNEL_NAME: "weekly-test" - CUSTOMER_NAME: "weekly-test-customer" - jobs: - validate-charts: - runs-on: ubuntu-24.04 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Validate charts - uses: ./.github/actions/chart-validate - with: - app-dir: ${{ env.APP_DIR }} - helm-version: ${{ env.HELM_VERSION }} - - - name: Validate Taskfile syntax - run: task --list-all - working-directory: ${{ env.APP_DIR }} - - build-and-package: - runs-on: ubuntu-24.04 - needs: validate-charts - outputs: - release-path: ${{ steps.package.outputs.release-path }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Package charts - id: package - uses: ./.github/actions/chart-package - with: - app-dir: ${{ env.APP_DIR }} - helm-version: ${{ env.HELM_VERSION }} - - - name: Upload release artifacts - uses: actions/upload-artifact@v4 - with: - name: wg-easy-weekly-test-${{ github.run_number }} - path: ${{ steps.package.outputs.release-path }} - retention-days: 7 - - create-resources: - runs-on: ubuntu-24.04 - needs: build-and-package - outputs: - channel-slug: ${{ steps.set-outputs.outputs.channel-slug }} - release-sequence: ${{ steps.set-outputs.outputs.release-sequence }} - customer-id: ${{ steps.set-outputs.outputs.customer-id }} - license-id: ${{ steps.set-outputs.outputs.license-id }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Download release artifacts - uses: actions/download-artifact@v4 - with: - name: wg-easy-weekly-test-${{ github.run_number }} - path: ${{ env.APP_DIR }}/release - - - name: Check if channel exists - id: check-channel - run: | - echo "Checking for existing channel: ${{ env.CHANNEL_NAME }}" - - RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \ - "https://api.replicated.com/vendor/v3/apps/${{ env.REPLICATED_APP }}/channels") - - if [ $? -ne 0 ]; then - echo "curl command failed" - echo "channel-exists=false" >> $GITHUB_OUTPUT - exit 0 - fi - - HTTP_CODE=$(echo "$RESPONSE" | tail -n1) - BODY=$(echo "$RESPONSE" | sed '$d') - - if [ "$HTTP_CODE" != "200" ]; then - echo "API request failed with HTTP $HTTP_CODE" - echo "channel-exists=false" >> $GITHUB_OUTPUT - exit 0 - fi - - CHANNEL_ID=$(echo "$BODY" | jq -r --arg name "${{ env.CHANNEL_NAME }}" \ - 'if .channels then .channels[] | select(.name == $name) | .id else empty end' 2>/dev/null | head -1) - - if [ -n "$CHANNEL_ID" ] && [ "$CHANNEL_ID" != "null" ]; then - echo "Found existing channel: $CHANNEL_ID" - echo "channel-exists=true" >> $GITHUB_OUTPUT - echo "channel-id=$CHANNEL_ID" >> $GITHUB_OUTPUT - echo "channel-slug=${{ env.CHANNEL_NAME }}" >> $GITHUB_OUTPUT - else - echo "Channel does not exist" - echo "channel-exists=false" >> $GITHUB_OUTPUT - fi - - - name: Create Replicated release - id: release - uses: replicatedhq/replicated-actions/create-release@v1.19.0 - with: - app-slug: ${{ env.REPLICATED_APP }} - api-token: ${{ env.REPLICATED_API_TOKEN }} - yaml-dir: ${{ env.APP_DIR }}/release - promote-channel: ${{ env.CHANNEL_NAME }} - - - name: Check if customer exists - id: check-customer - run: | - echo "Checking for existing customer: ${{ env.CUSTOMER_NAME }}" - - RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \ - "https://api.replicated.com/vendor/v3/customers") - - if [ $? -ne 0 ]; then - echo "curl command failed" - echo "customer-exists=false" >> $GITHUB_OUTPUT - exit 0 - fi - - HTTP_CODE=$(echo "$RESPONSE" | tail -n1) - BODY=$(echo "$RESPONSE" | sed '$d') - - if [ "$HTTP_CODE" != "200" ]; then - echo "API request failed with HTTP $HTTP_CODE" - echo "customer-exists=false" >> $GITHUB_OUTPUT - exit 0 - fi - - CUSTOMER_DATA=$(echo "$BODY" | jq -r --arg name "${{ env.CUSTOMER_NAME }}" \ - 'if .customers then .customers[] | select(.name == $name) | {id: .id, created: .createdAt} else empty end' 2>/dev/null \ - | jq -s 'sort_by(.created) | reverse | .[0] // empty' 2>/dev/null) - - CUSTOMER_ID=$(echo "$CUSTOMER_DATA" | jq -r '.id // empty' 2>/dev/null) - - if [ -n "$CUSTOMER_ID" ] && [ "$CUSTOMER_ID" != "null" ]; then - echo "Found existing customer: $CUSTOMER_ID" - echo "customer-exists=true" >> $GITHUB_OUTPUT - echo "customer-id=$CUSTOMER_ID" >> $GITHUB_OUTPUT - - LICENSE_RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: ${{ env.REPLICATED_API_TOKEN }}" \ - "https://api.replicated.com/vendor/v3/customer/$CUSTOMER_ID") - - LICENSE_HTTP_CODE=$(echo "$LICENSE_RESPONSE" | tail -n1) - LICENSE_BODY=$(echo "$LICENSE_RESPONSE" | sed '$d') - - if [ "$LICENSE_HTTP_CODE" = "200" ]; then - LICENSE_ID=$(echo "$LICENSE_BODY" | jq -r '.customer.installationId // empty' 2>/dev/null) - echo "license-id=$LICENSE_ID" >> $GITHUB_OUTPUT - else - echo "Failed to get license ID for customer $CUSTOMER_ID" - echo "customer-exists=false" >> $GITHUB_OUTPUT - fi - else - echo "Customer does not exist" - echo "customer-exists=false" >> $GITHUB_OUTPUT - fi - - - name: Create customer - id: create-customer - if: steps.check-customer.outputs.customer-exists == 'false' - uses: replicatedhq/replicated-actions/create-customer@v1.19.0 - with: - app-slug: ${{ env.REPLICATED_APP }} - api-token: ${{ env.REPLICATED_API_TOKEN }} - customer-name: ${{ env.CUSTOMER_NAME }} - channel-slug: ${{ steps.check-channel.outputs.channel-exists == 'true' && steps.check-channel.outputs.channel-slug || steps.release.outputs.channel-slug }} - license-type: dev - - - name: Set consolidated outputs - id: set-outputs - run: | - if [ "${{ steps.check-channel.outputs.channel-exists }}" == "true" ]; then - echo "channel-slug=${{ steps.check-channel.outputs.channel-slug }}" >> $GITHUB_OUTPUT - else - echo "channel-slug=${{ steps.release.outputs.channel-slug }}" >> $GITHUB_OUTPUT - fi - echo "release-sequence=${{ steps.release.outputs.release-sequence }}" >> $GITHUB_OUTPUT - - if [ "${{ steps.check-customer.outputs.customer-exists }}" == "true" ]; then - echo "customer-id=${{ steps.check-customer.outputs.customer-id }}" >> $GITHUB_OUTPUT - echo "license-id=${{ steps.check-customer.outputs.license-id }}" >> $GITHUB_OUTPUT - else - echo "customer-id=${{ steps.create-customer.outputs.customer-id }}" >> $GITHUB_OUTPUT - echo "license-id=${{ steps.create-customer.outputs.license-id }}" >> $GITHUB_OUTPUT - fi - - test-deployment: - runs-on: ubuntu-24.04 - needs: create-resources - strategy: - matrix: - # Test on latest stable Kubernetes version only for weekly runs - include: - - k8s-version: "v1.35" - distribution: "k3s" - nodes: 1 - instance-type: "r1.small" - timeout-minutes: 15 - fail-fast: true - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup tools - uses: ./.github/actions/setup-tools - with: - helm-version: ${{ env.HELM_VERSION }} - install-helmfile: 'true' - - - name: Create test cluster - id: create-cluster - shell: bash - run: | - CLUSTER_NAME="weekly-test-${{ github.run_number }}" - echo "Creating cluster: $CLUSTER_NAME" - - replicated cluster create \ - --name "$CLUSTER_NAME" \ - --distribution "${{ matrix.distribution }}" \ - --version "${{ matrix.k8s-version }}" \ - --disk "50" \ - --instance-type "${{ matrix.instance-type }}" \ - --nodes "${{ matrix.nodes }}" \ - --ttl "4h" - - if [ $? -ne 0 ]; then - echo "Failed to create cluster" - exit 1 - fi - - echo "Waiting for cluster to be running..." - for i in {1..60}; do - STATUS=$(replicated cluster ls --output json | jq -r '.[] | select(.name == "'$CLUSTER_NAME'") | .status' 2>/dev/null) - if [ "$STATUS" = "running" ]; then - echo "Cluster is running!" - break - fi - echo "Cluster status: $STATUS, waiting... (attempt $i/60)" - sleep 10 - done - - if [ "$STATUS" != "running" ]; then - echo "Cluster failed to reach running state" - exit 1 - fi - - replicated cluster kubeconfig --name "$CLUSTER_NAME" --output-path /tmp/kubeconfig - echo "KUBECONFIG=/tmp/kubeconfig" >> $GITHUB_ENV - echo "CLUSTER_NAME=$CLUSTER_NAME" >> $GITHUB_ENV - - CLUSTER_ID=$(replicated cluster ls --output json | jq -r '.[] | select(.name == "'$CLUSTER_NAME'") | .id' 2>/dev/null) - echo "cluster-id=$CLUSTER_ID" >> $GITHUB_OUTPUT - - - name: Setup cluster ports - working-directory: ${{ env.APP_DIR }} - run: task cluster-ports-expose CLUSTER_NAME="${{ env.CLUSTER_NAME }}" - - - name: Validate cluster readiness - run: | - echo "Validating cluster readiness..." - - if ! kubectl cluster-info; then - echo "ERROR: Cluster API server not accessible" - exit 1 - fi - - if ! kubectl wait --for=condition=Ready nodes --all --timeout=300s; then - echo "ERROR: Cluster nodes did not become ready" - kubectl get nodes -o wide - exit 1 - fi - - echo "✅ Cluster is ready" - kubectl get nodes -o wide - - - name: Deploy application - working-directory: ${{ env.APP_DIR }} - run: | - task customer-helm-install \ - CUSTOMER_NAME="${{ env.CUSTOMER_NAME }}" \ - CLUSTER_NAME="${{ env.CLUSTER_NAME }}" \ - CHANNEL_SLUG="${{ needs.create-resources.outputs.channel-slug }}" \ - REPLICATED_LICENSE_ID="${{ needs.create-resources.outputs.license-id }}" - timeout-minutes: ${{ matrix.timeout-minutes }} - - - name: Run application tests - working-directory: ${{ env.APP_DIR }} - run: task test - timeout-minutes: 10 - - - name: Verify deployment health - run: | - echo "Verifying deployment health..." - - # Check pods are running - kubectl get pods -A - - # Verify WG-Easy pod is running - kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=wg-easy --timeout=300s -n wg-easy || { - echo "WG-Easy pod failed to become ready" - kubectl describe pods -l app.kubernetes.io/name=wg-easy -n wg-easy - kubectl logs -l app.kubernetes.io/name=wg-easy -n wg-easy --tail=100 - exit 1 - } - - echo "✅ Application deployed and healthy" - - - name: Cleanup test cluster - if: always() - run: | - if [ -n "${{ env.CLUSTER_NAME }}" ]; then - echo "Cleaning up cluster: ${{ env.CLUSTER_NAME }}" - replicated cluster rm --name "${{ env.CLUSTER_NAME }}" || echo "Cluster cleanup failed or already removed" - fi - - - name: Upload debug logs on failure - if: failure() - uses: actions/upload-artifact@v4 - with: - name: debug-logs-weekly-test-${{ github.run_number }} - path: | - /tmp/*.log - ~/.replicated/ + run-tests: + uses: ./.github/workflows/wg-easy-pr-validation.yaml + with: + channel-name: weekly-test + customer-name: weekly-test-customer + cleanup-cluster: true + secrets: + WG_EASY_REPLICATED_API_TOKEN: ${{ secrets.WG_EASY_REPLICATED_API_TOKEN }} notify-on-failure: runs-on: ubuntu-24.04 - needs: [validate-charts, build-and-package, create-resources, test-deployment] + needs: run-tests if: failure() steps: - name: Create GitHub Issue on Failure @@ -383,7 +62,7 @@ jobs: --- *This issue was automatically created by the weekly test workflow.*`; - // Check if there's already an open issue for this week + // Check if there's already an open issue for today const issues = await github.rest.issues.listForRepo({ owner: context.repo.owner, repo: context.repo.repo, @@ -398,7 +77,6 @@ jobs: ); if (existingIssue) { - // Add a comment to existing issue await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, @@ -407,7 +85,6 @@ jobs: }); console.log(`Updated existing issue #${existingIssue.number}`); } else { - // Create new issue const issue = await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, @@ -420,7 +97,7 @@ jobs: notify-on-success: runs-on: ubuntu-24.04 - needs: [validate-charts, build-and-package, create-resources, test-deployment] + needs: run-tests if: success() && (github.event.inputs.notify_on_success == 'true' || github.event_name == 'schedule') steps: - name: Comment on latest open issue if exists @@ -429,7 +106,6 @@ jobs: script: | const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; - // Find the most recent open test failure issue const issues = await github.rest.issues.listForRepo({ owner: context.repo.owner, repo: context.repo.repo, @@ -455,7 +131,7 @@ jobs: update-readme-status: runs-on: ubuntu-24.04 - needs: [validate-charts, build-and-package, create-resources, test-deployment] + needs: run-tests if: always() && github.event_name == 'schedule' permissions: contents: write @@ -469,55 +145,40 @@ jobs: run: | README_PATH="applications/wg-easy/README.md" - # Determine test status - if [ "${{ needs.test-deployment.result }}" == "success" ]; then + if [ "${{ needs.run-tests.outputs.test-result }}" == "success" ]; then STATUS="✅ Passing" - EMOJI="✅" else STATUS="❌ Failed" - EMOJI="❌" fi - # Get current date TEST_DATE=$(date -u +"%Y-%m-%d %H:%M UTC") - - # Get workflow run URL RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - # Create updated status table cat > /tmp/status_section.md < ## Test Status | Component | Status | Last Tested | Kubernetes Version | Details | |-----------|--------|-------------|-------------------|---------| - | Chart Installation | ${STATUS} | ${TEST_DATE} | v1.35 | [View Run](${RUN_URL}) | + | Chart Installation | ${STATUS} | ${TEST_DATE} | v1.33 / v1.34 / v1.35 | [View Run](${RUN_URL}) | *Status automatically updated by [weekly test workflow](${RUN_URL})* EOF - # Replace the section in README sed -i.bak '//,//d' "$README_PATH" - - # Find the position after the header and insert new status sed -i.bak '4r /tmp/status_section.md' "$README_PATH" - - # Clean up backup file rm -f "${README_PATH}.bak" echo "Updated README with test status: ${STATUS}" - cat "$README_PATH" | head -20 - name: Check for changes id: check_changes run: | if git diff --quiet applications/wg-easy/README.md; then echo "changed=false" >> $GITHUB_OUTPUT - echo "No changes to commit" else echo "changed=true" >> $GITHUB_OUTPUT - echo "Changes detected" fi - name: Commit and push changes @@ -531,7 +192,7 @@ jobs: Automated test status update from weekly test workflow. - Status: ${{ needs.test-deployment.result }} + Status: ${{ needs.run-tests.outputs.test-result }} Run: ${{ github.run_id }}" git push From 97e37e94cb29e0bc277902de1a2c5ee4423f0c5a Mon Sep 17 00:00:00 2001 From: ada mancini Date: Wed, 1 Apr 2026 13:31:22 -0400 Subject: [PATCH 7/8] fix(wg-easy): address Copilot review feedback on weekly test workflow - Add Details column to README Test Status table to match workflow output - Add permissions: issues: write to notify-on-failure and notify-on-success jobs so GitHub token can create/comment on issues - Filter notify-on-success to only comment on [Weekly Test Failure] issues, avoiding false comments on unrelated issues sharing the same labels - Exclude README.md from wg-easy-image.yml push trigger so automated status updates do not trigger unnecessary image builds --- .github/workflows/wg-easy-image.yml | 1 + .github/workflows/wg-easy-weekly-test.yaml | 20 ++++++++++++++------ applications/wg-easy/README.md | 6 +++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/wg-easy-image.yml b/.github/workflows/wg-easy-image.yml index a3c41da6..a1d1a8c7 100644 --- a/.github/workflows/wg-easy-image.yml +++ b/.github/workflows/wg-easy-image.yml @@ -6,6 +6,7 @@ on: tags: [ 'v*' ] paths: - 'applications/wg-easy/**' + - '!applications/wg-easy/README.md' - '.github/workflows/wg-easy-image.yml' pull_request: paths: diff --git a/.github/workflows/wg-easy-weekly-test.yaml b/.github/workflows/wg-easy-weekly-test.yaml index a5a9e848..9015e6c7 100644 --- a/.github/workflows/wg-easy-weekly-test.yaml +++ b/.github/workflows/wg-easy-weekly-test.yaml @@ -31,6 +31,8 @@ jobs: runs-on: ubuntu-24.04 needs: run-tests if: failure() + permissions: + issues: write steps: - name: Create GitHub Issue on Failure uses: actions/github-script@v7 @@ -99,6 +101,8 @@ jobs: runs-on: ubuntu-24.04 needs: run-tests if: success() && (github.event.inputs.notify_on_success == 'true' || github.event_name == 'schedule') + permissions: + issues: write steps: - name: Comment on latest open issue if exists uses: actions/github-script@v7 @@ -113,20 +117,24 @@ jobs: labels: 'automated-test,wg-easy', sort: 'created', direction: 'desc', - per_page: 1 + per_page: 10 }); - if (issues.data.length > 0) { - const issue = issues.data[0]; + // Only comment on issues that are weekly test failure reports + const failureIssue = issues.data.find(issue => + issue.title.includes('[Weekly Test Failure]') + ); + + if (failureIssue) { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, - issue_number: issue.number, + issue_number: failureIssue.number, body: `✅ **Weekly test passed!**\n\nThe WG-Easy chart is now installing and working correctly.\n\n**Successful Run:** ${runUrl}\n\nConsider closing this issue if the problem is resolved.` }); - console.log(`Added success comment to issue #${issue.number}`); + console.log(`Added success comment to issue #${failureIssue.number}`); } else { - console.log('No open test failure issues found - weekly test passed successfully!'); + console.log('No open weekly test failure issues found - weekly test passed successfully!'); } update-readme-status: diff --git a/applications/wg-easy/README.md b/applications/wg-easy/README.md index 2f0e8996..f09a0d07 100644 --- a/applications/wg-easy/README.md +++ b/applications/wg-easy/README.md @@ -7,9 +7,9 @@ The target scenario: multiple teams, multiple product verticals, one Replicated ## Test Status -| Component | Status | Last Tested | Kubernetes Version | -|-----------|--------|-------------|-------------------| -| Chart Installation | ⏳ Pending | Never | v1.35 | +| Component | Status | Last Tested | Kubernetes Version | Details | +|-----------|--------|-------------|--------------------|---------| +| Chart Installation | ⏳ Pending | Never | v1.33 / v1.34 / v1.35 | - | *Status automatically updated by weekly test workflow* From a2319e2c22f9f138b138901c091e9cebe35e359c Mon Sep 17 00:00:00 2001 From: ada mancini Date: Wed, 1 Apr 2026 14:15:48 -0400 Subject: [PATCH 8/8] fix(wg-easy): anchor README status update on markers, not line number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded `sed '4r ...'` with a sed range that uses the TEST_STATUS_START/END marker comments as anchors. This is robust to README restructuring — the inner content is replaced in-place between the markers regardless of what line they're on. --- .github/workflows/wg-easy-weekly-test.yaml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/wg-easy-weekly-test.yaml b/.github/workflows/wg-easy-weekly-test.yaml index 9015e6c7..7ef2163a 100644 --- a/.github/workflows/wg-easy-weekly-test.yaml +++ b/.github/workflows/wg-easy-weekly-test.yaml @@ -162,20 +162,26 @@ jobs: TEST_DATE=$(date -u +"%Y-%m-%d %H:%M UTC") RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - cat > /tmp/status_section.md < + # Write only the inner content (the markers remain in place as anchors) + cat > /tmp/status_inner.md < + EOF - sed -i.bak '//,//d' "$README_PATH" - sed -i.bak '4r /tmp/status_section.md' "$README_PATH" + # Replace content between the marker comments, keeping the markers as anchors + sed -i.bak '//,//{ + //{ + r /tmp/status_inner.md + } + //!{//!d} + }' "$README_PATH" rm -f "${README_PATH}.bak" echo "Updated README with test status: ${STATUS}"