From d1abd7799c4370e526a7bd1440c2a526a3d351a3 Mon Sep 17 00:00:00 2001 From: David Neal Date: Wed, 25 Feb 2026 14:33:33 -0500 Subject: [PATCH 1/2] feat: add Heroku auto-deploy via container registry Add a GitHub Actions workflow that builds and deploys all three services (auth, api, app) to Heroku on every push to main. Dockerfiles are updated to map Heroku's dynamic PORT env var to each service's expected port variable, with backward-compatible fallback to existing defaults. post_logout_redirect_uris is now configurable via POST_LOGOUT_REDIRECT_URI env var to support non-localtest.me deployments. Co-Authored-By: Claude Opus 4.6 --- .env.example | 1 + .github/workflows/deploy-heroku.yml | 68 +++++++++++++++++++++++++++++ apps/api/Dockerfile | 2 +- apps/app/Dockerfile | 2 +- apps/auth/.env.example | 1 + apps/auth/Dockerfile | 2 +- apps/auth/src/index.ts | 2 +- 7 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/deploy-heroku.yml diff --git a/.env.example b/.env.example index c125ca5..e2b16e2 100644 --- a/.env.example +++ b/.env.example @@ -28,6 +28,7 @@ API_PORT=3003 CLIENT_ID=dev-rp-CHANGE-FOR-PRODUCTION CLIENT_SECRET=dev-secret-CHANGE-FOR-PRODUCTION REDIRECT_URI=https://app.localtest.me/callback +POST_LOGOUT_REDIRECT_URI=https://app.localtest.me # ===== APPLICATION SECRETS ===== # For development: Use this placeholder value diff --git a/.github/workflows/deploy-heroku.yml b/.github/workflows/deploy-heroku.yml new file mode 100644 index 0000000..4da6458 --- /dev/null +++ b/.github/workflows/deploy-heroku.yml @@ -0,0 +1,68 @@ +# Deploy all services to Heroku via container registry +# Triggers on every push to main, or manually via workflow_dispatch +name: Deploy to Heroku + +on: + push: + branches: [main] + workflow_dispatch: + +env: + HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - service: auth + dockerfile: apps/auth/Dockerfile + app: plaidypus-auth + - service: api + dockerfile: apps/api/Dockerfile + app: plaidypus-api + - service: app + dockerfile: apps/app/Dockerfile + app: plaidypus-app + steps: + - uses: actions/checkout@v4 + + - name: Login to Heroku Container Registry + run: echo "$HEROKU_API_KEY" | docker login --username=_ --password-stdin registry.heroku.com + + - name: Build Docker image + run: docker build -f ${{ matrix.dockerfile }} -t registry.heroku.com/${{ matrix.app }}/web . + + - name: Push Docker image + run: docker push registry.heroku.com/${{ matrix.app }}/web + + release-auth: + needs: build-and-push + runs-on: ubuntu-latest + steps: + - name: Install Heroku CLI + run: curl https://cli-assets.heroku.com/install.sh | sh + + - name: Release auth + run: heroku container:release web --app plaidypus-auth + + release-api: + needs: release-auth + runs-on: ubuntu-latest + steps: + - name: Install Heroku CLI + run: curl https://cli-assets.heroku.com/install.sh | sh + + - name: Release API + run: heroku container:release web --app plaidypus-api + + release-app: + needs: release-api + runs-on: ubuntu-latest + steps: + - name: Install Heroku CLI + run: curl https://cli-assets.heroku.com/install.sh | sh + + - name: Release app + run: heroku container:release web --app plaidypus-app diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index 9120de1..f263a42 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -71,4 +71,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3003/health || exit 1 # Start the application -CMD ["node", "apps/api/dist/index.js"] +CMD ["sh", "-c", "API_PORT=${PORT:-${API_PORT}} node apps/api/dist/index.js"] diff --git a/apps/app/Dockerfile b/apps/app/Dockerfile index b76f3c9..fe9c1af 100644 --- a/apps/app/Dockerfile +++ b/apps/app/Dockerfile @@ -73,4 +73,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3004/ || exit 1 # Start the application -CMD ["node", "apps/app/dist/index.js"] +CMD ["sh", "-c", "APP_PORT=${PORT:-${APP_PORT}} node apps/app/dist/index.js"] diff --git a/apps/auth/.env.example b/apps/auth/.env.example index 809c148..f19374d 100644 --- a/apps/auth/.env.example +++ b/apps/auth/.env.example @@ -10,6 +10,7 @@ OP_PORT=3001 CLIENT_ID=dev-rp-CHANGE-FOR-PRODUCTION CLIENT_SECRET=dev-secret-CHANGE-FOR-PRODUCTION REDIRECT_URI=https://app.localtest.me/callback +POST_LOGOUT_REDIRECT_URI=https://app.localtest.me # ===== API CONFIGURATION ===== API_AUDIENCE=api://my-api diff --git a/apps/auth/Dockerfile b/apps/auth/Dockerfile index c03a4b6..51866a6 100644 --- a/apps/auth/Dockerfile +++ b/apps/auth/Dockerfile @@ -73,4 +73,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3001/.well-known/openid-configuration || exit 1 # Start the application -CMD ["node", "apps/auth/dist/index.js"] +CMD ["sh", "-c", "OP_PORT=${PORT:-${OP_PORT}} node apps/auth/dist/index.js"] diff --git a/apps/auth/src/index.ts b/apps/auth/src/index.ts index 3f0399a..57b0ec1 100644 --- a/apps/auth/src/index.ts +++ b/apps/auth/src/index.ts @@ -88,7 +88,7 @@ function loadOIDCClients() { client_id: getRequiredEnv( "CLIENT_ID", "dev-rp" ), client_secret: getRequiredEnv( "CLIENT_SECRET", "dev-secret" ), redirect_uris: [ getRequiredEnv( "REDIRECT_URI", "https://app.localtest.me/callback" ) ], - post_logout_redirect_uris: [ "https://app.localtest.me" ], + post_logout_redirect_uris: [ getRequiredEnv( "POST_LOGOUT_REDIRECT_URI", "https://app.localtest.me" ) ], grant_types: [ "authorization_code", "refresh_token" ], response_types: [ "code" ], token_endpoint_auth_method: "client_secret_basic" From 1317f4e72c258b9779043dce22afec8598979c62 Mon Sep 17 00:00:00 2001 From: David Neal Date: Wed, 25 Feb 2026 15:44:27 -0500 Subject: [PATCH 2/2] Add Heroku deployment setup guide Documents the end-to-end process for deploying to Heroku: app creation, secret generation, config vars, GitHub Actions secret, and manual/CI deploy steps. All generated secrets use placeholders. Co-Authored-By: Claude Opus 4.6 --- docs/heroku-setup.md | 149 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 docs/heroku-setup.md diff --git a/docs/heroku-setup.md b/docs/heroku-setup.md new file mode 100644 index 0000000..54b9ccf --- /dev/null +++ b/docs/heroku-setup.md @@ -0,0 +1,149 @@ +# Heroku Deployment Setup + +Prerequisites for deploying the three services (`plaidypus-auth`, `plaidypus-api`, `plaidypus-app`) via the GitHub Actions workflow. + +## 1. Create Heroku Apps + +```bash +# If you're using a personal account, use these commands +heroku create plaidypus-auth --stack container +heroku create plaidypus-api --stack container +heroku create plaidypus-app --stack container + +# If you're using a team account, add the --team argument +heroku create plaidypus-auth --stack container --team +heroku create plaidypus-api --stack container --team +heroku create plaidypus-app --stack container --team +``` + +These commands will generate unique URLs for the services, such as `https://plaidypus-auth-xxxxx.herokuapp.com` where the `xxxxx` is a unique key. + +If the apps already exist but aren't on the container stack: + +```bash +heroku stack:set container --app plaidypus-auth +heroku stack:set container --app plaidypus-api +heroku stack:set container --app plaidypus-app +``` + +## 2. Generate Production Secrets + +```bash +node scripts/secrets.js all +``` + +Save the output — you'll use the generated values in the next step. + +## 3. Set Config Vars + +`CLIENT_ID` and `CLIENT_SECRET` must match between plaidypus-auth and plaidypus-app. + +### plaidypus-auth + +```bash +heroku config:set --app plaidypus-auth \ + OP_ISSUER=https://plaidypus-auth-xxxxx.herokuapp.com \ + API_AUDIENCE=api://my-api \ + CLIENT_ID= \ + CLIENT_SECRET= \ + REDIRECT_URI=https://plaidypus-app-xxxxx.herokuapp.com/callback \ + POST_LOGOUT_REDIRECT_URI=https://plaidypus-app-xxxxx.herokuapp.com \ + JWKS='' \ + LOG_LEVEL=info +``` + +### plaidypus-api + +```bash +heroku config:set --app plaidypus-api \ + OP_ISSUER=https://plaidypus-auth-xxxxx.herokuapp.com \ + API_AUDIENCE=api://my-api \ + LOG_LEVEL=info +``` + +### plaidypus-app + +```bash +heroku config:set --app plaidypus-app \ + OP_ISSUER=https://plaidypus-auth-xxxxx.herokuapp.com \ + APP_HOST=https://plaidypus-app-xxxxx.herokuapp.com \ + APP_BASE_URL=https://plaidypus-app-xxxxx.herokuapp.com \ + API_BASE_URL=https://plaidypus-api-xxxxx.herokuapp.com \ + REDIRECT_URI=https://plaidypus-app-xxxxx.herokuapp.com/callback \ + API_AUDIENCE=api://my-api \ + CLIENT_ID= \ + CLIENT_SECRET= \ + COOKIE_SECRET= \ + LOG_LEVEL=info +``` + +## 4. Get Your Heroku API Key + +```bash +# Short-lived token (1 month) +heroku auth:token + +# Long-lived token (1 year) +heroku authorizations:create + +# Token: +``` + +## 5. Add GitHub Repository Secret + +```bash +gh secret set HEROKU_API_KEY --repo plaid/core-exchange-node-example +``` + +Paste the token from step 4 when prompted. + +## 6. Deploy + +### Manual deploy (before workflow is on main) + +Run from the repo root: + +```bash +heroku container:login + +# Auth +docker buildx build --platform linux/amd64 --provenance=false -f apps/auth/Dockerfile -t registry.heroku.com/plaidypus-auth/web --load . +docker push registry.heroku.com/plaidypus-auth/web +heroku container:release web --app plaidypus-auth + +# API +docker buildx build --platform linux/amd64 --provenance=false -f apps/api/Dockerfile -t registry.heroku.com/plaidypus-api/web --load . +docker push registry.heroku.com/plaidypus-api/web +heroku container:release web --app plaidypus-api + +# App +docker buildx build --platform linux/amd64 --provenance=false -f apps/app/Dockerfile -t registry.heroku.com/plaidypus-app/web --load . +docker push registry.heroku.com/plaidypus-app/web +heroku container:release web --app plaidypus-app +``` + +### GitHub Actions (after workflow is merged to main) + +Deploys automatically on every push to `main`. Can also be triggered manually: + +```bash +gh workflow run "Deploy to Heroku" +``` + +Or use the "Run workflow" button on the Actions tab in GitHub. + +## 7. Verify + +```bash +# Check logs +heroku logs --tail --app plaidypus-auth +heroku logs --tail --app plaidypus-api +heroku logs --tail --app plaidypus-app + +# Verify endpoints +curl https://plaidypus-auth-xxxxx.herokuapp.com/.well-known/openid-configuration +curl https://plaidypus-api-xxxxx.herokuapp.com/public/health +curl -I https://plaidypus-app-xxxxx.herokuapp.com +``` + +Test the full OIDC flow by visiting `https://plaidypus-app-xxxxx.herokuapp.com` and logging in with the demo credentials (`user@example.test` / `passw0rd!`).