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:
-rw-r--r--.gitlab/ci/memory.gitlab-ci.yml29
-rw-r--r--.rubocop.yml8
-rw-r--r--.rubocop_todo/rspec/multiple_memoized_helpers.yml11
-rw-r--r--.rubocop_todo/style/open_struct_use.yml1
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--app/assets/javascripts/alert_management/components/alert_management_table.vue15
-rw-r--r--app/assets/javascripts/incidents/components/incidents_list.vue15
-rw-r--r--app/assets/javascripts/issues/create_merge_request_dropdown.js35
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/components/app.vue53
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/components/sign_in_button.vue6
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/pages/sign_in.vue40
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/pages/subscriptions.vue43
-rw-r--r--app/assets/javascripts/repository/components/table/parent_row.vue13
-rw-r--r--app/assets/stylesheets/page_bundles/jira_connect.scss4
-rw-r--r--app/controllers/projects/commit_controller.rb2
-rw-r--r--app/models/blob.rb4
-rw-r--r--app/models/ci/runner.rb17
-rw-r--r--app/services/branches/create_service.rb2
-rw-r--r--app/views/projects/blob/_header_content.html.haml2
-rw-r--r--app/views/projects/issues/_new_branch.html.haml4
-rw-r--r--doc/api/runners.md6
-rw-r--r--doc/architecture/blueprints/ci_data_decay/index.md35
-rw-r--r--doc/development/documentation/styleguide/word_list.md10
-rw-r--r--lib/api/ci/runners.rb7
-rw-r--r--locale/gitlab.pot49
-rw-r--r--package.json2
-rwxr-xr-xscripts/generate-gems-memory-metrics-static19
-rwxr-xr-xscripts/generate-gems-size-metrics-static31
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb12
-rw-r--r--spec/features/issues/user_creates_branch_and_merge_request_spec.rb25
-rw-r--r--spec/finders/template_finder_spec.rb7
-rw-r--r--spec/frontend/issues/create_merge_request_dropdown_spec.js4
-rw-r--r--spec/frontend/jira_connect/subscriptions/components/app_spec.js79
-rw-r--r--spec/frontend/jira_connect/subscriptions/components/sign_in_button_spec.js12
-rw-r--r--spec/frontend/jira_connect/subscriptions/pages/sign_in_spec.js62
-rw-r--r--spec/frontend/jira_connect/subscriptions/pages/subscriptions_spec.js56
-rw-r--r--spec/models/blob_spec.rb14
-rw-r--r--spec/models/ci/runner_spec.rb41
-rw-r--r--spec/requests/api/ci/runners_spec.rb11
-rw-r--r--spec/services/branches/create_service_spec.rb2
-rw-r--r--spec/support/helpers/fake_blob_helpers.rb5
-rw-r--r--workhorse/Makefile34
-rwxr-xr-xworkhorse/_support/make-gnu-build-id.sh30
-rw-r--r--yarn.lock12
44 files changed, 555 insertions, 316 deletions
diff --git a/.gitlab/ci/memory.gitlab-ci.yml b/.gitlab/ci/memory.gitlab-ci.yml
index 520f77cd8ca..c6572d9709c 100644
--- a/.gitlab/ci/memory.gitlab-ci.yml
+++ b/.gitlab/ci/memory.gitlab-ci.yml
@@ -11,36 +11,9 @@
metrics: "${METRICS_FILE}"
expire_in: 31d
-# Disabled since it causes intermittent SIGSEGV in CI:
-# https://gitlab.com/gitlab-org/gitlab/-/issues/350296
-.memory-static:
- extends: .only-code-memory-job-base
- stage: test
- needs: ["setup-test-env"]
- variables:
- SETUP_DB: "false"
- MEMORY_BUNDLE_MEM_FILE: "tmp/memory_bundle_mem.txt"
- MEMORY_BUNDLE_OBJECTS_FILE: "tmp/memory_bundle_objects.txt"
- script:
- # Uses two different reports from the 'derailed_benchmars' gem.
-
- # Loads each of gems in the Gemfile and checks how much memory they consume when they are required.
- # 'derailed_benchmarks' internally uses 'get_process_mem'
- - bundle exec derailed bundle:mem > "${MEMORY_BUNDLE_MEM_FILE}"
- - scripts/generate-gems-size-metrics-static "${MEMORY_BUNDLE_MEM_FILE}" >> "${METRICS_FILE}"
-
- # Outputs detailed information about objects created while gems are loaded.
- # 'derailed_benchmarks' internally uses 'memory_profiler'
- - bundle exec derailed bundle:objects > "${MEMORY_BUNDLE_OBJECTS_FILE}"
- - scripts/generate-gems-memory-metrics-static "${MEMORY_BUNDLE_OBJECTS_FILE}" >> "${METRICS_FILE}"
- artifacts:
- paths:
- - "${METRICS_FILE}"
- - "${MEMORY_BUNDLE_MEM_FILE}"
- - "${MEMORY_BUNDLE_OBJECTS_FILE}"
# Show memory usage caused by invoking require per gem.
-# Unlike `memory-static`, it hits the app with one request to ensure that any last minute require-s have been called.
+# Hits the app with one request to ensure that any last minute require-s have been called.
# The application is booted in `production` environment.
# All tests are run without a webserver (directly using Rack::Mock by default).
memory-on-boot:
diff --git a/.rubocop.yml b/.rubocop.yml
index caa9d72ef47..bea2cf66efd 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -97,14 +97,8 @@ RSpec/FilePath:
# Configuration parameters: AllowSubject.
RSpec/MultipleMemoizedHelpers:
- Max: 28
+ Max: 25
AllowSubject: true
- Exclude:
- - 'spec/migrations/**/*.rb'
- - 'spec/lib/gitlab/background_migration/populate_project_snippet_statistics_spec.rb'
- - 'spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings_spec.rb'
- - 'ee/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb'
Naming/FileName:
ExpectMatchingDefinition: true
diff --git a/.rubocop_todo/rspec/multiple_memoized_helpers.yml b/.rubocop_todo/rspec/multiple_memoized_helpers.yml
new file mode 100644
index 00000000000..2f6d390bec2
--- /dev/null
+++ b/.rubocop_todo/rspec/multiple_memoized_helpers.yml
@@ -0,0 +1,11 @@
+---
+RSpec/MultipleMemoizedHelpers:
+ Exclude:
+ - spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb
+ - spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb
+ - spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb
+ - spec/requests/api/ci/runner/jobs_artifacts_spec.rb
+ - ee/spec/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids_spec.rb
+ - ee/spec/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings_spec.rb
+ - ee/spec/services/ee/boards/issues/move_service_spec.rb
+ - ee/spec/services/security/store_report_service_spec.rb
diff --git a/.rubocop_todo/style/open_struct_use.yml b/.rubocop_todo/style/open_struct_use.yml
index 4f7faa8f484..54ae23d232a 100644
--- a/.rubocop_todo/style/open_struct_use.yml
+++ b/.rubocop_todo/style/open_struct_use.yml
@@ -17,7 +17,6 @@ Style/OpenStructUse:
- spec/factories/go_module_versions.rb
- spec/factories/wiki_pages.rb
- spec/features/projects/clusters_spec.rb
- - spec/finders/template_finder_spec.rb
- spec/graphql/mutations/branches/create_spec.rb
- spec/graphql/mutations/clusters/agent_tokens/create_spec.rb
- spec/graphql/mutations/clusters/agents/create_spec.rb
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index a63cb35e6f0..3f4830156cb 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.52.0
+1.53.0
diff --git a/app/assets/javascripts/alert_management/components/alert_management_table.vue b/app/assets/javascripts/alert_management/components/alert_management_table.vue
index 79a6bac3ba7..84c2b216859 100644
--- a/app/assets/javascripts/alert_management/components/alert_management_table.vue
+++ b/app/assets/javascripts/alert_management/components/alert_management_table.vue
@@ -15,7 +15,7 @@ import getAlertsQuery from '~/graphql_shared/queries/get_alerts.query.graphql';
import { fetchPolicies } from '~/lib/graphql';
import { convertToSnakeCase } from '~/lib/utils/text_utility';
import { joinPaths, visitUrl } from '~/lib/utils/url_utility';
-import { s__, __ } from '~/locale';
+import { s__, __, n__ } from '~/locale';
import AlertStatus from '~/vue_shared/alert_details/components/alert_status.vue';
import {
tdClass,
@@ -32,8 +32,11 @@ const TH_TEST_ID = { 'data-testid': 'alert-management-severity-sort' };
const TWELVE_HOURS_IN_MS = 12 * 60 * 60 * 1000;
+const MAX_VISIBLE_ASSIGNEES = 4;
+
export default {
trackAlertListViewsOptions,
+ MAX_VISIBLE_ASSIGNEES,
i18n: {
noAlertsMsg: s__(
'AlertManagement|No alerts available to display. See %{linkStart}enabling alert management%{linkEnd} for more information on adding alerts to the list.',
@@ -258,6 +261,13 @@ export default {
this.serverErrorMessage = '';
this.isErrorAlertDismissed = true;
},
+ assigneesBadgeSrOnlyText(item) {
+ return n__(
+ '%d additional assignee',
+ '%d additional assignees',
+ item.assignees.nodes.length - MAX_VISIBLE_ASSIGNEES,
+ );
+ },
},
};
</script>
@@ -365,10 +375,11 @@ export default {
<gl-avatars-inline
:avatars="item.assignees.nodes"
:collapsed="true"
- :max-visible="4"
+ :max-visible="$options.MAX_VISIBLE_ASSIGNEES"
:avatar-size="24"
badge-tooltip-prop="name"
:badge-tooltip-max-chars="100"
+ :badge-sr-only-text="assigneesBadgeSrOnlyText(item)"
>
<template #avatar="{ avatar }">
<gl-avatar-link
diff --git a/app/assets/javascripts/incidents/components/incidents_list.vue b/app/assets/javascripts/incidents/components/incidents_list.vue
index 37597da3c8e..7a904bdb6ad 100644
--- a/app/assets/javascripts/incidents/components/incidents_list.vue
+++ b/app/assets/javascripts/incidents/components/incidents_list.vue
@@ -12,7 +12,7 @@ import {
} from '@gitlab/ui';
import { isValidSlaDueAt } from 'ee_else_ce/vue_shared/components/incidents/utils';
import { visitUrl, mergeUrlParams, joinPaths } from '~/lib/utils/url_utility';
-import { s__ } from '~/locale';
+import { s__, n__ } from '~/locale';
import { INCIDENT_SEVERITY } from '~/sidebar/components/severity/constants';
import SeverityToken from '~/sidebar/components/severity/severity.vue';
import Tracking from '~/tracking';
@@ -38,6 +38,8 @@ import {
import getIncidentsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql';
import getIncidents from '../graphql/queries/get_incidents.query.graphql';
+const MAX_VISIBLE_ASSIGNEES = 4;
+
export default {
trackIncidentCreateNewOptions,
trackIncidentListViewsOptions,
@@ -94,6 +96,7 @@ export default {
thAttr: TH_PUBLISHED_TEST_ID,
},
],
+ MAX_VISIBLE_ASSIGNEES,
components: {
GlLoadingIcon,
GlTable,
@@ -295,6 +298,13 @@ export default {
errorAlertDismissed() {
this.isErrorAlertDismissed = true;
},
+ assigneesBadgeSrOnlyText(item) {
+ return n__(
+ '%d additional assignee',
+ '%d additional assignees',
+ item.assignees.nodes.length - MAX_VISIBLE_ASSIGNEES,
+ );
+ },
isValidSlaDueAt,
},
};
@@ -391,10 +401,11 @@ export default {
<gl-avatars-inline
:avatars="item.assignees.nodes"
:collapsed="true"
- :max-visible="4"
+ :max-visible="$options.MAX_VISIBLE_ASSIGNEES"
:avatar-size="24"
badge-tooltip-prop="name"
:badge-tooltip-max-chars="100"
+ :badge-sr-only-text="assigneesBadgeSrOnlyText(item)"
>
<template #avatar="{ avatar }">
<gl-avatar-link
diff --git a/app/assets/javascripts/issues/create_merge_request_dropdown.js b/app/assets/javascripts/issues/create_merge_request_dropdown.js
index 5d36396bc6e..a3752c7043c 100644
--- a/app/assets/javascripts/issues/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/issues/create_merge_request_dropdown.js
@@ -69,11 +69,11 @@ export default class CreateMergeRequestDropdown {
this.regexps = {
branch: {
createBranchPath: new RegExp('(branch_name=)(.+?)(?=&issue)'),
- createMrPath: new RegExp('(branch_name=)(.+?)(?=&ref)'),
+ createMrPath: new RegExp('(source_branch%5D=)(.+?)(?=&)'),
},
ref: {
createBranchPath: new RegExp('(ref=)(.+?)$'),
- createMrPath: new RegExp('(ref=)(.+?)$'),
+ createMrPath: new RegExp('(target_branch%5D=)(.+?)$'),
},
};
@@ -167,23 +167,18 @@ export default class CreateMergeRequestDropdown {
}
createMergeRequest() {
- this.isCreatingMergeRequest = true;
-
- return axios
- .post(this.createMrPath, {
- target_project_id: canCreateConfidentialMergeRequest()
- ? confidentialMergeRequestState.selectedProject.id
- : null,
- })
- .then(({ data }) => {
- this.mergeRequestCreated = true;
- window.location.href = data.url;
- })
- .catch(() =>
- createFlash({
- message: __('Failed to create merge request. Please try again.'),
- }),
- );
+ return new Promise(() => {
+ this.isCreatingMergeRequest = true;
+
+ return this.createBranch().then(() => {
+ window.location.href = canCreateConfidentialMergeRequest()
+ ? this.createMrPath.replace(
+ this.projectPath,
+ confidentialMergeRequestState.selectedProject.pathWithNamespace,
+ )
+ : this.createMrPath;
+ });
+ });
}
disable() {
@@ -562,5 +557,7 @@ export default class CreateMergeRequestDropdown {
this.regexps[target].createMrPath,
pathReplacement,
);
+
+ this.wrapperEl.dataset.createMrPath = this.createMrPath;
}
}
diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/app.vue b/app/assets/javascripts/jira_connect/subscriptions/components/app.vue
index 32b22f5103c..905e242e977 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/components/app.vue
+++ b/app/assets/javascripts/jira_connect/subscriptions/components/app.vue
@@ -1,12 +1,11 @@
<script>
-import { GlAlert, GlLink, GlSprintf, GlEmptyState } from '@gitlab/ui';
+import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { mapState, mapMutations } from 'vuex';
import { retrieveAlert } from '~/jira_connect/subscriptions/utils';
import { SET_ALERT } from '../store/mutation_types';
-import SubscriptionsList from './subscriptions_list.vue';
-import AddNamespaceButton from './add_namespace_button.vue';
-import SignInButton from './sign_in_button.vue';
+import SignInPage from '../pages/sign_in.vue';
+import SubscriptionsPage from '../pages/subscriptions.vue';
import UserLink from './user_link.vue';
import CompatibilityAlert from './compatibility_alert.vue';
@@ -16,12 +15,10 @@ export default {
GlAlert,
GlLink,
GlSprintf,
- GlEmptyState,
- SubscriptionsList,
- AddNamespaceButton,
- SignInButton,
UserLink,
CompatibilityAlert,
+ SignInPage,
+ SubscriptionsPage,
},
inject: {
usersPath: {
@@ -84,43 +81,9 @@ export default {
<user-link :user-signed-in="userSignedIn" :has-subscriptions="hasSubscriptions" />
<h2 class="gl-text-center gl-mb-7">{{ s__('JiraService|GitLab for Jira Configuration') }}</h2>
- <div class="jira-connect-app-body gl-mx-auto gl-px-5 gl-mb-7">
- <template v-if="hasSubscriptions">
- <div class="gl-display-flex gl-justify-content-end">
- <sign-in-button v-if="!userSignedIn" :users-path="usersPath" />
- <add-namespace-button v-else />
- </div>
-
- <subscriptions-list />
- </template>
- <template v-else>
- <div v-if="!userSignedIn" class="gl-text-center">
- <p class="gl-mb-7">{{ s__('JiraService|Sign in to GitLab.com to get started.') }}</p>
- <sign-in-button class="gl-mb-7" :users-path="usersPath">
- {{ __('Sign in to GitLab') }}
- </sign-in-button>
- <p>
- {{
- s__(
- 'Integrations|Note: this integration only works with accounts on GitLab.com (SaaS).',
- )
- }}
- </p>
- </div>
- <gl-empty-state
- v-else
- :title="s__('Integrations|No linked namespaces')"
- :description="
- s__(
- 'Integrations|Namespaces are the GitLab groups and subgroups you link to this Jira instance.',
- )
- "
- >
- <template #actions>
- <add-namespace-button />
- </template>
- </gl-empty-state>
- </template>
+ <div class="gl-layout-w-limited gl-mx-auto gl-px-5 gl-mb-7">
+ <sign-in-page v-if="!userSignedIn" :has-subscriptions="hasSubscriptions" />
+ <subscriptions-page v-else :has-subscriptions="hasSubscriptions" />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_button.vue b/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_button.vue
index dc0a77e99c2..627abcdd4a0 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_button.vue
+++ b/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_button.vue
@@ -1,6 +1,7 @@
<script>
import { GlButton } from '@gitlab/ui';
import { getGitlabSignInURL } from '~/jira_connect/subscriptions/utils';
+import { s__ } from '~/locale';
export default {
components: {
@@ -25,12 +26,15 @@ export default {
this.signInURL = await getGitlabSignInURL(this.usersPath);
},
},
+ i18n: {
+ defaultButtonText: s__('Integrations|Sign in to GitLab'),
+ },
};
</script>
<template>
<gl-button category="primary" variant="info" :href="signInURL" target="_blank">
<slot>
- {{ s__('Integrations|Sign in to add namespaces') }}
+ {{ $options.i18n.defaultButtonText }}
</slot>
</gl-button>
</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in.vue b/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in.vue
new file mode 100644
index 00000000000..2bce5afc72b
--- /dev/null
+++ b/app/assets/javascripts/jira_connect/subscriptions/pages/sign_in.vue
@@ -0,0 +1,40 @@
+<script>
+import { s__ } from '~/locale';
+import SubscriptionsList from '../components/subscriptions_list.vue';
+import SignInButton from '../components/sign_in_button.vue';
+
+export default {
+ name: 'SignInPage',
+ components: {
+ SubscriptionsList,
+ SignInButton,
+ },
+ inject: ['usersPath'],
+ props: {
+ hasSubscriptions: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ i18n: {
+ signinButtonTextWithSubscriptions: s__('Integrations|Sign in to add namespaces'),
+ signInText: s__('JiraService|Sign in to GitLab.com to get started.'),
+ },
+};
+</script>
+
+<template>
+ <div v-if="hasSubscriptions">
+ <div class="gl-display-flex gl-justify-content-end">
+ <sign-in-button :users-path="usersPath">
+ {{ $options.i18n.signinButtonTextWithSubscriptions }}
+ </sign-in-button>
+ </div>
+
+ <subscriptions-list />
+ </div>
+ <div v-else class="gl-text-center">
+ <p class="gl-mb-7">{{ $options.i18n.signInText }}</p>
+ <sign-in-button class="gl-mb-7" :users-path="usersPath" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/pages/subscriptions.vue b/app/assets/javascripts/jira_connect/subscriptions/pages/subscriptions.vue
new file mode 100644
index 00000000000..426f2999370
--- /dev/null
+++ b/app/assets/javascripts/jira_connect/subscriptions/pages/subscriptions.vue
@@ -0,0 +1,43 @@
+<script>
+import { GlEmptyState } from '@gitlab/ui';
+import SubscriptionsList from '../components/subscriptions_list.vue';
+import AddNamespaceButton from '../components/add_namespace_button.vue';
+
+export default {
+ name: 'SubscriptionsPage',
+ components: {
+ GlEmptyState,
+ SubscriptionsList,
+ AddNamespaceButton,
+ },
+ props: {
+ hasSubscriptions: {
+ type: Boolean,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="hasSubscriptions">
+ <div class="gl-display-flex gl-justify-content-end">
+ <add-namespace-button />
+ </div>
+
+ <subscriptions-list />
+ </div>
+ <gl-empty-state
+ v-else
+ :title="s__('Integrations|No linked namespaces')"
+ :description="
+ s__(
+ 'Integrations|Namespaces are the GitLab groups and subgroups you link to this Jira instance.',
+ )
+ "
+ >
+ <template #actions>
+ <add-namespace-button />
+ </template>
+ </gl-empty-state>
+</template>
diff --git a/app/assets/javascripts/repository/components/table/parent_row.vue b/app/assets/javascripts/repository/components/table/parent_row.vue
index fb0e505a16e..8a081944600 100644
--- a/app/assets/javascripts/repository/components/table/parent_row.vue
+++ b/app/assets/javascripts/repository/components/table/parent_row.vue
@@ -1,10 +1,13 @@
<script>
-import { GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
export default {
components: {
GlLoadingIcon,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
commitRef: {
type: String,
@@ -41,7 +44,13 @@ export default {
<template>
<tr class="tree-item">
- <td colspan="3" class="tree-item-file-name" @click.self="clickRow">
+ <td
+ v-gl-tooltip.left.viewport
+ :title="__('Go to parent directory')"
+ colspan="3"
+ class="tree-item-file-name"
+ @click.self="clickRow"
+ >
<gl-loading-icon
v-if="parentPath === loadingPath"
size="sm"
diff --git a/app/assets/stylesheets/page_bundles/jira_connect.scss b/app/assets/stylesheets/page_bundles/jira_connect.scss
index 9fe0490571e..1c8fd7e2590 100644
--- a/app/assets/stylesheets/page_bundles/jira_connect.scss
+++ b/app/assets/stylesheets/page_bundles/jira_connect.scss
@@ -40,10 +40,6 @@ $header-height: 40px;
max-width: 1000px;
}
-.jira-connect-app-body {
- max-width: 768px;
-}
-
// needed for external_link
svg.s16 {
width: 16px;
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 62935e133c5..0ce0b8b8895 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -92,6 +92,8 @@ class Projects::CommitController < Projects::ApplicationController
end
def branches
+ return git_not_found! unless commit
+
# branch_names_contains/tag_names_contains can take a long time when there are thousands of
# branches/tags - each `git branch --contains xxx` request can consume a cpu core.
# so only do the query when there are a manageable number of branches/tags
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 5731d38abe4..cc7758d9674 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -178,6 +178,10 @@ class Blob < SimpleDelegator
end
end
+ def symlink?
+ mode == MODE_SYMLINK
+ end
+
def extension
@extension ||= extname.downcase.delete('.')
end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index d08cc71305e..d52129d73f3 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -162,6 +162,23 @@ module Ci
)
end
+ scope :group_or_instance_wide, -> (group) do
+ group_and_ancestor_runners =
+ if ::Feature.enabled?(:ci_find_runners_by_ci_mirrors, group, default_enabled: :yaml)
+ belonging_to_group_and_ancestors(group.id)
+ else
+ legacy_belonging_to_group(group.id, include_ancestors: true)
+ end
+
+ from_union(
+ [
+ group_and_ancestor_runners,
+ instance_type
+ ],
+ remove_duplicates: false
+ )
+ end
+
scope :assignable_for, ->(project) do
# FIXME: That `to_sql` is needed to workaround a weird Rails bug.
# Without that, placeholders would miss one and couldn't match.
diff --git a/app/services/branches/create_service.rb b/app/services/branches/create_service.rb
index b5faf2ec281..7300b31e3b3 100644
--- a/app/services/branches/create_service.rb
+++ b/app/services/branches/create_service.rb
@@ -21,7 +21,7 @@ module Branches
error("Failed to create branch '#{branch_name}': invalid reference name '#{ref}'")
end
rescue Gitlab::Git::PreReceiveError => e
- Gitlab::ErrorTracking.track_exception(e, pre_receive_message: e.raw_message, branch_name: branch_name, ref: ref)
+ Gitlab::ErrorTracking.log_exception(e, pre_receive_message: e.raw_message, branch_name: branch_name, ref: ref)
error(e.message)
end
diff --git a/app/views/projects/blob/_header_content.html.haml b/app/views/projects/blob/_header_content.html.haml
index f5e32e7f589..9cd2f583fdd 100644
--- a/app/views/projects/blob/_header_content.html.haml
+++ b/app/views/projects/blob/_header_content.html.haml
@@ -9,7 +9,7 @@
= copy_file_path_button(blob.path)
%small.mr-1
- - if blob.mode == Blob::MODE_SYMLINK
+ - if blob.symlink?
= _('Symbolic link') << ' ·'
= number_to_human_size(blob.raw_size)
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index 1cf0551535b..8d6c0e29b6a 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -6,8 +6,8 @@
- create_mr_text = can_create_confidential_merge_request? ? _('Create confidential merge request') : _('Create merge request')
- can_create_path = can_create_branch_project_issue_path(@project, @issue)
- - create_mr_path = create_merge_request_project_issue_path(@project, @issue, branch_name: @issue.to_branch_name, ref: @project.default_branch)
- - create_branch_path = project_branches_path(@project, branch_name: @issue.to_branch_name, ref: @project.default_branch, issue_iid: @issue.iid)
+ - create_mr_path = project_new_merge_request_path(@project, merge_request: { source_branch: @issue.to_branch_name, target_branch: @project.default_branch })
+ - create_branch_path = project_branches_path(@project, branch_name: @issue.to_branch_name, ref: @project.default_branch, issue_iid: @issue.iid, format: :json)
- refs_path = refs_namespace_project_path(@project.namespace, @project, search: '')
.create-mr-dropdown-wrap.d-inline-block.full-width-mobile.js-create-mr{ data: { project_path: @project.full_path, project_id: @project.id, can_create_path: can_create_path, create_mr_path: create_mr_path, create_branch_path: create_branch_path, refs_path: refs_path, is_confidential: can_create_confidential_merge_request?.to_s } }
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 3cccb86194a..a8b98069719 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -448,8 +448,7 @@ Example response:
## List project's runners
-List all runners (specific and shared) available in the project. Shared runners
-are listed if at least one shared runner is defined.
+List all runners available in the project, including from ancestor groups and any shared runners.
```plaintext
GET /projects/:id/runners
@@ -567,8 +566,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
## List group's runners
-List all runners (specific and shared) available in the group as well it's ancestor groups.
-Shared runners are listed if at least one shared runner is defined.
+List all runners available in the group as well as its ancestor groups, including any shared runners.
```plaintext
GET /groups/:id/runners
diff --git a/doc/architecture/blueprints/ci_data_decay/index.md b/doc/architecture/blueprints/ci_data_decay/index.md
index 155c781b04a..cbd9f5dea7a 100644
--- a/doc/architecture/blueprints/ci_data_decay/index.md
+++ b/doc/architecture/blueprints/ci_data_decay/index.md
@@ -74,13 +74,13 @@ we might want to follow these three tracks described below.
<!-- markdownlint-disable MD029 -->
-1. Partition builds queuing tables
-2. Archive CI/CD data into partitioned database schema
-3. Migrate archived builds metadata out of primary database
+1. Partition CI/CD builds queuing database tables
+2. Partition CI/CD pipelines database tables
+3. Reduce the rate of builds metadata table growth
<!-- markdownlint-enable MD029 -->
-### Migrate archived builds metadata out of primary database
+### Reduce the rate of builds metadata table growth
Once a build (or a pipeline) gets archived, it is no longer possible to resume
pipeline processing in such pipeline. It means that all the metadata, we store
@@ -98,15 +98,16 @@ be able to use de-duplication of metadata entries and other normalization
strategies to consume less storage while retaining ability to query this
dataset. Technical evaluation will be required to find the best solution here.
-Epic: [Migrate archived builds metadata out of primary database](https://gitlab.com/groups/gitlab-org/-/epics/7216).
+Epic: [Reduce the rate of builds metadata table growth](https://gitlab.com/groups/gitlab-org/-/epics/7434).
-### Archive CI/CD data into partitioned database schema
+### Partition CI/CD pipelines database tables
-After we move CI/CD metadata to a different store, the problem of having
-billions of rows describing pipelines, builds and artifacts, remains. We still
-need to keep reference to the metadata we store in object storage and we still
-do need to be able to retrieve this information reliably in bulk (or search
-through it).
+After we move CI/CD metadata to a different store, or reduce the rate of
+metadata growth in a different way, the problem of having billions of rows
+describing pipelines, builds and artifacts, remains. We still need to keep
+reference to the metadata we might store in object storage and we still do need
+to be able to retrieve this information reliably in bulk (or search through
+it).
It means that by moving data to object storage we might not be able to reduce
the number of rows in CI/CD tables. Moving data to object storage should help
@@ -132,9 +133,9 @@ partitioning on the application level.
Partitioning rarely accessed data should also follow the policy defined for
builds archival, to make it consistent and reliable.
-Epic: [Archive CI/CD data into partitioned database schema](https://gitlab.com/groups/gitlab-org/-/epics/5417).
+Epic: [Partition CI/CD pipelines database tables](https://gitlab.com/groups/gitlab-org/-/epics/5417).
-### Partition builds queuing tables
+### Partition CI/CD builds queuing database tables
While working on the [CI/CD Scale](../ci_scale/index.md) blueprint, we have
introduced a [new architecture for queuing CI/CD builds](https://gitlab.com/groups/gitlab-org/-/epics/5909#note_680407908)
@@ -156,7 +157,7 @@ for builds archival. Instead we should leverage a long-standing policy saying
that builds created more 24 hours ago need to be removed from the queue. This
business rule is present in the product since the inception of GitLab CI.
-Epic: [Partition builds queuing tables](https://gitlab.com/gitlab-org/gitlab/-/issues/347027).
+Epic: [Partition CI/CD builds queuing database tables](https://gitlab.com/groups/gitlab-org/-/epics/7438).
## Principles
@@ -215,9 +216,9 @@ pipelines data, although a user provided partition identifier may be required.
All three tracks can be worked on in parallel:
-1. [Migrate archived build metadata to object storage](https://gitlab.com/groups/gitlab-org/-/epics/7216).
-1. [Partition CI/CD data that have been archived](https://gitlab.com/groups/gitlab-org/-/epics/5417).
-1. [Partition CI/CD queuing tables using list partitioning](https://gitlab.com/gitlab-org/gitlab/-/issues/347027)
+1. [Reduce the rate of builds metadata table growth](https://gitlab.com/groups/gitlab-org/-/epics/7434).
+1. [Partition CI/CD pipelines database tables](https://gitlab.com/groups/gitlab-org/-/epics/5417).
+1. [Partition CI/CD queuing tables using list partitioning](https://gitlab.com/groups/gitlab-org/-/epics/7438)
## Status
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index 5fdc8404ed8..f3908398123 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -316,6 +316,11 @@ Instead of:
Use **expand** instead of **open** when you are talking about expanding or collapsing a section in the UI.
+## FAQ
+
+We want users to find information quickly, and they rarely search for the term **FAQ**.
+Information in FAQs belongs with other similar information, under an easily searchable topic title.
+
## field
Use **box** instead of **field** or **text box**.
@@ -371,6 +376,11 @@ Use title case for **GitLab Runner**. This is the product you install. See also
Use **GitLab self-managed** to refer to the product license for GitLab instances managed by customers themselves.
+## guide
+
+We want to speak directly to users. On `docs.gitlab.com`, do not use **guide** as part of a page title.
+For example, **Snowplow Guide**. Instead, speak about the feature itself, and how to use it. For example, **Use Snowplow to do xyz**.
+
## Guest
When writing about the Guest role:
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 51a83b1c415..12c336e0938 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -232,12 +232,7 @@ module API
use :pagination
end
get ':id/runners' do
- runners = if ::Feature.enabled?(:ci_find_runners_by_ci_mirrors, user_group, default_enabled: :yaml)
- ::Ci::Runner.belonging_to_group_and_ancestors(user_group.id)
- else
- ::Ci::Runner.legacy_belonging_to_group(user_group.id, include_ancestors: true)
- end
-
+ runners = ::Ci::Runner.group_or_instance_wide(user_group)
runners = apply_filter(runners, params)
present paginate(runners), with: Entities::Ci::Runner
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 46179ff75d0..2286eba1b62 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -116,6 +116,26 @@ msgid_plural "%d URLs scanned"
msgstr[0] ""
msgstr[1] ""
+msgid "%d additional approver"
+msgid_plural "%d additional approvers"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d additional assignee"
+msgid_plural "%d additional assignees"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d additional commenter"
+msgid_plural "%d additional commenters"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d additional committer"
+msgid_plural "%d additional committers"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d approver"
msgid_plural "%d approvers"
msgstr[0] ""
@@ -14758,9 +14778,6 @@ msgstr ""
msgid "Failed to create import label for jira import."
msgstr ""
-msgid "Failed to create merge request. Please try again."
-msgstr ""
-
msgid "Failed to create new access token: %{token_response_message}"
msgstr ""
@@ -15780,6 +15797,9 @@ msgstr ""
msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
msgstr ""
+msgid "Geo|All"
+msgstr ""
+
msgid "Geo|All %{replicable_name}"
msgstr ""
@@ -16632,6 +16652,9 @@ msgstr ""
msgid "Go to parent"
msgstr ""
+msgid "Go to parent directory"
+msgstr ""
+
msgid "Go to previous page"
msgstr ""
@@ -19317,9 +19340,6 @@ msgstr ""
msgid "Integrations|No linked namespaces"
msgstr ""
-msgid "Integrations|Note: this integration only works with accounts on GitLab.com (SaaS)."
-msgstr ""
-
msgid "Integrations|Projects using custom settings"
msgstr ""
@@ -19356,6 +19376,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to GitLab"
+msgstr ""
+
msgid "Integrations|Sign in to add namespaces"
msgstr ""
@@ -23596,9 +23619,6 @@ msgstr ""
msgid "NetworkPolicies|%{strongOpen}any%{strongClose} port"
msgstr ""
-msgid "NetworkPolicies|.yaml"
-msgstr ""
-
msgid "NetworkPolicies|.yaml mode"
msgstr ""
@@ -23677,9 +23697,6 @@ msgstr ""
msgid "NetworkPolicies|Policy definition"
msgstr ""
-msgid "NetworkPolicies|Rule"
-msgstr ""
-
msgid "NetworkPolicies|Rule mode"
msgstr ""
@@ -31924,6 +31941,9 @@ msgstr ""
msgid "SecurityOrchestration|Status"
msgstr ""
+msgid "SecurityOrchestration|Summary"
+msgstr ""
+
msgid "SecurityOrchestration|The %{scanners} %{severities} in an open merge request targeting %{branches}."
msgstr ""
@@ -34942,7 +34962,10 @@ msgstr ""
msgid "SuperSonics|current subscription"
msgstr ""
-msgid "SuperSonics|history subscriptions"
+msgid "SuperSonics|future subscriptions"
+msgstr ""
+
+msgid "SuperSonics|past subscriptions"
msgstr ""
msgid "Support"
diff --git a/package.json b/package.json
index a235d2aaf9e..93c6450a99e 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,7 @@
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "2.2.0",
- "@gitlab/ui": "33.1.0",
+ "@gitlab/ui": "34.0.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "6.1.4-1",
"@rails/ujs": "6.1.4-1",
diff --git a/scripts/generate-gems-memory-metrics-static b/scripts/generate-gems-memory-metrics-static
deleted file mode 100755
index 42191f078f1..00000000000
--- a/scripts/generate-gems-memory-metrics-static
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-abort "usage: #{__FILE__} <memory_bundle_objects_file_name>" unless ARGV.length == 1
-memory_bundle_objects_file_name = ARGV.first
-
-full_report = File.readlines(memory_bundle_objects_file_name)
-
-allocated_str = full_report[1]
-retained_str = full_report[2]
-allocated_stats = /Total allocated: (?<bytes>.*) bytes \((?<objects>.*) objects\)/.match(allocated_str)
-retained_stats = /Total retained: (?<bytes>.*) bytes \((?<objects>.*) objects\)/.match(retained_str)
-
-abort 'failed to process the benchmark output' unless allocated_stats && retained_stats
-
-puts "memory_static_objects_allocated_mb #{(allocated_stats[:bytes].to_f / (1024 * 1024)).round(1)}"
-puts "memory_static_objects_retained_mb #{(retained_stats[:bytes].to_f / (1024 * 1024)).round(1)}"
-puts "memory_static_objects_allocated_items #{allocated_stats[:objects]}"
-puts "memory_static_objects_retained_items #{retained_stats[:objects]}"
diff --git a/scripts/generate-gems-size-metrics-static b/scripts/generate-gems-size-metrics-static
deleted file mode 100755
index 2406e720916..00000000000
--- a/scripts/generate-gems-size-metrics-static
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-abort "usage: #{__FILE__} <memory_bundle_mem_file_name>" unless ARGV.length == 1
-memory_bundle_mem_file_name = ARGV.first
-
-full_report = File.readlines(memory_bundle_mem_file_name)
-
-def total_size(memory_bundle_mem_report)
- stats = /TOP: (?<total_mibs_str>.*) MiB/.match(memory_bundle_mem_report.first)
- abort 'failed to process the benchmark output' unless stats
- "gem_total_size_mb #{stats[:total_mibs_str].to_f.round(1)}"
-end
-
-TOP_LEVEL_GEM_LOG_FORMAT = /^ (?<gem_name>\S.*):\s*(?<gem_size>\d[.\d]*)\s*MiB/.freeze
-def all_gems(memory_bundle_mem_report)
- memory_bundle_mem_report.map do |line|
- TOP_LEVEL_GEM_LOG_FORMAT.match(line)
- end.compact
-end
-
-def gems_as_metrics(gems_match_data)
- gems_match_data.map do |gem|
- gem_name = gem[:gem_name]
- gem_size_mb = gem[:gem_size].to_f.round(1)
- "gem_size_mb{name=\"#{gem_name}\"} #{gem_size_mb}"
- end
-end
-
-puts total_size(full_report)
-puts gems_as_metrics(all_gems(full_report)).sort(&:casecmp)
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 16bb33e95c8..72fee40a6e9 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -183,6 +183,18 @@ RSpec.describe Projects::CommitController do
expect(assigns(:tags)).to eq([])
expect(assigns(:tags_limit_exceeded)).to be_truthy
end
+
+ context 'when commit is not found' do
+ it 'responds with 404' do
+ get(:branches, params: {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: '11111111111111111111111111111111111111'
+ })
+
+ expect(response).to be_not_found
+ end
+ end
end
describe 'POST revert' do
diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
index 875b0a60634..167521134b1 100644
--- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
+++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
@@ -71,16 +71,10 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
perform_enqueued_jobs do
select_dropdown_option('create-mr')
- expect(page).to have_content('Draft: Resolve "Cherry-Coloured Funk"')
- expect(current_path).to eq(project_merge_request_path(project, MergeRequest.first))
-
- wait_for_requests
+ expect(page).to have_content('New merge request')
+ expect(page).to have_content("From #{issue.to_branch_name} into #{project.default_branch}")
+ expect(page).to have_current_path(project_new_merge_request_path(project, merge_request: { source_branch: issue.to_branch_name, target_branch: project.default_branch }))
end
-
- visit project_issue_path(project, issue)
-
- expect(page).to have_content("created merge request !1 to address this issue")
- expect(page).to have_content('mentioned in merge request !1')
end
it 'creates a branch' do
@@ -100,17 +94,10 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
perform_enqueued_jobs do
select_dropdown_option('create-mr', branch_name)
- expect(page).to have_content('Draft: Resolve "Cherry-Coloured Funk"')
- expect(page).to have_content('Request to merge custom-branch-name into')
- expect(current_path).to eq(project_merge_request_path(project, MergeRequest.first))
-
- wait_for_requests
+ expect(page).to have_content('New merge request')
+ expect(page).to have_content("From #{branch_name} into #{project.default_branch}")
+ expect(page).to have_current_path(project_new_merge_request_path(project, merge_request: { source_branch: branch_name, target_branch: project.default_branch }))
end
-
- visit project_issue_path(project, issue)
-
- expect(page).to have_content("created merge request !1 to address this issue")
- expect(page).to have_content('mentioned in merge request !1')
end
it 'creates a branch' do
diff --git a/spec/finders/template_finder_spec.rb b/spec/finders/template_finder_spec.rb
index 97eecf8a89d..8e2426e697b 100644
--- a/spec/finders/template_finder_spec.rb
+++ b/spec/finders/template_finder_spec.rb
@@ -153,7 +153,12 @@ RSpec.describe TemplateFinder do
let(:params) { {} }
- subject(:result) { described_class.new(type, project, params).template_names.values.flatten.map { |el| OpenStruct.new(el) } }
+ let(:template_name_struct) { Struct.new(:name, :id, :key, :project_id, keyword_init: true) }
+
+ subject(:result) do
+ described_class.new(type, project, params).template_names.values.flatten
+ .map { |el| template_name_struct.new(el) }
+ end
where(:type, :vendored_name) do
:dockerfiles | 'Binary'
diff --git a/spec/frontend/issues/create_merge_request_dropdown_spec.js b/spec/frontend/issues/create_merge_request_dropdown_spec.js
index fdc0bd7d72e..637b4d31999 100644
--- a/spec/frontend/issues/create_merge_request_dropdown_spec.js
+++ b/spec/frontend/issues/create_merge_request_dropdown_spec.js
@@ -59,7 +59,7 @@ describe('CreateMergeRequestDropdown', () => {
describe('updateCreatePaths', () => {
it('escapes branch names correctly', () => {
dropdown.createBranchPath = `${TEST_HOST}/branches?branch_name=some-branch&issue=42`;
- dropdown.createMrPath = `${TEST_HOST}/create_merge_request?branch_name=some-branch&ref=main`;
+ dropdown.createMrPath = `${TEST_HOST}/create_merge_request?merge_request%5Bsource_branch%5D=test&merge_request%5Btarget_branch%5D=master`;
dropdown.updateCreatePaths('branch', 'contains#hash');
@@ -68,7 +68,7 @@ describe('CreateMergeRequestDropdown', () => {
);
expect(dropdown.createMrPath).toBe(
- `${TEST_HOST}/create_merge_request?branch_name=contains%23hash&ref=main`,
+ `${TEST_HOST}/create_merge_request?merge_request%5Bsource_branch%5D=contains%23hash&merge_request%5Btarget_branch%5D=master`,
);
});
});
diff --git a/spec/frontend/jira_connect/subscriptions/components/app_spec.js b/spec/frontend/jira_connect/subscriptions/components/app_spec.js
index 57e1b8fb90c..aa0f1440b20 100644
--- a/spec/frontend/jira_connect/subscriptions/components/app_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/components/app_spec.js
@@ -1,11 +1,10 @@
-import { GlLink, GlEmptyState } from '@gitlab/ui';
+import { GlLink } from '@gitlab/ui';
import { nextTick } from 'vue';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import JiraConnectApp from '~/jira_connect/subscriptions/components/app.vue';
-import AddNamespaceButton from '~/jira_connect/subscriptions/components/add_namespace_button.vue';
-import SignInButton from '~/jira_connect/subscriptions/components/sign_in_button.vue';
-import SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
+import SignInPage from '~/jira_connect/subscriptions/pages/sign_in.vue';
+import SubscriptionsPage from '~/jira_connect/subscriptions/pages/subscriptions.vue';
import UserLink from '~/jira_connect/subscriptions/components/user_link.vue';
import createStore from '~/jira_connect/subscriptions/store';
import { SET_ALERT } from '~/jira_connect/subscriptions/store/mutation_types';
@@ -23,10 +22,8 @@ describe('JiraConnectApp', () => {
const findAlert = () => wrapper.findByTestId('jira-connect-persisted-alert');
const findAlertLink = () => findAlert().findComponent(GlLink);
- const findSignInButton = () => wrapper.findComponent(SignInButton);
- const findAddNamespaceButton = () => wrapper.findComponent(AddNamespaceButton);
- const findSubscriptionsList = () => wrapper.findComponent(SubscriptionsList);
- const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findSignInPage = () => wrapper.findComponent(SignInPage);
+ const findSubscriptionsPage = () => wrapper.findComponent(SubscriptionsPage);
const createComponent = ({ provide, mountFn = shallowMountExtended } = {}) => {
store = createStore();
@@ -43,49 +40,35 @@ describe('JiraConnectApp', () => {
describe('template', () => {
describe.each`
- scenario | usersPath | subscriptions | expectSignInButton | expectEmptyState | expectNamespaceButton | expectSubscriptionsList
- ${'user is not signed in with subscriptions'} | ${'/users'} | ${[mockSubscription]} | ${true} | ${false} | ${false} | ${true}
- ${'user is not signed in without subscriptions'} | ${'/users'} | ${undefined} | ${true} | ${false} | ${false} | ${false}
- ${'user is signed in with subscriptions'} | ${undefined} | ${[mockSubscription]} | ${false} | ${false} | ${true} | ${true}
- ${'user is signed in without subscriptions'} | ${undefined} | ${undefined} | ${false} | ${true} | ${false} | ${false}
- `(
- 'when $scenario',
- ({
- usersPath,
- expectSignInButton,
- subscriptions,
- expectEmptyState,
- expectNamespaceButton,
- expectSubscriptionsList,
- }) => {
- beforeEach(() => {
- createComponent({
- provide: {
- usersPath,
- subscriptions,
- },
- });
- });
-
- it(`${expectSignInButton ? 'renders' : 'does not render'} sign in button`, () => {
- expect(findSignInButton().exists()).toBe(expectSignInButton);
- });
-
- it(`${expectEmptyState ? 'renders' : 'does not render'} empty state`, () => {
- expect(findEmptyState().exists()).toBe(expectEmptyState);
+ scenario | usersPath | shouldRenderSignInPage | shouldRenderSubscriptionsPage
+ ${'user is not signed in'} | ${'/users'} | ${true} | ${false}
+ ${'user is signed in'} | ${undefined} | ${false} | ${true}
+ `('when $scenario', ({ usersPath, shouldRenderSignInPage, shouldRenderSubscriptionsPage }) => {
+ beforeEach(() => {
+ createComponent({
+ provide: {
+ usersPath,
+ subscriptions: [mockSubscription],
+ },
});
+ });
- it(`${
- expectNamespaceButton ? 'renders' : 'does not render'
- } button to add namespace`, () => {
- expect(findAddNamespaceButton().exists()).toBe(expectNamespaceButton);
- });
+ it(`${shouldRenderSignInPage ? 'renders' : 'does not render'} sign in page`, () => {
+ expect(findSignInPage().exists()).toBe(shouldRenderSignInPage);
+ if (shouldRenderSignInPage) {
+ expect(findSignInPage().props('hasSubscriptions')).toBe(true);
+ }
+ });
- it(`${expectSubscriptionsList ? 'renders' : 'does not render'} subscriptions list`, () => {
- expect(findSubscriptionsList().exists()).toBe(expectSubscriptionsList);
- });
- },
- );
+ it(`${
+ shouldRenderSubscriptionsPage ? 'renders' : 'does not render'
+ } subscriptions page`, () => {
+ expect(findSubscriptionsPage().exists()).toBe(shouldRenderSubscriptionsPage);
+ if (shouldRenderSubscriptionsPage) {
+ expect(findSubscriptionsPage().props('hasSubscriptions')).toBe(true);
+ }
+ });
+ });
it('renders UserLink component', () => {
createComponent({
diff --git a/spec/frontend/jira_connect/subscriptions/components/sign_in_button_spec.js b/spec/frontend/jira_connect/subscriptions/components/sign_in_button_spec.js
index cb5ae877c47..94dcf9decec 100644
--- a/spec/frontend/jira_connect/subscriptions/components/sign_in_button_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/components/sign_in_button_spec.js
@@ -11,11 +11,12 @@ jest.mock('~/jira_connect/subscriptions/utils');
describe('SignInButton', () => {
let wrapper;
- const createComponent = () => {
+ const createComponent = ({ slots } = {}) => {
wrapper = shallowMount(SignInButton, {
propsData: {
usersPath: MOCK_USERS_PATH,
},
+ slots,
});
};
@@ -29,6 +30,7 @@ describe('SignInButton', () => {
createComponent();
expect(findButton().exists()).toBe(true);
+ expect(findButton().text()).toBe(SignInButton.i18n.defaultButtonText);
});
describe.each`
@@ -45,4 +47,12 @@ describe('SignInButton', () => {
expect(findButton().attributes('href')).toBe(expectedHref);
});
});
+
+ describe('with slot', () => {
+ const mockSlotContent = 'custom button content!';
+ it('renders slot content in button', () => {
+ createComponent({ slots: { default: mockSlotContent } });
+ expect(wrapper.text()).toMatchInterpolatedText(mockSlotContent);
+ });
+ });
});
diff --git a/spec/frontend/jira_connect/subscriptions/pages/sign_in_spec.js b/spec/frontend/jira_connect/subscriptions/pages/sign_in_spec.js
new file mode 100644
index 00000000000..4e3297506f1
--- /dev/null
+++ b/spec/frontend/jira_connect/subscriptions/pages/sign_in_spec.js
@@ -0,0 +1,62 @@
+import { mount } from '@vue/test-utils';
+
+import SignInPage from '~/jira_connect/subscriptions/pages/sign_in.vue';
+import SignInButton from '~/jira_connect/subscriptions/components/sign_in_button.vue';
+import SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
+import createStore from '~/jira_connect/subscriptions/store';
+
+jest.mock('~/jira_connect/subscriptions/utils');
+
+describe('SignInPage', () => {
+ let wrapper;
+ let store;
+
+ const findSignInButton = () => wrapper.findComponent(SignInButton);
+ const findSubscriptionsList = () => wrapper.findComponent(SubscriptionsList);
+
+ const createComponent = ({ provide, props } = {}) => {
+ store = createStore();
+
+ wrapper = mount(SignInPage, {
+ store,
+ provide,
+ propsData: props,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ const mockUsersPath = '/test';
+ describe.each`
+ scenario | expectSubscriptionsList | signInButtonText
+ ${'with subscriptions'} | ${true} | ${SignInPage.i18n.signinButtonTextWithSubscriptions}
+ ${'without subscriptions'} | ${false} | ${SignInButton.i18n.defaultButtonText}
+ `('$scenario', ({ expectSubscriptionsList, signInButtonText }) => {
+ beforeEach(() => {
+ createComponent({
+ provide: {
+ usersPath: mockUsersPath,
+ },
+ props: {
+ hasSubscriptions: expectSubscriptionsList,
+ },
+ });
+ });
+
+ it(`renders sign in button with text ${signInButtonText}`, () => {
+ expect(findSignInButton().text()).toMatchInterpolatedText(signInButtonText);
+ });
+
+ it('renders sign in button with `usersPath` prop', () => {
+ expect(findSignInButton().props('usersPath')).toBe(mockUsersPath);
+ });
+
+ it(`${expectSubscriptionsList ? 'renders' : 'does not render'} subscriptions list`, () => {
+ expect(findSubscriptionsList().exists()).toBe(expectSubscriptionsList);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/jira_connect/subscriptions/pages/subscriptions_spec.js b/spec/frontend/jira_connect/subscriptions/pages/subscriptions_spec.js
new file mode 100644
index 00000000000..198278efc1f
--- /dev/null
+++ b/spec/frontend/jira_connect/subscriptions/pages/subscriptions_spec.js
@@ -0,0 +1,56 @@
+import { GlEmptyState } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import SubscriptionsPage from '~/jira_connect/subscriptions/pages/subscriptions.vue';
+import AddNamespaceButton from '~/jira_connect/subscriptions/components/add_namespace_button.vue';
+import SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
+import createStore from '~/jira_connect/subscriptions/store';
+
+describe('SubscriptionsPage', () => {
+ let wrapper;
+ let store;
+
+ const findAddNamespaceButton = () => wrapper.findComponent(AddNamespaceButton);
+ const findSubscriptionsList = () => wrapper.findComponent(SubscriptionsList);
+ const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+
+ const createComponent = ({ props } = {}) => {
+ store = createStore();
+
+ wrapper = shallowMount(SubscriptionsPage, {
+ store,
+ propsData: props,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('template', () => {
+ describe.each`
+ scenario | expectSubscriptionsList | expectEmptyState
+ ${'with subscriptions'} | ${true} | ${false}
+ ${'without subscriptions'} | ${false} | ${true}
+ `('$scenario', ({ expectEmptyState, expectSubscriptionsList }) => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ hasSubscriptions: expectSubscriptionsList,
+ },
+ });
+ });
+
+ it('renders button to add namespace', () => {
+ expect(findAddNamespaceButton().exists()).toBe(true);
+ });
+
+ it(`${expectEmptyState ? 'renders' : 'does not render'} empty state`, () => {
+ expect(findEmptyState().exists()).toBe(expectEmptyState);
+ });
+
+ it(`${expectSubscriptionsList ? 'renders' : 'does not render'} subscriptions list`, () => {
+ expect(findSubscriptionsList().exists()).toBe(expectSubscriptionsList);
+ });
+ });
+ });
+});
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb
index bd4832bd978..6eba9ca63b0 100644
--- a/spec/models/blob_spec.rb
+++ b/spec/models/blob_spec.rb
@@ -215,6 +215,20 @@ RSpec.describe Blob do
end
end
+ describe '#symlink?' do
+ it 'is true for symlinks' do
+ symlink_blob = fake_blob(path: 'file', mode: '120000')
+
+ expect(symlink_blob.symlink?).to eq true
+ end
+
+ it 'is false for non-symlinks' do
+ non_symlink_blob = fake_blob(path: 'file', mode: '100755')
+
+ expect(non_symlink_blob.symlink?).to eq false
+ end
+ end
+
describe '#extension' do
it 'returns the extension' do
blob = fake_blob(path: 'file.md')
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 14ffb714c6e..275c9ef8f56 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -265,22 +265,37 @@ RSpec.describe Ci::Runner do
it_behaves_like '.belonging_to_parent_group_of_project'
end
- describe '.owned_or_instance_wide' do
- it 'returns a globally shared, a project specific and a group specific runner' do
- # group specific
- group = create(:group)
- project = create(:project, group: group)
- group_runner = create(:ci_runner, :group, groups: [group])
+ context 'with existing system wide, group and project runners' do
+ # group specific
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) }
- # project specific
- project_runner = create(:ci_runner, :project, projects: [project])
+ # project specific
+ let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project]) }
- # globally shared
- shared_runner = create(:ci_runner, :instance)
+ # globally shared
+ let_it_be(:shared_runner) { create(:ci_runner, :instance) }
- expect(described_class.owned_or_instance_wide(project.id)).to contain_exactly(
- group_runner, project_runner, shared_runner
- )
+ describe '.owned_or_instance_wide' do
+ subject { described_class.owned_or_instance_wide(project.id) }
+
+ it 'returns a globally shared, a project specific and a group specific runner' do
+ is_expected.to contain_exactly(group_runner, project_runner, shared_runner)
+ end
+ end
+
+ describe '.group_or_instance_wide' do
+ subject { described_class.group_or_instance_wide(group) }
+
+ before do
+ # Ensure the project runner is instantiated
+ project_runner
+ end
+
+ it 'returns a globally shared and a group specific runner' do
+ is_expected.to contain_exactly(group_runner, shared_runner)
+ end
end
end
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index 7ba67f7f29b..4c2fb649e4d 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -1026,7 +1026,8 @@ RSpec.describe API::Ci::Runners do
get api("/groups/#{group.id}/runners", user)
expect(json_response).to match_array([
- a_hash_including('description' => 'Group runner A', 'active' => true, 'paused' => false)
+ a_hash_including('description' => 'Group runner A', 'active' => true, 'paused' => false),
+ a_hash_including('description' => 'Shared runner', 'active' => true, 'paused' => false)
])
end
@@ -1039,6 +1040,14 @@ RSpec.describe API::Ci::Runners do
])
end
+ it 'returns instance runners when instance_type is specified' do
+ get api("/groups/#{group.id}/runners?type=instance_type", user)
+
+ expect(json_response).to match_array([
+ a_hash_including('description' => 'Shared runner')
+ ])
+ end
+
it 'returns empty result when type does not match' do
get api("/groups/#{group.id}/runners?type=project_type", user)
diff --git a/spec/services/branches/create_service_spec.rb b/spec/services/branches/create_service_spec.rb
index 1962aca35e1..0d2f5838574 100644
--- a/spec/services/branches/create_service_spec.rb
+++ b/spec/services/branches/create_service_spec.rb
@@ -65,7 +65,7 @@ RSpec.describe Branches::CreateService do
allow(project.repository).to receive(:add_branch).and_raise(pre_receive_error)
- expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
pre_receive_error,
pre_receive_message: raw_message,
branch_name: 'new-feature',
diff --git a/spec/support/helpers/fake_blob_helpers.rb b/spec/support/helpers/fake_blob_helpers.rb
index 6c8866deac4..47fb9a345a3 100644
--- a/spec/support/helpers/fake_blob_helpers.rb
+++ b/spec/support/helpers/fake_blob_helpers.rb
@@ -4,13 +4,14 @@ module FakeBlobHelpers
class FakeBlob
include BlobLike
- attr_reader :path, :size, :data, :lfs_oid, :lfs_size
+ attr_reader :path, :size, :data, :lfs_oid, :lfs_size, :mode
- def initialize(path: 'file.txt', size: 1.kilobyte, data: 'foo', binary: false, lfs: nil)
+ def initialize(path: 'file.txt', size: 1.kilobyte, data: 'foo', binary: false, lfs: nil, mode: nil)
@path = path
@size = size
@data = data
@binary = binary
+ @mode = mode
@lfs_pointer = lfs.present?
if @lfs_pointer
diff --git a/workhorse/Makefile b/workhorse/Makefile
index 031fe581d28..44b3e2b8248 100644
--- a/workhorse/Makefile
+++ b/workhorse/Makefile
@@ -12,7 +12,7 @@ ifdef SOURCE_DATE_EPOCH
else
BUILD_TIME := $(shell date -u "$(DATE_FMT)")
endif
-GOBUILD := go build -ldflags "-X main.Version=$(VERSION_STRING) -X main.BuildTime=$(BUILD_TIME)"
+GO_BUILD_GENERIC_LDFLAGS := -X main.Version=$(VERSION_STRING) -X main.BuildTime=$(BUILD_TIME)
GITALY := tmp/tests/gitaly/_build/bin/gitaly
GITALY_PID_FILE := gitaly.pid
EXE_ALL := gitlab-resize-image gitlab-zip-cat gitlab-zip-metadata gitlab-workhorse
@@ -30,31 +30,35 @@ define message
@echo "### $(1)"
endef
+# To compute a unique and deterministic value for GNU build-id, we build the Go binary a second time.
+# From the first build, we extract its unique and deterministic Go build-id, and use that to derive
+# a comparably unique and deterministic GNU build-id to inject into the final binary.
+# If we cannot extract a Go build-id, we punt and fallback to using a random 32-byte hex string.
+# This fallback is unique but non-deterministic. Uniqueness is critical, because the GNU build-id
+# can be used as a cache key in a build cache. Without the fallback, we risk cache key collisions.
+## Skip generation of the GNU build ID if set to speed up builds.
+WITHOUT_BUILD_ID ?=
.NOTPARALLEL:
.PHONY: all
all: clean-build $(EXE_ALL)
-.PHONY: gitlab-resize-image
-gitlab-resize-image:
+.PHONY: gitlab-resize-image gitlab-zip-cat gitlab-zip-metadata
+gitlab-resize-image gitlab-zip-cat gitlab-zip-metadata:
$(call message,Building $@)
- $(GOBUILD) -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)/cmd/$@
-
-.PHONY: gitlab-zip-cat
-gitlab-zip-cat:
- $(call message,Building $@)
- $(GOBUILD) -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)/cmd/$@
-
-.PHONY: gitlab-zip-metadata
-gitlab-zip-metadata:
- $(call message,Building $@)
- $(GOBUILD) -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)/cmd/$@
+ go build -ldflags "$(GO_BUILD_GENERIC_LDFLAGS)" -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)/cmd/$@
+ifndef WITHOUT_BUILD_ID
+ go build -ldflags "$(GO_BUILD_GENERIC_LDFLAGS) -B 0x$$(_support/make-gnu-build-id.sh $(BUILD_DIR)/$@)" -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)/cmd/$@
+endif
.PHONY: gitlab-workhorse
gitlab-workhorse:
$(call message,Building $@)
- $(GOBUILD) -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)
+ go build -ldflags "$(GO_BUILD_GENERIC_LDFLAGS)" -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)
+ifndef WITHOUT_BUILD_ID
+ go build -ldflags "$(GO_BUILD_GENERIC_LDFLAGS) -B 0x$$(_support/make-gnu-build-id.sh $(BUILD_DIR)/$@)" -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)
+endif
.PHONY: install
install: $(EXE_ALL)
diff --git a/workhorse/_support/make-gnu-build-id.sh b/workhorse/_support/make-gnu-build-id.sh
new file mode 100755
index 00000000000..815966dab7e
--- /dev/null
+++ b/workhorse/_support/make-gnu-build-id.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+main()
+{
+ GO_BINARY=$1
+
+ if [ $# -ne 1 ] || [ ! -f $GO_BINARY ] ; then
+ fail "Usage: $0 [path_to_go_binary]"
+ fi
+
+ GO_BUILD_ID=$( go tool buildid "$GO_BINARY" || openssl rand -hex 32 )
+ if [ -z "$GO_BUILD_ID" ] ; then
+ fail "ERROR: Could not extract Go build-id or generate a random hex string."
+ fi
+
+ GNU_BUILD_ID=$( echo $GO_BUILD_ID | sha1sum | cut -d' ' -f1 )
+ if [ -z "$GNU_BUILD_ID" ] ; then
+ fail "ERROR: Could not generate a GNU build-id"
+ fi
+
+ echo "$GNU_BUILD_ID"
+}
+
+fail()
+{
+ echo "$@" 1>&2
+ exit 1
+}
+
+main "$@"
diff --git a/yarn.lock b/yarn.lock
index 5bf6651b59d..ac1494be01c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -962,15 +962,15 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.2.0.tgz#95cf58d6ae634d535145159f08f5cff6241d4013"
integrity sha512-mCwR3KfNPsxRoojtTjMIZwdd4FFlBh5DlR9AeodP+7+k8rILdWGYxTZbJMPNXoPbZx16R94nG8c5bR7toD4QBw==
-"@gitlab/ui@33.1.0":
- version "33.1.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-33.1.0.tgz#45ac2e6362546530b5756b1973f97f74a9c920da"
- integrity sha512-kSpnGx7UjWxl0s6RBl9njhthD2tSyLHoVd3Q/E3WEE9gdw8VSa4tEeI2srD9eGl2npd/VUvO7tNIXzk5NpjLZg==
+"@gitlab/ui@34.0.0":
+ version "34.0.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-34.0.0.tgz#0fe9574df2c38aeb63add94e4549ed4e65975ef8"
+ integrity sha512-BFh3x+GCqWAoWhNJhJUunW3eHQLQkBOTBwZFJWSS+1+9ZtetqU3t0/OoqYjJuyTsqdra7A/e6BZsU0j7CnbY+Q==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "2.20.1"
copy-to-clipboard "^3.0.8"
- dompurify "^2.3.4"
+ dompurify "^2.3.5"
echarts "^5.2.1"
highlight.js "^10.6.0"
iframe-resizer "^4.3.2"
@@ -4890,7 +4890,7 @@ dompurify@2.3.4:
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.4.tgz#1cf5cf0105ccb4debdf6db162525bd41e6ddacc6"
integrity sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ==
-dompurify@^2.3.4, dompurify@^2.3.5:
+dompurify@^2.3.5:
version "2.3.5"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.5.tgz#c83ed5a3ae5ce23e52efe654ea052ffb358dd7e3"
integrity sha512-kD+f8qEaa42+mjdOpKeztu9Mfx5bv9gVLO6K9jRx4uGvh6Wv06Srn4jr1wPNY2OOUGGSKHNFN+A8MA3v0E0QAQ==