Skip to content

Frontend Pipeline — Staff Engineer Overview

Technical overview of the proposed Arda frontend deployment pipeline for staff-level review. Focuses on steady-state architecture, deployment mechanics, and the IAM/OIDC trust model. For implementation sequencing, see the Development Blueprint.

The current frontend deploys through Amplify branch-sync tied to a dev → stage → main promotion model. Each promotion is a separate PR with review, CI, and approval — tripling the release cycle time. The backend already deploys from main via GitHub Actions with sequential environment gates. The frontend should follow the same pattern.

After cutover, all Amplify apps point at main with auto-build disabled. The pipeline supports two complementary workflows: PR previews for fast developer feedback during development, and production deployments for the official release path from main.

Amplify remains the build and hosting platform — it resolves environment variables, runs unit tests and npm run build, and serves the SSR application. GitHub Actions owns the production deployment orchestration (StartJob + GetJob). PR previews are Amplify-managed via the built-in pull request preview feature.

Developer opens PR against main
┌─────────────────┴───────────────────┐
▼ ▼
Amplify PR Preview GitHub Actions CI
(Amplify-managed webhook) ci.yaml (lint, build, test, e2e)
│ │
▼ │
Amplify builds PR branch │
(npm ci → npm test → npm run build) │
│ │
▼ │
Preview URL deployed │
(pr-{N}.{appId}.amplifyapp.com) │
● Same backend as dev partition │
● Redeploys on each push │
● Deleted on PR close/merge │
PR merges to main ◄──────────────┘
│ (CI must pass to merge)
deploy.yaml (workflow_run trigger)
├── matrix: [dev, stage, demo, prod] max-parallel: 1
│ For each partition:
│ 1. Fetch purpose-configuration → derive frontend role ARN
│ 2. OIDC AssumeRole → temporary AWS credentials
│ 3. Fetch Amplify App ID + Branch Name from CloudFormation exports
│ 4. aws amplify start-job --commit-id {sha}
│ 5. Poll aws amplify get-job until SUCCEED | FAILED
│ Environment gates:
│ dev → no gate
│ stage → required_reviewers
│ demo → no gate
│ prod → required_reviewers
└── Operator notified of result
redeploy.yaml (workflow_dispatch)
└── Deploy a specific SHA to a single partition
(with CI-pass verification before StartJob)

Two quality gates, two owners:

GateOwnerScopeWhat it protects
Unit tests in Amplify build specAmplifyPR previews + official deploymentsPrevents broken previews from deploying
Full CI (lint, build, test, e2e)GitHub ActionsMerge to mainPrevents broken code from reaching the production pipeline

Amplify’s StartDeployment (zip upload) does not work for SSR apps (WEB_COMPUTE platform). This is a known AWS limitation. StartJob with jobType: RELEASE is the only API path — it triggers Amplify’s own build from the connected branch. The commitId parameter pins the exact commit. See the Design Analysis — Approaches Considered for the full evaluation.

What Amplify Owns vs. What GitHub Actions Owns

Section titled “What Amplify Owns vs. What GitHub Actions Owns”
ConcernOwner
Build execution (npm ci, npm test, npm run build)Amplify
Environment variable resolution (Secrets Manager, CloudFormation exports)Amplify
SSR hosting and CDNAmplify
PR preview lifecycle (build, deploy, cleanup)Amplify (webhook-driven)
Production deployment orchestration (when, what order, which commit)GitHub Actions
Authorization gates (stage/prod approval)GitHub Environments
Full CI gating (lint, build, test, e2e → merge to main)GitHub Actions (workflow_run / commit status API)

This split is the key architectural property: no secrets or environment variables flow through GitHub Actions. The workflow has only Amplify API permissions and cloudformation:ListExports (to read deployment parameters). PR previews are entirely Amplify-managed — GitHub Actions is not involved.

Three workflow files, mirroring the backend (operations) pattern:

WorkflowTriggerPurpose
deploy.yamlworkflow_run on CI success (+ manual fallback)Sequential deployment to all partitions
redeploy.yamlworkflow_dispatch (partition + SHA)Targeted single-partition deployment or rollback
reusable_deployment.yamlCalled by the above twoShared logic: config fetch → OIDC → StartJob → poll

The reusable workflow accepts partition and optional commit_sha as inputs. Each caller sets environment: <partition> to activate GitHub Environment protection rules.

deploy.yaml uses a workflow_run trigger — it only fires when ci.yaml succeeds on main. redeploy.yaml checks the commit status API before deploying; if CI never ran for that SHA, it runs CI inline and aborts on failure. There is no path to production without passing lint, build, and unit tests.

A dedicated IAM role is created per AWS account via the existing GhOidcProvider CDK construct:

PropertyValue
Role name${prefix}-API-GitHubActionFrontEnd
OIDC subjectrepo:Arda-cards/arda-frontend-app
Branch scoperefs/heads/main, refs/heads/patch, refs/heads/demo (demo removed after cutover)
Permissionsamplify:StartJob, amplify:GetJob, amplify:GetApp, amplify:GetBranch, cloudformation:ListExports
Resource scopearn:aws:amplify:${region}:${account}:apps/*

This role has no access to Secrets Manager, S3, IAM, or any other service. Its only permissions are Amplify operations and cloudformation:ListExports (to read Amplify App ID and Branch Name from CloudFormation exports). The existing Infrastructure, Management, and Service roles are unchanged.

The role is deployed to both accounts:

  • Alpha001 (009765408297) — serves demo and prod partitions
  • Alpha002 (139852620346) — serves dev and stage partitions

See the Design Analysis — IAM and OIDC for the full role inventory.

Multi-Account Routing — Derived from Existing Configuration

Section titled “Multi-Account Routing — Derived from Existing Configuration”

The frontend workflow requires no new properties in purpose-configuration. All deployment parameters are derived from existing configuration and AWS state, following the same pattern used by the backend (operations, accounts-component):

  1. Frontend IAM role: Derived from the existing aws_role property by appending FrontEnd to the role name. For example, arn:aws:iam::009765408297:role/Alpha001-API-GitHubAction becomes arn:aws:iam::009765408297:role/Alpha001-API-GitHubActionFrontEnd.
  2. Infrastructure prefix: Parsed from the aws_role role name (e.g., Alpha001 from Alpha001-API-GitHubAction). Used to construct CloudFormation export names.
  3. Amplify App ID: Read from the CloudFormation export ${Infrastructure}-${Partition}-I-AmplifyAppId. For the demo app (created via CloudFormation), this export is created automatically. For existing manually-created apps (dev, stage, prod), a lightweight CloudFormation stack creates the export.
  4. Amplify Branch Name: Read from the CloudFormation export ${Infrastructure}-${Partition}-I-AmplifyBranchName. Same mechanism as above.

The Amplify branch resource name is not the same as the partition name in all cases. The existing prod app’s branch resource is named main (not prod) — a legacy of the original branch-sync model. Rather than special-casing this, amm.sh maintains a uniform partition-to-branch-name mapping for all partitions, and the branch name is published as a CloudFormation export so the workflow has a single, consistent lookup mechanism. See Amplify Current Configuration for the live state of all apps.

Each partition has its own Amplify app with partition-specific environment variables resolved from CloudFormation exports and Secrets Manager at build time:

PartitionAccountAmplify Branch ResourceGit Branch (after cutover)URL
devAlpha002 (139852620346)devmaindev.alpha002.app.arda.cards
stageAlpha002 (139852620346)stagemainstage.alpha002.app.arda.cards
demoAlpha001 (009765408297)demomaindemo.app.arda.cards
prodAlpha001 (009765408297)mainmainlive.app.arda.cards

All four apps build the same commit (via --commit-id {sha}), but each resolves its own Cognito pools, API endpoints, and secrets. The Amplify App Analysis documents how environment variables are provisioned via CloudFormation.

Amplify’s built-in pull request preview feature is enabled on the dev Amplify app to provide fast developer feedback. When a PR is opened against main, Amplify automatically builds the PR branch and deploys it to a temporary preview URL (e.g., pr-42.d38w5m1ngjza76.amplifyapp.com). The preview:

  • Inherits the dev app’s environment variables — same API Gateway, Cognito pools, and database
  • Automatically redeploys on every push to the PR branch (same URL)
  • Is deleted when the PR is closed or merged
  • Amplify posts the preview URL as a comment on the GitHub PR

PR previews are Amplify-managed and build independently of GitHub Actions CI — they trigger immediately on PR open/push via a GitHub webhook. The Amplify build spec includes npm run test (Jest unit tests) as a quality gate: if tests fail, the build fails and the preview is not deployed. Lint and e2e tests remain GitHub Actions-only and gate the merge to main, not the preview.

Once the PR merges to main, the official pipeline takes over: dev → stage → demo → prod with CI gates and reviewer approval.

Two rollback mechanisms, no artifact storage required:

  1. Redeploy a previous SHA: redeploy.yaml with a known-good commit SHA. Amplify pulls the commit from git and rebuilds. Execution time equals a normal build (~3-5 min).
  2. Re-enable auto-build: Emergency fallback. Run aws amplify update-branch --enable-auto-build on the affected app. The next push to the connected branch triggers an Amplify-managed build, bypassing GitHub Actions entirely.

The pipeline is developed and validated on an isolated demo partition before touching any existing Amplify app. The demo Amplify app is created from the same CloudFormation templates used for all other apps, connected to a demo branch with auto-build disabled. Once validated, existing apps are migrated incrementally (dev → stage → prod), each verified before proceeding.

The full five-step sequence — demo setup, IAM role, workflow development, validation, and production cutover — is detailed in the Development Blueprint. The Design Analysis covers the decision record, rejected approaches, and artifact inventory.

DecisionRationale
StartJob (not StartDeployment)StartDeployment doesn’t support SSR/WEB_COMPUTE apps
Dedicated IAM role (not extending Service role)Minimal permissions; scoped to arda-frontend-app repo only
Amplify branch resources keep original namesAvoids recreating Amplify resources; StartJob addresses the resource name
Sequential matrix (max-parallel: 1)Matches backend pattern; catches issues before they reach prod
Frontend role derived from aws_role (no new purpose-config properties)Consistent with backend pattern; all Amplify parameters from CloudFormation exports
Reusable workflows only (no composite actions)StartJob + poll is simple enough; no need for the extra abstraction
Additive development on demo branchZero risk to existing pipeline until cutover
Amplify PR previews on dev app (not custom workflow)Built-in feature; no workflow changes; automatic cleanup on PR close