summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md9
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_mermaid.js8
-rw-r--r--app/assets/javascripts/boards/mixins/sortable_default_options.js2
-rw-r--r--app/assets/javascripts/clusters/services/application_state_machine.js6
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue7
-rw-r--r--app/assets/javascripts/ide/stores/actions.js12
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js4
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js9
-rw-r--r--app/assets/javascripts/ide/stores/utils.js4
-rw-r--r--app/assets/javascripts/ide/utils.js2
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue112
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle.js12
-rw-r--r--app/assets/javascripts/monitoring/stores/actions.js21
-rw-r--r--app/assets/javascripts/monitoring/stores/mutation_types.js2
-rw-r--r--app/assets/javascripts/monitoring/stores/mutations.js7
-rw-r--r--app/assets/javascripts/monitoring/stores/state.js3
-rw-r--r--app/assets/javascripts/pages/users/user_tabs.js4
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_mediator.js2
-rw-r--r--app/assets/javascripts/repository/components/breadcrumbs.vue2
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue2
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue49
-rw-r--r--app/assets/javascripts/repository/graphql.js17
-rw-r--r--app/assets/javascripts/repository/index.js1
-rw-r--r--app/assets/javascripts/repository/log_tree.js64
-rw-r--r--app/assets/javascripts/repository/queries/getCommit.query.graphql10
-rw-r--r--app/assets/javascripts/repository/queries/getCommits.query.graphql10
-rw-r--r--app/assets/javascripts/repository/queries/getFiles.query.graphql1
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue3
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js2
-rw-r--r--app/assets/javascripts/visual_review_toolbar/components/comment.js50
-rw-r--r--app/assets/javascripts/visual_review_toolbar/components/constants.js8
-rw-r--r--app/assets/javascripts/visual_review_toolbar/components/index.js16
-rw-r--r--app/assets/javascripts/visual_review_toolbar/components/login.js3
-rw-r--r--app/assets/javascripts/visual_review_toolbar/components/note.js14
-rw-r--r--app/assets/javascripts/visual_review_toolbar/components/utils.js6
-rw-r--r--app/assets/javascripts/visual_review_toolbar/components/wrapper.js36
-rw-r--r--app/assets/javascripts/visual_review_toolbar/index.js9
-rw-r--r--app/assets/javascripts/visual_review_toolbar/store/state.js3
-rw-r--r--app/assets/javascripts/visual_review_toolbar/styles/toolbar.css48
-rw-r--r--app/assets/stylesheets/pages/issuable.scss6
-rw-r--r--app/assets/stylesheets/pages/notes.scss2
-rw-r--r--app/controllers/concerns/continue_params.rb2
-rw-r--r--app/controllers/concerns/internal_redirect.rb4
-rw-r--r--app/controllers/concerns/requires_whitelisted_monitoring_client.rb4
-rw-r--r--app/controllers/projects/forks_controller.rb18
-rw-r--r--app/controllers/projects/imports_controller.rb8
-rw-r--r--app/controllers/projects/jobs_controller.rb2
-rw-r--r--app/controllers/projects/refs_controller.rb1
-rw-r--r--app/helpers/application_settings_helper.rb4
-rw-r--r--app/models/application_setting.rb13
-rw-r--r--app/models/application_setting_implementation.rb21
-rw-r--r--app/models/clusters/platforms/kubernetes.rb49
-rw-r--r--app/models/environment.rb19
-rw-r--r--app/models/namespace.rb2
-rw-r--r--app/models/namespace/aggregation_schedule.rb7
-rw-r--r--app/models/namespace/root_storage_statistics.rb10
-rw-r--r--app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb10
-rw-r--r--app/services/projects/propagate_service_template.rb2
-rw-r--r--app/views/admin/application_settings/_logging.html.haml38
-rw-r--r--app/views/admin/application_settings/reporting.html.haml11
-rw-r--r--app/views/layouts/_head.html.haml2
-rw-r--r--app/views/profiles/personal_access_tokens/index.html.haml40
-rw-r--r--app/views/shared/_personal_access_tokens_created_container.html.haml7
-rw-r--r--app/views/shared/_personal_access_tokens_form.html.haml14
-rw-r--r--app/views/shared/_personal_access_tokens_table.html.haml23
-rw-r--r--app/views/shared/tokens/_scopes_list.html.haml2
-rw-r--r--changelogs/unreleased/51952-forking-via-webide.yml5
-rw-r--r--changelogs/unreleased/58802-rename-webide.yml5
-rw-r--r--changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml5
-rw-r--r--changelogs/unreleased/62938-wcag-aa-edited-text-color.yml5
-rw-r--r--changelogs/unreleased/63247-add-conf-toast-and-link.yml5
-rw-r--r--changelogs/unreleased/always-allow-prometheus-access-in-dev.yml5
-rw-r--r--changelogs/unreleased/always-display-environment-selector.yml5
-rw-r--r--changelogs/unreleased/dz-remove-deprecated-user-routes.yml5
-rw-r--r--changelogs/unreleased/refactor-sentry.yml5
-rw-r--r--changelogs/unreleased/sh-cache-negative-entries-find-commit.yml5
-rw-r--r--changelogs/unreleased/sh-service-template-bug.yml5
-rw-r--r--changelogs/unreleased/sh-update-mermaid.yml5
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/sentry.rb13
-rw-r--r--config/routes/snippets.rb2
-rw-r--r--config/routes/user.rb9
-rw-r--r--db/migrate/20190531153110_create_namespace_root_storage_statistics.rb22
-rw-r--r--db/migrate/20190605184422_create_namespace_aggregation_schedules.rb14
-rw-r--r--db/post_migrate/20190625184066_remove_sentry_from_application_settings.rb38
-rw-r--r--db/schema.rb23
-rw-r--r--doc/README.md2
-rw-r--r--doc/administration/high_availability/gitaly.md42
-rw-r--r--doc/administration/high_availability/gitlab.md13
-rw-r--r--doc/administration/high_availability/monitoring_node.md2
-rw-r--r--doc/administration/high_availability/pgbouncer.md27
-rw-r--r--doc/administration/high_availability/redis.md72
-rw-r--r--doc/administration/operations/extra_sidekiq_processes.md199
-rw-r--r--doc/api/merge_request_approvals.md8
-rw-r--r--doc/api/settings.md4
-rw-r--r--doc/ci/README.md2
-rw-r--r--doc/development/documentation/styleguide.md21
-rw-r--r--doc/development/testing_guide/frontend_testing.md13
-rw-r--r--doc/install/requirements.md12
-rw-r--r--doc/topics/autodevops/index.md2
-rw-r--r--doc/university/process/README.md6
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md10
-rw-r--r--doc/user/group/clusters/index.md8
-rw-r--r--doc/user/img/color_inline_colorchip_render_gfm.pngbin4684 -> 0 bytes
-rw-r--r--doc/user/img/markdown_inline_diffs_tags_rendered.pngbin1804 -> 0 bytes
-rw-r--r--doc/user/img/math_inline_sup_render_gfm.pngbin1119 -> 0 bytes
-rw-r--r--doc/user/img/task_list_ordered_render_gfm.pngbin2855 -> 0 bytes
-rw-r--r--doc/user/markdown.md1537
-rw-r--r--doc/user/project/pages/getting_started_part_three.md39
-rw-r--r--doc/user/project/settings/index.md2
-rw-r--r--lib/api/settings.rb8
-rw-r--r--lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml2
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb40
-rw-r--r--lib/gitlab/gon_helper.rb4
-rw-r--r--lib/gitlab/path_regex.rb1
-rw-r--r--lib/gitlab/sentry.rb2
-rw-r--r--locale/gitlab.pot108
-rw-r--r--package.json2
-rw-r--r--spec/controllers/concerns/continue_params_spec.rb8
-rw-r--r--spec/controllers/concerns/internal_redirect_spec.rb77
-rw-r--r--spec/controllers/projects/forks_controller_spec.rb16
-rw-r--r--spec/factories/namespace/aggregation_schedules.rb7
-rw-r--r--spec/factories/namespace/root_storage_statistics.rb7
-rw-r--r--spec/factories/namespaces.rb8
-rw-r--r--spec/features/groups/issues_spec.rb31
-rw-r--r--spec/features/projects/environments/environment_metrics_spec.rb39
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb40
-rw-r--r--spec/features/raven_js_spec.rb2
-rw-r--r--spec/features/users/signup_spec.rb2
-rw-r--r--spec/frontend/boards/modal_store_spec.js2
-rw-r--r--spec/frontend/clusters/services/application_state_machine_spec.js16
-rw-r--r--spec/frontend/ide/utils_spec.js44
-rw-r--r--spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap14
-rw-r--r--spec/frontend/repository/components/table/row_spec.js2
-rw-r--r--spec/frontend/repository/log_tree_spec.js129
-rw-r--r--spec/frontend/test_setup.js6
-rw-r--r--spec/javascripts/ide/components/ide_tree_list_spec.js14
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js37
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js10
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js11
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js96
-rw-r--r--spec/javascripts/monitoring/mock_data.js13
-rw-r--r--spec/javascripts/monitoring/store/actions_spec.js17
-rw-r--r--spec/javascripts/monitoring/store/mutations_spec.js15
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb13
-rw-r--r--spec/models/application_setting_spec.rb30
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb43
-rw-r--r--spec/models/environment_spec.rb63
-rw-r--r--spec/models/internal_id_spec.rb2
-rw-r--r--spec/models/namespace/aggregation_schedule_spec.rb7
-rw-r--r--spec/models/namespace/root_storage_statistics_spec.rb10
-rw-r--r--spec/models/namespace_spec.rb2
-rw-r--r--spec/requests/api/helpers_spec.rb6
-rw-r--r--spec/routing/routing_spec.rb30
-rw-r--r--spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb12
-rw-r--r--spec/services/projects/propagate_service_template_spec.rb2
-rw-r--r--spec/spec_helper.rb6
-rw-r--r--spec/support/helpers/jira_service_helper.rb4
-rw-r--r--spec/support/helpers/prometheus_helpers.rb4
-rw-r--r--spec/support/helpers/stub_configuration.rb6
-rw-r--r--spec/support/shared_examples/application_setting_examples.rb39
-rw-r--r--spec/support/sidekiq.rb4
-rw-r--r--spec/workers/reactive_caching_worker_spec.rb7
-rw-r--r--yarn.lock124
164 files changed, 2867 insertions, 1482 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d30fc6553f..8d4509e370d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -333,6 +333,15 @@ entry.
- Moves snowplow to CE repo.
+## 11.11.4 (2019-06-26)
+
+### Fixed (3 changes)
+
+- Fix Fogbugz Importer not working. !29383
+- Fix scrolling to top on assignee change. !29500
+- Fix IDE commit using latest ref in branch and overriding contents. !29769
+
+
## 11.11.3 (2019-06-10)
### Fixed (5 changes)
diff --git a/app/assets/javascripts/behaviors/markdown/render_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
index d0b7f3ff7a2..b23de36f860 100644
--- a/app/assets/javascripts/behaviors/markdown/render_mermaid.js
+++ b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
@@ -59,6 +59,14 @@ export default function renderMermaid($els) {
mermaid.init(undefined, el, id => {
const svg = document.getElementById(id);
+ // As of https://github.com/knsv/mermaid/commit/57b780a0d,
+ // Mermaid will make two init callbacks:one to initialize the
+ // flow charts, and another to initialize the Gannt charts.
+ // Guard against an error caused by double initialization.
+ if (svg.classList.contains('mermaid')) {
+ return;
+ }
+
svg.classList.add('mermaid');
// pre > code > svg
diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js b/app/assets/javascripts/boards/mixins/sortable_default_options.js
index 983b28d2e67..636ca99952c 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js
@@ -1,7 +1,7 @@
/* global DocumentTouch */
import $ from 'jquery';
-import sortableConfig from '../../sortable/sortable_config';
+import sortableConfig from 'ee_else_ce/sortable/sortable_config';
export function sortableStart() {
$('.has-tooltip')
diff --git a/app/assets/javascripts/clusters/services/application_state_machine.js b/app/assets/javascripts/clusters/services/application_state_machine.js
index 17ea4d77795..6e632519d8a 100644
--- a/app/assets/javascripts/clusters/services/application_state_machine.js
+++ b/app/assets/javascripts/clusters/services/application_state_machine.js
@@ -80,6 +80,9 @@ const applicationStateMachine = {
installFailed: false,
},
},
+ [NOT_INSTALLABLE]: {
+ target: NOT_INSTALLABLE,
+ },
// This is possible in artificial environments for E2E testing
[INSTALLED]: {
target: INSTALLED,
@@ -108,6 +111,9 @@ const applicationStateMachine = {
updateSuccessful: false,
},
},
+ [NOT_INSTALLABLE]: {
+ target: NOT_INSTALLABLE,
+ },
[UNINSTALL_EVENT]: {
target: UNINSTALLING,
effects: {
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index 95782b2c88a..1af86a94482 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -30,6 +30,9 @@ export default {
showLoading() {
return !this.currentTree || this.currentTree.loading;
},
+ actualTreeList() {
+ return this.currentTree.tree.filter(entry => !entry.moved);
+ },
},
mounted() {
this.updateViewer(this.viewerType);
@@ -54,9 +57,9 @@ export default {
<slot name="header"></slot>
</header>
<div class="ide-tree-body h-100">
- <template v-if="currentTree.tree.length">
+ <template v-if="actualTreeList.length">
<file-row
- v-for="file in currentTree.tree"
+ v-for="file in actualTreeList"
:key="file.key"
:file="file"
:level="0"
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 48aabaf9dcf..507dc363529 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -8,6 +8,7 @@ import * as types from './mutation_types';
import { decorateFiles } from '../lib/files';
import { stageKeys } from '../constants';
import service from '../services';
+import router from '../ide_router';
export const redirectToUrl = (self, url) => visitUrl(url);
@@ -234,10 +235,15 @@ export const renameEntry = (
parentPath: newParentPath,
});
});
- }
+ } else {
+ const newPath = parentPath ? `${parentPath}/${name}` : name;
+ const newEntry = state.entries[newPath];
+ commit(types.TOGGLE_FILE_CHANGED, { file: newEntry, changed: true });
- if (!entryPath && !entry.tempFile) {
- dispatch('deleteEntry', path);
+ if (entry.opened) {
+ router.push(`/project${newEntry.url}`);
+ commit(types.TOGGLE_FILE_OPEN, entry.path);
+ }
}
dispatch('triggerFilesChange');
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index dc40a1fa6a2..7627b6e03af 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -73,7 +73,9 @@ export const getFileData = (
.getFileData(joinPaths(gon.relative_url_root || '', url.replace('/-/', '/')))
.then(({ data, headers }) => {
const normalizedHeaders = normalizeHeaders(headers);
- setPageTitle(decodeURI(normalizedHeaders['PAGE-TITLE']));
+ let title = normalizedHeaders['PAGE-TITLE'];
+ title = file.prevPath ? title.replace(file.prevPath, file.path) : title;
+ setPageTitle(decodeURI(title));
if (data) commit(types.SET_FILE_DATA, { data, file });
if (openFile) commit(types.TOGGLE_FILE_OPEN, path);
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index ae42b87c9a7..ec4c2fdcde2 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -216,15 +216,16 @@ export default {
Vue.set(state.entries, newPath, {
...oldEntry,
id: newPath,
- key: `${newPath}-${oldEntry.type}-${oldEntry.id}`,
+ key: `${newPath}-${oldEntry.type}-${oldEntry.path}`,
path: newPath,
name: entryPath ? oldEntry.name : name,
tempFile: true,
prevPath: oldEntry.tempFile ? null : oldEntry.path,
url: oldEntry.url.replace(new RegExp(`${oldEntry.path}/?$`), newPath),
tree: [],
- parentPath,
raw: '',
+ opened: false,
+ parentPath,
});
oldEntry.moved = true;
@@ -241,10 +242,6 @@ export default {
state.changedFiles = state.changedFiles.concat(newEntry);
}
- if (state.entries[newPath].opened) {
- state.openFiles.push(state.entries[newPath]);
- }
-
if (oldEntry.tempFile) {
const filterMethod = f => f.path !== oldEntry.path;
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index 4e7a8765abe..fb132c1afc1 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -147,9 +147,9 @@ export const createCommitPayload = ({
commit_message: state.commitMessage || getters.preBuiltCommitMessage,
actions: getCommitFiles(rootState.stagedFiles).map(f => ({
action: commitActionForFile(f),
- file_path: f.path,
+ file_path: f.moved ? f.movedPath : f.path,
previous_path: f.prevPath === '' ? undefined : f.prevPath,
- content: f.content || undefined,
+ content: f.prevPath ? null : f.content || undefined,
encoding: f.base64 ? 'base64' : 'text',
last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
})),
diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js
index d895eca7af0..ae579fef25f 100644
--- a/app/assets/javascripts/ide/utils.js
+++ b/app/assets/javascripts/ide/utils.js
@@ -3,7 +3,7 @@ import { commitItemIconMap } from './constants';
export const getCommitIconMap = file => {
if (file.deleted) {
return commitItemIconMap.deleted;
- } else if (file.tempFile) {
+ } else if (file.tempFile && !file.prevPath) {
return commitItemIconMap.addition;
}
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 0a652329dfe..23687c54fd3 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -106,17 +106,24 @@ export default {
},
customMetricsPath: {
type: String,
- required: true,
+ required: false,
+ default: invalidUrl,
},
validateQueryPath: {
type: String,
- required: true,
+ required: false,
+ default: invalidUrl,
},
dashboardEndpoint: {
type: String,
required: false,
default: invalidUrl,
},
+ currentDashboard: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
return {
@@ -139,10 +146,15 @@ export default {
'deploymentData',
'metricsWithData',
'useDashboardEndpoint',
+ 'allDashboards',
+ 'multipleDashboardsEnabled',
]),
groupsWithData() {
return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0);
},
+ selectedDashboardText() {
+ return this.currentDashboard || (this.allDashboards[0] && this.allDashboards[0].display_name);
+ },
},
created() {
this.setEndpoints({
@@ -150,6 +162,7 @@ export default {
environmentsEndpoint: this.environmentsEndpoint,
deploymentsEndpoint: this.deploymentsEndpoint,
dashboardEndpoint: this.dashboardEndpoint,
+ currentDashboard: this.currentDashboard,
});
this.timeWindows = timeWindows;
@@ -234,12 +247,30 @@ export default {
</script>
<template>
- <div v-if="!showEmptyState" class="prometheus-graphs">
+ <div class="prometheus-graphs">
<div class="gl-p-3 border-bottom bg-gray-light d-flex justify-content-between">
<div
v-if="environmentsEndpoint"
class="dropdowns d-flex align-items-center justify-content-between"
>
+ <div v-if="multipleDashboardsEnabled" class="d-flex align-items-center">
+ <label class="mb-0">{{ __('Dashboard') }}</label>
+ <gl-dropdown
+ class="ml-2 mr-3 js-dashboards-dropdown"
+ toggle-class="dropdown-menu-toggle"
+ :text="selectedDashboardText"
+ >
+ <gl-dropdown-item
+ v-for="dashboard in allDashboards"
+ :key="dashboard.path"
+ :active="dashboard.path === currentDashboard"
+ active-class="is-active"
+ :href="`?dashboard=${dashboard.path}`"
+ >
+ {{ dashboard.display_name || dashboard.path }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </div>
<div class="d-flex align-items-center">
<strong>{{ s__('Metrics|Environment') }}</strong>
<gl-dropdown
@@ -253,11 +284,12 @@ export default {
:key="environment.id"
:active="environment.name === currentEnvironmentName"
active-class="is-active"
+ :href="environment.metrics_path"
>{{ environment.name }}</gl-dropdown-item
>
</gl-dropdown>
</div>
- <div class="d-flex align-items-center prepend-left-8">
+ <div v-if="!showEmptyState" class="d-flex align-items-center prepend-left-8">
<strong>{{ s__('Metrics|Show last') }}</strong>
<gl-dropdown
class="prepend-left-10 js-time-window-dropdown"
@@ -276,7 +308,7 @@ export default {
</div>
</div>
<div class="d-flex">
- <div v-if="isEE && canAddMetrics">
+ <div v-if="isEE && canAddMetrics && !showEmptyState">
<gl-button
v-gl-modal-directive="$options.addMetric.modalId"
class="js-add-metric-button text-success border-success"
@@ -317,40 +349,42 @@ export default {
</gl-button>
</div>
</div>
- <graph-group
- v-for="(groupData, index) in groupsWithData"
- :key="index"
- :name="groupData.group"
- :show-panels="showPanels"
- >
- <monitor-area-chart
- v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
- :key="graphIndex"
- :graph-data="graphData"
- :deployment-data="deploymentData"
- :thresholds="getGraphAlertValues(graphData.queries)"
- :container-width="elWidth"
- group-id="monitor-area-chart"
+ <div v-if="!showEmptyState">
+ <graph-group
+ v-for="(groupData, index) in groupsWithData"
+ :key="index"
+ :name="groupData.group"
+ :show-panels="showPanels"
>
- <alert-widget
- v-if="isEE && prometheusAlertsAvailable && alertsEndpoint && graphData"
- :alerts-endpoint="alertsEndpoint"
- :relevant-queries="graphData.queries"
- :alerts-to-manage="getGraphAlerts(graphData.queries)"
- @setAlerts="setAlerts"
- />
- </monitor-area-chart>
- </graph-group>
+ <monitor-area-chart
+ v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
+ :key="graphIndex"
+ :graph-data="graphData"
+ :deployment-data="deploymentData"
+ :thresholds="getGraphAlertValues(graphData.queries)"
+ :container-width="elWidth"
+ group-id="monitor-area-chart"
+ >
+ <alert-widget
+ v-if="isEE && prometheusAlertsAvailable && alertsEndpoint && graphData"
+ :alerts-endpoint="alertsEndpoint"
+ :relevant-queries="graphData.queries"
+ :alerts-to-manage="getGraphAlerts(graphData.queries)"
+ @setAlerts="setAlerts"
+ />
+ </monitor-area-chart>
+ </graph-group>
+ </div>
+ <empty-state
+ v-else
+ :selected-state="emptyState"
+ :documentation-path="documentationPath"
+ :settings-path="settingsPath"
+ :clusters-path="clustersPath"
+ :empty-getting-started-svg-path="emptyGettingStartedSvgPath"
+ :empty-loading-svg-path="emptyLoadingSvgPath"
+ :empty-no-data-svg-path="emptyNoDataSvgPath"
+ :empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath"
+ />
</div>
- <empty-state
- v-else
- :selected-state="emptyState"
- :documentation-path="documentationPath"
- :settings-path="settingsPath"
- :clusters-path="clustersPath"
- :empty-getting-started-svg-path="emptyGettingStartedSvgPath"
- :empty-loading-svg-path="emptyLoadingSvgPath"
- :empty-no-data-svg-path="emptyNoDataSvgPath"
- :empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath"
- />
</template>
diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js
index 1d33537b3b2..edbcf84b342 100644
--- a/app/assets/javascripts/monitoring/monitoring_bundle.js
+++ b/app/assets/javascripts/monitoring/monitoring_bundle.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
+import { getParameterValues } from '~/lib/utils/url_utility';
import Dashboard from 'ee_else_ce/monitoring/components/dashboard.vue';
import store from './stores';
@@ -7,10 +8,12 @@ export default (props = {}) => {
const el = document.getElementById('prometheus-graphs');
if (el && el.dataset) {
- store.dispatch(
- 'monitoringDashboard/setDashboardEnabled',
- gon.features.environmentMetricsUsePrometheusEndpoint,
- );
+ store.dispatch('monitoringDashboard/setFeatureFlags', {
+ prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint,
+ multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards,
+ });
+
+ const [currentDashboard] = getParameterValues('dashboard');
// eslint-disable-next-line no-new
new Vue({
@@ -20,6 +23,7 @@ export default (props = {}) => {
return createElement(Dashboard, {
props: {
...el.dataset,
+ currentDashboard,
hasMetrics: parseBoolean(el.dataset.hasMetrics),
...props,
},
diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js
index f41e215cb5d..0fa2a5d6370 100644
--- a/app/assets/javascripts/monitoring/stores/actions.js
+++ b/app/assets/javascripts/monitoring/stores/actions.js
@@ -35,14 +35,24 @@ export const setEndpoints = ({ commit }, endpoints) => {
commit(types.SET_ENDPOINTS, endpoints);
};
-export const setDashboardEnabled = ({ commit }, enabled) => {
- commit(types.SET_DASHBOARD_ENABLED, enabled);
+export const setFeatureFlags = (
+ { commit },
+ { prometheusEndpointEnabled, multipleDashboardsEnabled },
+) => {
+ commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled);
+ commit(types.SET_MULTIPLE_DASHBOARDS_ENABLED, multipleDashboardsEnabled);
};
export const requestMetricsDashboard = ({ commit }) => {
commit(types.REQUEST_METRICS_DATA);
};
-export const receiveMetricsDashboardSuccess = ({ commit, dispatch }, { response, params }) => {
+export const receiveMetricsDashboardSuccess = (
+ { state, commit, dispatch },
+ { response, params },
+) => {
+ if (state.multipleDashboardsEnabled) {
+ commit(types.SET_ALL_DASHBOARDS, response.all_dashboards);
+ }
commit(types.RECEIVE_METRICS_DATA_SUCCESS, response.dashboard.panel_groups);
dispatch('fetchPrometheusMetrics', params);
};
@@ -95,6 +105,11 @@ export const fetchMetricsData = ({ state, dispatch }, params) => {
export const fetchDashboard = ({ state, dispatch }, params) => {
dispatch('requestMetricsDashboard');
+ if (state.currentDashboard) {
+ // eslint-disable-next-line no-param-reassign
+ params.dashboard = state.currentDashboard;
+ }
+
return axios
.get(state.dashboardEndpoint, { params })
.then(resp => resp.data)
diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js
index 63894e83362..2c78a0b9315 100644
--- a/app/assets/javascripts/monitoring/stores/mutation_types.js
+++ b/app/assets/javascripts/monitoring/stores/mutation_types.js
@@ -10,6 +10,8 @@ export const RECEIVE_ENVIRONMENTS_DATA_FAILURE = 'RECEIVE_ENVIRONMENTS_DATA_FAIL
export const SET_QUERY_RESULT = 'SET_QUERY_RESULT';
export const SET_TIME_WINDOW = 'SET_TIME_WINDOW';
export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED';
+export const SET_MULTIPLE_DASHBOARDS_ENABLED = 'SET_MULTIPLE_DASHBOARDS_ENABLED';
+export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS';
export const SET_ENDPOINTS = 'SET_ENDPOINTS';
export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE';
export const SET_NO_DATA_EMPTY_STATE = 'SET_NO_DATA_EMPTY_STATE';
diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js
index d4b816e2717..a85a7723c1f 100644
--- a/app/assets/javascripts/monitoring/stores/mutations.js
+++ b/app/assets/javascripts/monitoring/stores/mutations.js
@@ -74,10 +74,14 @@ export default {
state.environmentsEndpoint = endpoints.environmentsEndpoint;
state.deploymentsEndpoint = endpoints.deploymentsEndpoint;
state.dashboardEndpoint = endpoints.dashboardEndpoint;
+ state.currentDashboard = endpoints.currentDashboard;
},
[types.SET_DASHBOARD_ENABLED](state, enabled) {
state.useDashboardEndpoint = enabled;
},
+ [types.SET_MULTIPLE_DASHBOARDS_ENABLED](state, enabled) {
+ state.multipleDashboardsEnabled = enabled;
+ },
[types.SET_GETTING_STARTED_EMPTY_STATE](state) {
state.emptyState = 'gettingStarted';
},
@@ -85,4 +89,7 @@ export default {
state.showEmptyState = true;
state.emptyState = 'noData';
},
+ [types.SET_ALL_DASHBOARDS](state, dashboards) {
+ state.allDashboards = dashboards;
+ },
};
diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js
index c33529cd588..de711d6ccae 100644
--- a/app/assets/javascripts/monitoring/stores/state.js
+++ b/app/assets/javascripts/monitoring/stores/state.js
@@ -8,10 +8,13 @@ export default () => ({
deploymentsEndpoint: null,
dashboardEndpoint: invalidUrl,
useDashboardEndpoint: false,
+ multipleDashboardsEnabled: false,
emptyState: 'gettingStarted',
showEmptyState: true,
groups: [],
deploymentData: [],
environments: [],
metricsWithData: [],
+ allDashboards: [],
+ currentDashboard: null,
});
diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js
index 7f800d20835..1d8b388e935 100644
--- a/app/assets/javascripts/pages/users/user_tabs.js
+++ b/app/assets/javascripts/pages/users/user_tabs.js
@@ -18,12 +18,12 @@ import UserOverviewBlock from './user_overview_block';
*
* <ul class="nav-links">
* <li class="activity-tab active">
- * <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
+ * <a data-action="activity" data-target="#activity" data-toggle="tab" href="/username">
* Activity
* </a>
* </li>
* <li class="groups-tab">
- * <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
+ * <a data-action="groups" data-target="#groups" data-toggle="tab" href="/users/username/groups">
* Groups
* </a>
* </li>
diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediator.js b/app/assets/javascripts/pipelines/pipeline_details_mediator.js
index d67d88c4dba..c8819cf35cf 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_mediator.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_mediator.js
@@ -1,8 +1,8 @@
import Visibility from 'visibilityjs';
+import PipelineStore from 'ee_else_ce/pipelines/stores/pipeline_store';
import Flash from '../flash';
import Poll from '../lib/utils/poll';
import { __ } from '../locale';
-import PipelineStore from './stores/pipeline_store';
import PipelineService from './services/pipeline_service';
export default class pipelinesMediator {
diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue
index 0d4d431855c..67963dc1923 100644
--- a/app/assets/javascripts/repository/components/breadcrumbs.vue
+++ b/app/assets/javascripts/repository/components/breadcrumbs.vue
@@ -36,7 +36,7 @@ export default {
to: `/tree/${this.ref}${path}`,
});
},
- [{ name: this.projectShortPath, path: '/', to: `/tree/${this.ref}` }],
+ [{ name: this.projectShortPath, path: '/', to: `/tree/${this.ref}/` }],
);
},
},
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index 891e3fe9d16..1e66ccbfa29 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -131,7 +131,9 @@ export default {
v-for="entry in val"
:id="entry.id"
:key="`${entry.flatPath}-${entry.id}`"
+ :project-path="projectPath"
:current-path="path"
+ :name="entry.name"
:path="entry.flatPath"
:type="entry.type"
:url="entry.webUrl"
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index 4519f82fc93..c31e7fa71a2 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -1,12 +1,30 @@
<script>
-import { GlBadge } from '@gitlab/ui';
+import { GlBadge, GlLink, GlSkeletonLoading } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
+import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { getIconName } from '../../utils/icon';
import getRefMixin from '../../mixins/get_ref';
+import getCommit from '../../queries/getCommit.query.graphql';
export default {
components: {
GlBadge,
+ GlLink,
+ GlSkeletonLoading,
+ TimeagoTooltip,
+ },
+ apollo: {
+ commit: {
+ query: getCommit,
+ variables() {
+ return {
+ fileName: this.name,
+ type: this.type,
+ path: this.currentPath,
+ projectPath: this.projectPath,
+ };
+ },
+ },
},
mixins: [getRefMixin],
props: {
@@ -14,10 +32,18 @@ export default {
type: String,
required: true,
},
+ projectPath: {
+ type: String,
+ required: true,
+ },
currentPath: {
type: String,
required: true,
},
+ name: {
+ type: String,
+ required: true,
+ },
path: {
type: String,
required: true,
@@ -37,6 +63,11 @@ export default {
default: null,
},
},
+ data() {
+ return {
+ commit: null,
+ };
+ },
computed: {
routerLinkTo() {
return this.isFolder ? { path: `/tree/${this.ref}/${this.path}` } : null;
@@ -73,7 +104,7 @@ export default {
</script>
<template>
- <tr v-once :class="`file_${id}`" class="tree-item" @click="openRow">
+ <tr :class="`file_${id}`" class="tree-item" @click="openRow">
<td class="tree-item-file-name">
<i :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
<component :is="linkComponent" :to="routerLinkTo" :href="url" class="str-truncated">
@@ -83,10 +114,18 @@ export default {
LFS
</gl-badge>
<template v-if="isSubmodule">
- @ <a href="#" class="commit-sha">{{ shortSha }}</a>
+ @ <gl-link href="#" class="commit-sha">{{ shortSha }}</gl-link>
</template>
</td>
- <td class="d-none d-sm-table-cell tree-commit"></td>
- <td class="tree-time-ago text-right"></td>
+ <td class="d-none d-sm-table-cell tree-commit">
+ <gl-link v-if="commit" :href="commit.commitPath" class="str-truncated-100 tree-commit-link">
+ {{ commit.message }}
+ </gl-link>
+ <gl-skeleton-loading v-else :lines="1" class="h-auto" />
+ </td>
+ <td class="tree-time-ago text-right">
+ <timeago-tooltip v-if="commit" :time="commit.committedDate" tooltip-placement="bottom" />
+ <gl-skeleton-loading v-else :lines="1" class="ml-auto h-auto w-50" />
+ </td>
</tr>
</template>
diff --git a/app/assets/javascripts/repository/graphql.js b/app/assets/javascripts/repository/graphql.js
index ef147ec15cb..6cb253c8169 100644
--- a/app/assets/javascripts/repository/graphql.js
+++ b/app/assets/javascripts/repository/graphql.js
@@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import createDefaultClient from '~/lib/graphql';
import introspectionQueryResultData from './fragmentTypes.json';
+import { fetchLogsTree } from './log_tree';
Vue.use(VueApollo);
@@ -13,7 +14,21 @@ const fragmentMatcher = new IntrospectionFragmentMatcher({
});
const defaultClient = createDefaultClient(
- {},
+ {
+ Query: {
+ commit(_, { path, fileName, type }) {
+ return new Promise(resolve => {
+ fetchLogsTree(defaultClient, path, '0', {
+ resolve,
+ entry: {
+ name: fileName,
+ type,
+ },
+ });
+ });
+ },
+ },
+ },
{
cacheConfig: {
fragmentMatcher,
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index d9216e88676..6280977b05b 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -16,6 +16,7 @@ export default function setupVueRepositoryList() {
projectPath,
projectShortPath,
ref,
+ commits: [],
},
});
diff --git a/app/assets/javascripts/repository/log_tree.js b/app/assets/javascripts/repository/log_tree.js
new file mode 100644
index 00000000000..2c19aca2397
--- /dev/null
+++ b/app/assets/javascripts/repository/log_tree.js
@@ -0,0 +1,64 @@
+import axios from '~/lib/utils/axios_utils';
+import getCommits from './queries/getCommits.query.graphql';
+import getProjectPath from './queries/getProjectPath.query.graphql';
+import getRef from './queries/getRef.query.graphql';
+
+let fetchpromise;
+let resolvers = [];
+
+export function normalizeData(data) {
+ return data.map(d => ({
+ sha: d.commit.id,
+ message: d.commit.message,
+ committedDate: d.commit.committed_date,
+ commitPath: d.commit_path,
+ fileName: d.file_name,
+ type: d.type,
+ __typename: 'LogTreeCommit',
+ }));
+}
+
+export function resolveCommit(commits, { resolve, entry }) {
+ const commit = commits.find(c => c.fileName === entry.name && c.type === entry.type);
+
+ if (commit) {
+ resolve(commit);
+ }
+}
+
+export function fetchLogsTree(client, path, offset, resolver = null) {
+ if (resolver) {
+ resolvers.push(resolver);
+ }
+
+ if (fetchpromise) return fetchpromise;
+
+ const { projectPath } = client.readQuery({ query: getProjectPath });
+ const { ref } = client.readQuery({ query: getRef });
+
+ fetchpromise = axios
+ .get(`${gon.gitlab_url}/${projectPath}/refs/${ref}/logs_tree${path ? `/${path}` : ''}`, {
+ params: { format: 'json', offset },
+ })
+ .then(({ data, headers }) => {
+ const headerLogsOffset = headers['more-logs-offset'];
+ const { commits } = client.readQuery({ query: getCommits });
+ const newCommitData = [...commits, ...normalizeData(data)];
+ client.writeQuery({
+ query: getCommits,
+ data: { commits: newCommitData },
+ });
+
+ resolvers.forEach(r => resolveCommit(newCommitData, r));
+
+ fetchpromise = null;
+
+ if (headerLogsOffset) {
+ fetchLogsTree(client, path, headerLogsOffset);
+ } else {
+ resolvers = [];
+ }
+ });
+
+ return fetchpromise;
+}
diff --git a/app/assets/javascripts/repository/queries/getCommit.query.graphql b/app/assets/javascripts/repository/queries/getCommit.query.graphql
new file mode 100644
index 00000000000..e2a2d831e47
--- /dev/null
+++ b/app/assets/javascripts/repository/queries/getCommit.query.graphql
@@ -0,0 +1,10 @@
+query getCommit($fileName: String!, $type: String!, $path: String!) {
+ commit(path: $path, fileName: $fileName, type: $type) @client {
+ sha
+ message
+ committedDate
+ commitPath
+ fileName
+ type
+ }
+}
diff --git a/app/assets/javascripts/repository/queries/getCommits.query.graphql b/app/assets/javascripts/repository/queries/getCommits.query.graphql
new file mode 100644
index 00000000000..df9e67cc440
--- /dev/null
+++ b/app/assets/javascripts/repository/queries/getCommits.query.graphql
@@ -0,0 +1,10 @@
+query getCommits {
+ commits @client {
+ sha
+ message
+ committedDate
+ commitPath
+ fileName
+ type
+ }
+}
diff --git a/app/assets/javascripts/repository/queries/getFiles.query.graphql b/app/assets/javascripts/repository/queries/getFiles.query.graphql
index ef924fde556..4c24fc4087f 100644
--- a/app/assets/javascripts/repository/queries/getFiles.query.graphql
+++ b/app/assets/javascripts/repository/queries/getFiles.query.graphql
@@ -1,5 +1,6 @@
fragment TreeEntry on Entry {
id
+ name
flatPath
type
}
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
index 13955529cab..bc263bc36e4 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
@@ -70,9 +70,6 @@ export default {
:title="timeRemainingTooltip"
:class="timeRemainingStatusClass"
class="compare-meter"
- data-toggle="tooltip"
- data-placement="top"
- role="timeRemainingDisplay"
>
<gl-progress-bar :value="timeRemainingPercent" :variant="progressBarVariant" />
<div class="compare-display-container">
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index 22ac8df9699..643fe6c00b6 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -1,7 +1,7 @@
import { visitUrl } from '../lib/utils/url_utility';
import Flash from '../flash';
import Service from './services/sidebar_service';
-import Store from './stores/sidebar_store';
+import Store from 'ee_else_ce/sidebar/stores/sidebar_store';
import { __ } from '~/locale';
export default class SidebarMediator {
diff --git a/app/assets/javascripts/visual_review_toolbar/components/comment.js b/app/assets/javascripts/visual_review_toolbar/components/comment.js
index 2fec96d1435..04bfb5e9532 100644
--- a/app/assets/javascripts/visual_review_toolbar/components/comment.js
+++ b/app/assets/javascripts/visual_review_toolbar/components/comment.js
@@ -1,54 +1,62 @@
import { BLACK, COMMENT_BOX, MUTED, LOGOUT } from './constants';
-import { clearNote, note, postError } from './note';
-import { buttonClearStyles, selectCommentBox, selectCommentButton, selectNote } from './utils';
+import { clearNote, postError } from './note';
+import {
+ buttonClearStyles,
+ selectCommentBox,
+ selectCommentButton,
+ selectNote,
+ selectNoteContainer,
+} from './utils';
const comment = `
<div>
<textarea id="${COMMENT_BOX}" name="${COMMENT_BOX}" rows="3" placeholder="Enter your feedback or idea" class="gitlab-input" aria-required="true"></textarea>
- ${note}
<p class="gitlab-metadata-note">Additional metadata will be included: browser, OS, current page, user agent, and viewport dimensions.</p>
</div>
<div class="gitlab-button-wrapper">
- <button class="gitlab-button gitlab-button-secondary" style="${buttonClearStyles}" type="button" id="${LOGOUT}"> Logout </button>
+ <button class="gitlab-button gitlab-button-secondary" style="${buttonClearStyles}" type="button" id="${LOGOUT}"> Log out </button>
<button class="gitlab-button gitlab-button-success" style="${buttonClearStyles}" type="button" id="gitlab-comment-button"> Send feedback </button>
</div>
`;
-const resetCommentBox = () => {
- const commentBox = selectCommentBox();
+const resetCommentButton = () => {
const commentButton = selectCommentButton();
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
commentButton.innerText = 'Send feedback';
commentButton.classList.replace('gitlab-button-secondary', 'gitlab-button-success');
commentButton.style.opacity = 1;
+};
+const resetCommentBox = () => {
+ const commentBox = selectCommentBox();
commentBox.style.pointerEvents = 'auto';
commentBox.style.color = BLACK;
};
-const resetCommentButton = () => {
+const resetCommentText = () => {
const commentBox = selectCommentBox();
- const currentNote = selectNote();
-
commentBox.value = '';
- currentNote.innerText = '';
};
const resetComment = () => {
- resetCommentBox();
resetCommentButton();
+ resetCommentBox();
+ resetCommentText();
};
-const confirmAndClear = mergeRequestId => {
+const confirmAndClear = feedbackInfo => {
const commentButton = selectCommentButton();
const currentNote = selectNote();
+ const noteContainer = selectNoteContainer();
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
commentButton.innerText = 'Feedback sent';
- /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
- currentNote.innerText = `Your comment was successfully posted to merge request #${mergeRequestId}`;
- setTimeout(resetComment, 2000);
+ noteContainer.style.visibility = 'visible';
+ currentNote.insertAdjacentHTML('beforeend', feedbackInfo);
+
+ setTimeout(resetComment, 1000);
+ setTimeout(clearNote, 6000);
};
const setInProgressState = () => {
@@ -71,6 +79,7 @@ const postComment = ({
innerWidth,
innerHeight,
projectId,
+ projectPath,
mergeRequestId,
mrUrl,
token,
@@ -86,6 +95,7 @@ const postComment = ({
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
postError('Your comment appears to be empty.', COMMENT_BOX);
resetCommentBox();
+ resetCommentButton();
return;
}
@@ -114,18 +124,24 @@ const postComment = ({
})
.then(response => {
if (response.ok) {
- confirmAndClear(mergeRequestId);
- return;
+ return response.json();
}
throw new Error(`${response.status}: ${response.statusText}`);
})
+ .then(data => {
+ const commentId = data.notes[0].id;
+ const feedbackLink = `${mrUrl}/${projectPath}/merge_requests/${mergeRequestId}#note_${commentId}`;
+ const feedbackInfo = `Feedback sent. View at <a class="gitlab-link" href="${feedbackLink}">${projectPath} #${mergeRequestId} (comment ${commentId})</a>`;
+ confirmAndClear(feedbackInfo);
+ })
.catch(err => {
postError(
`Your comment could not be sent. Please try again. Error: ${err.message}`,
COMMENT_BOX,
);
resetCommentBox();
+ resetCommentButton();
});
};
diff --git a/app/assets/javascripts/visual_review_toolbar/components/constants.js b/app/assets/javascripts/visual_review_toolbar/components/constants.js
index 32ed1153515..07fcb179d15 100644
--- a/app/assets/javascripts/visual_review_toolbar/components/constants.js
+++ b/app/assets/javascripts/visual_review_toolbar/components/constants.js
@@ -2,10 +2,12 @@
const COLLAPSE_BUTTON = 'gitlab-collapse';
const COMMENT_BOX = 'gitlab-comment';
const COMMENT_BUTTON = 'gitlab-comment-button';
-const FORM = 'gitlab-form-wrapper';
+const FORM = 'gitlab-form';
+const FORM_CONTAINER = 'gitlab-form-wrapper';
const LOGIN = 'gitlab-login';
const LOGOUT = 'gitlab-logout-button';
const NOTE = 'gitlab-validation-note';
+const NOTE_CONTAINER = 'gitlab-note-wrapper';
const REMEMBER_TOKEN = 'gitlab-remember_token';
const REVIEW_CONTAINER = 'gitlab-review-container';
const TOKEN_BOX = 'gitlab-token';
@@ -16,16 +18,18 @@ const BLACK = 'rgba(46, 46, 46, 1)';
const CLEAR = 'rgba(255, 255, 255, 0)';
const MUTED = 'rgba(223, 223, 223, 0.5)';
const RED = 'rgba(219, 59, 33, 1)';
-const WHITE = 'rgba(255, 255, 255, 1)';
+const WHITE = 'rgba(250, 250, 250, 1)';
export {
COLLAPSE_BUTTON,
COMMENT_BOX,
COMMENT_BUTTON,
FORM,
+ FORM_CONTAINER,
LOGIN,
LOGOUT,
NOTE,
+ NOTE_CONTAINER,
REMEMBER_TOKEN,
REVIEW_CONTAINER,
TOKEN_BOX,
diff --git a/app/assets/javascripts/visual_review_toolbar/components/index.js b/app/assets/javascripts/visual_review_toolbar/components/index.js
index 43581818152..50b52d7d3a2 100644
--- a/app/assets/javascripts/visual_review_toolbar/components/index.js
+++ b/app/assets/javascripts/visual_review_toolbar/components/index.js
@@ -1,22 +1,32 @@
import { comment, postComment } from './comment';
-import { COLLAPSE_BUTTON, COMMENT_BUTTON, LOGIN, LOGOUT, REVIEW_CONTAINER } from './constants';
+import {
+ COLLAPSE_BUTTON,
+ COMMENT_BUTTON,
+ FORM_CONTAINER,
+ LOGIN,
+ LOGOUT,
+ REVIEW_CONTAINER,
+} from './constants';
import { authorizeUser, login } from './login';
+import { note } from './note';
import { selectContainer } from './utils';
-import { form, logoutUser, toggleForm } from './wrapper';
+import { buttonAndForm, logoutUser, toggleForm } from './wrapper';
import { collapseButton } from './wrapper_icons';
export {
authorizeUser,
+ buttonAndForm,
collapseButton,
comment,
- form,
login,
logoutUser,
+ note,
postComment,
selectContainer,
toggleForm,
COLLAPSE_BUTTON,
COMMENT_BUTTON,
+ FORM_CONTAINER,
LOGIN,
LOGOUT,
REVIEW_CONTAINER,
diff --git a/app/assets/javascripts/visual_review_toolbar/components/login.js b/app/assets/javascripts/visual_review_toolbar/components/login.js
index ce713cdc520..0a71299f041 100644
--- a/app/assets/javascripts/visual_review_toolbar/components/login.js
+++ b/app/assets/javascripts/visual_review_toolbar/components/login.js
@@ -1,5 +1,5 @@
import { LOGIN, REMEMBER_TOKEN, TOKEN_BOX } from './constants';
-import { clearNote, note, postError } from './note';
+import { clearNote, postError } from './note';
import { buttonClearStyles, selectRemember, selectToken } from './utils';
import { addCommentForm } from './wrapper';
@@ -7,7 +7,6 @@ const login = `
<div>
<label for="${TOKEN_BOX}" class="gitlab-label">Enter your <a class="gitlab-link" href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html">personal access token</a></label>
<input class="gitlab-input" type="password" id="${TOKEN_BOX}" name="${TOKEN_BOX}" aria-required="true" autocomplete="current-password">
- ${note}
</div>
<div class="gitlab-checkbox-wrapper">
<input type="checkbox" id="${REMEMBER_TOKEN}" name="${REMEMBER_TOKEN}" value="remember">
diff --git a/app/assets/javascripts/visual_review_toolbar/components/note.js b/app/assets/javascripts/visual_review_toolbar/components/note.js
index dfebf58fd95..0150f640aae 100644
--- a/app/assets/javascripts/visual_review_toolbar/components/note.js
+++ b/app/assets/javascripts/visual_review_toolbar/components/note.js
@@ -1,14 +1,19 @@
-import { NOTE, RED } from './constants';
-import { selectById, selectNote } from './utils';
+import { NOTE, NOTE_CONTAINER, RED } from './constants';
+import { selectById, selectNote, selectNoteContainer } from './utils';
const note = `
- <p id=${NOTE} class='gitlab-message'></p>
+ <div id="${NOTE_CONTAINER}" style="visibility: hidden;">
+ <p id="${NOTE}" class="gitlab-message"></p>
+ </div>
`;
const clearNote = inputId => {
const currentNote = selectNote();
+ const noteContainer = selectNoteContainer();
+
currentNote.innerText = '';
currentNote.style.color = '';
+ noteContainer.style.visibility = 'hidden';
if (inputId) {
const field = document.getElementById(inputId);
@@ -18,10 +23,13 @@ const clearNote = inputId => {
const postError = (message, inputId) => {
const currentNote = selectNote();
+ const noteContainer = selectNoteContainer();
const field = selectById(inputId);
field.style.borderColor = RED;
currentNote.style.color = RED;
currentNote.innerText = message;
+ noteContainer.style.visibility = 'visible';
+ setTimeout(clearNote.bind(null, inputId), 5000);
};
export { clearNote, note, postError };
diff --git a/app/assets/javascripts/visual_review_toolbar/components/utils.js b/app/assets/javascripts/visual_review_toolbar/components/utils.js
index 7bc2e5a905b..00f4460925d 100644
--- a/app/assets/javascripts/visual_review_toolbar/components/utils.js
+++ b/app/assets/javascripts/visual_review_toolbar/components/utils.js
@@ -5,7 +5,9 @@ import {
COMMENT_BOX,
COMMENT_BUTTON,
FORM,
+ FORM_CONTAINER,
NOTE,
+ NOTE_CONTAINER,
REMEMBER_TOKEN,
REVIEW_CONTAINER,
TOKEN_BOX,
@@ -24,7 +26,9 @@ const selectCommentBox = () => document.getElementById(COMMENT_BOX);
const selectCommentButton = () => document.getElementById(COMMENT_BUTTON);
const selectContainer = () => document.getElementById(REVIEW_CONTAINER);
const selectForm = () => document.getElementById(FORM);
+const selectFormContainer = () => document.getElementById(FORM_CONTAINER);
const selectNote = () => document.getElementById(NOTE);
+const selectNoteContainer = () => document.getElementById(NOTE_CONTAINER);
const selectRemember = () => document.getElementById(REMEMBER_TOKEN);
const selectToken = () => document.getElementById(TOKEN_BOX);
@@ -36,7 +40,9 @@ export {
selectCommentBox,
selectCommentButton,
selectForm,
+ selectFormContainer,
selectNote,
+ selectNoteContainer,
selectRemember,
selectToken,
};
diff --git a/app/assets/javascripts/visual_review_toolbar/components/wrapper.js b/app/assets/javascripts/visual_review_toolbar/components/wrapper.js
index 233b7ec496c..f2eaf1d7916 100644
--- a/app/assets/javascripts/visual_review_toolbar/components/wrapper.js
+++ b/app/assets/javascripts/visual_review_toolbar/components/wrapper.js
@@ -1,15 +1,28 @@
import { comment } from './comment';
-import { CLEAR, FORM, WHITE } from './constants';
+import { CLEAR, FORM, FORM_CONTAINER, WHITE } from './constants';
import { login } from './login';
-import { selectCollapseButton, selectContainer, selectForm } from './utils';
+import { clearNote } from './note';
+import {
+ selectCollapseButton,
+ selectForm,
+ selectFormContainer,
+ selectNoteContainer,
+} from './utils';
import { commentIcon, compressIcon } from './wrapper_icons';
const form = content => `
- <form id=${FORM}>
+ <form id="${FORM}">
${content}
</form>
`;
+const buttonAndForm = ({ content, toggleButton }) => `
+ <div id="${FORM_CONTAINER}" class="gitlab-form-open">
+ ${toggleButton}
+ ${form(content)}
+ </div>
+`;
+
const addCommentForm = () => {
const formWrapper = selectForm();
formWrapper.innerHTML = comment;
@@ -31,13 +44,15 @@ function logoutUser() {
return;
}
+ clearNote();
addLoginForm();
}
function toggleForm() {
- const container = selectContainer();
const collapseButton = selectCollapseButton();
const currentForm = selectForm();
+ const formContainer = selectFormContainer();
+ const noteContainer = selectNoteContainer();
const OPEN = 'open';
const CLOSED = 'closed';
@@ -49,7 +64,7 @@ function toggleForm() {
const openButtonClasses = ['gitlab-collapse-closed', 'gitlab-collapse-open'];
const closedButtonClasses = [...openButtonClasses].reverse();
- const openContainerClasses = ['gitlab-closed-wrapper', 'gitlab-open-wrapper'];
+ const openContainerClasses = ['gitlab-wrapper-closed', 'gitlab-wrapper-open'];
const closedContainerClasses = [...openContainerClasses].reverse();
const stateVals = {
@@ -72,11 +87,16 @@ function toggleForm() {
const nextState = collapseButton.classList.contains('gitlab-collapse-open') ? CLOSED : OPEN;
const currentVals = stateVals[nextState];
- container.classList.replace(...currentVals.containerClasses);
- container.style.backgroundColor = currentVals.backgroundColor;
+ formContainer.classList.replace(...currentVals.containerClasses);
+ formContainer.style.backgroundColor = currentVals.backgroundColor;
+ formContainer.classList.toggle('gitlab-form-open');
currentForm.style.display = currentVals.display;
collapseButton.classList.replace(...currentVals.buttonClasses);
collapseButton.innerHTML = currentVals.icon;
+
+ if (noteContainer && noteContainer.innerText.length > 0) {
+ noteContainer.style.display = currentVals.display;
+ }
}
-export { addCommentForm, addLoginForm, form, logoutUser, toggleForm };
+export { addCommentForm, addLoginForm, buttonAndForm, logoutUser, toggleForm };
diff --git a/app/assets/javascripts/visual_review_toolbar/index.js b/app/assets/javascripts/visual_review_toolbar/index.js
index 941d77e25b4..f94eb88835a 100644
--- a/app/assets/javascripts/visual_review_toolbar/index.js
+++ b/app/assets/javascripts/visual_review_toolbar/index.js
@@ -1,6 +1,6 @@
import './styles/toolbar.css';
-import { form, selectContainer, REVIEW_CONTAINER } from './components';
+import { buttonAndForm, note, selectContainer, REVIEW_CONTAINER } from './components';
import { debounce, eventLookup, getInitialView, initializeState, updateWindowSize } from './store';
/*
@@ -20,12 +20,11 @@ import { debounce, eventLookup, getInitialView, initializeState, updateWindowSiz
window.addEventListener('load', () => {
initializeState(window, document);
- const { content, toggleButton } = getInitialView(window);
+ const mainContent = buttonAndForm(getInitialView(window));
const container = document.createElement('div');
-
container.setAttribute('id', REVIEW_CONTAINER);
- container.insertAdjacentHTML('beforeend', toggleButton);
- container.insertAdjacentHTML('beforeend', form(content));
+ container.insertAdjacentHTML('beforeend', note);
+ container.insertAdjacentHTML('beforeend', mainContent);
document.body.insertBefore(container, document.body.firstChild);
diff --git a/app/assets/javascripts/visual_review_toolbar/store/state.js b/app/assets/javascripts/visual_review_toolbar/store/state.js
index f5ede6e85b2..22702d524b8 100644
--- a/app/assets/javascripts/visual_review_toolbar/store/state.js
+++ b/app/assets/javascripts/visual_review_toolbar/store/state.js
@@ -34,7 +34,7 @@ const initializeState = (wind, doc) => {
const browser = getBrowserId(userAgent);
const scriptEl = doc.getElementById('review-app-toolbar-script');
- const { projectId, mergeRequestId, mrUrl } = scriptEl.dataset;
+ const { projectId, mergeRequestId, mrUrl, projectPath } = scriptEl.dataset;
// This mutates our default state object above. It's weird but it makes the linter happy.
Object.assign(state, {
@@ -46,6 +46,7 @@ const initializeState = (wind, doc) => {
mrUrl,
platform,
projectId,
+ projectPath,
userAgent,
});
};
diff --git a/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css b/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css
index 342b3599a44..00a55c0027a 100644
--- a/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css
+++ b/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css
@@ -6,23 +6,42 @@
pointer-events: none;
}
-#gitlab-form-wrapper {
+#gitlab-comment {
+ background-color: #fafafa;
+}
+
+#gitlab-form {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ margin-bottom: 0;
+}
+
+#gitlab-note-wrapper {
display: flex;
flex-direction: column;
- width: 100%
+ background-color: #fafafa;
+ border-radius: 4px;
+ margin-bottom: .5rem;
+ padding: 1rem;
+}
+
+#gitlab-form-wrapper {
+ overflow: auto;
+ display: flex;
+ flex-direction: row-reverse;
+ border-radius: 4px;
}
#gitlab-review-container {
max-width: 22rem;
max-height: 22rem;
- overflow: scroll;
+ overflow: auto;
+ display: flex;
+ flex-direction: column;
position: fixed;
bottom: 1rem;
right: 1rem;
- display: flex;
- flex-direction: row-reverse;
- padding: 1rem;
- background-color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
@@ -31,12 +50,12 @@
color: #2e2e2e;
}
-.gitlab-open-wrapper {
+.gitlab-wrapper-open {
max-width: 22rem;
max-height: 22rem;
}
-.gitlab-closed-wrapper {
+.gitlab-wrapper-closed {
max-width: 3.4rem;
max-height: 3.4rem;
}
@@ -47,7 +66,7 @@
}
.gitlab-button-secondary {
- background: none #fff;
+ background: none #fafafa;
margin: 0 .5rem;
border: 1px solid #e3e3e3;
}
@@ -113,6 +132,11 @@
align-items: baseline;
}
+.gitlab-form-open {
+ padding: 1rem;
+ background-color: #fafafa;
+}
+
.gitlab-label {
font-weight: 600;
display: inline-block;
@@ -126,6 +150,10 @@
background-image: none;
}
+.gitlab-link:hover {
+ text-decoration: underline;
+}
+
.gitlab-message {
padding: .25rem 0;
margin: 0;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index dcbb23684d1..6a0127eb51c 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -594,18 +594,18 @@
padding: 16px 0;
small {
- color: $gray-darkest;
+ color: $gray-700;
}
}
.edited-text {
- color: $gray-darkest;
+ color: $gray-700;
display: block;
margin: 16px 0 0;
font-size: 85%;
.author-link {
- color: $gray-darkest;
+ color: $gray-700;
}
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 5cacd42bf0d..824edb2869f 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -628,7 +628,7 @@ $note-form-margin-left: 72px;
.note-headline-meta {
.system-note-separator {
- color: $gl-text-color-disabled;
+ color: $gray-700;
}
.note-timestamp {
diff --git a/app/controllers/concerns/continue_params.rb b/app/controllers/concerns/continue_params.rb
index 54c0510497f..d5830f6648c 100644
--- a/app/controllers/concerns/continue_params.rb
+++ b/app/controllers/concerns/continue_params.rb
@@ -6,7 +6,7 @@ module ContinueParams
def continue_params
continue_params = params[:continue]
- return unless continue_params
+ return {} unless continue_params
continue_params = continue_params.permit(:to, :notice, :notice_now)
continue_params[:to] = safe_redirect_path(continue_params[:to])
diff --git a/app/controllers/concerns/internal_redirect.rb b/app/controllers/concerns/internal_redirect.rb
index 6785e6972d0..fa3716502a0 100644
--- a/app/controllers/concerns/internal_redirect.rb
+++ b/app/controllers/concerns/internal_redirect.rb
@@ -5,8 +5,8 @@ module InternalRedirect
def safe_redirect_path(path)
return unless path
- # Verify that the string starts with a `/` but not a double `/`.
- return unless path =~ %r{^/\w.*$}
+ # Verify that the string starts with a `/` and a known route character.
+ return unless path =~ %r{^/[-\w].*$}
uri = URI(path)
# Ignore anything path of the redirect except for the path, querystring and,
diff --git a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb
index 426f224d26b..f47ead2f0da 100644
--- a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb
+++ b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb
@@ -14,6 +14,10 @@ module RequiresWhitelistedMonitoringClient
end
def client_ip_whitelisted?
+ # Always allow developers to access http://localhost:3000/-/metrics for
+ # debugging purposes
+ return true if Rails.env.development? && request.local?
+
ip_whitelist.any? { |e| e.include?(Gitlab::RequestContext.client_ip) }
end
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index 7a1700a206a..ac1c4bc7fd3 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -46,18 +46,14 @@ class Projects::ForksController < Projects::ApplicationController
@forked_project ||= ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
- if @forked_project.saved? && @forked_project.forked?
- if @forked_project.import_in_progress?
- redirect_to project_import_path(@forked_project, continue: continue_params)
- else
- if continue_params
- redirect_to continue_params[:to], notice: continue_params[:notice]
- else
- redirect_to project_path(@forked_project), notice: "The project '#{@forked_project.name}' was successfully forked."
- end
- end
- else
+ if !@forked_project.saved? || !@forked_project.forked?
render :error
+ elsif @forked_project.import_in_progress?
+ redirect_to project_import_path(@forked_project, continue: continue_params)
+ elsif continue_params[:to]
+ redirect_to continue_params[:to], notice: continue_params[:notice]
+ else
+ redirect_to project_path(@forked_project), notice: "The project '#{@forked_project.name}' was successfully forked."
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index afbf9fd7720..da32ab9e2e0 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -23,7 +23,7 @@ class Projects::ImportsController < Projects::ApplicationController
def show
if @project.import_finished?
- if continue_params&.key?(:to)
+ if continue_params[:to]
redirect_to continue_params[:to], notice: continue_params[:notice]
else
redirect_to project_path(@project), notice: finished_notice
@@ -31,11 +31,7 @@ class Projects::ImportsController < Projects::ApplicationController
elsif @project.import_failed?
redirect_to new_project_import_path(@project)
else
- if continue_params && continue_params[:notice_now]
- flash.now[:notice] = continue_params[:notice_now]
- end
-
- # Render
+ flash.now[:notice] = continue_params[:notice_now]
end
end
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index d7c0039b234..02ff6e872c9 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -103,7 +103,7 @@ class Projects::JobsController < Projects::ApplicationController
@build.cancel
- if continue_params
+ if continue_params[:to]
redirect_to continue_params[:to]
else
redirect_to builds_project_pipeline_path(@project, @build.pipeline.id)
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index b3447812ef2..b4ca9074ca9 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -55,6 +55,7 @@ class Projects::RefsController < Projects::ApplicationController
format.html { render_404 }
format.json do
response.headers["More-Logs-Url"] = @more_log_url if summary.more?
+ response.headers["More-Logs-Offset"] = summary.next_offset if summary.more?
render json: @logs
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index d837c42fd68..aaaa954047f 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -165,8 +165,6 @@ module ApplicationSettingsHelper
:authorized_keys_enabled,
:auto_devops_enabled,
:auto_devops_domain,
- :clientside_sentry_dsn,
- :clientside_sentry_enabled,
:container_registry_token_expire_delay,
:default_artifacts_expire_in,
:default_branch_protection,
@@ -235,8 +233,6 @@ module ApplicationSettingsHelper
:restricted_visibility_levels,
:rsa_key_restriction,
:send_user_confirmation_email,
- :sentry_dsn,
- :sentry_enabled,
:session_expire_delay,
:shared_runners_enabled,
:shared_runners_text,
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index bbe2d2e8fd4..cd645850af3 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -30,6 +30,10 @@ class ApplicationSetting < ApplicationRecord
ignore_column :circuitbreaker_check_interval
ignore_column :koding_url
ignore_column :koding_enabled
+ ignore_column :sentry_enabled
+ ignore_column :sentry_dsn
+ ignore_column :clientside_sentry_enabled
+ ignore_column :clientside_sentry_dsn
cache_markdown_field :sign_in_text
cache_markdown_field :help_page_text
@@ -75,14 +79,6 @@ class ApplicationSetting < ApplicationRecord
presence: true,
if: :recaptcha_enabled
- validates :sentry_dsn,
- presence: true,
- if: :sentry_enabled
-
- validates :clientside_sentry_dsn,
- presence: true,
- if: :clientside_sentry_enabled
-
validates :akismet_api_key,
presence: true,
if: :akismet_enabled
@@ -264,7 +260,6 @@ class ApplicationSetting < ApplicationRecord
encode: true
before_validation :ensure_uuid!
- before_validation :strip_sentry_values
before_save :ensure_runners_registration_token
before_save :ensure_health_check_access_token
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index cf328bcd994..df4caed175d 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -180,27 +180,6 @@ module ApplicationSettingImplementation
super(levels&.map { |level| Gitlab::VisibilityLevel.level_value(level) })
end
- def strip_sentry_values
- sentry_dsn.strip! if sentry_dsn.present?
- clientside_sentry_dsn.strip! if clientside_sentry_dsn.present?
- end
-
- def sentry_enabled
- Gitlab.config.sentry.enabled || read_attribute(:sentry_enabled)
- end
-
- def sentry_dsn
- Gitlab.config.sentry.dsn || read_attribute(:sentry_dsn)
- end
-
- def clientside_sentry_enabled
- Gitlab.config.sentry.enabled || read_attribute(:clientside_sentry_enabled)
- end
-
- def clientside_sentry_dsn
- Gitlab.config.sentry.clientside_dsn || read_attribute(:clientside_sentry_dsn)
- end
-
def performance_bar_allowed_group
Group.find_by_id(performance_bar_allowed_group_id)
end
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index 5afb193cf86..9296c28776b 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -4,7 +4,6 @@ module Clusters
module Platforms
class Kubernetes < ApplicationRecord
include Gitlab::Kubernetes
- include ReactiveCaching
include EnumWithNil
include AfterCommitQueue
@@ -46,8 +45,6 @@ module Clusters
validate :prevent_modification, on: :update
- after_save :clear_reactive_cache!
-
alias_attribute :ca_pem, :ca_cert
delegate :enabled?, to: :cluster, allow_nil: true
@@ -96,27 +93,16 @@ module Clusters
end
end
- # Constructs a list of terminals from the reactive cache
- #
- # Returns nil if the cache is empty, in which case you should try again a
- # short time later
- def terminals(environment)
- with_reactive_cache do |data|
- project = environment.project
-
- pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug)
- terminals = pods.flat_map { |pod| terminals_for_pod(api_url, cluster.kubernetes_namespace_for(project), pod) }.compact
- terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
- end
- end
-
- # Caches resources in the namespace so other calls don't need to block on
- # network access
- def calculate_reactive_cache
+ def calculate_reactive_cache_for(environment)
return unless enabled?
- # We may want to cache extra things in the future
- { pods: read_pods }
+ { pods: read_pods(environment.deployment_namespace) }
+ end
+
+ def terminals(environment, data)
+ pods = filter_by_project_environment(data[:pods], environment.project.full_path_slug, environment.slug)
+ terminals = pods.flat_map { |pod| terminals_for_pod(api_url, environment.deployment_namespace, pod) }.compact
+ terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
end
def kubeclient
@@ -133,6 +119,12 @@ module Clusters
ca_pem: ca_pem)
end
+ def read_pods(namespace)
+ kubeclient.get_pods(namespace: namespace).as_json
+ rescue Kubeclient::ResourceNotFoundError
+ []
+ end
+
def build_kube_client!
raise "Incomplete settings" unless api_url
@@ -148,19 +140,6 @@ module Clusters
)
end
- # Returns a hash of all pods in the namespace
- def read_pods
- # TODO: The project lookup here should be moved (to environment?),
- # which will enable reading pods from the correct namespace for group
- # and instance clusters.
- # This will be done in https://gitlab.com/gitlab-org/gitlab-ce/issues/61156
- return [] unless cluster.project_type?
-
- kubeclient.get_pods(namespace: cluster.kubernetes_namespace_for(cluster.first_project)).as_json
- rescue Kubeclient::ResourceNotFoundError
- []
- end
-
def kubeclient_ssl_options
opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 1f7e8815c8e..b8ee54c1696 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -2,6 +2,8 @@
class Environment < ApplicationRecord
include Gitlab::Utils::StrongMemoize
+ include ReactiveCaching
+
# Used to generate random suffixes for the slug
LETTERS = ('a'..'z').freeze
NUMBERS = ('0'..'9').freeze
@@ -17,6 +19,7 @@ class Environment < ApplicationRecord
before_validation :generate_slug, if: ->(env) { env.slug.blank? }
before_save :set_environment_type
+ after_save :clear_reactive_cache!
validates :name,
presence: true,
@@ -159,7 +162,21 @@ class Environment < ApplicationRecord
end
def terminals
- deployment_platform.terminals(self) if has_terminals?
+ with_reactive_cache do |data|
+ deployment_platform.terminals(self, data)
+ end
+ end
+
+ def calculate_reactive_cache
+ return unless has_terminals? && !project.pending_delete?
+
+ deployment_platform.calculate_reactive_cache_for(self)
+ end
+
+ def deployment_namespace
+ strong_memoize(:kubernetes_namespace) do
+ deployment_platform&.kubernetes_namespace_for(project)
+ end
end
def has_metrics?
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 3c270c7396a..f9b53b2b70a 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -35,6 +35,8 @@ class Namespace < ApplicationRecord
belongs_to :parent, class_name: "Namespace"
has_many :children, class_name: "Namespace", foreign_key: :parent_id
has_one :chat_team, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_one :root_storage_statistics, class_name: 'Namespace::RootStorageStatistics'
+ has_one :aggregation_schedule, class_name: 'Namespace::AggregationSchedule'
validates :owner, presence: true, unless: ->(n) { n.type == "Group" }
validates :name,
diff --git a/app/models/namespace/aggregation_schedule.rb b/app/models/namespace/aggregation_schedule.rb
new file mode 100644
index 00000000000..43afd0b954c
--- /dev/null
+++ b/app/models/namespace/aggregation_schedule.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Namespace::AggregationSchedule < ApplicationRecord
+ self.primary_key = :namespace_id
+
+ belongs_to :namespace
+end
diff --git a/app/models/namespace/root_storage_statistics.rb b/app/models/namespace/root_storage_statistics.rb
new file mode 100644
index 00000000000..de28eb6b37f
--- /dev/null
+++ b/app/models/namespace/root_storage_statistics.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class Namespace::RootStorageStatistics < ApplicationRecord
+ self.primary_key = :namespace_id
+
+ belongs_to :namespace
+ has_one :route, through: :namespace
+
+ delegate :all_projects, to: :namespace
+end
diff --git a/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb b/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb
index 3413a9e4612..58f795e639e 100644
--- a/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb
+++ b/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb
@@ -2,6 +2,14 @@
module PagesDomains
class ObtainLetsEncryptCertificateService
+ # time for processing validation requests for acme challenges
+ # 5-15 seconds is usually enough
+ CHALLENGE_PROCESSING_DELAY = 1.minute.freeze
+
+ # time LetsEncrypt ACME server needs to generate the certificate
+ # no particular SLA, usually takes 10-15 seconds
+ CERTIFICATE_PROCESSING_DELAY = 1.minute.freeze
+
attr_reader :pages_domain
def initialize(pages_domain)
@@ -14,6 +22,7 @@ module PagesDomains
unless acme_order
::PagesDomains::CreateAcmeOrderService.new(pages_domain).execute
+ PagesDomainSslRenewalWorker.perform_in(CHALLENGE_PROCESSING_DELAY, pages_domain.id)
return
end
@@ -23,6 +32,7 @@ module PagesDomains
case api_order.status
when 'ready'
api_order.request_certificate(private_key: acme_order.private_key, domain: pages_domain.domain)
+ PagesDomainSslRenewalWorker.perform_in(CERTIFICATE_PROCESSING_DELAY, pages_domain.id)
when 'valid'
save_certificate(acme_order.private_key, api_order)
acme_order.destroy!
diff --git a/app/services/projects/propagate_service_template.rb b/app/services/projects/propagate_service_template.rb
index a2f36d2bd1b..a25c985585b 100644
--- a/app/services/projects/propagate_service_template.rb
+++ b/app/services/projects/propagate_service_template.rb
@@ -24,7 +24,7 @@ module Projects
def propagate_projects_with_template
loop do
- batch = project_ids_batch
+ batch = Project.uncached { project_ids_batch }
bulk_create_from_template(batch) unless batch.empty?
diff --git a/app/views/admin/application_settings/_logging.html.haml b/app/views/admin/application_settings/_logging.html.haml
deleted file mode 100644
index d57066bba01..00000000000
--- a/app/views/admin/application_settings/_logging.html.haml
+++ /dev/null
@@ -1,38 +0,0 @@
-= form_for @application_setting, url: reporting_admin_application_settings_path(anchor: 'js-logging-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting)
-
- %p
- %strong
- NOTE:
- These settings will be removed from the UI in a GitLab 12.0 release and made available within gitlab.yml.
- In addition, you will be able to define a Sentry Environment to differentiate between multiple deployments. For example, development, staging, and production.
-
- %fieldset
- .form-group
- .form-check
- = f.check_box :sentry_enabled, class: 'form-check-input'
- = f.label :sentry_enabled, class: 'form-check-label' do
- Enable Sentry
- .form-text.text-muted
- %p This setting requires a restart to take effect.
- Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here:
- %a{ href: 'https://getsentry.com', target: '_blank', rel: 'noopener noreferrer' } https://getsentry.com
-
- .form-group
- = f.label :sentry_dsn, 'Sentry DSN', class: 'label-bold'
- = f.text_field :sentry_dsn, class: 'form-control'
-
- .form-group
- .form-check
- = f.check_box :clientside_sentry_enabled, class: 'form-check-input'
- = f.label :clientside_sentry_enabled, class: 'form-check-label' do
- Enable Clientside Sentry
- .form-text.text-muted
- Sentry can also be used for reporting and logging clientside exceptions.
- %a{ href: 'https://sentry.io/for/javascript/', target: '_blank', rel: 'noopener noreferrer' } https://sentry.io/for/javascript/
-
- .form-group
- = f.label :clientside_sentry_dsn, 'Clientside Sentry DSN', class: 'label-bold'
- = f.text_field :clientside_sentry_dsn, class: 'form-control'
-
- = f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/reporting.html.haml b/app/views/admin/application_settings/reporting.html.haml
index 1c2d9ccdb2d..46e3d1c4570 100644
--- a/app/views/admin/application_settings/reporting.html.haml
+++ b/app/views/admin/application_settings/reporting.html.haml
@@ -23,14 +23,3 @@
= _('Set notification email for abuse reports.')
.settings-content
= render 'abuse'
-
-%section.settings.as-logging.no-animate#js-logging-settings{ class: ('expanded' if expanded_by_default?) }
- .settings-header
- %h4
- = _('Error Reporting and Logging')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded_by_default? ? _('Collapse') : _('Expand')
- %p
- = _('Enable Sentry for error reporting and logging.')
- .settings-content
- = render 'logging'
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 7535aee83a3..20b844f9fd8 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -46,7 +46,7 @@
= yield :library_javascripts
= javascript_include_tag locale_path unless I18n.locale == :en
- = webpack_bundle_tag "raven" if Gitlab::CurrentSettings.clientside_sentry_enabled
+ = webpack_bundle_tag "raven" if Gitlab.config.sentry.enabled
- if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts
diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index 4c18398e3dc..65ef9690062 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -1,5 +1,5 @@
-- breadcrumb_title "Access Tokens"
-- page_title "Personal Access Tokens"
+- breadcrumb_title s_('AccessTokens|Access Tokens')
+- page_title s_('AccessTokens|Personal Access Tokens')
- @content_class = "limit-container-width" unless fluid_layout
.row.prepend-top-default
@@ -7,10 +7,10 @@
%h4.prepend-top-0
= page_title
%p
- You can generate a personal access token for each application you use that needs access to the GitLab API.
+ = s_('AccessTokens|You can generate a personal access token for each application you use that needs access to the GitLab API.')
%p
- You can also use personal access tokens to authenticate against Git over HTTP.
- They are the only accepted password when you have Two-Factor Authentication (2FA) enabled.
+ = s_('AccessTokens|You can also use personal access tokens to authenticate against Git over HTTP.')
+ = s_('AccessTokens|They are the only accepted password when you have Two-Factor Authentication (2FA) enabled.')
.col-lg-8
- if @new_personal_access_token
@@ -24,35 +24,33 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
- Feed token
+ = s_('AccessTokens|Feed token')
%p
- Your feed token is used to authenticate you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar, and is included in those feed URLs.
+ = s_('AccessTokens|Your feed token is used to authenticate you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar, and is included in those feed URLs.')
%p
- It cannot be used to access any other data.
+ = s_('AccessTokens|It cannot be used to access any other data.')
.col-lg-8.feed-token-reset
- = label_tag :feed_token, 'Feed token', class: "label-bold"
+ = label_tag :feed_token, s_('AccessTokens|Feed token'), class: "label-bold"
= text_field_tag :feed_token, current_user.feed_token, class: 'form-control', readonly: true, onclick: 'this.select()'
%p.form-text.text-muted
- Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds or your calendar feed as if they were you.
- You should
- = link_to 'reset it', [:reset, :feed_token, :profile], method: :put, data: { confirm: 'Are you sure? Any RSS or calendar URLs currently in use will stop working.' }
- if that ever happens.
+ - reset_link = link_to s_('AccessTokens|reset it'), [:reset, :feed_token, :profile], method: :put, data: { confirm: s_('AccessTokens|Are you sure? Any RSS or calendar URLs currently in use will stop working.') }
+ - reset_message = s_('AccessTokens|Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds or your calendar feed as if they were you. You should %{link_reset_it} if that ever happens.') % { link_reset_it: reset_link }
+ = reset_message.html_safe
- if incoming_email_token_enabled?
%hr
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
- Incoming email token
+ = s_('AccessTokens|Incoming email token')
%p
- Your incoming email token is used to authenticate you when you create a new issue by email, and is included in your personal project-specific email addresses.
+ = s_('AccessTokens|Your incoming email token is used to authenticate you when you create a new issue by email, and is included in your personal project-specific email addresses.')
%p
- It cannot be used to access any other data.
+ = s_('AccessTokens|It cannot be used to access any other data.')
.col-lg-8.incoming-email-token-reset
- = label_tag :incoming_email_token, 'Incoming email token', class: "label-bold"
+ = label_tag :incoming_email_token, s_('AccessTokens|Incoming email token'), class: "label-bold"
= text_field_tag :incoming_email_token, current_user.incoming_email_token, class: 'form-control', readonly: true, onclick: 'this.select()'
%p.form-text.text-muted
- Keep this token secret. Anyone who gets ahold of it can create issues as if they were you.
- You should
- = link_to 'reset it', [:reset, :incoming_email_token, :profile], method: :put, data: { confirm: 'Are you sure? Any issue email addresses currently in use will stop working.' }
- if that ever happens.
+ - reset_link = link_to s_('AccessTokens|reset it'), [:reset, :incoming_email_token, :profile], method: :put, data: { confirm: s_('AccessTokens|Are you sure? Any issue email addresses currently in use will stop working.') }
+ - reset_message = s_('AccessTokens|Keep this token secret. Anyone who gets ahold of it can create issues as if they were you. You should %{link_reset_it} if that ever happens.') % { link_reset_it: reset_link }
+ = reset_message.html_safe
diff --git a/app/views/shared/_personal_access_tokens_created_container.html.haml b/app/views/shared/_personal_access_tokens_created_container.html.haml
index a8d3de66418..42989b145a2 100644
--- a/app/views/shared/_personal_access_tokens_created_container.html.haml
+++ b/app/views/shared/_personal_access_tokens_created_container.html.haml
@@ -1,5 +1,5 @@
-- container_title = local_assigns.fetch(:container_title, 'Your New Personal Access Token')
-- clipboard_button_title = local_assigns.fetch(:clipboard_button_title, 'Copy personal access token to clipboard')
+- container_title = local_assigns.fetch(:container_title, _('Your New Personal Access Token'))
+- clipboard_button_title = local_assigns.fetch(:clipboard_button_title, _('Copy personal access token to clipboard'))
.created-personal-access-token-container
%h5.prepend-top-0
@@ -9,6 +9,7 @@
= text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: "qa-created-personal-access-token form-control js-select-on-focus", 'aria-describedby' => "created-token-help-block"
%span.input-group-append
= clipboard_button(text: new_token_value, title: clipboard_button_title, placement: "left", class: "input-group-text btn-default btn-clipboard")
- %span#created-token-help-block.form-text.text-muted.text-danger Make sure you save it - you won't be able to access it again.
+ %span#created-token-help-block.form-text.text-muted.text-danger
+ = _("Make sure you save it - you won't be able to access it again.")
%hr
diff --git a/app/views/shared/_personal_access_tokens_form.html.haml b/app/views/shared/_personal_access_tokens_form.html.haml
index 0891b3459ec..1d96feda3b0 100644
--- a/app/views/shared/_personal_access_tokens_form.html.haml
+++ b/app/views/shared/_personal_access_tokens_form.html.haml
@@ -1,9 +1,9 @@
-- type = impersonation ? "impersonation" : "personal access"
+- type = impersonation ? s_('Profiles|impersonation') : s_('Profiles|personal access')
%h5.prepend-top-0
- Add a #{type} token
+ = _('Add a %{type} token') % { type: type }
%p.profile-settings-content
- Pick a name for the application, and we'll give you a unique #{type} token.
+ = _("Pick a name for the application, and we'll give you a unique %{type} token.") % { type: type }
= form_for token, url: path, method: :post, html: { class: 'js-requires-input' } do |f|
@@ -11,19 +11,19 @@
.row
.form-group.col-md-6
- = f.label :name, class: 'label-bold'
+ = f.label :name, _('Name'), class: 'label-bold'
= f.text_field :name, class: "form-control qa-personal-access-token-name-field", required: true
.row
.form-group.col-md-6
- = f.label :expires_at, class: 'label-bold'
+ = f.label :expires_at, _('Expires at'), class: 'label-bold'
.input-icon-wrapper
= f.text_field :expires_at, class: "datepicker form-control", placeholder: 'YYYY-MM-DD'
= icon('calendar', { class: 'input-icon-right' })
.form-group
- = f.label :scopes, class: 'label-bold'
+ = f.label :scopes, _('Scopes'), class: 'label-bold'
= render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: token, scopes: scopes
.prepend-top-default
- = f.submit "Create #{type} token", class: "btn btn-success qa-create-token-button"
+ = f.submit _('Create %{type} token') % { type: type }, class: "btn btn-success qa-create-token-button"
diff --git a/app/views/shared/_personal_access_tokens_table.html.haml b/app/views/shared/_personal_access_tokens_table.html.haml
index 49f3aae0f98..823117f37ca 100644
--- a/app/views/shared/_personal_access_tokens_table.html.haml
+++ b/app/views/shared/_personal_access_tokens_table.html.haml
@@ -1,20 +1,21 @@
-- type = impersonation ? "Impersonation" : "Personal Access"
+- type = impersonation ? s_('Profiles|Impersonation') : s_('Profiles|Personal Access')
%hr
-%h5 Active #{type} Tokens (#{active_tokens.length})
+%h5
+ = _('Active %{type} Tokens (%{token_length})') % { type: type, token_length: active_tokens.length }
- if impersonation
%p.profile-settings-content
- To see all the user's personal access tokens you must impersonate them first.
+ = _("To see all the user's personal access tokens you must impersonate them first.")
- if active_tokens.present?
.table-responsive
%table.table.active-tokens
%thead
%tr
- %th Name
- %th Created
- %th Expires
- %th Scopes
+ %th= _('Name')
+ %th= s_('AccessTokens|Created')
+ %th= _('Expires')
+ %th= _('Scopes')
%th
%tbody
- active_tokens.each do |token|
@@ -26,10 +27,10 @@
%span{ class: ('text-warning' if token.expires_soon?) }
In #{distance_of_time_in_words_to_now(token.expires_at)}
- else
- %span.token-never-expires-label Never
- %td= token.scopes.present? ? token.scopes.join(", ") : "<no scopes selected>"
+ %span.token-never-expires-label= _('Never')
+ %td= token.scopes.present? ? token.scopes.join(", ") : _('<no scopes selected>')
- path = impersonation ? revoke_admin_user_impersonation_token_path(token.user, token) : revoke_profile_personal_access_token_path(token)
- %td= link_to "Revoke", path, method: :put, class: "btn btn-danger float-right qa-revoke-button", data: { confirm: "Are you sure you want to revoke this #{type} Token? This action cannot be undone." }
+ %td= link_to _('Revoke'), path, method: :put, class: "btn btn-danger float-right qa-revoke-button", data: { confirm: _('Are you sure you want to revoke this %{type} Token? This action cannot be undone.') % { type: type } }
- else
.settings-message.text-center
- This user has no active #{type} Tokens.
+ = _('This user has no active %{type} Tokens.') % { type: type }
diff --git a/app/views/shared/tokens/_scopes_list.html.haml b/app/views/shared/tokens/_scopes_list.html.haml
index f99e905e95c..428861485b4 100644
--- a/app/views/shared/tokens/_scopes_list.html.haml
+++ b/app/views/shared/tokens/_scopes_list.html.haml
@@ -4,7 +4,7 @@
%tr
%td
- Scopes
+ = _('Scopes')
%td
%ul.scopes-list.append-bottom-0
- token.scopes.each do |scope|
diff --git a/changelogs/unreleased/51952-forking-via-webide.yml b/changelogs/unreleased/51952-forking-via-webide.yml
new file mode 100644
index 00000000000..4497c6b6ca4
--- /dev/null
+++ b/changelogs/unreleased/51952-forking-via-webide.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve "500 error when forking via the web IDE button"
+merge_request: 29909
+author:
+type: fixed
diff --git a/changelogs/unreleased/58802-rename-webide.yml b/changelogs/unreleased/58802-rename-webide.yml
new file mode 100644
index 00000000000..40471d967ce
--- /dev/null
+++ b/changelogs/unreleased/58802-rename-webide.yml
@@ -0,0 +1,5 @@
+---
+title: Re-name files in Web IDE in a more natural way
+merge_request: 29948
+author:
+type: changed
diff --git a/changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml b/changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml
new file mode 100644
index 00000000000..0b8d301352b
--- /dev/null
+++ b/changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml
@@ -0,0 +1,5 @@
+---
+title: Enable terminals for instance and group clusters
+merge_request: 28613
+author:
+type: added
diff --git a/changelogs/unreleased/62938-wcag-aa-edited-text-color.yml b/changelogs/unreleased/62938-wcag-aa-edited-text-color.yml
new file mode 100644
index 00000000000..6652e495869
--- /dev/null
+++ b/changelogs/unreleased/62938-wcag-aa-edited-text-color.yml
@@ -0,0 +1,5 @@
+---
+title: Use darker gray color for system note metadata and edited text
+merge_request: 30054
+author:
+type: other
diff --git a/changelogs/unreleased/63247-add-conf-toast-and-link.yml b/changelogs/unreleased/63247-add-conf-toast-and-link.yml
new file mode 100644
index 00000000000..915cc20dcc8
--- /dev/null
+++ b/changelogs/unreleased/63247-add-conf-toast-and-link.yml
@@ -0,0 +1,5 @@
+---
+title: Include a link back to the MR for Visual Review feedback form
+merge_request: 29719
+author:
+type: changed
diff --git a/changelogs/unreleased/always-allow-prometheus-access-in-dev.yml b/changelogs/unreleased/always-allow-prometheus-access-in-dev.yml
new file mode 100644
index 00000000000..acd944ea684
--- /dev/null
+++ b/changelogs/unreleased/always-allow-prometheus-access-in-dev.yml
@@ -0,0 +1,5 @@
+---
+title: Always allow access to health endpoints from localhost in dev
+merge_request: 29930
+author:
+type: other
diff --git a/changelogs/unreleased/always-display-environment-selector.yml b/changelogs/unreleased/always-display-environment-selector.yml
new file mode 100644
index 00000000000..7a55e8f3e5d
--- /dev/null
+++ b/changelogs/unreleased/always-display-environment-selector.yml
@@ -0,0 +1,5 @@
+---
+title: Fix broken environment selector and always display it on monitoring dashboard
+merge_request: 29705
+author:
+type: fixed
diff --git a/changelogs/unreleased/dz-remove-deprecated-user-routes.yml b/changelogs/unreleased/dz-remove-deprecated-user-routes.yml
new file mode 100644
index 00000000000..92c2e39dd20
--- /dev/null
+++ b/changelogs/unreleased/dz-remove-deprecated-user-routes.yml
@@ -0,0 +1,5 @@
+---
+title: Remove depreated /u/:username routing
+merge_request: 30044
+author:
+type: removed
diff --git a/changelogs/unreleased/refactor-sentry.yml b/changelogs/unreleased/refactor-sentry.yml
new file mode 100644
index 00000000000..25c5534fae0
--- /dev/null
+++ b/changelogs/unreleased/refactor-sentry.yml
@@ -0,0 +1,5 @@
+---
+title: Remove Sentry from application settings
+merge_request: 28447
+author: Roger Meier
+type: added
diff --git a/changelogs/unreleased/sh-cache-negative-entries-find-commit.yml b/changelogs/unreleased/sh-cache-negative-entries-find-commit.yml
new file mode 100644
index 00000000000..98eb13ee620
--- /dev/null
+++ b/changelogs/unreleased/sh-cache-negative-entries-find-commit.yml
@@ -0,0 +1,5 @@
+---
+title: Allow caching of negative FindCommit matches
+merge_request: 29952
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-service-template-bug.yml b/changelogs/unreleased/sh-service-template-bug.yml
new file mode 100644
index 00000000000..1ea5ac84f26
--- /dev/null
+++ b/changelogs/unreleased/sh-service-template-bug.yml
@@ -0,0 +1,5 @@
+---
+title: Disable Rails SQL query cache when applying service templates
+merge_request: 30060
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-update-mermaid.yml b/changelogs/unreleased/sh-update-mermaid.yml
new file mode 100644
index 00000000000..9a7726cf716
--- /dev/null
+++ b/changelogs/unreleased/sh-update-mermaid.yml
@@ -0,0 +1,5 @@
+---
+title: Update Mermaid to 8.1.0
+merge_request: 30036
+author:
+type: fixed
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 4b0bb86e42a..9e74a67b73f 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -368,7 +368,7 @@ Settings.cron_jobs['pages_domain_removal_cron_worker']['cron'] ||= '47 0 * * *'
Settings.cron_jobs['pages_domain_removal_cron_worker']['job_class'] = 'PagesDomainRemovalCronWorker'
Settings.cron_jobs['pages_domain_ssl_renewal_cron_worker'] ||= Settingslogic.new({})
-Settings.cron_jobs['pages_domain_ssl_renewal_cron_worker']['cron'] ||= '*/5 * * * *'
+Settings.cron_jobs['pages_domain_ssl_renewal_cron_worker']['cron'] ||= '*/10 * * * *'
Settings.cron_jobs['pages_domain_ssl_renewal_cron_worker']['job_class'] = 'PagesDomainSslRenewalCronWorker'
Settings.cron_jobs['issue_due_scheduler_worker'] ||= Settingslogic.new({})
diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb
index e5589ce0ad1..fcc6bfa5c92 100644
--- a/config/initializers/sentry.rb
+++ b/config/initializers/sentry.rb
@@ -3,18 +3,11 @@
require 'gitlab/current_settings'
def configure_sentry
- # allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done
- begin
- sentry_enabled = Gitlab::CurrentSettings.current_application_settings.sentry_enabled
- rescue
- sentry_enabled = false
- end
-
- if sentry_enabled
+ if Gitlab::Sentry.enabled?
Raven.configure do |config|
- config.dsn = Gitlab::CurrentSettings.current_application_settings.sentry_dsn
+ config.dsn = Gitlab.config.sentry.dsn
config.release = Gitlab.revision
- config.current_environment = Gitlab.config.sentry.environment.presence
+ config.current_environment = Gitlab.config.sentry.environment
# Sanitize fields based on those sanitized from Rails.
config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s)
diff --git a/config/routes/snippets.rb b/config/routes/snippets.rb
index 81bc890d86b..ba6da3ac57e 100644
--- a/config/routes/snippets.rb
+++ b/config/routes/snippets.rb
@@ -17,5 +17,5 @@ resources :snippets, concerns: :awardable do
end
end
-get '/s/:username', to: redirect('u/%{username}/snippets'),
+get '/s/:username', to: redirect('users/%{username}/snippets'),
constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }
diff --git a/config/routes/user.rb b/config/routes/user.rb
index e0ae264e2c0..f93a3fc6161 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -48,15 +48,6 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d
get :activity
get '/', to: redirect('%{username}'), as: nil
end
-
- # Compatibility with old routing
- # TODO (dzaporozhets): remove in 10.0
- get '/u/:username', to: redirect('%{username}')
- # TODO (dzaporozhets): remove in 9.0
- get '/u/:username/groups', to: redirect('users/%{username}/groups')
- get '/u/:username/projects', to: redirect('users/%{username}/projects')
- get '/u/:username/snippets', to: redirect('users/%{username}/snippets')
- get '/u/:username/contributed', to: redirect('users/%{username}/contributed')
end
constraints(::Constraints::UserUrlConstrainer.new) do
diff --git a/db/migrate/20190531153110_create_namespace_root_storage_statistics.rb b/db/migrate/20190531153110_create_namespace_root_storage_statistics.rb
new file mode 100644
index 00000000000..702560d05cc
--- /dev/null
+++ b/db/migrate/20190531153110_create_namespace_root_storage_statistics.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class CreateNamespaceRootStorageStatistics < ActiveRecord::Migration[5.1]
+ DOWNTIME = false
+
+ def change
+ create_table :namespace_root_storage_statistics, id: false, primary_key: :namespace_id do |t|
+ t.integer :namespace_id, null: false, primary_key: true
+ t.datetime_with_timezone :updated_at, null: false
+
+ t.bigint :repository_size, null: false, default: 0
+ t.bigint :lfs_objects_size, null: false, default: 0
+ t.bigint :wiki_size, null: false, default: 0
+ t.bigint :build_artifacts_size, null: false, default: 0
+ t.bigint :storage_size, null: false, default: 0
+ t.bigint :packages_size, null: false, default: 0
+
+ t.index :namespace_id, unique: true
+ t.foreign_key :namespaces, column: :namespace_id, on_delete: :cascade
+ end
+ end
+end
diff --git a/db/migrate/20190605184422_create_namespace_aggregation_schedules.rb b/db/migrate/20190605184422_create_namespace_aggregation_schedules.rb
new file mode 100644
index 00000000000..5e8cb616cc1
--- /dev/null
+++ b/db/migrate/20190605184422_create_namespace_aggregation_schedules.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class CreateNamespaceAggregationSchedules < ActiveRecord::Migration[5.1]
+ DOWNTIME = false
+
+ def change
+ create_table :namespace_aggregation_schedules, id: false, primary_key: :namespace_id do |t|
+ t.integer :namespace_id, null: false, primary_key: true
+
+ t.index :namespace_id, unique: true
+ t.foreign_key :namespaces, column: :namespace_id, on_delete: :cascade
+ end
+ end
+end
diff --git a/db/post_migrate/20190625184066_remove_sentry_from_application_settings.rb b/db/post_migrate/20190625184066_remove_sentry_from_application_settings.rb
new file mode 100644
index 00000000000..427df343193
--- /dev/null
+++ b/db/post_migrate/20190625184066_remove_sentry_from_application_settings.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveSentryFromApplicationSettings < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ SENTRY_ENABLED_COLUMNS = [
+ :sentry_enabled,
+ :clientside_sentry_enabled
+ ].freeze
+
+ SENTRY_DSN_COLUMNS = [
+ :sentry_dsn,
+ :clientside_sentry_dsn
+ ].freeze
+
+ disable_ddl_transaction!
+
+ def up
+ (SENTRY_ENABLED_COLUMNS + SENTRY_DSN_COLUMNS).each do |column|
+ remove_column(:application_settings, column) if column_exists?(:application_settings, column)
+ end
+ end
+
+ def down
+ SENTRY_ENABLED_COLUMNS.each do |column|
+ add_column_with_default(:application_settings, column, :boolean, default: false, allow_null: false) unless column_exists?(:application_settings, column)
+ end
+
+ SENTRY_DSN_COLUMNS.each do |column|
+ add_column(:application_settings, column, :string) unless column_exists?(:application_settings, column)
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a94e5142627..054dbc7201f 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20190620112608) do
+ActiveRecord::Schema.define(version: 20190625184066) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -93,8 +93,6 @@ ActiveRecord::Schema.define(version: 20190620112608) do
t.boolean "akismet_enabled", default: false
t.string "akismet_api_key"
t.integer "metrics_sample_interval", default: 15
- t.boolean "sentry_enabled", default: false
- t.string "sentry_dsn"
t.boolean "email_author_in_body", default: false
t.integer "default_group_visibility"
t.boolean "repository_checks_enabled", default: false
@@ -135,8 +133,6 @@ ActiveRecord::Schema.define(version: 20190620112608) do
t.string "uuid"
t.decimal "polling_interval_multiplier", default: "1.0", null: false
t.integer "cached_markdown_version"
- t.boolean "clientside_sentry_enabled", default: false, null: false
- t.string "clientside_sentry_dsn"
t.boolean "prometheus_metrics_enabled", default: true, null: false
t.boolean "help_page_hide_commercial_content", default: false
t.string "help_page_support_url"
@@ -2055,6 +2051,21 @@ ActiveRecord::Schema.define(version: 20190620112608) do
t.index ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
end
+ create_table "namespace_aggregation_schedules", primary_key: "namespace_id", id: :integer, default: nil, force: :cascade do |t|
+ t.index ["namespace_id"], name: "index_namespace_aggregation_schedules_on_namespace_id", unique: true, using: :btree
+ end
+
+ create_table "namespace_root_storage_statistics", primary_key: "namespace_id", id: :integer, default: nil, force: :cascade do |t|
+ t.datetime_with_timezone "updated_at", null: false
+ t.bigint "repository_size", default: 0, null: false
+ t.bigint "lfs_objects_size", default: 0, null: false
+ t.bigint "wiki_size", default: 0, null: false
+ t.bigint "build_artifacts_size", default: 0, null: false
+ t.bigint "storage_size", default: 0, null: false
+ t.bigint "packages_size", default: 0, null: false
+ t.index ["namespace_id"], name: "index_namespace_root_storage_statistics_on_namespace_id", unique: true, using: :btree
+ end
+
create_table "namespace_statistics", id: :serial, force: :cascade do |t|
t.integer "namespace_id", null: false
t.integer "shared_runners_seconds", default: 0, null: false
@@ -3757,6 +3768,8 @@ ActiveRecord::Schema.define(version: 20190620112608) do
add_foreign_key "merge_trains", "users", on_delete: :cascade
add_foreign_key "milestones", "namespaces", column: "group_id", name: "fk_95650a40d4", on_delete: :cascade
add_foreign_key "milestones", "projects", name: "fk_9bd0a0c791", on_delete: :cascade
+ add_foreign_key "namespace_aggregation_schedules", "namespaces", on_delete: :cascade
+ add_foreign_key "namespace_root_storage_statistics", "namespaces", on_delete: :cascade
add_foreign_key "namespace_statistics", "namespaces", on_delete: :cascade
add_foreign_key "namespaces", "namespaces", column: "custom_project_templates_group_id", name: "fk_e7a0b20a6b", on_delete: :nullify
add_foreign_key "namespaces", "plans", name: "fk_fdd12e5b80", on_delete: :nullify
diff --git a/doc/README.md b/doc/README.md
index 3863e17c268..cfda2c9293d 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -54,7 +54,7 @@ GitLab provides solutions for [all the stages of the DevOps lifecycle](https://a
![DevOps Stages](img/devops-stages.png)
GitLab is like a top-of-the-line kitchen for making software. As the executive
-chef, you decide what software you want serve. Using your recipe, GitLab handles
+chef, you decide what software you want to serve. Using your recipe, GitLab handles
all the prep work, cooking, and delivery, so you can turn around orders faster
than ever.
diff --git a/doc/administration/high_availability/gitaly.md b/doc/administration/high_availability/gitaly.md
index 90e5f71d835..b7eaa4ce105 100644
--- a/doc/administration/high_availability/gitaly.md
+++ b/doc/administration/high_availability/gitaly.md
@@ -2,13 +2,13 @@
Gitaly does not yet support full high availability. However, Gitaly is quite
stable and is in use on GitLab.com. Scaled and highly available GitLab environments
-should consider using Gitaly on a separate node.
+should consider using Gitaly on a separate node.
-See the [Gitaly HA Epic](https://gitlab.com/groups/gitlab-org/-/epics/289) to
-track plans and progress toward high availability support.
+See the [Gitaly HA Epic](https://gitlab.com/groups/gitlab-org/-/epics/289) to
+track plans and progress toward high availability support.
This document is relevant for [Scaled Architecture](README.md#scalable-architecture-examples)
-environments and [High Availability Architecture](README.md#high-availability-architecture-examples).
+environments and [High Availability Architecture](README.md#high-availability-architecture-examples).
## Running Gitaly on its own server
@@ -24,23 +24,25 @@ Continue configuration of other components by going back to:
> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/3786) in GitLab 12.0.
- 1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration:
+1. Make sure to collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z`
- ```ruby
- # Enable service discovery for Prometheus
- consul['enable'] = true
- consul['monitoring_service_discovery'] = true
+1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration:
- # Replace placeholders
- # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
- # with the addresses of the Consul server nodes
- consul['configuration'] = {
- retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z),
- }
+ ```ruby
+ # Enable service discovery for Prometheus
+ consul['enable'] = true
+ consul['monitoring_service_discovery'] = true
- # Set the network addresses that the exporters will listen on
- node_exporter['listen_address'] = '0.0.0.0:9100'
- gitaly['prometheus_listen_addr'] = "0.0.0.0:9236"
- ```
+ # Replace placeholders
+ # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
+ # with the addresses of the Consul server nodes
+ consul['configuration'] = {
+ retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z),
+ }
- 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
+ # Set the network addresses that the exporters will listen on
+ node_exporter['listen_address'] = '0.0.0.0:9100'
+ gitaly['prometheus_listen_addr'] = "0.0.0.0:9236"
+ ```
+
+1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md
index 0e655e49922..3045be616a6 100644
--- a/doc/administration/high_availability/gitlab.md
+++ b/doc/administration/high_availability/gitlab.md
@@ -138,6 +138,8 @@ need some extra configuration.
If you enable Monitoring, it must be enabled on **all** GitLab servers.
+1. Make sure to collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z`
+
1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration:
```ruby
@@ -158,12 +160,11 @@ If you enable Monitoring, it must be enabled on **all** GitLab servers.
sidekiq['listen_address'] = "0.0.0.0"
unicorn['listen'] = '0.0.0.0'
- # Add the monitoring node's IP address to the monitoring whitelist and allow it to scrape the NGINX metrics
- # Replace placeholder
- # monitoring.gitlab.example.com
- # with the addresses gathered for the monitoring node
- gitlab_rails['monitoring_whitelist'] = ['monitoring.gitlab.example.com']
- nginx['status']['options']['allow'] = ['monitoring.gitlab.example.com']
+ # Add the monitoring node's IP address to the monitoring whitelist and allow it to
+ # scrape the NGINX metrics. Replace placeholder `monitoring.gitlab.example.com` with
+ # the address and/or subnets gathered from the monitoring node(s).
+ gitlab_rails['monitoring_whitelist'] = ['monitoring.gitlab.example.com', '127.0.0.0/8']
+ nginx['status']['options']['allow'] = ['monitoring.gitlab.example.com', '127.0.0.0/8']
```
1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
diff --git a/doc/administration/high_availability/monitoring_node.md b/doc/administration/high_availability/monitoring_node.md
index d16bf7dc0f0..ef415dde10a 100644
--- a/doc/administration/high_availability/monitoring_node.md
+++ b/doc/administration/high_availability/monitoring_node.md
@@ -16,6 +16,8 @@ Omnibus:
package you want using **steps 1 and 2** from the GitLab downloads page.
- Do not complete any other steps on the download page.
+1. Make sure to collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z`
+
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
```ruby
diff --git a/doc/administration/high_availability/pgbouncer.md b/doc/administration/high_availability/pgbouncer.md
index 762179cf756..053dae25823 100644
--- a/doc/administration/high_availability/pgbouncer.md
+++ b/doc/administration/high_availability/pgbouncer.md
@@ -62,6 +62,33 @@ See our [HA documentation for PostgreSQL](database.md) for information on runnin
1. At this point, your instance should connect to the database through pgbouncer. If you are having issues, see the [Troubleshooting](#troubleshooting) section
+## Enable Monitoring
+
+> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/3786) in GitLab 12.0.
+
+ If you enable Monitoring, it must be enabled on **all** pgbouncer servers.
+
+ 1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration:
+
+ ```ruby
+ # Enable service discovery for Prometheus
+ consul['enable'] = true
+ consul['monitoring_service_discovery'] = true
+
+ # Replace placeholders
+ # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
+ # with the addresses of the Consul server nodes
+ consul['configuration'] = {
+ retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z),
+ }
+
+ # Set the network addresses that the exporters will listen on
+ node_exporter['listen_address'] = '0.0.0.0:9100'
+ pgbouncer_exporter['listen_address'] = '0.0.0.0:9188'
+ ```
+
+ 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
+
### Interacting with pgbouncer
#### Administrative console
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index f61a8834af3..8621224272c 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -22,10 +22,10 @@ environments including [Basic Scaling](README.md#basic-scaling) and
### Provide your own Redis instance **[CORE ONLY]**
-If you want to use your own deployed Redis instance(s),
-see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only)
-for more details. However, you can use the GitLab Omnibus package to easily
-deploy the bundled Redis.
+If you want to use your own deployed Redis instance(s),
+see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only)
+for more details. However, you can use the GitLab Omnibus package to easily
+deploy the bundled Redis.
### Standalone Redis using GitLab Omnibus **[CORE ONLY]**
@@ -62,11 +62,11 @@ Omnibus:
pgbouncer_exporter['enable'] = false
gitlab_monitor['enable'] = false
gitaly['enable'] = false
-
+
redis['bind'] = '0.0.0.0'
redis['port'] = '6379'
redis['password'] = 'SECRET_PASSWORD_HERE'
-
+
gitlab_rails['auto_migrate'] = false
```
@@ -74,7 +74,7 @@ Omnibus:
1. Note the Redis node's IP address or hostname, port, and
Redis password. These will be necessary when configuring the GitLab
application servers later.
-1. [Enable Monitoring](#enable-monitoring)
+1. [Enable Monitoring](#enable-monitoring)
Advanced configuration options are supported and can be added if
needed.
@@ -91,10 +91,10 @@ environments including [Horizontal](README.md#horizontal),
### Provide your own Redis instance **[CORE ONLY]**
-If you want to use your own deployed Redis instance(s),
-see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only)
-for more details. However, you can use the GitLab Omnibus package to easily
-deploy the bundled Redis.
+If you want to use your own deployed Redis instance(s),
+see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only)
+for more details. However, you can use the GitLab Omnibus package to easily
+deploy the bundled Redis.
### High Availability with GitLab Omnibus **[PREMIUM ONLY]**
@@ -368,7 +368,7 @@ The prerequisites for a HA Redis setup are the following:
```ruby
# Specify server role as 'redis_master_role'
roles ['redis_master_role']
-
+
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
# If you really need to bind to an external accessible IP, make
@@ -382,7 +382,7 @@ The prerequisites for a HA Redis setup are the following:
# Set up password authentication for Redis (use the same password in all nodes).
redis['password'] = 'redis-password-goes-here'
```
-
+
1. Only the primary GitLab application server should handle migrations. To
prevent database migrations from running on upgrade, add the following
@@ -394,8 +394,8 @@ The prerequisites for a HA Redis setup are the following:
1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect.
-> Note: You can specify multiple roles like sentinel and redis as:
-> roles ['redis_sentinel_role', 'redis_master_role']. Read more about high
+> Note: You can specify multiple roles like sentinel and redis as:
+> roles ['redis_sentinel_role', 'redis_master_role']. Read more about high
> availability roles at https://docs.gitlab.com/omnibus/roles/
### Step 2. Configuring the slave Redis instances
@@ -412,7 +412,7 @@ The prerequisites for a HA Redis setup are the following:
```ruby
# Specify server role as 'redis_slave_role'
roles ['redis_slave_role']
-
+
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
# If you really need to bind to an external accessible IP, make
@@ -443,8 +443,8 @@ The prerequisites for a HA Redis setup are the following:
1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect.
1. Go through the steps again for all the other slave nodes.
-> Note: You can specify multiple roles like sentinel and redis as:
-> roles ['redis_sentinel_role', 'redis_slave_role']. Read more about high
+> Note: You can specify multiple roles like sentinel and redis as:
+> roles ['redis_sentinel_role', 'redis_slave_role']. Read more about high
> availability roles at https://docs.gitlab.com/omnibus/roles/
---
@@ -754,28 +754,30 @@ gitlab_rails['redis_sentinels'] = [
> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/3786) in GitLab 12.0.
- If you enable Monitoring, it must be enabled on **all** Redis servers.
+If you enable Monitoring, it must be enabled on **all** Redis servers.
+
+1. Make sure to collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z`
- 1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration:
+1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration:
- ```ruby
- # Enable service discovery for Prometheus
- consul['enable'] = true
- consul['monitoring_service_discovery'] = true
+ ```ruby
+ # Enable service discovery for Prometheus
+ consul['enable'] = true
+ consul['monitoring_service_discovery'] = true
- # Replace placeholders
- # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
- # with the addresses of the Consul server nodes
- consul['configuration'] = {
- retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z),
- }
+ # Replace placeholders
+ # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
+ # with the addresses of the Consul server nodes
+ consul['configuration'] = {
+ retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z),
+ }
- # Set the network addresses that the exporters will listen on
- node_exporter['listen_address'] = '0.0.0.0:9100'
- redis_exporter['listen_address'] = '0.0.0.0:9121'
- ```
+ # Set the network addresses that the exporters will listen on
+ node_exporter['listen_address'] = '0.0.0.0:9100'
+ redis_exporter['listen_address'] = '0.0.0.0:9121'
+ ```
- 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
+1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
## Advanced configuration
diff --git a/doc/administration/operations/extra_sidekiq_processes.md b/doc/administration/operations/extra_sidekiq_processes.md
index 286b99aceb5..7297507f599 100644
--- a/doc/administration/operations/extra_sidekiq_processes.md
+++ b/doc/administration/operations/extra_sidekiq_processes.md
@@ -1,70 +1,132 @@
# Extra Sidekiq processes **[STARTER ONLY]**
-GitLab Enterprise Edition allows one to start an extra set of Sidekiq processes
+NOTE: **Note:**
+The information in this page applies only to Omnibus GitLab.
+
+GitLab Starter allows one to start an extra set of Sidekiq processes
besides the default one. These processes can be used to consume a dedicated set
of queues. This can be used to ensure certain queues always have dedicated
workers, no matter the number of jobs that need to be processed.
-## Starting extra processes via Omnibus GitLab
+## Available Sidekiq queues
-To enable `sidekiq-cluster`, you must apply the `sidekiq_cluster['enable'] = true`
-setting `/etc/gitlab/gitlab.rb`:
+For a list of the existing Sidekiq queues, check the following files:
-```ruby
-sidekiq_cluster['enable'] = true
-```
+- [Queues for both GitLab Community and Enterprise Editions](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/app/workers/all_queues.yml)
+- [Queues for GitLab Enterprise Editions only](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/app/workers/all_queues.yml)
-You will then specify how many additional processes to create via `sidekiq-cluster`
-as well as which queues for them to handle. This is done via the
-`sidekiq_cluster['queue_groups']` setting. This is an array whose items contain
-which queues to process. Each item in the array will equate to one additional
-sidekiq process.
+Each entry in the above files represents a queue on which extra Sidekiq processes
+can be started.
-As an example, to make additional sidekiq processes that process the
-`elastic_indexer` and `mailers` queues, you would apply the following:
+## Starting extra processes
-```ruby
-sidekiq_cluster['queue_groups'] = [
- "elastic_indexer",
- "mailers"
-]
-```
+To start extra Sidekiq processes, you must enable `sidekiq-cluster`:
-To have an additional sidekiq process handle multiple queues, you simply put a
-comma after the first queue name and then put the next queue name:
+1. Edit `/etc/gitlab/gitlab.rb` and add:
-```ruby
-sidekiq_cluster['queue_groups'] = [
- "elastic_indexer,elastic_commit_indexer",
- "mailers"
-]
-```
+ ```ruby
+ sidekiq_cluster['enable'] = true
+ ```
-Keep in mind, all changes must be followed by reconfiguring your GitLab
-application via `sudo gitlab-ctl reconfigure`.
+1. You will then need to specify how many additional processes to create via `sidekiq-cluster`
+ and which queue they should handle via the `sidekiq_cluster['queue_groups']`
+ array setting. Each item in the array equates to one additional Sidekiq
+ process, and values in each item determine the queues it works on.
-### Monitoring
+ For example, the following setting adds additional Sidekiq processes to two
+ queues, one to `elastic_indexer` and one to `mailers`:
-Once the Sidekiq processes are added, you can visit the "Background Jobs"
+ ```ruby
+ sidekiq_cluster['queue_groups'] = [
+ "elastic_indexer",
+ "mailers"
+ ]
+ ```
+
+ To have an additional Sidekiq process handle multiple queues, add multiple
+ queue names to its item delimited by commas. For example:
+
+ ```ruby
+ sidekiq_cluster['queue_groups'] = [
+ "elastic_indexer, elastic_commit_indexer",
+ "mailers"
+ ]
+ ```
+
+1. Save the file and reconfigure GitLab for the changes to take effect:
+
+ ```sh
+ sudo gitlab-ctl reconfigure
+ ```
+
+Once the extra Sidekiq processes are added, you can visit the "Background Jobs"
section under the admin area in GitLab (`/admin/background_jobs`).
-![Extra sidekiq processes](img/sidekiq-cluster.png)
+![Extra Sidekiq processes](img/sidekiq-cluster.png)
-### All queues with exceptions
+## Negating settings
-To have the additional sidekiq processes work on every queue EXCEPT the ones
+To have the additional Sidekiq processes work on every queue **except** the ones
you list:
+1. After you follow the steps for [starting extra processes](#starting-extra-processes),
+ edit `/etc/gitlab/gitlab.rb` and add:
+
+ ```ruby
+ sidekiq_cluster['negate'] = true
+ ```
+
+1. Save the file and reconfigure GitLab for the changes to take effect:
+
+ ```sh
+ sudo gitlab-ctl reconfigure
+ ```
+
+## Ignore all GitHub import queues
+
+When [importing from GitHub](../../user/project/import/github.md), Sidekiq might
+use all of its resources to perform those operations. To set up a separate
+`sidekiq-cluster` process to ignore all GitHub import-related queues:
+
1. Edit `/etc/gitlab/gitlab.rb` and add:
```ruby
+ sidekiq_cluster['enable'] = true
sidekiq_cluster['negate'] = true
+ sidekiq_cluster['queue_groups'] = [
+ "github_import_advance_stage",
+ "github_importer:github_import_import_diff_note",
+ "github_importer:github_import_import_issue",
+ "github_importer:github_import_import_note",
+ "github_importer:github_import_import_lfs_object",
+ "github_importer:github_import_import_pull_request",
+ "github_importer:github_import_refresh_import_jid",
+ "github_importer:github_import_stage_finish_import",
+ "github_importer:github_import_stage_import_base_data",
+ "github_importer:github_import_stage_import_issues_and_diff_notes",
+ "github_importer:github_import_stage_import_notes",
+ "github_importer:github_import_stage_import_lfs_objects",
+ "github_importer:github_import_stage_import_pull_requests",
+ "github_importer:github_import_stage_import_repository"
+ ]
```
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Save the file and reconfigure GitLab for the changes to take effect:
+ ```sh
+ sudo gitlab-ctl reconfigure
+ ```
-### Limiting concurrency
+## Number of threads
+
+Each process defined under `sidekiq_cluster` starts with a
+number of threads that equals the number of queues, plus one spare thread.
+For example, a process that handles the `process_commit` and `post_receive`
+queues will use three threads in total.
+
+## Limiting concurrency
+
+To limit the concurrency of the Sidekiq processes:
1. Edit `/etc/gitlab/gitlab.rb` and add:
@@ -72,11 +134,22 @@ you list:
sidekiq_cluster['concurrency'] = 25
```
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Save the file and reconfigure GitLab for the changes to take effect:
-Keep in mind, this normally would not exceed the number of CPU cores available.
+ ```sh
+ sudo gitlab-ctl reconfigure
+ ```
-### Modifying the check interval
+For each queue group, the concurrency factor will be set to `min(number of queues, N)`.
+Setting the value to 0 will disable the limit. Keep in mind this normally would
+not exceed the number of CPU cores available.
+
+Each thread requires a Redis connection, so adding threads may
+increase Redis latency and potentially cause client timeouts. See the [Sidekiq
+documentation about Redis](https://github.com/mperham/sidekiq/wiki/Using-Redis)
+for more details.
+
+## Modifying the check interval
To modify the check interval for the additional Sidekiq processes:
@@ -90,9 +163,14 @@ To modify the check interval for the additional Sidekiq processes:
This tells the additional processes how often to check for enqueued jobs.
-## Starting extra processes via command line
+## Troubleshooting using the CLI
-Starting extra Sidekiq processes can be done using the command
+CAUTION: **Warning:**
+It's recommended to use `/etc/gitlab/gitlab.rb` to configure the Sidekiq processes.
+If you experience a problem, you should contact GitLab support. Use the command
+line at your own risk.
+
+For debugging purposes, you can start extra Sidekiq processes by using the command
`/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster`. This command
takes arguments using the following syntax:
@@ -111,29 +189,29 @@ see the relevant section in the
[Sidekiq style guide](../../development/sidekiq_style_guide.md#queue-namespaces).
For example, say you want to start 2 extra processes: one to process the
-"process_commit" queue, and one to process the "post_receive" queue. This can be
+`process_commit` queue, and one to process the `post_receive` queue. This can be
done as follows:
```bash
/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit post_receive
```
-If you instead want to start one process processing both queues you'd use the
+If you instead want to start one process processing both queues, you'd use the
following syntax:
```bash
/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive
```
-If you want to have one Sidekiq process process the "process_commit" and
-"post_receive" queues, and one process to process the "gitlab_shell" queue,
+If you want to have one Sidekiq process dealing with the `process_commit` and
+`post_receive` queues, and one process to process the `gitlab_shell` queue,
you'd use the following:
```bash
/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive gitlab_shell
```
-### Monitoring
+### Monitoring the `sidekiq-cluster` command
The `sidekiq-cluster` command will not terminate once it has started the desired
amount of Sidekiq processes. Instead, the process will continue running and
@@ -172,24 +250,24 @@ command and not the PID(s) of the started Sidekiq processes.
The Rails environment can be set by passing the `--environment` flag to the
`sidekiq-cluster` command, or by setting `RAILS_ENV` to a non-empty value. The
-default value is "development".
+default value can be found in `/opt/gitlab/etc/gitlab-rails/env/RAILS_ENV`.
-### All queues with exceptions
+### Using negation
You're able to run all queues in `sidekiq_queues.yml` file on a single or
multiple processes with exceptions using the `--negate` flag.
For example, say you want to run a single process for all queues,
-except "process_commit" and "post_receive". You can do so by executing:
+except `process_commit` and `post_receive`:
```bash
-sidekiq-cluster process_commit,post_receive --negate
+/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive --negate
```
-For multiple processes of all queues (except "process_commit" and "post_receive"):
+For multiple processes of all queues (except `process_commit` and `post_receive`):
```bash
-sidekiq-cluster process_commit,post_receive process_commit,post_receive --negate
+/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive process_commit,post_receive --negate
```
### Limiting concurrency
@@ -201,18 +279,3 @@ the `-m N` option. For example, this would cap the maximum number of threads to
```bash
/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive -m 1
```
-
-For each queue group, the concurrency factor will be set to min(number of
-queues, N). Setting the value to 0 will disable the limit.
-
-Note that each thread requires a Redis connection, so adding threads may
-increase Redis latency and potentially cause client timeouts. See the [Sidekiq
-documentation about Redis](https://github.com/mperham/sidekiq/wiki/Using-Redis)
-for more details.
-
-## Number of threads
-
-Each process started using `sidekiq-cluster` (whether it be via command line or
-via the gitlab.rb file) starts with a number of threads that equals the number
-of queues, plus one spare thread. For example, a process that handles the
-"process_commit" and "post_receive" queues will use 3 threads in total.
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index ddac81328b9..49aaac06b46 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -222,7 +222,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/approvals
"id": 1,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
- "web_url": "http://localhost:3000/u/root"
+ "web_url": "http://localhost:3000/root"
}
}
],
@@ -314,7 +314,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid/approvers
"id": 1,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
- "web_url": "http://localhost:3000/u/root"
+ "web_url": "http://localhost:3000/root"
}
}
],
@@ -387,7 +387,7 @@ does not match, the response code will be `409`.
"id": 1,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
- "web_url": "http://localhost:3000/u/root"
+ "web_url": "http://localhost:3000/root"
}
},
{
@@ -397,7 +397,7 @@ does not match, the response code will be `409`.
"id": 2,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/cf7ad14b34162a76d593e3affca2adca?s=80\u0026d=identicon",
- "web_url": "http://localhost:3000/u/ryley"
+ "web_url": "http://localhost:3000/ryley"
}
}
],
diff --git a/doc/api/settings.md b/doc/api/settings.md
index eb3f39e6670..b01cec64837 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -142,8 +142,6 @@ are listed in the descriptions of the relevant settings.
| `authorized_keys_enabled` | boolean | no | By default, we write to the `authorized_keys` file to support Git over SSH without additional configuration. GitLab can be optimized to authenticate SSH keys via the database file. Only disable this if you have configured your OpenSSH server to use the AuthorizedKeysCommand. |
| `auto_devops_domain` | string | no | Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages. |
| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for projects by default. It will automatically build, test, and deploy applications based on a predefined CI/CD configuration. |
-| `clientside_sentry_dsn` | string | required by: `clientside_sentry_enabled` | Clientside Sentry Data Source Name. |
-| `clientside_sentry_enabled` | boolean | no | (**If enabled, requires:** `clientside_sentry_dsn`) Enable Sentry error reporting for the client side. |
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes. |
| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. |
| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take: `0` _(not protected, both developers and maintainers can push new commits, force push, or delete the branch)_, `1` _(partially protected, developers and maintainers can push new commits, but cannot force push or delete the branch)_ or `2` _(fully protected, developers cannot push new commits, but maintainers can; no-one can force push or delete the branch)_ as a parameter. Default is `2`. |
@@ -212,8 +210,6 @@ are listed in the descriptions of the relevant settings.
| `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for groups, projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is `null` which means there is no restriction. |
| `rsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded RSA key. Default is `0` (no restriction). `-1` disables RSA keys. |
| `send_user_confirmation_email` | boolean | no | Send confirmation email on sign-up. |
-| `sentry_dsn` | string | required by: `sentry_enabled` | Sentry Data Source Name. |
-| `sentry_enabled` | boolean | no | (**If enabled, requires:** `sentry_dsn`) Sentry is an error reporting and logging tool which is currently not shipped with GitLab, available at <https://sentry.io>. |
| `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes |
| `shared_runners_enabled` | boolean | no | (**If enabled, requires:** `shared_runners_text`) Enable shared runners for new projects. |
| `shared_runners_text` | string | required by: `shared_runners_enabled` | Shared runners text. |
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 1743c38eb46..da864a0b3cc 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -63,7 +63,7 @@ Once you're familiar with how GitLab CI/CD works, see the
for all the attributes you can set and use.
NOTE: **Note:**
-GitLab CI/CD and [shared runners](runners/README.md#shared-specific-and-group-runners) are enabled in GitLab.com and available for all users, limited only to the [user's pipelines quota](../user/admin_area/settings/continuous_integration.md#extra-shared-runners-pipeline-minutes-quota).
+GitLab CI/CD and [shared runners](runners/README.md#shared-specific-and-group-runners) are enabled in GitLab.com and available for all users, limited only to the [user's pipelines quota](../user/admin_area/settings/continuous_integration.md#extra-shared-runners-pipeline-minutes-quota-free-only).
## Configuration
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index 23d52a33881..ff6dc16d1a0 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -409,11 +409,20 @@ To indicate the steps of navigation through the UI:
## Images
- Place images in a separate directory named `img/` in the same directory where
- the `.md` document that you're working on is located. Always prepend their
- names with the name of the document that they will be included in. For
- example, if there is a document called `twitter.md`, then a valid image name
- could be `twitter_login_screen.png`.
-- Images should have a specific, non-generic name that will differentiate and describe them properly.
+ the `.md` document that you're working on is located.
+- Images should have a specific, non-generic name that will
+ differentiate and describe them properly.
+- Always add to the end of the file name the GitLab release version
+ number corresponding to the release milestone the image was added to,
+ or corresponding to the release the screenshot was taken from, using the
+ format `image_name_vX_Y.png`.
+ ([Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/61027) in GitLab 12.1.)
+- For example, for a screenshot taken from the pipelines page of
+ GitLab 11.1, a valid name is `pipelines_v11_1.png`. If you're
+ adding an illustration that does not include parts of the UI,
+ add the release number corresponding to the release the image
+ was added to. Example, for an MR added to 11.1's milestone,
+ a valid name for an illustration is `devops_diagram_v11_1.png`.
- Keep all file names in lower case.
- Consider using PNG images instead of JPEG.
- Compress all images with <https://tinypng.com/> or similar tool.
@@ -426,7 +435,7 @@ To indicate the steps of navigation through the UI:
Inside the document:
- The Markdown way of using an image inside a document is:
- `![Proper description what the image is about](img/document_image_title.png)`
+ `![Proper description what the image is about](img/document_image_title_vX_Y.png)`
- Always use a proper description for what the image is about. That way, when a
browser fails to show the image, this text will be used as an alternative
description.
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index fc9b175bc8a..28ebb6f0f64 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -27,7 +27,7 @@ we need to solve before being able to use Jest for all our needs.
### Differences to Karma
- Jest runs in a Node.js environment, not in a browser. Support for running Jest tests in a browser [is planned](https://gitlab.com/gitlab-org/gitlab-ce/issues/58205).
-- Because Jest runs in a Node.js environment, it uses [jsdom](https://github.com/jsdom/jsdom) by default.
+- Because Jest runs in a Node.js environment, it uses [jsdom](https://github.com/jsdom/jsdom) by default. See also its [limitations](#limitations-of-jsdom) below.
- Jest does not have access to Webpack loaders or aliases.
The aliases used by Jest are defined in its [own config](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/jest.config.js).
- All calls to `setTimeout` and `setInterval` are mocked away. See also [Jest Timer Mocks](https://jestjs.io/docs/en/timer-mocks).
@@ -40,6 +40,17 @@ we need to solve before being able to use Jest for all our needs.
- Unhandled Promise rejections.
- Calls to `console.warn`, including warnings from libraries like Vue.
+### Limitations of jsdom
+
+As mentioned [above](#differences-to-karma), Jest uses jsdom instead of a browser for running tests.
+This comes with a number of limitations, namely:
+
+- [No scrolling support](https://github.com/jsdom/jsdom/blob/15.1.1/lib/jsdom/browser/Window.js#L623-L625)
+- [No element sizes or positions](https://github.com/jsdom/jsdom/blob/15.1.1/lib/jsdom/living/nodes/Element-impl.js#L334-L371)
+- [No layout engine](https://github.com/jsdom/jsdom/issues/1322) in general
+
+See also the issue for [support running Jest tests in browsers](https://gitlab.com/gitlab-org/gitlab-ce/issues/58205).
+
### Debugging Jest tests
Running `yarn jest-debug` will run Jest in debug mode, allowing you to debug/inspect as described in the [Jest docs](https://jestjs.io/docs/en/troubleshooting#tests-are-failing-and-you-don-t-know-why).
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 92122fca7dd..68c1bcbc801 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -9,7 +9,7 @@ as the hardware requirements that are needed to install and use GitLab.
## Operating Systems
-### Supported Unix distributions
+### Supported Linux distributions
- Ubuntu
- Debian
@@ -21,7 +21,7 @@ as the hardware requirements that are needed to install and use GitLab.
For the installations options, see [the main installation page](README.md).
-### Unsupported Unix distributions
+### Unsupported Linux distributions and Unix-like operating systems
- Arch Linux
- Fedora
@@ -29,13 +29,13 @@ For the installations options, see [the main installation page](README.md).
- Gentoo
- macOS
-On the above unsupported distributions is still possible to install GitLab yourself.
+Installation of GitLab on these operating systems is possible, but not supported.
Please see the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/installation/) for more information.
-### Non-Unix operating systems such as Windows
+### Microsoft Windows
-GitLab is developed for Unix operating systems.
-It does **not** run on Windows, and we have no plans to support it in the near future. For the latest development status view this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46567).
+GitLab is developed for Linux-based operating systems.
+It does **not** run on Microsoft Windows, and we have no plans to support it in the near future. For the latest development status view this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46567).
Please consider using a virtual machine to run GitLab.
## Ruby versions
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 702245b22a0..41d12128e51 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -723,7 +723,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | From Gitlab 11.11, this variable can be used to set a username to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD) |
| `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From Gitlab 11.11, this variable can be used to set a password to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_USERNAME) |
| `REPLICAS` | The number of replicas to deploy; defaults to 1. |
-| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. This takes precedence over `REPLICAS`; defaults to 1. |
+| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. |
| `CANARY_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md); defaults to 1 |
| `CANARY_PRODUCTION_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. This takes precedence over `CANARY_REPLICAS`; defaults to 1 |
| `ADDITIONAL_HOSTS` | Fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. |
diff --git a/doc/university/process/README.md b/doc/university/process/README.md
index fdf6224f7f6..b278e02ccd5 100644
--- a/doc/university/process/README.md
+++ b/doc/university/process/README.md
@@ -9,11 +9,11 @@ title: University | Process
# Suggesting improvements
If you would like to teach a class or participate or help in any way please
-submit a merge request and assign it to [Job](https://gitlab.com/u/JobV).
+submit a merge request and assign it to [Job](https://gitlab.com/JobV).
If you have suggestions for additional courses you would like to see,
please submit a merge request to add an upcoming class, assign to
-[Chad](https://gitlab.com/u/chadmalchow) and /cc [Job](https://gitlab.com/u/JobV).
+[Chad](https://gitlab.com/chadmalchow) and /cc [Job](https://gitlab.com/JobV).
## Adding classes
@@ -31,4 +31,4 @@ please submit a merge request to add an upcoming class, assign to
1. Please upload any video recordings to our Youtube channel. We prefer them to
be public, if needed they can be unlisted but if so they should be linked from
this page.
-1. Please create a merge request and assign to [Erica](https://gitlab.com/u/Erica).
+1. Please create a merge request and assign to [Erica](https://gitlab.com/Erica).
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index 5768a97e727..84596ff6a2c 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -94,13 +94,11 @@ a group in the **Usage Quotas** page available to the group page settings list.
![Group pipelines quota](img/group_pipelines_quota.png)
-## Extra Shared Runners pipeline minutes quota
+## Extra Shared Runners pipeline minutes quota **[FREE ONLY]**
-NOTE: **Note:**
-Only available on GitLab.com.
-
-You can purchase additional CI minutes so your pipelines will not be blocked after you have
-used all your CI minutes from your main quota.
+If you're using GitLab.com, you can purchase additional CI minutes so your
+pipelines will not be blocked after you have used all your CI minutes from your
+main quota.
In order to purchase additional minutes, you should follow these steps:
diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md
index 26d764fa2cf..8d4ffd93f59 100644
--- a/doc/user/group/clusters/index.md
+++ b/doc/user/group/clusters/index.md
@@ -138,14 +138,6 @@ The result will then be:
- The Staging cluster will be used for the `deploy to staging` job.
- The Production cluster will be used for the `deploy to production` job.
-## Unavailable features
-
-The following features are not currently available for group-level clusters:
-
-1. Terminals (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55487)).
-1. Pod logs (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55488)).
-1. Deployment boards (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55489)).
-
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/user/img/color_inline_colorchip_render_gfm.png b/doc/user/img/color_inline_colorchip_render_gfm.png
deleted file mode 100644
index 6f93dbeeb10..00000000000
--- a/doc/user/img/color_inline_colorchip_render_gfm.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/img/markdown_inline_diffs_tags_rendered.png b/doc/user/img/markdown_inline_diffs_tags_rendered.png
deleted file mode 100644
index 4279a20b5a0..00000000000
--- a/doc/user/img/markdown_inline_diffs_tags_rendered.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/img/math_inline_sup_render_gfm.png b/doc/user/img/math_inline_sup_render_gfm.png
deleted file mode 100644
index 3ee2abb14df..00000000000
--- a/doc/user/img/math_inline_sup_render_gfm.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/img/task_list_ordered_render_gfm.png b/doc/user/img/task_list_ordered_render_gfm.png
deleted file mode 100644
index 98ec791e958..00000000000
--- a/doc/user/img/task_list_ordered_render_gfm.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 31c8093ced7..16df6d93277 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -1,15 +1,20 @@
# GitLab Markdown
-This markdown guide is **valid for GitLab's system markdown entries and files**.
-It is not valid for the [GitLab documentation website](https://docs.gitlab.com)
-nor [GitLab's main website](https://about.gitlab.com), as they both use
-[Kramdown](https://kramdown.gettalong.org) as their markdown engine.
-The documentation website uses an extended Kramdown gem, [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown).
-Consult the [GitLab Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/) for a complete Kramdown reference.
+This markdown guide is **valid only for GitLab's internal markdown rendering system for entries and files**.
+It is **not** valid for the [GitLab documentation website](https://docs.gitlab.com)
+or [GitLab's main website](https://about.gitlab.com), as they both use
+[Kramdown](https://kramdown.gettalong.org) as their markdown engine. The documentation
+website uses an extended Kramdown gem, [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown).
+Consult the [GitLab Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/)
+for a complete Kramdown reference.
+
+NOTE: **Note:** We encourage you to view this document as [rendered by GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md).
## GitLab Flavored Markdown (GFM)
-GitLab uses "GitLab Flavored Markdown" (GFM). It extends the [CommonMark specification][commonmark-spec] (which is based on standard Markdown) in a few significant ways to add additional useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
+GitLab uses "GitLab Flavored Markdown" (GFM). It extends the [CommonMark specification](https://spec.commonmark.org/current/)
+(which is based on standard Markdown) in several ways to add additional useful functionality.
+It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
You can use GFM in the following areas:
@@ -22,35 +27,29 @@ You can use GFM in the following areas:
- Markdown documents inside repositories
- Epics **[ULTIMATE]**
-You can also use other rich text files in GitLab. You might have to install a
-dependency to do so. Please see the [`github-markup` gem readme](https://github.com/gitlabhq/markup#markups) for more information.
-
-> **Notes:**
->
-> We encourage you to view this document as [rendered by GitLab itself](markdown.md).
->
-> As of 11.1, GitLab uses the [CommonMark Ruby Library][commonmarker] for Markdown
-processing of all new issues, merge requests, comments, and other Markdown content
-in the GitLab system. As of 11.3, wiki pages and Markdown files (`.md`) in the
-repositories are also processed with CommonMark. As of 11.8, the [Redcarpet
-Ruby library][redcarpet] has been removed and all issues/comments, including
-those from pre-11.1, are now processed using [CommonMark Ruby
-Library][commonmarker].
->
-> The documentation website had its [markdown engine migrated from Redcarpet to Kramdown](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/108)
-in October 2018.
->
-> _Where there are significant differences, we will try to call them out in this document._
+You can also use other rich text files in GitLab. You might have to install a dependency
+to do so. Please see the [`gitlab-markup` gem project](https://gitlab.com/gitlab-org/gitlab-markup)
+for more information.
+
+### Transition from Redcarpet to CommonMark
-### Transitioning to CommonMark
+Since 11.1, GitLab uses the [CommonMark Ruby Library](https://github.com/gjtorikian/commonmarker)
+for Markdown processing of all new issues, merge requests, comments, and other Markdown
+content in the GitLab system. Since 11.3, wiki pages and Markdown files (`*.md`) in
+repositories are also processed with CommonMark. As of 11.8, the [Redcarpet Ruby library](https://github.com/vmg/redcarpet)
+has been removed and all issues and comments, including those from pre-11.1, are now processed
+using the [CommonMark Ruby Library](https://github.com/gjtorikian/commonmarker).
-You may have older issues/merge requests or Markdown documents in your
-repository that were written using some of the nuances of RedCarpet's version
+The documentation website had its [markdown engine migrated from Redcarpet to Kramdown](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/108)
+in October 2018.
+
+You may have older issues, merge requests, or Markdown documents in your
+repository that were written using some of the nuances of GitLab's RedCarpet version
of Markdown. Since CommonMark uses a slightly stricter syntax, these documents
-may now display a little strangely since we've transitioned to CommonMark.
-Numbered lists with nested lists in particular can be displayed incorrectly.
+may now display a little differently since we've transitioned to CommonMark.
-It is usually quite easy to fix. In the case of a nested list such as this:
+It is usually quite easy to fix. For example, numbered lists with nested lists may
+render incorrectly:
```markdown
1. Chocolate
@@ -58,7 +57,14 @@ It is usually quite easy to fix. In the case of a nested list such as this:
- milk
```
-simply add a space to each nested item:
+1. Chocolate
+ - dark
+ - milk
+
+---
+
+Simply add a space to each nested item to align the `-` with the first character of
+the top list item (`C` in this case):
```markdown
1. Chocolate
@@ -66,515 +72,674 @@ simply add a space to each nested item:
- milk
```
-In the documentation below, we try to highlight some of the differences.
+1. Chocolate
+ - dark
+ - milk
+
+NOTE: **Note:** We will flag any significant differences between Redcarpet and CommonMark
+ markdown in this document.
If you have a large volume of Markdown files, it can be tedious to determine
-if they will be displayed correctly or not. You can use the
+if they will display correctly or not. You can use the
[diff_redcarpet_cmark](https://gitlab.com/digitalmoksha/diff_redcarpet_cmark)
-tool (not an officially supported product) to generate a list of files and
+tool (not an officially supported product) to generate a list of files, and the
differences between how RedCarpet and CommonMark render the files. It can give
-you a great idea if anything needs to be changed - many times nothing will need
-to changed.
+an indication if anything needs to be changed - often nothing will need
+to change.
+
+### GFM extends standard markdown
+
+GitLab makes full use of the standard (CommonMark) formatting, but also includes additional
+functionality useful for GitLab users.
+
+It makes use of [new markdown features](#new-GFM-markdown-extensions),
+not found in standard markdown:
+
+- [Color "chips" written in HEX, RGB or HSL](#colors)
+- [Diagrams and flowcharts using Mermaid](#diagrams-and-flowcharts-using-mermaid)
+- [Emoji](#emoji)
+- [Front matter](#front-matter)
+- [Inline diffs](#inline-diff)
+- [Math equations and symbols written in LaTeX](#math)
+- [Special GitLab references](#special-gitlab-references)
+- [Task Lists](#task-lists)
+- [Wiki specific markdown](#wiki-specific-markdown)
+
+It also has [extended markdown features](#standard-markdown-and-extensions-in-gitlab), without
+changing how standard markdown is used:
+
+| Standard markdown | Extended markdown in GitLab |
+| ------------------------------------- | ------------------------- |
+| [blockquotes](#blockquotes) | [multiline blockquotes](#multiline-blockquote) |
+| [code blocks](#code-spans-and-blocks) | [colored code and syntax highlighting](#colored-code-and-syntax-highlighting) |
+| [emphasis](#emphasis) | [multiple underscores in words](#multiple-underscores-in-words-and-mid-word-emphasis)
+| [headers](#headers) | [linkable Header IDs](#header-ids-and-links) |
+| [images](#images) | [embedded videos](#videos) |
+| [linebreaks](#line-breaks) | [more linebreak control](#newlines) |
+| [links](#links) | [automatically linking URLs](#url-auto-linking) |
+
+## New GFM markdown extensions
+
+### Colors
-### Newlines
+> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#colors).
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#newlines
+It is possible to have color written in HEX, RGB or HSL format rendered with a color
+indicator.
-GFM honors the markdown specification in how [paragraphs and line breaks are handled][commonmark-spec].
+Supported formats (named colors are not supported):
-A paragraph is simply one or more consecutive lines of text, separated by one or
-more blank lines.
-Line-breaks, or soft returns, are rendered if you end a line with two or more spaces:
+- HEX: `` `#RGB[A]` `` or `` `#RRGGBB[AA]` ``
+- RGB: `` `RGB[A](R, G, B[, A])` ``
+- HSL: `` `HSL[A](H, S, L[, A])` ``
-<!-- (Do *NOT* remove the two ending whitespaces in the following line.) -->
-<!-- (They are needed for the Markdown text to render correctly.) -->
- Roses are red [followed by two or more spaces]
- Violets are blue
+Color written inside backticks will be followed by a color "chip":
- Sugar is sweet
+```markdown
+`#F00`
+`#F00A`
+`#FF0000`
+`#FF0000AA`
+`RGB(0,255,0)`
+`RGB(0%,100%,0%)`
+`RGBA(0,255,0,0.3)`
+`HSL(540,70%,50%)`
+`HSLA(540,70%,50%,0.3)`
+```
-<!-- (Do *NOT* remove the two ending whitespaces in the following line.) -->
-<!-- (They are needed for the Markdown text to render correctly.) -->
-Roses are red
-Violets are blue
+`#F00`
+`#F00A`
+`#FF0000`
+`#FF0000AA`
+`RGB(0,255,0)`
+`RGB(0%,100%,0%)`
+`RGBA(0,255,0,0.3)`
+`HSL(540,70%,50%)`
+`HSLA(540,70%,50%,0.3)`
-Sugar is sweet
+### Diagrams and flowcharts using Mermaid
-### Multiple underscores in words
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107) in
+GitLab 10.3.
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiple-underscores-in-words
+It is possible to generate diagrams and flowcharts from text using [Mermaid](https://mermaidjs.github.io/).
+Visit the official page for more details.
-It is not reasonable to italicize just _part_ of a word, especially when you're
-dealing with code and names that often appear with multiple underscores.
-Therefore, GFM ignores multiple underscores in words:
+In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block:
- perform_complicated_task
+~~~
+```mermaid
+graph TD;
+ A-->B;
+ A-->C;
+ B-->D;
+ C-->D;
+```
+~~~
- do_this_and_do_that_and_another_thing
+```mermaid
+graph TD;
+ A-->B;
+ A-->C;
+ B-->D;
+ C-->D;
+```
-perform_complicated_task
+### Emoji
-do_this_and_do_that_and_another_thing
+> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji).
-### URL auto-linking
+```md
+Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#url-auto-linking
+:zap: You can use emoji anywhere GFM is supported. :v:
-GFM will autolink almost any URL you copy and paste into your text:
+You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
- * https://www.google.com
- * https://google.com/
- * ftp://ftp.us.debian.org/debian/
- * smb://foo/bar/baz
- * irc://irc.freenode.net/gitlab
- * http://localhost:3000
+If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
-* https://www.google.com
-* https://google.com/
-* ftp://ftp.us.debian.org/debian/
-* <a href="smb://foo/bar/baz">smb://foo/bar/baz</a>
-* <a href="irc://irc.freenode.net/gitlab">irc://irc.freenode.net/gitlab</a>
-* http://localhost:3000
+Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
+```
-### Multiline blockquote
+Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/monkey.png" width="20px" height="20px" style="display:inline;margin:0"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/star2.png" width="20px" height="20px" style="display:inline;margin:0"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speech_balloon.png" width="20px" height="20px" style="display:inline;margin:0">. Well we have a gift for you:
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiline-blockquote
+<img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/zap.png" width="20px" height="20px" style="display:inline;margin:0">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/v.png" width="20px" height="20px" style="display:inline;margin:0">
-On top of standard Markdown [blockquotes](#blockquotes), which require prepending `>` to quoted lines,
-GFM supports multiline blockquotes fenced by <code>>>></code>:
+You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px" style="display:inline;margin:0"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px" style="display:inline;margin:0"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px" style="display:inline;margin:0"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px" style="display:inline;margin:0">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px" style="display:inline;margin:0"> you for that.
-```
->>>
-If you paste a message from somewhere else
+If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px" style="display:inline;margin:0">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px" style="display:inline;margin:0">. All you need to do is to look up one of the supported codes.
-that
+Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px" style="display:inline;margin:0">
-spans
+> **Note:** The emoji example above uses hard-coded images for this documentation. The emoji,
+when rendered within GitLab, may appear different depending on the OS and browser used.
-multiple lines,
+Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
-you can quote that without having to manually prepend `>` to every line!
->>>
-```
+NOTE: **Note:** On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/)
+to get full native emoji support. Ubuntu 18.04 (like many modern Linux distros) has
+this font installed by default.
-<blockquote dir="auto">
-<p>If you paste a message from somewhere else</p>
-<p>that</p>
-<p>spans</p>
-<p>multiple lines,</p>
-<p>you can quote that without having to manually prepend <code>&gt;</code> to every line!</p>
-</blockquote>
+### Front matter
-### Code and syntax highlighting
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23331)
+ in GitLab 11.6.
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting
+Front matter is metadata included at the beginning of a markdown document, preceding
+its content. This data can be used by static site generators such as [Jekyll](https://jekyllrb.com/docs/front-matter/),
+[Hugo](https://gohugo.io/content-management/front-matter/), and many other applications.
-_GitLab uses the [Rouge Ruby library][rouge] for syntax highlighting. For a
-list of supported languages visit the Rouge website._
+When you view a Markdown file rendered by GitLab, any front matter is displayed as-is,
+in a box at the top of the document, before the rendered HTML content. To view an example,
+you can toggle between the source and rendered version of a [GitLab documentation file](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/README.md).
-Blocks of code are either fenced by lines with three back-ticks <code>```</code>,
-or are indented with four spaces. Only the fenced code blocks support syntax
-highlighting:
+In GitLab, front matter is only used in Markdown files and wiki pages, not the other
+places where Markdown formatting is supported. It must be at the very top of the document,
+and must be between delimiters, as explained below.
-```
-Inline `code` has `back-ticks around` it.
-```
+The following delimeters are supported:
-Inline `code` has `back-ticks around` it.
+- YAML (`---`):
-Example:
+ ~~~yaml
+ ---
+ title: About Front Matter
+ example:
+ language: yaml
+ ---
+ ~~~
- ```javascript
- var s = "JavaScript syntax highlighting";
- alert(s);
- ```
+- TOML (`+++`):
- ```python
- def function():
- #indenting works just fine in the fenced code block
- s = "Python syntax highlighting"
- print s
- ```
+ ~~~toml
+ +++
+ title = "About Front Matter"
+ [example]
+ language = "toml"
+ +++
+ ~~~
- ```ruby
- require 'redcarpet'
- markdown = Redcarpet.new("Hello World!")
- puts markdown.to_html
- ```
+- JSON (`;;;`):
- ```
- No language indicated, so no syntax highlighting.
- s = "There is no highlighting for this."
- But let's throw in a <b>tag</b>.
- ```
+ ~~~json
+ ;;;
+ {
+ "title": "About Front Matter"
+ "example": {
+ "language": "json"
+ }
+ }
+ ;;;
+ ~~~
-becomes:
+Other languages are supported by adding a specifier to any of the existing
+delimiters. For example:
-```javascript
-var s = "JavaScript syntax highlighting";
-alert(s);
+```php
+---php
+$title = "About Front Matter";
+$example = array(
+ 'language' => "php",
+);
+---
```
-```python
-def function():
- #indenting works just fine in the fenced code block
- s = "Python syntax highlighting"
- print s
-```
+### Inline diff
-```ruby
-require 'redcarpet'
-markdown = Redcarpet.new("Hello World!")
-puts markdown.to_html
-```
+> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-diff).
-```
-No language indicated, so no syntax highlighting.
-s = "There is no highlighting for this."
-But let's throw in a <b>tag</b>.
-```
+With inline diff tags you can display {+ additions +} or [- deletions -].
-### Inline diff
+The wrapping tags can be either curly braces or square brackets:
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-diff
+```markdown
+- {+ addition 1 +}
+- [+ addition 2 +]
+- {- deletion 3 -}
+- [- deletion 4 -]
+```
-With inline diffs tags you can display {+ additions +} or [- deletions -].
+- {+ addition 1 +}
+- [+ addition 2 +]
+- {- deletion 3 -}
+- [- deletion 4 -]
-The wrapping tags can be either curly braces or square brackets.
+---
-Examples:
+However the wrapping tags cannot be mixed:
+```markdown
+- {+ addition +]
+- [+ addition +}
+- {- deletion -]
+- [- deletion -}
```
-- {+ additions +}
-- [+ additions +]
-- {- deletions -}
-- [- deletions -]
-```
-becomes:
+### Math
+
+> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#math).
+
+It is possible to have math written with LaTeX syntax rendered using [KaTeX](https://github.com/Khan/KaTeX).
-![inline diffs tags rendered](img/markdown_inline_diffs_tags_rendered.png)
+Math written between dollar signs `$` will be rendered inline with the text. Math written
+inside a [code block](#code-spans-and-blocks) with the language declared as `math`, will be rendered
+on a separate line:
-However the wrapping tags cannot be mixed as such:
+~~~
+This math is inline $`a^2+b^2=c^2`$.
+This is on a separate line
+
+```math
+a^2+b^2=c^2
```
-- {+ additions +]
-- [+ additions +}
-- {- deletions -]
-- [- deletions -}
+~~~
+
+This math is inline $`a^2+b^2=c^2`$.
+
+This is on a separate line
+
+```math
+a^2+b^2=c^2
```
-### Emoji
+_Be advised that KaTeX only supports a [subset](https://katex.org/docs/supported.html) of LaTeX._
-```md
-Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
+NOTE: **Note:** This also works for the asciidoctor `:stem: latexmath`. For details see
+the [asciidoctor user manual](http://asciidoctor.org/docs/user-manual/#activating-stem-support).
-:zap: You can use emoji anywhere GFM is supported. :v:
+### Special GitLab references
-You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
+GFM recognizes special GitLab related references. For example, you can easily reference
+an issue, a commit, a team member or even the whole team within a project. GFM will turn
+that reference into a link so you can navigate between them easily.
-If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
+Additionally, GFM recognizes certain cross-project references, and also has a shorthand
+version to reference other projects from the same namespace.
-Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
+GFM will recognize the following:
-Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
+| references | input | cross-project reference | shortcut within same namespace |
+| :------------------------------ | :------------------------- | :-------------------------------------- | :----------------------------- |
+| specific user | `@user_name` | | |
+| specific group | `@group_name` | | |
+| entire team | `@all` | | |
+| project | `namespace/project>` | | |
+| issue | ``#123`` | `namespace/project#123` | `project#123` |
+| merge request | `!123` | `namespace/project!123` | `project!123` |
+| snippet | `$123` | `namespace/project$123` | `project$123` |
+| epic **[ULTIMATE]** | `&123` | `group1/subgroup&123` | |
+| label by ID | `~123` | `namespace/project~123` | `project~123` |
+| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` |
+| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` |
+| project milestone by ID | `%123` | `namespace/project%123` | `project%123` |
+| one-word milestone by name | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` |
+| multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` |
+| specific commit | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` |
+| commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` |
+| repository file references | `[README](doc/README)` | | |
+| repository file line references | `[README](doc/README#L13)` | | |
-On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+### Task lists
+
+> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#task-lists).
+
+You can add task lists anywhere markdown is supported, but you can only "click" to
+toggle the boxes if they are in issues, merge requests, or comments. In other places
+you must edit the markdown manually to change the status by adding or removing the `x`.
-Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
+To create a task list, add a specially-formatted Markdown list. You can use either
+unordered or ordered lists:
+
+```markdown
+- [x] Completed task
+- [ ] Incomplete task
+ - [ ] Sub-task 1
+ - [x] Sub-task 2
+ - [ ] Sub-task 3
+1. [x] Completed task
+1. [ ] Incomplete task
+ 1. [ ] Sub-task 1
+ 1. [x] Sub-task 2
```
-Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/monkey.png" width="20px" height="20px" style="display:inline;margin:0"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/star2.png" width="20px" height="20px" style="display:inline;margin:0"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speech_balloon.png" width="20px" height="20px" style="display:inline;margin:0">. Well we have a gift for you:
+- [x] Completed task
+- [ ] Incomplete task
+ - [ ] Sub-task 1
+ - [x] Sub-task 2
+ - [ ] Sub-task 3
+1. [x] Completed task
+1. [ ] Incomplete task
+ 1. [ ] Sub-task 1
+ 1. [x] Sub-task 2
-<img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/zap.png" width="20px" height="20px" style="display:inline;margin:0">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/v.png" width="20px" height="20px" style="display:inline;margin:0">
+### Wiki-specific Markdown
-You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px" style="display:inline;margin:0"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px" style="display:inline;margin:0"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px" style="display:inline;margin:0"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px" style="display:inline;margin:0">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px" style="display:inline;margin:0"> you for that.
+The following examples show how links inside wikis behave.
-If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px" style="display:inline;margin:0">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px" style="display:inline;margin:0">. All you need to do is to look up one of the supported codes.
+#### Wiki - Direct page link
-Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px" style="display:inline;margin:0">
+A link which just includes the slug for a page will point to that page,
+_at the base level of the wiki_.
-Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
+This snippet would link to a `documentation` page at the root of your wiki:
-On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+```markdown
+[Link to Documentation](documentation)
+```
-Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
+#### Wiki - Direct file link
-### Special GitLab references
+Links with a file extension point to that file, _relative to the current page_.
-GFM recognizes special references.
+If the snippet below was placed on a page at `<your_wiki>/documentation/related`,
+it would link to `<your_wiki>/documentation/file.md`:
-You can easily reference e.g. an issue, a commit, a team member or even the whole team within a project.
+```markdown
+[Link to File](file.md)
+```
-GFM will turn that reference into a link so you can navigate between them easily.
+#### Wiki - Hierarchical link
-GFM will recognize the following:
+A link can be constructed relative to the current wiki page using `./<page>`,
+`../<page>`, etc.
-| input | references |
-|:---------------------------|:--------------------------------|
-| `@user_name` | specific user |
-| `@group_name` | specific group |
-| `@all` | entire team |
-| `namespace/project>` | project |
-| `#12345` | issue |
-| `!123` | merge request |
-| `$123` | snippet |
-| `&123` | epic **[ULTIMATE]** |
-| `~123` | label by ID |
-| `~bug` | one-word label by name |
-| `~"feature request"` | multi-word label by name |
-| `%123` | project milestone by ID |
-| `%v1.23` | one-word milestone by name |
-| `%"release candidate"` | multi-word milestone by name |
-| `9ba12248` | specific commit |
-| `9ba12248...b19a04f5` | commit range comparison |
-| `[README](doc/README)` | repository file references |
-| `[README](doc/README#L13)` | repository file line references |
-
-GFM also recognizes certain cross-project references:
-
-| input | references |
-|:----------------------------------------|:------------------------|
-| `namespace/project#123` | issue |
-| `namespace/project!123` | merge request |
-| `namespace/project%123` | project milestone |
-| `namespace/project$123` | snippet |
-| `namespace/project@9ba12248` | specific commit |
-| `group1/subgroup&123` | epic **[ULTIMATE]** |
-| `namespace/project@9ba12248...b19a04f5` | commit range comparison |
-| `namespace/project~"Some label"` | issues with given label |
-
-It also has a shorthand version to reference other projects from the same namespace:
-
-| input | references |
-|:------------------------------|:------------------------|
-| `project#123` | issue |
-| `project!123` | merge request |
-| `project%123` | project milestone |
-| `project$123` | snippet |
-| `project@9ba12248` | specific commit |
-| `project@9ba12248...b19a04f5` | commit range comparison |
-| `project~"Some label"` | issues with given label |
+If this snippet was placed on a page at `<your_wiki>/documentation/main`,
+it would link to `<your_wiki>/documentation/related`:
-### Task lists
+```markdown
+[Link to Related Page](./related)
+```
+
+If this snippet was placed on a page at `<your_wiki>/documentation/related/content`,
+it would link to `<your_wiki>/documentation/main`:
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#task-lists
+```markdown
+[Link to Related Page](../main)
+```
-You can add task lists to issues, merge requests and comments. To create a task list, add a specially-formatted Markdown list, like so:
+If this snippet was placed on a page at `<your_wiki>/documentation/main`,
+it would link to `<your_wiki>/documentation/related.md`:
+```markdown
+[Link to Related Page](./related.md)
```
-- [x] Completed task
-- [ ] Incomplete task
- - [ ] Sub-task 1
- - [x] Sub-task 2
- - [ ] Sub-task 3
+
+If this snippet was placed on a page at `<your_wiki>/documentation/related/content`,
+it would link to `<your_wiki>/documentation/main.md`:
+
+```markdown
+[Link to Related Page](../main.md)
```
-![alt unordered-check-list-render-gfm](img/unordered_check_list_render_gfm.png)
+#### Wiki - Root link
-Tasks formatted as ordered lists are supported as well:
+A link starting with a `/` is relative to the wiki root.
+
+This snippet links to `<wiki_root>/documentation`:
+```markdown
+[Link to Related Page](/documentation)
```
-1. [x] Completed task
-1. [ ] Incomplete task
- 1. [ ] Sub-task 1
- 1. [x] Sub-task 2
+
+This snippet links to `<wiki_root>/miscellaneous.md`:
+
+```markdown
+[Link to Related Page](/miscellaneous.md)
```
-![alt task-list-ordered-render-gfm](img/task_list_ordered_render_gfm.png)
+## Standard markdown and extensions in GitLab
-Task lists can only be created in descriptions, not in titles. Task item state can be managed by editing the description's Markdown or by toggling the rendered check boxes.
+All standard markdown formatting should work as expected within GitLab. Some standard
+functionality is extended with additional features, without affecting the standard usage.
+If a functionality is extended, the new option will be listed as a sub-section.
-### Videos
+### Blockquotes
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#videos
+Blockquotes are an easy way to highlight information, such as a side-note. It is generated
+by starting the lines of the blockquote with `>`:
-Image tags with a video extension are automatically converted to a video player.
+```markdown
+> Blockquotes are very handy to emulate reply text.
+> This line is part of the same quote.
-The valid video extensions are `.mp4`, `.m4v`, `.mov`, `.webm`, and `.ogv`.
+Quote break.
- Here's a sample video:
+> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
+```
- ![Sample Video](img/markdown_video.mp4)
+> Blockquotes are very handy to emulate reply text.
+> This line is part of the same quote.
-Here's a sample video:
+Quote break.
-<div class="video-container">
- <video src="img/markdown_video.mp4" width="400" controls="true" data-setup="{}" data-title="Sample Video"></video>
- <p><a href="img/markdown_video.mp4" target="_blank" rel="noopener noreferrer" title="Download 'Sample Video'">Sample Video</a></p>
-</div>
+> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
-### Math
+#### Multiline blockquote
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#math
+> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiline-blockquote).
-It is possible to have math written with the LaTeX syntax rendered using [KaTeX][katex].
+GFM extends the standard markdown standard by also supporting multiline blockquotes
+fenced by `>>>`:
-Math written inside ```$``$``` will be rendered inline with the text.
+```
+>>>
+If you paste a message from somewhere else
-Math written inside triple back quotes, with the language declared as `math`, will be rendered on a separate line.
+that spans multiple lines,
-Example:
+you can quote that without having to manually prepend `>` to every line!
+>>>
+```
- This math is inline $`a^2+b^2=c^2`$.
+>>>
+If you paste a message from somewhere else
- This is on a separate line
- ```math
- a^2+b^2=c^2
- ```
+that spans multiple lines,
-Becomes:
+you can quote that without having to manually prepend `>` to every line!
+>>>
-This math is inline ![alt text](img/math_inline_sup_render_gfm.png).
+### Code spans and blocks
-This is on a separate line
+You can easily highlight anything that should be viewed as code and not simple text.
-<img src="./img/math_inline_sup_render_gfm.png" >
+Simple inline code is easily highlighted with single backticks `` ` ``:
-_Be advised that KaTeX only supports a [subset][katex-subset] of LaTeX._
+```markdown
+Inline `code` has `back-ticks around` it.
+```
->**Note:**
-This also works for the asciidoctor `:stem: latexmath`. For details see the [asciidoctor user manual][asciidoctor-manual].
+Inline `code` has `back-ticks around` it.
-### Colors
+---
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#colors
+Similarly, a whole block of code can be fenced with triple backticks ```` ``` ````,
+triple tildes (`~~~`), or indended 4 or more spaces to achieve a similar effect for
+a larger body of code. test.
-It is possible to have color written in HEX, RGB or HSL format rendered with a color indicator.
+~~~
+```
+def function():
+ #indenting works just fine in the fenced code block
+ s = "Python code"
+ print s
+```
-Color written inside backticks will be followed by a color "chip".
+ Using 4 spaces
+ is like using
+ 3-backtick fences.
+~~~
-Examples:
+```
+~~~
+Tildes are OK too.
+~~~
+```
- `#F00`
- `#F00A`
- `#FF0000`
- `#FF0000AA`
- `RGB(0,255,0)`
- `RGB(0%,100%,0%)`
- `RGBA(0,255,0,0.7)`
- `HSL(540,70%,50%)`
- `HSLA(540,70%,50%,0.7)`
+The three examples above render as:
-Becomes:
+```
+def function():
+ #indenting works just fine in the fenced code block
+ s = "Python code"
+ print s
+```
-![alt color-inline-colorchip-render-gfm](img/color_inline_colorchip_render_gfm.png)
+ Using 4 spaces
+ is like using
+ 3-backtick fences.
-#### Supported formats:
+~~~
+Tildes are OK too.
+~~~
-* HEX: `` `#RGB[A]` `` or `` `#RRGGBB[AA]` ``
-* RGB: `` `RGB[A](R, G, B[, A])` ``
-* HSL: `` `HSL[A](H, S, L[, A])` ``
+#### Colored code and syntax highlighting
-### Mermaid
+> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#colored-code-and-syntax-highlighting).
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107) in
-GitLab 10.3.
->
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#mermaid
+GitLab uses the [Rouge Ruby library](http://rouge.jneen.net/) for more colorful syntax
+highlighting in code blocks. For a list of supported languages visit the
+[Rouge project wiki](https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers).
+Syntax highlighting is only supported in code blocks, it is not possible to highlight
+code when it is inline.
-It is possible to generate diagrams and flowcharts from text using [Mermaid](https://mermaidjs.github.io/).
+Blocks of code are fenced by lines with three back-ticks ```` ``` ```` or three tildes `~~~`, and have
+the language identified at the end of the first fence:
-In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block.
+~~~
+```javascript
+var s = "JavaScript syntax highlighting";
+alert(s);
+```
-Example:
+```python
+def function():
+ #indenting works just fine in the fenced code block
+ s = "Python syntax highlighting"
+ print s
+```
- ```mermaid
- graph TD;
- A-->B;
- A-->C;
- B-->D;
- C-->D;
- ```
+```ruby
+require 'redcarpet'
+markdown = Redcarpet.new("Hello World!")
+puts markdown.to_html
+```
-Becomes:
+```
+No language indicated, so no syntax highlighting.
+s = "There is no highlighting for this."
+But let's throw in a <b>tag</b>.
+```
+~~~
-```mermaid
-graph TD;
- A-->B;
- A-->C;
- B-->D;
- C-->D;
+The four examples above render as:
+
+```javascript
+var s = "JavaScript syntax highlighting";
+alert(s);
```
-For details see the [Mermaid official page](https://mermaidjs.github.io/).
+```python
+def function():
+ #indenting works just fine in the fenced code block
+ s = "Python syntax highlighting"
+ print s
+```
-### Front matter
+```ruby
+require 'redcarpet'
+markdown = Redcarpet.new("Hello World!")
+puts markdown.to_html
+```
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23331)
- in GitLab 11.6.
+```
+No language indicated, so no syntax highlighting.
+s = "There is no highlighting for this."
+But let's throw in a <b>tag</b>.
+```
-Front matter is metadata included at the beginning of a markdown document, preceding
-its content. This data can be used by static site generators such as [Jekyll](https://jekyllrb.com/docs/front-matter/) and [Hugo](https://gohugo.io/content-management/front-matter/),
-and many other applications.
+### Emphasis
-In GitLab, front matter is only used in Markdown files and wiki pages, not the other places where Markdown formatting is supported.
-When you view a Markdown file rendered by GitLab, any front matter is displayed as-is, in a box at the top of the document, before the rendered HTML content.
-To view an example, you can toggle between the source and rendered version of a [GitLab documentation file](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/README.md).
+There are multiple ways to emphasize text in markdown. You can italicize, bold, strikethrough,
+as well as combine these emphasis styles together.
-The following delimeters are supported:
+Examples:
-- YAML (`---`):
+```markdown
+Emphasis, aka italics, with *asterisks* or _underscores_.
- ```
- ---
- title: About Front Matter
- example:
- language: yaml
- ---
- ```
+Strong emphasis, aka bold, with double **asterisks** or __underscores__.
-- TOML (`+++`):
+Combined emphasis with **asterisks and _underscores_**.
- ```
- +++
- title = "About Front Matter"
- [example]
- language = "toml"
- +++
- ```
+Strikethrough uses two tildes. ~~Scratch this.~~
+```
-- JSON (`;;;`):
+Emphasis, aka italics, with *asterisks* or _underscores_.
- ```
- ;;;
- {
- "title": "About Front Matter"
- "example": {
- "language": "json"
- }
- }
- ;;;
- ```
+Strong emphasis, aka bold, with double **asterisks** or __underscores__.
-Other languages are supported by adding a specifier to any of the existing
-delimiters. For example:
+Combined emphasis with **asterisks and _underscores_**.
+
+Strikethrough uses two tildes. ~~Scratch this.~~
+NOTE: **Note:** Strikethrough is not part of the core Markdown standard, but is part of GFM.
+
+#### Multiple underscores in words and mid-word emphasis
+
+> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiple-underscores-in-words).
+
+It is not usually useful to italicize just _part_ of a word, especially when you're
+dealing with code and names that often appear with multiple underscores. As a result,
+GFM extends the standard markdown standard by ignoring multiple underlines in words,
+to allow better rendering of markdown documents discussing code:
+
+```md
+perform_complicated_task
+
+do_this_and_do_that_and_another_thing
+
+but_emphasis is_desired _here_
```
----php
-$title = "About Front Matter";
-$example = array(
- 'language' => "php",
-);
+
+perform_complicated_task
+
+do_this_and_do_that_and_another_thing
+
+but_emphasis is_desired _here_
+
---
+
+If you wish to emphasize only a part of a word, it can still be done with asterisks:
+
+```md
+perform*complicated*task
+
+do*this*and*do*that*and*another thing
```
-## Standard Markdown
+perform*complicated*task
+
+do*this*and*do*that*and*another thing
-### Headers
+### Footnotes
+
+Footnotes add a link to a note rendered at the end of a markdown file:
+
+```markdown
+You can add footnotes to your text as follows.[^1]
+[^1]: This is my awesome footnote (later in file).
```
+
+You can add footnotes to your text as follows.[^1]
+
+[^1]: This is my awesome footnote (later in file).
+
+### Headers
+
+```markdown
# H1
## H2
### H3
@@ -593,9 +758,11 @@ Alt-H2
#### Header IDs and links
-All Markdown-rendered headers automatically get IDs, which can be linked to, except in comments.
+GFM extends the standard markdown standard so that all Markdown-rendered headers automatically
+get IDs, which can be linked to, except in comments.
-On hover, a link to those IDs becomes visible to make it easier to copy the link to the header to use it somewhere else.
+On hover, a link to those IDs becomes visible to make it easier to copy the link to
+the header to use it somewhere else.
The IDs are generated from the content of the header according to the following rules:
@@ -606,7 +773,7 @@ The IDs are generated from the content of the header according to the following
1. If a header with the same ID has already been generated, a unique
incrementing number is appended, starting at 1.
-For example:
+Example:
```
# This header has spaces in it
@@ -626,215 +793,164 @@ Would generate the following link IDs:
1. `this-header-has-spaces-in-it-2`
1. `this-header-has-3-5-in-it-and-parentheses`
-Note that the Emoji processing happens before the header IDs are generated, so the Emoji is converted to an image which then gets removed from the ID.
-
-### Emphasis
-
-Examples:
-
-```
-Emphasis, aka italics, with *asterisks* or _underscores_.
-
-Strong emphasis, aka bold, with **asterisks** or __underscores__.
-
-Combined emphasis with **asterisks and _underscores_**.
-
-Strikethrough uses two tildes. ~~Scratch this.~~
-```
-
-Becomes:
-
-Emphasis, aka italics, with *asterisks* or _underscores_.
-
-Strong emphasis, aka bold, with **asterisks** or __underscores__.
-
-Combined emphasis with **asterisks and _underscores_**.
-
-Strikethrough uses two tildes. ~~Scratch this.~~
-
-### Lists
-
-Examples:
-
-```
-1. First ordered list item
-2. Another item
- * Unordered sub-list.
-1. Actual numbers don't matter, just that it's a number
- 1. Ordered sub-list
-4. And another item.
-
-* Unordered list can use asterisks
-- Or minuses
-+ Or pluses
-```
-
-Becomes:
-
-1. First ordered list item
-2. Another item
- * Unordered sub-list.
-1. Actual numbers don't matter, just that it's a number
- 1. Ordered sub-list
-4. And another item.
-
-* Unordered list can use asterisks
-- Or minuses
-+ Or pluses
-
-If a list item contains multiple paragraphs,
-each subsequent paragraph should be indented to the same level as the start of the list item text
+Note that the Emoji processing happens before the header IDs are generated, so the
+Emoji is converted to an image which is then removed from the ID.
-Example:
-
-```
-1. First ordered list item
-
- Second paragraph of first item.
-
-2. Another item
-```
+### Horizontal Rule
-Becomes:
+It's very simple to create a horizontal rule, by using three or more hyphens, asterisks,
+or underscores:
-1. First ordered list item
+```markdown
+Three or more hyphens,
- Paragraph of first item.
+---
-2. Another item
+asterisks,
-If the paragraph of the first item is not indented with the proper number of spaces,
-the paragraph will appear outside the list, instead of properly indented under the list item.
+***
-Example:
+or underscores
+___
```
-1. First ordered list item
- Paragraph of first item.
+Three or more hyphens,
-2. Another item
-```
+---
-Becomes:
+asterisks,
-1. First ordered list item
+***
- Paragraph of first item.
+or underscores
-2. Another item
+___
-### Links
+### Images
-There are two ways to create links, inline-style and reference-style.
+Examples:
```markdown
-[I'm an inline-style link](https://www.google.com)
-[I'm a link to a repository file in the same directory](index.md)
-[I am an absolute reference within the repository](/doc/user/index.md)
-[I'm a relative link to the Milestones page](../README.md)
+Inline-style (hover to see title text):
-[I link to a section on a different markdown page, using a header ID](index.md#overview)
-[I link to a different section on the same page, using the header ID](#header-ids-and-links)
+![alt text](img/markdown_logo.png "Title Text")
-[I'm a reference-style link][Arbitrary case-insensitive reference text]
-[You can use numbers for reference-style link definitions][1]
-Or leave it empty and use the [link text itself][]
+Reference-style (hover to see title text):
-Some text to show that the reference links can follow later.
+![alt text1][logo]
-[arbitrary case-insensitive reference text]: https://www.mozilla.org
-[1]: http://slashdot.org
-[link text itself]: https://www.reddit.com
+[logo]: img/markdown_logo.png "Title Text"
```
->**Note:**
-Relative links do not allow referencing project files in a wiki page or wiki
-page in a project file. The reason for this is that, in GitLab, wiki is always
-a separate Git repository. For example, `[I'm a reference-style link](style)`
-will point the link to `wikis/style` when the link is inside of a wiki markdown file.
-
-### Images
+Inline-style (hover to see title text):
-Examples:
+![alt text](img/markdown_logo.png "Title Text")
- Here's our logo (hover to see the title text):
+Reference-style (hover to see title text):
- Inline-style:
- ![alt text](img/markdown_logo.png)
+![alt text][logo]
- Reference-style:
- ![alt text1][logo]
+[logo]: img/markdown_logo.png "Title Text"
- [logo]: img/markdown_logo.png
+#### Videos
-Becomes:
+> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#videos).
-Here's our logo:
+Image tags that link to files with a video extension are automatically converted to
+a video player. The valid video extensions are `.mp4`, `.m4v`, `.mov`, `.webm`, and `.ogv`:
-Inline-style:
+```md
+Here's a sample video:
-![alt text](img/markdown_logo.png)
+![Sample Video](img/markdown_video.mp4)
+```
-Reference-style:
+Here's a sample video:
-![alt text][logo]
+![Sample Video](img/markdown_video.mp4)
-[logo]: img/markdown_logo.png
+### Inline HTML
-### Blockquotes
+> To see the markdown rendered within HTML in the second example, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-html).
-Examples:
+You can also use raw HTML in your Markdown, and it'll usually work pretty well.
-```
-> Blockquotes are very handy in email to emulate reply text.
-> This line is part of the same quote.
+See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/1.11.0/HTML/Pipeline/SanitizationFilter#WHITELIST-constant)
+class for the list of allowed HTML tags and attributes. In addition to the default
+`SanitizationFilter` whitelist, GitLab allows `span`, `abbr`, `details` and `summary` elements.
-Quote break.
+```html
+<dl>
+ <dt>Definition list</dt>
+ <dd>Is something people use sometimes.</dd>
-> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
+ <dt>Markdown in HTML</dt>
+ <dd>Does *not* work **very** well. HTML <em>tags</em> will <b>always</b> work.</dd>
+</dl>
```
-Becomes:
-
-> Blockquotes are very handy in email to emulate reply text.
-> This line is part of the same quote.
-
-Quote break.
-
-> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
-
-### Inline HTML
+<dl>
+ <dt>Definition list</dt>
+ <dd>Is something people use sometimes.</dd>
-You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
+ <dt>Markdown in HTML</dt>
+ <dd>Does *not* work **very** well. HTML <em>tags</em> will <b>always</b> work.</dd>
+</dl>
-See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/1.11.0/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows `span`, `abbr`, `details` and `summary` elements.
+---
-Examples:
+It is still possible to use markdown inside HTML tags, but only if the lines containing markdown
+are separated into their own lines:
-```
+```html
<dl>
- <dt>Definition list</dt>
- <dd>Is something people use sometimes.</dd>
+ <dt>Markdown in HTML</dt>
+ <dd>Does *not* work **very** well. HTML tags will always work.</dd>
<dt>Markdown in HTML</dt>
- <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd>
+ <dd>
+
+ Does *not* work **very** well. HTML tags will always work.
+
+ </dd>
</dl>
```
-Becomes:
+<!-- Note: The example below uses HTML to force correct rendering on docs.gitlab.com, markdown will be fine in GitLab -->
<dl>
- <dt>Definition list</dt>
- <dd>Is something people use sometimes.</dd>
+ <dt>Markdown in HTML</dt>
+ <dd>Does *not* work **very** well. HTML tags will always work.</dd>
<dt>Markdown in HTML</dt>
- <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd>
+ <dd>
+
+ Does <em>not</em> work <b>very</b> well. HTML tags will always work.
+
+ </dd>
</dl>
#### Details and Summary
-Content can be collapsed using HTML's [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) and [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary) tags. This is especially useful for collapsing long logs so they take up less screen space.
+> To see the markdown rendered within HTML in the second example, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#details-and-summary).
+
+Content can be collapsed using HTML's [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
+and [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
+tags. This is especially useful for collapsing long logs so they take up less screen space.
+
+```html
+<p>
+<details>
+<summary>Click me to collapse/fold.</summary>
+
+These details <em>will</em> remain <strong>hidden</strong> until expanded.
+
+<pre><code>PASTE LOGS HERE</code></pre>
+
+</details>
+</p>
+```
<p>
<details>
@@ -847,7 +963,10 @@ These details <em>will</em> remain <strong>hidden</strong> until expanded.
</details>
</p>
-**Note:** Markdown inside these tags is supported, as long as you have a blank line after the `</summary>` tag and before the `</details>` tag, as shown in the example.
+---
+
+Markdown inside these tags is supported as well, as long as you have a blank line
+after the `</summary>` tag and before the `</details>` tag, as shown in the example:
```html
<details>
@@ -860,232 +979,302 @@ These details _will_ remain **hidden** until expanded.
</details>
```
-### Horizontal Rule
+<!-- Note: The example below uses HTML to force correct rendering on docs.gitlab.com, markdown will be fine in GitLab -->
-Examples:
+<details>
+<summary>Click me to collapse/fold.</summary>
-```
-Three or more...
+These details <em>will</em> remain <b>hidden</b> until expanded.
----
+ PASTE LOGS HERE
+
+</details>
-Hyphens
+### Line Breaks
-***
+A line break will be inserted (a new paragraph will start) if the previous text is
+ended with two newlines, i.e. you hit <kbd>Enter</kbd> twice in a row. If you only
+use one newline (hit <kbd>Enter</kbd> once), the next sentence will be part of the
+same paragraph. This is useful if you want to keep long lines from wrapping, and keep
+them easily editable:
-Asterisks
+```markdown
+Here's a line for us to start with.
-___
+This longer line is separated from the one above by two newlines, so it will be a *separate paragraph*.
-Underscores
+This line is also a separate paragraph, but...
+These lines are only separated by single newlines,
+so they *do not break* and just follow the previous lines
+in the *same paragraph*.
```
-Becomes:
+Here's a line for us to start with.
-Three or more...
+This longer line is separated from the one above by two newlines, so it will be a *separate paragraph*.
----
+This line is also a separate paragraph, but...
+These lines are only separated by single newlines,
+so they *do not break* and just follow the previous lines
+in the *same paragraph*.
-Hyphens
+#### Newlines
-***
+GFM adheres to the markdown specification in how [paragraphs and line breaks are handled](https://spec.commonmark.org/current/).
-Asterisks
+A paragraph is simply one or more consecutive lines of text, separated by one or
+more blank lines (i.e. two newlines at the end of the first paragraph), as [explained above](#line-breaks).
-___
+If you need more control over line-breaks or soft returns, you can add a single line-break
+by ending a line with a backslash, or two or more spaces. Two newlines in a row will create a new
+paragraph, with a blank line in between:
-Underscores
+```markdown
+First paragraph.
+Another line in the same paragraph.
+A third line in the same paragraph, but this time ending with two spaces.{space}{space}
+A new line directly under the first paragraph.
+
+Second paragraph.
+Another line, this time ending with a backslash.\
+A new line due to the previous backslash.
+```
-### Line Breaks
+<!-- (Do *NOT* remove the two ending whitespaces in the third line) -->
+<!-- (They are needed for the Markdown text to render correctly) -->
-A good way to learn how line breaks work is to experiment and discover -- hit <kbd>Enter</kbd> once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. The "Preview" tab is your friend.
+First paragraph.
+Another line in the same paragraph.
+A third line in the same paragraph, but this time ending with two spaces.
+A new line directly under the first paragraph.
-Here are some things to try out:
+<!-- (Do *NOT* remove the two ending whitespaces in the second line) -->
+<!-- (They are needed for the Markdown text to render correctly on docs.gitlab.com, the backslash works fine inside GitLab itself) -->
-Examples:
+Second paragraph.
+Another line, this time ending with a backslash.
+A new line due to the previous backslash.
-```
-Here's a line for us to start with.
+### Links
-This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
+There are two ways to create links, inline-style and reference-style:
-This line is also a separate paragraph, but...
-This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
+```md
+- This is an [inline-style link](https://www.google.com)
+- This is a [link to a repository file in the same directory](index.md)
+- This is a [relative link to a readme one directory higher](../README.md)
+- This is a [link that also has title text](https://www.google.com "This link takes you to Google!")
-This line is also a separate paragraph, and...
-This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
+Using header ID anchors:
-spaces.
-```
+- This links to [a section on a different markdown page, using a "#" and the header ID](index.md#overview)
+- This links to [a different section on the same page, using a "#" and the header ID](#header-ids-and-links)
-Becomes:
+Using references:
-Here's a line for us to start with.
+- This is a [reference-style link, see below][Arbitrary case-insensitive reference text]
+- You can [use numbers for reference-style link definitions, see below][1]
+- Or leave it empty and use the [link text itself][], see below.
-This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
+Some text to show that the reference links can follow later.
-This line is also a separate paragraph, but...
-This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
+[arbitrary case-insensitive reference text]: https://www.mozilla.org
+[1]: http://slashdot.org
+[link text itself]: https://www.reddit.com
+```
-This line is also a separate paragraph, and...
-This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
+- This is an [inline-style link](https://www.google.com)
+- This is a [link to a repository file in the same directory](index.md)
+- This is a [relative link to a readme one directory higher](../README.md)
+- This is a [link that also has title text](https://www.google.com "This link takes you to Google!")
-spaces.
+Using header ID anchors:
-### Tables
+- This links to [a section on a different markdown page, using a "#" and the header ID](index.md#overview)
+- This links to [a different section on the same page, using a "#" and the header ID](#header-ids-and-links)
-Tables aren't part of the core Markdown spec, but they are part of GFM.
+Using references:
-Example:
+- This is a [reference-style link, see below][Arbitrary case-insensitive reference text]
+- You can [use numbers for reference-style link definitions, see below][1]
+- Or leave it empty and use the [link text itself][], see below.
-```
-| header 1 | header 2 |
-| -------- | -------- |
-| cell 1 | cell 2 |
-| cell 3 | cell 4 |
-```
-
-Becomes:
+Some text to show that the reference links can follow later.
-| header 1 | header 2 |
-| -------- | -------- |
-| cell 1 | cell 2 |
-| cell 3 | cell 4 |
+[arbitrary case-insensitive reference text]: https://www.mozilla.org
+[1]: http://slashdot.org
+[link text itself]: https://www.reddit.com
-**Note:** The row of dashes between the table header and body must have at least three dashes in each column.
+NOTE: **Note:** Relative links do not allow the referencing of project files in a wiki
+page, or a wiki page in a project file. The reason for this is that a wiki is always
+in a separate Git repository in GitLab. For example, `[I'm a reference-style link](style)`
+will point the link to `wikis/style` only when the link is inside of a wiki markdown file.
-By including colons in the header row, you can align the text within that column.
+#### URL auto-linking
-Example:
+GFM will autolink almost any URL you put into your text:
-```
-| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned |
-| :----------- | :------: | ------------: | :----------- | :------: | ------------: |
-| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 |
-| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 |
+```markdown
+* https://www.google.com
+* https://google.com/
+* ftp://ftp.us.debian.org/debian/
+* smb://foo/bar/baz
+* irc://irc.freenode.net/gitlab
+* http://localhost:3000
```
-Becomes:
+* https://www.google.com
+* https://google.com/
+* ftp://ftp.us.debian.org/debian/
+* smb://foo/bar/baz
+* irc://irc.freenode.net/gitlab
+* http://localhost:3000
-| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned |
-| :----------- | :------: | ------------: | :----------- | :------: | ------------: |
-| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 |
-| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 |
+### Lists
-### Footnotes
+Ordered and unordered lists can be easily created. Add the number you want the list
+to start with, like `1. ` (with a space) at the start of each line for ordered lists.
+After the first number, it does not matter what number you use, ordered lists will be
+numbered automatically by vertical order, so repeating `1. ` for all items in the
+same list is common. If you start with a number other than `1. `, it will use that as the first
+number, and count up from there.
-Example:
+Add a `* `, `- ` or `+ ` (with a space) at the start of each line for unordered lists, but
+you should not use a mix of them.
-```
-You can add footnotes to your text as follows.[^2]
-[^2]: This is my awesome footnote.
+Examples:
+
+```md
+1. First ordered list item
+2. Another item
+ * Unordered sub-list.
+1. Actual numbers don't matter, just that it's a number
+ 1. Ordered sub-list
+ 1. Next ordered sub-list item
+4. And another item.
+
+* Unordered lists can use asterisks
+- Or minuses
++ Or pluses
```
-Becomes:
+1. First ordered list item
+2. Another item
+ * Unordered sub-list.
+1. Actual numbers don't matter, just that it's a number
+ 1. Ordered sub-list
+ 1. Next ordered sub-list item
+4. And another item.
-You can add footnotes to your text as follows.[^2]
+* Unordered lists can use asterisks
+- Or minuses
++ Or pluses
-### Superscripts / Subscripts
+---
-CommonMark and GFM currently do not support the superscript syntax ( `x^2` ) that Redcarpet does. You can use the standard HTML syntax for superscripts and subscripts.
+If a list item contains multiple paragraphs, each subsequent paragraph should be indented
+to the same level as the start of the list item text.
-```
-The formula for water is H<sub>2</sub>O
-while the equation for the theory of relativity is E = mc<sup>2</sup>.
-```
+Example:
-The formula for water is H<sub>2</sub>O while the equation for the theory of relativity is E = mc<sup>2</sup>.
+```markdown
+1. First ordered list item
-## Wiki-specific Markdown
+ Second paragraph of first item.
-The following examples show how links inside wikis behave.
+2. Another item
+```
-### Wiki - Direct page link
+1. First ordered list item
-A link which just includes the slug for a page will point to that page,
-_at the base level of the wiki_.
+ Second paragraph of first item.
-This snippet would link to a `documentation` page at the root of your wiki:
+2. Another item
-```markdown
-[Link to Documentation](documentation)
-```
+---
-### Wiki - Direct file link
+If the paragraph of the first item is not indented with the proper number of spaces,
+the paragraph will appear outside the list, instead of properly indented under the list item.
-Links with a file extension point to that file, _relative to the current page_.
+Example:
-If this snippet was placed on a page at `<your_wiki>/documentation/related`,
-it would link to `<your_wiki>/documentation/file.md`:
+```
+1. First ordered list item
-```markdown
-[Link to File](file.md)
+ Paragraph of first item.
+
+2. Another item
```
-### Wiki - Hierarchical link
+1. First ordered list item
-A link can be constructed relative to the current wiki page using `./<page>`,
-`../<page>`, etc.
+ Paragraph of first item.
-- If this snippet was placed on a page at `<your_wiki>/documentation/main`,
- it would link to `<your_wiki>/documentation/related`:
+2. Another item
- ```markdown
- [Link to Related Page](related)
- ```
+### Superscripts / Subscripts
-- If this snippet was placed on a page at `<your_wiki>/documentation/related/content`,
- it would link to `<your_wiki>/documentation/main`:
+CommonMark and GFM currently do not support the superscript syntax ( `x^2` ) that
+Redcarpet does. You can use the standard HTML syntax for superscripts and subscripts:
- ```markdown
- [Link to Related Page](../main)
- ```
+```html
+The formula for water is H<sub>2</sub>O
+while the equation for the theory of relativity is E = mc<sup>2</sup>.
+```
-- If this snippet was placed on a page at `<your_wiki>/documentation/main`,
- it would link to `<your_wiki>/documentation/related.md`:
+The formula for water is H<sub>2</sub>O
+while the equation for the theory of relativity is E = mc<sup>2</sup>.
- ```markdown
- [Link to Related Page](related.md)
- ```
+### Tables
-- If this snippet was placed on a page at `<your_wiki>/documentation/related/content`,
- it would link to `<your_wiki>/documentation/main.md`:
+Tables aren't part of the core Markdown spec, but they are part of GFM.
- ```markdown
- [Link to Related Page](../main.md)
- ```
+1. The first line contains the headers, separated by "pipes" (`|`).
+1. The second line separates the headers from the cells, and must contain three or more dashes.
+1. The third, and any following lines, contain the cell values.
+ - You **can't** have cells separated over many lines in the markdown, they must be kept to single lines,
+ but they can be very long. You can also include HTML `<br>` tags to force newlines if needed.
+ - The cell sizes **don't** have to match each other. They are flexible, but must be separated
+ by pipes (`|`).
+ - You **can** have blank cells.
-### Wiki - Root link
+Example:
-A link starting with a `/` is relative to the wiki root.
+```markdown
+| header 1 | header 2 | header 3 |
+| --- | ------ |----------|
+| cell 1 | cell 2 | cell 3 |
+| cell 4 | cell 5 is longer | cell 6 is much longer than the others, but that's ok. It will eventually wrap the text when the cell is too large for the display size. |
+| cell 7 | | cell <br> 9 |
+```
-- This snippet links to `<wiki_root>/documentation`:
+| header 1 | header 2 | header 3 |
+| --- | ------ |---------:|
+| cell 1 | cell 2 | cell 3 |
+| cell 4 | cell 5 is longer | cell 6 is much longer than the others, but that's ok. It will eventually wrap the text when the cell is too large for the display size. |
+| cell 7 | | cell <br> 9 |
- ```markdown
- [Link to Related Page](/documentation)
- ```
+Additionally, you can choose the alignment of text within columns by adding colons (`:`)
+to the sides of the "dash" lines in the second row. This will affect every cell in the column.
-- This snippet links to `<wiki_root>/miscellaneous.md`:
+> Note that the headers are always right aligned [within GitLab itself itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#tables).
- ```markdown
- [Link to Related Page](/miscellaneous.md)
- ```
+```markdown
+| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned |
+| :--- | :---: | ---: | :----------- | :------: | ------------: |
+| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 |
+| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 |
+```
+
+| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned |
+| :--- | :---: | ---: | :----------- | :------: | ------------: |
+| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 |
+| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 |
## References
- This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
-- The original [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
-- The detailed specification for CommonMark can be found in the [CommonMark Spec][commonmark-spec]
+- The original [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax)
+ at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
+- The detailed specification for CommonMark can be found in the [CommonMark Spec](https://spec.commonmark.org/current/)
- The [CommonMark Dingus](http://try.commonmark.org) is a handy tool for testing CommonMark syntax.
-
-[^1]: This link will be broken if you see this document from the Help page or docs.gitlab.com
-[^2]: This is my awesome footnote.
-
-[markdown.md]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md
-[rouge]: http://rouge.jneen.net/ "Rouge website"
-[redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website"
-[katex]: https://github.com/Khan/KaTeX "KaTeX website"
-[katex-subset]: https://katex.org/docs/supported.html "Macros supported by KaTeX"
-[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual"
-[commonmarker]: https://github.com/gjtorikian/commonmarker
-[commonmark-spec]: https://spec.commonmark.org/current/
diff --git a/doc/user/project/pages/getting_started_part_three.md b/doc/user/project/pages/getting_started_part_three.md
index d585c19fc5c..bc9a11504cd 100644
--- a/doc/user/project/pages/getting_started_part_three.md
+++ b/doc/user/project/pages/getting_started_part_three.md
@@ -1,5 +1,5 @@
---
-last_updated: 2019-06-04
+last_updated: 2019-06-25
type: concepts, reference, howto
---
@@ -138,9 +138,9 @@ verify your domain's ownership with a TXT record:
> - **Do not** add any special chars after the default Pages
domain. E.g., **do not** point your `subdomain.domain.com` to
`namespace.gitlab.io.` or `namespace.gitlab.io/`.
-> - GitLab Pages IP on GitLab.com [was changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) in 2017
+> - GitLab Pages IP on GitLab.com [was changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) in 2017.
> - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2018/07/19/gcp-move-update/#gitlab-pages-and-custom-domains)
- from `52.167.214.135` to `35.185.44.232` in 2018
+ from `52.167.214.135` to `35.185.44.232` in 2018.
### Add your custom domain to GitLab Pages settings
@@ -199,7 +199,7 @@ Certificates are NOT required to add to your custom
highly recommendable.
Let's start with an introduction to the importance of HTTPS.
-Alternatively, jump ahead to [adding certificates to your project](#adding-certificates-to-your-project).
+Alternatively, jump ahead to [adding certificates to your project](#adding-certificates-to-pages).
### Why should I care about HTTPS?
@@ -255,12 +255,12 @@ which also offers a [free CDN service](https://blog.cloudflare.com/cloudflares-f
Their certs are valid up to 15 years. See the tutorial on
[how to add a CloudFlare Certificate to your GitLab Pages website](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/).
-### Adding certificates to your project
+### Adding certificates to Pages
Regardless the CA you choose, the steps to add your certificate to
your Pages project are the same.
-### What do you need
+#### Requirements
1. A PEM certificate
1. An intermediate certificate
@@ -270,7 +270,7 @@ your Pages project are the same.
These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**.
-### What's what?
+#### Certificate types
- A PEM certificate is the certificate generated by the CA,
which needs to be added to the field **Certificate (PEM)**.
@@ -283,21 +283,32 @@ These fields are found under your **Project**'s **Settings** > **Pages** > **New
- A private key is an encrypted key which validates
your PEM against your domain.
-### Now what?
+#### Add the certificate to your project
-Now that you hopefully understand why you need all
-of this, it's simple:
+Once you've met the requirements:
-- Your PEM certificate needs to be added to the first field
+- Your PEM certificate needs to be added to the first field.
- If your certificate is missing its intermediate, copy
and paste the root certificate (usually available from your CA website)
and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/),
just jumping a line between them.
-- Copy your private key and paste it in the last field
+- Copy your private key and paste it in the last field.
->**Note:**
+NOTE: **Note:**
**Do not** open certificates or encryption keys in
regular text editors. Always use code editors (such as
Sublime Text, Atom, Dreamweaver, Brackets, etc).
-_Read on about [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md)_
+## Force HTTPS for GitLab Pages websites
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28857) in GitLab 10.7.
+
+To make your website's visitors even more secure, you can choose to
+force HTTPS for GitLab Pages. By doing so, all attempts to visit your
+website via HTTP will be automatically redirected to HTTPS via 301.
+
+It works with both GitLab's default domain and with your custom
+domain (as long as you've set a valid certificate for it).
+
+To enable this setting, navigate to your project's **Settings > Pages**
+and tick the checkbox **Force HTTPS (requires valid certificates)**.
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index be4215b1934..2bf8d4dfe7b 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -18,7 +18,7 @@ Adjust your project's name, description, avatar, [default branch](../repository/
![general project settings](img/general_settings.png)
-The project description also partially supports [standard markdown](../../markdown.md#standard-markdown). You can use [emphasis](../../markdown.md#emphasis), [links](../../markdown.md#links), and [line-breaks](../../markdown.md#line-breaks) to add more context to the project description.
+The project description also partially supports [standard markdown](../../markdown.md#standard-markdown-and-extensions-in-gitlab). You can use [emphasis](../../markdown.md#emphasis), [links](../../markdown.md#links), and [line-breaks](../../markdown.md#line-breaks) to add more context to the project description.
### Sharing and permissions
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 6767ef882cb..3c5c1a9fd5f 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -36,10 +36,6 @@ module API
given akismet_enabled: ->(val) { val } do
requires :akismet_api_key, type: String, desc: 'Generate API key at http://www.akismet.com'
end
- optional :clientside_sentry_enabled, type: Boolean, desc: 'Sentry can also be used for reporting and logging clientside exceptions. https://sentry.io/for/javascript/'
- given clientside_sentry_enabled: ->(val) { val } do
- requires :clientside_sentry_dsn, type: String, desc: 'Clientside Sentry Data Source Name'
- end
optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts"
optional :default_project_creation, type: Integer, values: ::Gitlab::Access.project_creation_values, desc: 'Determine if developers can create projects in the group'
@@ -114,10 +110,6 @@ module API
end
optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up'
- optional :sentry_enabled, type: Boolean, desc: 'Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here: https://getsentry.com'
- given sentry_enabled: ->(val) { val } do
- requires :sentry_dsn, type: String, desc: 'Sentry Data Source Name'
- end
optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.'
optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects'
given shared_runners_enabled: ->(val) { val } do
diff --git a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
index 46c4c755729..8a84744aa2d 100644
--- a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
@@ -17,7 +17,7 @@ code_quality:
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
- "registry.gitlab.com/gitlab-org/security-products/codequality:11-8-stable" /code
+ "registry.gitlab.com/gitlab-org/security-products/codequality:12-0-stable" /code
artifacts:
reports:
codequality: gl-code-quality-report.json
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index d21b98d36ea..a80ce462ab0 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -271,26 +271,30 @@ module Gitlab
end
def find_commit(revision)
- if Gitlab::SafeRequestStore.active?
- # We don't use Gitlab::SafeRequestStore.fetch(key) { ... } directly
- # because `revision` can be a branch name, so we can't use it as a key
- # as it could point to another commit later on (happens a lot in
- # tests).
- key = {
- storage: @gitaly_repo.storage_name,
- relative_path: @gitaly_repo.relative_path,
- commit_id: revision
- }
- return Gitlab::SafeRequestStore[key] if Gitlab::SafeRequestStore.exist?(key)
-
- commit = call_find_commit(revision)
- return unless commit
-
- key[:commit_id] = commit.id unless GitalyClient.ref_name_caching_allowed?
+ return call_find_commit(revision) unless Gitlab::SafeRequestStore.active?
+
+ # We don't use Gitlab::SafeRequestStore.fetch(key) { ... } directly
+ # because `revision` can be a branch name, so we can't use it as a key
+ # as it could point to another commit later on (happens a lot in
+ # tests).
+ key = {
+ storage: @gitaly_repo.storage_name,
+ relative_path: @gitaly_repo.relative_path,
+ commit_id: revision
+ }
+ return Gitlab::SafeRequestStore[key] if Gitlab::SafeRequestStore.exist?(key)
+
+ commit = call_find_commit(revision)
+
+ if GitalyClient.ref_name_caching_allowed?
Gitlab::SafeRequestStore[key] = commit
- else
- call_find_commit(revision)
+ return commit
end
+
+ return unless commit
+
+ key[:commit_id] = commit.id
+ Gitlab::SafeRequestStore[key] = commit
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 582c3065189..92917028851 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -16,8 +16,8 @@ module Gitlab
gon.shortcuts_path = Gitlab::Routing.url_helpers.help_page_path('shortcuts')
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
- if Gitlab::CurrentSettings.clientside_sentry_enabled
- gon.sentry_dsn = Gitlab::CurrentSettings.clientside_sentry_dsn
+ if Gitlab.config.sentry.enabled
+ gon.sentry_dsn = Gitlab.config.sentry.clientside_dsn
gon.sentry_environment = Gitlab.config.sentry.environment
end
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index a07b1246bee..a13b3f9e069 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -53,7 +53,6 @@ module Gitlab
sent_notifications
slash-command-logo.png
snippets
- u
unsubscribes
uploads
users
diff --git a/lib/gitlab/sentry.rb b/lib/gitlab/sentry.rb
index 72c44114001..764db14d720 100644
--- a/lib/gitlab/sentry.rb
+++ b/lib/gitlab/sentry.rb
@@ -4,7 +4,7 @@ module Gitlab
module Sentry
def self.enabled?
(Rails.env.production? || Rails.env.development?) &&
- Gitlab::CurrentSettings.sentry_enabled?
+ Gitlab.config.sentry.enabled
end
def self.context(current_user = nil)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e63ccbc39c7..9ea368816f9 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -396,6 +396,9 @@ msgstr ""
msgid "<no name set>"
msgstr ""
+msgid "<no scopes selected>"
+msgstr ""
+
msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
msgstr ""
@@ -507,6 +510,54 @@ msgstr ""
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
+msgid "AccessTokens|Access Tokens"
+msgstr ""
+
+msgid "AccessTokens|Are you sure? Any RSS or calendar URLs currently in use will stop working."
+msgstr ""
+
+msgid "AccessTokens|Are you sure? Any issue email addresses currently in use will stop working."
+msgstr ""
+
+msgid "AccessTokens|Created"
+msgstr ""
+
+msgid "AccessTokens|Feed token"
+msgstr ""
+
+msgid "AccessTokens|Incoming email token"
+msgstr ""
+
+msgid "AccessTokens|It cannot be used to access any other data."
+msgstr ""
+
+msgid "AccessTokens|Keep this token secret. Anyone who gets ahold of it can create issues as if they were you. You should %{link_reset_it} if that ever happens."
+msgstr ""
+
+msgid "AccessTokens|Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds or your calendar feed as if they were you. You should %{link_reset_it} if that ever happens."
+msgstr ""
+
+msgid "AccessTokens|Personal Access Tokens"
+msgstr ""
+
+msgid "AccessTokens|They are the only accepted password when you have Two-Factor Authentication (2FA) enabled."
+msgstr ""
+
+msgid "AccessTokens|You can also use personal access tokens to authenticate against Git over HTTP."
+msgstr ""
+
+msgid "AccessTokens|You can generate a personal access token for each application you use that needs access to the GitLab API."
+msgstr ""
+
+msgid "AccessTokens|Your feed token is used to authenticate you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar, and is included in those feed URLs."
+msgstr ""
+
+msgid "AccessTokens|Your incoming email token is used to authenticate you when you create a new issue by email, and is included in your personal project-specific email addresses."
+msgstr ""
+
+msgid "AccessTokens|reset it"
+msgstr ""
+
msgid "Account"
msgstr ""
@@ -519,6 +570,9 @@ msgstr ""
msgid "Active"
msgstr ""
+msgid "Active %{type} Tokens (%{token_length})"
+msgstr ""
+
msgid "Active Sessions"
msgstr ""
@@ -537,6 +591,9 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a %{type} token"
+msgstr ""
+
msgid "Add a GPG key"
msgstr ""
@@ -1164,6 +1221,9 @@ msgstr ""
msgid "Are you sure you want to reset the health check token?"
msgstr ""
+msgid "Are you sure you want to revoke this %{type} Token? This action cannot be undone."
+msgstr ""
+
msgid "Are you sure you want to revoke this nickname?"
msgstr ""
@@ -2992,6 +3052,9 @@ msgstr ""
msgid "Copy link"
msgstr ""
+msgid "Copy personal access token to clipboard"
+msgstr ""
+
msgid "Copy reference to clipboard"
msgstr ""
@@ -3040,6 +3103,9 @@ msgstr ""
msgid "Create"
msgstr ""
+msgid "Create %{type} token"
+msgstr ""
+
msgid "Create New Directory"
msgstr ""
@@ -3804,9 +3870,6 @@ msgstr ""
msgid "Enable HTML emails"
msgstr ""
-msgid "Enable Sentry for error reporting and logging."
-msgstr ""
-
msgid "Enable access to the Performance Bar for a given group."
msgstr ""
@@ -4026,9 +4089,6 @@ msgstr ""
msgid "Error"
msgstr ""
-msgid "Error Reporting and Logging"
-msgstr ""
-
msgid "Error Tracking"
msgstr ""
@@ -4251,6 +4311,12 @@ msgstr ""
msgid "Expired %{expiredOn}"
msgstr ""
+msgid "Expires"
+msgstr ""
+
+msgid "Expires at"
+msgstr ""
+
msgid "Expires in %{expires_at}"
msgstr ""
@@ -5966,6 +6032,9 @@ msgstr ""
msgid "Make issue confidential."
msgstr ""
+msgid "Make sure you save it - you won't be able to access it again."
+msgstr ""
+
msgid "Make sure you're logged into the account that owns the projects you'd like to import."
msgstr ""
@@ -7095,6 +7164,9 @@ msgstr ""
msgid "Pick a name"
msgstr ""
+msgid "Pick a name for the application, and we'll give you a unique %{type} token."
+msgstr ""
+
msgid "Pin code"
msgstr ""
@@ -7620,6 +7692,9 @@ msgstr ""
msgid "Profiles|Full name"
msgstr ""
+msgid "Profiles|Impersonation"
+msgstr ""
+
msgid "Profiles|Include private contributions on my profile"
msgstr ""
@@ -7662,6 +7737,9 @@ msgstr ""
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Personal Access"
+msgstr ""
+
msgid "Profiles|Position and size your new avatar"
msgstr ""
@@ -7797,6 +7875,12 @@ msgstr ""
msgid "Profiles|e.g. My MacBook key"
msgstr ""
+msgid "Profiles|impersonation"
+msgstr ""
+
+msgid "Profiles|personal access"
+msgstr ""
+
msgid "Profiles|username"
msgstr ""
@@ -8830,6 +8914,9 @@ msgstr ""
msgid "Scoped label"
msgstr ""
+msgid "Scopes"
+msgstr ""
+
msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
msgstr ""
@@ -10547,6 +10634,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user has no active %{type} Tokens."
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
@@ -10812,6 +10902,9 @@ msgstr ""
msgid "To preserve performance only <strong>%{display_size} of %{real_size}</strong> files are displayed."
msgstr ""
+msgid "To see all the user's personal access tokens you must impersonate them first."
+msgstr ""
+
msgid "To specify the notification level per project of a group you belong to, you need to visit project page and change notification level there."
msgstr ""
@@ -12035,6 +12128,9 @@ msgstr ""
msgid "Your Groups"
msgstr ""
+msgid "Your New Personal Access Token"
+msgstr ""
+
msgid "Your Primary Email will be used for avatar detection."
msgstr ""
diff --git a/package.json b/package.json
index 577967d23b9..ce3e5bd4490 100644
--- a/package.json
+++ b/package.json
@@ -96,7 +96,7 @@
"jszip-utils": "^0.0.2",
"katex": "^0.10.0",
"marked": "^0.3.12",
- "mermaid": "^8.0.0",
+ "mermaid": "^8.1.0",
"monaco-editor": "^0.15.6",
"monaco-editor-webpack-plugin": "^1.7.0",
"mousetrap": "^1.4.6",
diff --git a/spec/controllers/concerns/continue_params_spec.rb b/spec/controllers/concerns/continue_params_spec.rb
index 5e47f5e9f28..b4b62cbe1e3 100644
--- a/spec/controllers/concerns/continue_params_spec.rb
+++ b/spec/controllers/concerns/continue_params_spec.rb
@@ -18,6 +18,14 @@ describe ContinueParams do
ActionController::Parameters.new(continue: params)
end
+ it 'returns an empty hash if params are not present' do
+ allow(controller).to receive(:params) do
+ ActionController::Parameters.new
+ end
+
+ expect(controller.continue_params).to eq({})
+ end
+
it 'cleans up any params that are not allowed' do
allow(controller).to receive(:params) do
strong_continue_params(to: '/hello',
diff --git a/spec/controllers/concerns/internal_redirect_spec.rb b/spec/controllers/concerns/internal_redirect_spec.rb
index 97119438ca1..da68c8c8697 100644
--- a/spec/controllers/concerns/internal_redirect_spec.rb
+++ b/spec/controllers/concerns/internal_redirect_spec.rb
@@ -15,44 +15,71 @@ describe InternalRedirect do
subject(:controller) { controller_class.new }
describe '#safe_redirect_path' do
- it 'is `nil` for invalid uris' do
- expect(controller.safe_redirect_path('Hello world')).to be_nil
+ where(:input) do
+ [
+ 'Hello world',
+ '//example.com/hello/world',
+ 'https://example.com/hello/world'
+ ]
end
- it 'is `nil` for paths trying to include a host' do
- expect(controller.safe_redirect_path('//example.com/hello/world')).to be_nil
+ with_them 'being invalid' do
+ it 'returns nil' do
+ expect(controller.safe_redirect_path(input)).to be_nil
+ end
end
- it 'returns the path if it is valid' do
- expect(controller.safe_redirect_path('/hello/world')).to eq('/hello/world')
+ where(:input) do
+ [
+ '/hello/world',
+ '/-/ide/project/path'
+ ]
end
- it 'returns the path with querystring if it is valid' do
- expect(controller.safe_redirect_path('/hello/world?hello=world#L123'))
- .to eq('/hello/world?hello=world#L123')
+ with_them 'being valid' do
+ it 'returns the path' do
+ expect(controller.safe_redirect_path(input)).to eq(input)
+ end
+
+ it 'returns the path with querystring and fragment' do
+ expect(controller.safe_redirect_path("#{input}?hello=world#L123"))
+ .to eq("#{input}?hello=world#L123")
+ end
end
end
describe '#safe_redirect_path_for_url' do
- it 'is `nil` for invalid urls' do
- expect(controller.safe_redirect_path_for_url('Hello world')).to be_nil
+ where(:input) do
+ [
+ 'Hello world',
+ 'http://example.com/hello/world',
+ 'http://test.host:3000/hello/world'
+ ]
end
- it 'is `nil` for urls from a with a different host' do
- expect(controller.safe_redirect_path_for_url('http://example.com/hello/world')).to be_nil
+ with_them 'being invalid' do
+ it 'returns nil' do
+ expect(controller.safe_redirect_path_for_url(input)).to be_nil
+ end
end
- it 'is `nil` for urls from a with a different port' do
- expect(controller.safe_redirect_path_for_url('http://test.host:3000/hello/world')).to be_nil
+ where(:input) do
+ [
+ 'http://test.host/hello/world'
+ ]
end
- it 'returns the path if the url is on the same host' do
- expect(controller.safe_redirect_path_for_url('http://test.host/hello/world')).to eq('/hello/world')
- end
+ with_them 'being on the same host' do
+ let(:path) { URI(input).path }
- it 'returns the path including querystring if the url is on the same host' do
- expect(controller.safe_redirect_path_for_url('http://test.host/hello/world?hello=world#L123'))
- .to eq('/hello/world?hello=world#L123')
+ it 'returns the path' do
+ expect(controller.safe_redirect_path_for_url(input)).to eq(path)
+ end
+
+ it 'returns the path with querystring and fragment' do
+ expect(controller.safe_redirect_path_for_url("#{input}?hello=world#L123"))
+ .to eq("#{path}?hello=world#L123")
+ end
end
end
@@ -82,12 +109,16 @@ describe InternalRedirect do
end
describe '#host_allowed?' do
- it 'allows uris with the same host and port' do
+ it 'allows URI with the same host and port' do
expect(controller.host_allowed?(URI('http://test.host/test'))).to be(true)
end
- it 'rejects uris with other host and port' do
+ it 'rejects URI with other host' do
expect(controller.host_allowed?(URI('http://example.com/test'))).to be(false)
end
+
+ it 'rejects URI with other port' do
+ expect(controller.host_allowed?(URI('http://test.host:3000/test'))).to be(false)
+ end
end
end
diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb
index 3423fdf4c41..5ac5279e997 100644
--- a/spec/controllers/projects/forks_controller_spec.rb
+++ b/spec/controllers/projects/forks_controller_spec.rb
@@ -115,24 +115,34 @@ describe Projects::ForksController do
end
describe 'POST create' do
- def post_create
+ def post_create(params = {})
post :create,
params: {
namespace_id: project.namespace,
project_id: project,
namespace_key: user.namespace.id
- }
+ }.merge(params)
end
context 'when user is signed in' do
- it 'responds with status 302' do
+ before do
sign_in(user)
+ end
+ it 'responds with status 302' do
post_create
expect(response).to have_gitlab_http_status(302)
expect(response).to redirect_to(namespace_project_import_path(user.namespace, project))
end
+
+ it 'passes continue params to the redirect' do
+ continue_params = { to: '/-/ide/project/path', notice: 'message' }
+ post_create continue: continue_params
+
+ expect(response).to have_gitlab_http_status(302)
+ expect(response).to redirect_to(namespace_project_import_path(user.namespace, project, continue: continue_params))
+ end
end
context 'when user is not signed in' do
diff --git a/spec/factories/namespace/aggregation_schedules.rb b/spec/factories/namespace/aggregation_schedules.rb
new file mode 100644
index 00000000000..c172c3360e2
--- /dev/null
+++ b/spec/factories/namespace/aggregation_schedules.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :namespace_aggregation_schedules, class: Namespace::AggregationSchedule do
+ namespace
+ end
+end
diff --git a/spec/factories/namespace/root_storage_statistics.rb b/spec/factories/namespace/root_storage_statistics.rb
new file mode 100644
index 00000000000..54c5921eb44
--- /dev/null
+++ b/spec/factories/namespace/root_storage_statistics.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :namespace_root_storage_statistics, class: Namespace::RootStorageStatistics do
+ namespace
+ end
+end
diff --git a/spec/factories/namespaces.rb b/spec/factories/namespaces.rb
index 6feafa5ece9..0cfc6e3aa46 100644
--- a/spec/factories/namespaces.rb
+++ b/spec/factories/namespaces.rb
@@ -19,5 +19,13 @@ FactoryBot.define do
owner.namespace = namespace
end
end
+
+ trait :with_aggregation_schedule do
+ association :aggregation_schedule, factory: :namespace_aggregation_schedules
+ end
+
+ trait :with_root_storage_statistics do
+ association :root_storage_statistics, factory: :namespace_root_storage_statistics
+ end
end
end
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 2e7525e0513..c000165ccd9 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -102,9 +102,15 @@ describe 'Group issues page' do
end
context 'manual ordering' do
- let!(:issue1) { create(:issue, project: project, title: 'Issue #1') }
- let!(:issue2) { create(:issue, project: project, title: 'Issue #2') }
- let!(:issue3) { create(:issue, project: project, title: 'Issue #3') }
+ let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
+
+ let!(:issue1) { create(:issue, project: project, title: 'Issue #1', relative_position: 1) }
+ let!(:issue2) { create(:issue, project: project, title: 'Issue #2', relative_position: 2) }
+ let!(:issue3) { create(:issue, project: project, title: 'Issue #3', relative_position: 3) }
+
+ before do
+ sign_in(user_in_group)
+ end
it 'displays all issues' do
visit issues_group_path(group, sort: 'relative_position')
@@ -132,16 +138,23 @@ describe 'Group issues page' do
visit issues_group_path(group, sort: 'relative_position')
drag_to(selector: '.manual-ordering',
- scrollable: '#board-app',
- list_from_index: 0,
from_index: 0,
- to_index: 2,
- list_to_index: 0)
+ to_index: 2)
+
+ wait_for_requests
+
+ check_issue_order
+
+ visit issues_group_path(group, sort: 'relative_position')
+
+ check_issue_order
+ end
+ def check_issue_order
page.within('.manual-ordering') do
expect(find('.issue:nth-child(1) .title')).to have_content('Issue #2')
- expect(find('.issue:nth-child(2) .title')).to have_content('Issue #1')
- expect(find('.issue:nth-child(3) .title')).to have_content('Issue #3')
+ expect(find('.issue:nth-child(2) .title')).to have_content('Issue #3')
+ expect(find('.issue:nth-child(3) .title')).to have_content('Issue #1')
end
end
end
diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb
index edbab14f7c1..b08ccdc2a7c 100644
--- a/spec/features/projects/environments/environment_metrics_spec.rb
+++ b/spec/features/projects/environments/environment_metrics_spec.rb
@@ -9,11 +9,11 @@ describe 'Environment > Metrics' do
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:environment) { create(:environment, project: project) }
let(:current_time) { Time.now.utc }
+ let!(:staging) { create(:environment, name: 'staging', project: project) }
before do
project.add_developer(user)
- create(:deployment, environment: environment, deployable: build)
- stub_all_prometheus_requests(environment.slug)
+ stub_any_prometheus_request
sign_in(user)
visit_environment(environment)
@@ -23,15 +23,50 @@ describe 'Environment > Metrics' do
Timecop.freeze(current_time) { example.run }
end
+ shared_examples 'has environment selector' do
+ it 'has a working environment selector', :js do
+ click_link('See metrics')
+
+ expect(page).to have_metrics_path(environment)
+ expect(page).to have_css('div.js-environments-dropdown')
+
+ within('div.js-environments-dropdown') do
+ # Click on the dropdown
+ click_on(environment.name)
+
+ # Select the staging environment
+ click_on(staging.name)
+ end
+
+ expect(page).to have_metrics_path(staging)
+
+ wait_for_requests
+ end
+ end
+
+ context 'without deployments' do
+ it_behaves_like 'has environment selector'
+ end
+
context 'with deployments and related deployable present' do
+ before do
+ create(:deployment, environment: environment, deployable: build)
+ end
+
it 'shows metrics' do
click_link('See metrics')
expect(page).to have_css('div#prometheus-graphs')
end
+
+ it_behaves_like 'has environment selector'
end
def visit_environment(environment)
visit project_environment_path(environment.project, environment)
end
+
+ def have_metrics_path(environment)
+ have_current_path(metrics_project_environment_path(project, id: environment.id))
+ end
end
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index 683268d064a..e0fa9dbb5fa 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -118,19 +118,31 @@ describe 'Projects > Files > User edits files', :js do
wait_for_requests
end
- it 'inserts a content of a file in a forked project' do
- click_link('.gitignore')
- find('.js-edit-blob').click
-
+ def expect_fork_prompt
expect(page).to have_link('Fork')
expect(page).to have_button('Cancel')
+ expect(page).to have_content(
+ "You're not allowed to edit files in this project directly. "\
+ "Please fork this project, make your changes there, and submit a merge request."
+ )
+ end
- click_link('Fork')
-
+ def expect_fork_status
expect(page).to have_content(
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
)
+ end
+
+ it 'inserts a content of a file in a forked project' do
+ click_link('.gitignore')
+ click_button('Edit')
+
+ expect_fork_prompt
+
+ click_link('Fork')
+
+ expect_fork_status
find('.file-editor', match: :first)
@@ -140,12 +152,24 @@ describe 'Projects > Files > User edits files', :js do
expect(evaluate_script('ace.edit("editor").getValue()')).to eq('*.rbca')
end
+ it 'opens the Web IDE in a forked project' do
+ click_link('.gitignore')
+ click_button('Web IDE')
+
+ expect_fork_prompt
+
+ click_link('Fork')
+
+ expect_fork_status
+
+ expect(page).to have_css('.ide .multi-file-tab', text: '.gitignore')
+ end
+
it 'commits an edited file in a forked project' do
click_link('.gitignore')
find('.js-edit-blob').click
- expect(page).to have_link('Fork')
- expect(page).to have_button('Cancel')
+ expect_fork_prompt
click_link('Fork')
diff --git a/spec/features/raven_js_spec.rb b/spec/features/raven_js_spec.rb
index 9a049764dec..a4dd79b3179 100644
--- a/spec/features/raven_js_spec.rb
+++ b/spec/features/raven_js_spec.rb
@@ -10,7 +10,7 @@ describe 'RavenJS' do
end
it 'loads raven if sentry is enabled' do
- stub_application_setting(clientside_sentry_dsn: 'https://key@domain.com/id', clientside_sentry_enabled: true)
+ stub_sentry_settings
visit new_user_session_path
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 8a6901ea4e9..50befa7028d 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -90,7 +90,7 @@ describe 'Signup' do
expect(page).to have_content("Invalid input, please avoid emojis")
end
- it 'shows a pending message if the username availability is being fetched' do
+ it 'shows a pending message if the username availability is being fetched', :quarantine do
fill_in 'new_user_username', with: 'new-user'
expect(find('.username > .validation-pending')).not_to have_css '.hide'
diff --git a/spec/frontend/boards/modal_store_spec.js b/spec/frontend/boards/modal_store_spec.js
index 4dd27e94d97..5b5ae4b6556 100644
--- a/spec/frontend/boards/modal_store_spec.js
+++ b/spec/frontend/boards/modal_store_spec.js
@@ -25,7 +25,7 @@ describe('Modal store', () => {
});
issue2 = new ListIssue({
title: 'Testing',
- id: 1,
+ id: 2,
iid: 2,
confidential: false,
labels: [],
diff --git a/spec/frontend/clusters/services/application_state_machine_spec.js b/spec/frontend/clusters/services/application_state_machine_spec.js
index c146ef79be7..8632c5c4e26 100644
--- a/spec/frontend/clusters/services/application_state_machine_spec.js
+++ b/spec/frontend/clusters/services/application_state_machine_spec.js
@@ -72,9 +72,10 @@ describe('applicationStateMachine', () => {
describe(`current state is ${INSTALLABLE}`, () => {
it.each`
- expectedState | event | effects
- ${INSTALLING} | ${INSTALL_EVENT} | ${{ installFailed: false }}
- ${INSTALLED} | ${INSTALLED} | ${NO_EFFECTS}
+ expectedState | event | effects
+ ${INSTALLING} | ${INSTALL_EVENT} | ${{ installFailed: false }}
+ ${INSTALLED} | ${INSTALLED} | ${NO_EFFECTS}
+ ${NOT_INSTALLABLE} | ${NOT_INSTALLABLE} | ${NO_EFFECTS}
`(`transitions to $expectedState on $event event and applies $effects`, data => {
const { expectedState, event, effects } = data;
const currentAppState = {
@@ -108,9 +109,10 @@ describe('applicationStateMachine', () => {
describe(`current state is ${INSTALLED}`, () => {
it.each`
- expectedState | event | effects
- ${UPDATING} | ${UPDATE_EVENT} | ${{ updateFailed: false, updateSuccessful: false }}
- ${UNINSTALLING} | ${UNINSTALL_EVENT} | ${{ uninstallFailed: false, uninstallSuccessful: false }}
+ expectedState | event | effects
+ ${UPDATING} | ${UPDATE_EVENT} | ${{ updateFailed: false, updateSuccessful: false }}
+ ${UNINSTALLING} | ${UNINSTALL_EVENT} | ${{ uninstallFailed: false, uninstallSuccessful: false }}
+ ${NOT_INSTALLABLE} | ${NOT_INSTALLABLE} | ${NO_EFFECTS}
`(`transitions to $expectedState on $event event and applies $effects`, data => {
const { expectedState, event, effects } = data;
const currentAppState = {
@@ -119,7 +121,7 @@ describe('applicationStateMachine', () => {
expect(transitionApplicationState(currentAppState, event)).toEqual({
status: expectedState,
- ...effects,
+ ...noEffectsToEmptyObject(effects),
});
});
});
diff --git a/spec/frontend/ide/utils_spec.js b/spec/frontend/ide/utils_spec.js
new file mode 100644
index 00000000000..2b7dffdcd88
--- /dev/null
+++ b/spec/frontend/ide/utils_spec.js
@@ -0,0 +1,44 @@
+import { commitItemIconMap } from '~/ide/constants';
+import { getCommitIconMap } from '~/ide/utils';
+import { decorateData } from '~/ide/stores/utils';
+
+describe('WebIDE utils', () => {
+ const createFile = (name = 'name', id = name, type = '', parent = null) =>
+ decorateData({
+ id,
+ type,
+ icon: 'icon',
+ url: 'url',
+ name,
+ path: parent ? `${parent.path}/${name}` : name,
+ parentPath: parent ? parent.path : '',
+ lastCommit: {},
+ });
+
+ describe('getCommitIconMap', () => {
+ let entry;
+
+ beforeEach(() => {
+ entry = createFile('Entry item');
+ });
+
+ it('renders "deleted" icon for deleted entries', () => {
+ entry.deleted = true;
+ expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.deleted);
+ });
+ it('renders "addition" icon for temp entries', () => {
+ entry.tempFile = true;
+ expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.addition);
+ });
+ it('renders "modified" icon for newly-renamed entries', () => {
+ entry.prevPath = 'foo/bar';
+ entry.tempFile = false;
+ expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.modified);
+ });
+ it('renders "modified" icon even for temp entries if they are newly-renamed', () => {
+ entry.prevPath = 'foo/bar';
+ entry.tempFile = true;
+ expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.modified);
+ });
+ });
+});
diff --git a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
index 1f06d693411..d55dc553031 100644
--- a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
+++ b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
@@ -29,10 +29,20 @@ exports[`Repository table row component renders table row 1`] = `
<td
class="d-none d-sm-table-cell tree-commit"
- />
+ >
+ <glskeletonloading-stub
+ class="h-auto"
+ lines="1"
+ />
+ </td>
<td
class="tree-time-ago text-right"
- />
+ >
+ <glskeletonloading-stub
+ class="ml-auto h-auto w-50"
+ lines="1"
+ />
+ </td>
</tr>
`;
diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js
index 5a345ddeacd..c566057ad3f 100644
--- a/spec/frontend/repository/components/table/row_spec.js
+++ b/spec/frontend/repository/components/table/row_spec.js
@@ -16,6 +16,8 @@ function factory(propsData = {}) {
vm = shallowMount(TableRow, {
propsData: {
...propsData,
+ name: propsData.path,
+ projectPath: 'gitlab-org/gitlab-ce',
url: `https://test.com`,
},
mocks: {
diff --git a/spec/frontend/repository/log_tree_spec.js b/spec/frontend/repository/log_tree_spec.js
new file mode 100644
index 00000000000..a9499f7c61b
--- /dev/null
+++ b/spec/frontend/repository/log_tree_spec.js
@@ -0,0 +1,129 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import { normalizeData, resolveCommit, fetchLogsTree } from '~/repository/log_tree';
+
+const mockData = [
+ {
+ commit: {
+ id: '123',
+ message: 'testing message',
+ committed_date: '2019-01-01',
+ },
+ commit_path: `https://test.com`,
+ file_name: 'index.js',
+ type: 'blob',
+ },
+];
+
+describe('normalizeData', () => {
+ it('normalizes data into LogTreeCommit object', () => {
+ expect(normalizeData(mockData)).toEqual([
+ {
+ sha: '123',
+ message: 'testing message',
+ committedDate: '2019-01-01',
+ commitPath: 'https://test.com',
+ fileName: 'index.js',
+ type: 'blob',
+ __typename: 'LogTreeCommit',
+ },
+ ]);
+ });
+});
+
+describe('resolveCommit', () => {
+ it('calls resolve when commit found', () => {
+ const resolver = {
+ entry: { name: 'index.js', type: 'blob' },
+ resolve: jest.fn(),
+ };
+ const commits = [{ fileName: 'index.js', type: 'blob' }];
+
+ resolveCommit(commits, resolver);
+
+ expect(resolver.resolve).toHaveBeenCalledWith({ fileName: 'index.js', type: 'blob' });
+ });
+});
+
+describe('fetchLogsTree', () => {
+ let mock;
+ let client;
+ let resolver;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+
+ mock.onGet(/(.*)/).reply(200, mockData, {});
+
+ jest.spyOn(axios, 'get');
+
+ global.gon = { gitlab_url: 'https://test.com' };
+
+ client = {
+ readQuery: () => ({
+ projectPath: 'gitlab-org/gitlab-ce',
+ ref: 'master',
+ commits: [],
+ }),
+ writeQuery: jest.fn(),
+ };
+
+ resolver = {
+ entry: { name: 'index.js', type: 'blob' },
+ resolve: jest.fn(),
+ };
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('calls axios get', () =>
+ fetchLogsTree(client, '', '0', resolver).then(() => {
+ expect(axios.get).toHaveBeenCalledWith(
+ 'https://test.com/gitlab-org/gitlab-ce/refs/master/logs_tree',
+ { params: { format: 'json', offset: '0' } },
+ );
+ }));
+
+ it('calls axios get once', () =>
+ Promise.all([
+ fetchLogsTree(client, '', '0', resolver),
+ fetchLogsTree(client, '', '0', resolver),
+ ]).then(() => {
+ expect(axios.get.mock.calls.length).toEqual(1);
+ }));
+
+ it('calls entry resolver', () =>
+ fetchLogsTree(client, '', '0', resolver).then(() => {
+ expect(resolver.resolve).toHaveBeenCalledWith({
+ __typename: 'LogTreeCommit',
+ commitPath: 'https://test.com',
+ committedDate: '2019-01-01',
+ fileName: 'index.js',
+ message: 'testing message',
+ sha: '123',
+ type: 'blob',
+ });
+ }));
+
+ it('writes query to client', () =>
+ fetchLogsTree(client, '', '0', resolver).then(() => {
+ expect(client.writeQuery).toHaveBeenCalledWith({
+ query: expect.anything(),
+ data: {
+ commits: [
+ {
+ __typename: 'LogTreeCommit',
+ commitPath: 'https://test.com',
+ committedDate: '2019-01-01',
+ fileName: 'index.js',
+ message: 'testing message',
+ sha: '123',
+ type: 'blob',
+ },
+ ],
+ },
+ });
+ }));
+});
diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js
index 7e7cc1488b8..c17d5253997 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -1,10 +1,16 @@
import Vue from 'vue';
import * as jqueryMatchers from 'custom-jquery-matchers';
+import $ from 'jquery';
import Translate from '~/vue_shared/translate';
import axios from '~/lib/utils/axios_utils';
import { initializeTestTimeout } from './helpers/timeout';
import { loadHTMLFixture, setHTMLFixture } from './helpers/fixtures';
+// Expose jQuery so specs using jQuery plugins can be imported nicely.
+// Here is an issue to explore better alternatives:
+// https://gitlab.com/gitlab-org/gitlab-ee/issues/12448
+window.jQuery = $;
+
process.on('unhandledRejection', global.promiseRejectionHandler);
afterEach(() =>
diff --git a/spec/javascripts/ide/components/ide_tree_list_spec.js b/spec/javascripts/ide/components/ide_tree_list_spec.js
index f63007c7dd2..554bd1ae3b5 100644
--- a/spec/javascripts/ide/components/ide_tree_list_spec.js
+++ b/spec/javascripts/ide/components/ide_tree_list_spec.js
@@ -58,6 +58,20 @@ describe('IDE tree list', () => {
it('renders list of files', () => {
expect(vm.$el.textContent).toContain('fileName');
});
+
+ it('does not render moved entries', done => {
+ const tree = [file('moved entry'), file('normal entry')];
+ tree[0].moved = true;
+ store.state.trees['abcproject/master'].tree = tree;
+ const container = vm.$el.querySelector('.ide-tree-body');
+
+ vm.$nextTick(() => {
+ expect(container.children.length).toBe(1);
+ expect(vm.$el.textContent).not.toContain('moved entry');
+ expect(vm.$el.textContent).toContain('normal entry');
+ done();
+ });
+ });
});
describe('empty-branch state', () => {
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index dd2313dc800..021c3076094 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -275,6 +275,43 @@ describe('IDE store file actions', () => {
});
});
+ describe('Re-named success', () => {
+ beforeEach(() => {
+ localFile = file(`newCreate-${Math.random()}`);
+ localFile.url = `project/getFileDataURL`;
+ localFile.prevPath = 'old-dull-file';
+ localFile.path = 'new-shiny-file';
+ store.state.entries[localFile.path] = localFile;
+
+ mock.onGet(`${RELATIVE_URL_ROOT}/project/getFileDataURL`).replyOnce(
+ 200,
+ {
+ blame_path: 'blame_path',
+ commits_path: 'commits_path',
+ permalink: 'permalink',
+ raw_path: 'raw_path',
+ binary: false,
+ html: '123',
+ render_error: '',
+ },
+ {
+ 'page-title': 'testing old-dull-file',
+ },
+ );
+ });
+
+ it('sets document title considering `prevPath` on a file', done => {
+ store
+ .dispatch('getFileData', { path: localFile.path })
+ .then(() => {
+ expect(document.title).toBe('testing new-shiny-file');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
describe('error', () => {
beforeEach(() => {
mock.onGet(`project/getFileDataURL`).networkError();
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index 537152f5eed..2d105103c1c 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -536,8 +536,15 @@ describe('Multi-file store actions', () => {
type: types.RENAME_ENTRY,
payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
},
+ {
+ type: types.TOGGLE_FILE_CHANGED,
+ payload: {
+ file: store.state.entries['parent-path/new-name'],
+ changed: true,
+ },
+ },
],
- [{ type: 'deleteEntry', payload: 'test' }, { type: 'triggerFilesChange' }],
+ [{ type: 'triggerFilesChange' }],
done,
);
});
@@ -584,7 +591,6 @@ describe('Multi-file store actions', () => {
parentPath: 'parent-path/new-name',
},
},
- { type: 'deleteEntry', payload: 'test' },
{ type: 'triggerFilesChange' },
],
done,
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index 5ee098bf17f..460c5b01081 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -309,7 +309,7 @@ describe('Multi-file store mutations', () => {
...localState.entries.oldPath,
id: 'newPath',
name: 'newPath',
- key: 'newPath-blob-name',
+ key: 'newPath-blob-oldPath',
path: 'newPath',
tempFile: true,
prevPath: 'oldPath',
@@ -318,6 +318,7 @@ describe('Multi-file store mutations', () => {
url: `${gl.TEST_HOST}/newPath`,
moved: jasmine.anything(),
movedPath: jasmine.anything(),
+ opened: false,
});
});
@@ -349,13 +350,5 @@ describe('Multi-file store mutations', () => {
expect(localState.entries.parentPath.tree.length).toBe(1);
});
-
- it('adds to openFiles if previously opened', () => {
- localState.entries.oldPath.opened = true;
-
- mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
-
- expect(localState.openFiles).toEqual([localState.entries.newPath]);
- });
});
});
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index f4166987aed..ab8360193be 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -10,6 +10,7 @@ import {
mockApiEndpoint,
environmentData,
singleGroupResponse,
+ dashboardGitResponse,
} from './mock_data';
const propsData = {
@@ -62,16 +63,34 @@ describe('Dashboard', () => {
});
describe('no metrics are available yet', () => {
- it('shows a getting started empty state when no metrics are present', () => {
+ beforeEach(() => {
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData },
store,
});
+ });
+ it('shows a getting started empty state when no metrics are present', () => {
expect(component.$el.querySelector('.prometheus-graphs')).toBe(null);
expect(component.emptyState).toEqual('gettingStarted');
});
+
+ it('shows the environment selector', () => {
+ expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
+ });
+ });
+
+ describe('no data found', () => {
+ it('shows the environment selector dropdown', () => {
+ component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: { ...propsData, showEmptyState: true },
+ store,
+ });
+
+ expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
+ });
});
describe('requests information to the server', () => {
@@ -150,14 +169,24 @@ describe('Dashboard', () => {
singleGroupResponse,
);
- setTimeout(() => {
- const dropdownMenuEnvironments = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item',
- );
+ Vue.nextTick()
+ .then(() => {
+ const dropdownMenuEnvironments = component.$el.querySelectorAll(
+ '.js-environments-dropdown .dropdown-item',
+ );
- expect(dropdownMenuEnvironments.length).toEqual(component.environments.length);
- done();
- });
+ expect(component.environments.length).toEqual(environmentData.length);
+ expect(dropdownMenuEnvironments.length).toEqual(component.environments.length);
+
+ Array.from(dropdownMenuEnvironments).forEach((value, index) => {
+ if (environmentData[index].metrics_path) {
+ expect(value).toHaveAttr('href', environmentData[index].metrics_path);
+ }
+ });
+
+ done();
+ })
+ .catch(done.fail);
});
it('hides the environments dropdown list when there is no environments', done => {
@@ -212,7 +241,7 @@ describe('Dashboard', () => {
Vue.nextTick()
.then(() => {
const dropdownItems = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item[active="true"]',
+ '.js-environments-dropdown .dropdown-item.is-active',
);
expect(dropdownItems.length).toEqual(1);
@@ -281,10 +310,6 @@ describe('Dashboard', () => {
const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff');
component.$store.commit(
- `monitoringDashboard/${types.SET_ENVIRONMENTS_ENDPOINT}`,
- '/environments',
- );
- component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
@@ -402,4 +427,49 @@ describe('Dashboard', () => {
});
});
});
+
+ describe('Dashboard dropdown', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ },
+ store,
+ });
+
+ component.$store.dispatch('monitoringDashboard/setFeatureFlags', {
+ prometheusEndpoint: false,
+ multipleDashboardsEnabled: true,
+ });
+
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
+ singleGroupResponse,
+ );
+
+ component.$store.commit(
+ `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
+ dashboardGitResponse,
+ );
+ });
+
+ it('shows the dashboard dropdown', done => {
+ setTimeout(() => {
+ const dashboardDropdown = component.$el.querySelector('.js-dashboards-dropdown');
+
+ expect(dashboardDropdown).not.toEqual(null);
+ done();
+ });
+ });
+ });
});
diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index 82e42fe9ade..7bbb215475a 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -922,3 +922,16 @@ export const metricsDashboardResponse = {
},
status: 'success',
};
+
+export const dashboardGitResponse = [
+ {
+ path: 'config/prometheus/common_metrics.yml',
+ display_name: 'Common Metrics',
+ default: true,
+ },
+ {
+ path: '.gitlab/dashboards/super.yml',
+ display_name: 'Custom Dashboard 1',
+ default: false,
+ },
+];
diff --git a/spec/javascripts/monitoring/store/actions_spec.js b/spec/javascripts/monitoring/store/actions_spec.js
index 083a01c4d74..677455275de 100644
--- a/spec/javascripts/monitoring/store/actions_spec.js
+++ b/spec/javascripts/monitoring/store/actions_spec.js
@@ -22,6 +22,7 @@ import {
environmentData,
metricsDashboardResponse,
metricsGroupsAPIResponse,
+ dashboardGitResponse,
} from '../mock_data';
describe('Monitoring store actions', () => {
@@ -212,17 +213,19 @@ describe('Monitoring store actions', () => {
describe('receiveMetricsDashboardSuccess', () => {
let commit;
let dispatch;
+ let state;
beforeEach(() => {
commit = jasmine.createSpy();
dispatch = jasmine.createSpy();
+ state = storeState();
});
it('stores groups ', () => {
const params = {};
const response = metricsDashboardResponse;
- receiveMetricsDashboardSuccess({ commit, dispatch }, { response, params });
+ receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response, params });
expect(commit).toHaveBeenCalledWith(
types.RECEIVE_METRICS_DATA_SUCCESS,
@@ -231,6 +234,18 @@ describe('Monitoring store actions', () => {
expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetrics', params);
});
+
+ it('sets the dashboards loaded from the repository', () => {
+ const params = {};
+ const response = metricsDashboardResponse;
+
+ response.all_dashboards = dashboardGitResponse;
+ state.multipleDashboardsEnabled = true;
+
+ receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response, params });
+
+ expect(commit).toHaveBeenCalledWith(types.SET_ALL_DASHBOARDS, dashboardGitResponse);
+ });
});
describe('receiveMetricsDashboardFailure', () => {
diff --git a/spec/javascripts/monitoring/store/mutations_spec.js b/spec/javascripts/monitoring/store/mutations_spec.js
index 02ff5847b34..91580366531 100644
--- a/spec/javascripts/monitoring/store/mutations_spec.js
+++ b/spec/javascripts/monitoring/store/mutations_spec.js
@@ -1,7 +1,12 @@
import mutations from '~/monitoring/stores/mutations';
import * as types from '~/monitoring/stores/mutation_types';
import state from '~/monitoring/stores/state';
-import { metricsGroupsAPIResponse, deploymentData, metricsDashboardResponse } from '../mock_data';
+import {
+ metricsGroupsAPIResponse,
+ deploymentData,
+ metricsDashboardResponse,
+ dashboardGitResponse,
+} from '../mock_data';
describe('Monitoring mutations', () => {
let stateCopy;
@@ -156,4 +161,12 @@ describe('Monitoring mutations', () => {
expect(stateCopy.metricsWithData).toEqual([]);
});
});
+
+ describe('SET_ALL_DASHBOARDS', () => {
+ it('stores the dashboards loaded from the git repository', () => {
+ mutations[types.SET_ALL_DASHBOARDS](stateCopy, dashboardGitResponse);
+
+ expect(stateCopy.allDashboards).toEqual(dashboardGitResponse);
+ });
+ });
});
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 6d6107ca3e7..ba6abba4e61 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -223,6 +223,19 @@ describe Gitlab::GitalyClient::CommitService do
end
context 'when caching of the ref name is enabled' do
+ it 'caches negative entries' do
+ expect_any_instance_of(Gitaly::CommitService::Stub).to receive(:find_commit).once.and_return(double(commit: nil))
+
+ commit = nil
+ 2.times do
+ ::Gitlab::GitalyClient.allow_ref_name_caching do
+ commit = described_class.new(repository).find_commit('master')
+ end
+ end
+
+ expect(commit).to eq(nil)
+ end
+
it 'returns a cached commit' do
expect_any_instance_of(Gitaly::CommitService::Stub).to receive(:find_commit).once.and_return(double(commit: commit_dbl))
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index f8dc1541dd3..ab6f6dfe720 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -354,36 +354,6 @@ describe ApplicationSetting do
end
end
- describe 'setting Sentry DSNs' do
- context 'server DSN' do
- it 'strips leading and trailing whitespace' do
- subject.update(sentry_dsn: ' http://test ')
-
- expect(subject.sentry_dsn).to eq('http://test')
- end
-
- it 'handles nil values' do
- subject.update(sentry_dsn: nil)
-
- expect(subject.sentry_dsn).to be_nil
- end
- end
-
- context 'client-side DSN' do
- it 'strips leading and trailing whitespace' do
- subject.update(clientside_sentry_dsn: ' http://test ')
-
- expect(subject.clientside_sentry_dsn).to eq('http://test')
- end
-
- it 'handles nil values' do
- subject.update(clientside_sentry_dsn: nil)
-
- expect(subject.clientside_sentry_dsn).to be_nil
- end
- end
- end
-
describe '#disabled_oauth_sign_in_sources=' do
before do
allow(Devise).to receive(:omniauth_providers).and_return([:github])
diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb
index 05b3035e591..471769e4aab 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -2,13 +2,11 @@
require 'spec_helper'
-describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching do
+describe Clusters::Platforms::Kubernetes do
include KubernetesHelpers
- include ReactiveCachingHelpers
it { is_expected.to belong_to(:cluster) }
it { is_expected.to be_kind_of(Gitlab::Kubernetes) }
- it { is_expected.to be_kind_of(ReactiveCaching) }
it { is_expected.to respond_to :ca_pem }
it { is_expected.to validate_exclusion_of(:namespace).in_array(%w(gitlab-managed-apps)) }
@@ -397,17 +395,16 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end
describe '#terminals' do
- subject { service.terminals(environment) }
+ subject { service.terminals(environment, pods: pods) }
let!(:cluster) { create(:cluster, :project, platform_kubernetes: service) }
let(:project) { cluster.project }
let(:service) { create(:cluster_platform_kubernetes, :configured) }
let(:environment) { build(:environment, project: project, name: "env", slug: "env-000000") }
+ let(:pods) { [{ "bad" => "pod" }] }
context 'with invalid pods' do
it 'returns no terminals' do
- stub_reactive_cache(service, pods: [{ "bad" => "pod" }])
-
is_expected.to be_empty
end
end
@@ -416,13 +413,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
let(:pod) { kube_pod(environment_slug: environment.slug, namespace: cluster.kubernetes_namespace_for(project), project_slug: project.full_path_slug) }
let(:pod_with_no_terminal) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug, status: "Pending") }
let(:terminals) { kube_terminals(service, pod) }
-
- before do
- stub_reactive_cache(
- service,
- pods: [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")]
- )
- end
+ let(:pods) { [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")] }
it 'returns terminals' do
is_expected.to eq(terminals + terminals)
@@ -437,16 +428,18 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end
end
- describe '#calculate_reactive_cache' do
- subject { service.calculate_reactive_cache }
-
- let!(:cluster) { create(:cluster, :project, enabled: enabled, platform_kubernetes: service) }
+ describe '#calculate_reactive_cache_for' do
let(:service) { create(:cluster_platform_kubernetes, :configured) }
- let(:enabled) { true }
- let(:namespace) { cluster.kubernetes_namespace_for(cluster.project) }
+ let(:pod) { kube_pod }
+ let(:namespace) { pod["metadata"]["namespace"] }
+ let(:environment) { instance_double(Environment, deployment_namespace: namespace) }
- context 'when cluster is disabled' do
- let(:enabled) { false }
+ subject { service.calculate_reactive_cache_for(environment) }
+
+ context 'when the kubernetes integration is disabled' do
+ before do
+ allow(service).to receive(:enabled?).and_return(false)
+ end
it { is_expected.to be_nil }
end
@@ -457,7 +450,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
stub_kubeclient_deployments(namespace)
end
- it { is_expected.to include(pods: [kube_pod]) }
+ it { is_expected.to include(pods: [pod]) }
end
context 'when kubernetes responds with 500s' do
@@ -477,11 +470,5 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it { is_expected.to include(pods: []) }
end
-
- context 'when the cluster is not project level' do
- let(:cluster) { create(:cluster, :group, platform_kubernetes: service) }
-
- it { is_expected.to include(pods: []) }
- end
end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 379dda1f5c4..fe4d64818b4 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -2,10 +2,14 @@
require 'spec_helper'
-describe Environment do
+describe Environment, :use_clean_rails_memory_store_caching do
+ include ReactiveCachingHelpers
+
let(:project) { create(:project, :stubbed_repository) }
subject(:environment) { create(:environment, project: project) }
+ it { is_expected.to be_kind_of(ReactiveCaching) }
+
it { is_expected.to belong_to(:project).required }
it { is_expected.to have_many(:deployments) }
@@ -573,32 +577,65 @@ describe Environment do
describe '#terminals' do
subject { environment.terminals }
- context 'when the environment has terminals' do
+ before do
+ allow(environment).to receive(:deployment_platform).and_return(double)
+ end
+
+ context 'reactive cache is empty' do
before do
- allow(environment).to receive(:has_terminals?).and_return(true)
+ stub_reactive_cache(environment, nil)
end
- context 'when user configured kubernetes from CI/CD > Clusters' do
- let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
- let(:project) { cluster.project }
+ it { is_expected.to be_nil }
+ end
+
+ context 'reactive cache has pod data' do
+ let(:cache_data) { Hash(pods: %w(pod1 pod2)) }
+
+ before do
+ stub_reactive_cache(environment, cache_data)
+ end
- it 'returns the terminals from the deployment service' do
- expect(environment.deployment_platform)
- .to receive(:terminals).with(environment)
- .and_return(:fake_terminals)
+ it 'retrieves terminals from the deployment platform' do
+ expect(environment.deployment_platform)
+ .to receive(:terminals).with(environment, cache_data)
+ .and_return(:fake_terminals)
- is_expected.to eq(:fake_terminals)
- end
+ is_expected.to eq(:fake_terminals)
end
end
+ end
+
+ describe '#calculate_reactive_cache' do
+ let(:cluster) { create(:cluster, :project, :provided_by_user) }
+ let(:project) { cluster.project }
+ let(:environment) { create(:environment, project: project) }
+ let!(:deployment) { create(:deployment, :success, environment: environment) }
+
+ subject { environment.calculate_reactive_cache }
+
+ it 'returns cache data from the deployment platform' do
+ expect(environment.deployment_platform).to receive(:calculate_reactive_cache_for)
+ .with(environment).and_return(pods: %w(pod1 pod2))
+
+ is_expected.to eq(pods: %w(pod1 pod2))
+ end
- context 'when the environment does not have terminals' do
+ context 'environment does not have terminals available' do
before do
allow(environment).to receive(:has_terminals?).and_return(false)
end
it { is_expected.to be_nil }
end
+
+ context 'project is pending deletion' do
+ before do
+ allow(environment.project).to receive(:pending_delete?).and_return(true)
+ end
+
+ it { is_expected.to be_nil }
+ end
end
describe '#has_metrics?' do
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 806b4f61bd8..28630f7d3fe 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -158,7 +158,7 @@ describe InternalId do
before do
described_class.reset_column_information
# Project factory will also call the current_version
- expect(ActiveRecord::Migrator).to receive(:current_version).twice.and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
+ expect(ActiveRecord::Migrator).to receive(:current_version).at_least(:once).and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
end
it 'does not reset any of the iids' do
diff --git a/spec/models/namespace/aggregation_schedule_spec.rb b/spec/models/namespace/aggregation_schedule_spec.rb
new file mode 100644
index 00000000000..5ba7547ff4d
--- /dev/null
+++ b/spec/models/namespace/aggregation_schedule_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Namespace::AggregationSchedule, type: :model do
+ it { is_expected.to belong_to :namespace }
+end
diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb
new file mode 100644
index 00000000000..f6fb5af5aae
--- /dev/null
+++ b/spec/models/namespace/root_storage_statistics_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Namespace::RootStorageStatistics, type: :model do
+ it { is_expected.to belong_to :namespace }
+ it { is_expected.to have_one(:route).through(:namespace) }
+
+ it { is_expected.to delegate_method(:all_projects).to(:namespace) }
+end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index d80183af33e..30e49cf204f 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -15,6 +15,8 @@ describe Namespace do
it { is_expected.to have_many :project_statistics }
it { is_expected.to belong_to :parent }
it { is_expected.to have_many :children }
+ it { is_expected.to have_one :root_storage_statistics }
+ it { is_expected.to have_one :aggregation_schedule }
end
describe 'validations' do
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index ed907841bd8..1c69f5dbb67 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -226,10 +226,8 @@ describe API::Helpers do
allow_any_instance_of(self.class).to receive(:rack_response)
allow(Gitlab::Sentry).to receive(:enabled?).and_return(true)
- stub_application_setting(
- sentry_enabled: true,
- sentry_dsn: "dummy://12345:67890@sentry.localdomain/sentry/42"
- )
+ stub_sentry_settings
+
configure_sentry
Raven.client.configuration.encoding = 'json'
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index a170bb14144..ff4228c9b99 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -1,12 +1,12 @@
require 'spec_helper'
-# user GET /u/:username/
-# user_groups GET /u/:username/groups(.:format)
-# user_projects GET /u/:username/projects(.:format)
-# user_contributed_projects GET /u/:username/contributed(.:format)
-# user_snippets GET /u/:username/snippets(.:format)
-# user_calendar GET /u/:username/calendar(.:format)
-# user_calendar_activities GET /u/:username/calendar_activities(.:format)
+# user GET /users/:username/
+# user_groups GET /users/:username/groups(.:format)
+# user_projects GET /users/:username/projects(.:format)
+# user_contributed_projects GET /users/:username/contributed(.:format)
+# user_snippets GET /users/:username/snippets(.:format)
+# user_calendar GET /users/:username/calendar(.:format)
+# user_calendar_activities GET /users/:username/calendar_activities(.:format)
describe UsersController, "routing" do
it "to #show" do
allow_any_instance_of(::Constraints::UserUrlConstrainer).to receive(:matches?).and_return(true)
@@ -37,22 +37,6 @@ describe UsersController, "routing" do
it "to #calendar_activities" do
expect(get("/users/User/calendar_activities")).to route_to('users#calendar_activities', username: 'User')
end
-
- describe 'redirect alias routes' do
- include RSpec::Rails::RequestExampleGroup
-
- it '/u/user1 redirects to /user1' do
- expect(get("/u/user1")).to redirect_to('/user1')
- end
-
- it '/u/user1/groups redirects to /user1/groups' do
- expect(get("/u/user1/groups")).to redirect_to('/users/user1/groups')
- end
-
- it '/u/user1/projects redirects to /user1/projects' do
- expect(get("/u/user1/projects")).to redirect_to('/users/user1/projects')
- end
- end
end
# search GET /search(.:format) search#show
diff --git a/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
index d5f77f3354b..8d43ce4f662 100644
--- a/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
+++ b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
@@ -34,8 +34,12 @@ describe PagesDomains::ObtainLetsEncryptCertificateService do
end
context 'when there is no acme order' do
- it 'creates acme order' do
+ it 'creates acme order and schedules next step' do
expect_to_create_acme_challenge
+ expect(PagesDomainSslRenewalWorker).to(
+ receive(:perform_in).with(described_class::CHALLENGE_PROCESSING_DELAY, pages_domain.id)
+ .and_return(nil).once
+ )
service.execute
end
@@ -82,8 +86,12 @@ describe PagesDomains::ObtainLetsEncryptCertificateService do
stub_lets_encrypt_order(existing_order.url, 'ready')
end
- it 'request certificate' do
+ it 'request certificate and schedules next step' do
expect(api_order).to receive(:request_certificate).and_call_original
+ expect(PagesDomainSslRenewalWorker).to(
+ receive(:perform_in).with(described_class::CERTIFICATE_PROCESSING_DELAY, pages_domain.id)
+ .and_return(nil).once
+ )
service.execute
end
diff --git a/spec/services/projects/propagate_service_template_spec.rb b/spec/services/projects/propagate_service_template_spec.rb
index f93e5aae82a..2c3effec617 100644
--- a/spec/services/projects/propagate_service_template_spec.rb
+++ b/spec/services/projects/propagate_service_template_spec.rb
@@ -72,7 +72,7 @@ describe Projects::PropagateServiceTemplate do
expect(project.pushover_service.properties).to eq(service_template.properties)
end
- describe 'bulk update' do
+ describe 'bulk update', :use_sql_query_cache do
let(:project_total) { 5 }
before do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 390a869d93f..3bd2408dc72 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -218,6 +218,12 @@ RSpec.configure do |config|
ActionController::Base.cache_store = caching_store
end
+ config.around(:each, :use_sql_query_cache) do |example|
+ ActiveRecord::Base.cache do
+ example.run
+ end
+ end
+
# The :each scope runs "inside" the example, so this hook ensures the DB is in the
# correct state before any examples' before hooks are called. This prevents a
# problem where `ScheduleIssuesClosedAtTypeChange` (or any migration that depends
diff --git a/spec/support/helpers/jira_service_helper.rb b/spec/support/helpers/jira_service_helper.rb
index f4d5343c4ed..477bbf1c2e0 100644
--- a/spec/support/helpers/jira_service_helper.rb
+++ b/spec/support/helpers/jira_service_helper.rb
@@ -25,7 +25,7 @@ module JiraServiceHelper
\"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\",
\"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},
\"displayName\":\"GitLab\",\"active\":true},
- \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned JIRA-1 in Merge request of [gitlab-org/gitlab-test|http://localhost:3000/gitlab-org/gitlab-test/merge_requests/2].\",
+ \"body\":\"[Administrator|http://localhost:3000/root] mentioned JIRA-1 in Merge request of [gitlab-org/gitlab-test|http://localhost:3000/gitlab-org/gitlab-test/merge_requests/2].\",
\"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\",
\"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\",
\"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\",
@@ -40,7 +40,7 @@ module JiraServiceHelper
\"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\",
\"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\",
\"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true},
- \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned this issue in [a commit of h5bp/html5-boilerplate|http://localhost:3000/h5bp/html5-boilerplate/commit/2439f77897122fbeee3bfd9bb692d3608848433e].\",
+ \"body\":\"[Administrator|http://localhost:3000/root] mentioned this issue in [a commit of h5bp/html5-boilerplate|http://localhost:3000/h5bp/html5-boilerplate/commit/2439f77897122fbeee3bfd9bb692d3608848433e].\",
\"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\",
\"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\",
\"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\",
diff --git a/spec/support/helpers/prometheus_helpers.rb b/spec/support/helpers/prometheus_helpers.rb
index 87f825152cf..db662836013 100644
--- a/spec/support/helpers/prometheus_helpers.rb
+++ b/spec/support/helpers/prometheus_helpers.rb
@@ -70,6 +70,10 @@ module PrometheusHelpers
WebMock.stub_request(:get, url).to_raise(exception_type)
end
+ def stub_any_prometheus_request
+ WebMock.stub_request(:any, /prometheus.example.com/)
+ end
+
def stub_all_prometheus_requests(environment_slug, body: nil, status: 200)
stub_prometheus_request(
prometheus_query_with_time_url(prometheus_memory_query(environment_slug), Time.now.utc),
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index f6c613ad5aa..0d591f038ce 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -81,6 +81,12 @@ module StubConfiguration
allow(Gitlab.config.repositories).to receive(:storages).and_return(Settingslogic.new(messages))
end
+ def stub_sentry_settings
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(true)
+ allow(Gitlab.config.sentry).to receive(:dsn).and_return('dummy://b44a0828b72421a6d8e99efd68d44fa8@example.com/42')
+ allow(Gitlab.config.sentry).to receive(:clientside_dsn).and_return('dummy://b44a0828b72421a6d8e99efd68d44fa8@example.com/43')
+ end
+
def stub_kerberos_setting(messages)
allow(Gitlab.config.kerberos).to receive_messages(to_settings(messages))
end
diff --git a/spec/support/shared_examples/application_setting_examples.rb b/spec/support/shared_examples/application_setting_examples.rb
index 421303c97be..e7ec24c5b7e 100644
--- a/spec/support/shared_examples/application_setting_examples.rb
+++ b/spec/support/shared_examples/application_setting_examples.rb
@@ -249,43 +249,4 @@ RSpec.shared_examples 'application settings examples' do
expect(setting.password_authentication_enabled_for_web?).to be_falsey
end
-
- describe 'sentry settings' do
- context 'when the sentry settings are not set in gitlab.yml' do
- it 'fallbacks to the settings in the database' do
- setting.sentry_enabled = true
- setting.sentry_dsn = 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/40'
- setting.clientside_sentry_enabled = true
- setting.clientside_sentry_dsn = 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/41'
-
- allow(Gitlab.config.sentry).to receive(:enabled).and_return(false)
- allow(Gitlab.config.sentry).to receive(:dsn).and_return(nil)
- allow(Gitlab.config.sentry).to receive(:clientside_dsn).and_return(nil)
-
- expect(setting.sentry_enabled).to eq true
- expect(setting.sentry_dsn).to eq 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/40'
- expect(setting.clientside_sentry_enabled).to eq true
- expect(setting.clientside_sentry_dsn). to eq 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/41'
- end
- end
-
- context 'when the sentry settings are set in gitlab.yml' do
- it 'does not fallback to the settings in the database' do
- setting.sentry_enabled = false
- setting.sentry_dsn = 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/40'
- setting.clientside_sentry_enabled = false
- setting.clientside_sentry_dsn = 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/41'
-
- allow(Gitlab.config.sentry).to receive(:enabled).and_return(true)
- allow(Gitlab.config.sentry).to receive(:dsn).and_return('https://b44a0828b72421a6d8e99efd68d44fa8@example.com/42')
- allow(Gitlab.config.sentry).to receive(:clientside_dsn).and_return('https://b44a0828b72421a6d8e99efd68d44fa8@example.com/43')
-
- expect(setting).not_to receive(:read_attribute)
- expect(setting.sentry_enabled).to eq true
- expect(setting.sentry_dsn).to eq 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/42'
- expect(setting.clientside_sentry_enabled).to eq true
- expect(setting.clientside_sentry_dsn). to eq 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/43'
- end
- end
- end
end
diff --git a/spec/support/sidekiq.rb b/spec/support/sidekiq.rb
index 6c4e11910d3..d1a765f27b9 100644
--- a/spec/support/sidekiq.rb
+++ b/spec/support/sidekiq.rb
@@ -30,6 +30,8 @@ RSpec.configure do |config|
end
config.after(:each, :sidekiq, :redis) do
- Sidekiq.redis { |redis| redis.flushdb }
+ Sidekiq.redis do |connection|
+ connection.redis.flushdb
+ end
end
end
diff --git a/spec/workers/reactive_caching_worker_spec.rb b/spec/workers/reactive_caching_worker_spec.rb
index b8ca6063ccd..ca0e76fc19a 100644
--- a/spec/workers/reactive_caching_worker_spec.rb
+++ b/spec/workers/reactive_caching_worker_spec.rb
@@ -3,17 +3,16 @@
require 'spec_helper'
describe ReactiveCachingWorker do
- let(:service) { project.deployment_platform }
-
describe '#perform' do
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
+ let!(:environment) { create(:environment, project: project) }
it 'calls #exclusively_update_reactive_cache!' do
- expect_any_instance_of(Clusters::Platforms::Kubernetes).to receive(:exclusively_update_reactive_cache!)
+ expect_any_instance_of(Environment).to receive(:exclusively_update_reactive_cache!)
- described_class.new.perform("Clusters::Platforms::Kubernetes", service.id)
+ described_class.new.perform("Environment", environment.id)
end
end
end
diff --git a/yarn.lock b/yarn.lock
index bbab157d4c7..1f1b35eed0a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2231,6 +2231,14 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3"
integrity sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==
+camel-case@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
+ integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
+ dependencies:
+ no-case "^2.2.0"
+ upper-case "^1.1.1"
+
camelcase-keys@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
@@ -2460,6 +2468,13 @@ classlist-polyfill@^1.2.0:
resolved "https://registry.yarnpkg.com/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz#935bc2dfd9458a876b279617514638bcaa964a2e"
integrity sha1-k1vC39lFiodrJ5YXUUY4vKqWSi4=
+clean-css@^4.1.6, clean-css@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
+ integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==
+ dependencies:
+ source-map "~0.6.0"
+
cli-boxes@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
@@ -2953,6 +2968,11 @@ crypto-random-string@^1.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
+css-b64-images@~0.2.5:
+ version "0.2.5"
+ resolved "https://registry.yarnpkg.com/css-b64-images/-/css-b64-images-0.2.5.tgz#42005d83204b2b4a5d93b6b1a5644133b5927a02"
+ integrity sha1-QgBdgyBLK0pdk7axpWRBM7WSegI=
+
css-loader@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.1.tgz#6885bb5233b35ec47b006057da01cc640b6b79fe"
@@ -5370,6 +5390,19 @@ html-entities@^1.2.0:
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2"
integrity sha1-QZSMr4XOgv7Tbk5qDtNxpmZDeeI=
+html-minifier@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-4.0.0.tgz#cca9aad8bce1175e02e17a8c33e46d8988889f56"
+ integrity sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==
+ dependencies:
+ camel-case "^3.0.0"
+ clean-css "^4.2.1"
+ commander "^2.19.0"
+ he "^1.2.0"
+ param-case "^2.1.1"
+ relateurl "^0.2.7"
+ uglify-js "^3.5.1"
+
html-tags@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b"
@@ -7141,6 +7174,11 @@ loud-rejection@^1.0.0:
currently-unhandled "^0.4.1"
signal-exit "^3.0.0"
+lower-case@^1.1.1:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
+ integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
+
lowercase-keys@1.0.0, lowercase-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
@@ -7384,10 +7422,10 @@ merge2@^1.2.3:
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5"
integrity sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==
-mermaid@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.0.0.tgz#8f6c75017e788a8c3997e20c5e5046c2b88d1a8f"
- integrity sha512-vUQRykev0A6RtxIVqQT3a9TDxcSbdZbQF5JDyKgidnYuJy8BE8jp6LM+HKDSQuroKm6buu4NlpMO+qhxIP/cTg==
+mermaid@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.1.0.tgz#f9f4c02cf98d2d9fae230d5ce28f531e605e9b72"
+ integrity sha512-fsCN8bOukYHZT6FlA0eIeLs/O3H2+CWcHnxRrS86Ci1cpJes5/qvoye0xjhe8lbXJCFLM8sXWVg57aMHPtnAaw==
dependencies:
d3 "^5.7.0"
dagre-d3-renderer "^0.5.8"
@@ -7395,7 +7433,8 @@ mermaid@^8.0.0:
graphlibrary "^2.2.0"
he "^1.2.0"
lodash "^4.17.11"
- moment "^2.23.0"
+ minify "^4.1.1"
+ moment-mini "^2.22.1"
scope-css "^1.2.1"
methods@~1.1.2:
@@ -7467,6 +7506,19 @@ mimic-response@^1.0.0:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e"
integrity sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=
+minify@^4.1.1:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/minify/-/minify-4.1.2.tgz#88755f4faa5f7ab6d0c64fdd659aa34ea658f180"
+ integrity sha512-YY6b6VzV7AY2MTMt1GjoFqKthGWvAr2L7MrzmFyiEsvPX+XAvidHcKqu36LlDT1V4I80ncbV5bsdTnIJq4/Sdw==
+ dependencies:
+ clean-css "^4.1.6"
+ css-b64-images "~0.2.5"
+ debug "^4.1.0"
+ html-minifier "^4.0.0"
+ terser "^4.0.0"
+ try-catch "^2.0.0"
+ try-to-catch "^1.0.2"
+
minimalistic-assert@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
@@ -7553,7 +7605,12 @@ mkdirp@0.5.x, mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp
dependencies:
minimist "0.0.8"
-moment@2.x, moment@^2.10.2, moment@^2.23.0:
+moment-mini@^2.22.1:
+ version "2.22.1"
+ resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.22.1.tgz#bc32d73e43a4505070be6b53494b17623183420d"
+ integrity sha512-OUCkHOz7ehtNMYuZjNciXUfwTuz8vmF1MTbAy59ebf+ZBYZO5/tZKuChVWCX+uDo+4idJBpGltNfV8st+HwsGw==
+
+moment@2.x, moment@^2.10.2:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
@@ -7666,6 +7723,13 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+no-case@^2.2.0:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
+ integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
+ dependencies:
+ lower-case "^1.1.1"
+
node-ensure@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/node-ensure/-/node-ensure-0.0.0.tgz#ecae764150de99861ec5c810fd5d096b183932a7"
@@ -8259,6 +8323,13 @@ parallel-transform@^1.1.0:
inherits "^2.0.3"
readable-stream "^2.1.5"
+param-case@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
+ integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
+ dependencies:
+ no-case "^2.2.0"
+
parse-asn1@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712"
@@ -9305,6 +9376,11 @@ regjsparser@^0.6.0:
dependencies:
jsesc "~0.5.0"
+relateurl@^0.2.7:
+ version "0.2.7"
+ resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
+ integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
+
remark-parse@^6.0.0:
version "6.0.3"
resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-6.0.3.tgz#c99131052809da482108413f87b0ee7f52180a3a"
@@ -10027,7 +10103,7 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
source-map-url "^0.4.0"
urix "^0.1.0"
-source-map-support@^0.5.6, source-map-support@~0.5.6:
+source-map-support@^0.5.6, source-map-support@~0.5.10, source-map-support@~0.5.6:
version "0.5.12"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599"
integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==
@@ -10057,7 +10133,7 @@ source-map@^0.5.0, source-map@^0.5.6:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
-source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@@ -10588,6 +10664,15 @@ terser@^3.8.1:
source-map "~0.6.1"
source-map-support "~0.5.6"
+terser@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-4.0.0.tgz#ef356f6f359a963e2cc675517f21c1c382877374"
+ integrity sha512-dOapGTU0hETFl1tCo4t56FN+2jffoKyER9qBGoUFyZ6y7WLoKT0bF+lAYi6B6YsILcGF3q1C2FBh8QcKSCgkgA==
+ dependencies:
+ commander "^2.19.0"
+ source-map "~0.6.1"
+ source-map-support "~0.5.10"
+
test-exclude@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.0.0.tgz#cdce7cece785e0e829cd5c2b27baf18bc583cfb7"
@@ -10842,6 +10927,16 @@ trough@^1.0.0:
dependencies:
glob "^7.1.2"
+try-catch@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/try-catch/-/try-catch-2.0.0.tgz#a491141d597f8b72b46757fe1c47059341a16aed"
+ integrity sha512-RPXpVjsbtWgymwGq5F/OWDFsjEzdvzwHFaMjWWW6f/p6+uk/N7YSKJHQfIfGqITfj8qH4cBqCLMnhKZBaKk7Kg==
+
+try-to-catch@^1.0.2:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/try-to-catch/-/try-to-catch-1.1.1.tgz#770162dd13b9a0e55da04db5b7f888956072038a"
+ integrity sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA==
+
tryer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7"
@@ -10928,10 +11023,10 @@ uc.micro@^1.0.1, uc.micro@^1.0.5:
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376"
integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg==
-uglify-js@^3.1.4:
- version "3.5.15"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.5.15.tgz#fe2b5378fd0b09e116864041437bff889105ce24"
- integrity sha512-fe7aYFotptIddkwcm6YuA0HmknBZ52ZzOsUxZEdhhkSsz7RfjHDX2QDxwKTiv4JQ5t5NhfmpgAK+J7LiDhKSqg==
+uglify-js@^3.1.4, uglify-js@^3.5.1:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"
+ integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
dependencies:
commander "~2.20.0"
source-map "~0.6.1"
@@ -11128,6 +11223,11 @@ update-notifier@^2.5.0:
semver-diff "^2.0.0"
xdg-basedir "^3.0.0"
+upper-case@^1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
+ integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
+
uri-js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"