Add Vue 3 rollout flag for the CI/CD catalog page
Description
Adds the vue3_migrate_ci_catalog feature flag and a vue3_migration.yml
file for the CI/CD catalog page. This opts the page into the Vue 3 migration
with Option 1. The bundler builds both a Vue 2 and a Vue 3 chunk. Rails
serves the Vue 3 chunk only when the flag is on. The flag is default-off.
The page mounts one app, GlobalCatalog, on #js-ci-cd-catalog. It reaches
135 .vue files. 31 are specific to this page, and 28 of those are in
ci/catalog. Only 6% of that tree already runs under Vue 3, so most of this
code runs on @vue/compat for the first time.
How to verify
- Check out this branch, then run
gdk restart. - Seed some catalog content, otherwise both runtimes render an identical empty page and prove nothing:
The seeder does not create releases, so every resource shows as
bundle exec rake "gitlab:seed:ci_catalog_resources[flightjs, 25]"Unreleased. To exercise the version selector and the Components tab you also need a release per project. See #616347. Feature.enable(:vue3_migrate_ci_catalog), then load/explore/catalog.
Confirm the Vue 3 bundle is really serving before comparing anything. Without this check, two identical screenshots prove nothing.
| Signal | Flag off | Flag on |
|---|---|---|
| Entry chunk in the page HTML | pages.explore.catalog.js |
pages.explore.catalog.vue3.js |
document.querySelectorAll('[data-gitlab-vue3-app]') |
empty | GlobalCatalog, CustomBreadcrumbsRoot |
| Console | no notice | [gitlab] [V] Using Vue.js 3 for GlobalCatalog[gitlab] [V] Using Vue.js 3 (with @vue/compat) for CustomBreadcrumbsRoot |
Both the main app and the breadcrumb app run under Vue 3.
Screenshots
Same GDK, same seeded data, only the feature flag differs.
Catalog list, 24 seeded resources:
| Before (Vue 2) | After (Vue 3) |
|---|---|
![]() |
![]() |
Resource detail page, showing the version selector, the include: snippet, the Inputs table and the "On this page" navigation:
| Before (Vue 2) | After (Vue 3) |
|---|---|
![]() |
![]() |
After selecting the non-latest version v1.0.0 from the version dropdown:
| Before (Vue 2) | After (Vue 3) |
|---|---|
![]() |
![]() |
The list and version-selected pairs are byte-identical. The detail pair differs only by a maximum of 1/255 on a single channel, in the empty area below the content, which is rendering noise rather than a visible change.
Manual checks
The catalog was reviewed for the patterns that @vue/compat changes. Each row
was exercised with the flag off and then on.
| Area | Why it is a risk | Result |
|---|---|---|
| Version selector | v-model and @select on the same GlCollapsibleListbox; value/input becomes modelValue/update:modelValue |
Pass. Opening, searching, and selecting v1.0.0 updated the toggle text, the version badge, the ?version= query and the include: snippet identically in both runtimes. |
| Tabs | GlTabs enumerates child vnodes; tabs are lazy and sync with query params |
Pass. Components and Readme both render. |
| Breadcrumb | A second Vue app sharing the same router instance | Pass. Updates to the resource name on navigation. |
| Components tab | Inputs table and the "On this page" sidebar | Pass. Table and sidebar render with correct values. |
| List rendering | Avatars, version badges, descriptions, tab counters, keyset pagination | Pass. 24 resources, matching tab counts. |
Not exercised in this round, and worth a look before the flag ramps past
gitlab-org: the filtered-search tokens, the directive-based tooltips, the
list skeleton loader, README GFM rendering, row click-through, and the
"Components:" name series.
Automated checks
bundle exec rspec spec/lib/gitlab/vue3_migration_files_spec.rb— 539 examples, 0 failuresnode scripts/frontend/vue3_migration_stats.mjs --ownersreports the page asrolloutundervue3_migrate_ci_catalog
Follow-ups found while verifying
- #616347 — the catalog seeder creates no releases, so a seeded GDK cannot exercise the version selector, the Components tab or the Readme tab.
- #616349 — the version selector keeps two sources of truth.
selectedVersionIdis written byv-modelbut read by nothing, so av-modelbreakage would be almost silent.





