GitHub App Bootstrap Val

This Val Town project exposes an HTTP webhook that acts as a lightweight GitHub App backend. It listens for repository creation events, verifies the webhook signature, infers the project type (Android vs. Node.js), synchronizes the required GitHub Actions secrets, and finally dispatches a bootstrap workflow in the new repository.

Request Flow

  • Validates POST requests signed with x-hub-signature-256 using GITHUB_APP_WEBHOOK_SECRET.
  • Confirms the repository was cloned from an approved template. If the template is unknown, it scores the repo tree for Android or Node.js indicators.
  • Creates an installation-scoped Octokit client, normalizes the app private key (PKCS#1 → PKCS#8), and hydrates GitHub Actions secrets using the repo public key.
  • Waits for .github/workflows/bootstrap-action.yml to register, enables it if necessary, and triggers a workflow dispatch on the default branch.

Environment Configuration

Set these secrets in the Val configuration before deploying:

VariableDescription
GITHUB_APP_WEBHOOK_SECRETShared secret used to validate incoming GitHub webhooks.
GITHUB_APP_IDNumeric GitHub App identifier.
GITHUB_APP_PRIVATE_KEYPEM string for the GitHub App private key (PKCS#1 or PKCS#8; literal newlines or \n escapes both work).
ANDROID_ACTION_SECRETS_JSONJSON object mapping secret names (prefixed with ANDROID_) to plaintext values for Android projects.
NODE_ACTION_SECRETS_JSONJSON object mapping secret names (prefixed with NPM_) to plaintext values for Node.js projects.

Example secret JSON:

{ "ANDROID_KEYSTORE": "base64-keystore", "ANDROID_KEYSTORE_PASSWORD": "super-secret" }

Project Type Detection

  1. Template Mapping – Known templates (e.g., https://github.com/cmwen/min-android-app-template) map directly to android.
  2. Repo Tree Heuristics – If the template is unknown, the handler fetches the git tree and counts indicator files:
    • Android: build.gradle, gradlew, AndroidManifest.xml, etc.
    • Node.js: package.json, pnpm-lock.yaml, vite.config.ts, etc.
  3. Fallback – When the score is inconclusive, type becomes unknown and no secrets are synced.

Secret Synchronization

  • Pulls the repo public key via the Actions API.
  • Encrypts each secret with Tweetsodium (crypto_box_seal).
  • Upserts secrets using the installation token. Secret names missing the expected prefix still upload, but a warning is logged.

Workflow Dispatch Logic

  • Polls for bootstrap-action.yml registration (up to ~60 seconds).
  • Enables the workflow if it is disabled.
  • Dispatches the workflow once the repo is ready; otherwise responds with a 202 to allow retrying later.

Extending the Handler

  • Add Templates: Update TEMPLATE_TYPE_MAP with new template URLs.
  • Add Heuristics: Extend ANDROID_INDICATORS / NODE_INDICATORS or introduce new project types.
  • Add Secrets: Include additional key/value pairs in the JSON env vars. The handler will encrypt and upload everything it finds.

Testing Tips

  • Use vt watch for rapid iteration while sending signed webhook payloads (e.g., via curl).
  • Inspect Val Town logs to confirm classification, secret sync status, and workflow dispatch attempts.
  • If workflow dispatch fails with 404, ensure the workflow file exists on the default branch and that GitHub has finished initializing the repo (may take a minute on newly-created repositories).