diff --git a/ee/app/assets/javascripts/ci/merge_trains/index.js b/ee/app/assets/javascripts/ci/merge_trains/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e1507fed9be30bb2c68f449959ad5fb518b7e69b --- /dev/null +++ b/ee/app/assets/javascripts/ci/merge_trains/index.js @@ -0,0 +1,32 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import MergeTrainsApp from './merge_trains_app.vue'; + +Vue.use(VueApollo); + +const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), +}); + +export const initMergeTrainsApp = () => { + const el = document.querySelector('#js-merge-trains'); + + if (!el) { + return false; + } + + const { fullPath } = el.dataset; + + return new Vue({ + el, + name: 'MergeTrainsRoot', + apolloProvider, + provide: { + fullPath, + }, + render(createElement) { + return createElement(MergeTrainsApp); + }, + }); +}; diff --git a/ee/app/assets/javascripts/ci/merge_trains/merge_trains_app.vue b/ee/app/assets/javascripts/ci/merge_trains/merge_trains_app.vue new file mode 100644 index 0000000000000000000000000000000000000000..b3f45a05c070066667e74f95b5a7d0818f768487 --- /dev/null +++ b/ee/app/assets/javascripts/ci/merge_trains/merge_trains_app.vue @@ -0,0 +1,14 @@ +<script> +export default { + name: 'MergeTrainsApp', + inject: { + fullPath: { + default: '', + }, + }, +}; +</script> + +<template> + <div></div> +</template> diff --git a/ee/app/assets/javascripts/pages/projects/merge_trains/index.js b/ee/app/assets/javascripts/pages/projects/merge_trains/index.js new file mode 100644 index 0000000000000000000000000000000000000000..fd16debc3c81149254ab9aef626e6b96fe48e3fc --- /dev/null +++ b/ee/app/assets/javascripts/pages/projects/merge_trains/index.js @@ -0,0 +1,3 @@ +import { initMergeTrainsApp } from 'ee/ci/merge_trains/index'; + +initMergeTrainsApp(); diff --git a/ee/app/controllers/projects/merge_trains_controller.rb b/ee/app/controllers/projects/merge_trains_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..353f3a8ab6b5babb7402ce884e21e2872dec67bd --- /dev/null +++ b/ee/app/controllers/projects/merge_trains_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Projects + class MergeTrainsController < Projects::ApplicationController + feature_category :merge_trains + + before_action :authorize_read_merge_train! + before_action :check_enabled! + + def index; end + + private + + def check_enabled! + render_404 unless Feature.enabled?(:merge_trains_viz, project) + end + end +end diff --git a/ee/app/views/projects/merge_trains/index.html.haml b/ee/app/views/projects/merge_trains/index.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..1d6cdc7a78eea8c6d5f728122acf6c705885e6d0 --- /dev/null +++ b/ee/app/views/projects/merge_trains/index.html.haml @@ -0,0 +1,3 @@ +- page_title _('Merge trains') + +#js-merge-trains{ data: { full_path: @project.full_path } } diff --git a/ee/config/feature_flags/wip/merge_trains_viz.yml b/ee/config/feature_flags/wip/merge_trains_viz.yml new file mode 100644 index 0000000000000000000000000000000000000000..b9b9e01aad31e64cea8a1335751e172ff37091a1 --- /dev/null +++ b/ee/config/feature_flags/wip/merge_trains_viz.yml @@ -0,0 +1,9 @@ +--- +name: merge_trains_viz +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/454179 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149025 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/455342 +milestone: '16.11' +group: group::pipeline execution +type: wip +default_enabled: false diff --git a/ee/config/routes/project.rb b/ee/config/routes/project.rb index 91c74bba79314ce589057cde34023ae3cea5e843..10dfb68c95539c3eb9d15420cfbb8098e2319f65 100644 --- a/ee/config/routes/project.rb +++ b/ee/config/routes/project.rb @@ -159,6 +159,8 @@ end resources :compliance_frameworks, only: [:create] + + resources :merge_trains, only: [:index] end # End of the /-/ scope. diff --git a/ee/spec/frontend/ci/merge_trains/merge_trains_app_spec.js b/ee/spec/frontend/ci/merge_trains/merge_trains_app_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..80234e97b30eae6216eef63e06fd13be4f0b7ca4 --- /dev/null +++ b/ee/spec/frontend/ci/merge_trains/merge_trains_app_spec.js @@ -0,0 +1,16 @@ +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import MergeTrainsApp from 'ee/ci/merge_trains/merge_trains_app.vue'; + +describe('MergeTrainsApp', () => { + let wrapper; + + const createComponent = () => { + wrapper = shallowMountExtended(MergeTrainsApp, { provide: { fullPath: 'namespace/project' } }); + }; + + it('renders the merge trains app', () => { + createComponent(); + + expect(wrapper.findComponent(MergeTrainsApp).exists()).toBe(true); + }); +}); diff --git a/ee/spec/requests/projects/merge_trains_controller_spec.rb b/ee/spec/requests/projects/merge_trains_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..ef15f3f71ea2a0a9e36ef5fdb23cb99a12115e75 --- /dev/null +++ b/ee/spec/requests/projects/merge_trains_controller_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::MergeTrainsController, type: :request, feature_category: :merge_trains do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + + describe 'GET /:namespace/:project/-/merge_trains' do + subject(:request) { get project_merge_trains_url(project) } + + before_all do + project.add_maintainer(user) + end + + before do + sign_in(user) + end + + context 'when feature flag "merge_trains_viz" is enabled' do + it 'renders the merge trains index template' do + request + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template('projects/merge_trains/index') + end + end + + context 'when feature flag "merge_trains_viz" is disabled' do + before do + stub_feature_flags(merge_trains_viz: false) + end + + it 'returns "not found response"' do + request + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end +end