Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2024-01-05 21:21:08 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2024-01-05 21:21:08 +0300
commit534ce3b2d0a6ec24de9c370e5b85c9528ff63e34 (patch)
tree4ad964818b181fddc0925e33b63f9b1f2ded23d3
parent4ba8ae97071935c39216afc53304c60386bbfa68 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop.yml2
-rw-r--r--Gemfile.lock1
-rw-r--r--app/assets/javascripts/dropzone_input.js3
-rw-r--r--app/assets/javascripts/organizations/new/components/app.vue7
-rw-r--r--app/assets/javascripts/organizations/new/index.js5
-rw-r--r--app/assets/javascripts/organizations/settings/general/components/organization_settings.vue4
-rw-r--r--app/assets/javascripts/organizations/settings/general/index.js10
-rw-r--r--app/assets/javascripts/organizations/shared/components/new_edit_form.vue63
-rw-r--r--app/assets/javascripts/organizations/shared/components/organization_url_field.vue4
-rw-r--r--app/assets/javascripts/organizations/shared/constants.js1
-rw-r--r--app/assets/javascripts/organizations/show/components/app.vue4
-rw-r--r--app/assets/javascripts/organizations/show/components/organization_description.vue24
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue1
-rw-r--r--app/assets/stylesheets/application.scss6
-rw-r--r--app/assets/stylesheets/emoji_sprites.scss4
-rw-r--r--app/assets/stylesheets/fonts.scss8
-rw-r--r--app/assets/stylesheets/framework/diffs.scss18
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss2
-rw-r--r--app/assets/stylesheets/framework/files.scss2
-rw-r--r--app/assets/stylesheets/framework/source_editor.scss2
-rw-r--r--app/assets/stylesheets/framework/typography.scss2
-rw-r--r--app/assets/stylesheets/highlight/common.scss2
-rw-r--r--app/assets/stylesheets/pages/issues.scss2
-rw-r--r--app/assets/stylesheets/pages/notes.scss2
-rw-r--r--app/assets/stylesheets/print.scss162
-rw-r--r--app/assets/stylesheets/snippets.scss3
-rw-r--r--app/controllers/concerns/preview_markdown.rb1
-rw-r--r--app/controllers/organizations/organizations_controller.rb4
-rw-r--r--app/finders/ci/runner_jobs_finder.rb8
-rw-r--r--app/helpers/organizations/organization_helper.rb23
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/models/ci/runner_manager.rb4
-rw-r--r--app/models/organizations/organization.rb2
-rw-r--r--app/models/organizations/organization_detail.rb2
-rw-r--r--config/routes/organizations.rb4
-rw-r--r--doc/administration/auth/index.md1
-rw-r--r--doc/administration/configure.md1
-rw-r--r--doc/administration/dedicated/index.md4
-rw-r--r--doc/administration/license.md2
-rw-r--r--doc/administration/operations/index.md1
-rw-r--r--doc/administration/reference_architectures/index.md1
-rw-r--r--doc/administration/settings/index.md1
-rw-r--r--doc/api/runners.md16
-rw-r--r--doc/architecture/blueprints/cells/routing-service.md2
-rw-r--r--doc/architecture/blueprints/runner_admission_controller/index.md2
-rw-r--r--doc/architecture/blueprints/runner_tokens/index.md10
-rw-r--r--doc/architecture/blueprints/secret_manager/index.md6
-rw-r--r--doc/ci/runners/runner_fleet_dashboard.md13
-rw-r--r--doc/development/code_review.md6
-rw-r--r--doc/development/contributing/design.md10
-rw-r--r--doc/development/documentation/site_architecture/global_nav.md2
-rw-r--r--doc/development/documentation/styleguide/index.md2
-rw-r--r--doc/development/documentation/styleguide/word_list.md2
-rw-r--r--doc/development/documentation/topic_types/troubleshooting.md2
-rw-r--r--doc/development/ee_features.md2
-rw-r--r--doc/development/go_guide/index.md2
-rw-r--r--doc/development/i18n/translation.md2
-rw-r--r--doc/development/migration_style_guide.md4
-rw-r--r--doc/development/sec/analyzer_development_guide.md14
-rw-r--r--doc/development/secure_coding_guidelines.md2
-rw-r--r--doc/development/testing_guide/end_to_end/feature_flags.md4
-rw-r--r--doc/development/testing_guide/flaky_tests.md2
-rw-r--r--doc/install/cloud_providers.md2
-rw-r--r--doc/install/install_methods.md2
-rw-r--r--doc/install/requirements.md1
-rw-r--r--doc/security/index.md1
-rw-r--r--doc/subscriptions/gitlab_com/index.md18
-rw-r--r--doc/topics/offline/index.md1
-rw-r--r--doc/update/versions/gitlab_16_changes.md2
-rw-r--r--doc/user/application_security/container_scanning/index.md8
-rw-r--r--doc/user/application_security/dependency_scanning/troubleshooting_dependency_scanning.md2
-rw-r--r--doc/user/application_security/sast/index.md8
-rw-r--r--doc/user/application_security/secret_detection/index.md8
-rw-r--r--doc/user/feature_flags.md4
-rw-r--r--doc/user/organization/index.md14
-rw-r--r--doc/user/profile/account/create_accounts.md1
-rw-r--r--doc/user/project/repository/code_suggestions/self_managed.md2
-rw-r--r--gems/gitlab-database-load_balancing/Gemfile.lock1
-rw-r--r--gems/gitlab-safe_request_store/Gemfile.lock3
-rw-r--r--gems/gitlab-safe_request_store/gitlab-safe_request_store.gemspec1
-rw-r--r--lib/api/ci/runners.rb25
-rw-r--r--lib/gitlab/dependency_linker/base_linker.rb10
-rw-r--r--lib/gitlab/middleware/unauthenticated_session_expiry.rb3
-rw-r--r--locale/gitlab.pot21
-rw-r--r--rubocop/cop/scalability/file_uploads.rb2
-rw-r--r--spec/factories/projects.rb33
-rw-r--r--spec/finders/ci/runner_jobs_finder_spec.rb88
-rw-r--r--spec/frontend/organizations/new/components/app_spec.js27
-rw-r--r--spec/frontend/organizations/settings/general/components/organization_settings_spec.js14
-rw-r--r--spec/frontend/organizations/shared/components/new_edit_form_spec.js34
-rw-r--r--spec/frontend/organizations/show/components/app_spec.js7
-rw-r--r--spec/frontend/organizations/show/components/organization_description_spec.js46
-rw-r--r--spec/helpers/organizations/organization_helper_spec.rb13
-rw-r--r--spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb16
-rw-r--r--spec/models/ci/build_spec.rb52
-rw-r--r--spec/models/ci/runner_manager_spec.rb273
-rw-r--r--spec/models/organizations/organization_detail_spec.rb9
-rw-r--r--spec/models/organizations/organization_spec.rb1
-rw-r--r--spec/requests/api/ci/runners_spec.rb281
-rw-r--r--spec/requests/organizations/organizations_controller_spec.rb24
-rw-r--r--spec/routing/organizations/organizations_controller_routing_spec.rb5
-rw-r--r--spec/rubocop/cop/scalability/file_uploads_spec.rb2
-rw-r--r--spec/support/helpers/debug_with_puts.rb13
-rw-r--r--spec/support/helpers/stub_requests.rb18
-rw-r--r--vendor/gems/bundler-checksum/Gemfile.lock2
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler_checksum.rb18
-rw-r--r--vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.lock2
107 files changed, 1101 insertions, 529 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index dac452cb484..dd43221d20e 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -994,7 +994,7 @@ Gemspec/AvoidExecutingGit:
Lint/BinaryOperatorWithIdenticalOperands:
Exclude:
- - '{,ee/,qa/}spec/**/*_{spec,shared_examples,shared_context}.rb'
+ - '{,ee/,qa/,jh/}spec/**/*_{spec,shared_examples,shared_context}.rb'
Cop/SidekiqRedisCall:
Enabled: true
diff --git a/Gemfile.lock b/Gemfile.lock
index 3cbaf38ed71..69a3a0356cf 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -67,6 +67,7 @@ PATH
remote: gems/gitlab-safe_request_store
specs:
gitlab-safe_request_store (0.1.0)
+ rack (~> 2.2.8)
request_store
PATH
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 9ee4f7cf4aa..17744e2c6ab 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -46,7 +46,6 @@ export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
let hasPlainText;
formTextarea.wrap('<div class="div-dropzone"></div>');
- formTextarea.on('paste', (event) => handlePaste(event));
// Add dropzone area to the form.
const $mdArea = formTextarea.closest('.md-area');
@@ -60,6 +59,8 @@ export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
return null;
}
+ formTextarea.on('paste', (event) => handlePaste(event));
+
const dropzone = $formDropzone.dropzone({
url: uploadsPath,
dictDefaultMessage: '',
diff --git a/app/assets/javascripts/organizations/new/components/app.vue b/app/assets/javascripts/organizations/new/components/app.vue
index ee50afd3613..3a2b786dbae 100644
--- a/app/assets/javascripts/organizations/new/components/app.vue
+++ b/app/assets/javascripts/organizations/new/components/app.vue
@@ -42,7 +42,12 @@ export default {
} = await this.$apollo.mutate({
mutation: organizationCreateMutation,
variables: {
- input: { name: formValues.name, path: formValues.path, avatar: formValues.avatar },
+ input: {
+ name: formValues.name,
+ path: formValues.path,
+ description: formValues.description,
+ avatar: formValues.avatar,
+ },
},
context: {
hasUpload: formValues.avatar instanceof File,
diff --git a/app/assets/javascripts/organizations/new/index.js b/app/assets/javascripts/organizations/new/index.js
index 9c7e5344800..563e366b2c6 100644
--- a/app/assets/javascripts/organizations/new/index.js
+++ b/app/assets/javascripts/organizations/new/index.js
@@ -13,7 +13,9 @@ export const initOrganizationsNew = () => {
const {
dataset: { appData },
} = el;
- const { organizationsPath, rootUrl } = convertObjectPropsToCamelCase(JSON.parse(appData));
+ const { organizationsPath, rootUrl, previewMarkdownPath } = convertObjectPropsToCamelCase(
+ JSON.parse(appData),
+ );
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
@@ -26,6 +28,7 @@ export const initOrganizationsNew = () => {
provide: {
organizationsPath,
rootUrl,
+ previewMarkdownPath,
},
render(createElement) {
return createElement(App);
diff --git a/app/assets/javascripts/organizations/settings/general/components/organization_settings.vue b/app/assets/javascripts/organizations/settings/general/components/organization_settings.vue
index 283a652f90e..1cea2ceeb90 100644
--- a/app/assets/javascripts/organizations/settings/general/components/organization_settings.vue
+++ b/app/assets/javascripts/organizations/settings/general/components/organization_settings.vue
@@ -6,6 +6,7 @@ import NewEditForm from '~/organizations/shared/components/new_edit_form.vue';
import {
FORM_FIELD_NAME,
FORM_FIELD_ID,
+ FORM_FIELD_DESCRIPTION,
FORM_FIELD_AVATAR,
} from '~/organizations/shared/constants';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
@@ -29,7 +30,7 @@ export default {
),
successMessage: s__('Organization|Organization was successfully updated.'),
},
- fieldsToRender: [FORM_FIELD_NAME, FORM_FIELD_ID, FORM_FIELD_AVATAR],
+ fieldsToRender: [FORM_FIELD_NAME, FORM_FIELD_ID, FORM_FIELD_DESCRIPTION, FORM_FIELD_AVATAR],
data() {
return {
loading: false,
@@ -66,6 +67,7 @@ export default {
input: {
id: convertToGraphQLId(TYPE_ORGANIZATION, this.organization.id),
name: formValues.name,
+ description: formValues.description,
...this.avatarInput(formValues),
},
},
diff --git a/app/assets/javascripts/organizations/settings/general/index.js b/app/assets/javascripts/organizations/settings/general/index.js
index 138606a0aab..3ac1243ff0f 100644
--- a/app/assets/javascripts/organizations/settings/general/index.js
+++ b/app/assets/javascripts/organizations/settings/general/index.js
@@ -13,9 +13,12 @@ export const initOrganizationsSettingsGeneral = () => {
const {
dataset: { appData },
} = el;
- const { organization, organizationsPath, rootUrl } = convertObjectPropsToCamelCase(
- JSON.parse(appData),
- );
+ const {
+ organization,
+ organizationsPath,
+ rootUrl,
+ previewMarkdownPath,
+ } = convertObjectPropsToCamelCase(JSON.parse(appData));
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
@@ -29,6 +32,7 @@ export const initOrganizationsSettingsGeneral = () => {
organization,
organizationsPath,
rootUrl,
+ previewMarkdownPath,
},
render(createElement) {
return createElement(App);
diff --git a/app/assets/javascripts/organizations/shared/components/new_edit_form.vue b/app/assets/javascripts/organizations/shared/components/new_edit_form.vue
index 3567fa490ea..49519369e9a 100644
--- a/app/assets/javascripts/organizations/shared/components/new_edit_form.vue
+++ b/app/assets/javascripts/organizations/shared/components/new_edit_form.vue
@@ -4,10 +4,13 @@ import { formValidators } from '@gitlab/ui/dist/utils';
import { s__, __ } from '~/locale';
import { slugify } from '~/lib/utils/text_utility';
import AvatarUploadDropzone from '~/vue_shared/components/upload_dropzone/avatar_upload_dropzone.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
+import { helpPagePath } from '~/helpers/help_page_helper';
import {
FORM_FIELD_NAME,
FORM_FIELD_ID,
FORM_FIELD_PATH,
+ FORM_FIELD_DESCRIPTION,
FORM_FIELD_AVATAR,
FORM_FIELD_PATH_VALIDATORS,
} from '../constants';
@@ -21,12 +24,27 @@ export default {
GlButton,
OrganizationUrlField,
AvatarUploadDropzone,
+ MarkdownField,
},
i18n: {
cancel: __('Cancel'),
},
formId: 'new-organization-form',
- inject: ['organizationsPath'],
+ markdownDocsPath: helpPagePath('user/organization/index', {
+ anchor: 'organization-description-supported-markdown',
+ }),
+ restrictedToolBarItems: [
+ 'code',
+ 'quote',
+ 'bullet-list',
+ 'numbered-list',
+ 'task-list',
+ 'collapsible-section',
+ 'table',
+ 'attach-file',
+ 'full-screen',
+ ],
+ inject: ['organizationsPath', 'previewMarkdownPath'],
props: {
loading: {
type: Boolean,
@@ -39,6 +57,7 @@ export default {
return {
[FORM_FIELD_NAME]: '',
[FORM_FIELD_PATH]: '',
+ [FORM_FIELD_DESCRIPTION]: '',
[FORM_FIELD_AVATAR]: null,
};
},
@@ -47,7 +66,7 @@ export default {
type: Array,
required: false,
default() {
- return [FORM_FIELD_NAME, FORM_FIELD_PATH, FORM_FIELD_AVATAR];
+ return [FORM_FIELD_NAME, FORM_FIELD_PATH, FORM_FIELD_DESCRIPTION, FORM_FIELD_AVATAR];
},
},
submitButtonText: {
@@ -102,6 +121,12 @@ export default {
class: 'gl-w-full',
},
},
+ [FORM_FIELD_DESCRIPTION]: {
+ label: s__('Organization|Organization description (optional)'),
+ groupAttrs: {
+ class: 'gl-w-full common-note-form',
+ },
+ },
[FORM_FIELD_AVATAR]: {
label: s__('Organization|Organization avatar'),
groupAttrs: {
@@ -137,6 +162,7 @@ export default {
formFieldsInputEvent(event);
this.hasPathBeenManuallySet = true;
},
+ helpPagePath,
},
};
</script>
@@ -159,6 +185,28 @@ export default {
@blur="blur"
/>
</template>
+ <template #input(description)="{ id, value, input, blur }">
+ <div class="gl-md-form-input-xl">
+ <markdown-field
+ :can-attach-file="false"
+ :markdown-preview-path="previewMarkdownPath"
+ :markdown-docs-path="$options.markdownDocsPath"
+ :textarea-value="value"
+ :restricted-tool-bar-items="$options.restrictedToolBarItems"
+ >
+ <template #textarea>
+ <textarea
+ :id="id"
+ :value="value"
+ class="note-textarea js-gfm-input markdown-area"
+ maxlength="1024"
+ @input="input($event.target.value)"
+ @blur="blur"
+ ></textarea>
+ </template>
+ </markdown-field>
+ </div>
+ </template>
<template #input(avatar)="{ input, value }">
<avatar-upload-dropzone
:value="value"
@@ -169,9 +217,14 @@ export default {
</template>
</gl-form-fields>
<div class="gl-display-flex gl-gap-3">
- <gl-button type="submit" variant="confirm" class="js-no-auto-disable" :loading="loading">{{
- submitButtonText
- }}</gl-button>
+ <gl-button
+ type="submit"
+ variant="confirm"
+ class="js-no-auto-disable"
+ :loading="loading"
+ data-testid="submit-button"
+ >{{ submitButtonText }}</gl-button
+ >
<gl-button v-if="showCancelButton" :href="organizationsPath">{{
$options.i18n.cancel
}}</gl-button>
diff --git a/app/assets/javascripts/organizations/shared/components/organization_url_field.vue b/app/assets/javascripts/organizations/shared/components/organization_url_field.vue
index d36f62477e6..c4b31e6b8a6 100644
--- a/app/assets/javascripts/organizations/shared/components/organization_url_field.vue
+++ b/app/assets/javascripts/organizations/shared/components/organization_url_field.vue
@@ -39,7 +39,7 @@ export default {
</script>
<template>
- <gl-form-input-group>
+ <gl-form-input-group class="gl-md-form-input-xl">
<template #prepend>
<gl-input-group-text class="organization-root-path">
<gl-truncate :text="baseUrl" position="middle" />
@@ -50,7 +50,7 @@ export default {
:id="id"
:value="value"
:placeholder="$options.i18n.pathPlaceholder"
- class="gl-h-auto! gl-md-form-input-lg"
+ class="gl-h-auto!"
@input="$emit('input', $event)"
@blur="$emit('blur', $event)"
/>
diff --git a/app/assets/javascripts/organizations/shared/constants.js b/app/assets/javascripts/organizations/shared/constants.js
index 9f3d066f984..c4f4f348ff2 100644
--- a/app/assets/javascripts/organizations/shared/constants.js
+++ b/app/assets/javascripts/organizations/shared/constants.js
@@ -4,6 +4,7 @@ import { s__ } from '~/locale';
export const FORM_FIELD_NAME = 'name';
export const FORM_FIELD_ID = 'id';
export const FORM_FIELD_PATH = 'path';
+export const FORM_FIELD_DESCRIPTION = 'description';
export const FORM_FIELD_AVATAR = 'avatar';
export const FORM_FIELD_PATH_VALIDATORS = [
diff --git a/app/assets/javascripts/organizations/show/components/app.vue b/app/assets/javascripts/organizations/show/components/app.vue
index 47264d80454..4cd5ada1d66 100644
--- a/app/assets/javascripts/organizations/show/components/app.vue
+++ b/app/assets/javascripts/organizations/show/components/app.vue
@@ -1,11 +1,12 @@
<script>
import OrganizationAvatar from './organization_avatar.vue';
+import OrganizationDescription from './organization_description.vue';
import GroupsAndProjects from './groups_and_projects.vue';
import AssociationCounts from './association_counts.vue';
export default {
name: 'OrganizationShowApp',
- components: { OrganizationAvatar, GroupsAndProjects, AssociationCounts },
+ components: { OrganizationAvatar, OrganizationDescription, GroupsAndProjects, AssociationCounts },
props: {
organization: {
type: Object,
@@ -26,6 +27,7 @@ export default {
<template>
<div class="gl-py-6">
<organization-avatar :organization="organization" />
+ <organization-description :organization="organization" />
<association-counts
:association-counts="associationCounts"
:groups-and-projects-organization-path="groupsAndProjectsOrganizationPath"
diff --git a/app/assets/javascripts/organizations/show/components/organization_description.vue b/app/assets/javascripts/organizations/show/components/organization_description.vue
new file mode 100644
index 00000000000..6222bdcd082
--- /dev/null
+++ b/app/assets/javascripts/organizations/show/components/organization_description.vue
@@ -0,0 +1,24 @@
+<script>
+import SafeHtml from '~/vue_shared/directives/safe_html';
+
+export default {
+ name: 'OrganizationDescription',
+ directives: {
+ SafeHtml,
+ },
+ props: {
+ organization: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ v-if="organization.description_html"
+ v-safe-html="organization.description_html"
+ class="gl-mt-5 md"
+ ></div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index cffd8471d18..525f684df86 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -382,6 +382,7 @@ export default {
@click="handleQuote"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('code')"
v-show="!previewMarkdown"
tag="`"
tag-block="```"
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index ce8ccb2bc08..cd9bbf45727 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -27,7 +27,5 @@
// JH-only stylesheets
@import 'application_jh';
-/* print styles */
-@media print {
- @import 'print';
-}
+// print styles
+@import 'print';
diff --git a/app/assets/stylesheets/emoji_sprites.scss b/app/assets/stylesheets/emoji_sprites.scss
index 5a5f39a4b77..10bf54b4ffb 100644
--- a/app/assets/stylesheets/emoji_sprites.scss
+++ b/app/assets/stylesheets/emoji_sprites.scss
@@ -7176,7 +7176,7 @@
}
.emoji-icon {
- background-image: image-url('emoji.png');
+ background-image: url('emoji.png');
background-repeat: no-repeat;
color: transparent;
text-indent: -99em;
@@ -7190,7 +7190,7 @@
only screen and (min-device-pixel-ratio: 2),
only screen and (min-resolution: 192dpi),
only screen and (min-resolution: 2dppx) {
- background-image: image-url('emoji@2x.png');
+ background-image: url('emoji@2x.png');
background-size: 860px 840px;
}
/* stylelint-enable media-feature-name-no-vendor-prefix */
diff --git a/app/assets/stylesheets/fonts.scss b/app/assets/stylesheets/fonts.scss
index 6886e751b72..f776328ebdf 100644
--- a/app/assets/stylesheets/fonts.scss
+++ b/app/assets/stylesheets/fonts.scss
@@ -11,7 +11,7 @@ Usage:
font-style: normal;
/* stylelint-disable-next-line property-no-unknown */
font-named-instance: 'Regular';
- src: font-url('gitlab-sans/GitLabSans.woff2') format('woff2');
+ src: url('gitlab-sans/GitLabSans.woff2') format('woff2');
}
@font-face {
@@ -21,7 +21,7 @@ Usage:
font-style: italic;
/* stylelint-disable-next-line property-no-unknown */
font-named-instance: 'Regular';
- src: font-url('gitlab-sans/GitLabSans-Italic.woff2') format('woff2');
+ src: url('gitlab-sans/GitLabSans-Italic.woff2') format('woff2');
}
/* -------------------------------------------------------
@@ -35,7 +35,7 @@ Usage:
font-weight: 100 900;
font-display: swap;
font-style: normal;
- src: font-url('gitlab-mono/GitLabMono.woff2') format('woff2');
+ src: url('gitlab-mono/GitLabMono.woff2') format('woff2');
}
@font-face {
@@ -43,7 +43,7 @@ Usage:
font-weight: 100 900;
font-display: swap;
font-style: italic;
- src: font-url('gitlab-mono/GitLabMono-Italic.woff2') format('woff2');
+ src: url('gitlab-mono/GitLabMono-Italic.woff2') format('woff2');
}
// This isn't the best solution, but we needed a quick fix
diff --git a/app/assets/stylesheets/framework/diffs.scss b/app/assets/stylesheets/framework/diffs.scss
index b948a57ea33..c9ef9349c36 100644
--- a/app/assets/stylesheets/framework/diffs.scss
+++ b/app/assets/stylesheets/framework/diffs.scss
@@ -225,7 +225,7 @@ $diff-file-header: 41px;
width: 15px;
position: absolute;
top: 0;
- background: image-url('swipemode_sprites.gif') 0 3px no-repeat;
+ background: url('swipemode_sprites.gif') 0 3px no-repeat;
}
.bottom-handle {
@@ -234,7 +234,7 @@ $diff-file-header: 41px;
width: 15px;
position: absolute;
bottom: 0;
- background: image-url('swipemode_sprites.gif') 0 -11px no-repeat;
+ background: url('swipemode_sprites.gif') 0 -11px no-repeat;
}
}
}
@@ -272,7 +272,7 @@ $diff-file-header: 41px;
left: 12px;
height: 10px;
width: 276px;
- background: image-url('onion_skin_sprites.gif') -4px -20px repeat-x;
+ background: url('onion_skin_sprites.gif') -4px -20px repeat-x;
}
.dragger {
@@ -282,7 +282,7 @@ $diff-file-header: 41px;
top: 0;
height: 14px;
width: 14px;
- background: image-url('onion_skin_sprites.gif') 0 -34px repeat-x;
+ background: url('onion_skin_sprites.gif') 0 -34px repeat-x;
cursor: pointer;
}
@@ -293,7 +293,7 @@ $diff-file-header: 41px;
right: 0;
height: 10px;
width: 10px;
- background: image-url('onion_skin_sprites.gif') -2px 0 no-repeat;
+ background: url('onion_skin_sprites.gif') -2px 0 no-repeat;
}
.opaque {
@@ -303,7 +303,7 @@ $diff-file-header: 41px;
left: 0;
height: 10px;
width: 10px;
- background: image-url('onion_skin_sprites.gif') -2px -10px no-repeat;
+ background: url('onion_skin_sprites.gif') -2px -10px no-repeat;
}
}
}
@@ -770,12 +770,12 @@ table.code {
.frame.click-to-comment,
.btn-transparent.image-diff-overlay-add-comment {
position: relative;
- cursor: image-url('illustrations/image_comment_light_cursor.svg') $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
+ cursor: url('illustrations/image_comment_light_cursor.svg') $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
auto;
// Retina cursor
- cursor: image-set(image-url('illustrations/image_comment_light_cursor.svg') 1x,
- image-url('illustrations/image_comment_light_cursor@2x.svg') 2x) $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
+ cursor: image-set(url('illustrations/image_comment_light_cursor.svg') 1x,
+ url('illustrations/image_comment_light_cursor@2x.svg') 2x) $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
auto;
.comment-indicator {
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 8282f6143c2..649ceb95731 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -575,7 +575,7 @@
left: 1rem;
width: 1rem;
height: 1rem;
- mask-image: asset_url('icons-stacked.svg#check');
+ mask-image: url('icons-stacked.svg#check');
mask-repeat: no-repeat;
mask-size: cover;
mask-position: center center;
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 9cb264c992b..7dcde5f0b3c 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -420,7 +420,7 @@ span.idiff {
@include gl-h-5;
@include gl-float-left;
background-color: $gray-400;
- mask-image: asset_url('icons-stacked.svg#doc-versions');
+ mask-image: url('icons-stacked.svg#doc-versions');
mask-repeat: no-repeat;
mask-size: cover;
mask-position: center;
diff --git a/app/assets/stylesheets/framework/source_editor.scss b/app/assets/stylesheets/framework/source_editor.scss
index a09ab7ed64c..2b597634519 100644
--- a/app/assets/stylesheets/framework/source_editor.scss
+++ b/app/assets/stylesheets/framework/source_editor.scss
@@ -78,7 +78,7 @@
@include gl-mr-2;
@include gl-w-4;
@include gl-h-4;
- mask-image: asset_url('icons-stacked.svg#link');
+ mask-image: url('icons-stacked.svg#link');
mask-repeat: no-repeat;
mask-size: cover;
mask-position: center;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 8153c4d4717..15e794fc347 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -495,7 +495,7 @@
&::after {
@include gl-dark-invert-keep-hue;
- content: image-url('icon_anchor.svg');
+ content: url('icon_anchor.svg');
visibility: hidden;
}
}
diff --git a/app/assets/stylesheets/highlight/common.scss b/app/assets/stylesheets/highlight/common.scss
index 23fa1326881..3fd72904655 100644
--- a/app/assets/stylesheets/highlight/common.scss
+++ b/app/assets/stylesheets/highlight/common.scss
@@ -118,7 +118,7 @@
@include gl-w-5;
@include gl-h-5;
background-color: rgba($color, 0.3);
- mask-image: asset_url('icons-stacked.svg##{$icon}');
+ mask-image: url('icons-stacked.svg##{$icon}');
mask-repeat: no-repeat;
mask-size: cover;
mask-position: center;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 9748983d1ae..f57a8519992 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -225,7 +225,7 @@ ul.related-merge-requests > li gl-emoji {
&::after {
@include gl-dark-invert-keep-hue;
- content: image-url('icon_anchor.svg');
+ content: url('icon_anchor.svg');
visibility: hidden;
}
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 483d151adab..30c34c5ef32 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -966,7 +966,7 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
.unified-diff-components-diff-note-button {
&::before {
background-color: $blue-500;
- mask-image: asset_url('icons-stacked.svg#comment');
+ mask-image: url('icons-stacked.svg#comment');
mask-repeat: no-repeat;
mask-size: cover;
mask-position: center;
diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss
index 315b9c829a7..d6fcfb3461d 100644
--- a/app/assets/stylesheets/print.scss
+++ b/app/assets/stylesheets/print.scss
@@ -4,97 +4,99 @@
@import '@gitlab/ui/src/scss/variables';
@import '@gitlab/ui/src/scss/utility-mixins/index';
-.md h1,
-.md h2,
-.md h3,
-.md h4,
-.md h5,
-.md h6 {
- margin-top: 17px;
-}
+@media print {
+ .md h1,
+ .md h2,
+ .md h3,
+ .md h4,
+ .md h5,
+ .md h6 {
+ margin-top: 17px;
+ }
-.md h1 {
- font-size: 30px;
-}
+ .md h1 {
+ font-size: 30px;
+ }
-.md h2 {
- font-size: 22px;
-}
+ .md h2 {
+ font-size: 22px;
+ }
-.md h3 {
- font-size: 18px;
- font-weight: 600;
-}
+ .md h3 {
+ font-size: 18px;
+ font-weight: 600;
+ }
-.md {
- print-color-adjust: exact;
- -webkit-print-color-adjust: exact;
+ .md {
+ print-color-adjust: exact;
+ -webkit-print-color-adjust: exact;
- // fix blockquote style in print
- blockquote {
- &::before {
- position: absolute;
- top: 0;
- left: -4px;
- content: ' ';
- height: 100%;
- width: 4px;
- background-color: $gray-100;
- }
+ // fix blockquote style in print
+ blockquote {
+ &::before {
+ position: absolute;
+ top: 0;
+ left: -4px;
+ content: ' ';
+ height: 100%;
+ width: 4px;
+ background-color: $gray-100;
+ }
- position: relative;
- font-size: inherit;
- @include gl-text-gray-700;
- @include gl-py-3;
- @include gl-pl-6;
- @include gl-my-3;
- @include gl-mx-0;
- @include gl-inset-border-l-4-gray-100;
- margin-left: 4px;
- border: 0 !important;
+ position: relative;
+ font-size: inherit;
+ @include gl-text-gray-700;
+ @include gl-py-3;
+ @include gl-pl-6;
+ @include gl-my-3;
+ @include gl-mx-0;
+ @include gl-inset-border-l-4-gray-100;
+ margin-left: 4px;
+ border: 0 !important;
+ }
}
-}
-header,
-nav,
-.nav-sidebar,
-.super-sidebar,
-.profiler-results,
-.tree-ref-holder,
-.tree-holder .breadcrumb,
-.nav,
-.btn,
-ul.notes-form,
-.issuable-gutter-toggle,
-.gutter-toggle,
-.issuable-details .content-block-small,
-.edit-link,
-.note-action-button,
-.right-sidebar,
-.flash-container,
-copy-code,
-#js-peek {
- display: none !important;
-}
+ header,
+ nav,
+ .nav-sidebar,
+ .super-sidebar,
+ .profiler-results,
+ .tree-ref-holder,
+ .tree-holder .breadcrumb,
+ .nav,
+ .btn,
+ ul.notes-form,
+ .issuable-gutter-toggle,
+ .gutter-toggle,
+ .issuable-details .content-block-small,
+ .edit-link,
+ .note-action-button,
+ .right-sidebar,
+ .flash-container,
+ copy-code,
+ #js-peek {
+ display: none !important;
+ }
-pre {
- page-break-before: avoid;
- page-break-inside: auto;
-}
+ pre {
+ page-break-before: avoid;
+ page-break-inside: auto;
+ }
-.page-gutter {
- padding-top: 0;
- padding-left: 0;
-}
+ .page-gutter {
+ padding-top: 0;
+ padding-left: 0;
+ }
-.right-sidebar {
- top: 0;
-}
+ .right-sidebar {
+ top: 0;
+ }
-a[href]::after {
- content: none !important;
-}
+ a[href]::after {
+ content: none !important;
+ }
-.with-performance-bar .layout-page {
- padding-top: 0;
+ .with-performance-bar .layout-page {
+ padding-top: 0;
+ }
}
diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss
index 91b381462be..e1b14df683e 100644
--- a/app/assets/stylesheets/snippets.scss
+++ b/app/assets/stylesheets/snippets.scss
@@ -15,8 +15,7 @@
.gl-snippet-icon {
display: inline-block;
- /* stylelint-disable-next-line function-url-quotes */
- background: url(asset_path('ext_snippet_icons/ext_snippet_icons.png')) no-repeat;
+ background: url('ext_snippet_icons/ext_snippet_icons.png') no-repeat;
overflow: hidden;
text-align: left;
width: 16px;
diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb
index 7f1b961e92a..8bd120b5ed5 100644
--- a/app/controllers/concerns/preview_markdown.rb
+++ b/app/controllers/concerns/preview_markdown.rb
@@ -44,6 +44,7 @@ module PreviewMarkdown
when 'groups' then { group: group, issuable_reference_expansion_enabled: true }
when 'projects' then projects_filter_params
when 'timeline_events' then timeline_events_filter_params
+ when 'organizations' then { pipeline: :description }
else {}
end.merge(
requested_path: params[:path],
diff --git a/app/controllers/organizations/organizations_controller.rb b/app/controllers/organizations/organizations_controller.rb
index 9f09627b1e4..0596441591d 100644
--- a/app/controllers/organizations/organizations_controller.rb
+++ b/app/controllers/organizations/organizations_controller.rb
@@ -2,9 +2,11 @@
module Organizations
class OrganizationsController < ApplicationController
+ include PreviewMarkdown
+
feature_category :cell
- skip_before_action :authenticate_user!, except: [:index, :new, :users]
+ skip_before_action :authenticate_user!, only: [:show, :groups_and_projects]
def index; end
diff --git a/app/finders/ci/runner_jobs_finder.rb b/app/finders/ci/runner_jobs_finder.rb
index b659eda6646..91d8eccff21 100644
--- a/app/finders/ci/runner_jobs_finder.rb
+++ b/app/finders/ci/runner_jobs_finder.rb
@@ -13,7 +13,13 @@ module Ci
end
def execute
- items = @runner.builds
+ items = if params[:system_id].blank?
+ runner.builds
+ else
+ runner_manager = Ci::RunnerManager.for_runner(runner).with_system_xid(params[:system_id]).first
+ Ci::Build.belonging_to_runner_manager(runner_manager&.id)
+ end
+
items = by_permission(items)
items = by_status(items)
sort_items(items)
diff --git a/app/helpers/organizations/organization_helper.rb b/app/helpers/organizations/organization_helper.rb
index b6d39276a03..445dd3a1f6f 100644
--- a/app/helpers/organizations/organization_helper.rb
+++ b/app/helpers/organizations/organization_helper.rb
@@ -4,7 +4,8 @@ module Organizations
module OrganizationHelper
def organization_show_app_data(organization)
{
- organization: organization.slice(:id, :name).merge({ avatar_url: organization.avatar_url(size: 128) }),
+ organization: organization.slice(:id, :name, :description_html)
+ .merge({ avatar_url: organization.avatar_url(size: 128) }),
groups_and_projects_organization_path: groups_and_projects_organization_path(organization),
# TODO: Update counts to use real data
# https://gitlab.com/gitlab-org/gitlab/-/issues/424531
@@ -17,18 +18,14 @@ module Organizations
end
def organization_new_app_data
- {
- organizations_path: organizations_path,
- root_url: root_url
- }.to_json
+ shared_new_settings_general_app_data.to_json
end
def organization_settings_general_app_data(organization)
{
- organization: organization.slice(:id, :name, :path).merge({ avatar: organization.avatar_url(size: 192) }),
- organizations_path: organizations_path,
- root_url: root_url
- }.to_json
+ organization: organization.slice(:id, :name, :path, :description)
+ .merge({ avatar: organization.avatar_url(size: 192) })
+ }.merge(shared_new_settings_general_app_data).to_json
end
def organization_groups_and_projects_app_data
@@ -66,6 +63,14 @@ module Organizations
}
end
+ def shared_new_settings_general_app_data
+ {
+ preview_markdown_path: preview_markdown_organizations_path,
+ organizations_path: organizations_path,
+ root_url: root_url
+ }
+ end
+
# See UsersHelper#admin_users_paths for inspiration to this method
def organizations_users_paths
{
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 23185548554..66e9e20f3dc 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -190,6 +190,10 @@ module Ci
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123131
scope :with_runner_type, -> (runner_type) { joins(:runner).where(runner: { runner_type: runner_type }) }
+ scope :belonging_to_runner_manager, -> (runner_machine_id) {
+ joins(:runner_manager_build).where(p_ci_runner_machine_builds: { runner_machine_id: runner_machine_id })
+ }
+
scope :with_secure_reports_from_config_options, -> (job_types) do
joins(:metadata).where("#{Ci::BuildMetadata.quoted_table_name}.config_options -> 'artifacts' -> 'reports' ?| array[:job_types]", job_types: job_types)
end
diff --git a/app/models/ci/runner_manager.rb b/app/models/ci/runner_manager.rb
index 71005f4341a..44fe1bdd67d 100644
--- a/app/models/ci/runner_manager.rb
+++ b/app/models/ci/runner_manager.rb
@@ -55,6 +55,10 @@ module Ci
where(runner_id: runner_id)
end
+ scope :with_system_xid, ->(system_xid) do
+ where(system_xid: system_xid)
+ end
+
scope :with_running_builds, -> do
where('EXISTS(?)',
Ci::Build.select(1)
diff --git a/app/models/organizations/organization.rb b/app/models/organizations/organization.rb
index be96939daa8..7f86bf81375 100644
--- a/app/models/organizations/organization.rb
+++ b/app/models/organizations/organization.rb
@@ -30,7 +30,7 @@ module Organizations
'organizations/path': true,
length: { minimum: 2, maximum: 255 }
- delegate :description, :avatar, :avatar_url, :remove_avatar!, to: :organization_detail
+ delegate :description, :description_html, :avatar, :avatar_url, :remove_avatar!, to: :organization_detail
accepts_nested_attributes_for :organization_detail
diff --git a/app/models/organizations/organization_detail.rb b/app/models/organizations/organization_detail.rb
index b69ec5eae76..018e7579c5b 100644
--- a/app/models/organizations/organization_detail.rb
+++ b/app/models/organizations/organization_detail.rb
@@ -6,7 +6,7 @@ module Organizations
include Avatarable
include WithUploads
- cache_markdown_field :description
+ cache_markdown_field :description, pipeline: :description
belongs_to :organization, inverse_of: :organization_detail
diff --git a/config/routes/organizations.rb b/config/routes/organizations.rb
index 62c791cdf69..dbc9f2ce226 100644
--- a/config/routes/organizations.rb
+++ b/config/routes/organizations.rb
@@ -6,6 +6,10 @@ resources(
param: :organization_path,
module: :organizations
) do
+ collection do
+ post :preview_markdown
+ end
+
member do
get :groups_and_projects
get :users
diff --git a/doc/administration/auth/index.md b/doc/administration/auth/index.md
index 95268f6f39b..dd7f9cec77e 100644
--- a/doc/administration/auth/index.md
+++ b/doc/administration/auth/index.md
@@ -1,6 +1,7 @@
---
stage: Govern
group: Authentication
+description: Third-party authentication providers.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/administration/configure.md b/doc/administration/configure.md
index 8b4f8a9abc6..7ae088cd783 100644
--- a/doc/administration/configure.md
+++ b/doc/administration/configure.md
@@ -1,6 +1,7 @@
---
stage: Systems
group: Distribution
+description: Installation settings.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/administration/dedicated/index.md b/doc/administration/dedicated/index.md
index 05a241b65dc..c43169250f0 100644
--- a/doc/administration/dedicated/index.md
+++ b/doc/administration/dedicated/index.md
@@ -1,8 +1,8 @@
---
stage: SaaS Platforms
group: GitLab Dedicated
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments"
-description: 'Learn how to configure your GitLab Dedicated instance.'
+description: IP allowlists, SAML, maintenance.
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Configure GitLab Dedicated **(ULTIMATE)**
diff --git a/doc/administration/license.md b/doc/administration/license.md
index 1e77d83825d..9b95af28eed 100644
--- a/doc/administration/license.md
+++ b/doc/administration/license.md
@@ -40,7 +40,7 @@ The subscription is activated.
You can use one activation code or license key for multiple self-managed instances if the users on
these instances are the same or are a subset of your licensed production instance. This means that if
-you have a licensed production instance of GitLab, and other instances with the same list of users, the
+you have a licensed production instance of GitLab, and other instances with the same list of users, the
production activation code applies, even if these users are configured in different groups and projects.
### Uploading licenses for scaled architectures
diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md
index 33f407b2dbf..d1ec818a2a5 100644
--- a/doc/administration/operations/index.md
+++ b/doc/administration/operations/index.md
@@ -1,6 +1,7 @@
---
stage: Systems
group: Distribution
+description: Backup and restore, move repos, maintenance tasks.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md
index a9e3f2c1109..7e4e929f80d 100644
--- a/doc/administration/reference_architectures/index.md
+++ b/doc/administration/reference_architectures/index.md
@@ -1,6 +1,7 @@
---
stage: Systems
group: Distribution
+description: Recommended deployments at scale.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/administration/settings/index.md b/doc/administration/settings/index.md
index 531073887c5..2df9d1cd52d 100644
--- a/doc/administration/settings/index.md
+++ b/doc/administration/settings/index.md
@@ -1,6 +1,7 @@
---
stage: none
group: unassigned
+description: Product settings.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/api/runners.md b/doc/api/runners.md
index ac854a477d3..373fc4e4344 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -14,6 +14,7 @@ This page describes endpoints for runners registered to an instance. To create a
GET /runners
GET /runners/all
GET /runners/:id/jobs
+GET /runners/:id/managers/:system_id/jobs
GET /projects/:id/runners
GET /groups/:id/runners
```
@@ -367,7 +368,7 @@ NOTE:
The `active` form attribute was deprecated [in GitLab 14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/347211).
and will be removed in [a future version of the REST API](https://gitlab.com/gitlab-org/gitlab/-/issues/351109). It is replaced by the `paused` attribute.
-## List runner's jobs
+## List jobs processed by a runner
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/15432) in GitLab 10.3.
@@ -378,12 +379,13 @@ to projects where the user has at least the Reporter role.
GET /runners/:id/jobs
```
-| Attribute | Type | Required | Description |
-|-----------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a runner |
-| `status` | string | no | Status of the job; one of: `running`, `success`, `failed`, `canceled` |
-| `order_by`| string | no | Order jobs by `id` |
-| `sort` | string | no | Sort jobs in `asc` or `desc` order (default: `desc`). Specify `order_by` as well, including for `id`. |
+| Attribute | Type | Required | Description |
+|-------------|---------|----------|---------------------|
+| `id` | integer | yes | The ID of a runner |
+| `system_id` | string | no | System ID of the machine where the runner manager is running |
+| `status` | string | no | Status of the job; one of: `running`, `success`, `failed`, `canceled` |
+| `order_by` | string | no | Order jobs by `id` |
+| `sort` | string | no | Sort jobs in `asc` or `desc` order (default: `desc`). If `sort` is specified, `order_by` must be specified as well |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/1/jobs?status=running"
diff --git a/doc/architecture/blueprints/cells/routing-service.md b/doc/architecture/blueprints/cells/routing-service.md
index 2b4fa88f187..bd5570b68f4 100644
--- a/doc/architecture/blueprints/cells/routing-service.md
+++ b/doc/architecture/blueprints/cells/routing-service.md
@@ -378,7 +378,7 @@ Each Cell does implement classification endpoint:
requests for sharding keys that are not found.
- The cached response is for time defined by `expiry` and `refresh`.
- The `expiry` defines when the item is removed from cache unless used.
- - The `refresh` defines when the item needs to be reclassified if used.
+ - The `refresh` defines when the item needs to be reclassified if used.
- The refresh is done asynchronously as the request should be served without a delay if they were classified. The refresh is done to ensure that cache is always hot and up-to date.
For the above example:
diff --git a/doc/architecture/blueprints/runner_admission_controller/index.md b/doc/architecture/blueprints/runner_admission_controller/index.md
index 21dc1d53303..0a62b271901 100644
--- a/doc/architecture/blueprints/runner_admission_controller/index.md
+++ b/doc/architecture/blueprints/runner_admission_controller/index.md
@@ -140,7 +140,7 @@ Each runner has a tag identifier unique to that runner, e.g. `DiscoveryOne`, `tu
1. The `preparing` state will wait for a response from the webhook or until timeout.
1. The UI should be updated with the current status of the job prerequisites and admission
1. For jobs where the webhook times out (1 hour) their status should be set as though the admission was denied with a timeout reasoning. This should
-be rare in typical circumstances.
+ be rare in typical circumstances.
1. Jobs with denied admission can be retried. Retried jobs will be resent to the admission controller without tag mutations or runner filtering reset.
1. [`allow_failure`](../../../ci/yaml/index.md#allow_failure) should be updated to support jobs that fail on denied admissions, for example:
diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md
index f2e9d624d20..c667a460f5c 100644
--- a/doc/architecture/blueprints/runner_tokens/index.md
+++ b/doc/architecture/blueprints/runner_tokens/index.md
@@ -284,11 +284,11 @@ not an issue per-se.
New records are created in 2 situations:
-- when the runner calls the `POST /api/v4/runners/verify` endpoint as part of the
-`gitlab-runner register` command, if the specified runner token is prefixed with `glrt-`.
-This allows the frontend to determine whether the user has successfully completed the registration and take an
-appropriate action;
-- when GitLab is pinged for new jobs and a record matching the `token`+`system_id` does not already exist.
+- When the runner calls the `POST /api/v4/runners/verify` endpoint as part of the
+ `gitlab-runner register` command, if the specified runner token is prefixed with `glrt-`.
+ This allows the frontend to determine whether the user has successfully completed the registration and take an
+ appropriate action;
+- When GitLab is pinged for new jobs and a record matching the `token`+`system_id` does not already exist.
Due to the time-decaying nature of the `ci_runner_machines` records, they are automatically
cleaned after 7 days after the last contact from the respective runner.
diff --git a/doc/architecture/blueprints/secret_manager/index.md b/doc/architecture/blueprints/secret_manager/index.md
index ac30f3399d8..3a538f58dde 100644
--- a/doc/architecture/blueprints/secret_manager/index.md
+++ b/doc/architecture/blueprints/secret_manager/index.md
@@ -114,10 +114,10 @@ the data keys mentioned above.
### Further investigations required
1. Management of identities stored in GCP Key Management.
-We need to investigate how we can correlate and de-multiplex GitLab identities into
-GCP identities that are used to allow access to cryptographic operations on GCP Key Management.
+ We need to investigate how we can correlate and de-multiplex GitLab identities into
+ GCP identities that are used to allow access to cryptographic operations on GCP Key Management.
1. Authentication of clients. Clients to the Secrets Manager could be GitLab Runner or external clients.
-For each of these, we need a secure and reliable method to authenticate requests to decrypt a secret.
+ For each of these, we need a secure and reliable method to authenticate requests to decrypt a secret.
1. Assignment of GCP backed private keys to each identity.
### Availability on SaaS and Self-Managed
diff --git a/doc/ci/runners/runner_fleet_dashboard.md b/doc/ci/runners/runner_fleet_dashboard.md
index 08579f6ff92..ce487ff7386 100644
--- a/doc/ci/runners/runner_fleet_dashboard.md
+++ b/doc/ci/runners/runner_fleet_dashboard.md
@@ -17,6 +17,8 @@ The Runner Fleet Dashboard shows:
- Number of concurrent jobs executed on most busy runners.
- Histogram of job queue times [(available only with ClickHouse)](#enable-more-ci-analytics-features-with-clickhouse).
+Support for usage and cost analysis are proposed in [epic 11183](https://gitlab.com/groups/gitlab-org/-/epics/11183).
+
![Runner Fleet Dashboard](img/runner_fleet_dashboard.png)
## View the Runner Fleet Dashboard
@@ -32,7 +34,7 @@ To view the runner fleet dashboard:
1. Click **Fleet dashboard**.
Most of the dashboard works without any additional actions, with the
-exception of **Wait time to pick a job** chart and [proposed features](#whats-next).
+exception of **Wait time to pick a job** chart and features proposed in [epic 11183](https://gitlab.com/groups/gitlab-org/-/epics/11183).
These features require [setting up an additional infrastructure](#enable-more-ci-analytics-features-with-clickhouse).
## Enable more CI analytics features with ClickHouse **(ULTIMATE EXPERIMENT)**
@@ -41,7 +43,7 @@ These features require [setting up an additional infrastructure](#enable-more-ci
This feature is an [Experiment](../../policy/experiment-beta-support.md).
To test it, we have launched an early adopters program.
-To join the list of users testing this feature, contact us in
+To join the list of users testing this feature, see
[epic 11180](https://gitlab.com/groups/gitlab-org/-/epics/11180).
### Enable ClickHouse integration and features
@@ -53,17 +55,12 @@ To enable additional CI analytics features:
| Feature flag name | Purpose |
|------------------------------------|---------------------------------------------------------------------------|
- | `ci_data_ingestion_to_click_house` | Enables synchronization of new finished CI builds to Clickhouse database. |
+ | `ci_data_ingestion_to_click_house` | Enables synchronization of new finished CI builds to ClickHouse database. |
| `clickhouse_ci_analytics` | Enables the **Wait time to pick a job** chart. |
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a video walkthrough, see [Setting up Runner Fleet Dashboard with ClickHouse](https://www.youtube.com/watch?v=YpGV95Ctbpk).
-### What's next
-
-Support for usage and cost analysis are proposed in
-[epic 11183](https://gitlab.com/groups/gitlab-org/-/epics/11183).
-
## Feedback
To help us improve the Runner Fleet Dashboard, you can provide feedback in
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 84d2537d058..cad71d4b843 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -376,7 +376,7 @@ Avoid:
[_explain why, not what_](https://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/).
- Requesting maintainer reviews of merge requests with failed tests. If the tests are failing and you have to request a review, ensure you leave a comment with an explanation.
- Excessively mentioning maintainers through email or Slack (if the maintainer is reachable
-through Slack). If you can't add a reviewer for a merge request, `@` mentioning a maintainer in a comment is acceptable and in all other cases adding a reviewer is sufficient.
+ through Slack). If you can't add a reviewer for a merge request, `@` mentioning a maintainer in a comment is acceptable and in all other cases adding a reviewer is sufficient.
This saves reviewers time and helps authors catch mistakes earlier.
@@ -412,7 +412,7 @@ that it meets all requirements, you should:
- Select **Approve**.
- `@` mention the author to generate a to-do notification, and advise them that their merge request has been reviewed and approved.
- Request a review from a maintainer. Default to requests for a maintainer with [domain expertise](#domain-experts),
-however, if one isn't available or you think the merge request doesn't need a review by a [domain expert](#domain-experts), feel free to follow the [Reviewer roulette](#reviewer-roulette) suggestion.
+ however, if one isn't available or you think the merge request doesn't need a review by a [domain expert](#domain-experts), feel free to follow the [Reviewer roulette](#reviewer-roulette) suggestion.
- Remove yourself as a reviewer.
### The responsibility of the maintainer
@@ -580,7 +580,7 @@ experience, refactors the existing code). Then:
optionally resolve within the merge request or follow-up at a later stage.
- There's a [Chrome/Firefox add-on](https://gitlab.com/conventionalcomments/conventional-comments-button) which you can use to apply [Conventional Comment](https://conventionalcomments.org/) prefixes.
- Ensure there are no open dependencies. Check [linked issues](../user/project/issues/related_issues.md) for blockers. Clarify with the authors
-if necessary. If blocked by one or more open MRs, set an [MR dependency](../user/project/merge_requests/dependencies.md).
+ if necessary. If blocked by one or more open MRs, set an [MR dependency](../user/project/merge_requests/dependencies.md).
- After a round of line notes, it can be helpful to post a summary note such as
"Looks good to me", or "Just a couple things to address."
- Let the author know if changes are required following your review.
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
index a0888006937..c6027e27310 100644
--- a/doc/development/contributing/design.md
+++ b/doc/development/contributing/design.md
@@ -19,12 +19,12 @@ with additions and improvements.
As a merge request (MR) author, you must:
- Include _Before_ and _After_
-screenshots (or videos) of your changes in the description, as explained in our
-[MR workflow](merge_request_workflow.md). These screenshots/videos are very helpful
-for all reviewers and can speed up the review process, especially if the changes
-are small.
+ screenshots (or videos) of your changes in the description, as explained in our
+ [MR workflow](merge_request_workflow.md). These screenshots/videos are very helpful
+ for all reviewers and can speed up the review process, especially if the changes
+ are small.
- Attach the ~UX label to any merge request that has any user facing changes. This will trigger our
-Reviewer Roulette to suggest a UX [reviewer](https://about.gitlab.com/handbook/product/ux/product-designer/mr-reviews/#stage-group-mrs).
+ Reviewer Roulette to suggest a UX [reviewer](https://about.gitlab.com/handbook/product/ux/product-designer/mr-reviews/#stage-group-mrs).
If you are a **team member**: We recommend assigning the Product Designer suggested by the
[Reviewer Roulette](../code_review.md#reviewer-roulette) as reviewer. [This helps us](https://about.gitlab.com/handbook/product/ux/product-designer/mr-reviews/#benefits) spread work evenly, improve communication, and make our UI more
diff --git a/doc/development/documentation/site_architecture/global_nav.md b/doc/development/documentation/site_architecture/global_nav.md
index f5edbec88e1..be10688766f 100644
--- a/doc/development/documentation/site_architecture/global_nav.md
+++ b/doc/development/documentation/site_architecture/global_nav.md
@@ -52,7 +52,7 @@ the consent of one of the technical writers.
To add a topic to the global navigation:
1. In the [`navigation.yaml`](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/content/_data/navigation.yaml)
-file, add the item.
+ file, add the item.
1. Assign the MR to a technical writer for review and merge.
### Where to add
diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md
index 03a4b3eec94..c7783157575 100644
--- a/doc/development/documentation/styleguide/index.md
+++ b/doc/development/documentation/styleguide/index.md
@@ -1140,7 +1140,7 @@ When you take screenshots:
Reduce the size of your browser window as much as possible to keep elements close
together and reduce empty space. Try to keep the screenshot dimensions as small as possible.
- **Review how the image renders on the page.** Preview the image locally or use the
-review app in the merge request. Make sure the image isn't blurry or overwhelming.
+ review app in the merge request. Make sure the image isn't blurry or overwhelming.
- **Be consistent.** Coordinate screenshots with the other screenshots already on
a documentation page for a consistent reading experience. Ensure your navigation theme
is **Indigo** and the syntax highlighting theme is **Light**. These are the default preferences.
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index dfc18cf4139..fed295b8ec9 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -1595,7 +1595,7 @@ Searching is different from [filtering](#filter).
When referring to the subscription billing model:
- For GitLab SaaS, use **seats**. Customers purchase seats. Users occupy seats when they are invited
-to a group, with some [exceptions](../../../subscriptions/gitlab_com/index.md#how-seat-usage-is-determined).
+ to a group, with some [exceptions](../../../subscriptions/gitlab_com/index.md#how-seat-usage-is-determined).
- For GitLab self-managed, use **users**. Customers purchase subscriptions for a specified number of **users**.
## section
diff --git a/doc/development/documentation/topic_types/troubleshooting.md b/doc/development/documentation/topic_types/troubleshooting.md
index aee5bd1377c..f970b58e4fc 100644
--- a/doc/development/documentation/topic_types/troubleshooting.md
+++ b/doc/development/documentation/topic_types/troubleshooting.md
@@ -64,7 +64,7 @@ The workaround is...
If multiple causes or solutions exist, consider putting them into a table format.
If you use the exact error message, surround it in backticks so it's styled as code.
-For more guidance on solution types, see [workaround](../../documentation/styleguide/word_list.md#workaround) and [resolution, resolve](../../documentation/styleguide/word_list.md#resolution-resolve).
+For more guidance on solution types, see [workaround](../../documentation/styleguide/word_list.md#workaround) and [resolution, resolve](../../documentation/styleguide/word_list.md#resolution-resolve).
## Troubleshooting topic titles
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 08045675295..78177612aa9 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -200,7 +200,7 @@ To guard your licensed feature:
```
1. Optional. If your global feature is also available to namespaces with a paid plan, combine two
-feature identifiers to allow both administrators and group users. For example:
+ feature identifiers to allow both administrators and group users. For example:
```ruby
License.feature_available?(:my_feature_name) || group.licensed_feature_available?(:my_feature_name_for_namespace) # Both admins and group members can see this EE feature
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index a9f4b22c778..c6471d9720c 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -26,7 +26,7 @@ can still have specifics. They are described in their respective
The Go upgrade documentation [provides an overview](go_upgrade.md#overview)
of how GitLab manages and ships Go binary support.
-If a GitLab component requires a newer version of Go,
+If a GitLab component requires a newer version of Go,
follow the [upgrade process](go_upgrade.md#updating-go-version) to ensure no customer, team, or component is adversely impacted.
Sometimes, individual projects must also [manage builds with multiple versions of Go](go_upgrade.md#supporting-multiple-go-versions).
diff --git a/doc/development/i18n/translation.md b/doc/development/i18n/translation.md
index 7149d431c30..ed3afb8efa6 100644
--- a/doc/development/i18n/translation.md
+++ b/doc/development/i18n/translation.md
@@ -96,7 +96,7 @@ For example, in German, the word _user_ can be translated into _Benutzer_ (male)
### Updating the glossary
-To propose additions to the glossary,
+To propose additions to the glossary,
[open an issue](https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&utf8=✓&state=all&label_name[]=Category%3AInternationalization).
## French translation guidelines
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 30f598ef736..be76680c44c 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -723,7 +723,7 @@ The `with_lock_retries` method **cannot** be used within the `change` method, yo
1. For each iteration, set a pre-configured `lock_timeout`.
1. Try to execute the given block. (`remove_column`).
1. If `LockWaitTimeout` error is raised, sleep for the pre-configured `sleep_time`
-and retry the block.
+ and retry the block.
1. If no error is raised, the current iteration has successfully executed the block.
For more information check the [`Gitlab::Database::WithLockRetries`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/database/with_lock_retries.rb) class. The `with_lock_retries` helper method is implemented in the [`Gitlab::Database::MigrationHelpers`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/database/migration_helpers.rb) module.
@@ -733,7 +733,7 @@ In a worst-case scenario, the method:
- Executes the block for a maximum of 50 times over 40 minutes.
- Most of the time is spent in a pre-configured sleep period after each iteration.
- After the 50th retry, the block is executed without `lock_timeout`, just
-like a standard migration invocation.
+ like a standard migration invocation.
- If a lock cannot be acquired, the migration fails with `statement timeout` error.
The migration might fail if there is a very long running transaction (40+ minutes)
diff --git a/doc/development/sec/analyzer_development_guide.md b/doc/development/sec/analyzer_development_guide.md
index eb59d8fcaf5..843f41300a2 100644
--- a/doc/development/sec/analyzer_development_guide.md
+++ b/doc/development/sec/analyzer_development_guide.md
@@ -128,7 +128,7 @@ To use Docker with `replace` in the `go.mod` file:
1. Copy the contents of `command` into the directory of the analyzer. `cp -r /path/to/command path/to/analyzer/command`.
1. Add a copy statement in the analyzer's `Dockerfile`: `COPY command /command`.
1. Update the `replace` statement to make sure it matches the destination of the `COPY` statement in the step above:
-`replace gitlab.com/gitlab-org/security-products/analyzers/command/v3 => /command`
+ `replace gitlab.com/gitlab-org/security-products/analyzers/command/v3 => /command`
## Analyzer scripts
@@ -189,11 +189,11 @@ are integrated with the existing application, iteration should not be blocked by
1. Ensure that the release source (typically the `master` or `main` branch) has a passing pipeline.
1. Create a new release for the analyzer project by selecting the **Deployments** menu on the left-hand side of the project window, then selecting the **Releases** sub-menu.
1. Select **New release** to open the **New Release** page.
- 1. In the **Tag name** drop down, enter the same version used in the `CHANGELOG.md`, for example `v2.4.2`, and select the option to create the tag (`Create tag v2.4.2` here).
- 1. In the **Release title** text box enter the same version used above, for example `v2.4.2`.
- 1. In the `Release notes` text box, copy and paste the notes from the corresponding version in the `CHANGELOG.md`.
- 1. Leave all other settings as the default values.
- 1. Select **Create release**.
+ 1. In the **Tag name** drop down, enter the same version used in the `CHANGELOG.md`, for example `v2.4.2`, and select the option to create the tag (`Create tag v2.4.2` here).
+ 1. In the **Release title** text box enter the same version used above, for example `v2.4.2`.
+ 1. In the `Release notes` text box, copy and paste the notes from the corresponding version in the `CHANGELOG.md`.
+ 1. Leave all other settings as the default values.
+ 1. Select **Create release**.
After following the above process and creating a new release, a new Git tag is created with the `Tag name` provided above. This triggers a new pipeline with the given tag version and a new analyzer Docker image is built.
@@ -229,7 +229,7 @@ After the above steps have been completed, the automatic release process execute
1. After a new version of the analyzer Docker image has been tagged and deployed, test it with the corresponding test project.
1. Announce the release on the relevant group Slack channel. Example message:
- > FYI I've just released `ANALYZER_NAME` `ANALYZER_VERSION`. `LINK_TO_RELEASE`
+ > FYI I've just released `ANALYZER_NAME` `ANALYZER_VERSION`. `LINK_TO_RELEASE`
**Never delete a Git tag that has been pushed** as there is a good
chance that the tag will be used and/or cached by the Go package registry.
diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md
index a575d1ff890..d8fad6deb9c 100644
--- a/doc/development/secure_coding_guidelines.md
+++ b/doc/development/secure_coding_guidelines.md
@@ -182,7 +182,7 @@ For other regular expressions, here are a few guidelines:
- If there's a clean non-regex solution, such as `String#start_with?`, consider using it
- Ruby supports some advanced regex features like [atomic groups](https://www.regular-expressions.info/atomic.html)
-and [possessive quantifiers](https://www.regular-expressions.info/possessive.html) that eliminate backtracking
+ and [possessive quantifiers](https://www.regular-expressions.info/possessive.html) that eliminate backtracking
- Avoid nested quantifiers if possible (for example `(a+)+`)
- Try to be as precise as possible in your regex and avoid the `.` if there's an alternative
- For example, Use `_[^_]+_` instead of `_.*_` to match `_text here_`
diff --git a/doc/development/testing_guide/end_to_end/feature_flags.md b/doc/development/testing_guide/end_to_end/feature_flags.md
index e11119d2c0b..189b21ea607 100644
--- a/doc/development/testing_guide/end_to_end/feature_flags.md
+++ b/doc/development/testing_guide/end_to_end/feature_flags.md
@@ -24,14 +24,14 @@ Be sure to include the `feature_flag` tag so that the test can be skipped on the
- Format: `feature_flag: { name: 'feature_flag_name' }`
- Used only for informational purposes at this time. It should be included to help quickly determine what
-feature flag is under test.
+ feature flag is under test.
`scope`
- Format: `feature_flag: { name: 'feature_flag_name', scope: :project }`
- When `scope` is set to `:global`, the test will be **skipped on all live .com environments**. This is to avoid issues with feature flag changes affecting other tests or users on that environment.
- When `scope` is set to any other value (such as `:project`, `:group` or `:user`), or if no `scope` is specified, the test will only be **skipped on canary, production, and pre-production**.
-This is due to the fact that administrator access is not available there.
+ This is due to the fact that administrator access is not available there.
**WARNING:** You are strongly advised to first try and [enable feature flags only for a group, project, user](../../feature_flags/index.md#feature-actors),
or [feature group](../../feature_flags/index.md#feature-groups).
diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md
index 1895b9bdb39..4d6fac57c06 100644
--- a/doc/development/testing_guide/flaky_tests.md
+++ b/doc/development/testing_guide/flaky_tests.md
@@ -119,7 +119,7 @@ Adding a delay in API or controller could help reproducing the issue.
- [Example 2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101728/diffs): A CSS selector
only appears after a GraphQL requests has finished, and the UI has updated.
- [Example 3](https://gitlab.com/gitlab-org/gitlab/-/issues/408215): A false-positive test, Capybara immediately returns true after
-page visit and page is not fully loaded, or if the element is not detectable by webdriver (such as being rendered outside the viewport or behind other elements).
+ page visit and page is not fully loaded, or if the element is not detectable by webdriver (such as being rendered outside the viewport or behind other elements).
### Datetime-sensitive
diff --git a/doc/install/cloud_providers.md b/doc/install/cloud_providers.md
index 318895b6d89..407c2152569 100644
--- a/doc/install/cloud_providers.md
+++ b/doc/install/cloud_providers.md
@@ -1,8 +1,8 @@
---
stage: Systems
group: Distribution
+description: AWS, Google Cloud Platform, Azure.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
-description: Install GitLab on a cloud provider.
---
# Installing GitLab on a cloud provider **(FREE SELF)**
diff --git a/doc/install/install_methods.md b/doc/install/install_methods.md
index b40d89e957a..99be2709564 100644
--- a/doc/install/install_methods.md
+++ b/doc/install/install_methods.md
@@ -1,8 +1,8 @@
---
stage: Systems
group: Distribution
+description: Linux, Helm, Docker, Operator, source, or scripts.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
-description: Read through the GitLab installation methods.
---
# Installation methods **(FREE SELF)**
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index fa6be957d99..e3905dd4882 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -1,6 +1,7 @@
---
stage: Systems
group: Distribution
+description: Prerequisites for installation.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/security/index.md b/doc/security/index.md
index 01f227efbf9..ffc436e4286 100644
--- a/doc/security/index.md
+++ b/doc/security/index.md
@@ -1,6 +1,7 @@
---
stage: Govern
group: Authentication
+description: SSH key limits, 2FA, tokens, hardening.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md
index 76b6c59c9fe..74ecf1701a4 100644
--- a/doc/subscriptions/gitlab_com/index.md
+++ b/doc/subscriptions/gitlab_com/index.md
@@ -232,13 +232,13 @@ After you dismiss the alert, it doesn't display until another seat is used.
The alert displays based on the following seat usage. You cannot configure the
amounts at which the alert displays.
-| Seats in subscription | Seat usage |
-|-----------------------|------------------------------------------------------------------------|
-| 0-15 | One seat remaining in the subscription. |
-| 16-25 | Two seats remaining in the subscription. |
-| 26-99 | 10% of seats have been used. |
-| 100-999 | 8% of seats have been used. |
-| 1000+ | 5% of seats have been used |
+| Seats in subscription | Seat usage |
+|-----------------------|------------|
+| 0-15 | One seat remaining in the subscription. |
+| 16-25 | Two seats remaining in the subscription. |
+| 26-99 | 10% of seats have been used. |
+| 100-999 | 8% of seats have been used. |
+| 1000+ | 5% of seats have been used |
## Change the linked namespace
@@ -324,8 +324,8 @@ You can only renew your subscription 15 days before it is due to expire.
To renew your subscription:
1. Sign in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) and beneath your existing subscription, select **Renew**.
-The **Renew** button displays only 15 days before a subscription expires. If there are more than 15 days before
-the subscription expires, select **Subscription actions** (**{ellipsis_v}**), then select **Renew subscription** to view the date when you can renew.
+ The **Renew** button displays only 15 days before a subscription expires. If there are more than 15 days before
+ the subscription expires, select **Subscription actions** (**{ellipsis_v}**), then select **Renew subscription** to view the date when you can renew.
1. Review your renewal details and complete the payment process.
1. Select **Confirm purchase**.
diff --git a/doc/topics/offline/index.md b/doc/topics/offline/index.md
index b29a06463ba..b463abc79d3 100644
--- a/doc/topics/offline/index.md
+++ b/doc/topics/offline/index.md
@@ -1,6 +1,7 @@
---
stage: Systems
group: Distribution
+description: Isolated installation.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/update/versions/gitlab_16_changes.md b/doc/update/versions/gitlab_16_changes.md
index 3bf01e9143e..8efa056e70d 100644
--- a/doc/update/versions/gitlab_16_changes.md
+++ b/doc/update/versions/gitlab_16_changes.md
@@ -81,7 +81,7 @@ Specific information applies to Linux package installations:
- PostgreSQL version 14 is the default for fresh installations of GitLab 16.7 and later. However, due to an [issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/7768#note_1652076255), existing Geo secondary sites cannot be upgraded to PostgreSQL version 14. All Geo sites must run the same version of PostgreSQL. If you are adding a new Geo secondary site based on GitLab 16.7 you must take one of the following actions based on your configuration:
- You are adding your first Geo secondary site: [Upgrade the Primary site to PostgreSQL 14](https://docs.gitlab.com/omnibus/settings/database.html#upgrade-packaged-postgresql-server) before setting up the new Geo secondary site. No special action is required if your primary site is already running PostgreSQL 14.
- - You are adding a new Geo secondary site to a deployment that already has one or more Geo secondaries:
+ - You are adding a new Geo secondary site to a deployment that already has one or more Geo secondaries:
- All sites are running PostgreSQL 13: Install the new Geo secondary site with [pinned PostgreSQL version 13](https://docs.gitlab.com/omnibus/settings/database.html#pin-the-packaged-postgresql-version-fresh-installs-only).
- All sites are running PostgreSQL 14: No special action is required.
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index a3f12d157f9..d8eab5f9da8 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -128,10 +128,6 @@ Setting `CS_DEFAULT_BRANCH_IMAGE` avoids duplicate vulnerability findings when a
The value of `CS_DEFAULT_BRANCH_IMAGE` indicates the name of the scanned image as it appears on the default branch.
For more details on how this deduplication is achieved, see [Setting the default branch image](#setting-the-default-branch-image).
-## Running jobs in merge request pipelines
-
-See [Use security scanning tools with merge request pipelines](../index.md#use-security-scanning-tools-with-merge-request-pipelines)
-
### Customizing the container scanning settings
There may be cases where you want to customize how GitLab scans your containers. For example, you
@@ -243,6 +239,10 @@ if [Dependency Scanning](../dependency_scanning/index.md)
is enabled for your project. This happens because GitLab can't automatically deduplicate findings
across different types of scanning tools. To understand which types of dependencies are likely to be duplicated, see [Dependency Scanning compared to Container Scanning](../comparison_dependency_and_container_scanning.md).
+#### Running jobs in merge request pipelines
+
+See [Use security scanning tools with merge request pipelines](../index.md#use-security-scanning-tools-with-merge-request-pipelines).
+
#### Available CI/CD variables
You can [configure](#customizing-the-container-scanning-settings) analyzers by using the following CI/CD variables.
diff --git a/doc/user/application_security/dependency_scanning/troubleshooting_dependency_scanning.md b/doc/user/application_security/dependency_scanning/troubleshooting_dependency_scanning.md
index dae72e1a555..83004459051 100644
--- a/doc/user/application_security/dependency_scanning/troubleshooting_dependency_scanning.md
+++ b/doc/user/application_security/dependency_scanning/troubleshooting_dependency_scanning.md
@@ -125,7 +125,7 @@ The lock file is cached during the build phase and passed to the dependency scan
scan occurs. Because the cache is downloaded before the analyzer run occurs, the existence of a lock
file in the `CI_BUILDS_DIR` directory triggers the dependency scanning job.
-To prevent this warning, lock files should be committed.
+To prevent this warning, lock files should be committed.
## You no longer get the latest Docker image after setting `DS_MAJOR_VERSION` or `DS_ANALYZER_IMAGE`
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index f309b0f11fb..b626bdef929 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -318,10 +318,6 @@ When downloading, you always receive the most recent SAST artifact available.
You can enable and configure SAST by using the UI, either with the default settings or with customizations.
The method you can use depends on your GitLab license tier.
-### Running jobs in merge request pipelines
-
-See [Use security scanning tools with merge request pipelines](../index.md#use-security-scanning-tools-with-merge-request-pipelines)
-
#### Configure SAST with customizations **(ULTIMATE ALL)**
> [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/410013) individual SAST analyzers configuration options from the UI in GitLab 16.2.
@@ -520,6 +516,10 @@ spotbugs-sast:
sast: gl-sast-report.json
```
+### Running jobs in merge request pipelines
+
+See [Use security scanning tools with merge request pipelines](../index.md#use-security-scanning-tools-with-merge-request-pipelines).
+
### Available CI/CD variables
SAST can be configured using the [`variables`](../../../ci/yaml/index.md#variables) parameter in
diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md
index 0eb79bfbe5a..9e2d67237d3 100644
--- a/doc/user/application_security/secret_detection/index.md
+++ b/doc/user/application_security/secret_detection/index.md
@@ -110,10 +110,6 @@ Secret Detection can detect if a secret was added in one commit and removed in a
[merge request pipelines](../../../ci/pipelines/merge_request_pipelines.md). Secret Detection's
results are only available after the pipeline is completed.
-## Running jobs in merge request pipelines
-
-See [Use security scanning tools with merge request pipelines](../index.md#use-security-scanning-tools-with-merge-request-pipelines)
-
## Enable Secret Detection
Prerequisites:
@@ -265,6 +261,10 @@ For example:
"A personal token for GitLab will look like glpat-JUST20LETTERSANDNUMB" #gitleaks:allow
```
+### Running jobs in merge request pipelines
+
+See [Use security scanning tools with merge request pipelines](../index.md#use-security-scanning-tools-with-merge-request-pipelines).
+
### Available CI/CD variables
Secret Detection can be customized by defining available CI/CD variables:
diff --git a/doc/user/feature_flags.md b/doc/user/feature_flags.md
index 83e2926e8e3..ccce9e9f9b4 100644
--- a/doc/user/feature_flags.md
+++ b/doc/user/feature_flags.md
@@ -1,8 +1,8 @@
---
stage: none
group: unassigned
-info: "See the Technical Writers assigned to Development Guidelines: https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-development-guidelines"
-description: "View a list of all the flags available in the GitLab application."
+description: Complete list of flags.
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
layout: 'feature_flags'
---
diff --git a/doc/user/organization/index.md b/doc/user/organization/index.md
index c4fff4178f1..ecc62b0d510 100644
--- a/doc/user/organization/index.md
+++ b/doc/user/organization/index.md
@@ -44,6 +44,8 @@ To view the organizations you have access to:
1. On the left sidebar, at the top, select **Create new** (**{plus}**) and **New organization**.
1. In the **Organization name** text box, enter a name for the organization.
1. In the **Organization URL** text box, enter a path for the organization.
+1. In the **Organization description** text box, enter a description for the organization. Supports a [limited subset of Markdown](#supported-markdown-for-organization-description).
+1. In the **Organization avatar** field, select **Upload** or drag and drop an avatar.
1. Select **Create organization**.
## Edit an organization's name
@@ -51,6 +53,10 @@ To view the organizations you have access to:
1. On the left sidebar, select **Organizations** (**{organization}**) and find the organization you want to edit.
1. Select **Settings > General**.
1. In the **Organization name** text box, edit the name.
+1. In the **Organization description** text box, edit the description. Supports a [limited subset of Markdown](#supported-markdown-for-organization-description).
+1. In the **Organization avatar** field, if an avatar is:
+ - Selected, select **Remove avatar** to remove.
+ - Not selected, select **Upload** or drag and drop an avatar.
1. Select **Save changes**.
## Change an organization's URL
@@ -72,6 +78,14 @@ To view the organizations you have access to:
1. On the left sidebar, select **Organizations** (**{organization}**) and find the organization you want to manage.
1. Select **Manage > Users**.
+## Supported Markdown for Organization description
+
+The Organization description field supports a limited subset of [GitLab Flavored Markdown](../markdown.md), including:
+
+- [Emphasis](../markdown.md#emphasis)
+- [Links](../markdown.md#links)
+- [Superscripts / Subscripts](../markdown.md#superscripts--subscripts)
+
## Related topics
- [Organization developer documentation](../../development/organization/index.md)
diff --git a/doc/user/profile/account/create_accounts.md b/doc/user/profile/account/create_accounts.md
index 6c78152fa70..4611b3a6a40 100644
--- a/doc/user/profile/account/create_accounts.md
+++ b/doc/user/profile/account/create_accounts.md
@@ -1,6 +1,7 @@
---
stage: Govern
group: Authentication
+description: Passwords, user moderation, broadcast messages.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/user/project/repository/code_suggestions/self_managed.md b/doc/user/project/repository/code_suggestions/self_managed.md
index 5688985ab49..8cd499c13d0 100644
--- a/doc/user/project/repository/code_suggestions/self_managed.md
+++ b/doc/user/project/repository/code_suggestions/self_managed.md
@@ -139,7 +139,7 @@ In GitLab 16.3 and later, GitLab is enforcing the cloud licensing requirement fo
If you have a GitLab Free subscription and upgrade to GitLab 16.3 or later,
to continue having early access to Code Suggestions, you must:
-1. Have a [subscription that supports cloud licensing](https://about.gitlab.com/pricing/).
+1. Have a [subscription that supports cloud licensing](https://about.gitlab.com/pricing/licensing-faq/cloud-licensing/).
1. Make sure you have the latest version of your [IDE extension](index.md#supported-editor-extensions).
1. [Manually synchronize your subscription](#manually-synchronize-your-subscription).
diff --git a/gems/gitlab-database-load_balancing/Gemfile.lock b/gems/gitlab-database-load_balancing/Gemfile.lock
index a6148494c1b..953b84ea84f 100644
--- a/gems/gitlab-database-load_balancing/Gemfile.lock
+++ b/gems/gitlab-database-load_balancing/Gemfile.lock
@@ -16,6 +16,7 @@ PATH
remote: ../gitlab-safe_request_store
specs:
gitlab-safe_request_store (0.1.0)
+ rack (~> 2.2.8)
request_store
PATH
diff --git a/gems/gitlab-safe_request_store/Gemfile.lock b/gems/gitlab-safe_request_store/Gemfile.lock
index 50ff694d7d5..d26bab2f4f0 100644
--- a/gems/gitlab-safe_request_store/Gemfile.lock
+++ b/gems/gitlab-safe_request_store/Gemfile.lock
@@ -2,6 +2,7 @@ PATH
remote: .
specs:
gitlab-safe_request_store (0.1.0)
+ rack (~> 2.2.8)
request_store
GEM
@@ -35,7 +36,7 @@ GEM
coderay (~> 1.1)
method_source (~> 1.0)
racc (1.6.2)
- rack (3.0.4.1)
+ rack (2.2.8)
rainbow (3.1.1)
regexp_parser (2.7.0)
request_store (1.5.1)
diff --git a/gems/gitlab-safe_request_store/gitlab-safe_request_store.gemspec b/gems/gitlab-safe_request_store/gitlab-safe_request_store.gemspec
index a685a1eb447..b3ced3929c8 100644
--- a/gems/gitlab-safe_request_store/gitlab-safe_request_store.gemspec
+++ b/gems/gitlab-safe_request_store/gitlab-safe_request_store.gemspec
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
spec.files = Dir['lib/**/*.rb']
spec.require_paths = ["lib"]
+ spec.add_runtime_dependency "rack", "~> 2.2.8"
spec.add_runtime_dependency "request_store"
spec.add_development_dependency "gitlab-styles", "~> 10.1.0"
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 17bee275c51..300c30faf4a 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -94,6 +94,14 @@ module API
forbidden!("No access granted") unless can?(current_user, :read_builds, runner)
end
+
+ def preload_job_associations(jobs)
+ jobs.preload( # rubocop: disable CodeReuse/ActiveRecord -- this preload is tightly related to the endpoint
+ :user,
+ { pipeline: { project: [:route, { namespace: :route }] } },
+ { project: [:route, { namespace: :route }] }
+ )
+ end
end
resource :runners do
@@ -217,25 +225,24 @@ module API
end
params do
requires :id, type: Integer, desc: 'The ID of a runner'
+ optional :system_id, type: String, desc: 'System ID associated with the runner manager'
optional :status, type: String, desc: 'Status of the job', values: ::Ci::Build::AVAILABLE_STATUSES
optional :order_by, type: String, desc: 'Order by `id`', values: ::Ci::RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by `asc` or `desc` order. ' \
- 'Specify `order_by` as well, including for `id`'
+ 'Specify `order_by` as well, including for `id`'
+ optional :cursor, type: String, desc: 'Cursor for obtaining the next set of records'
use :pagination
end
get ':id/jobs' do
runner = get_runner(params[:id])
authenticate_list_runners_jobs!(runner)
+ # Optimize query when filtering by runner managers by not asking for count
+ paginator_params = params[:pagination] == :keyset || params[:system_id].blank? ? {} : { without_count: true }
+
jobs = ::Ci::RunnerJobsFinder.new(runner, current_user, params).execute
- jobs = jobs.preload( # rubocop: disable CodeReuse/ActiveRecord
- [
- :user,
- { pipeline: { project: [:route, { namespace: :route }] } },
- { project: [:route, { namespace: :route }] }
- ]
- )
- jobs = paginate(jobs)
+ jobs = preload_job_associations(jobs)
+ jobs = paginate_with_strategies(jobs, paginator_params: paginator_params)
jobs.each(&:commit) # batch loads all commits in the page
present jobs, with: Entities::Ci::JobBasicWithProject
diff --git a/lib/gitlab/dependency_linker/base_linker.rb b/lib/gitlab/dependency_linker/base_linker.rb
index 74bec55253f..2c9b559c8dc 100644
--- a/lib/gitlab/dependency_linker/base_linker.rb
+++ b/lib/gitlab/dependency_linker/base_linker.rb
@@ -31,13 +31,15 @@ module Gitlab
end
def external_url(name, external_ref)
- return if GIT_INVALID_URL_REGEX.match?(external_ref)
+ ref = external_ref.to_s
- case external_ref
+ return if GIT_INVALID_URL_REGEX.match?(ref)
+
+ case ref
when /\A#{URL_REGEX}\z/o
- external_ref
+ ref
when /\A#{REPO_REGEX}\z/o
- github_url(external_ref)
+ github_url(ref)
else
package_url(name)
end
diff --git a/lib/gitlab/middleware/unauthenticated_session_expiry.rb b/lib/gitlab/middleware/unauthenticated_session_expiry.rb
index f240a6b23bd..7c5c523c287 100644
--- a/lib/gitlab/middleware/unauthenticated_session_expiry.rb
+++ b/lib/gitlab/middleware/unauthenticated_session_expiry.rb
@@ -18,8 +18,9 @@ module Gitlab
result = @app.call(env)
warden = env['warden']
+ user = catch(:warden) { warden && warden.user } # rubocop:disable Cop/BanCatchThrow -- ignore Warden errors since we're outside Warden::Manager
- unless warden && warden.user
+ unless user
# This works because Rack uses these options every time a request is handled, and redis-store
# uses the Rack setting first:
# 1. https://github.com/rack/rack/blob/fdcd03a3c5a1c51d1f96fc97f9dfa1a9deac0c77/lib/rack/session/abstract/id.rb#L342
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 21862bdba69..920fbe3ad06 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -34035,6 +34035,9 @@ msgstr ""
msgid "Organization|Organization avatar"
msgstr ""
+msgid "Organization|Organization description (optional)"
+msgstr ""
+
msgid "Organization|Organization name"
msgstr ""
@@ -35618,12 +35621,30 @@ msgstr ""
msgid "PipelineStatusTooltip|Pipeline: %{ciStatus}"
msgstr ""
+msgid "PipelineSubscriptions|Add new"
+msgstr ""
+
msgid "PipelineSubscriptions|An error occurred while fetching downstream pipeline subscriptions."
msgstr ""
msgid "PipelineSubscriptions|An error occurred while fetching upstream pipeline subscriptions."
msgstr ""
+msgid "PipelineSubscriptions|Delete subscription"
+msgstr ""
+
+msgid "PipelineSubscriptions|No project subscribes to the pipelines in this project."
+msgstr ""
+
+msgid "PipelineSubscriptions|Subscribed to this project"
+msgstr ""
+
+msgid "PipelineSubscriptions|Subscriptions"
+msgstr ""
+
+msgid "PipelineSubscriptions|This project is not subscribed to any project pipelines."
+msgstr ""
+
msgid "PipelineWizardDefaultCommitMessage|Add %{filename}"
msgstr ""
diff --git a/rubocop/cop/scalability/file_uploads.rb b/rubocop/cop/scalability/file_uploads.rb
index fc52444c551..8f8c9605d09 100644
--- a/rubocop/cop/scalability/file_uploads.rb
+++ b/rubocop/cop/scalability/file_uploads.rb
@@ -27,7 +27,7 @@ module RuboCop
#
class FileUploads < RuboCop::Cop::Base
MSG = 'Do not upload files without workhorse acceleration. ' \
- 'Please refer to https://docs.gitlab.com/ee/development/uploads.html'
+ 'Please refer to https://docs.gitlab.com/ee/development/uploads/'
def_node_matcher :file_in_type, <<~PATTERN
(send nil? {:requires :optional}
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index a2848bd0256..83107e6cc4a 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -2,6 +2,10 @@
require_relative '../support/helpers/test_env'
+# TODO: Remove the debug_with_puts statements below! Used for debugging purposes.
+# TODO: https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/issues/323#note_1688925316
+require_relative '../support/helpers/debug_with_puts'
+
FactoryBot.define do
# Project without repository
#
@@ -66,6 +70,8 @@ FactoryBot.define do
end
after(:build) do |project, evaluator|
+ DebugWithPuts.debug_with_puts "Beginning of after :build of projects factory in spec/factories/projects.rb"
+
# Builds and MRs can't have higher visibility level than repository access level.
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
@@ -87,6 +93,8 @@ FactoryBot.define do
security_and_compliance_access_level: evaluator.security_and_compliance_access_level
}
+ DebugWithPuts.debug_with_puts "During after :build of projects factory in spec/factories/projects.rb:#{__LINE__}"
+
project_namespace_hash = {
name: evaluator.name,
path: evaluator.path,
@@ -97,10 +105,16 @@ FactoryBot.define do
project_namespace_hash[:id] = evaluator.project_namespace_id.presence
+ DebugWithPuts.debug_with_puts "During after :build of projects factory in spec/factories/projects.rb:#{__LINE__}"
+
project.build_project_namespace(project_namespace_hash)
project.build_project_feature(project_feature_hash)
+ DebugWithPuts.debug_with_puts "During after :build of projects factory in spec/factories/projects.rb:#{__LINE__}"
+
project.set_runners_token(evaluator.runners_token) if evaluator.runners_token.present?
+
+ DebugWithPuts.debug_with_puts "End of after :build of projects factory in spec/factories/projects.rb"
end
to_create do |project|
@@ -108,6 +122,7 @@ FactoryBot.define do
end
after(:create) do |project, evaluator|
+ DebugWithPuts.debug_with_puts "Beginning of after :create of projects factory in spec/factories/projects.rb"
# Normally the class Projects::CreateService is used for creating
# projects, and this class takes care of making sure the owner and current
# user have access to the project. Our specs don't use said service class,
@@ -116,12 +131,16 @@ FactoryBot.define do
project.add_owner(project.first_owner)
end
+ DebugWithPuts.debug_with_puts "During after :create of projects factory in spec/factories/projects.rb:#{__LINE__}"
+
if project.group
project.run_after_commit_or_now do
AuthorizedProjectUpdate::ProjectRecalculateService.new(project).execute
end
end
+ DebugWithPuts.debug_with_puts "During after :create of projects factory in spec/factories/projects.rb:#{__LINE__}"
+
# assign the delegated `#ci_cd_settings` attributes after create
project.group_runners_enabled = evaluator.group_runners_enabled unless evaluator.group_runners_enabled.nil?
project.merge_pipelines_enabled = evaluator.merge_pipelines_enabled unless evaluator.merge_pipelines_enabled.nil?
@@ -133,6 +152,8 @@ FactoryBot.define do
project.runner_token_expiration_interval = evaluator.runner_token_expiration_interval unless evaluator.runner_token_expiration_interval.nil?
project.runner_token_expiration_interval_human_readable = evaluator.runner_token_expiration_interval_human_readable unless evaluator.runner_token_expiration_interval_human_readable.nil?
+ DebugWithPuts.debug_with_puts "During after :create of projects factory in spec/factories/projects.rb:#{__LINE__}"
+
if evaluator.import_status
import_state = project.import_state || project.build_import_state
import_state.status = evaluator.import_status
@@ -142,8 +163,12 @@ FactoryBot.define do
import_state.save!
end
+ DebugWithPuts.debug_with_puts "During after :create of projects factory in spec/factories/projects.rb:#{__LINE__}"
+
# simulating ::Projects::ProcessSyncEventsWorker because most tests don't run Sidekiq inline
project.create_ci_project_mirror!(namespace_id: project.namespace_id) unless project.ci_project_mirror
+
+ DebugWithPuts.debug_with_puts "End of after :create of projects factory in spec/factories/projects.rb"
end
trait :public do
@@ -326,6 +351,7 @@ FactoryBot.define do
end
after :create do |project, evaluator|
+ DebugWithPuts.debug_with_puts "Beginning of after :create of trait :repository do in spec/factories/projects.rb"
# Specify `lfs: true` to create the LfsObject for the LFS file in the test repo:
# https://gitlab.com/gitlab-org/gitlab-test/-/blob/master/files/lfs/lfs_object.iso
if evaluator.lfs
@@ -351,6 +377,8 @@ FactoryBot.define do
end
end
+ DebugWithPuts.debug_with_puts "During after :create of trait :repository do in spec/factories/projects.rb:#{__LINE__}"
+
if evaluator.create_templates
templates_path = "#{evaluator.create_templates}_templates"
@@ -380,6 +408,8 @@ FactoryBot.define do
branch_name: 'master')
end
+ DebugWithPuts.debug_with_puts "During after :create of trait :repository do in spec/factories/projects.rb:#{__LINE__}"
+
if evaluator.create_branch
project.repository.create_file(
project.creator,
@@ -389,6 +419,8 @@ FactoryBot.define do
branch_name: evaluator.create_branch)
end
+ DebugWithPuts.debug_with_puts "During after :create of trait :repository do in spec/factories/projects.rb:#{__LINE__}"
+
if evaluator.create_tag
project.repository.add_tag(
project.creator,
@@ -397,6 +429,7 @@ FactoryBot.define do
end
project.track_project_repository
+ DebugWithPuts.debug_with_puts "End of after :create of trait :repository do in spec/factories/projects.rb"
end
end
diff --git a/spec/finders/ci/runner_jobs_finder_spec.rb b/spec/finders/ci/runner_jobs_finder_spec.rb
index 755b21ec08f..66cdde756be 100644
--- a/spec/finders/ci/runner_jobs_finder_spec.rb
+++ b/spec/finders/ci/runner_jobs_finder_spec.rb
@@ -2,26 +2,28 @@
require 'spec_helper'
-RSpec.describe Ci::RunnerJobsFinder do
- let(:project) { create(:project) }
- let(:runner) { create(:ci_runner, :instance) }
- let(:user) { create(:user) }
+RSpec.describe Ci::RunnerJobsFinder, feature_category: :fleet_visibility do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:runner) { create(:ci_runner, :instance) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:runner_manager) { create(:ci_runner_machine, runner: runner) }
+ let_it_be(:jobs) { create_list(:ci_build, 5, runner_manager: runner_manager, project: project) }
+
let(:params) { {} }
- subject { described_class.new(runner, user, params).execute }
+ subject(:returned_jobs) { described_class.new(runner, user, params).execute }
- before do
+ before_all do
project.add_developer(user)
end
describe '#execute' do
context 'when params is empty' do
- let!(:job) { create(:ci_build, runner: runner, project: project) }
let!(:job1) { create(:ci_build, project: project) }
it 'returns all jobs assigned to Runner' do
- is_expected.to match_array(job)
- is_expected.not_to match_array(job1)
+ is_expected.to match_array(jobs)
+ is_expected.not_to include(job1)
end
end
@@ -36,35 +38,34 @@ RSpec.describe Ci::RunnerJobsFinder do
end
end
- context 'when the user has permission to read all resources' do
- let(:user) { create(:user, :admin) }
+ context 'when the user is admin', :enable_admin_mode do
+ let_it_be(:user) { create(:user, :admin) }
- it 'returns all the jobs assigned to a runner' do
- jobs = create_list(:ci_build, 5, runner: runner, project: project)
+ it { is_expected.to match_array(jobs) }
+ end
- is_expected.to match_array(jobs)
+ context 'when user is developer' do
+ before_all do
+ project.add_developer(user)
end
+
+ it { is_expected.to match_array(jobs) }
end
context 'when the user has different access levels in different projects' do
- it 'returns only the jobs the user has permission to see' do
- guest_project = create(:project)
- reporter_project = create(:project)
-
- _guest_jobs = create_list(:ci_build, 2, runner: runner, project: guest_project)
- reporter_jobs = create_list(:ci_build, 3, runner: runner, project: reporter_project)
-
- guest_project.add_guest(user)
- reporter_project.add_reporter(user)
-
- is_expected.to match_array(reporter_jobs)
+ let_it_be(:guest_project) { create(:project).tap { |p| p.add_guest(user) } }
+ let_it_be(:guest_jobs) { create_list(:ci_build, 2, runner: runner, project: guest_project) }
+ let_it_be(:reporter_project) { create(:project).tap { |p| p.add_reporter(user) } }
+ let_it_be(:reporter_jobs) { create_list(:ci_build, 3, runner: runner, project: reporter_project) }
+
+ it 'returns only the jobs the user has permission to see', :aggregate_failures do
+ is_expected.to include(*reporter_jobs)
+ is_expected.not_to include(*guest_jobs)
end
end
context 'when the user has reporter access level or greater' do
- it 'returns jobs assigned to the Runner that the user has accesss to' do
- jobs = create_list(:ci_build, 3, runner: runner, project: project)
-
+ it 'returns jobs assigned to the Runner that the user has access to' do
is_expected.to match_array(jobs)
end
end
@@ -73,24 +74,38 @@ RSpec.describe Ci::RunnerJobsFinder do
Ci::HasStatus::AVAILABLE_STATUSES.each do |target_status|
context "when status is #{target_status}" do
let(:params) { { status: target_status } }
+ let(:exception_status) { (Ci::HasStatus::AVAILABLE_STATUSES - [target_status]).first }
let!(:job) { create(:ci_build, runner: runner, project: project, status: target_status) }
+ let!(:other_job) { create(:ci_build, runner: runner, project: project, status: exception_status) }
- before do
- exception_status = Ci::HasStatus::AVAILABLE_STATUSES - [target_status]
- create(:ci_build, runner: runner, project: project, status: exception_status.first)
- end
-
- it 'returns matched job' do
- is_expected.to eq([job])
+ it 'returns matched job', :aggregate_failures do
+ is_expected.to include(job)
+ is_expected.not_to include(other_job)
end
end
end
end
+ context 'when system_id is specified' do
+ let_it_be(:runner_manager2) { create(:ci_runner_machine, runner: runner) }
+ let_it_be(:job2) { create(:ci_build, runner_manager: runner_manager2, project: project) }
+
+ let(:params) { { system_id: runner_manager.system_xid } }
+
+ it 'returns jobs from the specified system' do
+ expect(returned_jobs).to match_array(jobs)
+ end
+
+ context 'when specified system_id does not exist' do
+ let(:params) { { system_id: 'unknown_system' } }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
context 'when order_by and sort are specified' do
context 'when order_by id and sort is asc' do
let(:params) { { order_by: 'id', sort: 'asc' } }
- let!(:jobs) { create_list(:ci_build, 2, runner: runner, project: project, user: create(:user)) }
it 'sorts as id: :asc' do
is_expected.to eq(jobs.sort_by(&:id))
@@ -101,7 +116,6 @@ RSpec.describe Ci::RunnerJobsFinder do
context 'when order_by is specified and sort is not specified' do
context 'when order_by id and sort is not specified' do
let(:params) { { order_by: 'id' } }
- let!(:jobs) { create_list(:ci_build, 2, runner: runner, project: project, user: create(:user)) }
it 'sorts as id: :desc' do
is_expected.to eq(jobs.sort_by(&:id).reverse)
diff --git a/spec/frontend/organizations/new/components/app_spec.js b/spec/frontend/organizations/new/components/app_spec.js
index 4f31baedbf6..e3e1c5b9684 100644
--- a/spec/frontend/organizations/new/components/app_spec.js
+++ b/spec/frontend/organizations/new/components/app_spec.js
@@ -24,10 +24,14 @@ describe('OrganizationNewApp', () => {
let wrapper;
let mockApollo;
+ const file = new File(['foo'], 'foo.jpg', {
+ type: 'text/plain',
+ });
+
+ const successfulResponseHandler = jest.fn().mockResolvedValue(organizationCreateResponse);
+
const createComponent = ({
- handlers = [
- [organizationCreateMutation, jest.fn().mockResolvedValue(organizationCreateResponse)],
- ],
+ handlers = [[organizationCreateMutation, successfulResponseHandler]],
} = {}) => {
mockApollo = createMockApollo(handlers);
@@ -36,7 +40,12 @@ describe('OrganizationNewApp', () => {
const findForm = () => wrapper.findComponent(NewEditForm);
const submitForm = async () => {
- findForm().vm.$emit('submit', { name: 'Foo bar', path: 'foo-bar' });
+ findForm().vm.$emit('submit', {
+ name: 'Foo bar',
+ path: 'foo-bar',
+ description: 'Foo bar description',
+ avatar: file,
+ });
await nextTick();
};
@@ -74,7 +83,15 @@ describe('OrganizationNewApp', () => {
await waitForPromises();
});
- it('redirects user to organization web url', () => {
+ it('calls mutation with correct variables and redirects user to organization web url', () => {
+ expect(successfulResponseHandler).toHaveBeenCalledWith({
+ input: {
+ name: 'Foo bar',
+ path: 'foo-bar',
+ description: 'Foo bar description',
+ avatar: file,
+ },
+ });
expect(visitUrlWithAlerts).toHaveBeenCalledWith(
organizationCreateResponse.data.organizationCreate.organization.webUrl,
[
diff --git a/spec/frontend/organizations/settings/general/components/organization_settings_spec.js b/spec/frontend/organizations/settings/general/components/organization_settings_spec.js
index eca6d9fdc4a..52e81d7fb5d 100644
--- a/spec/frontend/organizations/settings/general/components/organization_settings_spec.js
+++ b/spec/frontend/organizations/settings/general/components/organization_settings_spec.js
@@ -9,6 +9,7 @@ import {
FORM_FIELD_NAME,
FORM_FIELD_ID,
FORM_FIELD_AVATAR,
+ FORM_FIELD_DESCRIPTION,
} from '~/organizations/shared/constants';
import organizationUpdateMutation from '~/organizations/settings/general/graphql/mutations/organization_update.mutation.graphql';
import {
@@ -62,7 +63,13 @@ describe('OrganizationSettings', () => {
const findForm = () => wrapper.findComponent(NewEditForm);
const submitForm = async (data = {}) => {
- findForm().vm.$emit('submit', { name: 'Foo bar', path: 'foo-bar', avatar: file, ...data });
+ findForm().vm.$emit('submit', {
+ name: 'Foo bar',
+ path: 'foo-bar',
+ description: 'Foo bar description',
+ avatar: file,
+ ...data,
+ });
await nextTick();
};
@@ -84,7 +91,7 @@ describe('OrganizationSettings', () => {
expect(findForm().props()).toMatchObject({
loading: false,
initialFormValues: defaultProvide.organization,
- fieldsToRender: [FORM_FIELD_NAME, FORM_FIELD_ID, FORM_FIELD_AVATAR],
+ fieldsToRender: [FORM_FIELD_NAME, FORM_FIELD_ID, FORM_FIELD_DESCRIPTION, FORM_FIELD_AVATAR],
});
});
@@ -117,6 +124,7 @@ describe('OrganizationSettings', () => {
input: {
id: 'gid://gitlab/Organizations::Organization/1',
name: 'Foo bar',
+ description: 'Foo bar description',
avatar: file,
},
});
@@ -191,6 +199,7 @@ describe('OrganizationSettings', () => {
input: {
id: 'gid://gitlab/Organizations::Organization/1',
name: 'Foo bar',
+ description: 'Foo bar description',
avatar: null,
},
});
@@ -208,6 +217,7 @@ describe('OrganizationSettings', () => {
input: {
id: 'gid://gitlab/Organizations::Organization/1',
name: 'Foo bar',
+ description: 'Foo bar description',
},
});
});
diff --git a/spec/frontend/organizations/shared/components/new_edit_form_spec.js b/spec/frontend/organizations/shared/components/new_edit_form_spec.js
index 4897a81fc1c..5be26ef7cc3 100644
--- a/spec/frontend/organizations/shared/components/new_edit_form_spec.js
+++ b/spec/frontend/organizations/shared/components/new_edit_form_spec.js
@@ -1,9 +1,10 @@
-import { GlButton } from '@gitlab/ui';
import { nextTick } from 'vue';
import NewEditForm from '~/organizations/shared/components/new_edit_form.vue';
import OrganizationUrlField from '~/organizations/shared/components/organization_url_field.vue';
import AvatarUploadDropzone from '~/vue_shared/components/upload_dropzone/avatar_upload_dropzone.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
+import { helpPagePath } from '~/helpers/help_page_helper';
import {
FORM_FIELD_NAME,
FORM_FIELD_ID,
@@ -18,6 +19,7 @@ describe('NewEditForm', () => {
const defaultProvide = {
organizationsPath: '/-/organizations',
rootUrl: 'http://127.0.0.1:3000/',
+ previewMarkdownPath: '/-/organizations/preview_markdown',
};
const defaultPropsData = {
@@ -38,6 +40,7 @@ describe('NewEditForm', () => {
const findNameField = () => wrapper.findByLabelText('Organization name');
const findIdField = () => wrapper.findByLabelText('Organization ID');
const findUrlField = () => wrapper.findComponent(OrganizationUrlField);
+ const findDescriptionField = () => wrapper.findByLabelText('Organization description (optional)');
const findAvatarField = () => wrapper.findComponent(AvatarUploadDropzone);
const setUrlFieldValue = async (value) => {
@@ -70,6 +73,30 @@ describe('NewEditForm', () => {
});
});
+ it('renders `Organization description` field as markdown editor', () => {
+ createComponent();
+
+ expect(findDescriptionField().exists()).toBe(true);
+ expect(wrapper.findComponent(MarkdownField).props()).toMatchObject({
+ markdownPreviewPath: defaultProvide.previewMarkdownPath,
+ markdownDocsPath: helpPagePath('user/organization/index', {
+ anchor: 'organization-description-supported-markdown',
+ }),
+ textareaValue: '',
+ restrictedToolBarItems: [
+ 'code',
+ 'quote',
+ 'bullet-list',
+ 'numbered-list',
+ 'task-list',
+ 'collapsible-section',
+ 'table',
+ 'attach-file',
+ 'full-screen',
+ ],
+ });
+ });
+
describe('when `Organization avatar` field is changed', () => {
const file = new File(['foo'], 'foo.jpg', {
type: 'text/plain',
@@ -154,12 +181,13 @@ describe('NewEditForm', () => {
await findNameField().setValue('Foo bar');
await setUrlFieldValue('foo-bar');
+ await findDescriptionField().setValue('Foo bar description');
await submitForm();
});
it('emits `submit` event with form values', () => {
expect(wrapper.emitted('submit')).toEqual([
- [{ name: 'Foo bar', path: 'foo-bar', avatar: null }],
+ [{ name: 'Foo bar', path: 'foo-bar', description: 'Foo bar description', avatar: null }],
]);
});
});
@@ -221,7 +249,7 @@ describe('NewEditForm', () => {
});
it('shows button with loading icon', () => {
- expect(wrapper.findComponent(GlButton).props('loading')).toBe(true);
+ expect(wrapper.findByTestId('submit-button').props('loading')).toBe(true);
});
});
diff --git a/spec/frontend/organizations/show/components/app_spec.js b/spec/frontend/organizations/show/components/app_spec.js
index 46496e40bdd..6cf8845bdbe 100644
--- a/spec/frontend/organizations/show/components/app_spec.js
+++ b/spec/frontend/organizations/show/components/app_spec.js
@@ -1,6 +1,7 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import App from '~/organizations/show/components/app.vue';
import OrganizationAvatar from '~/organizations/show/components/organization_avatar.vue';
+import OrganizationDescription from '~/organizations/show/components/organization_description.vue';
import GroupsAndProjects from '~/organizations/show/components/groups_and_projects.vue';
import AssociationCount from '~/organizations/show/components/association_counts.vue';
@@ -34,6 +35,12 @@ describe('OrganizationShowApp', () => {
);
});
+ it('renders organization description and passes organization prop', () => {
+ expect(wrapper.findComponent(OrganizationDescription).props('organization')).toEqual(
+ defaultPropsData.organization,
+ );
+ });
+
it('renders groups and projects component and passes `groupsAndProjectsOrganizationPath` prop', () => {
expect(
wrapper.findComponent(GroupsAndProjects).props('groupsAndProjectsOrganizationPath'),
diff --git a/spec/frontend/organizations/show/components/organization_description_spec.js b/spec/frontend/organizations/show/components/organization_description_spec.js
new file mode 100644
index 00000000000..2aaf6f24a72
--- /dev/null
+++ b/spec/frontend/organizations/show/components/organization_description_spec.js
@@ -0,0 +1,46 @@
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import OrganizationDescription from '~/organizations/show/components/organization_description.vue';
+
+describe('OrganizationDescription', () => {
+ let wrapper;
+
+ const defaultPropsData = {
+ organization: {
+ id: 1,
+ name: 'GitLab',
+ description_html: '<h1>Foo bar description</h1><script>alert("foo")</script>',
+ },
+ };
+
+ const createComponent = ({ propsData = {} } = {}) => {
+ wrapper = mountExtended(OrganizationDescription, {
+ propsData: { ...defaultPropsData, ...propsData },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ describe('when organization has description', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders description as safe HTML', () => {
+ expect(wrapper.element.innerHTML).toBe('<h1>Foo bar description</h1>');
+ });
+ });
+
+ describe('when organization does not have description', () => {
+ beforeEach(() => {
+ createComponent({
+ propsData: { organization: { ...defaultPropsData.organization, description_html: '' } },
+ });
+ });
+
+ it('renders nothing', () => {
+ expect(wrapper.html()).toBe('');
+ });
+ });
+});
diff --git a/spec/helpers/organizations/organization_helper_spec.rb b/spec/helpers/organizations/organization_helper_spec.rb
index a3613e29da9..0f2f4ed1b54 100644
--- a/spec/helpers/organizations/organization_helper_spec.rb
+++ b/spec/helpers/organizations/organization_helper_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe Organizations::OrganizationHelper, feature_category: :cell do
- let_it_be(:organization) { build_stubbed(:organization) }
+ let_it_be(:organization_detail) { build_stubbed(:organization_detail, description_html: '<em>description</em>') }
+ let_it_be(:organization) { organization_detail.organization }
let_it_be(:new_group_path) { '/groups/new' }
let_it_be(:new_project_path) { '/projects/new' }
let_it_be(:organizations_empty_state_svg_path) { 'illustrations/empty-state/empty-organizations-md.svg' }
@@ -11,6 +12,7 @@ RSpec.describe Organizations::OrganizationHelper, feature_category: :cell do
let_it_be(:root_url) { 'http://127.0.0.1:3000/' }
let_it_be(:groups_empty_state_svg_path) { 'illustrations/empty-state/empty-groups-md.svg' }
let_it_be(:projects_empty_state_svg_path) { 'illustrations/empty-state/empty-projects-md.svg' }
+ let_it_be(:preview_markdown_organizations_path) { '/-/organizations/preview_markdown' }
before do
allow(helper).to receive(:new_group_path).and_return(new_group_path)
@@ -21,6 +23,7 @@ RSpec.describe Organizations::OrganizationHelper, feature_category: :cell do
allow(helper).to receive(:root_url).and_return(root_url)
allow(helper).to receive(:image_path).with(groups_empty_state_svg_path).and_return(groups_empty_state_svg_path)
allow(helper).to receive(:image_path).with(projects_empty_state_svg_path).and_return(projects_empty_state_svg_path)
+ allow(helper).to receive(:preview_markdown_organizations_path).and_return(preview_markdown_organizations_path)
end
describe '#organization_show_app_data' do
@@ -41,6 +44,7 @@ RSpec.describe Organizations::OrganizationHelper, feature_category: :cell do
'organization' => {
'id' => organization.id,
'name' => organization.name,
+ 'description_html' => organization.description_html,
'avatar_url' => 'avatar.jpg'
},
'groups_and_projects_organization_path' => '/-/organizations/default/groups_and_projects',
@@ -91,7 +95,8 @@ RSpec.describe Organizations::OrganizationHelper, feature_category: :cell do
expect(Gitlab::Json.parse(helper.organization_new_app_data)).to eq(
{
'organizations_path' => organizations_path,
- 'root_url' => root_url
+ 'root_url' => root_url,
+ 'preview_markdown_path' => preview_markdown_organizations_path
}
)
end
@@ -119,10 +124,12 @@ RSpec.describe Organizations::OrganizationHelper, feature_category: :cell do
'id' => organization.id,
'name' => organization.name,
'path' => organization.path,
+ 'description' => organization.description,
'avatar' => 'avatar.jpg'
},
'organizations_path' => organizations_path,
- 'root_url' => root_url
+ 'root_url' => root_url,
+ 'preview_markdown_path' => preview_markdown_organizations_path
}
)
end
diff --git a/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb
index 127f437dd54..e3cddceb7a9 100644
--- a/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb
+++ b/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb
@@ -100,5 +100,21 @@ RSpec.describe Gitlab::DependencyLinker::PackageJsonLinker do
it 'does not link scripts with the same key as a package' do
expect(subject).not_to include(link('karma start config/karma.config.js --single-run', 'https://github.com/karma start config/karma.config.js --single-run'))
end
+
+ context 'when dependency is not a string' do
+ let(:file_content) do
+ <<-CONTENT.strip_heredoc
+ {
+ "dependencies": {
+ "wrong": {}
+ }
+ }
+ CONTENT
+ end
+
+ it 'does not link it' do
+ expect(subject).not_to include(%(<a href))
+ end
+ end
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index af3f54c5f0a..c5c97228988 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -325,14 +325,15 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
describe '.with_exposed_artifacts' do
subject { described_class.with_exposed_artifacts }
- let!(:job1) { create(:ci_build, pipeline: pipeline) }
+ let_it_be(:job1) { create(:ci_build, pipeline: pipeline) }
+ let_it_be(:job3) { create(:ci_build, pipeline: pipeline) }
+
let!(:job2) { create(:ci_build, options: options, pipeline: pipeline) }
- let!(:job3) { create(:ci_build, pipeline: pipeline) }
- context 'when some jobs have exposed artifacs and some not' do
+ context 'when some jobs have exposed artifacts and some not' do
let(:options) { { artifacts: { expose_as: 'test', paths: ['test'] } } }
- before do
+ before_all do
job1.ensure_metadata.update!(has_exposed_artifacts: nil)
job3.ensure_metadata.update!(has_exposed_artifacts: false)
end
@@ -356,10 +357,10 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
let(:artifact_scope) { Ci::JobArtifact.where(file_type: 'archive') }
- let!(:build_1) { create(:ci_build, :artifacts, pipeline: pipeline) }
- let!(:build_2) { create(:ci_build, :codequality_reports, pipeline: pipeline) }
- let!(:build_3) { create(:ci_build, :test_reports, pipeline: pipeline) }
- let!(:build_4) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let_it_be(:build_1) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let_it_be(:build_2) { create(:ci_build, :codequality_reports, pipeline: pipeline) }
+ let_it_be(:build_3) { create(:ci_build, :test_reports, pipeline: pipeline) }
+ let_it_be(:build_4) { create(:ci_build, :artifacts, pipeline: pipeline) }
it 'returns artifacts matching the given scope' do
expect(builds).to contain_exactly(build_1, build_4)
@@ -383,10 +384,10 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
end
describe '.with_needs' do
- let!(:build) { create(:ci_build, pipeline: pipeline) }
- let!(:build_b) { create(:ci_build, pipeline: pipeline) }
- let!(:build_need_a) { create(:ci_build_need, build: build) }
- let!(:build_need_b) { create(:ci_build_need, build: build_b) }
+ let_it_be(:build) { create(:ci_build, pipeline: pipeline) }
+ let_it_be(:build_b) { create(:ci_build, pipeline: pipeline) }
+ let_it_be(:build_need_a) { create(:ci_build_need, build: build) }
+ let_it_be(:build_need_b) { create(:ci_build_need, build: build_b) }
context 'when passing build name' do
subject { described_class.with_needs(build_need_a.name) }
@@ -421,6 +422,33 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
end
end
+ describe '.belonging_to_runner_manager' do
+ subject { described_class.belonging_to_runner_manager(runner_manager) }
+
+ let_it_be(:runner) { create(:ci_runner, :group, groups: [group]) }
+ let_it_be(:build_b) { create(:ci_build, :success) }
+
+ context 'with runner_manager of runner associated with build' do
+ let!(:runner_manager) { create(:ci_runner_machine, runner: runner) }
+ let!(:runner_manager_build) { create(:ci_runner_machine_build, build: build, runner_manager: runner_manager) }
+
+ it { is_expected.to contain_exactly(build) }
+ end
+
+ context 'with runner_manager of runner not associated with build' do
+ let!(:runner_manager) { create(:ci_runner_machine, runner: instance_runner) }
+ let!(:instance_runner) { create(:ci_runner, :with_runner_manager) }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'with nil runner_manager' do
+ let(:runner_manager) { nil }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe '#stick_build_if_status_changed' do
it 'sticks the build if the status changed' do
job = create(:ci_build, :pending, pipeline: pipeline)
diff --git a/spec/models/ci/runner_manager_spec.rb b/spec/models/ci/runner_manager_spec.rb
index ca762f7d618..4fdfbae997d 100644
--- a/spec/models/ci/runner_manager_spec.rb
+++ b/spec/models/ci/runner_manager_spec.rb
@@ -122,23 +122,60 @@ RSpec.describe Ci::RunnerManager, feature_category: :fleet_visibility, type: :mo
describe '.for_runner' do
subject(:runner_managers) { described_class.for_runner(runner_arg) }
- let_it_be(:runner1) { create(:ci_runner) }
- let_it_be(:runner_manager11) { create(:ci_runner_machine, runner: runner1) }
- let_it_be(:runner_manager12) { create(:ci_runner_machine, runner: runner1) }
+ let_it_be(:runner_a) { create(:ci_runner) }
+ let_it_be(:runner_manager_a1) { create(:ci_runner_machine, runner: runner_a) }
+ let_it_be(:runner_manager_a2) { create(:ci_runner_machine, runner: runner_a) }
context 'with single runner' do
- let(:runner_arg) { runner1 }
+ let(:runner_arg) { runner_a }
- it { is_expected.to contain_exactly(runner_manager11, runner_manager12) }
+ it { is_expected.to contain_exactly(runner_manager_a1, runner_manager_a2) }
end
context 'with multiple runners' do
- let(:runner_arg) { [runner1, runner2] }
+ let(:runner_arg) { [runner_a, runner_b] }
- let_it_be(:runner2) { create(:ci_runner) }
- let_it_be(:runner_manager2) { create(:ci_runner_machine, runner: runner2) }
+ let_it_be(:runner_b) { create(:ci_runner) }
+ let_it_be(:runner_manager_b1) { create(:ci_runner_machine, runner: runner_b) }
- it { is_expected.to contain_exactly(runner_manager11, runner_manager12, runner_manager2) }
+ it { is_expected.to contain_exactly(runner_manager_a1, runner_manager_a2, runner_manager_b1) }
+ end
+ end
+
+ describe '.with_system_xid' do
+ subject(:runner_managers) { described_class.with_system_xid(system_xid) }
+
+ let_it_be(:runner_a) { create(:ci_runner) }
+ let_it_be(:runner_b) { create(:ci_runner) }
+ let_it_be(:runner_manager_a1) { create(:ci_runner_machine, runner: runner_a, system_xid: 'id1') }
+ let_it_be(:runner_manager_a2) { create(:ci_runner_machine, runner: runner_a, system_xid: 'id2') }
+ let_it_be(:runner_manager_b1) { create(:ci_runner_machine, runner: runner_b, system_xid: 'id1') }
+
+ context 'with single system id' do
+ let(:system_xid) { 'id2' }
+
+ it { is_expected.to contain_exactly(runner_manager_a2) }
+ end
+
+ context 'with multiple system ids' do
+ let(:system_xid) { %w[id1 id2] }
+
+ it { is_expected.to contain_exactly(runner_manager_a1, runner_manager_a2, runner_manager_b1) }
+ end
+
+ context 'when chained with another scope' do
+ subject(:runner_managers) { described_class.for_runner(runner).with_system_xid(system_xid) }
+
+ let(:runner) { runner_a }
+ let(:system_xid) { 'id1' }
+
+ it { is_expected.to contain_exactly(runner_manager_a1) }
+
+ context 'with another runner' do
+ let(:runner) { runner_b }
+
+ it { is_expected.to contain_exactly(runner_manager_b1) }
+ end
end
end
@@ -146,18 +183,18 @@ RSpec.describe Ci::RunnerManager, feature_category: :fleet_visibility, type: :mo
let!(:runner_version1) { create(:ci_runner_version, version: '16.0.0', status: :recommended) }
let!(:runner_version2) { create(:ci_runner_version, version: '16.0.1', status: :available) }
- let!(:runner1) { create(:ci_runner) }
- let!(:runner2) { create(:ci_runner) }
- let!(:runner_manager11) { create(:ci_runner_machine, runner: runner1, version: runner_version1.version) }
- let!(:runner_manager12) { create(:ci_runner_machine, runner: runner1, version: runner_version2.version) }
- let!(:runner_manager2) { create(:ci_runner_machine, runner: runner2, version: runner_version2.version) }
+ let!(:runner_a) { create(:ci_runner) }
+ let!(:runner_b) { create(:ci_runner) }
+ let!(:runner_manager_a1) { create(:ci_runner_machine, runner: runner_a, version: runner_version1.version) }
+ let!(:runner_manager_a2) { create(:ci_runner_machine, runner: runner_a, version: runner_version2.version) }
+ let!(:runner_manager_b1) { create(:ci_runner_machine, runner: runner_b, version: runner_version2.version) }
subject { described_class.aggregate_upgrade_status_by_runner_id }
it 'contains aggregate runner upgrade status by runner ID' do
is_expected.to eq({
- runner1.id => :recommended,
- runner2.id => :available
+ runner_a.id => :recommended,
+ runner_b.id => :available
})
end
end
@@ -189,6 +226,108 @@ RSpec.describe Ci::RunnerManager, feature_category: :fleet_visibility, type: :mo
it { is_expected.to eq([runner_manager2, runner_manager1]) }
end
+ describe '.with_upgrade_status' do
+ subject(:scope) { described_class.with_upgrade_status(upgrade_status) }
+
+ let_it_be(:runner_manager_14_0_0) { create(:ci_runner_machine, version: '14.0.0') }
+ let_it_be(:runner_manager_14_1_0) { create(:ci_runner_machine, version: '14.1.0') }
+ let_it_be(:runner_manager_14_1_1) { create(:ci_runner_machine, version: '14.1.1') }
+
+ before_all do
+ create(:ci_runner_version, version: '14.0.0', status: :available)
+ create(:ci_runner_version, version: '14.1.0', status: :recommended)
+ create(:ci_runner_version, version: '14.1.1', status: :unavailable)
+ end
+
+ context 'as :unavailable' do
+ let(:upgrade_status) { :unavailable }
+
+ it 'returns runners with runner managers whose version is assigned :unavailable' do
+ is_expected.to contain_exactly(runner_manager_14_1_1)
+ end
+ end
+
+ context 'as :available' do
+ let(:upgrade_status) { :available }
+
+ it 'returns runners with runner managers whose version is assigned :available' do
+ is_expected.to contain_exactly(runner_manager_14_0_0)
+ end
+ end
+
+ context 'as :recommended' do
+ let(:upgrade_status) { :recommended }
+
+ it 'returns runners with runner managers whose version is assigned :recommended' do
+ is_expected.to contain_exactly(runner_manager_14_1_0)
+ end
+ end
+ end
+
+ describe '.with_version_prefix' do
+ subject { described_class.with_version_prefix(version_prefix) }
+
+ let_it_be(:runner_manager1) { create(:ci_runner_machine, version: '15.11.0') }
+ let_it_be(:runner_manager2) { create(:ci_runner_machine, version: '15.9.0') }
+ let_it_be(:runner_manager3) { create(:ci_runner_machine, version: '15.11.5') }
+
+ context 'with a prefix string of "15."' do
+ let(:version_prefix) { "15." }
+
+ it 'returns runner managers' do
+ is_expected.to contain_exactly(runner_manager1, runner_manager2, runner_manager3)
+ end
+ end
+
+ context 'with a prefix string of "15"' do
+ let(:version_prefix) { "15" }
+
+ it 'returns runner managers' do
+ is_expected.to contain_exactly(runner_manager1, runner_manager2, runner_manager3)
+ end
+ end
+
+ context 'with a prefix string of "15.11."' do
+ let(:version_prefix) { "15.11." }
+
+ it 'returns runner managers' do
+ is_expected.to contain_exactly(runner_manager1, runner_manager3)
+ end
+ end
+
+ context 'with a prefix string of "15.11"' do
+ let(:version_prefix) { "15.11" }
+
+ it 'returns runner managers' do
+ is_expected.to contain_exactly(runner_manager1, runner_manager3)
+ end
+ end
+
+ context 'with a prefix string of "15.9"' do
+ let(:version_prefix) { "15.9" }
+
+ it 'returns runner managers' do
+ is_expected.to contain_exactly(runner_manager2)
+ end
+ end
+
+ context 'with a prefix string of "15.11.5"' do
+ let(:version_prefix) { "15.11.5" }
+
+ it 'returns runner managers' do
+ is_expected.to contain_exactly(runner_manager3)
+ end
+ end
+
+ context 'with a malformed prefix of "V2"' do
+ let(:version_prefix) { "V2" }
+
+ it 'returns no runner managers' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '#status', :freeze_time do
let(:runner_manager) { build(:ci_runner_machine, created_at: 8.days.ago) }
@@ -425,106 +564,4 @@ RSpec.describe Ci::RunnerManager, feature_category: :fleet_visibility, type: :mo
it { is_expected.to contain_exactly build }
end
end
-
- describe '.with_upgrade_status' do
- subject(:scope) { described_class.with_upgrade_status(upgrade_status) }
-
- let_it_be(:runner_manager_14_0_0) { create(:ci_runner_machine, version: '14.0.0') }
- let_it_be(:runner_manager_14_1_0) { create(:ci_runner_machine, version: '14.1.0') }
- let_it_be(:runner_manager_14_1_1) { create(:ci_runner_machine, version: '14.1.1') }
-
- before_all do
- create(:ci_runner_version, version: '14.0.0', status: :available)
- create(:ci_runner_version, version: '14.1.0', status: :recommended)
- create(:ci_runner_version, version: '14.1.1', status: :unavailable)
- end
-
- context 'as :unavailable' do
- let(:upgrade_status) { :unavailable }
-
- it 'returns runners with runner managers whose version is assigned :unavailable' do
- is_expected.to contain_exactly(runner_manager_14_1_1)
- end
- end
-
- context 'as :available' do
- let(:upgrade_status) { :available }
-
- it 'returns runners with runner managers whose version is assigned :available' do
- is_expected.to contain_exactly(runner_manager_14_0_0)
- end
- end
-
- context 'as :recommended' do
- let(:upgrade_status) { :recommended }
-
- it 'returns runners with runner managers whose version is assigned :recommended' do
- is_expected.to contain_exactly(runner_manager_14_1_0)
- end
- end
- end
-
- describe '.with_version_prefix' do
- subject { described_class.with_version_prefix(version_prefix) }
-
- let_it_be(:runner_manager1) { create(:ci_runner_machine, version: '15.11.0') }
- let_it_be(:runner_manager2) { create(:ci_runner_machine, version: '15.9.0') }
- let_it_be(:runner_manager3) { create(:ci_runner_machine, version: '15.11.5') }
-
- context 'with a prefix string of "15."' do
- let(:version_prefix) { "15." }
-
- it 'returns runner managers' do
- is_expected.to contain_exactly(runner_manager1, runner_manager2, runner_manager3)
- end
- end
-
- context 'with a prefix string of "15"' do
- let(:version_prefix) { "15" }
-
- it 'returns runner managers' do
- is_expected.to contain_exactly(runner_manager1, runner_manager2, runner_manager3)
- end
- end
-
- context 'with a prefix string of "15.11."' do
- let(:version_prefix) { "15.11." }
-
- it 'returns runner managers' do
- is_expected.to contain_exactly(runner_manager1, runner_manager3)
- end
- end
-
- context 'with a prefix string of "15.11"' do
- let(:version_prefix) { "15.11" }
-
- it 'returns runner managers' do
- is_expected.to contain_exactly(runner_manager1, runner_manager3)
- end
- end
-
- context 'with a prefix string of "15.9"' do
- let(:version_prefix) { "15.9" }
-
- it 'returns runner managers' do
- is_expected.to contain_exactly(runner_manager2)
- end
- end
-
- context 'with a prefix string of "15.11.5"' do
- let(:version_prefix) { "15.11.5" }
-
- it 'returns runner managers' do
- is_expected.to contain_exactly(runner_manager3)
- end
- end
-
- context 'with a malformed prefix of "V2"' do
- let(:version_prefix) { "V2" }
-
- it 'returns no runner managers' do
- is_expected.to be_empty
- end
- end
- end
end
diff --git a/spec/models/organizations/organization_detail_spec.rb b/spec/models/organizations/organization_detail_spec.rb
index 3f44a9cc637..dd49274e7dd 100644
--- a/spec/models/organizations/organization_detail_spec.rb
+++ b/spec/models/organizations/organization_detail_spec.rb
@@ -16,6 +16,15 @@ RSpec.describe Organizations::OrganizationDetail, type: :model, feature_category
let(:model) { create(:organization_detail) }
end
+ describe '#description_html' do
+ let_it_be(:model) { create(:organization_detail, description: '### Foo **Bar**') }
+ let(:expected_description) { ' Foo <strong>Bar</strong> ' }
+
+ subject { model.description_html }
+
+ it { is_expected.to eq_no_sourcepos(expected_description) }
+ end
+
context 'with uploads' do
it_behaves_like 'model with uploads', false do
let(:model_object) { create(:organization_detail) }
diff --git a/spec/models/organizations/organization_spec.rb b/spec/models/organizations/organization_spec.rb
index 053a32281aa..aba2b03d4d1 100644
--- a/spec/models/organizations/organization_spec.rb
+++ b/spec/models/organizations/organization_spec.rb
@@ -59,6 +59,7 @@ RSpec.describe Organizations::Organization, type: :model, feature_category: :cel
describe 'delegations' do
it { is_expected.to delegate_method(:description).to(:organization_detail) }
+ it { is_expected.to delegate_method(:description_html).to(:organization_detail) }
it { is_expected.to delegate_method(:avatar).to(:organization_detail) }
it { is_expected.to delegate_method(:avatar_url).to(:organization_detail) }
it { is_expected.to delegate_method(:remove_avatar!).to(:organization_detail) }
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index b4394f47105..11d906249e4 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -836,166 +836,219 @@ RSpec.describe API::Ci::Runners, :aggregate_failures, feature_category: :fleet_v
end
describe 'GET /runners/:id/jobs' do
- let_it_be(:job_1) { create(:ci_build) }
- let_it_be(:job_2) { create(:ci_build, :running, runner: shared_runner, project: project) }
- let_it_be(:job_3) { create(:ci_build, :failed, runner: shared_runner, project: project) }
- let_it_be(:job_4) { create(:ci_build, :running, runner: project_runner, project: project) }
- let_it_be(:job_5) { create(:ci_build, :failed, runner: project_runner, project: project) }
- let(:path) { "/runners/#{project_runner.id}/jobs" }
+ subject(:request) { get api(path, user, **api_params) }
+
+ let_it_be(:shared_runner_manager1) { create(:ci_runner_machine, runner: shared_runner, system_xid: 'id2') }
+ let_it_be(:jobs) do
+ project_runner_manager1 = create(:ci_runner_machine, runner: project_runner, system_xid: 'id1')
+ project_runner_manager2 = create(:ci_runner_machine, runner: two_projects_runner, system_xid: 'id1')
+
+ [
+ create(:ci_build),
+ create(:ci_build, :running, runner_manager: shared_runner_manager1, project: project),
+ create(:ci_build, :failed, runner_manager: shared_runner_manager1, project: project),
+ create(:ci_build, :running, runner_manager: project_runner_manager1, project: project),
+ create(:ci_build, :failed, runner_manager: project_runner_manager1, project: project),
+ create(:ci_build, :running, runner_manager: project_runner_manager2, project: project),
+ create(:ci_build, :running, runner_manager: project_runner_manager2, project: project2)
+ ]
+ end
+
+ let(:api_params) { {} }
+ let(:runner_id) { project_runner.id }
+ let(:query_part) { query_params.merge(system_id_params).map { |param| param.join('=') }.join('&') }
+ let(:path) { "/runners/#{runner_id}/jobs?#{query_part}" }
+ let(:query_params) { {} }
+ let(:system_id_params) { {} }
it_behaves_like 'GET request permissions for admin mode'
context 'admin user' do
+ let(:user) { admin }
+ let(:api_params) { { admin_mode: true } }
+
context 'when runner exists' do
context 'when runner is shared' do
+ let(:runner_id) { shared_runner.id }
+ let(:system_id) { 'id2' }
+
it 'return jobs' do
- get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
- expect(json_response).to be_an(Array)
- expect(json_response.length).to eq(2)
+ expect(json_response).to match([
+ a_hash_including('id' => jobs[1].id),
+ a_hash_including('id' => jobs[2].id)
+ ])
+ end
+
+ it_behaves_like 'an endpoint with keyset pagination', invalid_order: nil do
+ let(:first_record) { jobs[2] }
+ let(:second_record) { jobs[1] }
+ let(:api_call) { api(path, user, **api_params) }
end
end
context 'when runner is a project runner' do
it 'return jobs' do
- get api(path, admin, admin_mode: true)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
- expect(json_response).to be_an(Array)
- expect(json_response.length).to eq(2)
+ expect(json_response).to match([
+ a_hash_including('id' => jobs[3].id),
+ a_hash_including('id' => jobs[4].id)
+ ])
end
context 'when user does not have authorization to see all jobs' do
- it 'shows only jobs it has permission to see' do
- create(:ci_build, :running, runner: two_projects_runner, project: project)
- create(:ci_build, :running, runner: two_projects_runner, project: project2)
+ let(:runner_id) { two_projects_runner.id }
+ let(:user) { user2 }
+ let(:api_params) { {} }
+ before_all do
project.add_guest(user2)
project2.add_maintainer(user2)
- get api("/runners/#{two_projects_runner.id}/jobs", user2)
+ end
+
+ it 'shows only jobs it has permission to see' do
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
-
- expect(json_response).to be_an(Array)
- expect(json_response.length).to eq(1)
+ expect(json_response).to match([a_hash_including('id' => jobs[6].id)])
end
end
end
context 'when valid status is provided' do
+ let(:query_params) { { status: :failed } }
+
it 'return filtered jobs' do
- get api("/runners/#{project_runner.id}/jobs?status=failed", admin, admin_mode: true)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
- expect(json_response).to be_an(Array)
- expect(json_response.length).to eq(1)
- expect(json_response.first).to include('id' => job_5.id)
+ expect(json_response).to match([a_hash_including('id' => jobs[4].id)])
end
end
context 'when valid order_by is provided' do
+ let(:query_params) { { order_by: :id } }
+
context 'when sort order is not specified' do
it 'return jobs in descending order' do
- get api("/runners/#{project_runner.id}/jobs?order_by=id", admin, admin_mode: true)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
- expect(json_response).to be_an(Array)
- expect(json_response.length).to eq(2)
- expect(json_response.first).to include('id' => job_5.id)
+ expect(json_response).to match([
+ a_hash_including('id' => jobs[4].id),
+ a_hash_including('id' => jobs[3].id)
+ ])
end
end
context 'when sort order is specified as asc' do
+ let(:query_params) { { order_by: :id, sort: :asc } }
+
it 'return jobs sorted in ascending order' do
- get api("/runners/#{project_runner.id}/jobs?order_by=id&sort=asc", admin, admin_mode: true)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
- expect(json_response).to be_an(Array)
- expect(json_response.length).to eq(2)
- expect(json_response.first).to include('id' => job_4.id)
+ expect(json_response).to match([
+ a_hash_including('id' => jobs[3].id),
+ a_hash_including('id' => jobs[4].id)
+ ])
end
end
end
context 'when invalid status is provided' do
+ let(:query_params) { { status: 'non-existing' } }
+
it 'return 400' do
- get api("/runners/#{project_runner.id}/jobs?status=non-existing", admin, admin_mode: true)
+ request
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'when invalid order_by is provided' do
+ let(:query_params) { { order_by: 'non-existing' } }
+
it 'return 400' do
- get api("/runners/#{project_runner.id}/jobs?order_by=non-existing", admin, admin_mode: true)
+ request
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'when invalid sort is provided' do
+ let(:query_params) { { sort: 'non-existing' } }
+
it 'return 400' do
- get api("/runners/#{project_runner.id}/jobs?sort=non-existing", admin, admin_mode: true)
+ request
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
- it 'avoids N+1 DB queries' do
- get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true)
+ describe 'eager loading' do
+ let(:runner_id) { shared_runner.id }
- control = ActiveRecord::QueryRecorder.new do
- get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true)
- end
+ it 'avoids N+1 DB queries' do
+ get api(path, user, **api_params)
- create(:ci_build, :failed, runner: shared_runner, project: project)
+ control = ActiveRecord::QueryRecorder.new do
+ get api(path, user, **api_params)
+ end
- expect do
- get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true)
- end.not_to exceed_query_limit(control)
- end
+ create(:ci_build, :failed, runner: shared_runner, project: project)
- it 'batches loading of commits' do
- shared_runner = create(:ci_runner, :instance, description: 'Shared runner')
+ expect do
+ get api(path, user, **api_params)
+ end.not_to exceed_query_limit(control.count)
+ end
- project_with_repo = create(:project, :repository)
+ it 'batches loading of commits' do
+ project_with_repo = create(:project, :repository)
+ shared_runner_manager1 = create(:ci_runner_machine, runner: shared_runner, system_xid: 'id1')
- pipeline = create(:ci_pipeline, project: project_with_repo, sha: 'ddd0f15ae83993f5cb66a927a28673882e99100b')
- create(:ci_build, :running, runner: shared_runner, project: project_with_repo, pipeline: pipeline)
+ pipeline = create(:ci_pipeline, project: project_with_repo, sha: 'ddd0f15ae83993f5cb66a927a28673882e99100b')
+ create(:ci_build, :running, runner_manager: shared_runner_manager1, project: project_with_repo, pipeline: pipeline)
- pipeline = create(:ci_pipeline, project: project_with_repo, sha: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')
- create(:ci_build, :failed, runner: shared_runner, project: project_with_repo, pipeline: pipeline)
+ pipeline = create(:ci_pipeline, project: project_with_repo, sha: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')
+ create(:ci_build, :failed, runner_manager: shared_runner_manager1, project: project_with_repo, pipeline: pipeline)
- pipeline = create(:ci_pipeline, project: project_with_repo, sha: '1a0b36b3cdad1d2ee32457c102a8c0b7056fa863')
- create(:ci_build, :failed, runner: shared_runner, project: project_with_repo, pipeline: pipeline)
+ pipeline = create(:ci_pipeline, project: project_with_repo, sha: '1a0b36b3cdad1d2ee32457c102a8c0b7056fa863')
+ create(:ci_build, :failed, runner_manager: shared_runner_manager1, project: project_with_repo, pipeline: pipeline)
- expect_next_instance_of(Repository) do |repo|
- expect(repo).to receive(:commits_by).with(oids:
- %w[
- 1a0b36b3cdad1d2ee32457c102a8c0b7056fa863
- c1c67abbaf91f624347bb3ae96eabe3a1b742478
- ]).once.and_call_original
- end
+ expect_next_instance_of(Repository) do |repo|
+ expect(repo).to receive(:commits_by).with(oids:
+ %w[
+ 1a0b36b3cdad1d2ee32457c102a8c0b7056fa863
+ c1c67abbaf91f624347bb3ae96eabe3a1b742478
+ ]).once.and_call_original
+ end
- get api("/runners/#{shared_runner.id}/jobs", admin, admin_mode: true), params: { per_page: 2, order_by: 'id', sort: 'desc' }
+ get api(path, admin, admin_mode: true), params: { per_page: 2, order_by: 'id', sort: 'desc' }
+ end
end
context "when runner doesn't exist" do
+ let(:runner_id) { non_existing_record_id }
+
it 'returns 404' do
- get api('/runners/0/jobs', admin, admin_mode: true)
+ request
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -1004,70 +1057,118 @@ RSpec.describe API::Ci::Runners, :aggregate_failures, feature_category: :fleet_v
context "runner project's administrative user" do
context 'when runner exists' do
+ let(:runner_id) { shared_runner.id }
+
context 'when runner is shared' do
it 'returns 403' do
- get api("/runners/#{shared_runner.id}/jobs", user)
+ request
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when runner is a project runner' do
+ let(:runner_id) { project_runner.id }
+
it 'return jobs' do
- get api(path, user)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
- expect(json_response).to be_an(Array)
- expect(json_response.length).to eq(2)
+ expect(json_response).to match([
+ a_hash_including('id' => jobs[3].id),
+ a_hash_including('id' => jobs[4].id)
+ ])
end
- end
- context 'when valid status is provided' do
- it 'return filtered jobs' do
- get api("/runners/#{project_runner.id}/jobs?status=failed", user)
+ context 'when valid status is provided' do
+ let(:query_params) { { status: :failed } }
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to include_pagination_headers
+ it 'return filtered jobs' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
- expect(json_response).to be_an(Array)
- expect(json_response.length).to eq(1)
- expect(json_response.first).to include('id' => job_5.id)
+ expect(json_response).to match([
+ a_hash_including('id' => jobs[4].id)
+ ])
+ end
end
- end
- context 'when invalid status is provided' do
- it 'return 400' do
- get api("/runners/#{project_runner.id}/jobs?status=non-existing", user)
+ context 'when invalid status is provided' do
+ let(:query_params) { { status: 'non-existing' } }
- expect(response).to have_gitlab_http_status(:bad_request)
+ it 'return 400' do
+ request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
end
end
context "when runner doesn't exist" do
+ let(:runner_id) { non_existing_record_id }
+
it 'returns 404' do
- get api('/runners/0/jobs', user)
+ request
expect(response).to have_gitlab_http_status(:not_found)
end
end
- end
- context 'other authorized user' do
- it 'does not return jobs' do
- get api(path, user2)
+ context 'other authorized user' do
+ let(:user) { user2 }
- expect(response).to have_gitlab_http_status(:forbidden)
+ it 'does not return jobs' do
+ request
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'unauthorized user' do
+ let(:user) { nil }
+
+ it 'does not return jobs' do
+ request
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
end
end
- context 'unauthorized user' do
- it 'does not return jobs' do
- get api(path)
+ context 'with system_id param' do
+ let(:system_id_params) { { system_id: system_id } }
+ let(:system_id) { 'id1' }
+ let(:user) { admin }
+ let(:api_params) { { admin_mode: true } }
- expect(response).to have_gitlab_http_status(:unauthorized)
+ it 'returns jobs from the runner manager' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_limited_pagination_headers
+ expect(response.headers).not_to include('X-Total', 'X-Total-Pages')
+
+ expect(json_response).to match([
+ a_hash_including('id' => jobs[3].id),
+ a_hash_including('id' => jobs[4].id)
+ ])
+ end
+
+ context 'when system_id does not match runner' do
+ let(:runner_id) { shared_runner.id }
+
+ it 'does not return jobs' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ expect(json_response).to be_empty
+ end
end
end
end
diff --git a/spec/requests/organizations/organizations_controller_spec.rb b/spec/requests/organizations/organizations_controller_spec.rb
index bfd0603eb3d..381e7f98dbe 100644
--- a/spec/requests/organizations/organizations_controller_spec.rb
+++ b/spec/requests/organizations/organizations_controller_spec.rb
@@ -119,4 +119,28 @@ RSpec.describe Organizations::OrganizationsController, feature_category: :cell d
it_behaves_like 'controller action that requires authentication by any user'
end
+
+ describe 'POST #preview_markdown' do
+ subject(:gitlab_request) { post preview_markdown_organizations_path, params: { text: '### Foo \n **bar**' } }
+
+ it_behaves_like 'controller action that requires authentication by any user'
+
+ context 'when the user is signed in' do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'returns html from markdown' do
+ sign_in(user)
+ gitlab_request
+
+ body = Gitlab::Json.parse(response.body)['body']
+
+ expect(body).not_to include('Foo</h3>')
+ expect(body).to include('<strong>bar</strong>')
+ end
+ end
+ end
end
diff --git a/spec/routing/organizations/organizations_controller_routing_spec.rb b/spec/routing/organizations/organizations_controller_routing_spec.rb
index f105bb31ccf..c5c7a0ae377 100644
--- a/spec/routing/organizations/organizations_controller_routing_spec.rb
+++ b/spec/routing/organizations/organizations_controller_routing_spec.rb
@@ -29,4 +29,9 @@ RSpec.describe Organizations::OrganizationsController, :routing, feature_categor
expect(get("/-/organizations/#{organization.path}/users"))
.to route_to('organizations/organizations#users', organization_path: organization.path)
end
+
+ it 'routes to #preview_markdown' do
+ expect(post("/-/organizations/preview_markdown"))
+ .to route_to('organizations/organizations#preview_markdown')
+ end
end
diff --git a/spec/rubocop/cop/scalability/file_uploads_spec.rb b/spec/rubocop/cop/scalability/file_uploads_spec.rb
index 43ac9457ed6..50049d76f0e 100644
--- a/spec/rubocop/cop/scalability/file_uploads_spec.rb
+++ b/spec/rubocop/cop/scalability/file_uploads_spec.rb
@@ -4,7 +4,7 @@ require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/scalability/file_uploads'
RSpec.describe RuboCop::Cop::Scalability::FileUploads, feature_category: :scalability do
- let(:message) { 'Do not upload files without workhorse acceleration. Please refer to https://docs.gitlab.com/ee/development/uploads.html' }
+ let(:message) { 'Do not upload files without workhorse acceleration. Please refer to https://docs.gitlab.com/ee/development/uploads/' }
context 'with required params' do
it 'detects File in types array' do
diff --git a/spec/support/helpers/debug_with_puts.rb b/spec/support/helpers/debug_with_puts.rb
new file mode 100644
index 00000000000..b8599cc7d40
--- /dev/null
+++ b/spec/support/helpers/debug_with_puts.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+# TODO: Remove the debug_with_puts statements below! Used for debugging purposes.
+# TODO: https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/issues/323#note_1688925316
+module DebugWithPuts
+ def debug_with_puts(message)
+ return unless ENV['CI'] # rubocop:disable RSpec/AvoidConditionalStatements -- Debug information only in the CI
+
+ warn "[#{Time.current}] #{message}"
+ end
+
+ module_function :debug_with_puts
+end
diff --git a/spec/support/helpers/stub_requests.rb b/spec/support/helpers/stub_requests.rb
index bde5535705e..b77b366e037 100644
--- a/spec/support/helpers/stub_requests.rb
+++ b/spec/support/helpers/stub_requests.rb
@@ -18,15 +18,15 @@ module StubRequests
end
def stub_dns(url, ip_address:, port: 80)
- debug_with_puts "beginning of stub_dns"
+ DebugWithPuts.debug_with_puts "beginning of stub_dns"
url = parse_url(url)
- debug_with_puts "before socket = Socket.sockaddr_in"
+ DebugWithPuts.debug_with_puts "before socket = Socket.sockaddr_in"
socket = Socket.sockaddr_in(port, ip_address)
- debug_with_puts "after socket = Socket.sockaddr_in"
+ DebugWithPuts.debug_with_puts "after socket = Socket.sockaddr_in"
- debug_with_puts "before addr = Addrinfo.new(socket)"
+ DebugWithPuts.debug_with_puts "before addr = Addrinfo.new(socket)"
addr = Addrinfo.new(socket)
- debug_with_puts "after addr = Addrinfo.new(socket)"
+ DebugWithPuts.debug_with_puts "after addr = Addrinfo.new(socket)"
# See Gitlab::UrlBlocker
allow(Addrinfo).to receive(:getaddrinfo)
@@ -58,12 +58,4 @@ module StubRequests
def parse_url(url)
url.is_a?(URI) ? url : URI(url)
end
-
- # TODO: Remove the debug_with_puts statements below! Used for debugging purposes.
- # TODO: https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/issues/323#note_1688925316
- def debug_with_puts(message)
- return unless ENV['CI'] # rubocop:disable RSpec/AvoidConditionalStatements -- Debug information only in the CI
-
- puts "[#{Time.current}] #{message}"
- end
end
diff --git a/vendor/gems/bundler-checksum/Gemfile.lock b/vendor/gems/bundler-checksum/Gemfile.lock
index 8ae053f0105..4db0507a63b 100644
--- a/vendor/gems/bundler-checksum/Gemfile.lock
+++ b/vendor/gems/bundler-checksum/Gemfile.lock
@@ -15,4 +15,4 @@ DEPENDENCIES
bundler-checksum!
BUNDLED WITH
- 2.3.17
+ 2.5.4
diff --git a/vendor/gems/bundler-checksum/lib/bundler_checksum.rb b/vendor/gems/bundler-checksum/lib/bundler_checksum.rb
index b3d36521f24..083082c0ab1 100644
--- a/vendor/gems/bundler-checksum/lib/bundler_checksum.rb
+++ b/vendor/gems/bundler-checksum/lib/bundler_checksum.rb
@@ -41,12 +41,18 @@ module Bundler
raise "#{@package.inspect} does not have :@gem" unless source
raise "#{source.inspect} does not respond to :with_read_io" unless source.respond_to?(:with_read_io)
- digest = source.with_read_io do |io|
- digest = SharedHelpers.digest(:SHA256).new
- digest << io.read(16_384) until io.eof?
- io.rewind
- send(checksum_type(checksum), digest)
- end
+ digest =
+ if Gem::Version.new(Bundler::VERSION) >= Gem::Version.new("2.5.0")
+ gem_checksum.digest
+ else
+ source.with_read_io do |io|
+ digest = SharedHelpers.digest(:SHA256).new
+ digest << io.read(16_384) until io.eof?
+ io.rewind
+ send(checksum_type(checksum), digest)
+ end
+ end
+
unless digest == checksum
raise SecurityError, <<-MESSAGE
Bundler cannot continue installing #{spec.name} (#{spec.version}).
diff --git a/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.lock b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.lock
index d633184e300..2aa6a15070f 100644
--- a/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.lock
+++ b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.lock
@@ -135,4 +135,4 @@ DEPENDENCIES
rails (~> 6.1.6.1)
BUNDLED WITH
- 2.3.22
+ 2.5.4