Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9bfdb5cf67
commit
10fd79745d
64 changed files with 872 additions and 312 deletions
|
@ -81,7 +81,7 @@ export const getTimeAxisOptions = ({ timezone = timezones.LOCAL } = {}) => ({
|
|||
formatter: date => formatDate(date, { format: formats.shortTime, timezone }),
|
||||
},
|
||||
axisPointer: {
|
||||
snap: true,
|
||||
snap: false,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -85,7 +85,8 @@ export default {
|
|||
},
|
||||
defaultBranch: {
|
||||
type: String,
|
||||
required: true,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
emptyGettingStartedSvgPath: {
|
||||
type: String,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Vue from 'vue';
|
||||
import { GlToast } from '@gitlab/ui';
|
||||
import Dashboard from '~/monitoring/components/dashboard.vue';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { getParameterValues } from '~/lib/utils/url_utility';
|
||||
import { createStore } from './stores';
|
||||
import createRouter from './router';
|
||||
|
||||
Vue.use(GlToast);
|
||||
|
||||
|
@ -21,6 +21,7 @@ export default (props = {}) => {
|
|||
logsPath,
|
||||
currentEnvironmentName,
|
||||
dashboardTimezone,
|
||||
metricsDashboardBasePath,
|
||||
...dataProps
|
||||
} = el.dataset;
|
||||
|
||||
|
@ -40,18 +41,19 @@ export default (props = {}) => {
|
|||
dataProps.customMetricsAvailable = parseBoolean(dataProps.customMetricsAvailable);
|
||||
dataProps.prometheusAlertsAvailable = parseBoolean(dataProps.prometheusAlertsAvailable);
|
||||
|
||||
const router = createRouter(metricsDashboardBasePath);
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
store,
|
||||
render(createElement) {
|
||||
return createElement(Dashboard, {
|
||||
props: {
|
||||
...dataProps,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
router,
|
||||
data() {
|
||||
return {
|
||||
dashboardProps: { ...dataProps, ...props },
|
||||
};
|
||||
},
|
||||
template: `<router-view :dashboardProps="dashboardProps"/>`,
|
||||
});
|
||||
}
|
||||
};
|
18
app/assets/javascripts/monitoring/pages/dashboard_page.vue
Normal file
18
app/assets/javascripts/monitoring/pages/dashboard_page.vue
Normal file
|
@ -0,0 +1,18 @@
|
|||
<script>
|
||||
import Dashboard from '../components/dashboard.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Dashboard,
|
||||
},
|
||||
props: {
|
||||
dashboardProps: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<dashboard v-bind="{ ...dashboardProps }" />
|
||||
</template>
|
3
app/assets/javascripts/monitoring/router/constants.js
Normal file
3
app/assets/javascripts/monitoring/router/constants.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export const BASE_DASHBOARD_PAGE = 'dashboard';
|
||||
|
||||
export default {};
|
15
app/assets/javascripts/monitoring/router/index.js
Normal file
15
app/assets/javascripts/monitoring/router/index.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import routes from './routes';
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
export default function createRouter(base) {
|
||||
const router = new VueRouter({
|
||||
base,
|
||||
mode: 'history',
|
||||
routes,
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
18
app/assets/javascripts/monitoring/router/routes.js
Normal file
18
app/assets/javascripts/monitoring/router/routes.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import DashboardPage from '../pages/dashboard_page.vue';
|
||||
|
||||
import { BASE_DASHBOARD_PAGE } from './constants';
|
||||
|
||||
/**
|
||||
* Because the cluster health page uses the dashboard
|
||||
* app instead the of the dashboard component, hitting
|
||||
* `/` route is not possible. Hence using `*` until the
|
||||
* health page is refactored.
|
||||
* https://gitlab.com/gitlab-org/gitlab/-/issues/221096
|
||||
*/
|
||||
export default [
|
||||
{
|
||||
name: BASE_DASHBOARD_PAGE,
|
||||
path: '*',
|
||||
component: DashboardPage,
|
||||
},
|
||||
];
|
|
@ -1,3 +1,3 @@
|
|||
import monitoringBundle from '~/monitoring/monitoring_bundle';
|
||||
import monitoringApp from '~/monitoring/monitoring_app';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', monitoringBundle);
|
||||
document.addEventListener('DOMContentLoaded', monitoringApp);
|
||||
|
|
|
@ -32,6 +32,7 @@ export default {
|
|||
v-for="panel in panels"
|
||||
:key="panel.name"
|
||||
:href="`#${panel.name}`"
|
||||
:data-qa-selector="`${panel.name}_link`"
|
||||
class="blank-state blank-state-link experiment-new-project-page-blank-state"
|
||||
@click="track('click_tab', { label: panel.name })"
|
||||
>
|
||||
|
|
|
@ -308,7 +308,6 @@ body {
|
|||
);
|
||||
}
|
||||
|
||||
&.gl-dark,
|
||||
&.ui-light {
|
||||
@include gitlab-theme(
|
||||
$gray-700,
|
||||
|
@ -391,13 +390,47 @@ body {
|
|||
}
|
||||
|
||||
&.gl-dark {
|
||||
@include gitlab-theme(
|
||||
$gray-900,
|
||||
$gray-500,
|
||||
$gray-700,
|
||||
$gray-800,
|
||||
$gray-50,
|
||||
$gray-100
|
||||
);
|
||||
.logo-text svg {
|
||||
fill: $gl-text-color;
|
||||
}
|
||||
|
||||
.navbar-gitlab {
|
||||
background-color: $gray-50;
|
||||
|
||||
.navbar-sub-nav,
|
||||
.navbar-nav {
|
||||
li {
|
||||
> a:hover,
|
||||
> a:focus,
|
||||
> button:hover,
|
||||
> button:focus {
|
||||
color: $gl-text-color;
|
||||
background-color: $gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
li.active,
|
||||
li.dropdown.show {
|
||||
> a,
|
||||
> button {
|
||||
color: $gl-text-color;
|
||||
background-color: $gray-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
form {
|
||||
background-color: $gray-100;
|
||||
box-shadow: inset 0 0 0 1px $border-color;
|
||||
|
||||
&:active,
|
||||
&:hover {
|
||||
background-color: $gray-100;
|
||||
box-shadow: inset 0 0 0 1px $blue-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
.select2-container.select2-drop-above {
|
||||
.select2-choice {
|
||||
background: $white;
|
||||
color: $gl-text-color;
|
||||
border-color: $input-border;
|
||||
height: 34px;
|
||||
padding: $gl-vert-padding $gl-input-padding;
|
||||
|
|
|
@ -25,6 +25,13 @@ $ide-commit-header-height: 48px;
|
|||
@include str-truncated(250px);
|
||||
}
|
||||
|
||||
.ide-layout {
|
||||
// Fix for iOS 13+, the height of the page is actually less than
|
||||
// 100vh because of the presence of the bottom bar
|
||||
max-height: 100%;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.ide-view {
|
||||
position: relative;
|
||||
margin-top: 0;
|
||||
|
|
|
@ -99,7 +99,7 @@ $border-white-normal: $gray-900;
|
|||
|
||||
$body-bg: $gray-50;
|
||||
$input-bg: $gray-100;
|
||||
$input-focus-bg: $gray-50;
|
||||
$input-focus-bg: $gray-100;
|
||||
$input-color: $gray-900;
|
||||
$input-group-addon-bg: $gray-900;
|
||||
|
||||
|
|
24
app/controllers/concerns/find_snippet.rb
Normal file
24
app/controllers/concerns/find_snippet.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module FindSnippet
|
||||
extend ActiveSupport::Concern
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
private
|
||||
|
||||
# rubocop:disable CodeReuse/ActiveRecord
|
||||
def snippet
|
||||
strong_memoize(:snippet) do
|
||||
snippet_klass.inc_relations_for_view.find_by(id: snippet_id)
|
||||
end
|
||||
end
|
||||
# rubocop:enable CodeReuse/ActiveRecord
|
||||
|
||||
def snippet_klass
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def snippet_id
|
||||
params[:id]
|
||||
end
|
||||
end
|
23
app/controllers/concerns/snippet_authorizations.rb
Normal file
23
app/controllers/concerns/snippet_authorizations.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module SnippetAuthorizations
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
private
|
||||
|
||||
def authorize_read_snippet!
|
||||
return render_404 unless can?(current_user, :read_snippet, snippet)
|
||||
end
|
||||
|
||||
def authorize_update_snippet!
|
||||
return render_404 unless can?(current_user, :update_snippet, snippet)
|
||||
end
|
||||
|
||||
def authorize_admin_snippet!
|
||||
return render_404 unless can?(current_user, :admin_snippet, snippet)
|
||||
end
|
||||
|
||||
def authorize_create_snippet!
|
||||
return render_404 unless can?(current_user, :create_snippet)
|
||||
end
|
||||
end
|
|
@ -3,9 +3,18 @@
|
|||
module SnippetsActions
|
||||
extend ActiveSupport::Concern
|
||||
include SendsBlob
|
||||
include RendersNotes
|
||||
include RendersBlob
|
||||
include PaginatedCollection
|
||||
include Gitlab::NoteableMetadata
|
||||
|
||||
included do
|
||||
skip_before_action :verify_authenticity_token,
|
||||
if: -> { action_name == 'show' && js_request? }
|
||||
|
||||
before_action :redirect_if_binary, only: [:edit, :update]
|
||||
|
||||
respond_to :html
|
||||
end
|
||||
|
||||
def edit
|
||||
|
@ -43,6 +52,58 @@ module SnippetsActions
|
|||
request.format.js?
|
||||
end
|
||||
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
def show
|
||||
conditionally_expand_blob(blob)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@note = Note.new(noteable: @snippet, project: @snippet.project)
|
||||
@noteable = @snippet
|
||||
|
||||
@discussions = @snippet.discussions
|
||||
@notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
|
||||
render 'show'
|
||||
end
|
||||
|
||||
format.json do
|
||||
render_blob_json(blob)
|
||||
end
|
||||
|
||||
format.js do
|
||||
if @snippet.embeddable?
|
||||
render 'shared/snippets/show'
|
||||
else
|
||||
head :not_found
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
update_params = snippet_params.merge(spammable_params)
|
||||
|
||||
service_response = Snippets::UpdateService.new(@snippet.project, current_user, update_params).execute(@snippet)
|
||||
@snippet = service_response.payload[:snippet]
|
||||
|
||||
handle_repository_error(:edit)
|
||||
end
|
||||
|
||||
def destroy
|
||||
service_response = Snippets::DestroyService.new(current_user, @snippet).execute
|
||||
|
||||
if service_response.success?
|
||||
redirect_to gitlab_dashboard_snippets_path(@snippet), status: :found
|
||||
elsif service_response.http_status == 403
|
||||
access_denied!
|
||||
else
|
||||
redirect_to gitlab_snippet_path(@snippet),
|
||||
status: :found,
|
||||
alert: service_response.message
|
||||
end
|
||||
end
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
private
|
||||
|
||||
def content_disposition
|
||||
|
|
19
app/controllers/projects/snippets/application_controller.rb
Normal file
19
app/controllers/projects/snippets/application_controller.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::Snippets::ApplicationController < Projects::ApplicationController
|
||||
include FindSnippet
|
||||
include SnippetAuthorizations
|
||||
|
||||
private
|
||||
|
||||
# This overrides the default snippet create authorization
|
||||
# because ProjectSnippets are checked against the project rather
|
||||
# than the user
|
||||
def authorize_create_snippet!
|
||||
return render_404 unless can?(current_user, :create_snippet, project)
|
||||
end
|
||||
|
||||
def snippet_klass
|
||||
ProjectSnippet
|
||||
end
|
||||
end
|
|
@ -1,34 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::SnippetsController < Projects::ApplicationController
|
||||
include RendersNotes
|
||||
class Projects::SnippetsController < Projects::Snippets::ApplicationController
|
||||
include SnippetsActions
|
||||
include ToggleAwardEmoji
|
||||
include SpammableActions
|
||||
include SnippetsActions
|
||||
include RendersBlob
|
||||
include PaginatedCollection
|
||||
include Gitlab::NoteableMetadata
|
||||
|
||||
skip_before_action :verify_authenticity_token,
|
||||
if: -> { action_name == 'show' && js_request? }
|
||||
|
||||
before_action :check_snippets_available!
|
||||
|
||||
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
|
||||
|
||||
# Allow create snippet
|
||||
before_action :authorize_create_snippet!, only: [:new, :create]
|
||||
|
||||
# Allow read any snippet
|
||||
before_action :authorize_read_snippet!, except: [:new, :create, :index]
|
||||
|
||||
# Allow modify snippet
|
||||
before_action :authorize_update_snippet!, only: [:edit, :update]
|
||||
|
||||
# Allow destroy snippet
|
||||
before_action :authorize_admin_snippet!, only: [:destroy]
|
||||
|
||||
respond_to :html
|
||||
|
||||
def index
|
||||
@snippet_counts = Snippets::CountService
|
||||
.new(current_user, project: @project)
|
||||
|
@ -56,61 +41,8 @@ class Projects::SnippetsController < Projects::ApplicationController
|
|||
handle_repository_error(:new)
|
||||
end
|
||||
|
||||
def update
|
||||
update_params = snippet_params.merge(spammable_params)
|
||||
|
||||
service_response = Snippets::UpdateService.new(project, current_user, update_params).execute(@snippet)
|
||||
@snippet = service_response.payload[:snippet]
|
||||
|
||||
handle_repository_error(:edit)
|
||||
end
|
||||
|
||||
def show
|
||||
conditionally_expand_blob(blob)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@note = @project.notes.new(noteable: @snippet)
|
||||
@noteable = @snippet
|
||||
|
||||
@discussions = @snippet.discussions
|
||||
@notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
|
||||
render 'show'
|
||||
end
|
||||
|
||||
format.json do
|
||||
render_blob_json(blob)
|
||||
end
|
||||
|
||||
format.js do
|
||||
if @snippet.embeddable?
|
||||
render 'shared/snippets/show'
|
||||
else
|
||||
head :not_found
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
service_response = Snippets::DestroyService.new(current_user, @snippet).execute
|
||||
|
||||
if service_response.success?
|
||||
redirect_to project_snippets_path(project), status: :found
|
||||
elsif service_response.http_status == 403
|
||||
access_denied!
|
||||
else
|
||||
redirect_to project_snippet_path(project, @snippet),
|
||||
status: :found,
|
||||
alert: service_response.message
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def snippet
|
||||
@snippet ||= @project.snippets.inc_relations_for_view.find(params[:id])
|
||||
end
|
||||
alias_method :awardable, :snippet
|
||||
alias_method :spammable, :snippet
|
||||
|
||||
|
@ -118,18 +50,6 @@ class Projects::SnippetsController < Projects::ApplicationController
|
|||
project_snippet_path(@project, @snippet)
|
||||
end
|
||||
|
||||
def authorize_read_snippet!
|
||||
return render_404 unless can?(current_user, :read_snippet, @snippet)
|
||||
end
|
||||
|
||||
def authorize_update_snippet!
|
||||
return render_404 unless can?(current_user, :update_snippet, @snippet)
|
||||
end
|
||||
|
||||
def authorize_admin_snippet!
|
||||
return render_404 unless can?(current_user, :admin_snippet, @snippet)
|
||||
end
|
||||
|
||||
def snippet_params
|
||||
params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description)
|
||||
end
|
||||
|
|
|
@ -41,16 +41,20 @@ class Projects::TagsController < Projects::ApplicationController
|
|||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def create
|
||||
# TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
|
||||
evidence_pipeline = find_evidence_pipeline
|
||||
|
||||
result = ::Tags::CreateService.new(@project, current_user)
|
||||
.execute(params[:tag_name], params[:ref], params[:message])
|
||||
|
||||
if result[:status] == :success
|
||||
# Release creation with Tags was deprecated in GitLab 11.7
|
||||
# TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
|
||||
if params[:release_description].present?
|
||||
release_params = {
|
||||
tag: params[:tag_name],
|
||||
name: params[:tag_name],
|
||||
description: params[:release_description]
|
||||
description: params[:release_description],
|
||||
evidence_pipeline: evidence_pipeline
|
||||
}
|
||||
|
||||
Releases::CreateService
|
||||
|
@ -93,4 +97,14 @@ class Projects::TagsController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
|
||||
def find_evidence_pipeline
|
||||
evidence_pipeline_sha = @project.repository.commit(params[:ref])&.sha
|
||||
return unless evidence_pipeline_sha
|
||||
|
||||
@project.ci_pipelines.for_sha(evidence_pipeline_sha).last
|
||||
end
|
||||
end
|
||||
|
|
22
app/controllers/snippets/application_controller.rb
Normal file
22
app/controllers/snippets/application_controller.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Snippets::ApplicationController < ApplicationController
|
||||
include FindSnippet
|
||||
include SnippetAuthorizations
|
||||
|
||||
private
|
||||
|
||||
def authorize_read_snippet!
|
||||
return if can?(current_user, :read_snippet, snippet)
|
||||
|
||||
if current_user
|
||||
render_404
|
||||
else
|
||||
authenticate_user!
|
||||
end
|
||||
end
|
||||
|
||||
def snippet_klass
|
||||
PersonalSnippet
|
||||
end
|
||||
end
|
|
@ -1,19 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SnippetsController < ApplicationController
|
||||
include RendersNotes
|
||||
class SnippetsController < Snippets::ApplicationController
|
||||
include SnippetsActions
|
||||
include PreviewMarkdown
|
||||
include ToggleAwardEmoji
|
||||
include SpammableActions
|
||||
include SnippetsActions
|
||||
include RendersBlob
|
||||
include PreviewMarkdown
|
||||
include PaginatedCollection
|
||||
include Gitlab::NoteableMetadata
|
||||
|
||||
skip_before_action :verify_authenticity_token,
|
||||
if: -> { action_name == 'show' && js_request? }
|
||||
|
||||
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw]
|
||||
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
|
||||
|
||||
before_action :authorize_create_snippet!, only: [:new, :create]
|
||||
before_action :authorize_read_snippet!, only: [:show, :raw]
|
||||
|
@ -23,7 +16,6 @@ class SnippetsController < ApplicationController
|
|||
skip_before_action :authenticate_user!, only: [:index, :show, :raw]
|
||||
|
||||
layout 'snippets'
|
||||
respond_to :html
|
||||
|
||||
def index
|
||||
if params[:username].present?
|
||||
|
@ -60,62 +52,8 @@ class SnippetsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def update
|
||||
service_response = Snippets::UpdateService.new(nil, current_user, snippet_params).execute(@snippet)
|
||||
@snippet = service_response.payload[:snippet]
|
||||
|
||||
handle_repository_error(:edit)
|
||||
end
|
||||
|
||||
def show
|
||||
conditionally_expand_blob(blob)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@note = Note.new(noteable: @snippet)
|
||||
@noteable = @snippet
|
||||
|
||||
@discussions = @snippet.discussions
|
||||
@notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
|
||||
render 'show'
|
||||
end
|
||||
|
||||
format.json do
|
||||
render_blob_json(blob)
|
||||
end
|
||||
|
||||
format.js do
|
||||
if @snippet.embeddable?
|
||||
render 'shared/snippets/show'
|
||||
else
|
||||
head :not_found
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
service_response = Snippets::DestroyService.new(current_user, @snippet).execute
|
||||
|
||||
if service_response.success?
|
||||
redirect_to dashboard_snippets_path, status: :found
|
||||
elsif service_response.http_status == 403
|
||||
access_denied!
|
||||
else
|
||||
redirect_to snippet_path(@snippet),
|
||||
status: :found,
|
||||
alert: service_response.message
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def snippet
|
||||
@snippet ||= PersonalSnippet.inc_relations_for_view.find_by(id: params[:id])
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
alias_method :awardable, :snippet
|
||||
alias_method :spammable, :snippet
|
||||
|
||||
|
@ -123,28 +61,6 @@ class SnippetsController < ApplicationController
|
|||
snippet_path(@snippet)
|
||||
end
|
||||
|
||||
def authorize_read_snippet!
|
||||
return if can?(current_user, :read_snippet, @snippet)
|
||||
|
||||
if current_user
|
||||
render_404
|
||||
else
|
||||
authenticate_user!
|
||||
end
|
||||
end
|
||||
|
||||
def authorize_update_snippet!
|
||||
return render_404 unless can?(current_user, :update_snippet, @snippet)
|
||||
end
|
||||
|
||||
def authorize_admin_snippet!
|
||||
return render_404 unless can?(current_user, :admin_snippet, @snippet)
|
||||
end
|
||||
|
||||
def authorize_create_snippet!
|
||||
return render_404 unless can?(current_user, :create_snippet)
|
||||
end
|
||||
|
||||
def snippet_params
|
||||
params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description).merge(spammable_params)
|
||||
end
|
||||
|
|
|
@ -245,6 +245,14 @@ module GitlabRoutingHelper
|
|||
end
|
||||
end
|
||||
|
||||
def gitlab_dashboard_snippets_path(snippet, *args)
|
||||
if snippet.is_a?(ProjectSnippet)
|
||||
project_snippets_path(snippet.project, *args)
|
||||
else
|
||||
dashboard_snippets_path
|
||||
end
|
||||
end
|
||||
|
||||
def gitlab_raw_snippet_path(snippet, *args)
|
||||
if snippet.is_a?(ProjectSnippet)
|
||||
raw_project_snippet_path(snippet.project, snippet, *args)
|
||||
|
|
19
app/models/concerns/route_model_query.rb
Normal file
19
app/models/concerns/route_model_query.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Shared scope between Route and RedirectRoute
|
||||
module RouteModelQuery
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def find_source_of_path(path, case_sensitive: true)
|
||||
scope =
|
||||
if case_sensitive
|
||||
where(path: path)
|
||||
else
|
||||
where('LOWER(path) = LOWER(?)', path)
|
||||
end
|
||||
|
||||
scope.first&.source
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1253,7 +1253,7 @@ class Project < ApplicationRecord
|
|||
|
||||
available_services_names.map do |service_name|
|
||||
find_or_initialize_service(service_name)
|
||||
end
|
||||
end.sort_by(&:title)
|
||||
end
|
||||
|
||||
def disabled_services
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RedirectRoute < ApplicationRecord
|
||||
include RouteModelQuery
|
||||
|
||||
belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
|
||||
|
||||
validates :source, presence: true
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class Route < ApplicationRecord
|
||||
include CaseSensitivity
|
||||
include Gitlab::SQL::Pattern
|
||||
include RouteModelQuery
|
||||
|
||||
belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
|
||||
validates :source, presence: true
|
||||
|
|
|
@ -167,7 +167,11 @@ class Snippet < ApplicationRecord
|
|||
end
|
||||
|
||||
def self.find_by_id_and_project(id:, project:)
|
||||
Snippet.find_by(id: id, project: project)
|
||||
if project.is_a?(Project)
|
||||
ProjectSnippet.find_by(id: id, project: project)
|
||||
elsif project.nil?
|
||||
PersonalSnippet.find_by(id: id)
|
||||
end
|
||||
end
|
||||
|
||||
def self.max_file_limit(user)
|
||||
|
|
|
@ -64,7 +64,7 @@ module Projects
|
|||
end
|
||||
|
||||
def remove_snippets
|
||||
response = Snippets::BulkDestroyService.new(current_user, project.snippets).execute
|
||||
response = ::Snippets::BulkDestroyService.new(current_user, project.snippets).execute
|
||||
|
||||
response.success?
|
||||
end
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
module Releases
|
||||
class CreateEvidenceService
|
||||
def initialize(release)
|
||||
def initialize(release, pipeline: nil)
|
||||
@release = release
|
||||
@pipeline = pipeline
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
|
@ -9,11 +9,16 @@ module Releases
|
|||
return error('Release already exists', 409) if release
|
||||
return error("Milestone(s) not found: #{inexistent_milestones.join(', ')}", 400) if inexistent_milestones.any?
|
||||
|
||||
# should be found before the creation of new tag
|
||||
# because tag creation can spawn new pipeline
|
||||
# which won't have any data for evidence yet
|
||||
evidence_pipeline = find_evidence_pipeline
|
||||
|
||||
tag = ensure_tag
|
||||
|
||||
return tag unless tag.is_a?(Gitlab::Git::Tag)
|
||||
|
||||
create_release(tag)
|
||||
create_release(tag, evidence_pipeline)
|
||||
end
|
||||
|
||||
def find_or_build_release
|
||||
|
@ -42,14 +47,14 @@ module Releases
|
|||
Ability.allowed?(current_user, :create_release, project)
|
||||
end
|
||||
|
||||
def create_release(tag)
|
||||
def create_release(tag, evidence_pipeline)
|
||||
release = build_release(tag)
|
||||
|
||||
release.save!
|
||||
|
||||
notify_create_release(release)
|
||||
|
||||
create_evidence!(release)
|
||||
create_evidence!(release, evidence_pipeline)
|
||||
|
||||
success(tag: tag, release: release)
|
||||
rescue => e
|
||||
|
@ -73,13 +78,25 @@ module Releases
|
|||
)
|
||||
end
|
||||
|
||||
def create_evidence!(release)
|
||||
def find_evidence_pipeline
|
||||
# TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
|
||||
return params[:evidence_pipeline] if params[:evidence_pipeline]
|
||||
|
||||
sha = existing_tag&.dereferenced_target&.sha
|
||||
sha ||= repository.commit(ref)&.sha
|
||||
|
||||
return unless sha
|
||||
|
||||
project.ci_pipelines.for_sha(sha).last
|
||||
end
|
||||
|
||||
def create_evidence!(release, pipeline)
|
||||
return if release.historical_release?
|
||||
|
||||
if release.upcoming_release?
|
||||
CreateEvidenceWorker.perform_at(release.released_at, release.id)
|
||||
CreateEvidenceWorker.perform_at(release.released_at, release.id, pipeline&.id)
|
||||
else
|
||||
CreateEvidenceWorker.perform_async(release.id)
|
||||
CreateEvidenceWorker.perform_async(release.id, pipeline&.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,8 +7,8 @@ class AuthorizedProjectsWorker
|
|||
feature_category :authentication_and_authorization
|
||||
urgency :high
|
||||
weight 2
|
||||
|
||||
idempotent!
|
||||
loggable_arguments 1 # For the job waiter key
|
||||
|
||||
# This is a workaround for a Ruby 2.3.7 bug. rspec-mocks cannot restore the
|
||||
# visibility of prepended modules. See https://github.com/rspec/rspec-mocks/issues/1231
|
||||
|
|
|
@ -6,10 +6,15 @@ class CreateEvidenceWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
feature_category :release_evidence
|
||||
weight 2
|
||||
|
||||
def perform(release_id)
|
||||
# pipeline_id is optional for backward compatibility with existing jobs
|
||||
# caller should always try to provide the pipeline and pass nil only
|
||||
# if pipeline is absent
|
||||
def perform(release_id, pipeline_id = nil)
|
||||
release = Release.find_by_id(release_id)
|
||||
return unless release
|
||||
|
||||
::Releases::CreateEvidenceService.new(release).execute
|
||||
pipeline = Ci::Pipeline.find_by_id(pipeline_id)
|
||||
|
||||
::Releases::CreateEvidenceService.new(release, pipeline: pipeline).execute
|
||||
end
|
||||
end
|
||||
|
|
5
changelogs/unreleased/213587-ide-ipad-scroll-issue.yml
Normal file
5
changelogs/unreleased/213587-ide-ipad-scroll-issue.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix issues with scroll on iOS / iPad OS
|
||||
merge_request: 34486
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow CI_JOB_TOKEN for authenticating to the Terraform state API
|
||||
merge_request: 34618
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix order of integrations to be sorted alphabetically
|
||||
merge_request: 34501
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/services-usage-2.yml
Normal file
5
changelogs/unreleased/services-usage-2.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use Keys::DestroyService for deleting an SSH key when an admin deletes a key via the API
|
||||
merge_request: 34535
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -64,7 +64,7 @@ You can add a command to your `.gitlab-ci.yml` file to
|
|||
| `CI_JOB_MANUAL` | 8.12 | all | The flag to indicate that job was manually started |
|
||||
| `CI_JOB_NAME` | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` |
|
||||
| `CI_JOB_STAGE` | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
|
||||
| `CI_JOB_TOKEN` | 9.0 | 1.2 | Token used for authenticating with the [GitLab Container Registry](../../user/packages/container_registry/index.md) and downloading [dependent repositories](../../user/project/new_ci_build_permissions_model.md#dependent-repositories) |
|
||||
| `CI_JOB_TOKEN` | 9.0 | 1.2 | Token used for authenticating with the [GitLab Container Registry](../../user/packages/container_registry/index.md), downloading [dependent repositories](../../user/project/new_ci_build_permissions_model.md#dependent-repositories), and accessing [GitLab-managed Terraform state](../../user/infrastructure/index.md#gitlab-managed-terraform-state). |
|
||||
| `CI_JOB_JWT` | 12.10 | all | RS256 JSON web token that can be used for authenticating with third party systems that support JWT authentication, for example [HashiCorp's Vault](../examples/authenticating-with-hashicorp-vault). |
|
||||
| `CI_JOB_URL` | 11.1 | 0.5 | Job details URL |
|
||||
| `CI_KUBERNETES_ACTIVE` | 13.0 | all | Included with the value `true` only if the pipeline has a Kubernetes cluster available for deployments. Not included if no cluster is available. Can be used as an alternative to [`only:kubernetes`/`except:kubernetes`](../yaml/README.md#onlykubernetesexceptkubernetes) with [`rules:if`](../yaml/README.md#rulesif) |
|
||||
|
|
|
@ -25,7 +25,7 @@ Amazon S3 or Google Cloud Storage. Its features include:
|
|||
To get started with a GitLab-managed Terraform State, there are two different options:
|
||||
|
||||
- [Use a local machine](#get-started-using-local-development).
|
||||
- [Use GitLab CI](#get-started-using-a-gitlab-ci).
|
||||
- [Use GitLab CI](#get-started-using-gitlab-ci).
|
||||
|
||||
## Get started using local development
|
||||
|
||||
|
@ -44,10 +44,15 @@ local machine, this is a simple way to get started:
|
|||
}
|
||||
```
|
||||
|
||||
1. Create a [Personal Access Token](../profile/personal_access_tokens.md) with
|
||||
the `api` scope. The Terraform backend is restricted to users with
|
||||
[Maintainer access](../permissions.md) to the repository.
|
||||
|
||||
1. On your local machine, run `terraform init`, passing in the following options,
|
||||
replacing `<YOUR-PROJECT-NAME>` and `<YOUR-PROJECT-ID>` with the values for
|
||||
your project. This command initializes your Terraform state, and stores that
|
||||
state within your GitLab project. This example uses `gitlab.com`:
|
||||
replacing `<YOUR-PROJECT-NAME>`, `<YOUR-PROJECT-ID>`, `<YOUR-USERNAME>` and
|
||||
`<YOUR-ACCESS-TOKEN>` with the relevant values. This command initializes your
|
||||
Terraform state, and stores that state within your GitLab project. This example
|
||||
uses `gitlab.com`:
|
||||
|
||||
```shell
|
||||
terraform init \
|
||||
|
@ -61,30 +66,24 @@ local machine, this is a simple way to get started:
|
|||
-backend-config="retry_wait_min=5"
|
||||
```
|
||||
|
||||
Next, [configure the backend](#configure-the-variables-and-backend).
|
||||
Next, [configure the backend](#configure-the-backend).
|
||||
|
||||
## Get started using a GitLab CI
|
||||
## Get started using GitLab CI
|
||||
|
||||
If you don't want to start with local development, you can also use GitLab CI to
|
||||
run your `terraform plan` and `terraform apply` commands.
|
||||
|
||||
Next, [configure the backend](#configure-the-variables-and-backend).
|
||||
Next, [configure the backend](#configure-the-backend).
|
||||
|
||||
## Configure the variables and backend
|
||||
## Configure the backend
|
||||
|
||||
After executing the `terraform init` command, you must configure the needed CI
|
||||
variables, the Terraform backend, and the CI YAML file:
|
||||
After executing the `terraform init` command, you must configure the Terraform backend
|
||||
and the CI YAML file:
|
||||
|
||||
1. Create a [Personal Access Token](../profile/personal_access_tokens.md) with
|
||||
the `api` scope. The Terraform backend is restricted to tokens with
|
||||
[Maintainer access](../permissions.md) to the repository.
|
||||
1. To keep the Personal Access Token secure, add it as a
|
||||
[CI/CD environment variable](../../ci/variables/README.md). For the examples on
|
||||
this page, it's set to the environment variable `GITLAB_TF_PASSWORD`.
|
||||
CAUTION: **Important:**
|
||||
The Terraform backend is restricted to users with [Maintainer access](../permissions.md)
|
||||
to the repository.
|
||||
|
||||
CAUTION: **Important:**
|
||||
If you plan to use the environment variable on an unprotected branch, make sure
|
||||
to set the variable protection settings correctly.
|
||||
1. In your Terraform project, define the [HTTP backend](https://www.terraform.io/docs/backends/types/http.html)
|
||||
by adding the following code block in a `.tf` file (such as `backend.tf`) to
|
||||
define the remote backend:
|
||||
|
@ -129,7 +128,7 @@ variables, the Terraform backend, and the CI YAML file:
|
|||
before_script:
|
||||
- cd ${TF_ROOT}
|
||||
- terraform --version
|
||||
- terraform init -backend-config="address=${GITLAB_TF_ADDRESS}" -backend-config="lock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="unlock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="username=${GITLAB_USER_LOGIN}" -backend-config="password=${GITLAB_TF_PASSWORD}" -backend-config="lock_method=POST" -backend-config="unlock_method=DELETE" -backend-config="retry_wait_min=5"
|
||||
- terraform init -backend-config="address=${GITLAB_TF_ADDRESS}" -backend-config="lock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="unlock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="username=gitlab-ci-token" -backend-config="password=${CI_JOB_TOKEN}" -backend-config="lock_method=POST" -backend-config="unlock_method=DELETE" -backend-config="retry_wait_min=5"
|
||||
|
||||
stages:
|
||||
- validate
|
||||
|
|
|
@ -32,7 +32,7 @@ module API
|
|||
end
|
||||
|
||||
desc 'Get a terraform state by its name'
|
||||
route_setting :authentication, basic_auth_personal_access_token: true
|
||||
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
|
||||
get do
|
||||
remote_state_handler.find_with_lock do |state|
|
||||
no_content! unless state.file.exists?
|
||||
|
@ -44,7 +44,7 @@ module API
|
|||
end
|
||||
|
||||
desc 'Add a new terraform state or update an existing one'
|
||||
route_setting :authentication, basic_auth_personal_access_token: true
|
||||
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
|
||||
post do
|
||||
data = request.body.read
|
||||
no_content! if data.empty?
|
||||
|
@ -57,7 +57,7 @@ module API
|
|||
end
|
||||
|
||||
desc 'Delete a terraform state of a certain name'
|
||||
route_setting :authentication, basic_auth_personal_access_token: true
|
||||
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
|
||||
delete do
|
||||
remote_state_handler.handle_with_lock do |state|
|
||||
state.destroy!
|
||||
|
@ -66,7 +66,7 @@ module API
|
|||
end
|
||||
|
||||
desc 'Lock a terraform state of a certain name'
|
||||
route_setting :authentication, basic_auth_personal_access_token: true
|
||||
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
|
||||
params do
|
||||
requires :ID, type: String, limit: 255, desc: 'Terraform state lock ID'
|
||||
requires :Operation, type: String, desc: 'Terraform operation'
|
||||
|
@ -103,7 +103,7 @@ module API
|
|||
end
|
||||
|
||||
desc 'Unlock a terraform state of a certain name'
|
||||
route_setting :authentication, basic_auth_personal_access_token: true
|
||||
route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth
|
||||
params do
|
||||
optional :ID, type: String, limit: 255, desc: 'Terraform state lock ID'
|
||||
end
|
||||
|
|
|
@ -306,7 +306,10 @@ module API
|
|||
key = user.keys.find_by(id: params[:key_id])
|
||||
not_found!('Key') unless key
|
||||
|
||||
destroy_conditionally!(key)
|
||||
destroy_conditionally!(key) do |key|
|
||||
destroy_service = ::Keys::DestroyService.new(current_user)
|
||||
destroy_service.execute(key)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ module Gitlab
|
|||
|
||||
def find_user_from_job_token
|
||||
return unless route_authentication_setting[:job_token_allowed]
|
||||
return find_user_from_basic_auth_job if route_authentication_setting[:job_token_allowed] == :basic_auth
|
||||
|
||||
token = current_request.params[JOB_TOKEN_PARAM].presence ||
|
||||
current_request.params[RUNNER_JOB_TOKEN_PARAM].presence ||
|
||||
|
|
|
@ -4,6 +4,11 @@ module Gitlab
|
|||
module RepoPath
|
||||
NotFoundError = Class.new(StandardError)
|
||||
|
||||
# @return [Array]
|
||||
# 1. container (ActiveRecord which holds repository)
|
||||
# 2. project (Project)
|
||||
# 3. repo_type
|
||||
# 4. redirected_path
|
||||
def self.parse(path)
|
||||
repo_path = path.sub(/\.git\z/, '').sub(%r{\A/}, '')
|
||||
redirected_path = nil
|
||||
|
@ -17,7 +22,7 @@ module Gitlab
|
|||
# `Gitlab::GlRepository::PROJECT` type.
|
||||
next unless type.valid?(repo_path)
|
||||
|
||||
# Removing the suffix (.wiki, .design, ...) from the project path
|
||||
# Removing the suffix (.wiki, .design, ...) from path
|
||||
full_path = repo_path.chomp(type.path_suffix)
|
||||
container, project, redirected_path = find_container(type, full_path)
|
||||
|
||||
|
@ -36,23 +41,31 @@ module Gitlab
|
|||
|
||||
[snippet, snippet&.project, redirected_path]
|
||||
else
|
||||
project, redirected_path = find_project(full_path)
|
||||
container, redirected_path = find_routes_source(full_path)
|
||||
|
||||
[project, project, redirected_path]
|
||||
if container.is_a?(Project)
|
||||
[container, container, redirected_path]
|
||||
else
|
||||
[container, nil, redirected_path]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.find_project(project_path)
|
||||
return [nil, nil] if project_path.blank?
|
||||
def self.find_routes_source(path)
|
||||
return [nil, nil] if path.blank?
|
||||
|
||||
project = Project.find_by_full_path(project_path, follow_redirects: true)
|
||||
redirected_path = redirected?(project, project_path) ? project_path : nil
|
||||
source =
|
||||
Route.find_source_of_path(path) ||
|
||||
Route.find_source_of_path(path, case_sensitive: false) ||
|
||||
RedirectRoute.find_source_of_path(path, case_sensitive: false)
|
||||
|
||||
[project, redirected_path]
|
||||
redirected_path = redirected?(source, path) ? path : nil
|
||||
|
||||
[source, redirected_path]
|
||||
end
|
||||
|
||||
def self.redirected?(project, project_path)
|
||||
project && project.full_path.casecmp(project_path) != 0
|
||||
def self.redirected?(container, container_path)
|
||||
container && container.full_path.casecmp(container_path) != 0
|
||||
end
|
||||
|
||||
# Snippet_path can be either:
|
||||
|
@ -62,7 +75,7 @@ module Gitlab
|
|||
return [nil, nil] if snippet_path.blank?
|
||||
|
||||
snippet_id, project_path = extract_snippet_info(snippet_path)
|
||||
project, redirected_path = find_project(project_path)
|
||||
project, redirected_path = find_routes_source(project_path)
|
||||
|
||||
[Snippet.find_by_id_and_project(id: snippet_id, project: project), redirected_path]
|
||||
end
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
"@babel/preset-env": "^7.10.1",
|
||||
"@gitlab/at.js": "1.5.5",
|
||||
"@gitlab/svgs": "1.139.0",
|
||||
"@gitlab/ui": "16.10.0",
|
||||
"@gitlab/ui": "16.12.1",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-1",
|
||||
"@sentry/browser": "^5.10.2",
|
||||
|
|
1
qa/qa.rb
1
qa/qa.rb
|
@ -231,6 +231,7 @@ module QA
|
|||
|
||||
module Project
|
||||
autoload :New, 'qa/page/project/new'
|
||||
autoload :NewExperiment, 'qa/page/project/new_experiment'
|
||||
autoload :Show, 'qa/page/project/show'
|
||||
autoload :Activity, 'qa/page/project/activity'
|
||||
autoload :Menu, 'qa/page/project/menu'
|
||||
|
|
|
@ -14,6 +14,14 @@ module QA
|
|||
member_settings.add_member(username)
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_create_project_from_template
|
||||
if Page::Project::NewExperiment.perform(&:shown?)
|
||||
Page::Project::NewExperiment.perform(&:click_create_from_template_link)
|
||||
else
|
||||
Page::Project::New.perform(&:click_create_from_template_tab)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
26
qa/qa/page/project/new_experiment.rb
Normal file
26
qa/qa/page/project/new_experiment.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Project
|
||||
class NewExperiment < Page::Base
|
||||
view 'app/assets/javascripts/projects/experiment_new_project_creation/components/welcome.vue' do
|
||||
element :blank_project_link, ':data-qa-selector="`${panel.name}_link`"' # rubocop:disable QA/ElementWithPattern
|
||||
element :create_from_template_link, ':data-qa-selector="`${panel.name}_link`"' # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
def shown?
|
||||
has_element? :blank_project_link
|
||||
end
|
||||
|
||||
def click_blank_project_link
|
||||
click_element :blank_project_link
|
||||
end
|
||||
|
||||
def click_create_from_template_link
|
||||
click_element :create_from_template_link
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -71,12 +71,14 @@ module QA
|
|||
end
|
||||
|
||||
if @template_name
|
||||
QA::Flow::Project.go_to_create_project_from_template
|
||||
Page::Project::New.perform do |new_page|
|
||||
new_page.click_create_from_template_tab
|
||||
new_page.use_template_for_project(@template_name)
|
||||
end
|
||||
end
|
||||
|
||||
Page::Project::NewExperiment.perform(&:click_blank_project_link) if Page::Project::NewExperiment.perform(&:shown?)
|
||||
|
||||
Page::Project::New.perform do |new_page|
|
||||
new_page.choose_test_namespace
|
||||
new_page.choose_name(@name)
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
module QA
|
||||
context 'Create', :requires_admin do
|
||||
describe 'push after setting the file size limit via admin/application_settings' do
|
||||
# Note: The file size limits in this test should be greater than the limits in
|
||||
# ee/browser_ui/3_create/repository/push_rules_spec to prevent that test from
|
||||
# triggering the limit set in this test (which can happen on Staging where the
|
||||
# tests are run in parallel).
|
||||
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/218620#note_361634705
|
||||
|
||||
include Support::Api
|
||||
|
||||
before(:context) do
|
||||
|
@ -31,7 +37,7 @@ module QA
|
|||
end
|
||||
|
||||
it 'push fails when the file size is above the limit' do
|
||||
set_file_size_limit(1)
|
||||
set_file_size_limit(2)
|
||||
|
||||
retry_on_fail do
|
||||
expect { push_new_file('oversize_file_2.bin', wait_for_push: false) }
|
||||
|
@ -52,7 +58,7 @@ module QA
|
|||
output = Resource::Repository::Push.fabricate! do |p|
|
||||
p.repository_http_uri = @project.repository_http_location.uri
|
||||
p.file_name = file_name
|
||||
p.file_content = SecureRandom.random_bytes(2000000)
|
||||
p.file_content = SecureRandom.random_bytes(3000000)
|
||||
p.commit_message = commit_message
|
||||
p.new_branch = false
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ RSpec.describe Projects::TagsController do
|
|||
let(:project) { create(:project, :public, :repository) }
|
||||
let!(:release) { create(:release, project: project) }
|
||||
let!(:invalid_release) { create(:release, project: project, tag: 'does-not-exist') }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
describe 'GET index' do
|
||||
before do
|
||||
|
@ -61,4 +62,69 @@ RSpec.describe Projects::TagsController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
let(:release_description) { nil }
|
||||
let(:request) do
|
||||
post(:create, params: {
|
||||
namespace_id: project.namespace.to_param,
|
||||
project_id: project,
|
||||
tag_name: '1.0',
|
||||
ref: 'master',
|
||||
release_description: release_description
|
||||
})
|
||||
end
|
||||
|
||||
it 'creates tag' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(project.repository.find_tag('1.0')).to be_present
|
||||
end
|
||||
|
||||
# TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
|
||||
context 'when release description is set' do
|
||||
let(:release_description) { 'some release description' }
|
||||
|
||||
it 'creates tag and release' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(project.repository.find_tag('1.0')).to be_present
|
||||
|
||||
release = project.releases.find_by_tag!('1.0')
|
||||
|
||||
expect(release).to be_present
|
||||
expect(release.description).to eq(release_description)
|
||||
end
|
||||
|
||||
it 'passes the last pipeline for evidence creation', :sidekiq_inline do
|
||||
sha = project.repository.commit('master').sha
|
||||
create(:ci_empty_pipeline, sha: sha, project: project) # old pipeline
|
||||
pipeline = create(:ci_empty_pipeline, sha: sha, project: project)
|
||||
|
||||
# simulating pipeline creation by new tag
|
||||
expect_any_instance_of(Repository).to receive(:add_tag).and_wrap_original do |m, *args|
|
||||
create(:ci_empty_pipeline, sha: sha, project: project)
|
||||
m.call(*args)
|
||||
end
|
||||
|
||||
expect_next_instance_of(Releases::CreateEvidenceService, anything, pipeline: pipeline) do |service|
|
||||
expect(service).to receive(:execute).and_call_original
|
||||
end
|
||||
|
||||
request
|
||||
|
||||
release = project.releases.find_by_tag!('1.0')
|
||||
|
||||
expect(release).to be_present
|
||||
expect(release.description).to eq(release_description)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -411,6 +411,12 @@ describe('Time series component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('xAxis pointer', () => {
|
||||
it('snap is set to false by default', () => {
|
||||
expect(getChartOptions().xAxis.axisPointer.snap).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('are extended by `option`', () => {
|
||||
const mockSeriesName = 'Extra series 1';
|
||||
const mockOption = {
|
||||
|
|
36
spec/frontend/monitoring/pages/dashboard_page_spec.js
Normal file
36
spec/frontend/monitoring/pages/dashboard_page_spec.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import DashboardPage from '~/monitoring/pages/dashboard_page.vue';
|
||||
import Dashboard from '~/monitoring/components/dashboard.vue';
|
||||
import { propsData } from '../mock_data';
|
||||
|
||||
describe('monitoring/pages/dashboard_page', () => {
|
||||
let wrapper;
|
||||
|
||||
const buildWrapper = (props = {}) => {
|
||||
wrapper = shallowMount(DashboardPage, {
|
||||
propsData: {
|
||||
...props,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findDashboardComponent = () => wrapper.find(Dashboard);
|
||||
|
||||
afterEach(() => {
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
}
|
||||
});
|
||||
|
||||
it('throws errors if dashboard props are not passed', () => {
|
||||
expect(() => buildWrapper()).toThrow('Missing required prop: "dashboardProps"');
|
||||
});
|
||||
|
||||
it('renders the dashboard page with dashboard component', () => {
|
||||
buildWrapper({ dashboardProps: propsData });
|
||||
|
||||
expect(findDashboardComponent().props()).toMatchObject(propsData);
|
||||
expect(findDashboardComponent()).toExist();
|
||||
});
|
||||
});
|
|
@ -238,6 +238,16 @@ describe GitlabRoutingHelper do
|
|||
expect(gitlab_toggle_award_emoji_snippet_url(personal_snippet)).to eq("http://test.host/snippets/#{personal_snippet.id}/toggle_award_emoji")
|
||||
end
|
||||
end
|
||||
|
||||
describe '#gitlab_dashboard_snippets_path' do
|
||||
it 'returns the personal snippets dashboard path' do
|
||||
expect(gitlab_dashboard_snippets_path(personal_snippet)).to eq("/dashboard/snippets")
|
||||
end
|
||||
|
||||
it 'returns the project snippets dashboard path' do
|
||||
expect(gitlab_dashboard_snippets_path(project_snippet)).to eq("/#{project_snippet.project.full_path}/snippets")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'wikis' do
|
||||
|
|
|
@ -4,6 +4,7 @@ require 'spec_helper'
|
|||
|
||||
describe Gitlab::Auth::AuthFinders do
|
||||
include described_class
|
||||
include HttpBasicAuthHelpers
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:env) do
|
||||
|
@ -22,10 +23,7 @@ describe Gitlab::Auth::AuthFinders do
|
|||
end
|
||||
|
||||
def set_basic_auth_header(username, password)
|
||||
set_header(
|
||||
'HTTP_AUTHORIZATION',
|
||||
ActionController::HttpAuthentication::Basic.encode_credentials(username, password)
|
||||
)
|
||||
env.merge!(basic_auth_header(username, password))
|
||||
end
|
||||
|
||||
describe '#find_user_from_warden' do
|
||||
|
@ -653,6 +651,24 @@ describe Gitlab::Auth::AuthFinders do
|
|||
it_behaves_like 'job token params', described_class::JOB_TOKEN_PARAM
|
||||
it_behaves_like 'job token params', described_class::RUNNER_JOB_TOKEN_PARAM
|
||||
end
|
||||
|
||||
context 'when the job token is provided via basic auth' do
|
||||
let(:route_authentication_setting) { { job_token_allowed: :basic_auth } }
|
||||
let(:username) { Ci::Build::CI_REGISTRY_USER }
|
||||
let(:token) { job.token }
|
||||
|
||||
before do
|
||||
set_basic_auth_header(username, token)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(user) }
|
||||
|
||||
context 'credentials are provided but route setting is incorrect' do
|
||||
let(:route_authentication_setting) { { job_token_allowed: :unknown } }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_runner_from_token' do
|
||||
|
|
|
@ -67,11 +67,11 @@ describe ::Gitlab::RepoPath do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.find_project' do
|
||||
describe '.find_routes_source' do
|
||||
context 'when finding a project by its canonical path' do
|
||||
context 'when the cases match' do
|
||||
it 'returns the project and nil' do
|
||||
expect(described_class.find_project(project.full_path)).to eq([project, nil])
|
||||
expect(described_class.find_routes_source(project.full_path)).to eq([project, nil])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -81,14 +81,14 @@ describe ::Gitlab::RepoPath do
|
|||
# requests, we should accept wrongly-cased URLs because it is a pain to
|
||||
# block people's git operations and force them to update remote URLs.
|
||||
it 'returns the project and nil' do
|
||||
expect(described_class.find_project(project.full_path.upcase)).to eq([project, nil])
|
||||
expect(described_class.find_routes_source(project.full_path.upcase)).to eq([project, nil])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when finding a project via a redirect' do
|
||||
it 'returns the project and nil' do
|
||||
expect(described_class.find_project(redirect.path)).to eq([project, redirect.path])
|
||||
expect(described_class.find_routes_source(redirect.path)).to eq([project, redirect.path])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -110,6 +110,16 @@ describe ::Gitlab::RepoPath do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when path is namespace path, but has same id as project' do
|
||||
let(:namespace) { build_stubbed(:namespace, id: project.id) }
|
||||
|
||||
it 'returns nil if path is referring to namespace' do
|
||||
allow(described_class).to receive(:find_route_source).and_return(namespace)
|
||||
|
||||
expect(described_class.find_snippet("#{namespace.full_path}/snippets/#{project_snippet.id}")).to eq([nil, nil])
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns nil for snippets not associated with the project' do
|
||||
snippet = create(:project_snippet)
|
||||
|
||||
|
|
28
spec/models/concerns/route_model_query_spec.rb
Normal file
28
spec/models/concerns/route_model_query_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Route, 'RouteModelQuery', :aggregate_failures do
|
||||
let_it_be(:group1) { create(:group, path: 'Group1') }
|
||||
let_it_be(:group2) { create(:group, path: 'Group2') }
|
||||
let_it_be(:project1) { create(:project, path: 'Project1', group: group1) }
|
||||
let_it_be(:project2) { create(:project, path: 'Project2', group: group2) }
|
||||
|
||||
describe '.find_source_of_path' do
|
||||
it 'finds exact match' do
|
||||
expect(described_class.find_source_of_path('Group1')).to eq(group1)
|
||||
expect(described_class.find_source_of_path('Group2/Project2')).to eq(project2)
|
||||
|
||||
expect(described_class.find_source_of_path('GROUP1')).to be_nil
|
||||
expect(described_class.find_source_of_path('GROUP2/PROJECT2')).to be_nil
|
||||
end
|
||||
|
||||
it 'finds case insensitive match' do
|
||||
expect(described_class.find_source_of_path('Group1', case_sensitive: false)).to eq(group1)
|
||||
expect(described_class.find_source_of_path('Group2/Project2', case_sensitive: false)).to eq(project2)
|
||||
|
||||
expect(described_class.find_source_of_path('GROUP1', case_sensitive: false)).to eq(group1)
|
||||
expect(described_class.find_source_of_path('GROUP2/PROJECT2', case_sensitive: false)).to eq(project2)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5227,13 +5227,13 @@ describe Project do
|
|||
|
||||
describe '#find_or_initialize_services' do
|
||||
it 'returns only enabled services' do
|
||||
allow(Service).to receive(:available_services_names).and_return(%w[prometheus pushover])
|
||||
allow(Service).to receive(:available_services_names).and_return(%w[prometheus pushover teamcity])
|
||||
allow(subject).to receive(:disabled_services).and_return(%w[prometheus])
|
||||
|
||||
services = subject.find_or_initialize_services
|
||||
|
||||
expect(services.count).to eq 1
|
||||
expect(services).to include(PushoverService)
|
||||
expect(services.count).to eq(2)
|
||||
expect(services.map(&:title)).to eq(['JetBrains TeamCity CI', 'Pushover'])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -206,6 +206,32 @@ describe Snippet do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.find_by_id_and_project' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:project_snippet) { create(:project_snippet, project: project) }
|
||||
let_it_be(:personal_snippet) { create(:personal_snippet) }
|
||||
|
||||
context 'when project is provided' do
|
||||
it 'returns ProjectSnippet' do
|
||||
expect(described_class.find_by_id_and_project(id: project_snippet.id, project: project)).to eq(project_snippet)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is nil' do
|
||||
it 'returns PersonalSnippet' do
|
||||
expect(described_class.find_by_id_and_project(id: personal_snippet.id, project: nil)).to eq(personal_snippet)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project variable is not a Project' do
|
||||
let(:namespace) { build_stubbed(:namespace, id: project.id) }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(described_class.find_by_id_and_project(id: project_snippet.id, project: namespace)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.with_optional_visibility' do
|
||||
context 'when a visibility level is provided' do
|
||||
it 'returns snippets with the given visibility' do
|
||||
|
|
|
@ -3,20 +3,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'OAuth tokens' do
|
||||
include HttpBasicAuthHelpers
|
||||
|
||||
context 'Resource Owner Password Credentials' do
|
||||
def basic_auth_header(username, password)
|
||||
{
|
||||
'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(
|
||||
username,
|
||||
password
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
def client_basic_auth_header(client)
|
||||
basic_auth_header(client.uid, client.secret)
|
||||
end
|
||||
|
||||
def request_oauth_token(user, headers = {})
|
||||
post '/oauth/token',
|
||||
params: { username: user.username, password: user.password, grant_type: 'password' },
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe API::Terraform::State do
|
||||
include HttpBasicAuthHelpers
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:developer) { create(:user, developer_projects: [project]) }
|
||||
let_it_be(:maintainer) { create(:user, maintainer_projects: [project]) }
|
||||
|
@ -10,7 +12,7 @@ describe API::Terraform::State do
|
|||
let!(:state) { create(:terraform_state, :with_file, project: project) }
|
||||
|
||||
let(:current_user) { maintainer }
|
||||
let(:auth_header) { basic_auth_header(current_user) }
|
||||
let(:auth_header) { user_basic_auth_header(current_user) }
|
||||
let(:project_id) { project.id }
|
||||
let(:state_name) { state.name }
|
||||
let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name}" }
|
||||
|
@ -23,7 +25,7 @@ describe API::Terraform::State do
|
|||
subject(:request) { get api(state_path), headers: auth_header }
|
||||
|
||||
context 'without authentication' do
|
||||
let(:auth_header) { basic_auth_header('failing_token') }
|
||||
let(:auth_header) { basic_auth_header('bad', 'token') }
|
||||
|
||||
it 'returns 401 if user is not authenticated' do
|
||||
request
|
||||
|
@ -32,34 +34,71 @@ describe API::Terraform::State do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with maintainer permissions' do
|
||||
let(:current_user) { maintainer }
|
||||
context 'personal acceess token authentication' do
|
||||
context 'with maintainer permissions' do
|
||||
let(:current_user) { maintainer }
|
||||
|
||||
it 'returns terraform state belonging to a project of given state name' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.body).to eq(state.file.read)
|
||||
end
|
||||
|
||||
context 'for a project that does not exist' do
|
||||
let(:project_id) { '0000' }
|
||||
|
||||
it 'returns not found' do
|
||||
it 'returns terraform state belonging to a project of given state name' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.body).to eq(state.file.read)
|
||||
end
|
||||
|
||||
context 'for a project that does not exist' do
|
||||
let(:project_id) { '0000' }
|
||||
|
||||
it 'returns not found' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with developer permissions' do
|
||||
let(:current_user) { developer }
|
||||
|
||||
it 'returns forbidden if the user cannot access the state' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with developer permissions' do
|
||||
let(:current_user) { developer }
|
||||
context 'job token authentication' do
|
||||
let(:auth_header) { job_basic_auth_header(job) }
|
||||
|
||||
it 'returns forbidden if the user cannot access the state' do
|
||||
request
|
||||
context 'with maintainer permissions' do
|
||||
let(:job) { create(:ci_build, project: project, user: maintainer) }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
it 'returns terraform state belonging to a project of given state name' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.body).to eq(state.file.read)
|
||||
end
|
||||
|
||||
context 'for a project that does not exist' do
|
||||
let(:project_id) { '0000' }
|
||||
|
||||
it 'returns not found' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with developer permissions' do
|
||||
let(:job) { create(:ci_build, project: project, user: developer) }
|
||||
|
||||
it 'returns forbidden if the user cannot access the state' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -188,6 +188,7 @@ describe Releases::CreateService do
|
|||
end
|
||||
|
||||
context 'Evidence collection' do
|
||||
let(:sha) { project.repository.commit('master').sha }
|
||||
let(:params) do
|
||||
{
|
||||
name: 'New release',
|
||||
|
@ -229,6 +230,75 @@ describe Releases::CreateService do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'uses the right pipeline for evidence' do
|
||||
it 'creates evidence without pipeline if it does not exist', :sidekiq_inline do
|
||||
expect_next_instance_of(Releases::CreateEvidenceService, anything, pipeline: nil) do |service|
|
||||
expect(service).to receive(:execute).and_call_original
|
||||
end
|
||||
|
||||
expect { subject }.to change(Releases::Evidence, :count).by(1)
|
||||
end
|
||||
|
||||
it 'uses the last pipeline for evidence', :sidekiq_inline do
|
||||
create(:ci_empty_pipeline, sha: sha, project: project) # old pipeline
|
||||
pipeline = create(:ci_empty_pipeline, sha: sha, project: project)
|
||||
|
||||
expect_next_instance_of(Releases::CreateEvidenceService, anything, pipeline: pipeline) do |service|
|
||||
expect(service).to receive(:execute).and_call_original
|
||||
end
|
||||
|
||||
expect { subject }.to change(Releases::Evidence, :count).by(1)
|
||||
end
|
||||
|
||||
context 'when old evidence_pipeline is passed to service' do
|
||||
let!(:old_pipeline) { create(:ci_empty_pipeline, sha: sha, project: project) }
|
||||
let!(:new_pipeline) { create(:ci_empty_pipeline, sha: sha, project: project) }
|
||||
let(:params) do
|
||||
super().merge(
|
||||
evidence_pipeline: old_pipeline
|
||||
)
|
||||
end
|
||||
|
||||
it 'uses the old pipeline for evidence', :sidekiq_inline do
|
||||
expect_next_instance_of(Releases::CreateEvidenceService, anything, pipeline: old_pipeline) do |service|
|
||||
expect(service).to receive(:execute).and_call_original
|
||||
end
|
||||
|
||||
expect { subject }.to change(Releases::Evidence, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'pipeline is still being used for evidence if new pipeline is being created for tag', :sidekiq_inline do
|
||||
pipeline = create(:ci_empty_pipeline, sha: sha, project: project)
|
||||
|
||||
expect(project.repository).to receive(:add_tag).and_wrap_original do |m, *args|
|
||||
create(:ci_empty_pipeline, sha: sha, project: project)
|
||||
m.call(*args)
|
||||
end
|
||||
|
||||
expect_next_instance_of(Releases::CreateEvidenceService, anything, pipeline: pipeline) do |service|
|
||||
expect(service).to receive(:execute).and_call_original
|
||||
end
|
||||
|
||||
expect { subject }.to change(Releases::Evidence, :count).by(1)
|
||||
end
|
||||
|
||||
it 'uses the last pipeline for evidence when tag is already created', :sidekiq_inline do
|
||||
Tags::CreateService.new(project, user).execute('v0.1', 'master', nil)
|
||||
|
||||
expect(project.repository.find_tag('v0.1')).to be_present
|
||||
|
||||
create(:ci_empty_pipeline, sha: sha, project: project) # old pipeline
|
||||
pipeline = create(:ci_empty_pipeline, sha: sha, project: project)
|
||||
|
||||
expect_next_instance_of(Releases::CreateEvidenceService, anything, pipeline: pipeline) do |service|
|
||||
expect(service).to receive(:execute).and_call_original
|
||||
end
|
||||
|
||||
expect { subject }.to change(Releases::Evidence, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'immediate release' do
|
||||
let(:released_at) { nil }
|
||||
|
||||
|
@ -257,6 +327,8 @@ describe Releases::CreateService do
|
|||
|
||||
expect(last_release.upcoming_release?).to be_falsy
|
||||
end
|
||||
|
||||
include_examples 'uses the right pipeline for evidence'
|
||||
end
|
||||
|
||||
context 'upcoming release' do
|
||||
|
@ -287,6 +359,8 @@ describe Releases::CreateService do
|
|||
|
||||
expect(last_release.upcoming_release?).to be_truthy
|
||||
end
|
||||
|
||||
include_examples 'uses the right pipeline for evidence'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,17 +40,6 @@ module ApiHelpers
|
|||
end
|
||||
end
|
||||
|
||||
def basic_auth_header(user = nil)
|
||||
return { 'HTTP_AUTHORIZATION' => user } unless user.respond_to?(:username)
|
||||
|
||||
{
|
||||
'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(
|
||||
user.username,
|
||||
create(:personal_access_token, user: user).token
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
def expect_empty_array_response
|
||||
expect_successful_response_with_paginated_array
|
||||
expect(json_response.length).to eq(0)
|
||||
|
|
26
spec/support/helpers/http_basic_auth_helpers.rb
Normal file
26
spec/support/helpers/http_basic_auth_helpers.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module HttpBasicAuthHelpers
|
||||
def user_basic_auth_header(user)
|
||||
access_token = create(:personal_access_token, user: user)
|
||||
|
||||
basic_auth_header(user.username, access_token.token)
|
||||
end
|
||||
|
||||
def job_basic_auth_header(job)
|
||||
basic_auth_header(Ci::Build::CI_REGISTRY_USER, job.token)
|
||||
end
|
||||
|
||||
def client_basic_auth_header(client)
|
||||
basic_auth_header(client.uid, client.secret)
|
||||
end
|
||||
|
||||
def basic_auth_header(username, password)
|
||||
{
|
||||
'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(
|
||||
username,
|
||||
password
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
|
@ -3,13 +3,24 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe CreateEvidenceWorker do
|
||||
let(:release) { create(:release) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:release) { create(:release, project: project) }
|
||||
let(:pipeline) { create(:ci_empty_pipeline, sha: release.sha, project: project) }
|
||||
|
||||
# support old scheduled workers without pipeline
|
||||
it 'creates a new Evidence record' do
|
||||
expect_next_instance_of(::Releases::CreateEvidenceService, release) do |service|
|
||||
expect_next_instance_of(::Releases::CreateEvidenceService, release, pipeline: nil) do |service|
|
||||
expect(service).to receive(:execute).and_call_original
|
||||
end
|
||||
|
||||
expect { described_class.new.perform(release.id) }.to change(Releases::Evidence, :count).by(1)
|
||||
end
|
||||
|
||||
it 'creates a new Evidence record with pipeline' do
|
||||
expect_next_instance_of(::Releases::CreateEvidenceService, release, pipeline: pipeline) do |service|
|
||||
expect(service).to receive(:execute).and_call_original
|
||||
end
|
||||
|
||||
expect { described_class.new.perform(release.id, pipeline.id) }.to change(Releases::Evidence, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -840,10 +840,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.139.0.tgz#8a4874e76000e2dd7d3ed3a8967d62bed47d7ea7"
|
||||
integrity sha512-o1KAmQLYL727HodlPHkmj+d+Kdw8OIgHzlKmmPYMzeE+As2l1oz6CTilca56KqXGklOgrouC9P2puMwyX8e/6g==
|
||||
|
||||
"@gitlab/ui@16.10.0":
|
||||
version "16.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-16.10.0.tgz#845d6b655baff813ba865e1abb3c318b8cfe2f8a"
|
||||
integrity sha512-IZj38XWjAsr/kS2hYVONQQHYKzQIHUQ9h/v/qpDd24CnFV9BODf5vh+iCO+vZFejKaDTI22a0d+VRyLjUIL8ag==
|
||||
"@gitlab/ui@16.12.1":
|
||||
version "16.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-16.12.1.tgz#4d6865308596b09e36961210df7a8a489aaadb6d"
|
||||
integrity sha512-jF6/I71Q0mjHetIRDO8O4VO2KIGWKL/yH2Mdb/CqQKaEasgnc/YpuyHGCsBXqDPxCjRbXPeKp0EywICQx4agZA==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
|
Loading…
Reference in a new issue