241 lines
10 KiB
YAML
241 lines
10 KiB
YAML
name: Gitea Actions - Terraform CI/CD
|
||
run-name: ${{ gitea.actor }} is running Terraform CI/CD 🚀
|
||
|
||
on:
|
||
pull_request:
|
||
types: [opened, synchronize, reopened]
|
||
branches:
|
||
- main # Проверка PRs В main
|
||
- dev # Проверка PRs В dev
|
||
|
||
push:
|
||
branches:
|
||
- dev # При push в dev создаем PR в main
|
||
- main # При push в main применяем изменения
|
||
|
||
permissions:
|
||
contents: write
|
||
pull-requests: write
|
||
|
||
jobs:
|
||
terraform-ci:
|
||
runs-on: code-runner00
|
||
env:
|
||
GITEA_URL: "http://code.hlc.lab" # ⚠️ ЗАМЕНИТЕ на ваш URL
|
||
GITEA_TOKEN: ${{ secrets.GITEATOKEN }}
|
||
|
||
# Terraform переменные
|
||
TF_IN_AUTOMATION: "true"
|
||
TF_VAR_PVE_CLUSTER_API_ID: ${{ secrets.TF_VAR_PVE_CLUSTER_API_ID }}
|
||
TF_VAR_PVE_CLUSTER_API_TOKEN: ${{ secrets.TF_VAR_PVE_CLUSTER_API_TOKEN }}
|
||
TF_VAR_PVE_CLUSTER_URL: ${{ secrets.TF_VAR_PVE_CLUSTER_URL }}
|
||
TF_VAR_POWERDNS_API_URL: ${{ secrets.TF_VAR_POWERDNS_API_URL }}
|
||
TF_VAR_POWERDNS_API_TOKEN: ${{ secrets.TF_VAR_POWERDNS_API_TOKEN }}
|
||
|
||
# AWS переменные (если используете)
|
||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||
|
||
steps:
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0 # Для корректной работы с ветками
|
||
|
||
- name: Create PR from dev to main
|
||
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
|
||
id: create-pr
|
||
run: |
|
||
echo "🔄 Создание Pull Request из dev в main..."
|
||
|
||
# Проверяем, существует ли уже открытый PR
|
||
EXISTING_PR=$(curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||
"$GITEA_URL/api/v1/repos/${{ github.repository }}/pulls?state=open&base=main&head=dev" | jq length)
|
||
|
||
if [ "$EXISTING_PR" -eq "0" ]; then
|
||
# Создаем новый PR
|
||
RESPONSE=$(curl -s -X POST \
|
||
-H "Authorization: token $GITEA_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{
|
||
\"title\": \"[Auto] Merge dev → main - $(date +'%Y-%m-%d %H:%M')\",
|
||
\"body\": \"Автоматически созданный PR из ветки dev.\\n\\n**Изменения:**\\n${{ github.event.head_commit.message }}\",
|
||
\"head\": \"dev\",
|
||
\"base\": \"main\",
|
||
\"draft\": false
|
||
}" \
|
||
"$GITEA_URL/api/v1/repos/${{ github.repository }}/pulls")
|
||
|
||
PR_NUMBER=$(echo "$RESPONSE" | jq -r '.number // empty')
|
||
|
||
if [ -n "$PR_NUMBER" ]; then
|
||
echo "✅ PR создан: #$PR_NUMBER"
|
||
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "❌ Ошибка создания PR"
|
||
echo "$RESPONSE" | jq .
|
||
fi
|
||
else
|
||
echo "📝 PR уже существует, обновляем..."
|
||
echo "pr_number=existing" >> $GITHUB_OUTPUT
|
||
fi
|
||
|
||
- name: Setup Terraform
|
||
run: |
|
||
# Проверяем установку Terraform
|
||
terraform version
|
||
echo "Terraform готов к работе"
|
||
|
||
# 📋 Terraform проверки (выполняются всегда)
|
||
- name: Terraform Format Check
|
||
id: fmt
|
||
run: |
|
||
if terraform fmt -check -recursive; then
|
||
echo "✅ Форматирование корректно"
|
||
echo "fmt_status=passed" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "⚠️ Найдены проблемы с форматированием"
|
||
echo "fmt_status=failed" >> $GITHUB_OUTPUT
|
||
fi
|
||
continue-on-error: true
|
||
|
||
- name: Terraform Init
|
||
id: init
|
||
run: terraform init
|
||
|
||
- name: Terraform Validate
|
||
id: validate
|
||
run: terraform validate -no-color
|
||
|
||
# 🗺️ Terraform Plan (для PR и push в dev)
|
||
- name: Terraform Plan
|
||
id: plan
|
||
if: |
|
||
github.event_name == 'pull_request' ||
|
||
(github.event_name == 'push' && github.ref == 'refs/heads/dev')
|
||
run: |
|
||
echo "📋 Генерация Terraform плана..."
|
||
|
||
# Генерируем план
|
||
terraform plan -no-color -out=tfplan 2>&1 | tee plan_output.txt
|
||
|
||
# Проверяем наличие опасных изменений
|
||
if grep -q "destroy and then create replacement" plan_output.txt; then
|
||
echo "has_replace=true" >> $GITHUB_OUTPUT
|
||
echo "replace_count=$(grep -c 'destroy and then create replacement' plan_output.txt)" >> $GITHUB_OUTPUT
|
||
|
||
# Извлекаем имена ресурсов для замены
|
||
grep -B1 "destroy and then create replacement" plan_output.txt | grep "# " | sed 's/^[[:space:]]*#[[:space:]]*//' > replace_resources.txt
|
||
echo "replace_resources<<EOF" >> $GITHUB_OUTPUT
|
||
cat replace_resources.txt >> $GITHUB_OUTPUT
|
||
echo "EOF" >> $GITHUB_OUTPUT
|
||
fi
|
||
|
||
if grep -q "^[[:space:]]*\- destroy" plan_output.txt; then
|
||
echo "has_destroy=true" >> $GITHUB_OUTPUT
|
||
echo "destroy_count=$(grep -c '^[[:space:]]*\- destroy' plan_output.txt)" >> $GITHUB_OUTPUT
|
||
|
||
# Извлекаем имена ресурсов для удаления
|
||
grep "^[[:space:]]*\- destroy" plan_output.txt -B1 | grep "# " | sed 's/^[[:space:]]*#[[:space:]]*//' > destroy_resources.txt
|
||
echo "destroy_resources<<EOF" >> $GITHUB_OUTPUT
|
||
cat destroy_resources.txt >> $GITHUB_OUTPUT
|
||
echo "EOF" >> $GITHUB_OUTPUT
|
||
fi
|
||
|
||
# Сохраняем полный вывод плана
|
||
PLAN_CONTENT=$(cat plan_output.txt | jq -Rs .)
|
||
echo "plan_content=$PLAN_CONTENT" >> $GITHUB_OUTPUT
|
||
|
||
echo "✅ План сгенерирован"
|
||
|
||
- name: Create Gitea PR Comment
|
||
if: github.event_name == 'pull_request'
|
||
env:
|
||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||
REPO_OWNER: ${{ github.repository_owner }}
|
||
REPO_NAME: ${{ github.event.repository.name }}
|
||
run: |
|
||
echo "💬 Добавление комментария в PR #$PR_NUMBER..."
|
||
|
||
# Проверяем, сгенерирован ли план
|
||
if [ ! -f "plan_output.txt" ]; then
|
||
echo "⚠️ Файл плана не найден, пропускаем комментарий"
|
||
exit 0
|
||
fi
|
||
|
||
# Создаем Markdown комментарий
|
||
cat > comment.md << 'EOF'
|
||
## 🔧 Terraform Plan Report
|
||
|
||
### 📊 Проверки:
|
||
EOF
|
||
|
||
# Добавляем статусы
|
||
echo "- **Format Check:** ${{ steps.fmt.outputs.fmt_status == 'passed' && '✅ passed' || '⚠️ failed' }}" >> comment.md
|
||
echo "- **Validation:** ${{ steps.validate.outcome == 'success' && '✅ passed' || '❌ failed' }}" >> comment.md
|
||
echo "- **Plan:** ${{ steps.plan.outcome == 'success' && '✅ generated' || '⚠️ issues' }}" >> comment.md
|
||
|
||
# Предупреждения о деструктивных изменениях
|
||
if [ "${{ steps.plan.outputs.has_replace }}" = "true" ]; then
|
||
echo "" >> comment.md
|
||
echo "### ⚠️ REPLACE OPERATIONS DETECTED!" >> comment.md
|
||
echo "**${{ steps.plan.outputs.replace_count }}** ресурсов будут пересозданы:" >> comment.md
|
||
echo '```' >> comment.md
|
||
echo "${{ steps.plan.outputs.replace_resources }}" >> comment.md
|
||
echo '```' >> comment.md
|
||
fi
|
||
|
||
if [ "${{ steps.plan.outputs.has_destroy }}" = "true" ]; then
|
||
echo "" >> comment.md
|
||
echo "### 🗑️ DESTROY OPERATIONS DETECTED!" >> comment.md
|
||
echo "**${{ steps.plan.outputs.destroy_count }}** ресурсов будут удалены:" >> comment.md
|
||
echo '```' >> comment.md
|
||
echo "${{ steps.plan.outputs.destroy_resources }}" >> comment.md
|
||
echo '```' >> comment.md
|
||
fi
|
||
|
||
# Полный план
|
||
echo "" >> comment.md
|
||
echo "<details>" >> comment.md
|
||
echo "<summary>📄 Full Terraform Plan Output</summary>" >> comment.md
|
||
echo "" >> comment.md
|
||
echo '```terraform' >> comment.md
|
||
cat plan_output.txt >> comment.md
|
||
echo '```' >> comment.md
|
||
echo "</details>" >> comment.md
|
||
|
||
# Отправляем через Gitea API
|
||
COMMENT_BODY=$(jq -n --arg body "$(cat comment.md)" '{"body": $body}')
|
||
|
||
curl -X POST \
|
||
-H "Authorization: token $GITEA_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$COMMENT_BODY" \
|
||
"$GITEA_URL/api/v1/repos/${{ github.repository }}/issues/$PR_NUMBER/comments"
|
||
|
||
echo "✅ Комментарий добавлен"
|
||
|
||
- name: Block on Destructive Changes
|
||
if: |
|
||
github.event_name == 'pull_request' &&
|
||
(steps.plan.outputs.has_replace == 'true' || steps.plan.outputs.has_destroy == 'true')
|
||
run: |
|
||
echo "🚨 ОБНАРУЖЕНЫ ОПАСНЫЕ ИЗМЕНЕНИЯ!"
|
||
echo "Этот PR содержит destroy/replace операции."
|
||
echo "Требуется ручной review перед мержем."
|
||
exit 1
|
||
|
||
- name: Terraform Apply
|
||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||
run: |
|
||
echo "🚀 Применение изменений в main..."
|
||
|
||
if [ -f "tfplan" ]; then
|
||
terraform apply -auto-approve tfplan
|
||
else
|
||
terraform apply -auto-approve
|
||
fi
|
||
|
||
echo "✅ Изменения применены"
|
||
|