Update deployment SSH key ownership and allow branch-based workflow testing#5
Update deployment SSH key ownership and allow branch-based workflow testing#5
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughWorkflows now accept Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant GH as GitHub Actions
participant Runner as Job Runner
participant Nix as Nix/rage
participant Git as Git Remote
User->>GH: trigger workflow_dispatch(target_ref, allow_non_main)
GH->>Runner: start job with inputs
Runner->>Runner: set env TARGET_REF = inputs.target_ref
Runner->>Runner: validate TARGET_REF == "main" OR allow_non_main == true
alt validation passes
Runner->>Git: actions/checkout ref=TARGET_REF
Runner->>Nix: nix shell -> rage decrypt config/runtime.env.age -> /tmp/runtime.env
Nix->>Runner: decrypted file
Runner->>Runner: source /tmp/runtime.env -> export DUMP_BASE_URL, SPACES_* to GITHUB_ENV
Runner->>Runner: run deployment/provision steps
Runner->>Git: git push origin HEAD:"$TARGET_REF"
Runner->>Runner: update step summaries with Target ref
else validation fails
Runner->>Runner: fail workflow
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/provision-host.yaml:
- Around line 37-44: The current Validate target ref step allows tag/SHA refs
when allow_non_main=true, which can lead to attempting a state push to a
non-branch ref; update that step to explicitly verify TARGET_REF is an existing
branch before proceeding (e.g., run a git query such as git ls-remote --heads
origin "$TARGET_REF" or use the GitHub API to confirm a refs/heads/$TARGET_REF
exists) and fail with a clear error if the branch is missing; keep the existing
allow_non_main gating but add this branch-existence check in the same step so
the later state push only runs for valid branch destinations.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 49e623d5-3c09-4f09-9351-63cc3ff7d4fe
📒 Files selected for processing (4)
.github/workflows/deploy.yaml.github/workflows/provision-host.yamlinfra/variables.tfkeys.nix
| - name: Validate target ref | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "${GITHUB_REF_NAME}" != "main" ]; then | ||
| echo "This workflow must be run from the main branch." >&2 | ||
| if [ "$TARGET_REF" != "main" ] && [ "${{ inputs.allow_non_main }}" != "true" ]; then | ||
| echo "Set allow_non_main=true to provision from a non-main ref." >&2 | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
Constrain target_ref to an existing branch before state push.
Line 90 assumes TARGET_REF is a branch destination. Current validation (Line 37-Line 44) permits tag/SHA-style refs when allow_non_main=true, which can let provisioning run and then fail persisting encrypted Terraform state.
🔧 Suggested hardening
- uses: actions/checkout@v4
with:
ref: ${{ env.TARGET_REF }}
submodules: recursive
fetch-depth: 0
+
+ - name: Ensure target_ref is an existing remote branch
+ shell: bash
+ run: |
+ set -euo pipefail
+ if ! git ls-remote --exit-code --heads origin "$TARGET_REF" >/dev/null; then
+ echo "target_ref must be an existing branch name because terraform state is pushed back to it." >&2
+ exit 1
+ fiAlso applies to: 90-90
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/provision-host.yaml around lines 37 - 44, The current
Validate target ref step allows tag/SHA refs when allow_non_main=true, which can
lead to attempting a state push to a non-branch ref; update that step to
explicitly verify TARGET_REF is an existing branch before proceeding (e.g., run
a git query such as git ls-remote --heads origin "$TARGET_REF" or use the GitHub
API to confirm a refs/heads/$TARGET_REF exists) and fail with a clear error if
the branch is missing; keep the existing allow_non_main gating but add this
branch-existence check in the same step so the later state push only runs for
valid branch destinations.
0b08c92 to
743a1f6
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/create-empty-local-db-manifest.yaml:
- Around line 30-48: Extract the repeated SSH/rage decryption block into a
reusable composite action (e.g. create
.github/actions/load-runtime-config/action.yaml) that exposes an input like
"ssh_key" and implements the steps currently in the workflow (mkdir ~/.ssh,
write and chmod id_ed25519, run rage to decrypt config/runtime.env.age to
/tmp/local-db-runtime.env, source it and append DUMP_BASE_URL, SPACES_REGION,
SPACES_BUCKET, SPACES_ENDPOINT to $GITHUB_ENV); then replace the inline block in
this workflow and deploy.yaml with a single step that calls the composite action
and passes secrets.SSH_KEY as the ssh_key input.
In `@infra/variables.tf`:
- Line 10: The default SSH key name variable is set to "github_do" which must
exist in DigitalOcean or Terraform will fail; verify or change the default by
updating the variable `var.ssh_key_name` in `infra/variables.tf` or add the
corresponding SSH key in your DigitalOcean account so the `data
"digitalocean_ssh_key"` lookup in `infra/main.tf` can succeed. Ensure the chosen
name matches the actual key label in DigitalOcean and keep `var.ssh_key_name`
consistent across your Terraform configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 07dcdc9b-ed2a-4ece-9ec7-f283d96c64e2
📒 Files selected for processing (8)
.github/workflows/create-empty-local-db-manifest.yaml.github/workflows/deploy.yamlconfig/runtime.env.ageconfig/runtime.env.exampleconfig/secrets.nixinfra/terraform.tfvars.ageinfra/terraform.tfvars.exampleinfra/variables.tf
| - name: Load encrypted runtime config | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| mkdir -p ~/.ssh | ||
| printf '%s\n' "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 | ||
| chmod 600 ~/.ssh/id_ed25519 | ||
| nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env' | ||
| set -a | ||
| # shellcheck disable=SC1091 | ||
| source /tmp/local-db-runtime.env | ||
| set +a | ||
| { | ||
| echo "DUMP_BASE_URL=$DUMP_BASE_URL" | ||
| echo "SPACES_REGION=$SPACES_REGION" | ||
| echo "SPACES_BUCKET=$SPACES_BUCKET" | ||
| echo "SPACES_ENDPOINT=$SPACES_ENDPOINT" | ||
| } >> "$GITHUB_ENV" | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider extracting the runtime config loading to a reusable composite action.
This decryption pattern (SSH key setup + rage decrypt + export to $GITHUB_ENV) is duplicated across deploy.yaml and this workflow. A composite action would reduce maintenance burden and ensure consistency.
♻️ Example composite action structure
Create .github/actions/load-runtime-config/action.yaml:
name: Load encrypted runtime config
description: Decrypt and load runtime.env.age into GITHUB_ENV
inputs:
ssh_key:
description: SSH private key for decryption
required: true
runs:
using: composite
steps:
- shell: bash
run: |
set -euo pipefail
mkdir -p ~/.ssh
printf '%s\n' "${{ inputs.ssh_key }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env'
set -a
source /tmp/local-db-runtime.env
set +a
{
echo "DUMP_BASE_URL=$DUMP_BASE_URL"
echo "SPACES_REGION=$SPACES_REGION"
echo "SPACES_BUCKET=$SPACES_BUCKET"
echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
} >> "$GITHUB_ENV"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/create-empty-local-db-manifest.yaml around lines 30 - 48,
Extract the repeated SSH/rage decryption block into a reusable composite action
(e.g. create .github/actions/load-runtime-config/action.yaml) that exposes an
input like "ssh_key" and implements the steps currently in the workflow (mkdir
~/.ssh, write and chmod id_ed25519, run rage to decrypt config/runtime.env.age
to /tmp/local-db-runtime.env, source it and append DUMP_BASE_URL, SPACES_REGION,
SPACES_BUCKET, SPACES_ENDPOINT to $GITHUB_ENV); then replace the inline block in
this workflow and deploy.yaml with a single step that calls the composite action
and passes secrets.SSH_KEY as the ssh_key input.
| description = "Name of the SSH key in DigitalOcean to add to the droplet" | ||
| type = string | ||
| default = "st0x-op" | ||
| default = "github_do" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how the SSH key is referenced in main.tf
ast-grep --pattern 'data "digitalocean_ssh_key" $_ {
$$$
}'Repository: rainlanguage/rain.local-db.remote
Length of output: 59
🏁 Script executed:
#!/bin/bash
# First, check if the infra directory exists and list relevant files
git ls-files infra/ | head -20Repository: rainlanguage/rain.local-db.remote
Length of output: 254
🏁 Script executed:
#!/bin/bash
# Search for digitalocean_ssh_key references using rg
rg "digitalocean_ssh_key" --type tf -B 2 -A 2Repository: rainlanguage/rain.local-db.remote
Length of output: 366
🏁 Script executed:
#!/bin/bash
# Read the actual variables.tf file around line 10
if [ -f infra/variables.tf ]; then
cat -n infra/variables.tf | head -30
else
echo "infra/variables.tf not found"
fiRepository: rainlanguage/rain.local-db.remote
Length of output: 912
Ensure the github_do SSH key exists in DigitalOcean before running Terraform.
The digitalocean_ssh_key data source in infra/main.tf looks up the SSH key by the name stored in var.ssh_key_name. If no SSH key named github_do exists in the DigitalOcean account, Terraform will fail during the apply step.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/variables.tf` at line 10, The default SSH key name variable is set to
"github_do" which must exist in DigitalOcean or Terraform will fail; verify or
change the default by updating the variable `var.ssh_key_name` in
`infra/variables.tf` or add the corresponding SSH key in your DigitalOcean
account so the `data "digitalocean_ssh_key"` lookup in `infra/main.tf` can
succeed. Ensure the chosen name matches the actual key label in DigitalOcean and
keep `var.ssh_key_name` consistent across your Terraform configuration.
743a1f6 to
ea4d719
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/create-empty-local-db-manifest.yaml:
- Around line 30-47: The decrypted runtime file /tmp/local-db-runtime.env
created in the "Load encrypted runtime config" step contains sensitive secrets
and must be removed after use; modify the step so that after sourcing and
appending the needed variables to GITHUB_ENV you securely delete the temporary
file (e.g., overwrite/shred if available or at minimum rm -f
/tmp/local-db-runtime.env) and ensure it had restrictive permissions while
present; reference the commands that create/source the file (nix shell ... >
/tmp/local-db-runtime.env and source /tmp/local-db-runtime.env) and add a
cleanup action immediately after exporting the variables.
In @.github/workflows/deploy.yaml:
- Around line 74-88: The decrypted runtime file /tmp/local-db-runtime.env is
left on disk after the "Load encrypted runtime config" step; update that step
(the shell block that runs nix/rage and sources /tmp/local-db-runtime.env) to
securely remove the file when done by adding a removal command (e.g., rm -f
/tmp/local-db-runtime.env) after exporting the variables to $GITHUB_ENV so the
temporary secret file does not persist on the runner.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 40a610b8-8592-4734-be71-fc916fb07d33
📒 Files selected for processing (8)
.github/workflows/create-empty-local-db-manifest.yaml.github/workflows/deploy.yamlconfig/runtime.env.ageconfig/runtime.env.exampleconfig/secrets.nixinfra/terraform.tfvars.ageinfra/terraform.tfvars.exampleinfra/variables.tf
| - name: Load encrypted runtime config | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| mkdir -p ~/.ssh | ||
| printf '%s\n' "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 | ||
| chmod 600 ~/.ssh/id_ed25519 | ||
| nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env' | ||
| set -a | ||
| # shellcheck disable=SC1091 | ||
| source /tmp/local-db-runtime.env | ||
| set +a | ||
| { | ||
| echo "DUMP_BASE_URL=$DUMP_BASE_URL" | ||
| echo "SPACES_REGION=$SPACES_REGION" | ||
| echo "SPACES_BUCKET=$SPACES_BUCKET" | ||
| echo "SPACES_ENDPOINT=$SPACES_ENDPOINT" | ||
| } >> "$GITHUB_ENV" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider cleaning up the decrypted secrets file.
The decrypted runtime config at /tmp/local-db-runtime.env contains sensitive values and persists after the step completes. Consider adding cleanup for defense in depth.
🛡️ Suggested cleanup
{
echo "DUMP_BASE_URL=$DUMP_BASE_URL"
echo "SPACES_REGION=$SPACES_REGION"
echo "SPACES_BUCKET=$SPACES_BUCKET"
echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
} >> "$GITHUB_ENV"
+ rm -f /tmp/local-db-runtime.env📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Load encrypted runtime config | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p ~/.ssh | |
| printf '%s\n' "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 | |
| chmod 600 ~/.ssh/id_ed25519 | |
| nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env' | |
| set -a | |
| # shellcheck disable=SC1091 | |
| source /tmp/local-db-runtime.env | |
| set +a | |
| { | |
| echo "DUMP_BASE_URL=$DUMP_BASE_URL" | |
| echo "SPACES_REGION=$SPACES_REGION" | |
| echo "SPACES_BUCKET=$SPACES_BUCKET" | |
| echo "SPACES_ENDPOINT=$SPACES_ENDPOINT" | |
| } >> "$GITHUB_ENV" | |
| - name: Load encrypted runtime config | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p ~/.ssh | |
| printf '%s\n' "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 | |
| chmod 600 ~/.ssh/id_ed25519 | |
| nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env' | |
| set -a | |
| # shellcheck disable=SC1091 | |
| source /tmp/local-db-runtime.env | |
| set +a | |
| { | |
| echo "DUMP_BASE_URL=$DUMP_BASE_URL" | |
| echo "SPACES_REGION=$SPACES_REGION" | |
| echo "SPACES_BUCKET=$SPACES_BUCKET" | |
| echo "SPACES_ENDPOINT=$SPACES_ENDPOINT" | |
| } >> "$GITHUB_ENV" | |
| rm -f /tmp/local-db-runtime.env |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/create-empty-local-db-manifest.yaml around lines 30 - 47,
The decrypted runtime file /tmp/local-db-runtime.env created in the "Load
encrypted runtime config" step contains sensitive secrets and must be removed
after use; modify the step so that after sourcing and appending the needed
variables to GITHUB_ENV you securely delete the temporary file (e.g.,
overwrite/shred if available or at minimum rm -f /tmp/local-db-runtime.env) and
ensure it had restrictive permissions while present; reference the commands that
create/source the file (nix shell ... > /tmp/local-db-runtime.env and source
/tmp/local-db-runtime.env) and add a cleanup action immediately after exporting
the variables.
| - name: Load encrypted runtime config | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env' | ||
| set -a | ||
| # shellcheck disable=SC1091 | ||
| source /tmp/local-db-runtime.env | ||
| set +a | ||
| { | ||
| echo "DUMP_BASE_URL=$DUMP_BASE_URL" | ||
| echo "SPACES_REGION=$SPACES_REGION" | ||
| echo "SPACES_BUCKET=$SPACES_BUCKET" | ||
| echo "SPACES_ENDPOINT=$SPACES_ENDPOINT" | ||
| } >> "$GITHUB_ENV" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider cleaning up the decrypted secrets file.
Same as in create-empty-local-db-manifest.yaml: the decrypted runtime config at /tmp/local-db-runtime.env persists after the step. Consider adding rm -f /tmp/local-db-runtime.env at the end for defense in depth.
🛡️ Suggested cleanup
{
echo "DUMP_BASE_URL=$DUMP_BASE_URL"
echo "SPACES_REGION=$SPACES_REGION"
echo "SPACES_BUCKET=$SPACES_BUCKET"
echo "SPACES_ENDPOINT=$SPACES_ENDPOINT"
} >> "$GITHUB_ENV"
+ rm -f /tmp/local-db-runtime.env📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Load encrypted runtime config | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env' | |
| set -a | |
| # shellcheck disable=SC1091 | |
| source /tmp/local-db-runtime.env | |
| set +a | |
| { | |
| echo "DUMP_BASE_URL=$DUMP_BASE_URL" | |
| echo "SPACES_REGION=$SPACES_REGION" | |
| echo "SPACES_BUCKET=$SPACES_BUCKET" | |
| echo "SPACES_ENDPOINT=$SPACES_ENDPOINT" | |
| } >> "$GITHUB_ENV" | |
| - name: Load encrypted runtime config | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| nix shell nixpkgs#rage -c sh -c 'rage -d -i ~/.ssh/id_ed25519 config/runtime.env.age > /tmp/local-db-runtime.env' | |
| set -a | |
| # shellcheck disable=SC1091 | |
| source /tmp/local-db-runtime.env | |
| set +a | |
| { | |
| echo "DUMP_BASE_URL=$DUMP_BASE_URL" | |
| echo "SPACES_REGION=$SPACES_REGION" | |
| echo "SPACES_BUCKET=$SPACES_BUCKET" | |
| echo "SPACES_ENDPOINT=$SPACES_ENDPOINT" | |
| } >> "$GITHUB_ENV" | |
| rm -f /tmp/local-db-runtime.env |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/deploy.yaml around lines 74 - 88, The decrypted runtime
file /tmp/local-db-runtime.env is left on disk after the "Load encrypted runtime
config" step; update that step (the shell block that runs nix/rage and sources
/tmp/local-db-runtime.env) to securely remove the file when done by adding a
removal command (e.g., rm -f /tmp/local-db-runtime.env) after exporting the
variables to $GITHUB_ENV so the temporary secret file does not persist on the
runner.
83ae77c to
8516824
Compare
8516824 to
3f38d00
Compare

Motivation
The local DB remote deployment should no longer depend on legacy SSH recipients that are not intended to have access in this repo.
This PR updates the provisioning/deployment access model to use the new
github_dokey, keepsardaas the only additional recipient, and makes the manual GitHub workflows testable from a non-mainbranch so provisioning and deploy can be exercised before merge.Solution
github_dokeys.nixso onlygithub_doandardaretain accessst0x-op,sid, andcirecipients from the repo configProvision Hostto accepttarget_refandallow_non_main, check out the selected ref, and persist Terraform state back to that same refDeployto accepttarget_refandallow_non_mainso the branch version of the workflow can be exercised against the real host before mergetarget_ref=mainandallow_non_main=falseChecks
By submitting this for review, I am confirming I have done the following:
Validated locally with:
Summary by CodeRabbit