From dc6e0b4792327f6d7875dc6b825e55ce786561eb Mon Sep 17 00:00:00 2001 From: matheus1lva Date: Thu, 18 Jun 2026 10:52:39 -0300 Subject: [PATCH 1/3] ci(deploy): deploy ingest to render with 1Password-sourced secrets --- .github/workflows/deploy.yml | 100 +++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..3954a120 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,100 @@ +name: deploy + +# Deploy the ingest worker to Render with 1Password as the source of truth for +# secrets. Every env var lives in the 1Password `kong` item (vault webops-prod); +# this workflow pushes them into the Render env group `kong` (fromGroup: kong in +# render.yaml) and then triggers a deploy via the Render API. +# +# IMPORTANT: turn OFF Render's own Auto-Deploy for the ingest-v-2 service so this +# workflow is the only thing that deploys (otherwise the git push and this API +# call both fire a deploy). +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: deploy-render + cancel-in-progress: false + +env: + OP_VAULT: webops-prod + OP_ITEM: kong + RENDER_ENV_GROUP_ID: evg-d3dkc7jipnbc73ce23lg + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: 1Password/install-cli-action@v1 + + - name: Sync 1Password -> Render env group + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }} + run: | + set -euo pipefail + + # Keys managed on the service itself (render.yaml), not the group. + EXCLUDE="REDIS_HOST REDIS_PORT NODE_VERSION" + + # Enumerate env-var fields from the 1Password item. Upper-snake labels + # only, skipping op defaults (notesPlain, username, password, ...). + keys="$(op item get "$OP_ITEM" --vault "$OP_VAULT" --format json \ + | jq -r '.fields[] | select(.value != null) | select(.label | test("^[A-Z][A-Z0-9_]+$")) | .label')" + + if [ -z "$keys" ]; then + echo "no env-var fields found in 1Password item $OP_VAULT/$OP_ITEM" >&2 + exit 1 + fi + + for key in $keys; do + case " $EXCLUDE " in *" $key "*) echo "skip $key (service-managed)"; continue;; esac + value="$(op read "op://${OP_VAULT}/${OP_ITEM}/${key}")" + curl -fsS -X PUT \ + "https://api.render.com/v1/env-groups/${RENDER_ENV_GROUP_ID}/env-vars/${key}" \ + -H "Authorization: Bearer ${RENDER_API_KEY}" \ + -H "Content-Type: application/json" \ + -d "$(jq -n --arg v "$value" '{value: $v}')" \ + -o /dev/null + echo "synced $key" + done + + - name: Trigger Render deploy + env: + RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }} + RENDER_SERVICE_ID: ${{ secrets.RENDER_SERVICE_ID }} + run: | + set -euo pipefail + + deploy_id="$(curl -fsS -X POST \ + "https://api.render.com/v1/services/${RENDER_SERVICE_ID}/deploys" \ + -H "Authorization: Bearer ${RENDER_API_KEY}" \ + -H "Content-Type: application/json" \ + -d '{"clearCache":"do_not_clear"}' | jq -r '.id')" + + if [ -z "$deploy_id" ] || [ "$deploy_id" = "null" ]; then + echo "failed to create deploy" >&2 + exit 1 + fi + echo "deploy $deploy_id created; waiting for it to go live" + + for _ in $(seq 1 80); do + status="$(curl -fsS \ + "https://api.render.com/v1/services/${RENDER_SERVICE_ID}/deploys/${deploy_id}" \ + -H "Authorization: Bearer ${RENDER_API_KEY}" | jq -r '.status')" + echo "status=$status" + case "$status" in + live) echo "deploy succeeded"; exit 0 ;; + build_failed|update_failed|pre_deploy_failed|canceled|deactivated) + echo "deploy failed: $status" >&2; exit 1 ;; + esac + sleep 15 + done + + echo "timed out waiting for deploy $deploy_id" >&2 + exit 1 From 9041685d001cf9438f9fbadd76ce5b5505c39be1 Mon Sep 17 00:00:00 2001 From: matheus1lva Date: Thu, 18 Jun 2026 10:58:23 -0300 Subject: [PATCH 2/3] ci(deploy): source secrets via 1Password load-secrets-action + env template --- .github/deploy.env.tpl | 57 ++++++++++++++++++++++++++++++++++++ .github/workflows/deploy.yml | 43 ++++++++++++++------------- 2 files changed, 79 insertions(+), 21 deletions(-) create mode 100644 .github/deploy.env.tpl diff --git a/.github/deploy.env.tpl b/.github/deploy.env.tpl new file mode 100644 index 00000000..5cd7fba1 --- /dev/null +++ b/.github/deploy.env.tpl @@ -0,0 +1,57 @@ +# 1Password secret template for the Render ingest deploy. +# Each line maps a Render env-var name -> a 1Password secret reference. +# load-secrets-action resolves these; the deploy workflow then pushes each one +# into the Render env group `kong`. This file is the single source of truth for +# which secrets get synced. Add/remove lines to match the 1Password `kong` item. +# +# NOT listed on purpose: +# REDIS_HOST / REDIS_PORT / NODE_VERSION -> set fromService in render.yaml +# +# RPC endpoints (chains 1, 10, 137, 250, 8453, 42161) +HTTP_ARCHIVE_1=op://webops-prod/kong/HTTP_ARCHIVE_1 +HTTP_ARCHIVE_10=op://webops-prod/kong/HTTP_ARCHIVE_10 +HTTP_ARCHIVE_137=op://webops-prod/kong/HTTP_ARCHIVE_137 +HTTP_ARCHIVE_250=op://webops-prod/kong/HTTP_ARCHIVE_250 +HTTP_ARCHIVE_8453=op://webops-prod/kong/HTTP_ARCHIVE_8453 +HTTP_ARCHIVE_42161=op://webops-prod/kong/HTTP_ARCHIVE_42161 +HTTP_FULLNODE_1=op://webops-prod/kong/HTTP_FULLNODE_1 +HTTP_FULLNODE_10=op://webops-prod/kong/HTTP_FULLNODE_10 +HTTP_FULLNODE_137=op://webops-prod/kong/HTTP_FULLNODE_137 +HTTP_FULLNODE_250=op://webops-prod/kong/HTTP_FULLNODE_250 +HTTP_FULLNODE_8453=op://webops-prod/kong/HTTP_FULLNODE_8453 +HTTP_FULLNODE_42161=op://webops-prod/kong/HTTP_FULLNODE_42161 + +# Postgres +POSTGRES_HOST=op://webops-prod/kong/POSTGRES_HOST +POSTGRES_DATABASE=op://webops-prod/kong/POSTGRES_DATABASE +POSTGRES_USER=op://webops-prod/kong/POSTGRES_USER +POSTGRES_PASSWORD=op://webops-prod/kong/POSTGRES_PASSWORD +POSTGRES_PORT=op://webops-prod/kong/POSTGRES_PORT +POSTGRES_SSL=op://webops-prod/kong/POSTGRES_SSL + +# Prices / yDaemon / yPrice +YDAEMON_API=op://webops-prod/kong/YDAEMON_API +YPRICE_ENABLED=op://webops-prod/kong/YPRICE_ENABLED +YPRICE_API=op://webops-prod/kong/YPRICE_API +YPRICE_API_X_SIGNER=op://webops-prod/kong/YPRICE_API_X_SIGNER +YPRICE_API_X_SIGNATURE=op://webops-prod/kong/YPRICE_API_X_SIGNATURE +PRICE_SERVICE_API_KEY=op://webops-prod/kong/PRICE_SERVICE_API_KEY +PRICE_SERVICE_URL=op://webops-prod/kong/PRICE_SERVICE_URL + +# Webhook auth +WEBHOOK_SECRET_S_SUBSCRIPTIONID=op://webops-prod/kong/WEBHOOK_SECRET_S_SUBSCRIPTIONID +WEBHOOK_SECRET_S_YVUSD_APR=op://webops-prod/kong/WEBHOOK_SECRET_S_YVUSD_APR +WEBHOOK_SECRET_S_KATANA_APR=op://webops-prod/kong/WEBHOOK_SECRET_S_KATANA_APR + +# Optional config / extras the ingest worker can read. Uncomment the ones that +# actually exist as fields in the 1Password `kong` item (an unresolved ref fails +# the whole job). +# MONITOR_API_KEY=op://webops-prod/kong/MONITOR_API_KEY +# SENTRY_DSN=op://webops-prod/kong/SENTRY_DSN +# RISK_CDN_URL=op://webops-prod/kong/RISK_CDN_URL +# REST_CACHE_REDIS_URL=op://webops-prod/kong/REST_CACHE_REDIS_URL +# GQL_CACHE_REDIS_URL=op://webops-prod/kong/GQL_CACHE_REDIS_URL +# APE_TAX_VAULTS=op://webops-prod/kong/APE_TAX_VAULTS +# DEFAULT_START_DAYS_AGO=op://webops-prod/kong/DEFAULT_START_DAYS_AGO +# FULL_NODE_DEPTH=op://webops-prod/kong/FULL_NODE_DEPTH +# LOG_STRIDE=op://webops-prod/kong/LOG_STRIDE diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3954a120..4a8d5be5 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,9 +1,10 @@ name: deploy # Deploy the ingest worker to Render with 1Password as the source of truth for -# secrets. Every env var lives in the 1Password `kong` item (vault webops-prod); -# this workflow pushes them into the Render env group `kong` (fromGroup: kong in -# render.yaml) and then triggers a deploy via the Render API. +# secrets. Secrets live in the 1Password `kong` item (vault webops-prod) and are +# declared in .github/deploy.env.tpl. This workflow resolves them with +# 1Password/load-secrets-action, pushes each into the Render env group `kong` +# (fromGroup: kong in render.yaml), then triggers a deploy via the Render API. # # IMPORTANT: turn OFF Render's own Auto-Deploy for the ingest-v-2 service so this # workflow is the only thing that deploys (otherwise the git push and this API @@ -22,39 +23,39 @@ concurrency: cancel-in-progress: false env: - OP_VAULT: webops-prod - OP_ITEM: kong + ENV_TEMPLATE: .github/deploy.env.tpl RENDER_ENV_GROUP_ID: evg-d3dkc7jipnbc73ce23lg jobs: deploy: runs-on: ubuntu-latest steps: - - uses: 1Password/install-cli-action@v1 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - name: Sync 1Password -> Render env group + - name: Load secrets from 1Password + uses: 1password/load-secrets-action@v4 + with: + export-env: true env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + OP_ENV_FILE: ${{ env.ENV_TEMPLATE }} + + - name: Sync secrets to Render env group + env: RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }} run: | set -euo pipefail - # Keys managed on the service itself (render.yaml), not the group. - EXCLUDE="REDIS_HOST REDIS_PORT NODE_VERSION" - - # Enumerate env-var fields from the 1Password item. Upper-snake labels - # only, skipping op defaults (notesPlain, username, password, ...). - keys="$(op item get "$OP_ITEM" --vault "$OP_VAULT" --format json \ - | jq -r '.fields[] | select(.value != null) | select(.label | test("^[A-Z][A-Z0-9_]+$")) | .label')" - - if [ -z "$keys" ]; then - echo "no env-var fields found in 1Password item $OP_VAULT/$OP_ITEM" >&2 - exit 1 - fi + # Derive the key list from the same template load-secrets-action used, + # so the two can never drift. + keys="$(grep -oE '^[A-Z][A-Z0-9_]*' "$ENV_TEMPLATE")" for key in $keys; do - case " $EXCLUDE " in *" $key "*) echo "skip $key (service-managed)"; continue;; esac - value="$(op read "op://${OP_VAULT}/${OP_ITEM}/${key}")" + value="$(printenv "$key" || true)" + if [ -z "$value" ]; then + echo "warn: $key resolved empty, skipping" >&2 + continue + fi curl -fsS -X PUT \ "https://api.render.com/v1/env-groups/${RENDER_ENV_GROUP_ID}/env-vars/${key}" \ -H "Authorization: Bearer ${RENDER_API_KEY}" \ From 0e9ecbb955598f895e52d8a4bb8df8e71c519f25 Mon Sep 17 00:00:00 2001 From: matheus1lva Date: Wed, 24 Jun 2026 11:39:13 -0300 Subject: [PATCH 3/3] last tests --- .github/deploy.env.tpl | 78 +++++++++++++++++++----------------- .github/workflows/deploy.yml | 42 ++++++++++--------- .gitignore | 1 + 3 files changed, 65 insertions(+), 56 deletions(-) diff --git a/.github/deploy.env.tpl b/.github/deploy.env.tpl index 5cd7fba1..22fe15b8 100644 --- a/.github/deploy.env.tpl +++ b/.github/deploy.env.tpl @@ -1,57 +1,63 @@ -# 1Password secret template for the Render ingest deploy. -# Each line maps a Render env-var name -> a 1Password secret reference. -# load-secrets-action resolves these; the deploy workflow then pushes each one -# into the Render env group `kong`. This file is the single source of truth for -# which secrets get synced. Add/remove lines to match the 1Password `kong` item. -# -# NOT listed on purpose: -# REDIS_HOST / REDIS_PORT / NODE_VERSION -> set fromService in render.yaml -# -# RPC endpoints (chains 1, 10, 137, 250, 8453, 42161) + +GQL_PORT=op://webops-prod/kong/GQL_PORT +GQL_ENABLE_CACHE=op://webops-prod/kong/GQL_ENABLE_CACHE +MQ_CONCURRENCY_MAX_PER_PROCESSOR=op://webops-prod/kong/MQ_CONCURRENCY_MAX_PER_PROCESSOR +MQ_CONCURRENCY_THRESHOLD=op://webops-prod/kong/MQ_CONCURRENCY_THRESHOLD +MQ_CONCURRENCY_MAX_PER_PROCESSOR_100=op://webops-prod/kong/MQ_CONCURRENCY_MAX_PER_PROCESSOR_100 +MQ_CONCURRENCY_THRESHOLD_100=op://webops-prod/kong/MQ_CONCURRENCY_THRESHOLD_100 +MQ_CONCURRENCY_MAX_PER_PROCESSOR_137=op://webops-prod/kong/MQ_CONCURRENCY_MAX_PER_PROCESSOR_137 +MQ_CONCURRENCY_THRESHOLD_137=op://webops-prod/kong/MQ_CONCURRENCY_THRESHOLD_137 + +DEFAULT_START_DAYS_AGO=op://webops-prod/kong/DEFAULT_START_DAYS_AGO + +POSTGRES_HOST=op://webops-prod/kong/POSTGRES_HOST +POSTGRES_DATABASE=op://webops-prod/kong/POSTGRES_DATABASE +POSTGRES_USER=op://webops-prod/kong/POSTGRES_USER +POSTGRES_PASSWORD=op://webops-prod/kong/POSTGRES_PASSWORD +POSTGRES_SSL=op://webops-prod/kong/POSTGRES_SSL +POSTGRES_SSL_REJECT_UNAUTHORIZED=op://webops-prod/kong/POSTGRES_SSL_REJECT_UNAUTHORIZED +POSTGRES_PORT=op://webops-prod/kong/POSTGRES_PORT + HTTP_ARCHIVE_1=op://webops-prod/kong/HTTP_ARCHIVE_1 HTTP_ARCHIVE_10=op://webops-prod/kong/HTTP_ARCHIVE_10 +HTTP_ARCHIVE_100=op://webops-prod/kong/HTTP_ARCHIVE_100 HTTP_ARCHIVE_137=op://webops-prod/kong/HTTP_ARCHIVE_137 HTTP_ARCHIVE_250=op://webops-prod/kong/HTTP_ARCHIVE_250 HTTP_ARCHIVE_8453=op://webops-prod/kong/HTTP_ARCHIVE_8453 +HTTP_ARCHIVE_34443=op://webops-prod/kong/HTTP_ARCHIVE_34443 HTTP_ARCHIVE_42161=op://webops-prod/kong/HTTP_ARCHIVE_42161 +HTTP_ARCHIVE_80094=op://webops-prod/kong/HTTP_ARCHIVE_80094 +HTTP_ARCHIVE_747474=op://webops-prod/kong/HTTP_ARCHIVE_747474 + HTTP_FULLNODE_1=op://webops-prod/kong/HTTP_FULLNODE_1 HTTP_FULLNODE_10=op://webops-prod/kong/HTTP_FULLNODE_10 +HTTP_FULLNODE_100=op://webops-prod/kong/HTTP_FULLNODE_100 HTTP_FULLNODE_137=op://webops-prod/kong/HTTP_FULLNODE_137 HTTP_FULLNODE_250=op://webops-prod/kong/HTTP_FULLNODE_250 HTTP_FULLNODE_8453=op://webops-prod/kong/HTTP_FULLNODE_8453 +HTTP_FULLNODE_34443=op://webops-prod/kong/HTTP_FULLNODE_34443 HTTP_FULLNODE_42161=op://webops-prod/kong/HTTP_FULLNODE_42161 +HTTP_FULLNODE_80094=op://webops-prod/kong/HTTP_FULLNODE_80094 +HTTP_FULLNODE_747474=op://webops-prod/kong/HTTP_FULLNODE_747474 -# Postgres -POSTGRES_HOST=op://webops-prod/kong/POSTGRES_HOST -POSTGRES_DATABASE=op://webops-prod/kong/POSTGRES_DATABASE -POSTGRES_USER=op://webops-prod/kong/POSTGRES_USER -POSTGRES_PASSWORD=op://webops-prod/kong/POSTGRES_PASSWORD -POSTGRES_PORT=op://webops-prod/kong/POSTGRES_PORT -POSTGRES_SSL=op://webops-prod/kong/POSTGRES_SSL - -# Prices / yDaemon / yPrice YDAEMON_API=op://webops-prod/kong/YDAEMON_API + YPRICE_ENABLED=op://webops-prod/kong/YPRICE_ENABLED YPRICE_API=op://webops-prod/kong/YPRICE_API YPRICE_API_X_SIGNER=op://webops-prod/kong/YPRICE_API_X_SIGNER YPRICE_API_X_SIGNATURE=op://webops-prod/kong/YPRICE_API_X_SIGNATURE -PRICE_SERVICE_API_KEY=op://webops-prod/kong/PRICE_SERVICE_API_KEY -PRICE_SERVICE_URL=op://webops-prod/kong/PRICE_SERVICE_URL -# Webhook auth -WEBHOOK_SECRET_S_SUBSCRIPTIONID=op://webops-prod/kong/WEBHOOK_SECRET_S_SUBSCRIPTIONID +GITHUB_PERSONAL_ACCESS_TOKEN=op://webops-prod/kong/GITHUB_PERSONAL_ACCESS_TOKEN +APE_TAX_VAULTS=op://webops-prod/kong/APE_TAX_VAULTS +WAVEYDB_HOST=op://webops-prod/kong/WAVEYDB_HOST +WAVEYDB_NAME=op://webops-prod/kong/WAVEYDB_NAME +WAVEYDB_USER=op://webops-prod/kong/WAVEYDB_USER +WAVEYDB_PASSWORD=op://webops-prod/kong/WAVEYDB_PASSWORD + +WEBHOOK_SECRET_S_0E70A395=op://webops-prod/kong/WEBHOOK_SECRET_S_0E70A395 +WEBHOOK_SECRET_S_C2795BC0=op://webops-prod/kong/WEBHOOK_SECRET_S_C2795BC0 WEBHOOK_SECRET_S_YVUSD_APR=op://webops-prod/kong/WEBHOOK_SECRET_S_YVUSD_APR WEBHOOK_SECRET_S_KATANA_APR=op://webops-prod/kong/WEBHOOK_SECRET_S_KATANA_APR - -# Optional config / extras the ingest worker can read. Uncomment the ones that -# actually exist as fields in the 1Password `kong` item (an unresolved ref fails -# the whole job). -# MONITOR_API_KEY=op://webops-prod/kong/MONITOR_API_KEY -# SENTRY_DSN=op://webops-prod/kong/SENTRY_DSN -# RISK_CDN_URL=op://webops-prod/kong/RISK_CDN_URL -# REST_CACHE_REDIS_URL=op://webops-prod/kong/REST_CACHE_REDIS_URL -# GQL_CACHE_REDIS_URL=op://webops-prod/kong/GQL_CACHE_REDIS_URL -# APE_TAX_VAULTS=op://webops-prod/kong/APE_TAX_VAULTS -# DEFAULT_START_DAYS_AGO=op://webops-prod/kong/DEFAULT_START_DAYS_AGO -# FULL_NODE_DEPTH=op://webops-prod/kong/FULL_NODE_DEPTH -# LOG_STRIDE=op://webops-prod/kong/LOG_STRIDE +SENTRY_DSN=op://webops-prod/kong/SENTRY_DSN +MQ_INVENTORY=op://webops-prod/kong/MQ_INVENTORY +PRICE_SERVICE_API_KEY=op://webops-prod/kong/PRICE_SERVICE_API_KEY diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4a8d5be5..8e630dcf 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,8 +3,9 @@ name: deploy # Deploy the ingest worker to Render with 1Password as the source of truth for # secrets. Secrets live in the 1Password `kong` item (vault webops-prod) and are # declared in .github/deploy.env.tpl. This workflow resolves them with -# 1Password/load-secrets-action, pushes each into the Render env group `kong` -# (fromGroup: kong in render.yaml), then triggers a deploy via the Render API. +# 1Password/load-secrets-action, writes them as the `.env` secret file on the +# Render env group `kong` (fromGroup: kong in render.yaml; the worker reads it via +# dotenv), then triggers a deploy via the Render API. # # IMPORTANT: turn OFF Render's own Auto-Deploy for the ingest-v-2 service so this # workflow is the only thing that deploys (otherwise the git push and this API @@ -40,30 +41,31 @@ jobs: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} OP_ENV_FILE: ${{ env.ENV_TEMPLATE }} - - name: Sync secrets to Render env group + - name: Sync secrets to Render env group `.env` secret file env: RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }} + RENDER_ENV_GROUP_ID: ${{ env.RENDER_ENV_GROUP_ID }} run: | set -euo pipefail - # Derive the key list from the same template load-secrets-action used, - # so the two can never drift. - keys="$(grep -oE '^[A-Z][A-Z0-9_]*' "$ENV_TEMPLATE")" - - for key in $keys; do + # Build the .env body from the same template (so the two can never drift), + # pulling each resolved value from the env load-secrets-action exported. + # One PUT replaces the whole secret file -> keys removed from the template + # are pruned, keeping the file a true mirror of this template. + # ponytail: KEY=value assumes single-line values (RPC URLs, creds, tokens + # qualify); a value with a literal newline would corrupt the file. + content="$(grep -oE '^[A-Z][A-Z0-9_]*' "$ENV_TEMPLATE" | while read -r key; do value="$(printenv "$key" || true)" - if [ -z "$value" ]; then - echo "warn: $key resolved empty, skipping" >&2 - continue - fi - curl -fsS -X PUT \ - "https://api.render.com/v1/env-groups/${RENDER_ENV_GROUP_ID}/env-vars/${key}" \ - -H "Authorization: Bearer ${RENDER_API_KEY}" \ - -H "Content-Type: application/json" \ - -d "$(jq -n --arg v "$value" '{value: $v}')" \ - -o /dev/null - echo "synced $key" - done + [ -n "$value" ] && printf '%s=%s\n' "$key" "$value" + done)" + + curl -fsS -X PUT \ + "https://api.render.com/v1/env-groups/${RENDER_ENV_GROUP_ID}/secret-files/.env" \ + -H "Authorization: Bearer ${RENDER_API_KEY}" \ + -H "Content-Type: application/json" \ + -d "$(jq -n --arg c "$content" '{content: $c}')" \ + -o /dev/null + echo "synced .env secret file ($(printf '%s\n' "$content" | grep -cE '^[A-Z][A-Z0-9_]*=') vars)" - name: Trigger Render deploy env: diff --git a/.gitignore b/.gitignore index 558f603e..840c1097 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ gql-cache* __pycache__ output/ .mcp.json +.secrets