name: Reconcile on: push: branches: - main paths: - "state/*.json" jobs: detect: runs-on: ubuntu-latest outputs: envs: ${{ steps.changed.outputs.envs }} steps: - uses: actions/checkout@v4 with: fetch-depth: 2 - name: Detect changed environments id: changed run: | FILES=$(git diff --name-only HEAD~1 HEAD -- 'state/*.json') ENVS="[]" for f in $FILES; do ENV=$(basename "$f" .json) ENVS=$(echo "$ENVS" | jq -c ". + [\"$ENV\"]") done echo "envs=$ENVS" >> "$GITHUB_OUTPUT" echo "Changed environments: $ENVS" reconcile: needs: detect runs-on: ubuntu-latest if: needs.detect.outputs.envs != '[]' strategy: matrix: env: ${{ fromJson(needs.detect.outputs.envs) }} steps: - uses: actions/checkout@v4 - name: Resolve environment secrets id: env run: | ENV_UPPER=$(echo "${{ matrix.env }}" | tr '[:lower:]-' '[:upper:]_') echo "token_key=${ENV_UPPER}_RECONCILER_TOKEN" >> "$GITHUB_OUTPUT" echo "url_key=${ENV_UPPER}_RECONCILER_URL" >> "$GITHUB_OUTPUT" echo "age_key=${ENV_UPPER}_AGE_PUBLIC_KEY" >> "$GITHUB_OUTPUT" - name: Sync events env: RECONCILER_TOKEN: ${{ secrets[steps.env.outputs.token_key] }} RECONCILER_URL: ${{ secrets[steps.env.outputs.url_key] }} run: | if [ -z "$RECONCILER_URL" ] || [ -z "$RECONCILER_TOKEN" ]; then echo "No secrets configured for environment '${{ matrix.env }}' — skipping" exit 0 fi curl -sf \ -X POST \ -H "Authorization: Bearer ${RECONCILER_TOKEN}" \ "${RECONCILER_URL}/sync-events" - name: Pull latest (poller may have committed) run: git pull --rebase - name: Apply reconcile id: reconcile env: RECONCILER_TOKEN: ${{ secrets[steps.env.outputs.token_key] }} RECONCILER_URL: ${{ secrets[steps.env.outputs.url_key] }} run: | RESPONSE=$(curl -sf \ -X POST \ -H "Authorization: Bearer ${RECONCILER_TOKEN}" \ -H "Content-Type: application/json" \ -d @state/${{ matrix.env }}.json \ "${RECONCILER_URL}/reconcile") echo "response<> "$GITHUB_OUTPUT" echo "$RESPONSE" >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" STATUS=$(echo "$RESPONSE" | jq -r '.status') if [ "$STATUS" = "error" ]; then echo "Reconcile failed for ${{ matrix.env }}" echo "$RESPONSE" | jq . exit 1 fi - name: Encrypt and upload setup keys if: success() env: AGE_PUBLIC_KEY: ${{ secrets[steps.env.outputs.age_key] }} run: | KEYS=$(echo '${{ steps.reconcile.outputs.response }}' | jq -r '.created_keys // empty') if [ -n "$KEYS" ] && [ "$KEYS" != "{}" ] && [ "$KEYS" != "null" ] && [ -n "$AGE_PUBLIC_KEY" ]; then echo "$KEYS" | age -r "$AGE_PUBLIC_KEY" -o setup-keys-${{ matrix.env }}.age echo "Setup keys for ${{ matrix.env }} encrypted" else echo "No new keys created for ${{ matrix.env }}" exit 0 fi - name: Upload artifact if: success() uses: actions/upload-artifact@v4 with: name: setup-keys-${{ matrix.env }} path: setup-keys-${{ matrix.env }}.age if-no-files-found: ignore