From 5a877404047e4168462f7db53494fc7dc74e5481 Mon Sep 17 00:00:00 2001 From: Pino de Candia <32303022+pinodeca@users.noreply.github.com> Date: Sun, 29 Mar 2026 21:59:05 +0000 Subject: [PATCH] Prebuild: init submodule via PAT secret and build pg_durable During the Codespace prebuild, use a GH_PAT Codespace secret to clone the private duroxide-pg-opt submodule, then build pg_durable. The PAT is scrubbed from the filesystem before the prebuild image is snapshotted. Interactive Codespaces use the token permissions already granted via customizations.codespaces.repositories in devcontainer.json (merged in #93). Changes: - onCreateCommand.sh: PAT-based submodule init with credential cleanup, fallback to default credentials, cargo build if submodule available - postCreateCommand.sh: verify submodule and build state on open - CODESPACES_PREBUILDS.md: document dual auth approach (PAT for prebuild, Codespace token for interactive use) Setup: add a GH_PAT Codespace secret in Settings > Secrets > Codespaces with a fine-grained PAT scoped read-only to microsoft/duroxide-pg-opt. --- .devcontainer/onCreateCommand.sh | 61 ++++++++++++++++++++++++++++++ .devcontainer/postCreateCommand.sh | 18 +++++++++ docs/CODESPACES_PREBUILDS.md | 41 +++++++++++++++++--- 3 files changed, 114 insertions(+), 6 deletions(-) diff --git a/.devcontainer/onCreateCommand.sh b/.devcontainer/onCreateCommand.sh index 1d71d306..3604fb5e 100755 --- a/.devcontainer/onCreateCommand.sh +++ b/.devcontainer/onCreateCommand.sh @@ -35,6 +35,67 @@ cargo install cargo-pgrx --version 0.16.1 --locked echo "Initializing pgrx with PostgreSQL 17..." cargo pgrx init --pg17 download +# ── Initialize private submodule (duroxide-pg-opt) ────────────────── +# duroxide-pg-opt is a private repo. Two auth mechanisms: +# +# 1. Prebuild phase: GH_PAT Codespace secret provides access. +# The PAT is injected as a temporary git insteadOf rewrite, used +# for clone, then scrubbed so it never persists in the image. +# +# 2. Interactive Codespace: devcontainer.json grants the built-in +# GITHUB_TOKEN read access via customizations.codespaces.repositories. +# The Codespace credential helper handles auth automatically. +# +# 3. Local Dev Container: user must have their own credentials. + +SUBMODULE_INITIALIZED=0 + +if [ -n "$GH_PAT" ]; then + echo "GH_PAT detected — initializing submodule with PAT..." + + # Temporarily rewrite GitHub HTTPS URLs to include the token. + git config --global url."https://x-access-token:${GH_PAT}@github.com/".insteadOf "https://github.com/" + + if git submodule update --init --recursive; then + echo "✅ Submodule initialized successfully (via PAT)" + SUBMODULE_INITIALIZED=1 + else + echo "⚠️ Submodule initialization failed with PAT" + fi + + # ── Credential cleanup ────────────────────────────────────────── + # Remove the insteadOf rewrite so the PAT is NOT baked into the + # prebuild filesystem snapshot. + git config --global --remove-section "url.https://x-access-token:${GH_PAT}@github.com/" 2>/dev/null || true + echo -e "protocol=https\nhost=github.com" | git credential reject 2>/dev/null || true + + # Belt-and-suspenders: verify no PAT traces remain + if grep -q "x-access-token" "$HOME/.gitconfig" 2>/dev/null; then + echo "⚠️ WARNING: PAT trace found in ~/.gitconfig — scrubbing" + sed -i '/x-access-token/d' "$HOME/.gitconfig" + fi + echo "✅ Credentials cleaned up" +else + echo "GH_PAT not set — trying submodule init with default credentials..." + if git submodule update --init --recursive; then + echo "✅ Submodule initialized successfully" + SUBMODULE_INITIALIZED=1 + else + echo "⚠️ Submodule initialization failed — skipping" + echo " Set GH_PAT secret or ensure credentials for microsoft/duroxide-pg-opt" + fi +fi + +# ── Build pg_durable ──────────────────────────────────────────────── +# Only build if the submodule is present (needed for compilation) +if [ "$SUBMODULE_INITIALIZED" = "1" ] && [ -f "duroxide-pg-opt/Cargo.toml" ]; then + echo "Building pg_durable..." + cargo build --features pg17 + echo "✅ pg_durable built successfully" +else + echo "⚠️ Submodule not available — skipping pg_durable build" +fi + echo "" echo "=========================================" echo "✅ Prebuild setup complete!" diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index 904b020f..ddd6559d 100755 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -25,7 +25,25 @@ else echo "⚠️ pgrx not initialized - running initialization..." cargo pgrx init --pg17 download fi +# Check if submodule is initialized +echo "Checking submodule status..." +if [ -f "duroxide-pg-opt/Cargo.toml" ]; then + echo "✓ duroxide-pg-opt submodule is initialized" +else + echo "⚠️ duroxide-pg-opt submodule not initialized" + echo " Run: git submodule update --init --recursive" +fi +# Check if pg_durable is already built +echo "Checking build status..." +if [ -n "$(find target/debug -name 'libpg_durable*' -print -quit 2>/dev/null)" ]; then + echo "✓ pg_durable is already built" +elif [ -f "duroxide-pg-opt/Cargo.toml" ]; then + echo "Building pg_durable (submodule present but build artifacts missing)..." + cargo build --features pg17 +else + echo "⚠️ pg_durable not built (submodule needed first)" +fi echo "" echo "=========================================" echo "✅ Development environment ready!" diff --git a/docs/CODESPACES_PREBUILDS.md b/docs/CODESPACES_PREBUILDS.md index 72239752..90a6e3fd 100644 --- a/docs/CODESPACES_PREBUILDS.md +++ b/docs/CODESPACES_PREBUILDS.md @@ -19,10 +19,37 @@ Pre-builds must be enabled by a repository administrator: - **Reduce prebuild available to specific regions**: Optional 4. Click **Create** -Once enabled, GitHub will automatically create prebuilt images when: -- Changes are pushed to the main branch -- The devcontainer configuration changes -- Dependencies (Cargo.toml/Cargo.lock) are updated +### Private Submodule Access + +The `duroxide-pg-opt` submodule is a **private repository**. Two mechanisms provide access: + +**1. Interactive Codespaces** — `devcontainer.json` grants the built-in Codespace token read access: + +```json +"codespaces": { + "repositories": { + "microsoft/duroxide-pg-opt": { + "permissions": { "contents": "read" } + } + } +} +``` + +This works when users open a Codespace directly. + +**2. Prebuild phase** — The Codespace token permissions are **not effective during prebuilds**. A GitHub PAT stored as a Codespace secret is required: + +1. Create a **fine-grained PAT** with **read-only** access to `microsoft/duroxide-pg-opt` (Contents: Read) +2. Go to repository **Settings** → **Secrets and variables** → **Codespaces** +3. Click **New repository secret** +4. Name: `GH_PAT`, Value: the PAT from step 1 +5. Click **Add secret** + +**Security notes:** +- `onCreateCommand.sh` uses a temporary `git config insteadOf` rewrite with the PAT, then **immediately scrubs** all traces (git config, credential cache) before the prebuild image is snapshotted. +- The prebuild image is a **filesystem snapshot** — environment variables from secrets are NOT persisted. +- Users who open a Codespace from the prebuild get the submodule files already present, without needing any PAT themselves. +- Use a fine-grained PAT scoped to only `duroxide-pg-opt` with read-only access to minimize exposure. ## How It Works @@ -33,12 +60,14 @@ Codespaces has two distinct phases: 1. **Pre-build Phase** (runs in GitHub Actions, cached for all users) - Triggered by: `.github/workflows/prebuild.yml` - Executes: `onCreateCommand` in `devcontainer.json` - - Duration: ~10 minutes (but only runs once per configuration change) + - Duration: ~15 minutes (but only runs once per configuration change) - Installs: - System dependencies (libssl, clang, bison, etc.) - cargo-pgrx 0.16.1 - PostgreSQL 17 (downloaded and compiled via pgrx) - - Result: Docker image with all dependencies baked in + - `duroxide-pg-opt` submodule (via `GH_PAT` Codespace secret) + - Pre-builds pg_durable (`cargo build --features pg17`) + - Result: Docker image with all dependencies and build artifacts baked in 2. **Post-Create Phase** (runs when user opens a Codespace) - Executes: `postCreateCommand` in `devcontainer.json`