Fix frontend URLs so they support Organization scoped URLs
## Problem statement We haven't had strict guidelines around constructing URLs in GitLab and as a result we have a number of instances where we construct/hardcode URLs on the frontend. Many of these URLs will not support [organization scoped routes](https://docs.gitlab.com/development/organization/#organization-routing) and therefore will be incorrect when we enable organization scoped routes. We need to use Rails as the SSOT for generating URLs and fix these violations of hardcoded URLs on the frontend. We have developer docs at https://docs.gitlab.com/development/urls_in_gitlab/ ## DRI @peterhegman ## Exit criteria - [ ] `.eslint_todo/gitlab-no-hardcoded-urls.mjs` is empty. This means the violations have been fixed or explicitly disabled with a documented reason - [ ] `.eslint_todo/gitlab-vue-no-hardcoded-urls.mjs` is empty. This means the violations have been fixed or explicitly disabled with a documented reason ### Violations that need to be fixed These will be moved into individual issues <details> <summary>Click to expand</summary> ```json { "Code Review": [ "app/assets/javascripts/add_context_commits_modal/store/actions.js", "app/assets/javascripts/batch_comments/components/draft_note.vue", "app/assets/javascripts/notes/components/comment_form.vue", "app/assets/javascripts/notes/components/noteable_discussion.vue", "app/assets/javascripts/notes/components/sidebar_subscription.vue", "app/assets/javascripts/notes/mixins/resolvable.js", "app/assets/javascripts/vue_merge_request_widget/components/checks/title_regex.vue", "app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue", "app/assets/javascripts/vue_merge_request_widget/widgets/test_report/index.vue", "ee/app/assets/javascripts/approvals/components/approval_settings/approval_settings.vue", "ee/app/assets/javascripts/notes/components/note_actions/ai_summarize_notes.vue", "ee/app/assets/javascripts/vue_merge_request_widget/widgets/security_reports/mr_widget_security_reports.vue" ], "Source Code": [ "app/assets/javascripts/blob/openapi/index.js", "app/assets/javascripts/content_editor/components/wrappers/code_block.vue", "app/assets/javascripts/content_editor/constants/index.js", "app/assets/javascripts/content_editor/extensions/code_suggestion.js", "app/assets/javascripts/content_editor/services/asset_resolver.js", "app/assets/javascripts/ide/constants.js", "app/assets/javascripts/ide/lib/gitlab_web_ide/get_gitlab_url.js", "app/assets/javascripts/ide/lib/gitlab_web_ide/get_web_ide_workbench_config.js", "app/assets/javascripts/ide/lib/languages/hcl.js", "app/assets/javascripts/repository/commits_service.js", "app/assets/javascripts/repository/components/header_area/breadcrumbs.vue", "app/assets/javascripts/repository/components/table/parent_row.vue", "app/assets/javascripts/repository/components/table/row.vue", "app/assets/javascripts/repository/file_tree_browser/components/tree_list.vue", "app/assets/javascripts/repository/log_tree.js", "app/assets/javascripts/repository/pages/blob_edit_header.vue", "app/assets/javascripts/repository/router.js", "app/assets/javascripts/repository/utils/ref_switcher_utils.js", "app/assets/javascripts/projects/commits/mock_commits.js", "app/assets/javascripts/projects/commits/store/actions.js", "app/assets/javascripts/projects/commit_box/info/components/commit_refs.vue", "app/assets/javascripts/snippets/components/edit.vue", "app/assets/javascripts/snippets/components/snippet_header.vue", "app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue", "app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/go_sum_linker.js", "app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/godeps_json_linker.js" ], "Pipeline Execution": [ "app/assets/javascripts/ci/pipeline_details/pipeline_details_bundle.js", "app/assets/javascripts/ci/pipeline_details/utils/index.js", "app/assets/javascripts/ci/pipeline_mini_graph/downstream_pipeline_dropdown.vue", "app/assets/javascripts/ci/pipeline_mini_graph/pipeline_stage_dropdown.vue", "app/assets/javascripts/ci/pipelines_page/components/empty_state/ci_cards.vue", "ee/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue" ], "Runner": [ "app/assets/javascripts/ci/runner/components/registration/utils.js", "ee/app/assets/javascripts/ci/runner/constants.js", "ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/scan_filters/runner_tags_list.vue" ], "Project Management": [ "app/assets/javascripts/boards/components/issue_board_filtered_search.vue", "app/assets/javascripts/boards/index.js", "app/assets/javascripts/issues/show/services/index.js", "app/assets/javascripts/glql/components/presenters/label.vue", "app/assets/javascripts/glql/utils/common.js", "app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue", "app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue", "app/assets/javascripts/sidebar/components/time_tracking/help_state.vue", "app/assets/javascripts/todos/components/filtered_search_tokens/author_token.vue", "app/assets/javascripts/work_items/components/design_management/design_item.vue", "app/assets/javascripts/work_items/components/design_management/design_management_widget.vue", "app/assets/javascripts/work_items/components/design_management/design_preview/design_description.vue", "app/assets/javascripts/work_items/components/design_management/design_preview/design_details.vue", "app/assets/javascripts/work_items/components/work_item_actions.vue", "app/assets/javascripts/work_items/components/work_item_development/work_item_create_branch_merge_request_modal.vue", "app/assets/javascripts/work_items/graphql/cache_utils.js", "app/assets/javascripts/work_items/pages/create_work_item.vue", "app/assets/javascripts/work_items/router/index.js", "app/assets/javascripts/work_items/utils.js", "app/assets/javascripts/work_items/components/work_item_attributes_wrapper.vue", "ee/app/assets/javascripts/epic_boards/index.js", "ee/app/assets/javascripts/iterations/components/iteration_form.vue", "ee/app/assets/javascripts/roadmap/mixins/filtered_search_mixin.js" ], "Optimize": [ "app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue", "app/assets/javascripts/analytics/cycle_analytics/constants.js", "app/assets/javascripts/analytics/shared/constants.js", "app/assets/javascripts/analytics/shared/utils.js", "ee/app/assets/javascripts/analytics/analytics_dashboards/components/visualizations/data_table/metric_label.vue", "ee/app/assets/javascripts/analytics/analytics_dashboards/components/visualizations/utils.js", "ee/app/assets/javascripts/analytics/analytics_dashboards/data_sources/cube_analytics.js", "ee/app/assets/javascripts/analytics/dashboards/components/stories_constants.js", "ee/app/assets/javascripts/analytics/dashboards/constants.js", "ee/app/assets/javascripts/analytics/devops_reports/devops_adoption/constants.js", "ee/app/assets/javascripts/analytics/devops_reports/devops_adoption/utils/helpers.js", "ee/app/assets/javascripts/analytics/productivity_analytics/utils.js", "ee/app/assets/javascripts/analytics/repository_analytics/components/test_coverage_table.vue", "ee/app/assets/javascripts/insights/components/insights_chart.vue", "ee/app/assets/javascripts/insights/constants.js", "ee/app/assets/javascripts/insights/insights_router.js", "ee/app/assets/javascripts/insights/components/insights_page.vue", "ee/app/assets/javascripts/product_analytics/onboarding/constants.js", "ee/app/assets/javascripts/project_quality_summary/app.vue" ], "Monitor": [ "app/assets/javascripts/alerts_settings/constants.js", "app/assets/javascripts/error_tracking/components/error_details_info.vue", "app/assets/javascripts/error_tracking/components/error_tracking_actions.vue", "app/assets/javascripts/error_tracking/components/error_tracking_list.vue", "app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue", "app/assets/javascripts/vue_shared/alert_details/router.js", "ee/app/assets/javascripts/sidebar/components/incidents/sidebar_escalation_policy.vue" ], "Deploy": [ "app/assets/javascripts/environments/components/environment_flux_resource_selector.vue", "app/assets/javascripts/environments/environment_details/components/kubernetes/pod_logs_button.vue", "app/assets/javascripts/environments/graphql/resolvers/flux.js", "app/assets/javascripts/environments/graphql/resolvers/kubernetes/k8s_logs.js", "app/assets/javascripts/environments/helpers/deployment_data_transformation_helper.js", "app/assets/javascripts/environments/helpers/k8s_integration_helper.js", "app/assets/javascripts/kubernetes_dashboard/graphql/helpers/resolver_helpers.js", "app/assets/javascripts/kubernetes_dashboard/router/constants.js", "app/assets/javascripts/releases/components/ci_cd_catalog_wrapper.vue", "app/assets/javascripts/releases/stores/modules/edit_new/getters.js", "app/assets/javascripts/feature_flags/components/feature_flags.vue" ], "Package Registry": [ "app/assets/javascripts/packages_and_registries/shared/utils.js" ], "GitLab Pages": [ "app/assets/javascripts/gitlab_pages/components/deployment.vue", "app/assets/javascripts/gitlab_pages/components/live_block.vue" ], "Wiki": [ "app/assets/javascripts/wikis/components/wiki_template.vue", "app/assets/javascripts/wikis/utils.js", "app/assets/javascripts/wikis/wiki_notes/components/wiki_comment_form.vue" ], "Global Search": [ "app/assets/javascripts/search/sidebar/components/author_filter/index.vue", "app/assets/javascripts/search/sidebar/components/source_branch_filter/index.vue", "app/assets/javascripts/search/sidebar/components/target_branch_filter/index.vue", "app/assets/javascripts/search/sidebar/constants/index.js", "app/assets/javascripts/search/store/utils.js", "app/assets/javascripts/super_sidebar/components/global_search/components/frequent_items.vue" ], "Import and Integrate": [ "app/assets/javascripts/import_entities/import_groups/components/import_source_cell.vue", "app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue", "ee/app/assets/javascripts/integrations/edit/components/google_artifact_management/configuration_instructions.vue" ], "Organization": [ "app/assets/javascripts/groups/settings/api/access_dropdown_api.js", "app/assets/javascripts/groups/show/index.js", "app/assets/javascripts/projects/project_new.js", "app/assets/javascripts/projects/projects_filterable_list.js", "app/assets/javascripts/projects/settings/api/access_dropdown_api.js", "app/assets/javascripts/projects/your_work/constants.js", "app/assets/javascripts/organizations/settings/general/components/change_url.vue", "app/assets/javascripts/vue_shared/components/groups_list/formatter.js", "app/assets/javascripts/vue_shared/components/nested_groups_projects_list/mock_data.js", "app/assets/javascripts/vue_shared/components/projects_list/formatter.js", "app/assets/javascripts/vue_shared/components/projects_list/projects_list_item.vue", "app/assets/javascripts/vue_shared/components/topic_badges.vue" ], "Authentication and Authorization": [ "app/assets/javascripts/members/components/action_buttons/approve_access_request_button.vue", "app/assets/javascripts/members/components/action_buttons/remove_group_link_button.vue", "app/assets/javascripts/members/components/action_buttons/remove_member_button.vue", "app/assets/javascripts/members/components/action_buttons/resend_invite_button.vue", "app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue", "app/assets/javascripts/members/components/modals/remove_group_link_modal.vue", "app/assets/javascripts/members/components/modals/remove_member_modal.vue", "app/assets/javascripts/members/components/table/expiration_datepicker.vue", "app/assets/javascripts/members/components/table/max_role.vue", "app/assets/javascripts/pages/sessions/new/username_validator.js", "app/assets/javascripts/users_select/index.js", "app/assets/javascripts/vue_shared/components/list_selector/api.js", "ee/app/assets/javascripts/members/components/action_buttons/banned_action_buttons.vue", "ee/app/assets/javascripts/members/components/action_dropdowns/ban_member_dropdown_item.vue", "ee/app/assets/javascripts/members/components/action_dropdowns/disable_two_factor_dropdown_item.vue", "ee/app/assets/javascripts/members/components/action_dropdowns/ldap_dropdown_footer.vue", "ee/app/assets/javascripts/members/components/action_dropdowns/ldap_override_dropdown_item.vue", "ee/app/assets/javascripts/members/components/modals/disable_two_factor_modal.vue", "ee/app/assets/javascripts/members/components/modals/ldap_override_confirmation_modal.vue", "ee/app/assets/javascripts/members/components/table/drawer/role_updater.vue", "ee/app/assets/javascripts/saml_providers/scim_token_service.js", "ee/app/assets/javascripts/arkose_labs/init_arkose_labs.js" ], "Threat Insights": [ "ee/app/assets/javascripts/security_dashboard/components/agent/agent_vulnerability_report.vue", "ee/app/assets/javascripts/security_dashboard/components/agent/constants.js", "ee/app/assets/javascripts/security_dashboard/components/shared/charts/risk_score_by_project.vue", "ee/app/assets/javascripts/security_inventory/constants.js", "ee/app/assets/javascripts/vulnerabilities/components/header.vue", "ee/app/assets/javascripts/vue_shared/security_reports/utils.js", "ee/app/assets/javascripts/sidebar/components/cve_id_request/cve_id_request.vue" ], "Security Policies": [ "ee/app/assets/javascripts/security_orchestration/components/policies/list_header.vue", "ee/app/assets/javascripts/security_orchestration/components/policy_drawer/pipeline_execution/details_drawer.vue", "ee/app/assets/javascripts/security_orchestration/components/policy_editor/utils.js", "ee/app/assets/javascripts/security_configuration/graphql/resolvers.js", "ee/app/assets/javascripts/security_configuration/secret_detection/components/experiment_header.vue" ], "Compliance": [ "ee/app/assets/javascripts/compliance_dashboard/components/dashboard/frameworks_needs_attention.vue", "ee/app/assets/javascripts/compliance_dashboard/components/frameworks_report/framework_info_drawer.vue", "ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/components/details_drawer/statuses_info.js", "ee/app/assets/javascripts/compliance_dashboard/components/violations_report/report_v2.vue", "ee/app/assets/javascripts/compliance_dashboard/router.js", "ee/app/assets/javascripts/compliance_violations/components/fix_suggestion_section.vue", "ee/app/assets/javascripts/compliance_dashboard/components/standards_adherence_report/standards_adherence_table_v2.vue" ], "Composition Analysis": [ "ee/app/assets/javascripts/dependencies/components/dependency_project_count.vue" ], "Dynamic Analysis": [ "ee/app/assets/javascripts/on_demand_scans/router.js" ], "Geo": [ "ee/app/assets/javascripts/geo_sites/components/details/secondary_site/geo_site_replication_details.vue" ], "Fulfillment": [ "ee/app/assets/javascripts/admin/subscriptions/show/components/subscription_activation_errors.vue", "ee/app/assets/javascripts/admin/subscriptions/show/components/subscription_details_user_info.vue", "ee/app/assets/javascripts/admin/subscriptions/show/components/subscription_activation_form.vue", "app/assets/javascripts/usage_quotas/storage/namespace/components/project_list.vue", "ee/app/assets/javascripts/usage_quotas/pages/components/project.vue" ], "AI-powered": [ "ee/app/assets/javascripts/ai/catalog/router/constants.js", "ee/app/assets/javascripts/ai/components/ai_panel.vue", "ee/app/assets/javascripts/ai/components/duo_workflow_action.vue", "ee/app/assets/javascripts/ai/constants.js", "ee/app/assets/javascripts/ai/duo_agentic_chat/components/app.vue", "ee/app/assets/javascripts/ai/duo_agentic_chat/components/duo_agentic_chat.vue", "ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/components/agent_flow_info.vue", "ee/app/assets/javascripts/ai/duo_agents_platform/pages/show/duo_agents_platform_show.vue", "ee/app/assets/javascripts/ai/duo_self_hosted/self_hosted_models/components/self_hosted_models_table.vue", "ee/app/assets/javascripts/ai/settings/components/ai_models_form.vue", "ee/app/assets/javascripts/ai/settings/components/duo_core_features_form.vue", "ee/app/assets/javascripts/ai/settings/components/duo_experiment_beta_features_form.vue", "ee/app/assets/javascripts/ai/tanuki_bot/components/app.vue", "ee/app/assets/javascripts/ai/tanuki_bot/components/duo_chat.vue", "ee/app/assets/javascripts/pages/admin/gitlab_duo/self_hosted/router.js" ], "Foundations": [ "app/assets/javascripts/api.js", "app/assets/javascripts/api/api_utils.js", "app/assets/javascripts/autosave.js", "app/assets/javascripts/badges/store/actions.js", "app/assets/javascripts/behaviors/markdown/render_sandboxed_mermaid.js", "app/assets/javascripts/behaviors/shortcuts/shortcuts_help.vue", "app/assets/javascripts/custom_emoji/components/list.vue", "app/assets/javascripts/emoji/awards_app/store/actions.js", "app/assets/javascripts/emoji/index.js", "app/assets/javascripts/filtered_search/available_dropdown_mappings.js", "app/assets/javascripts/filtered_search/dropdown_emoji.js", "app/assets/javascripts/filtered_search/dropdown_user.js", "app/assets/javascripts/gfm_auto_complete.js", "app/assets/javascripts/helpers/help_page_helper.js", "app/assets/javascripts/homepage/components/activity_widget.vue", "app/assets/javascripts/homepage/components/todos_widget.vue", "app/assets/javascripts/lazy_loader.js", "app/assets/javascripts/lib/graphql.js", "app/assets/javascripts/lib/swagger.js", "app/assets/javascripts/lib/utils/autosave.js", "app/assets/javascripts/lib/utils/common_utils.js", "app/assets/javascripts/lib/utils/cookies.js", "app/assets/javascripts/lib/utils/text_utility.js", "app/assets/javascripts/lib/utils/url_utility.js", "app/assets/javascripts/lib/utils/webpack.js", "app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/candidate_detail.vue", "app/assets/javascripts/network/raphael.js", "app/assets/javascripts/pages/projects/graphs/charts/index.js", "app/assets/javascripts/pdf/index.vue", "app/assets/javascripts/performance_bar/stores/performance_bar_store.js", "app/assets/javascripts/profile/components/snippets/snippet_row.vue", "app/assets/javascripts/super_sidebar/components/promo_menu.vue", "app/assets/javascripts/super_sidebar/utils.js", "app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue", "app/assets/javascripts/vue_shared/components/file_row.vue", "app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js", "app/assets/javascripts/whats_new/store/actions.js", "ee/app/assets/javascripts/api.js", "ee/app/assets/javascripts/api/analytics_api.js", "ee/app/assets/javascripts/api/groups_api.js", "ee/app/assets/javascripts/api/users_api.js", "ee/app/assets/javascripts/api/virtual_registries_api.js", "ee/app/assets/javascripts/gfm_auto_complete.js" ], "Configure": [ "app/assets/javascripts/google_cloud/databases/cloudsql/instance_table.vue" ], "@slashmanov": [ "app/assets/javascripts/batch_comments/components/draft_note.vue" ], "@iamphill": [ "app/assets/javascripts/batch_comments/components/draft_note.vue" ], "@thomasrandolph": [ "app/assets/javascripts/batch_comments/components/draft_note.vue" ], "@gitlab-org/ci-cd/verify/frontend": [ "app/assets/javascripts/ci/pipeline_details/pipeline_details_bundle.js", "app/assets/javascripts/ci/pipeline_details/utils/index.js", "app/assets/javascripts/ci/pipeline_mini_graph/downstream_pipeline_dropdown.vue", "app/assets/javascripts/ci/pipeline_mini_graph/pipeline_stage_dropdown.vue", "app/assets/javascripts/ci/pipelines_page/components/empty_state/ci_cards.vue", "app/assets/javascripts/ci/runner/components/registration/utils.js", "ee/app/assets/javascripts/ci/job_details/components/root_cause_analysis_button.vue", "ee/app/assets/javascripts/ci/runner/constants.js" ], "@gitlab-com/gl-security/appsec": [ "app/assets/javascripts/gfm_auto_complete.js", "ee/app/assets/javascripts/gfm_auto_complete.js" ], "@gitlab-org/fulfillment/utilization/fe": [ "app/assets/javascripts/usage_quotas/storage/namespace/components/project_list.vue", "ee/app/assets/javascripts/usage_quotas/pages/components/project.vue" ], "@gitlab-org/analytics-section/platform-insights/engineers/frontend": [ "ee/app/assets/javascripts/analytics/analytics_dashboards/components/visualizations/data_table/metric_label.vue", "ee/app/assets/javascripts/analytics/analytics_dashboards/components/visualizations/utils.js", "ee/app/assets/javascripts/analytics/analytics_dashboards/data_sources/cube_analytics.js", "ee/app/assets/javascripts/product_analytics/onboarding/constants.js" ], "@gitlab-org/plan-stage/optimize-group/engineers/frontend": [ "ee/app/assets/javascripts/analytics/analytics_dashboards/components/visualizations/data_table/metric_label.vue", "ee/app/assets/javascripts/analytics/analytics_dashboards/components/visualizations/utils.js", "ee/app/assets/javascripts/analytics/analytics_dashboards/data_sources/cube_analytics.js" ], "@gitlab-org/security-risk-management/security-insights/frontend": [ "ee/app/assets/javascripts/dependencies/components/dependency_project_count.vue", "ee/app/assets/javascripts/security_dashboard/components/agent/agent_vulnerability_report.vue", "ee/app/assets/javascripts/security_dashboard/components/agent/constants.js", "ee/app/assets/javascripts/security_dashboard/components/shared/charts/risk_score_by_project.vue", "ee/app/assets/javascripts/vue_merge_request_widget/widgets/security_reports/mr_widget_security_reports.vue", "ee/app/assets/javascripts/vulnerabilities/components/header.vue" ], "@zcuddy": [ "ee/app/assets/javascripts/geo_sites/components/details/secondary_site/geo_site_replication_details.vue" ], "@gitlab-org/software-supply-chain-security/authentication/approvers": [ "ee/app/assets/javascripts/members/components/action_dropdowns/ldap_dropdown_footer.vue", "ee/app/assets/javascripts/members/components/action_dropdowns/ldap_override_dropdown_item.vue", "ee/app/assets/javascripts/members/components/modals/ldap_override_confirmation_modal.vue" ], "@gitlab-org/security-risk-management/security-policies/frontend": [ "ee/app/assets/javascripts/security_orchestration/components/policies/list_header.vue", "ee/app/assets/javascripts/security_orchestration/components/policy_drawer/pipeline_execution/details_drawer.vue", "ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/scan_filters/runner_tags_list.vue", "ee/app/assets/javascripts/security_orchestration/components/policy_editor/utils.js" ] } ``` </details> ## Development Log - Docs added in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/215122 - ESLint rule added in https://gitlab.com/gitlab-org/frontend/eslint-plugin/-/merge_requests/147 - `js-routes` gem implemented in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/228001 - MRs generated to fix `no-hardcoded-urls` ESLint rule violation using JavaScript path helpers from `js-routes` ## Status Update - Work on better solution for frontend paths in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/216184 - Start coordinating with teams to get their violations fixed ## Start date and estimated due date **Start:** Feb 13th, 2026 **End:** June 12th, 2026 <!-- STATUS NOTE START --> ## Status 2026-04-09 <!-- Create a high level summary (optional but encouraged) --> :clock1: **total hours spent this week by all contributors**: 15 :tada: **achievements**: - Cleaned up 6 MRs and assigned to group owners/reviewers to move forward with fixing frontend hardcoded URLs :arrow_forward: **next**: - Continue to cleanup MRs and assign to group owners for review _Copied from https://gitlab.com/groups/gitlab-org/-/epics/20345#note_3233697848_ <!-- STATUS NOTE END -->
epic