# Red Hat Catalog Environment Promotion

LLMS index: [llms.txt](/llms.txt) | Full content: [llms-full.txt](/llms-full.txt)

---

## Environments

| Environment  | URL                                                        | Source                | Deploy trigger                    |
|--------------|------------------------------------------------------------|-----------------------|-----------------------------------|
| MR Preview   | GitLab Pages (`/mr-{IID}`)                                 | MR branch             | Auto on MR pipeline               |
| Experimental | `images.experimental.hummingbird-project.io/<branch-name>` | `experiment/*` branch | Auto on push to experiment branch |
| Staging      | `images.staging.hummingbird-project.io`                    | `main`                | Auto on merge to main             |
| Production   | `images.redhat.com`                                        | `main`                | Manual after staging deploy       |

> **Disambiguation:** The **infra experimental host** (`images.experimental.hummingbird-project.io`,
> `experiment/*` branches) is an off-main deploy sandbox. The former in-app `/experimental`
> product route was removed in HUM-2171 — use `/api` instead. See
> [Archived surfaces](../redhat-catalog/docs/archived-surfaces.md).

## Promotion Paths

### Routine: staging to production

1. MR merged to `main`
2. `redhat-catalog-visual-staging-reference` captures live visuals on current staging
3. `deploy_redhat_catalog_staging` runs automatically
4. `redhat-catalog-e2e-smoke` (smoke) and `visual_redhat_catalog_staging`
   (presentation diff) validate staging
5. **UAT sign-off** on staging — product, design, and engineering release
   approvers complete the [UAT checklist][uat-checklist] and file a record
   ([UAT program runbook][uat-program])
6. Approval owner clicks `deploy_redhat_catalog` (manual gate) after automated
   gates pass **and** UAT is approved
7. Production deploys from same commit

### Exploration: experimental to staging to production

1. Create branch `experiment/<name>` from `main` (must use exactly `experiment/`,
   not `experimental/` or other prefixes — without it, no deploy triggers)
2. Push triggers `deploy_redhat_catalog_experimental` automatically
3. Branch deploys to `images.experimental.hummingbird-project.io/<name>/`
4. Iterate on experimental host for days or weeks
5. **Light UAT** (optional spot-check on experimental URL — see
   [UAT program — experimental path][uat-program])
6. When ready: open MR from `experiment/<name>` to `main`
7. Normal MR review, merge, then routine promotion path (full UAT on staging)

### Retiring an experiment

1. Delete the `experiment/<name>` branch
2. Remove the branch prefix from the experimental S3 bucket:

    ```bash
    aws s3 rm s3://redhat-catalog-experimental-spa/<name>/ --recursive
    ```

3. Invalidate the CloudFront cache:

    ```bash
    aws cloudfront create-invalidation \
      --distribution-id <EXPERIMENTAL_DISTRIBUTION_ID> \
      --paths "/<name>/*"
    ```

## Build Configuration Per Environment

The infrastructure pipeline sets these environment variables at build time:

| Variable               | Experimental                                 | Staging                                 | Production          |
|------------------------|----------------------------------------------|-----------------------------------------|---------------------|
| `ASSET_PATH`           | `/<branch-name>/`                            | `/`                                     | `/`                 |
| `DPAL_USE_STAGING`     | `true`                                       | `true`                                  | (unset, uses prod)  |
| `TRUSTARC_DATA_DOMAIN` | `images.experimental.hummingbird-project.io` | `images.staging.hummingbird-project.io` | `images.redhat.com` |
| `CATALOG_API_BASE_URL` | (unset, uses app default)                    | (unset, uses app default)               | (unset, uses prod)  |
| `CATALOG_BASE_URL`     | (unset, uses app default)                    | (unset, uses app default)               | (unset, uses prod)  |
| `HUMMINGBIRD_API_URL`  | (unset, uses app default)                    | (unset, uses app default)               | (unset, uses prod)  |
| `JIRA_API_URL`         | Jira API URL                                 | Jira API URL                            | Jira API URL        |

## Infrastructure

### SAM Stacks

| Stack                         | Template                     | ResourcePrefix                | CatalogDomainName                             |
|-------------------------------|------------------------------|-------------------------------|-----------------------------------------------|
| `redhat-catalog-prod`         | `template.yaml`              | `redhat-catalog`              | `images.redhat.com`                           |
| `redhat-catalog-staging`      | `template.yaml`              | `redhat-catalog-staging`      | `images.staging.hummingbird-project.io`       |
| `redhat-catalog-experimental` | `template-experimental.yaml` | `redhat-catalog-experimental` | `images.experimental.hummingbird-project.io`  |

Staging uses the same `template.yaml` as production (single-site CloudFront distribution).
Experimental uses `template-experimental.yaml` which adds a CloudFront Function for
path-prefix SPA routing across multiple branches.

### DNS

ALIAS records (`images.staging/experimental.hummingbird-project.io` → CloudFront) are
created automatically by `post_deploy.sh` after each deploy.

**First-time bootstrap:** The SAM template creates an ACM certificate with DNS
validation. The ACM validation CNAME must exist in the `hummingbird-project.io`
Route 53 zone **before** the first `sam deploy`, or CloudFormation will wait
30 minutes and roll back. To bootstrap:

1. Deploy with `CatalogDomainName=""` (uses CloudFront default domain, no cert)
2. Create the ACM validation CNAME in Route 53 (get the value from the ACM console
   or `aws acm describe-certificate`)
3. Redeploy with `CatalogDomainName` set to the real domain

After the first successful deploy, no manual DNS steps are needed.

### CI/CD Variables

Set in GitLab project settings:

| Variable                      | Value                                            |
|-------------------------------|--------------------------------------------------|
| `REDHAT_CATALOG_STAGING_URL`  | `https://images.staging.hummingbird-project.io`  |

## Pipeline Flow

```text
experiment/* push:
  redhat-catalog-experimental-check
    -> deploy_redhat_catalog_experimental

MR (target main):
  redhat-catalog (build + test)
    -> redhat-catalog-browser-tests
    -> redhat-catalog-visual-mock (Playwright mock API; MR vs merge-base diff)
    -> pages (preview)

main push:
  redhat-catalog-visual-staging-reference (pre-deploy live capture)
    -> deploy_redhat_catalog_staging (auto)
      -> redhat-catalog-e2e-smoke (smoke)
      -> visual_redhat_catalog_staging (post-deploy vs pre-deploy diff)
    -> deploy_redhat_catalog (manual; needs staging deploy + smoke + visual + UAT sign-off)
```

[uat-checklist]: ../redhat-catalog/docs/uat-checklist.md
[uat-program]: redhat-catalog-uat.md

Visual regression details:
[redhat-catalog testing guide — Visual regression][testing-guide] and
[e2e/visual/README.md][visual-readme].

[testing-guide]: ../redhat-catalog/docs/testing-guide.md#visual-regression
[visual-readme]: ../redhat-catalog/e2e/visual/README.md
