name: Terraform CI/CD on: push: branches: - dev paths: - 'terraform/**' - '.gitea/workflows/terraform.yml' pull_request: branches: - dev paths: - 'terraform/**' - '.gitea/workflows/terraform.yml' env: TF_WORKING_DIR: terraform/prod # Backend S3 (Scaleway) AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # Terraform variables TF_VAR_proxmox_url: ${{ secrets.PROXMOX_URL }} TF_VAR_proxmox_api_token: ${{ secrets.PROXMOX_API_TOKEN }} TF_VAR_tailscale_auth_key: ${{ secrets.TAILSCALE_AUTH_KEY }} jobs: # =========================================================================== # Validation - Runs on all pushes and PRs # =========================================================================== validate: name: Validate runs-on: self-hosted container: image: gitea.arnodo.fr/damien/terraform-ci:latest steps: - name: Checkout run: | git clone --depth 1 --branch ${{ gitea.ref_name }} https://gitea.arnodo.fr/${{ gitea.repository }}.git . git checkout ${{ gitea.sha }} - name: Terraform Format Check id: fmt run: terraform fmt -check -recursive -diff working-directory: ${{ env.TF_WORKING_DIR }} continue-on-error: true - name: TFLint id: lint run: tflint --recursive --format compact working-directory: ${{ env.TF_WORKING_DIR }} continue-on-error: true - name: Terraform Init id: init run: terraform init -input=false working-directory: ${{ env.TF_WORKING_DIR }} - name: Terraform Validate id: validate run: terraform validate -no-color working-directory: ${{ env.TF_WORKING_DIR }} - name: Validation Summary if: always() run: | echo "## Validation Results" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY echo "| Format | ${{ steps.fmt.outcome == 'success' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY echo "| TFLint | ${{ steps.lint.outcome == 'success' && '✅' || '⚠️' }} |" >> $GITHUB_STEP_SUMMARY echo "| Init | ${{ steps.init.outcome == 'success' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY echo "| Validate | ${{ steps.validate.outcome == 'success' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY - name: Check Validation Status if: steps.fmt.outcome == 'failure' || steps.init.outcome == 'failure' || steps.validate.outcome == 'failure' run: exit 1 # =========================================================================== # Plan - Runs on PRs and pushes to dev # =========================================================================== plan: name: Plan runs-on: self-hosted container: image: gitea.arnodo.fr/damien/terraform-ci:latest needs: validate outputs: plan_exitcode: ${{ steps.plan.outputs.exitcode }} steps: - name: Checkout run: | git clone --depth 1 --branch ${{ gitea.ref_name }} https://gitea.arnodo.fr/${{ gitea.repository }}.git . git checkout ${{ gitea.sha }} - name: Terraform Init run: terraform init -input=false working-directory: ${{ env.TF_WORKING_DIR }} - name: Terraform Plan id: plan run: | set +e terraform plan -input=false -no-color -detailed-exitcode -out=tfplan 2>&1 | tee plan_output.txt EXITCODE=$? echo "exitcode=${EXITCODE}" >> $GITHUB_OUTPUT # Exit 0 for no changes, exit 2 for changes - both are OK if [ $EXITCODE -eq 1 ]; then exit 1 fi exit 0 working-directory: ${{ env.TF_WORKING_DIR }} - name: Save Plan Output if: always() run: | mkdir -p /tmp/tfplan-artifact cp tfplan /tmp/tfplan-artifact/ 2>/dev/null || true cp plan_output.txt /tmp/tfplan-artifact/ 2>/dev/null || true working-directory: ${{ env.TF_WORKING_DIR }} - name: Plan Summary if: always() run: | echo "## Terraform Plan" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Exit code:** \`${{ steps.plan.outputs.exitcode }}\`" >> $GITHUB_STEP_SUMMARY echo "- \`0\` = No changes" >> $GITHUB_STEP_SUMMARY echo "- \`2\` = Changes detected" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY cat plan_output.txt >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY working-directory: ${{ env.TF_WORKING_DIR }} - name: Comment PR with Plan if: gitea.event_name == 'pull_request' env: GIT_TOKEN: ${{ secrets.GIT_TOKEN }} run: | PLAN_OUTPUT=$(cat plan_output.txt | head -c 60000) COMMENT_BODY=$(cat < Click to expand plan output \`\`\`hcl ${PLAN_OUTPUT} \`\`\` EOF ) # Post comment via Gitea API curl -s -X POST \ -H "Authorization: token ${GIT_TOKEN}" \ -H "Content-Type: application/json" \ -d "$(jq -n --arg body "$COMMENT_BODY" '{body: $body}')" \ "https://gitea.arnodo.fr/api/v1/repos/${{ gitea.repository }}/issues/${{ gitea.event.pull_request.number }}/comments" working-directory: ${{ env.TF_WORKING_DIR }} # =========================================================================== # Apply - Runs on push to dev only (when changes detected) # =========================================================================== apply: name: Apply runs-on: self-hosted container: image: gitea.arnodo.fr/damien/terraform-ci:latest needs: plan if: | gitea.event_name == 'push' && gitea.ref == 'refs/heads/dev' && needs.plan.outputs.plan_exitcode == '2' steps: - name: Checkout run: | git clone --depth 1 --branch ${{ gitea.ref_name }} https://gitea.arnodo.fr/${{ gitea.repository }}.git . git checkout ${{ gitea.sha }} - name: Terraform Init run: terraform init -input=false working-directory: ${{ env.TF_WORKING_DIR }} - name: Terraform Plan and Apply run: | terraform plan -input=false -out=tfplan terraform apply -input=false -auto-approve tfplan working-directory: ${{ env.TF_WORKING_DIR }} - name: Apply Summary if: always() run: | echo "## ✅ Terraform Apply Complete" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Infrastructure has been updated successfully." >> $GITHUB_STEP_SUMMARY