Add tier to CD environments

What does this MR do and why?

Add an environment tier to cd_environments to support the tiered grouping that the Continuous Deployment Beta prototype renders.

  • Add a tier smallint column on cd_environments (default 0, NOT NULL), backed by a Rails enum with development, qa, staging, and production.
  • Expose tier on the CdEnvironment GraphQL type as a new CdEnvironmentTier enum.
  • Add the tier argument to cdEnvironmentCreate (required) and cdEnvironmentUpdate (optional).

Behind the ai_native_deploy feature flag (default off).

Why the column can be NOT NULL from the start

The tier column is added as NOT NULL with DEFAULT 0 in a single regular migration. CD is still in the implementation stage: cd_environments carries no production data, and the frontend doesn't call the create or update mutations yet. With no rows to backfill, the constraint can be enforced from the start instead of going through the usual add-nullable, backfill, then validate-and-tighten flow.

Why a smallint enum and not a separate cd_environment_tiers table

Long-term, tiers should be user-defined at the organization level. The post-Beta direction is a cd_environment_tiers table with a management UI where users can add, rename, and reorder tiers. For the Beta we deliberately do not introduce that table yet:

  • A table would need the four default tiers populated for every organization that interacts with CD, or worse, every organization in GitLab. Triggering that seeding has multiple entry points (FF enable, env list/filter, env create), each adding non-trivial complexity.
  • The management UI is not in Beta scope. Without users defining their own tiers, the table would only ever hold the four hardcoded defaults per org, which the enum represents directly with no inserts.
  • The exact shape of user-defined tiers may still evolve between Beta and the post-Beta UI work. Committing to a table shape now risks running backfills twice.

When user-defined tiers ship, we introduce cd_environment_tiers, backfill the four rows for each org that actually used the enum (bounded by the Beta footprint, expected to be small), migrate cd_environments.tier (smallint) to tier_id (FK), and deprecate the GraphQL enum in favour of an object type. The cost is paid once, only for orgs that actually used CD.

Deferred to follow-up MRs

References

Screenshots or screen recordings

cdEnvironmentCreate cdEnvironmentUpdate
Screenshot_2026-06-26_at_11.37.14_am Screenshot_2026-06-26_at_11.43.30_am

How to set up and validate locally

  1. Enable the ai_native_deploy feature flag:

  2. Create an environment with a tier:

mutation {
  cdEnvironmentCreate(input: {
    organizationId: "gid://gitlab/Organizations::Organization/1",
    name: "Production",
    description: "Production Environment",
    tier: PRODUCTION
  }) {
    environment { id name tier }
    errors
  }
}
  1. Using the id returned in step 2, update the tier:
mutation {
  cdEnvironmentUpdate(input: {
    id: "<id from step 2>",
    name: "Staging",
    description: "Staging Environment",
    tier: STAGING
  }) {
    environment { id name tier }
    errors
  }
}
  1. Read the environment back to confirm the value is persisted:
query {
  organization(id: "gid://gitlab/Organizations::Organization/1") {
    cdEnvironments {
      nodes { id name tier }
    }
  }
}

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Agnes Slota

Merge request reports

Loading