Renovate configuration.
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
name: Auto-PR Check/Creation and TF/OpenTofu Plan
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "main"
|
||||
- "renovate/**"
|
||||
paths:
|
||||
- "cloudflare/**"
|
||||
|
||||
env:
|
||||
OPENTOFU_VERSION: "1.10.6"
|
||||
HC_VAULT_VERSION: "1.20.4"
|
||||
TEA_VERSION: "0.10.1"
|
||||
|
||||
jobs:
|
||||
check-and-create-pr:
|
||||
name: Check and Create PR
|
||||
outputs:
|
||||
pr_number: ${{ steps.pr-check-create.outputs.pr_number }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setting Vault Token
|
||||
run: echo "VAULT_TOKEN=${{ secrets.VAULT_GITEA_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Gotify Notification - Start
|
||||
uses: eikendev/gotify-action@master
|
||||
with:
|
||||
gotify_api_base: ${{ secrets.RUNNER_GOTIFY_URL }}
|
||||
gotify_app_token: ${{ secrets.RUNNER_GOTIFY_TOKEN }}
|
||||
notification_title: "GITEA: PR Check @ Rinoa"
|
||||
notification_message: "Checking for existing PR... 🔍"
|
||||
|
||||
- name: PR Check/Creation
|
||||
id: pr-check-create
|
||||
uses: https://git.trez.wtf/Trez/gitea-auto-pr@main
|
||||
with:
|
||||
url: ${{ secrets.TREZ_GITEA_URL }}
|
||||
token: ${{ secrets.BOT_GITEA_TOKEN }}
|
||||
pr-label: "docker-compose,manual"
|
||||
assignee: ${{ github.actor }}
|
||||
|
||||
- name: Gotify Notification - Done
|
||||
uses: eikendev/gotify-action@master
|
||||
with:
|
||||
gotify_api_base: ${{ secrets.RUNNER_GOTIFY_URL }}
|
||||
gotify_app_token: ${{ secrets.RUNNER_GOTIFY_TOKEN }}
|
||||
notification_title: "GITEA: PR Check @ Rinoa"
|
||||
notification_message: "PR Check done 🎟️"
|
||||
|
||||
plan-approval:
|
||||
name: OpenTofu Plan
|
||||
needs: check-and-create-pr
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
VAULT_TOKEN: ${{ secrets.VAULT_GITEA_TOKEN }}
|
||||
outputs:
|
||||
tofu-cloudflare-plan: ${{ steps.tofu_plan.outputs.plan-output }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup OpenTofu
|
||||
uses: opentofu/setup-opentofu@v1.0.6
|
||||
with:
|
||||
version: ${{ env.OPENTOFU_VERSION }}
|
||||
tofu_wrapper: true
|
||||
|
||||
- name: Generate .env from Hashicorp Vault
|
||||
uses: https://git.trez.wtf/Trez/hc-vault-env@main
|
||||
with:
|
||||
HC_VAULT_VERSION: ${{ env.HC_VAULT_VERSION }}
|
||||
HC_VAULT_ADDR: ${{ secrets.TREZ_VAULT_ADDR }}
|
||||
HC_VAULT_AUTH: token
|
||||
HC_VAULT_TOKEN: ${{ env.VAULT_TOKEN }}
|
||||
HC_VAULT_SECRETS_PATH: tar-valon-terraform/env
|
||||
ENV_FILE_NAME: cloudflare/.env
|
||||
|
||||
- name: Export env vars from Vault .env
|
||||
id: env-vault-vars
|
||||
run: |
|
||||
sed -i 's/[\"'\'']//g' cloudflare/.env
|
||||
set -a
|
||||
source cloudflare/.env
|
||||
set +a
|
||||
while IFS='=' read -r key value; do
|
||||
if [[ -n "$key" ]]; then
|
||||
echo "$key=$value" >> $GITHUB_ENV
|
||||
fi
|
||||
done < cloudflare/.env
|
||||
repo_name=$(echo "${{ github.repository }}" | awk -F"/" '{print $2}')
|
||||
echo "repo_name=$repo_name" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Run tofu init
|
||||
uses: dnogu/tofu-init@v1
|
||||
with:
|
||||
working-directory: .
|
||||
chdir: cloudflare
|
||||
|
||||
- name: Tofu Plan
|
||||
id: tofu_plan
|
||||
continue-on-error: true
|
||||
uses: dnogu/tofu-plan@v1
|
||||
with:
|
||||
working-directory: .
|
||||
chdir: cloudflare
|
||||
out: cloudflare.tfplan
|
||||
|
||||
- name: Build Markdown PR comment from plan file
|
||||
run: |
|
||||
mkdir -p tmp
|
||||
{
|
||||
echo "## 🧩 OpenTofu Plan — Cloudflare"
|
||||
echo "**Branch:** \`${{ github.ref_name }}\`"
|
||||
echo "**Exit Code:** \`${{ steps.tofu_plan.outputs.exitcode }}\`"
|
||||
echo "**Working Directory:** \`cloudflare\`"
|
||||
echo ""
|
||||
echo "<details><summary>🪶 Click to expand full plan output</summary>"
|
||||
echo ""
|
||||
echo '```hcl'
|
||||
cat ${GITHUB_WORKSPACE}/cloudflare/cloudflare.tfplan # <-- read file directly, ACT-safe
|
||||
echo '```'
|
||||
echo ""
|
||||
echo "</details>"
|
||||
echo ""
|
||||
echo "*(This plan was automatically generated by the workflow.)*"
|
||||
} > tmp/tofu-plan.md
|
||||
echo "Markdown PR comment built: tmp/tofu-plan.md"
|
||||
|
||||
- name: Comment full Tofu Plan on PR (Gitea safe)
|
||||
uses: https://git.trez.wtf/Trez.One/git-auto-comment@main
|
||||
with:
|
||||
platform: gitea
|
||||
api_url: https://git.trez.wtf/api/v1
|
||||
token: ${{ secrets.BOT_GITEA_TOKEN }}
|
||||
pr_index: ${{ needs.check-and-create-pr.outputs.pr_number }}
|
||||
repo_owner: ${{ github.repository_owner }}
|
||||
repo_name: ${{ steps.env-vault-vars.outputs.repo_name }}
|
||||
comment_template_path: tmp/tofu-plan.md
|
||||
|
||||
- name: Wait for manual approval
|
||||
uses: trstringer/manual-approval@v1
|
||||
with:
|
||||
secret: ${{ secrets.BOT_GITEA_TOKEN }}
|
||||
approvers: WTF
|
||||
minimum-approvals: 1
|
||||
issue-title: "Tofu Plan for ${{ needs.check-and-create-pr.outputs.pr_number }}"
|
||||
issue-body: "Please approve or deny the deployment of the below Tofu plan"
|
||||
issue-body-file-path: cloudflare/cloudflare.tfplan
|
||||
exclude-workflow-initiator-as-approver: false
|
||||
fail-on-denial: true
|
||||
|
||||
# apply:
|
||||
# name: Apply Tofu Plan
|
||||
# needs: plan-approval
|
||||
# runs-on: ubuntu-latest
|
||||
# if: ${{ needs.plan-approval.result == 'success' }}
|
||||
# env:
|
||||
# VAULT_TOKEN: ${{ secrets.VAULT_GITEA_TOKEN }}
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
#
|
||||
# - name: Setup OpenTofu
|
||||
# uses: opentofu/setup-opentofu@v1.0.6
|
||||
# with:
|
||||
# version: ${{ env.OPENTOFU_VERSION }}
|
||||
# tofu_wrapper: true
|
||||
#
|
||||
# - name: Export env from Vault
|
||||
# run: |
|
||||
# set -a
|
||||
# source cloudflare/.env
|
||||
# set +a
|
||||
#
|
||||
# - name: Run Tofu Apply
|
||||
# uses: dnogu/tofu-apply@v1
|
||||
# with:
|
||||
# working-directory: .
|
||||
# chdir: cloudflare
|
||||
# plan: cloudflare.tfplan
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Renovate
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0/30 * * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
RENOVATE_VERSION: "41.146.8"
|
||||
|
||||
jobs:
|
||||
renovate:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Renovate Run
|
||||
env:
|
||||
DOCKER_HOST: tcp://dockerproxy:2375
|
||||
RENOVATE_PLATFORM: gitea
|
||||
RENOVATE_ENDPOINT: https://git.trez.wtf/api/v1
|
||||
RENOVATE_TOKEN: ${{ secrets.RENOVATE_BOT_TOKEN }}
|
||||
LOG_LEVEL: ${{ vars.RENOVATE_LOG_LEVEL }}
|
||||
RENOVATE_GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_TOKEN }}
|
||||
RENOVATE_CONFIG_FILE: renovate.json
|
||||
RENOVATE_REPOSITORIES: trez/rinoa-docker
|
||||
RENOVATE_HOST_RULES: |
|
||||
[
|
||||
{
|
||||
"description": "Docker Hub authentication",
|
||||
"hostType": "docker",
|
||||
"matchHost": "docker.io",
|
||||
"username": "${{ secrets.DOCKERHUB_USER }}",
|
||||
"password": "${{ secrets.DOCKERHUB_PASSWORD }}"
|
||||
},
|
||||
{
|
||||
"description": "GitHub Container Registry (GHCR)",
|
||||
"hostType": "docker",
|
||||
"matchHost": "ghcr.io",
|
||||
"username": "${{ secrets.GHCR_USER }}",
|
||||
"password": "${{ secrets.GHCR_LOGIN_TOKEN }}"
|
||||
},
|
||||
{
|
||||
"description": "Self-hosted Gitea Docker Registry",
|
||||
"hostType": "docker",
|
||||
"matchHost": "git.trez.wtf",
|
||||
"username": "${{ secrets.BOT_GITEA_USER }}",
|
||||
"password": "${{ secrets.BOT_GITEA_PASSWORD }}"
|
||||
}
|
||||
]
|
||||
run: |
|
||||
docker run --rm \
|
||||
-e RENOVATE_PLATFORM \
|
||||
-e RENOVATE_ENDPOINT \
|
||||
-e RENOVATE_TOKEN \
|
||||
-e LOG_LEVEL \
|
||||
-e RENOVATE_GITHUB_COM_TOKEN \
|
||||
-e RENOVATE_CONFIG_FILE \
|
||||
-e RENOVATE_REPOSITORIES \
|
||||
-e RENOVATE_HOST_RULES \
|
||||
--volumes-from ${{ env.JOB_CONTAINER_NAME }} \
|
||||
-w ${GITHUB_WORKSPACE} \
|
||||
renovate/renovate:${{ env.RENOVATE_VERSION }}-full
|
||||
+11
-7
@@ -1,5 +1,9 @@
|
||||
# Created by https://www.toptal.com/api/terraform
|
||||
# Edit at https://www.toptal.com?templates=terraform
|
||||
|
||||
### Terraform ###
|
||||
# Local .terraform directories
|
||||
.terraform/
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
@@ -10,8 +14,8 @@ crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
@@ -23,20 +27,20 @@ override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Ignore transient lock info files created by terraform apply
|
||||
.terraform.tfstate.lock.info
|
||||
*.terraform.lock*
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
**/*.tfplan
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# End of https://www.toptal.com/api/terraform
|
||||
# Envs
|
||||
*.env*
|
||||
|
||||
**/.cf-terraforming
|
||||
**/*.txt
|
||||
**/cloudflare_resource_imp.tf
|
||||
@@ -1,2 +0,0 @@
|
||||
email: "charish.patel@trez.wtf"
|
||||
key: "3dfcfd53a7af10f027ade7467a087017867e7"
|
||||
Generated
+10
-10
@@ -2,18 +2,18 @@
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||
version = "5.7.1"
|
||||
version = "5.12.0"
|
||||
constraints = "~> 5.0"
|
||||
hashes = [
|
||||
"h1:24uCJp1H3ZK8CCwszYyR3qtOn5Z5Hn1eCcv2gkH7+4g=",
|
||||
"zh:0bbd2eaa8210b2214dc426a2471b38cee53db9750b4916b34b0926fc9cbe4d7b",
|
||||
"zh:0de3675e2ace7478ab0d354b2b6db4be2ae5a9a5e68b725cdd10e956131ec687",
|
||||
"zh:4787a255919911aac5e1f8d47ed19fa45e5b90439ecf1fffbb17ff8bfb28de79",
|
||||
"zh:4eb1e4300b3cdee3a323457ebcc8df29a735ea6bbabe3cf9cbd3dc3fb5a9172b",
|
||||
"zh:580bbd5a727c9e3f31ae47c872df860c9a08ea998e0ff3dfe37dbac536146166",
|
||||
"zh:6a359212678ffcf88551e2d8d0f8e52418031cf1f8077bb8ddf500171ee90f2b",
|
||||
"zh:bec6890cb11511577c5f8ec8954e26ac51c44a114cb3e0349fea40f87930c029",
|
||||
"zh:dbe3585510283c8e53a2b24cc7a69fab0ee9d71addae0db1be0374bc32fc6355",
|
||||
"h1:IvMPMJrmyw6x+8GZklY7qb8VXrjr00zwsN+TFlxkCTM=",
|
||||
"zh:06166a72e69eb712ad2c8b49c1ed060223b0d57bb95ce5f6c8440ce19253913e",
|
||||
"zh:484c32dc4fbe1f7baaf00f8d0d1774d259e1a602aebf60b8dea8c6dd122c1d27",
|
||||
"zh:914b4796a5f2c5914cb94864a7541ce132c0e287bf49a5328706d50152117bc4",
|
||||
"zh:bbcf3effe11ad44988c2aa4482c3fd0089ca86527463a9a873cecda1a4a022bc",
|
||||
"zh:c2a59f29b4b4c0344dbb9ab3d78ebcc1d32153f1fd7e919eba7edf7d825119c2",
|
||||
"zh:d6900b39b9c58743e6b1f05b2db7c39276c94f74d501f23bebb88d413266c57c",
|
||||
"zh:f000d33075c30e616df8e58e341614e958eed4a51f3427d2e1a18ea1b7e0c6c6",
|
||||
"zh:f809ab383cca0a5f83072981c64208cbd7fa67e986a86ee02dd2c82333221e32",
|
||||
"zh:ff4fd5b3b0327f8f41fc65d909839288fb98ecfe32a9aff11d2e2638f2109302",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
email: ""
|
||||
key: ""
|
||||
# or
|
||||
token: ""
|
||||
@@ -1,5 +0,0 @@
|
||||
resource "cloudflare_argo_tiered_caching" "terraform_managed_resource_tiered_caching_0" {
|
||||
value = "on"
|
||||
zone_id = "17dbb71212204583b777783d25eb6738"
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
resource "cloudflare_email_routing_dns" "terraform_managed_resource_17dbb71212204583b777783d25eb6738_0" {
|
||||
name = "trez.wtf"
|
||||
zone_id = "17dbb71212204583b777783d25eb6738"
|
||||
}
|
||||
|
||||
resource "cloudflare_email_routing_dns" "terraform_managed_resource_17dbb71212204583b777783d25eb6738_1" {
|
||||
name = "trez.wtf"
|
||||
zone_id = "17dbb71212204583b777783d25eb6738"
|
||||
}
|
||||
|
||||
resource "cloudflare_email_routing_dns" "terraform_managed_resource_17dbb71212204583b777783d25eb6738_2" {
|
||||
name = "trez.wtf"
|
||||
zone_id = "17dbb71212204583b777783d25eb6738"
|
||||
}
|
||||
|
||||
resource "cloudflare_email_routing_dns" "terraform_managed_resource_17dbb71212204583b777783d25eb6738_3" {
|
||||
name = "cf2024-1._domainkey.trez.wtf"
|
||||
zone_id = "17dbb71212204583b777783d25eb6738"
|
||||
}
|
||||
|
||||
resource "cloudflare_email_routing_dns" "terraform_managed_resource_17dbb71212204583b777783d25eb6738_4" {
|
||||
name = "trez.wtf"
|
||||
zone_id = "17dbb71212204583b777783d25eb6738"
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
resource "cloudflare_email_routing_settings" "terraform_managed_resource_17dbb71212204583b777783d25eb6738_0" {
|
||||
zone_id = "17dbb71212204583b777783d25eb6738"
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
||||
resource "cloudflare_tiered_cache" "terraform_managed_resource_tiered_cache_smart_topology_enable_0" {
|
||||
value = "on"
|
||||
zone_id = "17dbb71212204583b777783d25eb6738"
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
resource "cloudflare_zone" "terraform_managed_resource_17dbb71212204583b777783d25eb6738_0" {
|
||||
name = "trez.wtf"
|
||||
paused = false
|
||||
type = "full"
|
||||
account = {
|
||||
id = "f5a5c0098ccae27fb0486ffbc2ee6087"
|
||||
name = "Charish.patel@trez.wtf's Account"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
resource "cloudflare_zone_dnssec" "terraform_managed_resource_17dbb71212204583b777783d25eb6738_0" {
|
||||
dnssec_multi_signer = true
|
||||
status = "active"
|
||||
zone_id = "17dbb71212204583b777783d25eb6738"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
CLOUDFLARE_API_TOKEN=""
|
||||
CLOUDFLARE_ZONE_ID=""
|
||||
AWS_ENDPOINT_URL_S3=""
|
||||
AWS_DEFAULT_REGION=""
|
||||
AWS_ACCESS_KEY_ID=""
|
||||
AWS_SECRET_ACCESS_KEY=""
|
||||
+218
-169
@@ -1,181 +1,230 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cf_generate () {
|
||||
resources=(
|
||||
cloudflare_account
|
||||
cloudflare_account_member
|
||||
cloudflare_account_subscription
|
||||
cloudflare_address_map
|
||||
cloudflare_api_shield_discovery_operation
|
||||
cloudflare_api_shield_operation
|
||||
cloudflare_api_shield_operation_schema_validation_settings
|
||||
cloudflare_api_shield_schema
|
||||
cloudflare_api_shield_schema_validation_settings
|
||||
cloudflare_argo_smart_routing
|
||||
cloudflare_argo_tiered_caching
|
||||
cloudflare_authenticated_origin_pulls
|
||||
cloudflare_authenticated_origin_pulls_certificate
|
||||
cloudflare_bot_management
|
||||
cloudflare_calls_sfu_app
|
||||
cloudflare_calls_turn_app
|
||||
cloudflare_certificate_pack
|
||||
cloudflare_content_scanning_expression
|
||||
cloudflare_custom_hostname
|
||||
cloudflare_custom_hostname_fallback_origin
|
||||
cloudflare_d1_database
|
||||
cloudflare_dns_firewall
|
||||
cloudflare_dns_record
|
||||
cloudflare_dns_zone_transfers_acl
|
||||
cloudflare_dns_zone_transfers_incoming
|
||||
cloudflare_dns_zone_transfers_outgoing
|
||||
cloudflare_dns_zone_transfers_peer
|
||||
cloudflare_dns_zone_transfers_tsig
|
||||
cloudflare_email_routing_address
|
||||
cloudflare_email_routing_catch_all
|
||||
cloudflare_email_routing_dns
|
||||
cloudflare_email_routing_rule
|
||||
cloudflare_email_routing_settings
|
||||
cloudflare_email_security_block_sender
|
||||
cloudflare_email_security_impersonation_registry
|
||||
cloudflare_email_security_trusted_domains
|
||||
cloudflare_filter
|
||||
cloudflare_healthcheck
|
||||
cloudflare_hostname_tls_setting
|
||||
cloudflare_keyless_certificate
|
||||
cloudflare_leaked_credential_check
|
||||
cloudflare_leaked_credential_check_rule
|
||||
cloudflare_list
|
||||
cloudflare_list_item
|
||||
cloudflare_load_balancer
|
||||
cloudflare_load_balancer_monitor
|
||||
cloudflare_load_balancer_pool
|
||||
cloudflare_logpull_retention
|
||||
cloudflare_logpush_job
|
||||
cloudflare_magic_wan_static_route
|
||||
cloudflare_managed_transforms
|
||||
cloudflare_mtls_certificate
|
||||
cloudflare_notification_policy
|
||||
cloudflare_notification_policy_webhooks
|
||||
cloudflare_observatory_scheduled_test
|
||||
cloudflare_origin_ca_certificate
|
||||
cloudflare_page_rule
|
||||
cloudflare_page_shield_policy
|
||||
cloudflare_pages_domain
|
||||
cloudflare_pages_project
|
||||
cloudflare_queue
|
||||
cloudflare_queue_consumer
|
||||
cloudflare_r2_bucket
|
||||
cloudflare_r2_custom_domain
|
||||
cloudflare_r2_managed_domain
|
||||
cloudflare_rate_limit
|
||||
cloudflare_regional_hostname
|
||||
cloudflare_regional_tiered_cache
|
||||
cloudflare_registrar_domain
|
||||
cloudflare_ruleset
|
||||
cloudflare_snippet_rules
|
||||
cloudflare_snippets
|
||||
cloudflare_spectrum_application
|
||||
cloudflare_stream
|
||||
cloudflare_stream_key
|
||||
cloudflare_stream_live_input
|
||||
cloudflare_stream_watermark
|
||||
cloudflare_stream_webhook
|
||||
cloudflare_tiered_cache
|
||||
cloudflare_total_tls
|
||||
cloudflare_turnstile_widget
|
||||
cloudflare_url_normalization_settings
|
||||
cloudflare_user
|
||||
cloudflare_waiting_room
|
||||
cloudflare_waiting_room_event
|
||||
cloudflare_waiting_room_rules
|
||||
cloudflare_waiting_room_settings
|
||||
cloudflare_web3_hostname
|
||||
cloudflare_web_analytics_rule
|
||||
cloudflare_web_analytics_site
|
||||
cloudflare_workers_cron_trigger
|
||||
cloudflare_workers_custom_domain
|
||||
cloudflare_workers_deployment
|
||||
cloudflare_workers_for_platforms_dispatch_namespace
|
||||
cloudflare_workers_kv_namespace
|
||||
cloudflare_workers_script_subdomain
|
||||
cloudflare_zero_trust_access_application
|
||||
cloudflare_zero_trust_access_custom_page
|
||||
cloudflare_zero_trust_access_group
|
||||
cloudflare_zero_trust_access_identity_provider
|
||||
cloudflare_zero_trust_access_infrastructure_target
|
||||
cloudflare_zero_trust_access_key_configuration
|
||||
cloudflare_zero_trust_access_mtls_certificate
|
||||
cloudflare_zero_trust_access_mtls_hostname_settings
|
||||
cloudflare_zero_trust_access_policy
|
||||
cloudflare_zero_trust_access_service_token
|
||||
cloudflare_zero_trust_access_short_lived_certificate
|
||||
cloudflare_zero_trust_access_tag
|
||||
cloudflare_zero_trust_device_custom_profile
|
||||
cloudflare_zero_trust_device_default_profile
|
||||
cloudflare_zero_trust_device_default_profile_certificates
|
||||
cloudflare_zero_trust_device_default_profile_local_domain_fallback
|
||||
cloudflare_zero_trust_device_managed_networks
|
||||
cloudflare_zero_trust_device_posture_integration
|
||||
cloudflare_zero_trust_device_posture_rule
|
||||
cloudflare_zero_trust_dex_test
|
||||
cloudflare_zero_trust_dlp_custom_profile
|
||||
cloudflare_zero_trust_dlp_dataset
|
||||
cloudflare_zero_trust_dlp_predefined_profile
|
||||
cloudflare_zero_trust_dns_location
|
||||
cloudflare_zero_trust_gateway_certificate
|
||||
cloudflare_zero_trust_gateway_policy
|
||||
cloudflare_zero_trust_gateway_proxy_endpoint
|
||||
cloudflare_zero_trust_gateway_settings
|
||||
cloudflare_zero_trust_list
|
||||
cloudflare_zero_trust_organization
|
||||
cloudflare_zero_trust_risk_behavior
|
||||
cloudflare_zero_trust_risk_scoring_integration
|
||||
cloudflare_zero_trust_tunnel_cloudflared
|
||||
cloudflare_zero_trust_tunnel_cloudflared_config
|
||||
cloudflare_zero_trust_tunnel_cloudflared_route
|
||||
cloudflare_zero_trust_tunnel_cloudflared_virtual_network
|
||||
cloudflare_zone
|
||||
cloudflare_zone_cache_reserve
|
||||
cloudflare_zone_cache_variants
|
||||
cloudflare_zone_dnssec
|
||||
cloudflare_zone_lockdown
|
||||
cloudflare_zone_setting
|
||||
)
|
||||
# -------------------------------
|
||||
# Detect Terraform binary: tofu vs terraform
|
||||
# -------------------------------
|
||||
if command -v tofu &>/dev/null; then
|
||||
TF_BIN="tofu"
|
||||
elif command -v terraform &>/dev/null; then
|
||||
TF_BIN="terraform"
|
||||
else
|
||||
echo "❌ Neither 'terraform' nor 'tofu' found in PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for resource in "${resources[@]}"; do
|
||||
echo "Generating ${resource}.tf..."
|
||||
cf-terraforming generate --config .cf-terraforming.yaml --resource-type ${resource} > ${resource}.tf
|
||||
done
|
||||
echo "ℹ️ Using $TF_BIN for Terraform operations"
|
||||
|
||||
echo "🧹 Cleaning up empty files..."
|
||||
find . -size 0 -name "*.tf" | xargs rm
|
||||
echo "✅ All Terraform files generated!"
|
||||
# -------------------------------
|
||||
# Ensure CF API token
|
||||
# -------------------------------
|
||||
CF_API_TOKEN="${CLOUDFLARE_API_TOKEN:-}"
|
||||
if [[ -z "${CF_API_TOKEN}" ]]; then
|
||||
echo "Please set CF_API_TOKEN before running this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# -------------------------------
|
||||
# Helper: fetch paginated results from Cloudflare API
|
||||
# -------------------------------
|
||||
cf_paginate() {
|
||||
local endpoint="$1"
|
||||
local page=1
|
||||
local per_page=100
|
||||
while :; do
|
||||
local result
|
||||
result=$(curl -s -X GET "${endpoint}?page=${page}&per_page=${per_page}" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json")
|
||||
local items
|
||||
items=$(echo "${result}" | jq -r '.result[]? | @base64')
|
||||
[[ -z "$items" ]] && break
|
||||
echo "$items"
|
||||
local total_pages
|
||||
total_pages=$(echo "$result" | jq -r '.result_info.total_pages')
|
||||
((page++))
|
||||
[[ $page -gt $total_pages ]] && break
|
||||
done
|
||||
}
|
||||
|
||||
cf_import () {
|
||||
for cfresource in $(find . -type f -name "cloudflare_*.tf"); do
|
||||
echo "Importing ${cfresource}..."
|
||||
cf-terraforming import --modern-import-block --config .cf-terraforming.yaml --resource-type ${cfresource}
|
||||
done
|
||||
# -------------------------------
|
||||
# Generate Cloudflare resources using cf-terraforming
|
||||
# -------------------------------
|
||||
generate_resources() {
|
||||
echo "🔧 Generating Cloudflare resources via cf-terraforming..."
|
||||
local output_file="cloudflare_resource_gen.tf"
|
||||
> "${output_file}"
|
||||
|
||||
echo "✅ All Cloudflare resources import."
|
||||
resources=(
|
||||
cloudflare_account
|
||||
cloudflare_account_member
|
||||
cloudflare_account_subscription
|
||||
cloudflare_address_map
|
||||
cloudflare_argo_tiered_caching
|
||||
cloudflare_authenticated_origin_pulls
|
||||
cloudflare_authenticated_origin_pulls_certificate
|
||||
cloudflare_bot_management
|
||||
cloudflare_certificate_pack
|
||||
cloudflare_content_scanning_expression
|
||||
cloudflare_custom_hostname
|
||||
cloudflare_custom_hostname_fallback_origin
|
||||
cloudflare_d1_database
|
||||
cloudflare_dns_firewall
|
||||
cloudflare_dns_record
|
||||
cloudflare_dns_zone_transfers_acl
|
||||
cloudflare_dns_zone_transfers_incoming
|
||||
cloudflare_dns_zone_transfers_outgoing
|
||||
cloudflare_dns_zone_transfers_peer
|
||||
cloudflare_dns_zone_transfers_tsig
|
||||
cloudflare_email_routing_address
|
||||
cloudflare_email_routing_catch_all
|
||||
cloudflare_email_routing_dns
|
||||
cloudflare_email_routing_rule
|
||||
cloudflare_email_routing_settings
|
||||
cloudflare_filter
|
||||
cloudflare_healthcheck
|
||||
cloudflare_hostname_tls_setting
|
||||
cloudflare_keyless_certificate
|
||||
cloudflare_leaked_credential_check
|
||||
cloudflare_leaked_credential_check_rule
|
||||
cloudflare_list_item
|
||||
cloudflare_load_balancer
|
||||
cloudflare_load_balancer_monitor
|
||||
cloudflare_load_balancer_pool
|
||||
cloudflare_logpull_retention
|
||||
cloudflare_logpush_job
|
||||
cloudflare_magic_wan_static_route
|
||||
cloudflare_managed_transforms
|
||||
cloudflare_mtls_certificate
|
||||
cloudflare_notification_policy
|
||||
cloudflare_notification_policy_webhooks
|
||||
cloudflare_observatory_scheduled_test
|
||||
cloudflare_origin_ca_certificate
|
||||
cloudflare_page_rule
|
||||
cloudflare_page_shield_policy
|
||||
cloudflare_pages_domain
|
||||
cloudflare_pages_project
|
||||
cloudflare_queue
|
||||
cloudflare_queue_consumer
|
||||
cloudflare_r2_bucket
|
||||
cloudflare_r2_custom_domain
|
||||
cloudflare_r2_managed_domain
|
||||
cloudflare_rate_limit
|
||||
cloudflare_regional_hostname
|
||||
cloudflare_regional_tiered_cache
|
||||
cloudflare_registrar_domain
|
||||
cloudflare_ruleset
|
||||
cloudflare_snippet_rules
|
||||
cloudflare_snippets
|
||||
cloudflare_spectrum_application
|
||||
cloudflare_stream
|
||||
cloudflare_stream_key
|
||||
cloudflare_stream_live_input
|
||||
cloudflare_stream_watermark
|
||||
cloudflare_stream_webhook
|
||||
cloudflare_tiered_cache
|
||||
cloudflare_total_tls
|
||||
cloudflare_turnstile_widget
|
||||
cloudflare_url_normalization_settings
|
||||
cloudflare_user
|
||||
cloudflare_waiting_room
|
||||
cloudflare_waiting_room_event
|
||||
cloudflare_waiting_room_rules
|
||||
cloudflare_waiting_room_settings
|
||||
cloudflare_web3_hostname
|
||||
cloudflare_web_analytics_rule
|
||||
cloudflare_web_analytics_site
|
||||
cloudflare_workers_cron_trigger
|
||||
cloudflare_workers_custom_domain
|
||||
cloudflare_workers_deployment
|
||||
cloudflare_workers_for_platforms_dispatch_namespace
|
||||
cloudflare_workers_kv_namespace
|
||||
cloudflare_workers_script_subdomain
|
||||
cloudflare_zone
|
||||
cloudflare_zone_cache_reserve
|
||||
cloudflare_zone_cache_variants
|
||||
cloudflare_zone_dnssec
|
||||
cloudflare_zone_lockdown
|
||||
cloudflare_zone_setting
|
||||
)
|
||||
|
||||
for r in "${resources[@]}"; do
|
||||
echo "Generating $r ..."
|
||||
cf-terraforming generate \
|
||||
--token "${CF_API_TOKEN}" \
|
||||
--resource-type "${r}" >> "${output_file}" || true
|
||||
done
|
||||
|
||||
echo "✅ Terraform resources generated in ${output_file}"
|
||||
}
|
||||
|
||||
# Prompt user for input
|
||||
# -------------------------------
|
||||
# Import Cloudflare resources into state using cf-terraforming
|
||||
# -------------------------------
|
||||
import_zone_resources() {
|
||||
local zone_id="$1"
|
||||
local zone_name="$2"
|
||||
|
||||
echo "⏳ Importing zone $zone_name ..."
|
||||
cf-terraforming import \
|
||||
--token "${CF_API_TOKEN}" \
|
||||
--modern-import-block \
|
||||
--resource-type cloudflare_zone \
|
||||
--resource-id "$zone_id" >> cloudflare_resource_imp.tf || true
|
||||
echo "✅ Imported cloudflare_zone for $zone_name"
|
||||
|
||||
echo "🔄 Importing DNS records for $zone_name ..."
|
||||
cf-terraforming import \
|
||||
--token "${CF_API_TOKEN}" \
|
||||
--zone "$zone_id" \
|
||||
--modern-import-block \
|
||||
--resource-type cloudflare_dns_record >> cloudflare_resource_imp.tf || true
|
||||
echo "✅ Imported DNS records for $zone_name"
|
||||
|
||||
# Optional: import other zone-level resources
|
||||
for res in cloudflare_argo_tiered_caching cloudflare_email_routing_settings cloudflare_tiered_cache cloudflare_zone_dnssec; do
|
||||
cf-terraforming import \
|
||||
--token "${CF_API_TOKEN}" \
|
||||
--resource-type "$res" \
|
||||
--modern-import-block \
|
||||
--resource-id "$zone_id" >> cloudflare_resource_imp.tf || true
|
||||
echo "✅ Imported $res for $zone_name"
|
||||
done
|
||||
}
|
||||
|
||||
# -------------------------------
|
||||
# Main
|
||||
# -------------------------------
|
||||
echo "Choose an option:"
|
||||
echo "1) Generate Cloudflare Terraform resources"
|
||||
echo "2) Import Cloudflare Terraform resources"
|
||||
read -rp "Enter 1 or 2: " user_choice
|
||||
echo "2) Import Cloudflare Terraform resources into state"
|
||||
read -rp "Enter 1 or 2: " choice
|
||||
|
||||
case "$user_choice" in
|
||||
1)
|
||||
cf_generate
|
||||
;;
|
||||
2)
|
||||
cf_import
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option. Please enter 1 or 2."
|
||||
exit 1
|
||||
;;
|
||||
case "$choice" in
|
||||
1)
|
||||
generate_resources
|
||||
;;
|
||||
2)
|
||||
echo "🔄 Fetching zones..."
|
||||
zones=$(cf_paginate "https://api.cloudflare.com/client/v4/zones")
|
||||
declare -A zone_map
|
||||
while read -r z; do
|
||||
zname=$(echo "$z" | base64 --decode | jq -r '.name')
|
||||
zid=$(echo "$z" | base64 --decode | jq -r '.id')
|
||||
zone_map["$zname"]="$zid"
|
||||
done <<< "$zones"
|
||||
|
||||
echo "⚡ Found ${#zone_map[@]} zones."
|
||||
|
||||
for zone_name in "${!zone_map[@]}"; do
|
||||
zid="${zone_map[$zone_name]}"
|
||||
import_zone_resources "$zid" "$zone_name"
|
||||
done
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option. Enter 1 or 2."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "🎉 All operations completed!"
|
||||
|
||||
+2
-10
@@ -7,12 +7,8 @@ terraform {
|
||||
}
|
||||
|
||||
backend "s3" {
|
||||
bucket = "rinoa-terraform"
|
||||
key = "cloudflare/.tfstate"
|
||||
|
||||
endpoints = { s3 = "http://192.168.1.254:9001" }
|
||||
|
||||
region = "us-east-fh-pln"
|
||||
bucket = "rinoa-terraform"
|
||||
key = "cloudflare/.tfstate"
|
||||
skip_credentials_validation = true
|
||||
skip_metadata_api_check = true
|
||||
skip_region_validation = true
|
||||
@@ -20,7 +16,3 @@ terraform {
|
||||
skip_requesting_account_id = true # Optional, set to true if MinIO does not support AWS account ID
|
||||
}
|
||||
}
|
||||
|
||||
provider "cloudflare" {
|
||||
api_token = var.cloudflare_token
|
||||
}
|
||||
Binary file not shown.
@@ -1,24 +0,0 @@
|
||||
# variable "cloudflare_zone_id" {
|
||||
# description = "The Cloudflare UUID for the Zone to use."
|
||||
# type = string
|
||||
# }
|
||||
|
||||
variable "cloudflare_account_id" {
|
||||
description = "The Cloudflare UUID for the Account the Zone lives in."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "cloudflare_email" {
|
||||
description = "The Cloudflare user."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "cloudflare_token" {
|
||||
description = "The Cloudflare user's API token."
|
||||
type = string
|
||||
}
|
||||
|
||||
# variable "zone" {
|
||||
# description = "The Cloudflare user's API token."
|
||||
# type = string
|
||||
# }
|
||||
@@ -0,0 +1 @@
|
||||
GITEA_TOKEN=""
|
||||
@@ -0,0 +1,6 @@
|
||||
// Generated by gitea-terraforming (OpenTofu compatible)
|
||||
|
||||
# source id=52
|
||||
resource "gitea_organization" "org_trez" {
|
||||
name = "Trez"
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Generated by gitea-terraforming (OpenTofu compatible)
|
||||
|
||||
# source id=5
|
||||
resource "gitea_repository" "repo_trez_rinoa-docker" {
|
||||
owner = "Trez"
|
||||
name = "rinoa-docker"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=9
|
||||
resource "gitea_repository" "repo_trez_meraki-naemon" {
|
||||
owner = "Trez"
|
||||
name = "meraki-naemon"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=13
|
||||
resource "gitea_repository" "repo_trez_benedikta-ovos" {
|
||||
owner = "Trez"
|
||||
name = "benedikta-ovos"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=16
|
||||
resource "gitea_repository" "repo_trez_rikku-home-assistant" {
|
||||
owner = "Trez"
|
||||
name = "rikku-home-assistant"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=17
|
||||
resource "gitea_repository" "repo_trez_tar-valon-terraform" {
|
||||
owner = "Trez"
|
||||
name = "tar-valon-terraform"
|
||||
private = true
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=18
|
||||
resource "gitea_repository" "repo_trez_hugo_it-services" {
|
||||
owner = "Trez"
|
||||
name = "hugo_it-services"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=19
|
||||
resource "gitea_repository" "repo_trez_docker-mods-uptime-kuma-timeout-fix" {
|
||||
owner = "Trez"
|
||||
name = "docker-mods-uptime-kuma-timeout-fix"
|
||||
private = false
|
||||
description = "Documentation and Examples of base container modifications"
|
||||
}
|
||||
|
||||
# source id=21
|
||||
resource "gitea_repository" "repo_trez_tar-valon-ansible" {
|
||||
owner = "Trez"
|
||||
name = "tar-valon-ansible"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=22
|
||||
resource "gitea_repository" "repo_trez_congo-hindi-gujarati" {
|
||||
owner = "Trez"
|
||||
name = "congo-hindi-gujarati"
|
||||
private = false
|
||||
description = "A powerful, lightweight theme for Hugo built with Tailwind CSS."
|
||||
}
|
||||
|
||||
# source id=26
|
||||
resource "gitea_repository" "repo_trez_action-home-assistant" {
|
||||
owner = "Trez"
|
||||
name = "action-home-assistant"
|
||||
private = false
|
||||
description = "🚀 Frenck's GitHub Action for running a Home Assistant Core configuration check"
|
||||
}
|
||||
|
||||
# source id=27
|
||||
resource "gitea_repository" "repo_trez_renovate-config" {
|
||||
owner = "Trez"
|
||||
name = "renovate-config"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=31
|
||||
resource "gitea_repository" "repo_trez_hc-vault-env" {
|
||||
owner = "Trez"
|
||||
name = "hc-vault-env"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=32
|
||||
resource "gitea_repository" "repo_trez_docker-select-image-pull" {
|
||||
owner = "Trez"
|
||||
name = "docker-select-image-pull"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=33
|
||||
resource "gitea_repository" "repo_trez_gitea-auto-pr" {
|
||||
owner = "Trez"
|
||||
name = "gitea-auto-pr"
|
||||
private = false
|
||||
description = ""
|
||||
}
|
||||
|
||||
# source id=34
|
||||
resource "gitea_repository" "repo_trez_ultima-ai" {
|
||||
owner = "Trez"
|
||||
name = "ultima-ai"
|
||||
private = true
|
||||
description = ""
|
||||
}
|
||||
Executable
+274
@@ -0,0 +1,274 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
gitea-terraforming: Reverse Terraform for Gitea (OpenTofu compatible)
|
||||
|
||||
Generates Terraform HCL for:
|
||||
- Users
|
||||
- Organizations
|
||||
- Repositories (user & org)
|
||||
- Branch protections
|
||||
|
||||
Output files are automatically split per resource type:
|
||||
gitea-<resource-type>.tf
|
||||
|
||||
Supports import generation:
|
||||
- Modern import blocks (--modern-import-block)
|
||||
- Shell script terraform import
|
||||
|
||||
Usage example:
|
||||
python gitea_terraforming.py --api https://gitea.example.com --token <ADMIN_TOKEN> --out-dir ./gitea_tf
|
||||
"""
|
||||
|
||||
import argparse, os, sys, time, json, re
|
||||
from typing import Any, Dict, List, Optional
|
||||
from datetime import datetime
|
||||
import requests
|
||||
|
||||
def slugify(s: str) -> str:
|
||||
s = re.sub(r'[^0-9a-zA-Z_-]', '_', s)
|
||||
return re.sub('_+', '_', s).strip('_').lower()
|
||||
|
||||
class GiteaClient:
|
||||
def __init__(self, api_base: str, token: str, verify: bool = True):
|
||||
self.base = api_base.rstrip('/')
|
||||
self.s = requests.Session()
|
||||
self.s.headers.update({
|
||||
'Authorization': f'token {token}',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'gitea-terraforming/0.1'
|
||||
})
|
||||
self.verify = verify
|
||||
|
||||
def _get(self, path: str, params: Optional[dict] = None):
|
||||
url = f"{self.base}{path}"
|
||||
out = []
|
||||
page = 1
|
||||
while True:
|
||||
qp = params.copy() if params else {}
|
||||
qp.update({'page': page, 'limit': 100})
|
||||
resp = self.s.get(url, params=qp, verify=self.verify, timeout=30)
|
||||
if resp.status_code == 404:
|
||||
return []
|
||||
if resp.status_code == 429:
|
||||
retry = int(resp.headers.get('Retry-After', '5'))
|
||||
time.sleep(retry)
|
||||
continue
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
if isinstance(data, list):
|
||||
out.extend(data)
|
||||
if len(data) < 100:
|
||||
break
|
||||
page += 1
|
||||
else:
|
||||
return data
|
||||
return out
|
||||
|
||||
def list_orgs(self) -> List[dict]:
|
||||
return self._get("/api/v1/orgs")
|
||||
|
||||
def list_users(self) -> List[dict]:
|
||||
return self._get("/api/v1/admin/users")
|
||||
|
||||
def list_user_repos(self, user: str) -> List[dict]:
|
||||
return self._get(f"/api/v1/users/{user}/repos")
|
||||
|
||||
def list_org_repos(self, org: str) -> List[dict]:
|
||||
return self._get(f"/api/v1/orgs/{org}/repos")
|
||||
|
||||
def list_branch_protections(self, owner: str, repo: str) -> List[dict]:
|
||||
return self._get(f"/api/v1/repos/{owner}/{repo}/branch_protections")
|
||||
|
||||
def hcl_block(resource_type: str, name: str, attrs: dict, comment: Optional[str] = None) -> str:
|
||||
lines = []
|
||||
if comment:
|
||||
lines.append(f"# {comment}")
|
||||
lines.append(f'resource "{resource_type}" "{name}" ' + "{")
|
||||
for k, v in attrs.items():
|
||||
if v is None:
|
||||
continue
|
||||
if isinstance(v, bool):
|
||||
lines.append(f" {k} = {str(v).lower()}")
|
||||
elif isinstance(v, (int, float)):
|
||||
lines.append(f" {k} = {v}")
|
||||
elif isinstance(v, str):
|
||||
safe = v.replace('"', '\\"')
|
||||
lines.append(f' {k} = "{safe}"')
|
||||
elif isinstance(v, list):
|
||||
joined = ", ".join([f'"{x}"' for x in v])
|
||||
lines.append(f" {k} = [{joined}]")
|
||||
else:
|
||||
lines.append(f' # {k} = {json.dumps(v)}')
|
||||
lines.append("}\n")
|
||||
return "\n".join(lines)
|
||||
|
||||
def modern_import_block(to: str, ident: str) -> str:
|
||||
return f'import {{\n to = {to}\n id = "{ident}"\n}}\n'
|
||||
|
||||
def generate(api: str, token: str, out_dir: str, modern: bool = False, dry: bool = False):
|
||||
client = GiteaClient(api, token)
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
imports = []
|
||||
files: dict = {}
|
||||
|
||||
orgs = client.list_orgs()
|
||||
org_buf = []
|
||||
for o in orgs:
|
||||
uname = o.get("username") or o.get("user_name") or o.get("name")
|
||||
rname = f"org_{slugify(uname)}"
|
||||
attrs = {"name": uname}
|
||||
org_buf.append(hcl_block("gitea_organization", rname, attrs, comment=f"source id={o.get('id')}"))
|
||||
imports.append((f"gitea_organization.{rname}", uname))
|
||||
if org_buf:
|
||||
files["orgs"] = "\n".join(org_buf)
|
||||
|
||||
users = []
|
||||
try:
|
||||
users = client.list_users()
|
||||
except Exception as e:
|
||||
print(f"Warning: cannot list users: {e}", file=sys.stderr)
|
||||
user_buf = []
|
||||
for u in users:
|
||||
uname = u.get("login") or u.get("username")
|
||||
if not uname:
|
||||
continue
|
||||
rname = f"user_{slugify(uname)}"
|
||||
attrs = {
|
||||
"username": uname,
|
||||
"email": u.get("email"),
|
||||
"full_name": u.get("full_name"),
|
||||
"is_admin": u.get("is_admin")
|
||||
}
|
||||
user_buf.append(hcl_block("gitea_user", rname, attrs, comment=f"source id={u.get('id')}"))
|
||||
imports.append((f"gitea_user.{rname}", uname))
|
||||
if user_buf:
|
||||
files["users"] = "\n".join(user_buf)
|
||||
|
||||
repo_buf = []
|
||||
for u in users:
|
||||
uname = u.get("login") or u.get("username")
|
||||
if not uname:
|
||||
continue
|
||||
try:
|
||||
repos = client.list_user_repos(uname)
|
||||
except Exception:
|
||||
repos = []
|
||||
for r in repos:
|
||||
rname = f"repo_{slugify(uname)}_{slugify(r['name'])}"
|
||||
attrs = {
|
||||
"owner": uname,
|
||||
"name": r["name"],
|
||||
"private": r.get("private", False),
|
||||
"description": r.get("description")
|
||||
}
|
||||
repo_buf.append(hcl_block("gitea_repository", rname, attrs, comment=f"source id={r.get('id')}"))
|
||||
imports.append((f"gitea_repository.{rname}", f"{uname}/{r['name']}"))
|
||||
for o in orgs:
|
||||
uname = o.get("username") or o.get("user_name") or o.get("name")
|
||||
if not uname:
|
||||
continue
|
||||
try:
|
||||
repos = client.list_org_repos(uname)
|
||||
except Exception:
|
||||
repos = []
|
||||
for r in repos:
|
||||
rname = f"repo_{slugify(uname)}_{slugify(r['name'])}"
|
||||
attrs = {
|
||||
"owner": uname,
|
||||
"name": r["name"],
|
||||
"private": r.get("private", False),
|
||||
"description": r.get("description")
|
||||
}
|
||||
repo_buf.append(hcl_block("gitea_repository", rname, attrs, comment=f"source id={r.get('id')}"))
|
||||
imports.append((f"gitea_repository.{rname}", f"{uname}/{r['name']}"))
|
||||
if repo_buf:
|
||||
files["repos"] = "\n".join(repo_buf)
|
||||
|
||||
bp_buf = []
|
||||
for to, ident in imports:
|
||||
if not to.startswith("gitea_repository."):
|
||||
continue
|
||||
owner, repo = ident.split("/", 1)
|
||||
try:
|
||||
bps = client.list_branch_protections(owner, repo)
|
||||
except Exception:
|
||||
bps = []
|
||||
for bp in bps:
|
||||
branch_name = bp.get("branch_name") or bp.get("branch") or bp.get("name")
|
||||
if branch_name is None:
|
||||
continue
|
||||
bn = f"branch_protection_{slugify(owner)}_{slugify(repo)}_{slugify(branch_name)}"
|
||||
attrs = {
|
||||
"repository": repo,
|
||||
"owner": owner,
|
||||
"branch": branch_name,
|
||||
"enable_status_check": bp.get("enable_status_check", False),
|
||||
"required_approvals": bp.get("required_approvals", 0),
|
||||
"enable_merge_whitelist": bp.get("enable_merge_whitelist", False),
|
||||
}
|
||||
bp_buf.append(hcl_block("gitea_branch_protection", bn, attrs, comment=f"protect branch {branch_name}"))
|
||||
imports.append((f"gitea_branch_protection.{bn}", f"{owner}/{repo}/{branch_name}"))
|
||||
if bp_buf:
|
||||
files["branches"] = "\n".join(bp_buf)
|
||||
|
||||
for rtype, content in files.items():
|
||||
fname = f"gitea-{rtype}.tf"
|
||||
fpath = os.path.join(out_dir, fname)
|
||||
if dry:
|
||||
print(f"--- {fpath} ---")
|
||||
print(content)
|
||||
else:
|
||||
with open(fpath, "w", encoding="utf-8") as f:
|
||||
f.write(f"// Generated by gitea-terraforming (OpenTofu compatible)\n\n")
|
||||
f.write(content)
|
||||
print(f"Wrote {fname}", file=sys.stderr)
|
||||
|
||||
if modern:
|
||||
imps = "\n".join([modern_import_block(to, ident) for to, ident in imports])
|
||||
imppath = os.path.join(out_dir, "imports.tf")
|
||||
if dry:
|
||||
print("--- imports.tf ---")
|
||||
print(imps)
|
||||
else:
|
||||
with open(imppath, "w", encoding="utf-8") as f:
|
||||
f.write("// Import blocks\n\n")
|
||||
f.write(imps)
|
||||
print(f"Wrote imports.tf", file=sys.stderr)
|
||||
else:
|
||||
lines = ["#!/usr/bin/env bash", "set -euo pipefail"]
|
||||
lines += [f'terraform import {to} "{ident}"' for to, ident in imports]
|
||||
imppath = os.path.join(out_dir, "terraform_imports.sh")
|
||||
if dry:
|
||||
print("--- terraform_imports.sh ---")
|
||||
print("\n".join(lines))
|
||||
else:
|
||||
with open(imppath, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(lines) + "\n")
|
||||
os.chmod(imppath, 0o755)
|
||||
print(f"Wrote terraform_imports.sh", file=sys.stderr)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="gitea-terraforming: Reverse Terraform for Gitea (OpenTofu compatible)\n\n"
|
||||
"Generates Terraform HCL for users, organizations, repositories, and branch protections.\n"
|
||||
"Output files are split per resource type and timestamped.\n\n"
|
||||
"Example usage:\n"
|
||||
" python gitea_terraforming.py --api https://gitea.example.com --token <ADMIN_TOKEN> --out-dir ./gitea_tf\n",
|
||||
formatter_class=argparse.RawTextHelpFormatter
|
||||
)
|
||||
parser.add_argument("--api", required=True, help="Gitea API URL (e.g., https://gitea.example.com)")
|
||||
parser.add_argument("--token", default=os.environ.get("GITEA_TOKEN"),
|
||||
help="Gitea admin token (or set GITEA_TOKEN environment variable)")
|
||||
parser.add_argument("--out-dir", default="./gitea_tf", help="Directory to write Terraform files")
|
||||
parser.add_argument("--modern-import-block", action="store_true",
|
||||
help="Generate modern OpenTofu import blocks (imports.tf) instead of shell script")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print output instead of writing files")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.token:
|
||||
parser.error("Missing Gitea token: provide --token or set GITEA_TOKEN environment variable")
|
||||
|
||||
generate(args.api, args.token, args.out_dir, modern=args.modern_import_block, dry=args.dry_run)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,33 @@
|
||||
// Generated by gitea-terraforming (OpenTofu compatible)
|
||||
|
||||
# source id=3
|
||||
resource "gitea_user" "user_gitea-sonarqube-bot" {
|
||||
username = "gitea-sonarqube-bot"
|
||||
email = "trezone@vivaldi.net"
|
||||
full_name = ""
|
||||
is_admin = false
|
||||
}
|
||||
|
||||
# source id=51
|
||||
resource "gitea_user" "user_renovate-bot" {
|
||||
username = "renovate-bot"
|
||||
email = "charish2k1@gmail.com"
|
||||
full_name = ""
|
||||
is_admin = false
|
||||
}
|
||||
|
||||
# source id=1
|
||||
resource "gitea_user" "user_root" {
|
||||
username = "root"
|
||||
email = "noreply@trez.wtf"
|
||||
full_name = ""
|
||||
is_admin = true
|
||||
}
|
||||
|
||||
# source id=2
|
||||
resource "gitea_user" "user_trez_one" {
|
||||
username = "Trez.One"
|
||||
email = "charish.patel@trez.wtf"
|
||||
full_name = ""
|
||||
is_admin = false
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// Import blocks
|
||||
|
||||
import {
|
||||
to = gitea_organization.org_trez
|
||||
id = "Trez"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_user.user_gitea-sonarqube-bot
|
||||
id = "gitea-sonarqube-bot"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_user.user_renovate-bot
|
||||
id = "renovate-bot"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_user.user_root
|
||||
id = "root"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_user.user_trez_one
|
||||
id = "Trez.One"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_rinoa-docker
|
||||
id = "Trez/rinoa-docker"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_meraki-naemon
|
||||
id = "Trez/meraki-naemon"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_benedikta-ovos
|
||||
id = "Trez/benedikta-ovos"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_rikku-home-assistant
|
||||
id = "Trez/rikku-home-assistant"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_tar-valon-terraform
|
||||
id = "Trez/tar-valon-terraform"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_hugo_it-services
|
||||
id = "Trez/hugo_it-services"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_docker-mods-uptime-kuma-timeout-fix
|
||||
id = "Trez/docker-mods-uptime-kuma-timeout-fix"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_tar-valon-ansible
|
||||
id = "Trez/tar-valon-ansible"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_congo-hindi-gujarati
|
||||
id = "Trez/congo-hindi-gujarati"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_action-home-assistant
|
||||
id = "Trez/action-home-assistant"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_renovate-config
|
||||
id = "Trez/renovate-config"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_hc-vault-env
|
||||
id = "Trez/hc-vault-env"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_docker-select-image-pull
|
||||
id = "Trez/docker-select-image-pull"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_gitea-auto-pr
|
||||
id = "Trez/gitea-auto-pr"
|
||||
}
|
||||
|
||||
import {
|
||||
to = gitea_repository.repo_trez_ultima-ai
|
||||
id = "Trez/ultima-ai"
|
||||
}
|
||||
@@ -2,6 +2,10 @@
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"local>Trez/renovate-config"
|
||||
<<<<<<< HEAD
|
||||
],
|
||||
"enabledManagers": ["terraform"]
|
||||
=======
|
||||
]
|
||||
>>>>>>> refs/remotes/origin/renovate/configure
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
SIGNOZ_ACCESS_TOKEN=""
|
||||
SIGNOZ_ENDPOINT=""
|
||||
+1
-1
@@ -2,7 +2,7 @@ terraform {
|
||||
required_providers {
|
||||
signoz = {
|
||||
source = "signoz/signoz"
|
||||
version = "0.0.8"
|
||||
version = "0.0.9"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user