Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
cde60c9291
commit
9de5cc2d1f
59 changed files with 626 additions and 310 deletions
|
@ -16,60 +16,32 @@ export const dateFormats = {
|
|||
// Some content is duplicated due to backward compatibility.
|
||||
// It will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/350614 in 14.9
|
||||
export const METRICS_POPOVER_CONTENT = {
|
||||
'lead-time': {
|
||||
description: s__('ValueStreamAnalytics|Median time from issue created to issue closed.'),
|
||||
},
|
||||
lead_time: {
|
||||
description: s__('ValueStreamAnalytics|Median time from issue created to issue closed.'),
|
||||
},
|
||||
'cycle-time': {
|
||||
description: s__(
|
||||
"ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed.",
|
||||
),
|
||||
},
|
||||
cycle_time: {
|
||||
description: s__(
|
||||
"ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed.",
|
||||
),
|
||||
},
|
||||
'lead-time-for-changes': {
|
||||
description: s__(
|
||||
'ValueStreamAnalytics|Median time between merge request merge and deployment to a production environment for all MRs deployed in the given time period.',
|
||||
),
|
||||
},
|
||||
lead_time_for_changes: {
|
||||
description: s__(
|
||||
'ValueStreamAnalytics|Median time between merge request merge and deployment to a production environment for all MRs deployed in the given time period.',
|
||||
),
|
||||
},
|
||||
issues: { description: s__('ValueStreamAnalytics|Number of new issues created.') },
|
||||
'new-issue': { description: s__('ValueStreamAnalytics|Number of new issues created.') },
|
||||
'new-issues': { description: s__('ValueStreamAnalytics|Number of new issues created.') },
|
||||
deploys: { description: s__('ValueStreamAnalytics|Total number of deploys to production.') },
|
||||
'deployment-frequency': {
|
||||
description: s__('ValueStreamAnalytics|Average number of deployments to production per day.'),
|
||||
},
|
||||
deployment_frequency: {
|
||||
description: s__('ValueStreamAnalytics|Average number of deployments to production per day.'),
|
||||
},
|
||||
commits: {
|
||||
description: s__('ValueStreamAnalytics|Number of commits pushed to the default branch'),
|
||||
},
|
||||
'time-to-restore-service': {
|
||||
description: s__(
|
||||
'ValueStreamAnalytics|Median time an incident was open on a production environment in the given time period.',
|
||||
),
|
||||
},
|
||||
time_to_restore_service: {
|
||||
description: s__(
|
||||
'ValueStreamAnalytics|Median time an incident was open on a production environment in the given time period.',
|
||||
),
|
||||
},
|
||||
'change-failure-rate': {
|
||||
description: s__(
|
||||
'ValueStreamAnalytics|Percentage of deployments that cause an incident in production.',
|
||||
),
|
||||
},
|
||||
change_failure_rate: {
|
||||
description: s__(
|
||||
'ValueStreamAnalytics|Percentage of deployments that cause an incident in production.',
|
||||
|
|
|
@ -209,6 +209,7 @@ export default {
|
|||
:textarea-value="timelineText"
|
||||
:restricted-tool-bar-items="$options.restrictedToolBarItems"
|
||||
markdown-docs-path=""
|
||||
:enable-preview="false"
|
||||
class="bordered-box gl-mt-0"
|
||||
>
|
||||
<template #textarea>
|
||||
|
|
|
@ -34,6 +34,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapState('editNew', [
|
||||
'isExistingRelease',
|
||||
'isFetchingRelease',
|
||||
'isUpdatingRelease',
|
||||
'fetchError',
|
||||
|
@ -50,7 +51,7 @@ export default {
|
|||
'groupMilestonesAvailable',
|
||||
'tagNotes',
|
||||
]),
|
||||
...mapGetters('editNew', ['isValid', 'isExistingRelease', 'formattedReleaseNotes']),
|
||||
...mapGetters('editNew', ['isValid', 'formattedReleaseNotes']),
|
||||
showForm() {
|
||||
return Boolean(!this.isFetchingRelease && !this.fetchError && this.release);
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { mapState } from 'vuex';
|
||||
import TagFieldExisting from './tag_field_existing.vue';
|
||||
import TagFieldNew from './tag_field_new.vue';
|
||||
|
||||
|
@ -9,7 +9,7 @@ export default {
|
|||
TagFieldNew,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('editNew', ['isExistingRelease']),
|
||||
...mapState('editNew', ['isExistingRelease']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -22,12 +22,10 @@ export default {
|
|||
// the input field. This is used to avoid showing validation
|
||||
// errors immediately when the page loads.
|
||||
isInputDirty: false,
|
||||
|
||||
showCreateFrom: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState('editNew', ['projectId', 'release', 'createFrom']),
|
||||
...mapState('editNew', ['projectId', 'release', 'createFrom', 'showCreateFrom']),
|
||||
...mapGetters('editNew', ['validationErrors']),
|
||||
tagName: {
|
||||
get() {
|
||||
|
@ -40,7 +38,7 @@ export default {
|
|||
// When this is called, the selection originated from the
|
||||
// dropdown list of existing tag names, so we know the tag
|
||||
// already exists and don't need to show the "create from" input
|
||||
this.showCreateFrom = false;
|
||||
this.updateShowCreateFrom(false);
|
||||
},
|
||||
},
|
||||
createFromModel: {
|
||||
|
@ -70,7 +68,12 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('editNew', ['updateReleaseTagName', 'updateCreateFrom', 'fetchTagNotes']),
|
||||
...mapActions('editNew', [
|
||||
'updateReleaseTagName',
|
||||
'updateCreateFrom',
|
||||
'fetchTagNotes',
|
||||
'updateShowCreateFrom',
|
||||
]),
|
||||
markInputAsDirty() {
|
||||
this.isInputDirty = true;
|
||||
},
|
||||
|
@ -80,7 +83,7 @@ export default {
|
|||
// This method is called when the user selects the "create tag"
|
||||
// option, so the tag does not already exist. Because of this,
|
||||
// we need to show the "create from" input.
|
||||
this.showCreateFrom = true;
|
||||
this.updateShowCreateFrom(true);
|
||||
},
|
||||
shouldShowCreateTagOption(isLoading, matches, query) {
|
||||
// Show the "create tag" option if:
|
||||
|
|
|
@ -11,7 +11,7 @@ export default () => {
|
|||
|
||||
const store = createStore({
|
||||
modules: {
|
||||
editNew: createEditNewModule(el.dataset),
|
||||
editNew: createEditNewModule({ ...el.dataset, isExistingRelease: true }),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ export default () => {
|
|||
|
||||
const store = createStore({
|
||||
modules: {
|
||||
editNew: createEditNewModule(el.dataset),
|
||||
editNew: createEditNewModule({ ...el.dataset, isExistingRelease: false }),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import { gqClient, convertOneReleaseGraphQLResponse } from '~/releases/util';
|
|||
|
||||
import * as types from './mutation_types';
|
||||
|
||||
export const initializeRelease = ({ commit, dispatch, getters }) => {
|
||||
if (getters.isExistingRelease) {
|
||||
export const initializeRelease = ({ commit, dispatch, state }) => {
|
||||
if (state.isExistingRelease) {
|
||||
// When editing an existing release,
|
||||
// fetch the release object from the API
|
||||
return dispatch('fetchRelease');
|
||||
|
@ -53,6 +53,9 @@ export const updateReleaseTagName = ({ commit }, tagName) =>
|
|||
export const updateCreateFrom = ({ commit }, createFrom) =>
|
||||
commit(types.UPDATE_CREATE_FROM, createFrom);
|
||||
|
||||
export const updateShowCreateFrom = ({ commit }, showCreateFrom) =>
|
||||
commit(types.UPDATE_SHOW_CREATE_FROM, showCreateFrom);
|
||||
|
||||
export const updateReleaseTitle = ({ commit }, title) => commit(types.UPDATE_RELEASE_TITLE, title);
|
||||
|
||||
export const updateReleaseNotes = ({ commit }, notes) => commit(types.UPDATE_RELEASE_NOTES, notes);
|
||||
|
@ -88,10 +91,10 @@ export const receiveSaveReleaseSuccess = ({ commit }, urlToRedirectTo) => {
|
|||
redirectTo(urlToRedirectTo);
|
||||
};
|
||||
|
||||
export const saveRelease = ({ commit, dispatch, getters }) => {
|
||||
export const saveRelease = ({ commit, dispatch, state }) => {
|
||||
commit(types.REQUEST_SAVE_RELEASE);
|
||||
|
||||
dispatch(getters.isExistingRelease ? 'updateRelease' : 'createRelease');
|
||||
dispatch(state.isExistingRelease ? 'updateRelease' : 'createRelease');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,14 +3,6 @@ import { s__ } from '~/locale';
|
|||
import { hasContent } from '~/lib/utils/text_utility';
|
||||
import { getDuplicateItemsFromArray } from '~/lib/utils/array_utility';
|
||||
|
||||
/**
|
||||
* @returns {Boolean} `true` if the app is editing an existing release.
|
||||
* `false` if the app is creating a new release.
|
||||
*/
|
||||
export const isExistingRelease = (state) => {
|
||||
return Boolean(state.tagName);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} link The link to test
|
||||
* @returns {Boolean} `true` if the release link is empty, i.e. it has
|
||||
|
|
|
@ -6,6 +6,7 @@ export const RECEIVE_RELEASE_ERROR = 'RECEIVE_RELEASE_ERROR';
|
|||
|
||||
export const UPDATE_RELEASE_TAG_NAME = 'UPDATE_RELEASE_TAG_NAME';
|
||||
export const UPDATE_CREATE_FROM = 'UPDATE_CREATE_FROM';
|
||||
export const UPDATE_SHOW_CREATE_FROM = 'UPDATE_SHOW_CREATE_FROM';
|
||||
export const UPDATE_RELEASE_TITLE = 'UPDATE_RELEASE_TITLE';
|
||||
export const UPDATE_RELEASE_NOTES = 'UPDATE_RELEASE_NOTES';
|
||||
export const UPDATE_RELEASE_MILESTONES = 'UPDATE_RELEASE_MILESTONES';
|
||||
|
|
|
@ -9,7 +9,7 @@ const findReleaseLink = (release, id) => {
|
|||
export default {
|
||||
[types.INITIALIZE_EMPTY_RELEASE](state) {
|
||||
state.release = {
|
||||
tagName: null,
|
||||
tagName: state.tagName,
|
||||
name: '',
|
||||
description: '',
|
||||
milestones: [],
|
||||
|
@ -42,6 +42,9 @@ export default {
|
|||
[types.UPDATE_CREATE_FROM](state, createFrom) {
|
||||
state.createFrom = createFrom;
|
||||
},
|
||||
[types.UPDATE_SHOW_CREATE_FROM](state, showCreateFrom) {
|
||||
state.showCreateFrom = showCreateFrom;
|
||||
},
|
||||
[types.UPDATE_RELEASE_TITLE](state, title) {
|
||||
state.release.name = title;
|
||||
},
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export default ({
|
||||
isExistingRelease,
|
||||
projectId,
|
||||
groupId,
|
||||
groupMilestonesAvailable = false,
|
||||
|
@ -15,6 +16,7 @@ export default ({
|
|||
tagName = null,
|
||||
defaultBranch = null,
|
||||
}) => ({
|
||||
isExistingRelease,
|
||||
projectId,
|
||||
groupId,
|
||||
groupMilestonesAvailable: Boolean(groupMilestonesAvailable),
|
||||
|
@ -30,9 +32,10 @@ export default ({
|
|||
|
||||
/**
|
||||
* The name of the tag associated with the release, provided by the backend.
|
||||
* When creating a new release, this value is null.
|
||||
* When creating a new release, this is the default from the URL
|
||||
*/
|
||||
tagName,
|
||||
showCreateFrom: !tagName,
|
||||
|
||||
defaultBranch,
|
||||
createFrom: defaultBranch,
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
|
||||
&.class_ {
|
||||
color: var(--color-hljs-class);
|
||||
|
||||
&.inherited__ {
|
||||
color: var(--color-hljs-variable);
|
||||
}
|
||||
}
|
||||
|
||||
&.function_ {
|
||||
|
|
|
@ -116,6 +116,7 @@ $monokai-gh: #75715e;
|
|||
@include hljs-override('bullet', $monokai-n);
|
||||
@include hljs-override('subst', $monokai-p);
|
||||
@include hljs-override('symbol', $monokai-ss);
|
||||
@include hljs-override('title.class_.inherited__', $monokai-no);
|
||||
|
||||
// Line numbers
|
||||
.file-line-num {
|
||||
|
|
|
@ -119,6 +119,7 @@ $solarized-dark-il: #2aa198;
|
|||
@include hljs-override('bullet', $solarized-dark-n);
|
||||
@include hljs-override('subst', $solarized-dark-p);
|
||||
@include hljs-override('symbol', $solarized-dark-ni);
|
||||
@include hljs-override('title.class_.inherited__', $solarized-dark-no);
|
||||
|
||||
// Line numbers
|
||||
.file-line-num {
|
||||
|
|
|
@ -106,6 +106,7 @@ $solarized-light-il: #2aa198;
|
|||
}
|
||||
|
||||
.code.solarized-light {
|
||||
@include hljs-override('title.class_.inherited__', $solarized-light-no);
|
||||
// Line numbers
|
||||
.file-line-num {
|
||||
@include line-link($black, 'link');
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
@import '../white_base';
|
||||
|
||||
@include conflict-colors('white');
|
||||
@include hljs-override('variable', $white-nv);
|
||||
@include hljs-override('symbol', $white-ss);
|
||||
@include hljs-override('title.class_.inherited__', $white-no);
|
||||
}
|
||||
|
||||
:root {
|
||||
|
|
|
@ -32,7 +32,8 @@ module Mutations
|
|||
def create_note_params(noteable, args)
|
||||
super(noteable, args).merge({
|
||||
type: 'DiffNote',
|
||||
position: position(noteable, args)
|
||||
position: position(noteable, args),
|
||||
merge_request_diff_head_sha: args[:position][:head_sha]
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ module ReleasesHelper
|
|||
|
||||
def data_for_new_release_page
|
||||
new_edit_pages_shared_data.merge(
|
||||
tag_name: params[:tag_name],
|
||||
default_branch: @project.default_branch,
|
||||
releases_page_path: project_releases_path(@project)
|
||||
)
|
||||
|
|
|
@ -408,14 +408,6 @@ class Issue < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
# Returns boolean if a related branch exists for the current issue
|
||||
# ignores merge requests branchs
|
||||
def has_related_branch?
|
||||
project.repository.branch_names.any? do |branch|
|
||||
/\A#{iid}-(?!\d+-stable)/i =~ branch
|
||||
end
|
||||
end
|
||||
|
||||
# To allow polymorphism with MergeRequest.
|
||||
def source_project
|
||||
project
|
||||
|
|
|
@ -52,8 +52,6 @@ module ServicePing
|
|||
ServicePing::DevopsReport.new(response).execute
|
||||
end
|
||||
|
||||
return unless Feature.enabled?(:measure_service_ping_metric_collection)
|
||||
|
||||
submit_payload({ metadata: { metrics: metrics_collection_time(usage_data) } }, path: METADATA_PATH)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: measure_service_ping_metric_collection
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82607
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/358128
|
||||
milestone: '15.0'
|
||||
type: development
|
||||
group: group::product intelligence
|
||||
default_enabled: true
|
|
@ -1,12 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CleanupBackfillDraftStatusOnMergeRequests < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
MIGRATION = 'BackfillDraftStatusOnMergeRequests'
|
||||
|
||||
def up
|
||||
finalize_background_migration(MIGRATION)
|
||||
# no-op
|
||||
#
|
||||
# moved to post-deployment migration:
|
||||
# db/post_migrate/20220713133515_cleanup_backfill_draft_statuses_on_merge_requests.rb
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CleanupBackfillDraftStatusesOnMergeRequests < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
MIGRATION = 'BackfillDraftStatusOnMergeRequests'
|
||||
|
||||
def up
|
||||
finalize_background_migration(MIGRATION)
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
1
db/schema_migrations/20220713133515
Normal file
1
db/schema_migrations/20220713133515
Normal file
|
@ -0,0 +1 @@
|
|||
4c0f48149987c821c8666df7a1d9e9780146d356ffb9539572d5a3c77038e237
|
|
@ -113,11 +113,8 @@ sequenceDiagram
|
|||
|
||||
1. Finally, the timing metadata information that is used for diagnostic purposes is submitted to the Versions application. It consists of a list of metric identifiers and the time it took to calculate the metrics:
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37911) in GitLab 15.0 [with a flag(../../user/feature_flags.md), enabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named `measure_service_ping_metric_collection`.
|
||||
On GitLab.com, this feature is available.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37911) in GitLab 15.0 [with a flag(../../user/feature_flags.md), enabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/295289) in GitLab 15.2. [Feature flag `measure_service_ping_metric_collection`](https://gitlab.com/gitlab-org/gitlab/-/issues/358128) removed.
|
||||
|
||||
```ruby
|
||||
{"metadata"=>
|
||||
|
@ -161,25 +158,6 @@ We also collect metrics specific to [Geo](../../administration/geo/index.md) sec
|
|||
]
|
||||
```
|
||||
|
||||
### Enable or disable service ping metadata reporting
|
||||
|
||||
Service Ping timing metadata reporting is under development but ready for production use.
|
||||
It is deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
|
||||
can opt to disable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:measure_service_ping_metric_collection)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:measure_service_ping_metric_collection)
|
||||
```
|
||||
|
||||
## Implementing Service Ping
|
||||
|
||||
See the [implement Service Ping](implement.md) guide.
|
||||
|
|
|
@ -28,7 +28,7 @@ module ContainerRegistry
|
|||
end
|
||||
|
||||
def self.deduplicated_size(path)
|
||||
with_dummy_client(token_config: { type: :nested_repositories_token, path: path }) do |client|
|
||||
with_dummy_client(token_config: { type: :nested_repositories_token, path: path&.downcase }) do |client|
|
||||
client.repository_details(path, sizing: :self_with_descendants)['size_bytes']
|
||||
end
|
||||
end
|
||||
|
|
|
@ -86,13 +86,15 @@ module Gitlab
|
|||
|
||||
create_labels
|
||||
|
||||
issue_type_id = WorkItems::Type.default_issue_type.id
|
||||
|
||||
client.issues(repo).each do |issue|
|
||||
import_issue(issue)
|
||||
import_issue(issue, issue_type_id)
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def import_issue(issue)
|
||||
def import_issue(issue, issue_type_id)
|
||||
description = ''
|
||||
description += @formatter.author_line(issue.author) unless find_user_id(issue.author)
|
||||
description += issue.description
|
||||
|
@ -108,6 +110,7 @@ module Gitlab
|
|||
author_id: gitlab_user_id(project, issue.author),
|
||||
namespace_id: project.project_namespace_id,
|
||||
milestone: milestone,
|
||||
work_item_type_id: issue_type_id,
|
||||
created_at: issue.created_at,
|
||||
updated_at: issue.updated_at
|
||||
)
|
||||
|
|
|
@ -651,8 +651,6 @@ module Gitlab
|
|||
end
|
||||
|
||||
def with_duration
|
||||
return yield unless Feature.enabled?(:measure_service_ping_metric_collection)
|
||||
|
||||
result = nil
|
||||
duration = Benchmark.realtime do
|
||||
result = yield
|
||||
|
|
|
@ -17,9 +17,16 @@ namespace :contracts do
|
|||
)
|
||||
end
|
||||
|
||||
Pact::VerificationTask.new(:get_pipeline_header_data) do |pact|
|
||||
pact.uri(
|
||||
"#{contracts}/contracts/project/pipeline/show/pipelines#show-get_pipeline_header_data.json",
|
||||
pact_helper: "#{provider}/pact_helpers/project/pipeline/get_pipeline_header_data_helper.rb"
|
||||
)
|
||||
end
|
||||
|
||||
desc 'Run all pipeline contract tests'
|
||||
task 'test:pipelines', :contract_mr do |_t, arg|
|
||||
errors = %w[get_list_project_pipelines].each_with_object([]) do |task, err|
|
||||
errors = %w[get_list_project_pipelines get_pipeline_header_data].each_with_object([]) do |task, err|
|
||||
Rake::Task["contracts:pipelines:pact:verify:#{task}"].execute
|
||||
rescue StandardError, SystemExit
|
||||
err << "contracts:pipelines:pact:verify:#{task}"
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
|
||||
import { Matchers } from '@pact-foundation/pact';
|
||||
import {
|
||||
JOB_STATUSES,
|
||||
PIPELINE_GROUPS,
|
||||
PIPELINE_STATUSES,
|
||||
PIPELINE_TEXTS,
|
||||
URL,
|
||||
URL_PATH,
|
||||
} from '../../../helpers/common_regex_patterns';
|
||||
|
||||
const body = {
|
||||
data: {
|
||||
project: {
|
||||
id: Matchers.string('gid://gitlab/Project/278964'),
|
||||
pipeline: {
|
||||
id: Matchers.string('gid://gitlab/Ci::Pipeline/577266584'),
|
||||
iid: Matchers.string('1175084'),
|
||||
status: Matchers.term({
|
||||
matcher: JOB_STATUSES,
|
||||
generate: 'RUNNING',
|
||||
}),
|
||||
retryable: Matchers.boolean(false),
|
||||
cancelable: Matchers.boolean(true),
|
||||
userPermissions: {
|
||||
destroyPipeline: Matchers.boolean(false),
|
||||
updatePipeline: Matchers.boolean(true),
|
||||
},
|
||||
detailedStatus: {
|
||||
id: Matchers.string('running-577266584-577266584'),
|
||||
detailsPath: Matchers.term({
|
||||
matcher: URL_PATH,
|
||||
generate: '/gitlab-org/gitlab/-/pipelines/577266584',
|
||||
}),
|
||||
icon: Matchers.term({
|
||||
matcher: PIPELINE_STATUSES,
|
||||
generate: 'status_running',
|
||||
}),
|
||||
group: Matchers.term({
|
||||
matcher: PIPELINE_GROUPS,
|
||||
generate: 'running',
|
||||
}),
|
||||
text: Matchers.term({
|
||||
matcher: PIPELINE_TEXTS,
|
||||
generate: 'running',
|
||||
}),
|
||||
},
|
||||
createdAt: Matchers.iso8601DateTime('2022-06-30T16:58:59Z'),
|
||||
user: {
|
||||
id: Matchers.string('gid://gitlab/User/194645'),
|
||||
name: Matchers.string('John Doe'),
|
||||
username: Matchers.string('jdoe'),
|
||||
webPath: Matchers.term({
|
||||
matcher: URL_PATH,
|
||||
generate: '/gitlab-bot',
|
||||
}),
|
||||
webUrl: Matchers.term({
|
||||
matcher: URL,
|
||||
generate: 'https://gitlab.com/gitlab-bot',
|
||||
}),
|
||||
email: null,
|
||||
avatarUrl: Matchers.term({
|
||||
matcher: URL,
|
||||
generate:
|
||||
'https://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon',
|
||||
}),
|
||||
status: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const PipelineHeaderData = {
|
||||
body: Matchers.extractPayload(body),
|
||||
|
||||
success: {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
body,
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
path: '/api/graphql',
|
||||
},
|
||||
|
||||
variables: {
|
||||
fullPath: 'gitlab-org/gitlab-qa',
|
||||
iid: 1,
|
||||
},
|
||||
};
|
||||
|
||||
export { PipelineHeaderData };
|
||||
|
||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
@ -16,5 +16,9 @@ export const PIPELINE_STATUSES =
|
|||
export const PIPELINE_TEXTS =
|
||||
'^(canceled|created|delayed|failed|manual|passed|pending|preparing|running|skipped|waiting)$';
|
||||
|
||||
// Jobs
|
||||
export const JOB_STATUSES =
|
||||
'^(CANCELED|CREATED|FAILED|MANUAL|PENDING|PREPARING|RUNNING|SCHEDULED|SKIPPED|SUCCESS|WAITING_FOR_RESOURCE)$';
|
||||
|
||||
// Users
|
||||
export const USER_STATES = '^(active|blocked)$';
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { readFile } from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
export async function extractGraphQLQuery(fileLocation) {
|
||||
const file = path.resolve(__dirname, '..', '..', '..', '..', fileLocation);
|
||||
|
||||
return readFile(file, { encoding: 'UTF-8' });
|
||||
}
|
25
spec/contracts/consumer/resources/graphql/pipelines.js
Normal file
25
spec/contracts/consumer/resources/graphql/pipelines.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import axios from 'axios';
|
||||
|
||||
import { extractGraphQLQuery } from '../../helpers/graphql_query_extractor';
|
||||
|
||||
export async function getPipelineHeaderDataRequest(endpoint) {
|
||||
const { url } = endpoint;
|
||||
const query = await extractGraphQLQuery(
|
||||
'app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql',
|
||||
);
|
||||
const graphqlQuery = {
|
||||
query,
|
||||
variables: {
|
||||
fullPath: 'gitlab-org/gitlab-qa',
|
||||
iid: 1,
|
||||
},
|
||||
};
|
||||
|
||||
return axios({
|
||||
baseURL: url,
|
||||
url: '/api/graphql',
|
||||
method: 'POST',
|
||||
headers: { Accept: '*/*' },
|
||||
data: graphqlQuery,
|
||||
});
|
||||
}
|
53
spec/contracts/consumer/specs/project/pipeline/show.spec.js
Normal file
53
spec/contracts/consumer/specs/project/pipeline/show.spec.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
|
||||
import { pactWith } from 'jest-pact';
|
||||
import { GraphQLInteraction } from '@pact-foundation/pact';
|
||||
|
||||
import { extractGraphQLQuery } from '../../../helpers/graphql_query_extractor';
|
||||
|
||||
import { PipelineHeaderData } from '../../../fixtures/project/pipeline/get_pipeline_header_data.fixture';
|
||||
import { getPipelineHeaderDataRequest } from '../../../resources/graphql/pipelines';
|
||||
|
||||
const CONSUMER_NAME = 'Pipelines#show';
|
||||
const CONSUMER_LOG = '../logs/consumer.log';
|
||||
const CONTRACT_DIR = '../contracts/project/pipeline/show';
|
||||
const PROVIDER_NAME = 'GET pipeline header data';
|
||||
|
||||
// GraphQL query: getPipelineHeaderData
|
||||
pactWith(
|
||||
{
|
||||
consumer: CONSUMER_NAME,
|
||||
provider: PROVIDER_NAME,
|
||||
log: CONSUMER_LOG,
|
||||
dir: CONTRACT_DIR,
|
||||
},
|
||||
|
||||
(provider) => {
|
||||
describe(PROVIDER_NAME, () => {
|
||||
beforeEach(async () => {
|
||||
const query = await extractGraphQLQuery(
|
||||
'app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql',
|
||||
);
|
||||
const graphqlQuery = new GraphQLInteraction()
|
||||
.given('a pipeline for a project exists')
|
||||
.uponReceiving('a request for the pipeline header data')
|
||||
.withQuery(query)
|
||||
.withRequest(PipelineHeaderData.request)
|
||||
.withVariables(PipelineHeaderData.variables)
|
||||
.willRespondWith(PipelineHeaderData.success);
|
||||
|
||||
provider.addInteraction(graphqlQuery);
|
||||
});
|
||||
|
||||
it('returns a successful body', async () => {
|
||||
const pipelineHeaderData = await getPipelineHeaderDataRequest({
|
||||
url: provider.mockService.baseUrl,
|
||||
});
|
||||
|
||||
expect(pipelineHeaderData.data).toEqual(PipelineHeaderData.body);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
@ -0,0 +1,152 @@
|
|||
{
|
||||
"consumer": {
|
||||
"name": "Pipelines#show"
|
||||
},
|
||||
"provider": {
|
||||
"name": "GET pipeline header data"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"description": "a request for the pipeline header data",
|
||||
"providerState": "a pipeline for a project exists",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/graphql",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": {
|
||||
"query": "query getPipelineHeaderData($fullPath: ID!, $iid: ID!) {\n project(fullPath: $fullPath) {\n id\n pipeline(iid: $iid) {\n id\n iid\n status\n retryable\n cancelable\n userPermissions {\n destroyPipeline\n updatePipeline\n }\n detailedStatus {\n id\n detailsPath\n icon\n group\n text\n }\n createdAt\n user {\n id\n name\n username\n webPath\n webUrl\n email\n avatarUrl\n status {\n message\n emoji\n }\n }\n }\n }\n}\n",
|
||||
"variables": {
|
||||
"fullPath": "gitlab-org/gitlab-qa",
|
||||
"iid": 1
|
||||
}
|
||||
},
|
||||
"matchingRules": {
|
||||
"$.body.query": {
|
||||
"match": "regex",
|
||||
"regex": "query\\s*getPipelineHeaderData\\(\\$fullPath:\\s*ID!,\\s*\\$iid:\\s*ID!\\)\\s*\\{\\s*project\\(fullPath:\\s*\\$fullPath\\)\\s*\\{\\s*id\\s*pipeline\\(iid:\\s*\\$iid\\)\\s*\\{\\s*id\\s*iid\\s*status\\s*retryable\\s*cancelable\\s*userPermissions\\s*\\{\\s*destroyPipeline\\s*updatePipeline\\s*\\}\\s*detailedStatus\\s*\\{\\s*id\\s*detailsPath\\s*icon\\s*group\\s*text\\s*\\}\\s*createdAt\\s*user\\s*\\{\\s*id\\s*name\\s*username\\s*webPath\\s*webUrl\\s*email\\s*avatarUrl\\s*status\\s*\\{\\s*message\\s*emoji\\s*\\}\\s*\\}\\s*\\}\\s*\\}\\s*\\}\\s*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"Content-Type": "application/json; charset=utf-8"
|
||||
},
|
||||
"body": {
|
||||
"data": {
|
||||
"project": {
|
||||
"id": "gid://gitlab/Project/278964",
|
||||
"pipeline": {
|
||||
"id": "gid://gitlab/Ci::Pipeline/577266584",
|
||||
"iid": "1175084",
|
||||
"status": "RUNNING",
|
||||
"retryable": false,
|
||||
"cancelable": true,
|
||||
"userPermissions": {
|
||||
"destroyPipeline": false,
|
||||
"updatePipeline": true
|
||||
},
|
||||
"detailedStatus": {
|
||||
"id": "running-577266584-577266584",
|
||||
"detailsPath": "/gitlab-org/gitlab/-/pipelines/577266584",
|
||||
"icon": "status_running",
|
||||
"group": "running",
|
||||
"text": "running"
|
||||
},
|
||||
"createdAt": "2022-06-30T16:58:59Z",
|
||||
"user": {
|
||||
"id": "gid://gitlab/User/194645",
|
||||
"name": "John Doe",
|
||||
"username": "jdoe",
|
||||
"webPath": "/gitlab-bot",
|
||||
"webUrl": "https://gitlab.com/gitlab-bot",
|
||||
"email": null,
|
||||
"avatarUrl": "https://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon",
|
||||
"status": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"matchingRules": {
|
||||
"$.body.data.project.id": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.id": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.iid": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.status": {
|
||||
"match": "regex",
|
||||
"regex": "^(CANCELED|CREATED|FAILED|MANUAL|PENDING|PREPARING|RUNNING|SCHEDULED|SKIPPED|SUCCESS|WAITING_FOR_RESOURCE)$"
|
||||
},
|
||||
"$.body.data.project.pipeline.retryable": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.cancelable": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.userPermissions.destroyPipeline": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.userPermissions.updatePipeline": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.detailedStatus.id": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.detailedStatus.detailsPath": {
|
||||
"match": "regex",
|
||||
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
|
||||
},
|
||||
"$.body.data.project.pipeline.detailedStatus.icon": {
|
||||
"match": "regex",
|
||||
"regex": "^status_(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|warning)$"
|
||||
},
|
||||
"$.body.data.project.pipeline.detailedStatus.group": {
|
||||
"match": "regex",
|
||||
"regex": "^(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|success_warning|waiting-for-resource)$"
|
||||
},
|
||||
"$.body.data.project.pipeline.detailedStatus.text": {
|
||||
"match": "regex",
|
||||
"regex": "^(canceled|created|delayed|failed|manual|passed|pending|preparing|running|skipped|waiting)$"
|
||||
},
|
||||
"$.body.data.project.pipeline.createdAt": {
|
||||
"match": "regex",
|
||||
"regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z)$"
|
||||
},
|
||||
"$.body.data.project.pipeline.user.id": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.user.name": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.user.username": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.data.project.pipeline.user.webPath": {
|
||||
"match": "regex",
|
||||
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
|
||||
},
|
||||
"$.body.data.project.pipeline.user.webUrl": {
|
||||
"match": "regex",
|
||||
"regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
|
||||
},
|
||||
"$.body.data.project.pipeline.user.avatarUrl": {
|
||||
"match": "regex",
|
||||
"regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"pactSpecification": {
|
||||
"version": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../../spec_helper'
|
||||
require_relative '../../../states/project/pipeline/pipeline_state'
|
||||
|
||||
module Provider
|
||||
module GetPipelinesHeaderDataHelper
|
||||
Pact.service_provider "GET pipeline header data" do
|
||||
app { Environments::Test.app }
|
||||
|
||||
honours_pact_with 'Pipelines#show' do
|
||||
pact_uri '../contracts/project/pipeline/show/pipelines#show-get_project_pipeline_header_data.json'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Pact.provider_states_for "Pipelines#show" do
|
||||
provider_state "a pipeline for a project exists" do
|
||||
set_up do
|
||||
user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
|
||||
namespace = create(:namespace, name: 'gitlab-org')
|
||||
project = create(:project, :repository, name: 'gitlab-qa', namespace: namespace, creator: user)
|
||||
scheduled_job = create(:ci_build, :scheduled)
|
||||
manual_job = create(:ci_build, :manual)
|
||||
|
||||
project.add_maintainer(user)
|
||||
|
||||
create(
|
||||
:ci_pipeline,
|
||||
:with_job,
|
||||
:success,
|
||||
iid: 1,
|
||||
project: project,
|
||||
user: user,
|
||||
duration: 10,
|
||||
finished_at: '2022-06-01T02:47:31.432Z',
|
||||
builds: [scheduled_job, manual_job]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -111,6 +111,27 @@ RSpec.describe 'User creates release', :js do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when tag name supplied in the parameters' do
|
||||
let(:new_page_url) { new_project_release_path(project, tag_name: 'v1.1.0') }
|
||||
|
||||
it 'creates release with preselected tag' do
|
||||
page.within '[data-testid="tag-name-field"]' do
|
||||
expect(page).to have_text('v1.1.0')
|
||||
end
|
||||
|
||||
expect(page).not_to have_selector('[data-testid="create-from-field"]')
|
||||
|
||||
fill_release_title("test release")
|
||||
click_button('Create release')
|
||||
|
||||
wait_for_all_requests
|
||||
|
||||
release = project.releases.last
|
||||
|
||||
expect(release.tag).to eq('v1.1.0')
|
||||
end
|
||||
end
|
||||
|
||||
def fill_out_form_and_submit
|
||||
select_new_tag_name(tag_name)
|
||||
|
||||
|
|
16
spec/fixtures/gitlab/git/gitattributes
vendored
Normal file
16
spec/fixtures/gitlab/git/gitattributes
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
# This is a comment, it should be ignored.
|
||||
|
||||
*.txt text
|
||||
*.jpg -text
|
||||
*.sh eol=lf gitlab-language=shell
|
||||
*.haml.* gitlab-language=haml
|
||||
foo/bar.* foo
|
||||
*.cgi key=value?p1=v1&p2=v2
|
||||
/*.png gitlab-language=png
|
||||
*.binary binary
|
||||
/custom-highlighting/*.gitlab-custom gitlab-language=ruby
|
||||
/custom-highlighting/*.gitlab-cgi gitlab-language=erb?parent=json
|
||||
|
||||
# This uses a tab instead of spaces to ensure the parser also supports this.
|
||||
*.md gitlab-language=markdown
|
||||
bla/bla.txt
|
BIN
spec/fixtures/gitlab/git/gitattributes_invalid
vendored
Normal file
BIN
spec/fixtures/gitlab/git/gitattributes_invalid
vendored
Normal file
Binary file not shown.
|
@ -30,6 +30,7 @@ describe('Release edit/new component', () => {
|
|||
const factory = async ({ featureFlags = {}, store: storeUpdates = {} } = {}) => {
|
||||
state = {
|
||||
release,
|
||||
isExistingRelease: true,
|
||||
markdownDocsPath: 'path/to/markdown/docs',
|
||||
releasesPagePath,
|
||||
projectId: '8',
|
||||
|
@ -46,7 +47,6 @@ describe('Release edit/new component', () => {
|
|||
|
||||
getters = {
|
||||
isValid: () => true,
|
||||
isExistingRelease: () => true,
|
||||
validationErrors: () => ({
|
||||
assets: {
|
||||
links: [],
|
||||
|
@ -206,9 +206,7 @@ describe('Release edit/new component', () => {
|
|||
store: {
|
||||
modules: {
|
||||
editNew: {
|
||||
getters: {
|
||||
isExistingRelease: () => false,
|
||||
},
|
||||
state: { isExistingRelease: false },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -9,14 +9,14 @@ describe('releases/components/tag_field', () => {
|
|||
let store;
|
||||
let wrapper;
|
||||
|
||||
const createComponent = ({ tagName }) => {
|
||||
const createComponent = ({ isExistingRelease }) => {
|
||||
store = createStore({
|
||||
modules: {
|
||||
editNew: createEditNewModule({}),
|
||||
},
|
||||
});
|
||||
|
||||
store.state.editNew.tagName = tagName;
|
||||
store.state.editNew.isExistingRelease = isExistingRelease;
|
||||
|
||||
wrapper = shallowMount(TagField, { store });
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ describe('releases/components/tag_field', () => {
|
|||
|
||||
describe('when an existing release is being edited', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ tagName: 'v1.0' });
|
||||
createComponent({ isExistingRelease: true });
|
||||
});
|
||||
|
||||
it('renders the TagFieldExisting component', () => {
|
||||
|
@ -45,7 +45,7 @@ describe('releases/components/tag_field', () => {
|
|||
|
||||
describe('when a new release is being created', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ tagName: null });
|
||||
createComponent({ isExistingRelease: false });
|
||||
});
|
||||
|
||||
it('renders the TagFieldNew component', () => {
|
||||
|
|
|
@ -37,19 +37,15 @@ describe('Release edit/new actions', () => {
|
|||
let error;
|
||||
|
||||
const setupState = (updates = {}) => {
|
||||
const getters = {
|
||||
isExistingRelease: true,
|
||||
};
|
||||
|
||||
state = {
|
||||
...createState({
|
||||
projectId: '18',
|
||||
isExistingRelease: true,
|
||||
tagName: releaseResponse.tag_name,
|
||||
releasesPagePath: 'path/to/releases/page',
|
||||
markdownDocsPath: 'path/to/markdown/docs',
|
||||
markdownPreviewPath: 'path/to/markdown/preview',
|
||||
}),
|
||||
...getters,
|
||||
...updates,
|
||||
};
|
||||
};
|
||||
|
@ -186,6 +182,15 @@ describe('Release edit/new actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('updateShowCreateFrom', () => {
|
||||
it(`commits ${types.UPDATE_SHOW_CREATE_FROM} with the updated ref`, () => {
|
||||
const newRef = 'my-feature-branch';
|
||||
return testAction(actions.updateShowCreateFrom, newRef, state, [
|
||||
{ type: types.UPDATE_SHOW_CREATE_FROM, payload: newRef },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateReleaseTitle', () => {
|
||||
it(`commits ${types.UPDATE_RELEASE_TITLE} with the updated release title`, () => {
|
||||
const newTitle = 'The new release title';
|
||||
|
|
|
@ -2,20 +2,6 @@ import { s__ } from '~/locale';
|
|||
import * as getters from '~/releases/stores/modules/edit_new/getters';
|
||||
|
||||
describe('Release edit/new getters', () => {
|
||||
describe('isExistingRelease', () => {
|
||||
it('returns true if the release is an existing release that already exists in the database', () => {
|
||||
const state = { tagName: 'test-tag-name' };
|
||||
|
||||
expect(getters.isExistingRelease(state)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false if the release is a new release that has not yet been saved to the database', () => {
|
||||
const state = { tagName: null };
|
||||
|
||||
expect(getters.isExistingRelease(state)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('releaseLinksToCreate', () => {
|
||||
it("returns an empty array if state.release doesn't exist", () => {
|
||||
const state = {};
|
||||
|
|
|
@ -25,7 +25,7 @@ describe('Release edit/new mutations', () => {
|
|||
mutations[types.INITIALIZE_EMPTY_RELEASE](state);
|
||||
|
||||
expect(state.release).toEqual({
|
||||
tagName: null,
|
||||
tagName: 'v1.3',
|
||||
name: '',
|
||||
description: '',
|
||||
milestones: [],
|
||||
|
@ -103,6 +103,16 @@ describe('Release edit/new mutations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe(`${types.UPDATE_SHOW_CREATE_FROM}`, () => {
|
||||
it('updates the ref that the ref will be created from', () => {
|
||||
state.showCreateFrom = true;
|
||||
const newValue = false;
|
||||
mutations[types.UPDATE_SHOW_CREATE_FROM](state, newValue);
|
||||
|
||||
expect(state.showCreateFrom).toBe(newValue);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`${types.UPDATE_RELEASE_TITLE}`, () => {
|
||||
it("updates the release's title", () => {
|
||||
state.release = release;
|
||||
|
|
|
@ -77,6 +77,7 @@ RSpec.describe ReleasesHelper do
|
|||
group_id
|
||||
group_milestones_available
|
||||
project_path
|
||||
tag_name
|
||||
releases_page_path
|
||||
markdown_preview_path
|
||||
markdown_docs_path
|
||||
|
|
|
@ -316,6 +316,17 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
|
|||
|
||||
it { is_expected.to eq(nil) }
|
||||
end
|
||||
|
||||
context 'with uppercase path' do
|
||||
let(:path) { 'foo/Bar' }
|
||||
|
||||
before do
|
||||
expect(Auth::ContainerRegistryAuthenticationService).to receive(:pull_nested_repositories_access_token).with(path.downcase).and_return(token)
|
||||
stub_repository_details(path, sizing: :self_with_descendants, status_code: 200, respond_with: response)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(555) }
|
||||
end
|
||||
end
|
||||
|
||||
def stub_pre_import(path, status_code, pre:)
|
||||
|
|
|
@ -363,6 +363,14 @@ RSpec.describe Gitlab::BitbucketImport::Importer do
|
|||
expect(project.issues.where("description LIKE ?", '%reporter3%').size).to eq(1)
|
||||
expect(importer.errors).to be_empty
|
||||
end
|
||||
|
||||
it 'sets work item type on new issues' do
|
||||
allow(importer).to receive(:import_wiki)
|
||||
|
||||
importer.execute
|
||||
|
||||
expect(project.issues.map(&:work_item_type_id).uniq).to contain_exactly(WorkItems::Type.default_issue_type.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'metrics' do
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Git::AttributesAtRefParser, :seed_helper do
|
||||
RSpec.describe Gitlab::Git::AttributesAtRefParser do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:repository) { project.repository }
|
||||
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Git::AttributesParser, :seed_helper do
|
||||
let(:attributes_path) { File.join(SEED_STORAGE_PATH, 'with-git-attributes.git', 'info', 'attributes') }
|
||||
let(:data) { File.read(attributes_path) }
|
||||
RSpec.describe Gitlab::Git::AttributesParser do
|
||||
let(:data) { fixture_file('gitlab/git/gitattributes') }
|
||||
|
||||
subject { described_class.new(data) }
|
||||
|
||||
|
@ -141,11 +140,12 @@ RSpec.describe Gitlab::Git::AttributesParser, :seed_helper do
|
|||
expect { |b| subject.each_line(&b) }.to yield_successive_args(*args)
|
||||
end
|
||||
|
||||
it 'does not yield when the attributes file has an unsupported encoding' do
|
||||
path = File.join(SEED_STORAGE_PATH, 'with-invalid-git-attributes.git', 'info', 'attributes')
|
||||
attrs = described_class.new(File.read(path))
|
||||
context 'unsupported encoding' do
|
||||
let(:data) { fixture_file('gitlab/git/gitattributes_invalid') }
|
||||
|
||||
expect { |b| attrs.each_line(&b) }.not_to yield_control
|
||||
it 'does not yield' do
|
||||
expect { |b| subject.each_line(&b) }.not_to yield_control
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1728,27 +1728,28 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
|
|||
end
|
||||
|
||||
describe '#gitattribute' do
|
||||
let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '', 'group/project') }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:repository) { project.repository }
|
||||
|
||||
after do
|
||||
ensure_seeds
|
||||
context 'with gitattributes' do
|
||||
before do
|
||||
repository.copy_gitattributes('gitattributes')
|
||||
end
|
||||
|
||||
it 'returns matching language attribute' do
|
||||
expect(repository.gitattribute("custom-highlighting/test.gitlab-custom", 'gitlab-language')).to eq('ruby')
|
||||
end
|
||||
|
||||
it 'returns matching language attribute with additional options' do
|
||||
expect(repository.gitattribute("custom-highlighting/test.gitlab-cgi", 'gitlab-language')).to eq('erb?parent=json')
|
||||
end
|
||||
|
||||
it 'returns nil if nothing matches' do
|
||||
expect(repository.gitattribute("report.xslt", 'gitlab-language')).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns matching language attribute' do
|
||||
expect(repository.gitattribute("custom-highlighting/test.gitlab-custom", 'gitlab-language')).to eq('ruby')
|
||||
end
|
||||
|
||||
it 'returns matching language attribute with additional options' do
|
||||
expect(repository.gitattribute("custom-highlighting/test.gitlab-cgi", 'gitlab-language')).to eq('erb?parent=json')
|
||||
end
|
||||
|
||||
it 'returns nil if nothing matches' do
|
||||
expect(repository.gitattribute("report.xslt", 'gitlab-language')).to eq(nil)
|
||||
end
|
||||
|
||||
context 'without gitattributes file' do
|
||||
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
|
||||
|
||||
context 'without gitattributes' do
|
||||
it 'returns nil' do
|
||||
expect(repository.gitattribute("README.md", 'gitlab-language')).to eq(nil)
|
||||
end
|
||||
|
|
|
@ -1365,29 +1365,11 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
end
|
||||
|
||||
describe ".with_duration" do
|
||||
context 'with feature flag measure_service_ping_metric_collection turned off' do
|
||||
before do
|
||||
stub_feature_flags(measure_service_ping_metric_collection: false)
|
||||
end
|
||||
it 'records duration' do
|
||||
expect(::Gitlab::Usage::ServicePing::LegacyMetricTimingDecorator)
|
||||
.to receive(:new).with(2, kind_of(Float))
|
||||
|
||||
it 'does NOT record duration and return block response' do
|
||||
expect(::Gitlab::Usage::ServicePing::LegacyMetricTimingDecorator).not_to receive(:new)
|
||||
|
||||
expect(described_class.with_duration { 1 + 1 }).to be 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'with feature flag measure_service_ping_metric_collection turned off' do
|
||||
before do
|
||||
stub_feature_flags(measure_service_ping_metric_collection: true)
|
||||
end
|
||||
|
||||
it 'records duration' do
|
||||
expect(::Gitlab::Usage::ServicePing::LegacyMetricTimingDecorator)
|
||||
.to receive(:new).with(2, kind_of(Float))
|
||||
|
||||
described_class.with_duration { 1 + 1 }
|
||||
end
|
||||
described_class.with_duration { 1 + 1 }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -681,28 +681,6 @@ RSpec.describe Issue do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#has_related_branch?' do
|
||||
let(:issue) { create(:issue, project: reusable_project, title: "Blue Bell Knoll") }
|
||||
|
||||
subject { issue.has_related_branch? }
|
||||
|
||||
context 'branch found' do
|
||||
before do
|
||||
allow(issue.project.repository).to receive(:branch_names).and_return(["iceblink-luck", issue.to_branch_name])
|
||||
end
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context 'branch not found' do
|
||||
before do
|
||||
allow(issue.project.repository).to receive(:branch_names).and_return(["lazy-calm"])
|
||||
end
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'an editable mentionable' do
|
||||
subject { create(:issue, project: create(:project, :repository)) }
|
||||
|
||||
|
|
|
@ -10,11 +10,12 @@ RSpec.describe 'Adding a DiffNote' do
|
|||
let(:noteable) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:diff_refs) { noteable.diff_refs }
|
||||
let(:body) { 'Body text' }
|
||||
|
||||
let(:base_variables) do
|
||||
{
|
||||
noteable_id: GitlabSchema.id_from_object(noteable).to_s,
|
||||
body: 'Body text',
|
||||
body: body,
|
||||
position: {
|
||||
paths: {
|
||||
old_path: 'files/ruby/popen.rb',
|
||||
|
@ -65,6 +66,17 @@ RSpec.describe 'Adding a DiffNote' do
|
|||
it_behaves_like 'a Note mutation when the given resource id is not for a Noteable'
|
||||
end
|
||||
|
||||
context 'with /merge quick action' do
|
||||
let(:body) { "Body text \n/merge" }
|
||||
|
||||
it 'merges the merge request', :sidekiq_inline do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect(noteable.reload.state).to eq('merged')
|
||||
expect(mutation_response['note']['body']).to eq('Body text')
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns the note with the correct position' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
|
|
|
@ -377,62 +377,41 @@ RSpec.describe ServicePing::SubmitService do
|
|||
stub_database_flavor_check
|
||||
stub_application_setting(usage_ping_enabled: true)
|
||||
stub_response(body: with_conv_index_params)
|
||||
end
|
||||
|
||||
context 'with feature flag measure_service_ping_metric_collection turned on' do
|
||||
let(:metric_double) { instance_double(Gitlab::Usage::ServicePing::LegacyMetricTimingDecorator, duration: 123) }
|
||||
let(:payload) do
|
||||
{
|
||||
metric_a: metric_double,
|
||||
metric_group: {
|
||||
metric_b: metric_double
|
||||
},
|
||||
metric_without_timing: "value",
|
||||
recorded_at: Time.current
|
||||
}
|
||||
end
|
||||
|
||||
let(:metadata_payload) do
|
||||
{
|
||||
metadata: {
|
||||
metrics: [
|
||||
{ name: 'metric_a', time_elapsed: 123 },
|
||||
{ name: 'metric_group.metric_b', time_elapsed: 123 }
|
||||
]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(measure_service_ping_metric_collection: true)
|
||||
|
||||
allow_next_instance_of(ServicePing::BuildPayload) do |service|
|
||||
allow(service).to receive(:execute).and_return(payload)
|
||||
end
|
||||
end
|
||||
|
||||
it 'submits metadata' do
|
||||
response = stub_full_request(service_ping_metadata_url, method: :post)
|
||||
.with(body: metadata_payload)
|
||||
|
||||
subject.execute
|
||||
|
||||
expect(response).to have_been_requested
|
||||
allow_next_instance_of(ServicePing::BuildPayload) do |service|
|
||||
allow(service).to receive(:execute).and_return(payload)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with feature flag measure_service_ping_metric_collection turned off' do
|
||||
before do
|
||||
stub_feature_flags(measure_service_ping_metric_collection: false)
|
||||
end
|
||||
let(:metric_double) { instance_double(Gitlab::Usage::ServicePing::LegacyMetricTimingDecorator, duration: 123) }
|
||||
let(:payload) do
|
||||
{
|
||||
metric_a: metric_double,
|
||||
metric_group: {
|
||||
metric_b: metric_double
|
||||
},
|
||||
metric_without_timing: "value",
|
||||
recorded_at: Time.current
|
||||
}
|
||||
end
|
||||
|
||||
it 'does NOT submit metadata' do
|
||||
response = stub_full_request(service_ping_metadata_url, method: :post)
|
||||
let(:metadata_payload) do
|
||||
{
|
||||
metadata: {
|
||||
metrics: [
|
||||
{ name: 'metric_a', time_elapsed: 123 },
|
||||
{ name: 'metric_group.metric_b', time_elapsed: 123 }
|
||||
]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
subject.execute
|
||||
it 'submits metadata' do
|
||||
response = stub_full_request(service_ping_metadata_url, method: :post)
|
||||
.with(body: metadata_payload)
|
||||
|
||||
expect(response).not_to have_been_requested
|
||||
end
|
||||
subject.execute
|
||||
|
||||
expect(response).to have_been_requested
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ TEST_REPO_PATH = 'gitlab-git-test.git'
|
|||
TEST_NORMAL_REPO_PATH = 'not-bare-repo.git'
|
||||
TEST_MUTABLE_REPO_PATH = 'mutable-repo.git'
|
||||
TEST_BROKEN_REPO_PATH = 'broken-repo.git'
|
||||
TEST_GITATTRIBUTES_REPO_PATH = 'with-git-attributes.git'
|
||||
|
||||
module SeedHelper
|
||||
GITLAB_GIT_TEST_REPO_URL = File.expand_path('../gitlab-git-test.git', __dir__)
|
||||
|
@ -25,8 +24,6 @@ module SeedHelper
|
|||
create_normal_seeds
|
||||
create_mutable_seeds
|
||||
create_broken_seeds
|
||||
create_git_attributes
|
||||
create_invalid_git_attributes
|
||||
end
|
||||
|
||||
def create_bare_seeds
|
||||
|
@ -67,48 +64,4 @@ module SeedHelper
|
|||
|
||||
FileUtils.rm_r(refs_path)
|
||||
end
|
||||
|
||||
def create_git_attributes
|
||||
system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --bare #{TEST_REPO_PATH} #{TEST_GITATTRIBUTES_REPO_PATH}),
|
||||
chdir: SEED_STORAGE_PATH,
|
||||
out: '/dev/null',
|
||||
err: '/dev/null')
|
||||
|
||||
dir = File.join(SEED_STORAGE_PATH, 'with-git-attributes.git', 'info')
|
||||
|
||||
FileUtils.mkdir_p(dir)
|
||||
|
||||
File.open(File.join(dir, 'attributes'), 'w') do |handle|
|
||||
handle.write <<-EOF.strip
|
||||
# This is a comment, it should be ignored.
|
||||
|
||||
*.txt text
|
||||
*.jpg -text
|
||||
*.sh eol=lf gitlab-language=shell
|
||||
*.haml.* gitlab-language=haml
|
||||
foo/bar.* foo
|
||||
*.cgi key=value?p1=v1&p2=v2
|
||||
/*.png gitlab-language=png
|
||||
*.binary binary
|
||||
/custom-highlighting/*.gitlab-custom gitlab-language=ruby
|
||||
/custom-highlighting/*.gitlab-cgi gitlab-language=erb?parent=json
|
||||
|
||||
# This uses a tab instead of spaces to ensure the parser also supports this.
|
||||
*.md\tgitlab-language=markdown
|
||||
bla/bla.txt
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
def create_invalid_git_attributes
|
||||
dir = File.join(SEED_STORAGE_PATH, 'with-invalid-git-attributes.git', 'info')
|
||||
|
||||
FileUtils.mkdir_p(dir)
|
||||
|
||||
enc = Encoding::UTF_16
|
||||
|
||||
File.open(File.join(dir, 'attributes'), 'w', encoding: enc) do |handle|
|
||||
handle.write('# hello'.encode(enc))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -46,10 +46,10 @@ GEM
|
|||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
method_source (1.0.0)
|
||||
mini_portile2 (2.8.0)
|
||||
minitest (5.16.0)
|
||||
nokogiri (1.13.6-arm64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.6-x86_64-linux)
|
||||
nokogiri (1.13.6)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
orm_adapter (0.5.0)
|
||||
racc (1.6.0)
|
||||
|
@ -92,8 +92,7 @@ GEM
|
|||
zeitwerk (2.6.0)
|
||||
|
||||
PLATFORMS
|
||||
arm64-darwin-21
|
||||
x86_64-linux
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
activemodel (~> 6.1, < 8)
|
||||
|
|
3
vendor/gems/ipynbdiff/Gemfile.lock
vendored
3
vendor/gems/ipynbdiff/Gemfile.lock
vendored
|
@ -52,8 +52,7 @@ GEM
|
|||
parser (>= 3.1.0)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-darwin-20
|
||||
x86_64-linux
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
benchmark-memory (~> 0.2.0)
|
||||
|
|
Loading…
Reference in a new issue