diff --git a/.gitea/workflows/poc-dispatch.yml b/.gitea/workflows/poc-dispatch.yml index f998a9c..6adca10 100644 --- a/.gitea/workflows/poc-dispatch.yml +++ b/.gitea/workflows/poc-dispatch.yml @@ -1,5 +1,5 @@ -name: POC GitOps Dispatch -run-name: "POC (${{ inputs.dispatch_id || 'orchestrator' }})" +name: POC GitOps E2E +run-name: "POC E2E (${{ inputs.dispatch_id || 'orchestrator' }})" on: push: branches: @@ -7,14 +7,11 @@ on: workflow_dispatch: inputs: dispatch_id: - required: true - type: string - type: - required: true + required: false type: string jobs: - orchestrator: + e2e: if: github.event_name == 'push' runs-on: ubuntu-latest steps: @@ -24,39 +21,46 @@ jobs: ID=$(date +%s | md5sum | head -c 8) echo "dispatch_id=$ID" >> "$GITHUB_OUTPUT" - - name: Dispatch test run with dispatch_id + - name: Dispatch to gitea-ci-gitops-tests run: | INPUTS=$(jq -nc \ --arg dispatch_id "${{ steps.gen.outputs.dispatch_id }}" \ - --arg type "auto" \ - '{dispatch_id: $dispatch_id, type: $type}') + --arg file "dev/Chart.yaml" \ + --arg yq_tpl '.version = "{{VERSION}}"' \ + --arg version "0.2.0" \ + --arg source_repo "${{ github.repository }}" \ + --arg source_commit "${{ github.sha }}" \ + '{dispatch_id: $dispatch_id, file: $file, yq_tpl: $yq_tpl, version: $version, source_repo: $source_repo, source_commit: $source_commit}') curl -s -X POST \ - "${{ gitea.server_url }}/api/v1/repos/${{ github.repository }}/actions/workflows/poc-dispatch.yml/dispatches" \ - -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \ + "https://gitea.app.keskikuja.site/api/v1/repos/niko/gitea-ci-gitops-tests/actions/workflows/gitops-service.yaml/dispatches" \ + -H "Authorization: token ${{ secrets.GITOPS_DISPATCH_TOKEN }}" \ -H "Content-Type: application/json" \ - -d "$(jq -nc --arg ref "${{ github.ref_name }}" --argjson inputs "$INPUTS" '{ref: $ref, inputs: $inputs}')" + -d "$(jq -nc --arg ref "main" --argjson inputs "$INPUTS" '{ref: "main", inputs: $inputs}')" - - name: Poll for dispatched run + - name: Poll for completion run: | ID=${{ steps.gen.outputs.dispatch_id }} - for i in $(seq 1 12); do - RUNS=$(curl -s "${{ gitea.server_url }}/api/v1/repos/${{ github.repository }}/actions/runs?event=workflow_dispatch&limit=10" \ - -H "Authorization: token ${{ secrets.GITEA_TOKEN }}") + for i in $(seq 1 60); do + RUNS=$(curl -s "https://gitea.app.keskikuja.site/api/v1/repos/niko/gitea-ci-gitops-tests/actions/runs?event=workflow_dispatch&limit=10" \ + -H "Authorization: token ${{ secrets.GITOPS_DISPATCH_TOKEN }}") FOUND=$(echo "$RUNS" | jq -r --arg id "$ID" \ - '[.workflow_runs[] | select(.display_title | contains($id))] | .[0].id // empty') - if [ -n "$FOUND" ]; then - echo "POC PASS: found run id=$FOUND with display_title containing '$ID'" - exit 0 + '[.workflow_runs[] | select(.display_title | contains($id))] | .[0]') + if [ -n "$FOUND" ] && [ "$FOUND" != "null" ]; then + STATUS=$(echo "$FOUND" | jq -r '.status') + CONCLUSION=$(echo "$FOUND" | jq -r '.conclusion // ""') + RUN_NUM=$(echo "$FOUND" | jq -r '.run_number') + echo "run found: id=$(echo $FOUND | jq -r '.id') number=$RUN_NUM display_title=$(echo $FOUND | jq -r '.display_title') status=$STATUS" + if [ "$STATUS" = "completed" ]; then + if [ "$CONCLUSION" = "success" ]; then + echo "POC PASS: gitops workflow completed successfully" + exit 0 + else + echo "POC FAIL: gitops workflow completed with conclusion=$CONCLUSION" + exit 1 + fi + fi fi sleep 5 done - echo "POC FAIL: no run found with display_title containing '$ID'" - echo "$RUNS" - exit 1 - - test: - if: github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - steps: - - run: | - echo "POC dispatch_id=${{ inputs.dispatch_id }} type=${{ inputs.type }}" + echo "POC FAIL: timeout waiting for gitops workflow" + exit 124 diff --git a/scripts/dispatch-workflow.sh b/scripts/dispatch-workflow.sh index 1564c84..280a62d 100755 --- a/scripts/dispatch-workflow.sh +++ b/scripts/dispatch-workflow.sh @@ -17,6 +17,11 @@ POLL_INTERVAL="${DISPATCH_POLL_INTERVAL:-10}" [ -z "$GITEA_API_URL" ] && echo "ERROR: gitea_api_url argument is required" >&2 && exit 1 [ -z "$GITEA_TOKEN" ] && echo "ERROR: gitea_token argument is required" >&2 && exit 1 +# Generate unique dispatch_id for display_title matching +# Can be overridden via DISPATCH_ID env var (for tests) +DISPATCH_ID="${DISPATCH_ID:-$(xxd -l 4 -p /dev/urandom 2>/dev/null || openssl rand -hex 4 2>/dev/null || date +%s | md5sum | head -c 8)}" +INPUTS_JSON=$(echo "$INPUTS_JSON" | jq --arg id "$DISPATCH_ID" '. + {dispatch_id: $id}') + DISPATCH_URL="$GITEA_API_URL/api/v1/repos/$TARGET_REPO/actions/workflows/$WORKFLOW_FILE/dispatches" DISPATCH_BODY=$(jq -nc --arg ref "$REF" --argjson inputs "$INPUTS_JSON" '{ref: $ref, inputs: $inputs}') @@ -32,19 +37,30 @@ if [ "$DISPATCH_CODE" != "201" ]; then exit 1 fi -RUNS_URL="$GITEA_API_URL/api/v1/repos/$TARGET_REPO/actions/runs?status=running" -RUNS_RESP=$(curl -s --connect-timeout 5 --max-time 10 \ - -H "Authorization: token $GITEA_TOKEN" "$RUNS_URL") - -RUN_ID=$(echo "$RUNS_RESP" | jq -r '.workflow_runs[0].id // empty') -if [ -z "$RUN_ID" ] || [ "$RUN_ID" = "null" ]; then - echo "ERROR: Could not find dispatched workflow run" >&2 - exit 1 -fi - +# Poll: find dispatched run by display_title matching +RUN_ID="" TIMEOUT_SECONDS=$(awk "BEGIN {printf \"%.3f\", $TIMEOUT_MINUTES * 60}") START_TIME=$(date +%s) +while [ -z "$RUN_ID" ]; do + NOW=$(date +%s) + ELAPSED=$((NOW - START_TIME)) + if awk -v e="$ELAPSED" -v t="$TIMEOUT_SECONDS" 'BEGIN { exit !(e >= t) }'; then + echo "ERROR: Timeout after ${TIMEOUT_MINUTES} minutes — run not found" >&2 + exit 124 + fi + + RUNS_RESP=$(curl -s --connect-timeout 5 --max-time 10 \ + "$GITEA_API_URL/api/v1/repos/$TARGET_REPO/actions/runs?event=workflow_dispatch&limit=10" \ + -H "Authorization: token $GITEA_TOKEN") + + RUN_ID=$(echo "$RUNS_RESP" | jq -r --arg id "$DISPATCH_ID" \ + '[.workflow_runs[] | select(.display_title | contains($id))] | .[0].id // empty') + + [ -z "$RUN_ID" ] && sleep "$POLL_INTERVAL" +done + +# Poll: wait for run to complete while true; do NOW=$(date +%s) ELAPSED=$((NOW - START_TIME)) diff --git a/scripts/gitops-update.sh b/scripts/gitops-update.sh index 3537d04..4317461 100755 --- a/scripts/gitops-update.sh +++ b/scripts/gitops-update.sh @@ -7,21 +7,14 @@ _gitops_fail() { local MSG="${1:-GitOps update failed}" echo "[ERROR] ${MSG}" >&2 - if [ -n "${SOURCE_REPO:-}" ] && [ -n "${SOURCE_COMMIT:-}" ] && \ + if [ -n "${GITOPS_REPO:-}" ] && [ -n "${GITOPS_SHA:-}" ] && \ + [ -n "${SOURCE_REPO:-}" ] && [ -n "${SOURCE_COMMIT:-}" ] && \ [ -n "${GITEA_API_URL:-}" ] && [ -n "${GITEA_TOKEN:-}" ]; then - local GITOPS_URL="${GITEA_API_URL}/${GITOPS_REPO:-unknown}/commits/${GITOPS_SHA:-unknown}" - ROOT_REPO="${SOURCE_REPO}" ROOT_COMMIT="${SOURCE_COMMIT}" \ + local SOURCE_URL="${GITEA_API_URL}/${SOURCE_REPO}/commits/${SOURCE_COMMIT}" + ROOT_REPO="${GITOPS_REPO}" ROOT_COMMIT="${GITOPS_SHA}" \ GITEA_API_URL="${GITEA_API_URL}" GITEA_TOKEN="${GITEA_TOKEN}" \ bash "${SCRIPT_DIR}/report-status.sh" failure "${MSG}" \ - "gitops/${SOURCE_REPO}" "" "${GITOPS_URL}" 2>/dev/null || true - - if [ -n "${GITOPS_REPO:-}" ] && [ -n "${GITOPS_SHA:-}" ]; then - local SOURCE_URL="${GITEA_API_URL}/${SOURCE_REPO}/commits/${SOURCE_COMMIT}" - ROOT_REPO="${GITOPS_REPO}" ROOT_COMMIT="${GITOPS_SHA}" \ - GITEA_API_URL="${GITEA_API_URL}" GITEA_TOKEN="${GITEA_TOKEN}" \ - bash "${SCRIPT_DIR}/report-status.sh" failure "${MSG}" \ - "source/${SOURCE_REPO}" "" "${SOURCE_URL}" 2>/dev/null || true - fi + "source/${SOURCE_REPO}" "" "${SOURCE_URL}" 2>/dev/null || true fi exit 1 @@ -39,14 +32,8 @@ _gitops_validate() { } _gitops_success() { - local GITOPS_URL="${GITEA_API_URL}/${GITOPS_REPO}/commits/${GITOPS_SHA}" local SOURCE_URL="${GITEA_API_URL}/${SOURCE_REPO}/commits/${SOURCE_COMMIT}" - ROOT_REPO="${SOURCE_REPO}" ROOT_COMMIT="${SOURCE_COMMIT}" \ - GITEA_API_URL="${GITEA_API_URL}" GITEA_TOKEN="${GITEA_TOKEN}" \ - bash "${SCRIPT_DIR}/report-status.sh" success "GitOps updated to ${VERSION}" \ - "gitops/${SOURCE_REPO}" "" "${GITOPS_URL}" - ROOT_REPO="${GITOPS_REPO}" ROOT_COMMIT="${GITOPS_SHA}" \ GITEA_API_URL="${GITEA_API_URL}" GITEA_TOKEN="${GITEA_TOKEN}" \ bash "${SCRIPT_DIR}/report-status.sh" success "Source build" \ diff --git a/tests/dispatch-workflow.bats b/tests/dispatch-workflow.bats index 3f271de..778ea81 100644 --- a/tests/dispatch-workflow.bats +++ b/tests/dispatch-workflow.bats @@ -3,6 +3,7 @@ setup() { source tests/helpers/mock-api.sh export DISPATCH_POLL_INTERVAL="0.1" + export DISPATCH_ID="test123" } teardown() { @@ -12,8 +13,7 @@ teardown() { @test "dispatch succeeds: POST 201, poll running x3 then success → exit 0" { mock_set_sequence '[ {"code":201}, - {"code":200,"body":{"workflow_runs":[{"id":1,"status":"running"}]}}, - {"code":200,"body":{"id":1,"status":"running"}}, + {"code":200,"body":{"workflow_runs":[{"id":1,"display_title":"POC (test123)","run_number":42,"status":"running"}]}}, {"code":200,"body":{"id":1,"status":"running"}}, {"code":200,"body":{"id":1,"status":"running"}}, {"code":200,"body":{"id":1,"status":"completed","conclusion":"success"}} @@ -26,7 +26,7 @@ teardown() { @test "dispatch: poll returns failure conclusion → exit 1" { mock_set_sequence '[ {"code":201}, - {"code":200,"body":{"workflow_runs":[{"id":1,"status":"running"}]}}, + {"code":200,"body":{"workflow_runs":[{"id":1,"display_title":"POC (test123)","run_number":42,"status":"running"}]}}, {"code":200,"body":{"id":1,"status":"running"}}, {"code":200,"body":{"id":1,"status":"completed","conclusion":"failure"}} ]' @@ -38,7 +38,7 @@ teardown() { @test "dispatch: poll returns cancelled conclusion → exit 1" { mock_set_sequence '[ {"code":201}, - {"code":200,"body":{"workflow_runs":[{"id":1,"status":"running"}]}}, + {"code":200,"body":{"workflow_runs":[{"id":1,"display_title":"POC (test123)","run_number":42,"status":"running"}]}}, {"code":200,"body":{"id":1,"status":"running"}}, {"code":200,"body":{"id":1,"status":"completed","conclusion":"cancelled"}} ]' @@ -47,18 +47,18 @@ teardown() { [ "$status" -eq 1 ] } -@test "timeout: poll never completes, exceeds timeout_minutes → exit 124" { +@test "timeout: no matching run found, exceeds timeout_minutes → exit 124" { mock_set_sequence '[ {"code":201}, - {"code":200,"body":{"workflow_runs":[{"id":1,"status":"running"}]}}, - {"code":200,"body":{"id":1,"status":"running"}}, - {"code":200,"body":{"id":1,"status":"running"}}, - {"code":200,"body":{"id":1,"status":"running"}}, - {"code":200,"body":{"id":1,"status":"running"}}, - {"code":200,"body":{"id":1,"status":"running"}}, - {"code":200,"body":{"id":1,"status":"running"}}, - {"code":200,"body":{"id":1,"status":"running"}}, - {"code":200,"body":{"id":1,"status":"running"}} + {"code":200,"body":{"workflow_runs":[]}}, + {"code":200,"body":{"workflow_runs":[]}}, + {"code":200,"body":{"workflow_runs":[]}}, + {"code":200,"body":{"workflow_runs":[]}}, + {"code":200,"body":{"workflow_runs":[]}}, + {"code":200,"body":{"workflow_runs":[]}}, + {"code":200,"body":{"workflow_runs":[]}}, + {"code":200,"body":{"workflow_runs":[]}}, + {"code":200,"body":{"workflow_runs":[]}} ]' mock_start run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "http://localhost:18080" "test-token-abc123" "0.001" @@ -77,7 +77,7 @@ teardown() { @test "POST dispatch is called with correct URL and payload" { mock_set_sequence '[ {"code":201}, - {"code":200,"body":{"workflow_runs":[{"id":1,"status":"running"}]}}, + {"code":200,"body":{"workflow_runs":[{"id":1,"display_title":"POC (test123)","run_number":42,"status":"running"}]}}, {"code":200,"body":{"id":1,"status":"completed","conclusion":"success"}} ]' mock_start @@ -91,6 +91,7 @@ teardown() { [[ "$body" == *'"ref":"main"'* ]] [[ "$body" == *'"inputs"'* ]] [[ "$body" == *'"version":"1.2.3"'* ]] + [[ "$body" == *'"dispatch_id":"test123"'* ]] } @test "missing gitea_api_url argument → exit 1 with error message" { @@ -120,15 +121,15 @@ teardown() { [ "$status" -eq 1 ] } -@test "dispatch: no workflow run found after dispatch → exit 1" { +@test "dispatch: no workflow run found after dispatch → exit 124 (timeout)" { mock_set_sequence '[ {"code":201}, {"code":200,"body":{"workflow_runs":[]}} ]' mock_start - run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{}' "http://localhost:18080" "test-token-abc123" - [ "$status" -eq 1 ] - [[ "$output" == *"ERROR"* ]] + run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{}' "http://localhost:18080" "test-token-abc123" "0.001" + [ "$status" -eq 124 ] + [[ "$output" == *"ERROR"* || "$output" == *"Timeout"* ]] } @test "missing inputs_json argument → exit 1" {