summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/pages/projects/blob/show/index.js1
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js1
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js2
-rw-r--r--app/assets/javascripts/pages/projects/pages_domains/form.js20
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue4
-rw-r--r--app/assets/javascripts/releases/detail/index.js2
-rw-r--r--app/assets/javascripts/repository/components/last_commit.vue4
-rw-r--r--app/assets/javascripts/repository/log_tree.js14
-rw-r--r--app/assets/javascripts/repository/queries/commit.fragment.graphql8
-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/pathLastCommit.query.graphql2
-rw-r--r--app/assets/javascripts/repository/utils/commit.js13
-rw-r--r--app/assets/javascripts/sourcegraph/index.js28
-rw-r--r--app/assets/javascripts/sourcegraph/load.js6
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue2
-rw-r--r--app/assets/stylesheets/framework/common.scss7
-rw-r--r--app/assets/stylesheets/framework/responsive_tables.scss11
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss12
-rw-r--r--app/controllers/concerns/sourcegraph_gon.rb30
-rw-r--r--app/controllers/profiles/preferences_controller.rb3
-rw-r--r--app/controllers/projects/blob_controller.rb2
-rw-r--r--app/controllers/projects/commit_controller.rb1
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/controllers/projects/pages_domains_controller.rb7
-rw-r--r--app/helpers/application_settings_helper.rb3
-rw-r--r--app/helpers/sourcegraph_helper.rb27
-rw-r--r--app/models/application_setting.rb8
-rw-r--r--app/models/application_setting_implementation.rb3
-rw-r--r--app/models/user.rb1
-rw-r--r--app/services/concerns/git/logger.rb10
-rw-r--r--app/services/merge_requests/base_service.rb13
-rw-r--r--app/services/merge_requests/rebase_service.rb6
-rw-r--r--app/services/merge_requests/squash_service.rb4
-rw-r--r--app/services/merge_requests/working_copy_base_service.rb26
-rw-r--r--app/views/admin/application_settings/_sourcegraph.html.haml38
-rw-r--r--app/views/admin/application_settings/integrations.html.haml1
-rw-r--r--app/views/profiles/preferences/_sourcegraph.html.haml26
-rw-r--r--app/views/profiles/preferences/show.html.haml3
-rw-r--r--app/views/projects/blob/_markdown_buttons.html.haml2
-rw-r--r--app/views/projects/pages/_list.html.haml4
-rw-r--r--app/views/projects/pages_domains/_certificate.html.haml79
-rw-r--r--app/views/projects/pages_domains/_dns.html.haml33
-rw-r--r--app/views/projects/pages_domains/_form.html.haml71
-rw-r--r--app/views/projects/pages_domains/_lets_encrypt_callout.html.haml13
-rw-r--r--app/views/projects/pages_domains/edit.html.haml15
-rw-r--r--app/views/projects/pages_domains/new.html.haml1
-rw-r--r--app/views/projects/pages_domains/show.html.haml2
-rw-r--r--app/views/shared/issuable/_form.html.haml1
-rw-r--r--app/views/shared/issuable/form/_branch_chooser.html.haml1
-rw-r--r--changelogs/unreleased/30660-allow-to-enable-disable-auto-ssl-letsencrypt-support-via-api.yml5
-rw-r--r--changelogs/unreleased/34426-use-new-list-task-icon-in-text-editor.yml6
-rw-r--r--changelogs/unreleased/35547-add-documentation-for-sign-in-application-setting.yml5
-rw-r--r--changelogs/unreleased/remove-domain-details.yml5
-rw-r--r--config/application.rb4
-rw-r--r--config/webpack.config.js5
-rw-r--r--danger/commit_messages/Dangerfile8
-rw-r--r--db/migrate/20190827222124_add_sourcegraph_configuration_to_application_settings.rb21
-rw-r--r--db/migrate/20191107173446_add_sourcegraph_admin_and_user_preferences.rb17
-rw-r--r--db/schema.rb4
-rw-r--r--doc/administration/pages/index.md117
-rw-r--r--doc/api/pages_domains.md71
-rw-r--r--doc/api/settings.md3
-rw-r--r--doc/development/feature_flags/development.md2
-rw-r--r--doc/integration/README.md1
-rw-r--r--doc/integration/img/sourcegraph_admin_v12_5.pngbin0 -> 61520 bytes
-rw-r--r--doc/integration/img/sourcegraph_demo_v12_5.pngbin0 -> 97025 bytes
-rw-r--r--doc/integration/img/sourcegraph_popover_v12_5.pngbin0 -> 27925 bytes
-rw-r--r--doc/integration/img/sourcegraph_user_preferences_v12_5.pngbin0 -> 37710 bytes
-rw-r--r--doc/integration/sourcegraph.md119
-rw-r--r--doc/user/admin_area/settings/img/two_factor_grace_period.pngbin0 -> 17591 bytes
-rw-r--r--doc/user/admin_area/settings/index.md1
-rw-r--r--doc/user/admin_area/settings/sign_in_restrictions.md56
-rw-r--r--doc/user/profile/preferences.md13
-rw-r--r--lib/api/entities.rb2
-rw-r--r--lib/api/pages_domains.rb14
-rw-r--r--lib/api/settings.rb5
-rw-r--r--lib/gitlab/redis/wrapper.rb2
-rw-r--r--lib/gitlab/sourcegraph.rb26
-rw-r--r--locale/gitlab.pot61
-rw-r--r--package.json1
-rwxr-xr-xscripts/review_apps/review-apps.sh30
-rw-r--r--spec/controllers/concerns/sourcegraph_gon_spec.rb118
-rw-r--r--spec/controllers/projects/pages_domains_controller_spec.rb12
-rw-r--r--spec/features/projects/pages_lets_encrypt_spec.rb31
-rw-r--r--spec/features/projects/pages_spec.rb62
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json3
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json3
-rw-r--r--spec/frontend/repository/log_tree_spec.js18
-rw-r--r--spec/frontend/repository/utils/commit_spec.js30
-rw-r--r--spec/helpers/sourcegraph_helper_spec.rb64
-rw-r--r--spec/lib/gitlab/sourcegraph_spec.rb66
-rw-r--r--spec/models/application_setting_spec.rb30
-rw-r--r--spec/requests/api/pages_domains_spec.rb94
-rw-r--r--spec/requests/api/settings_spec.rb19
-rw-r--r--spec/views/admin/application_settings/integrations.html.haml_spec.rb34
-rw-r--r--spec/views/profiles/preferences/show.html.haml_spec.rb72
-rw-r--r--spec/views/projects/pages_domains/show.html.haml_spec.rb34
-rw-r--r--yarn.lock5
99 files changed, 1574 insertions, 317 deletions
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index 84e5bb3c46e..aee67899ca2 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -3,6 +3,7 @@ import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_sta
import BlobViewer from '~/blob/viewer/index';
import initBlob from '~/pages/projects/init_blob';
import GpgBadges from '~/gpg_badges';
+import '~/sourcegraph/load';
document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index 5aa4734244e..0eb6f231839 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -9,6 +9,7 @@ import initNotes from '~/init_notes';
import initChangesDropdown from '~/init_changes_dropdown';
import initDiffNotes from '~/diff_notes/diff_notes_bundle';
import { fetchCommitMergeRequests } from '~/commit_merge_requests';
+import '~/sourcegraph/load';
document.addEventListener('DOMContentLoaded', () => {
const hasPerfBar = document.querySelector('.with-performance-bar');
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
index fa1de1f13cb..16034313af2 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
@@ -5,6 +5,7 @@ import { handleLocationHash } from '~/lib/utils/common_utils';
import howToMerge from '~/how_to_merge';
import initPipelines from '~/commit/pipelines/pipelines_bundle';
import initVueIssuableSidebarApp from '~/issuable_sidebar/sidebar_bundle';
+import initSourcegraph from '~/sourcegraph';
import initWidget from '../../../vue_merge_request_widget';
export default function() {
@@ -19,4 +20,5 @@ export default function() {
handleLocationHash();
howToMerge();
initWidget();
+ initSourcegraph();
}
diff --git a/app/assets/javascripts/pages/projects/pages_domains/form.js b/app/assets/javascripts/pages/projects/pages_domains/form.js
index cef8e92610c..ae5368179b1 100644
--- a/app/assets/javascripts/pages/projects/pages_domains/form.js
+++ b/app/assets/javascripts/pages/projects/pages_domains/form.js
@@ -1,17 +1,23 @@
import setupToggleButtons from '~/toggle_buttons';
+function updateVisibility(selector, isVisible) {
+ Array.from(document.querySelectorAll(selector)).forEach(el => {
+ if (isVisible) {
+ el.classList.remove('d-none');
+ } else {
+ el.classList.add('d-none');
+ }
+ });
+}
+
export default () => {
const toggleContainer = document.querySelector('.js-auto-ssl-toggle-container');
if (toggleContainer) {
const onToggleButtonClicked = isAutoSslEnabled => {
- Array.from(document.querySelectorAll('.js-shown-unless-auto-ssl')).forEach(el => {
- if (isAutoSslEnabled) {
- el.classList.add('d-none');
- } else {
- el.classList.remove('d-none');
- }
- });
+ updateVisibility('.js-shown-unless-auto-ssl', !isAutoSslEnabled);
+
+ updateVisibility('.js-shown-if-auto-ssl', isAutoSslEnabled);
Array.from(document.querySelectorAll('.js-enabled-unless-auto-ssl')).forEach(el => {
if (isAutoSslEnabled) {
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
index 688baa93b6d..96177512e35 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
@@ -64,14 +64,14 @@ export default {
v-for="(testSuite, index) in getTestSuites"
:key="index"
role="row"
- class="gl-responsive-table-row test-reports-summary-row rounded cursor-pointer js-suite-row"
+ class="gl-responsive-table-row gl-responsive-table-row-clickable test-reports-summary-row rounded cursor-pointer js-suite-row"
@click="tableRowClick(testSuite)"
>
<div class="table-section section-25">
<div role="rowheader" class="table-mobile-header font-weight-bold">
{{ __('Suite') }}
</div>
- <div class="table-mobile-content test-reports-summary-suite cgray pl-3">
+ <div class="table-mobile-content underline cgray pl-3">
{{ testSuite.name }}
</div>
</div>
diff --git a/app/assets/javascripts/releases/detail/index.js b/app/assets/javascripts/releases/detail/index.js
index 3da971e6d90..0dab90a1ede 100644
--- a/app/assets/javascripts/releases/detail/index.js
+++ b/app/assets/javascripts/releases/detail/index.js
@@ -5,7 +5,7 @@ import createStore from './store';
export default () => {
const el = document.getElementById('js-edit-release-page');
- const store = createStore(el.dataset);
+ const store = createStore();
store.dispatch('setInitialState', el.dataset);
return new Vue({
diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue
index 5a89efa4538..26c1f5813f5 100644
--- a/app/assets/javascripts/repository/components/last_commit.vue
+++ b/app/assets/javascripts/repository/components/last_commit.vue
@@ -109,7 +109,7 @@ export default {
class="text-expander"
@click="toggleShowDescription"
>
- <icon name="ellipsis_h" />
+ <icon name="ellipsis_h" :size="10" />
</gl-button>
<div class="committer">
<gl-link
@@ -124,7 +124,7 @@ export default {
</div>
<pre
v-if="commit.description"
- v-show="showDescription"
+ :class="{ 'd-block': showDescription }"
class="commit-row-description append-bottom-8"
>
{{ commit.description }}
diff --git a/app/assets/javascripts/repository/log_tree.js b/app/assets/javascripts/repository/log_tree.js
index 112f7d1a30e..87310278b9e 100644
--- a/app/assets/javascripts/repository/log_tree.js
+++ b/app/assets/javascripts/repository/log_tree.js
@@ -1,4 +1,5 @@
import axios from '~/lib/utils/axios_utils';
+import { normalizeData } from 'ee_else_ce/repository/utils/commit';
import getCommits from './queries/getCommits.query.graphql';
import getProjectPath from './queries/getProjectPath.query.graphql';
import getRef from './queries/getRef.query.graphql';
@@ -6,19 +7,6 @@ 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,
- lockLabel: d.lock_label,
- __typename: 'LogTreeCommit',
- }));
-}
-
export function resolveCommit(commits, { resolve, entry }) {
const commit = commits.find(c => c.fileName === entry.name && c.type === entry.type);
diff --git a/app/assets/javascripts/repository/queries/commit.fragment.graphql b/app/assets/javascripts/repository/queries/commit.fragment.graphql
new file mode 100644
index 00000000000..9bb13c475c7
--- /dev/null
+++ b/app/assets/javascripts/repository/queries/commit.fragment.graphql
@@ -0,0 +1,8 @@
+fragment TreeEntryCommit on LogTreeCommit {
+ sha
+ message
+ committedDate
+ commitPath
+ fileName
+ type
+}
diff --git a/app/assets/javascripts/repository/queries/getCommit.query.graphql b/app/assets/javascripts/repository/queries/getCommit.query.graphql
index 65d750884a8..e4aeaaff8fe 100644
--- a/app/assets/javascripts/repository/queries/getCommit.query.graphql
+++ b/app/assets/javascripts/repository/queries/getCommit.query.graphql
@@ -1,11 +1,7 @@
+#import "ee_else_ce/repository/queries/commit.fragment.graphql"
+
query getCommit($fileName: String!, $type: String!, $path: String!) {
commit(path: $path, fileName: $fileName, type: $type) @client {
- sha
- message
- committedDate
- commitPath
- fileName
- type
- lockLabel
+ ...TreeEntryCommit
}
}
diff --git a/app/assets/javascripts/repository/queries/getCommits.query.graphql b/app/assets/javascripts/repository/queries/getCommits.query.graphql
index 780a24d2663..0976b8f32d7 100644
--- a/app/assets/javascripts/repository/queries/getCommits.query.graphql
+++ b/app/assets/javascripts/repository/queries/getCommits.query.graphql
@@ -1,11 +1,7 @@
+#import "ee_else_ce/repository/queries/commit.fragment.graphql"
+
query getCommits {
commits @client {
- sha
- message
- committedDate
- commitPath
- fileName
- type
- lockLabel
+ ...TreeEntryCommit
}
}
diff --git a/app/assets/javascripts/repository/queries/pathLastCommit.query.graphql b/app/assets/javascripts/repository/queries/pathLastCommit.query.graphql
index 74ccdd79dd0..4bb959a8001 100644
--- a/app/assets/javascripts/repository/queries/pathLastCommit.query.graphql
+++ b/app/assets/javascripts/repository/queries/pathLastCommit.query.graphql
@@ -5,7 +5,7 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) {
lastCommit {
sha
title
- message
+ description
webUrl
authoredDate
author {
diff --git a/app/assets/javascripts/repository/utils/commit.js b/app/assets/javascripts/repository/utils/commit.js
new file mode 100644
index 00000000000..6c204b57b37
--- /dev/null
+++ b/app/assets/javascripts/repository/utils/commit.js
@@ -0,0 +1,13 @@
+// eslint-disable-next-line import/prefer-default-export
+export function normalizeData(data, extra = () => {}) {
+ 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',
+ ...extra(d),
+ }));
+}
diff --git a/app/assets/javascripts/sourcegraph/index.js b/app/assets/javascripts/sourcegraph/index.js
new file mode 100644
index 00000000000..796e90bf08e
--- /dev/null
+++ b/app/assets/javascripts/sourcegraph/index.js
@@ -0,0 +1,28 @@
+function loadScript(path) {
+ const script = document.createElement('script');
+ script.type = 'application/javascript';
+ script.src = path;
+ script.defer = true;
+ document.head.appendChild(script);
+}
+
+/**
+ * Loads the Sourcegraph integration for support for Sourcegraph extensions and
+ * code intelligence.
+ */
+export default function initSourcegraph() {
+ const { url } = gon.sourcegraph || {};
+
+ if (!url) {
+ return;
+ }
+
+ const assetsUrl = new URL('/assets/webpack/sourcegraph/', window.location.href);
+ const scriptPath = new URL('scripts/integration.bundle.js', assetsUrl).href;
+
+ window.SOURCEGRAPH_ASSETS_URL = assetsUrl.href;
+ window.SOURCEGRAPH_URL = url;
+ window.SOURCEGRAPH_INTEGRATION = 'gitlab-integration';
+
+ loadScript(scriptPath);
+}
diff --git a/app/assets/javascripts/sourcegraph/load.js b/app/assets/javascripts/sourcegraph/load.js
new file mode 100644
index 00000000000..f9491505d42
--- /dev/null
+++ b/app/assets/javascripts/sourcegraph/load.js
@@ -0,0 +1,6 @@
+import initSourcegraph from './index';
+
+/**
+ * Load sourcegraph in it's own listener so that it's isolated from failures.
+ */
+document.addEventListener('DOMContentLoaded', initSourcegraph);
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index 7596dbf1229..af4ac024e4f 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -168,7 +168,7 @@ export default {
:prepend="true"
tag="* [ ] "
:button-title="__('Add a task list')"
- icon="task-done"
+ icon="list-task"
/>
<toolbar-button
:tag="mdTable"
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index f4e7e4e456b..31ea59df4c5 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -572,3 +572,10 @@ img.emoji {
.gl-font-size-20 { font-size: $gl-font-size-20; }
.gl-font-size-28 { font-size: $gl-font-size-28; }
.gl-font-size-42 { font-size: $gl-font-size-42; }
+
+.border-section {
+ @include gl-py-6;
+ @include gl-m-0;
+
+ border-top: 1px solid $border-color;
+}
diff --git a/app/assets/stylesheets/framework/responsive_tables.scss b/app/assets/stylesheets/framework/responsive_tables.scss
index fd6f80e26cb..1878fac1c60 100644
--- a/app/assets/stylesheets/framework/responsive_tables.scss
+++ b/app/assets/stylesheets/framework/responsive_tables.scss
@@ -20,6 +20,17 @@
@extend .gl-responsive-table-row-layout;
margin-top: 10px;
border: 1px solid $border-color;
+ color: $gray-700;
+
+ &.gl-responsive-table-row-clickable {
+ &:hover {
+ background-color: $gray-light;
+
+ .underline {
+ text-decoration: underline;
+ }
+ }
+ }
@include media-breakpoint-up(md) {
margin: 0;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index faf669829e1..364fe3da71e 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -1084,18 +1084,6 @@ button.mini-pipeline-graph-dropdown-toggle {
}
.test-reports-table {
- color: $gray-700;
-
- .test-reports-summary-row {
- &:hover {
- background-color: $gray-light;
-
- .test-reports-summary-suite {
- text-decoration: underline;
- }
- }
- }
-
.build-trace {
@include build-trace();
}
diff --git a/app/controllers/concerns/sourcegraph_gon.rb b/app/controllers/concerns/sourcegraph_gon.rb
new file mode 100644
index 00000000000..01925cf9d4d
--- /dev/null
+++ b/app/controllers/concerns/sourcegraph_gon.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module SourcegraphGon
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :push_sourcegraph_gon, if: :html_request?
+ end
+
+ private
+
+ def push_sourcegraph_gon
+ return unless sourcegraph_enabled?
+
+ gon.push({
+ sourcegraph: { url: Gitlab::CurrentSettings.sourcegraph_url }
+ })
+ end
+
+ def sourcegraph_enabled?
+ Gitlab::CurrentSettings.sourcegraph_enabled && sourcegraph_enabled_for_project? && current_user&.sourcegraph_enabled
+ end
+
+ def sourcegraph_enabled_for_project?
+ return false unless project && Gitlab::Sourcegraph.feature_enabled?(project)
+ return project.public? if Gitlab::CurrentSettings.sourcegraph_public_only
+
+ true
+ end
+end
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index 42d4d785174..214640a5295 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -47,7 +47,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:preferred_language,
:time_display_relative,
:time_format_in_24h,
- :show_whitespace_in_diffs
+ :show_whitespace_in_diffs,
+ :sourcegraph_enabled
]
end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 205ec288ce9..7c97f771a70 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -8,6 +8,8 @@ class Projects::BlobController < Projects::ApplicationController
include NotesHelper
include ActionView::Helpers::SanitizeHelper
include RedirectsForMissingPathOnTree
+ include SourcegraphGon
+
prepend_before_action :authenticate_user!, only: [:edit]
around_action :allow_gitaly_ref_name_caching, only: [:show]
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 939a09d4fd2..afb670b687b 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -8,6 +8,7 @@ class Projects::CommitController < Projects::ApplicationController
include CreatesCommit
include DiffForPath
include DiffHelper
+ include SourcegraphGon
# Authorize
before_action :require_non_empty_project
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 59987da21ec..766ec1e33f3 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -9,6 +9,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
include ToggleAwardEmoji
include IssuableCollections
include RecordUserLastActivity
+ include SourcegraphGon
skip_before_action :merge_request, only: [:index, :bulk_update]
before_action :whitelist_query_limiting, only: [:assign_related_issues, :update]
diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb
index 50a47e98893..8f40eac5186 100644
--- a/app/controllers/projects/pages_domains_controller.rb
+++ b/app/controllers/projects/pages_domains_controller.rb
@@ -8,6 +8,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController
before_action :domain, except: [:new, :create]
def show
+ redirect_to edit_project_pages_domain_path(@project, @domain)
end
def new
@@ -23,7 +24,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController
flash[:alert] = 'Failed to verify domain ownership'
end
- redirect_to project_pages_domain_path(@project, @domain)
+ redirect_to edit_project_pages_domain_path(@project, @domain)
end
def edit
@@ -33,7 +34,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController
@domain = @project.pages_domains.create(create_params)
if @domain.valid?
- redirect_to project_pages_domain_path(@project, @domain)
+ redirect_to edit_project_pages_domain_path(@project, @domain)
else
render 'new'
end
@@ -77,7 +78,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController
end
def update_params
- params.require(:pages_domain).permit(:user_provided_key, :user_provided_certificate, :auto_ssl_enabled)
+ params.fetch(:pages_domain, {}).permit(:user_provided_key, :user_provided_certificate, :auto_ssl_enabled)
end
def domain
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index f0868a8d377..a011209375e 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -259,6 +259,9 @@ module ApplicationSettingsHelper
:shared_runners_text,
:sign_in_text,
:signup_enabled,
+ :sourcegraph_enabled,
+ :sourcegraph_url,
+ :sourcegraph_public_only,
:terminal_max_session_time,
:terms,
:throttle_authenticated_api_enabled,
diff --git a/app/helpers/sourcegraph_helper.rb b/app/helpers/sourcegraph_helper.rb
new file mode 100644
index 00000000000..cc5a5c77e9a
--- /dev/null
+++ b/app/helpers/sourcegraph_helper.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module SourcegraphHelper
+ def sourcegraph_url_message
+ link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: Gitlab::CurrentSettings.sourcegraph_url }
+ link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe
+
+ message =
+ if Gitlab::CurrentSettings.sourcegraph_url_is_com?
+ s_('SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}.').html_safe
+ else
+ s_('SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}.').html_safe
+ end
+
+ message % { link_start: link_start, link_end: link_end }
+ end
+
+ def sourcegraph_experimental_message
+ if Gitlab::Sourcegraph.feature_conditional?
+ s_("SourcegraphPreferences|This feature is experimental and currently limited to certain projects.")
+ elsif Gitlab::CurrentSettings.sourcegraph_public_only
+ s_("SourcegraphPreferences|This feature is experimental and limited to public projects.")
+ else
+ s_("SourcegraphPreferences|This feature is experimental.")
+ end
+ end
+end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index b47e1142cca..4028d711fd1 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -99,6 +99,10 @@ class ApplicationSetting < ApplicationRecord
presence: true,
if: :plantuml_enabled
+ validates :sourcegraph_url,
+ presence: true,
+ if: :sourcegraph_enabled
+
validates :snowplow_collector_hostname,
presence: true,
hostname: true,
@@ -343,6 +347,10 @@ class ApplicationSetting < ApplicationRecord
end
after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') }
+ def sourcegraph_url_is_com?
+ !!(sourcegraph_url =~ /\Ahttps:\/\/(www\.)?sourcegraph\.com/)
+ end
+
def self.create_from_defaults
transaction(requires_new: true) do
super
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index 80715fae68d..7bb89f0d1e2 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -102,6 +102,9 @@ module ApplicationSettingImplementation
shared_runners_text: nil,
sign_in_text: nil,
signup_enabled: Settings.gitlab['signup_enabled'],
+ sourcegraph_enabled: false,
+ sourcegraph_url: nil,
+ sourcegraph_public_only: true,
terminal_max_session_time: 0,
throttle_authenticated_api_enabled: false,
throttle_authenticated_api_period_in_seconds: 3600,
diff --git a/app/models/user.rb b/app/models/user.rb
index f704589ad80..d0e758b0055 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -240,6 +240,7 @@ class User < ApplicationRecord
delegate :time_display_relative, :time_display_relative=, to: :user_preference
delegate :time_format_in_24h, :time_format_in_24h=, to: :user_preference
delegate :show_whitespace_in_diffs, :show_whitespace_in_diffs=, to: :user_preference
+ delegate :sourcegraph_enabled, :sourcegraph_enabled=, to: :user_preference
delegate :setup_for_company, :setup_for_company=, to: :user_preference
accepts_nested_attributes_for :user_preference, update_only: true
diff --git a/app/services/concerns/git/logger.rb b/app/services/concerns/git/logger.rb
new file mode 100644
index 00000000000..7c036212e66
--- /dev/null
+++ b/app/services/concerns/git/logger.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Git
+ module Logger
+ def log_error(message, save_message_on_model: false)
+ Gitlab::GitLogger.error("#{self.class.name} error (#{merge_request.to_reference(full: true)}): #{message}")
+ merge_request.update(merge_error: message) if save_message_on_model
+ end
+ end
+end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index aacc3d6831e..00bf69739ad 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -29,6 +29,19 @@ module MergeRequests
.execute_for_merge_request(merge_request)
end
+ def source_project
+ @source_project ||= merge_request.source_project
+ end
+
+ def target_project
+ @target_project ||= merge_request.target_project
+ end
+
+ # Don't try to print expensive instance variables.
+ def inspect
+ "#<#{self.class} #{merge_request.to_reference(full: true)}>"
+ end
+
private
def create(merge_request)
diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb
index 4d36dd4feae..7e9442c0c7c 100644
--- a/app/services/merge_requests/rebase_service.rb
+++ b/app/services/merge_requests/rebase_service.rb
@@ -1,9 +1,13 @@
# frozen_string_literal: true
module MergeRequests
- class RebaseService < MergeRequests::WorkingCopyBaseService
+ class RebaseService < MergeRequests::BaseService
+ include Git::Logger
+
REBASE_ERROR = 'Rebase failed. Please rebase locally'
+ attr_reader :merge_request
+
def execute(merge_request)
@merge_request = merge_request
diff --git a/app/services/merge_requests/squash_service.rb b/app/services/merge_requests/squash_service.rb
index 88ca3b4f5a8..d25997c925e 100644
--- a/app/services/merge_requests/squash_service.rb
+++ b/app/services/merge_requests/squash_service.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module MergeRequests
- class SquashService < MergeRequests::WorkingCopyBaseService
+ class SquashService < MergeRequests::BaseService
+ include Git::Logger
+
def execute
# If performing a squash would result in no change, then
# immediately return a success message without performing a squash
diff --git a/app/services/merge_requests/working_copy_base_service.rb b/app/services/merge_requests/working_copy_base_service.rb
deleted file mode 100644
index 2d2be1f4c25..00000000000
--- a/app/services/merge_requests/working_copy_base_service.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-module MergeRequests
- class WorkingCopyBaseService < MergeRequests::BaseService
- attr_reader :merge_request
-
- def source_project
- @source_project ||= merge_request.source_project
- end
-
- def target_project
- @target_project ||= merge_request.target_project
- end
-
- def log_error(message, save_message_on_model: false)
- Gitlab::GitLogger.error("#{self.class.name} error (#{merge_request.to_reference(full: true)}): #{message}")
-
- merge_request.update(merge_error: message) if save_message_on_model
- end
-
- # Don't try to print expensive instance variables.
- def inspect
- "#<#{self.class} #{merge_request.to_reference(full: true)}>"
- end
- end
-end
diff --git a/app/views/admin/application_settings/_sourcegraph.html.haml b/app/views/admin/application_settings/_sourcegraph.html.haml
new file mode 100644
index 00000000000..23cda0334a2
--- /dev/null
+++ b/app/views/admin/application_settings/_sourcegraph.html.haml
@@ -0,0 +1,38 @@
+- return unless Gitlab::Sourcegraph.feature_available?
+- expanded = integration_expanded?('sourcegraph_')
+
+%section.settings.as-sourcegraph.no-animate#js-sourcegraph-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4
+ = _('Sourcegraph')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: 'https://sourcegraph.com/' }
+ - link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe
+ = s_('SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance\'s code views and merge requests.').html_safe % { link_start: link_start, link_end: link_end }
+ %span
+ = link_to s_('SourcegraphAdmin|More information'), help_page_path('integration/sourcegraph.md'), target: '_blank'
+
+
+ .settings-content
+ = form_for @application_setting, url: integrations_admin_application_settings_path(anchor: 'js-sourcegraph-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting)
+
+ %fieldset
+ .form-group
+ .form-check
+ = f.check_box :sourcegraph_enabled, class: 'form-check-input'
+ = f.label :sourcegraph_enabled, s_('SourcegraphAdmin|Enable Sourcegraph'), class: 'form-check-label'
+ .form-group
+ .form-check
+ = f.check_box :sourcegraph_public_only, class: 'form-check-input'
+ = f.label :sourcegraph_public_only, s_('SourcegraphAdmin|Block on private and internal projects'), class: 'form-check-label'
+ .form-text.text-muted
+ = s_('SourcegraphAdmin|If checked, only public projects will have code intelligence and communicate with Sourcegraph.')
+ .form-group
+ = f.label :sourcegraph_url, s_('SourcegraphAdmin|Sourcegraph URL'), class: 'label-bold'
+ = f.text_field :sourcegraph_url, class: 'form-control', placeholder: s_('SourcegraphAdmin|e.g. https://sourcegraph.example.com')
+ .form-text.text-muted
+ = s_('SourcegraphAdmin|Configure the URL to a Sourcegraph instance which can read your GitLab projects.')
+ = f.submit s_('SourcegraphAdmin|Save changes'), class: 'btn btn-success'
diff --git a/app/views/admin/application_settings/integrations.html.haml b/app/views/admin/application_settings/integrations.html.haml
index 3f459e0f491..0aa833e49a8 100644
--- a/app/views/admin/application_settings/integrations.html.haml
+++ b/app/views/admin/application_settings/integrations.html.haml
@@ -4,6 +4,7 @@
= render_if_exists 'admin/application_settings/elasticsearch_form'
= render 'admin/application_settings/plantuml'
+= render 'admin/application_settings/sourcegraph'
= render_if_exists 'admin/application_settings/slack'
= render 'admin/application_settings/third_party_offers'
= render 'admin/application_settings/snowplow'
diff --git a/app/views/profiles/preferences/_sourcegraph.html.haml b/app/views/profiles/preferences/_sourcegraph.html.haml
new file mode 100644
index 00000000000..20a904694ca
--- /dev/null
+++ b/app/views/profiles/preferences/_sourcegraph.html.haml
@@ -0,0 +1,26 @@
+- return unless Gitlab::Sourcegraph::feature_available? && Gitlab::CurrentSettings.sourcegraph_enabled
+- sourcegraph_url = Gitlab::CurrentSettings.sourcegraph_url
+
+.col-sm-12
+ %hr
+
+.col-lg-4.profile-settings-sidebar
+ %h4.prepend-top-0
+ = s_('Preferences|Integrations')
+ %p
+ = s_('Preferences|Customize integrations with third party services.')
+ = succeed '.' do
+ = link_to _('Learn more'), help_page_path('user/profile/preferences.md', anchor: 'integrations'), target: '_blank'
+.col-lg-8
+ %label.label-bold
+ = s_('Preferences|Sourcegraph')
+ = link_to icon('question-circle'), help_page_path('user/profile/preferences.md', anchor: 'sourcegraph'), target: '_blank', class: 'has-tooltip', title: _('More information')
+ .form-group.form-check
+ = f.check_box :sourcegraph_enabled, class: 'form-check-input'
+ = f.label :sourcegraph_enabled, class: 'form-check-label' do
+ - link_start = '<a href="%{url}">'.html_safe % { url: sourcegraph_url }
+ - link_end = '</a>'.html_safe
+ = s_('Preferences|Enable integrated code intelligence on code views').html_safe % { link_start: link_start, link_end: link_end }
+ .form-text.text-muted
+ = sourcegraph_url_message
+ = sourcegraph_experimental_message
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 84657592cd8..bf76b7379dd 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -111,6 +111,9 @@
= time_display_label
.form-text.text-muted
= s_('Preferences|For example: 30 mins ago.')
+
+ = render 'sourcegraph', f: f
+
.col-lg-4.profile-settings-sidebar
.col-lg-8
.form-group
diff --git a/app/views/projects/blob/_markdown_buttons.html.haml b/app/views/projects/blob/_markdown_buttons.html.haml
index 28d1ff97825..44ec2fa69cb 100644
--- a/app/views/projects/blob/_markdown_buttons.html.haml
+++ b/app/views/projects/blob/_markdown_buttons.html.haml
@@ -6,7 +6,7 @@
= markdown_toolbar_button({ icon: "link", data: { "md-tag" => "[{text}](url)", "md-select" => "url" }, title: _("Add a link") })
= markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: _("Add a bullet list") })
= markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: _("Add a numbered list") })
- = markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: _("Add a task list") })
+ = markdown_toolbar_button({ icon: "list-task", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: _("Add a task list") })
= markdown_toolbar_button({ icon: "table", data: { "md-tag" => "| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |", "md-prepend" => true }, title: _("Add a table") })
- if show_fullscreen_button
%button.toolbar-btn.toolbar-fullscreen-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: _("Go full screen"), data: { container: "body" } }
diff --git a/app/views/projects/pages/_list.html.haml b/app/views/projects/pages/_list.html.haml
index b05491f2c6e..4676c7399f1 100644
--- a/app/views/projects/pages/_list.html.haml
+++ b/app/views/projects/pages/_list.html.haml
@@ -21,11 +21,11 @@
%span.badge.badge-danger
= s_('GitLabPages|Expired')
%div
- = link_to s_('GitLabPages|Details'), project_pages_domain_path(@project, domain), class: "btn btn-sm btn-grouped"
+ = link_to s_('GitLabPages|Edit'), edit_project_pages_domain_path(@project, domain), class: "btn btn-sm btn-grouped btn-success btn-inverted"
= link_to s_('GitLabPages|Remove'), project_pages_domain_path(@project, domain), data: { confirm: s_('GitLabPages|Are you sure?')}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
- if verification_enabled && domain.unverified?
%li.list-group-item.bs-callout-warning
- - details_link_start = "<a href='#{project_pages_domain_path(@project, domain)}'>".html_safe
+ - details_link_start = "<a href='#{edit_project_pages_domain_path(@project, domain)}'>".html_safe
- details_link_end = '</a>'.html_safe
= s_('GitLabPages|%{domain} is not verified. To learn how to verify ownership, visit your %{link_start}domain details%{link_end}.').html_safe % { domain: domain.domain,
link_start: details_link_start,
diff --git a/app/views/projects/pages_domains/_certificate.html.haml b/app/views/projects/pages_domains/_certificate.html.haml
index 42631fca5e8..92d30e0b056 100644
--- a/app/views/projects/pages_domains/_certificate.html.haml
+++ b/app/views/projects/pages_domains/_certificate.html.haml
@@ -1,18 +1,63 @@
-- if @domain.auto_ssl_enabled?
- - if @domain.enabled?
- - if @domain.certificate_text
- %pre
- = @domain.certificate_text
- - else
- .bs-callout.bs-callout-info
- = _("GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later.")
+- auto_ssl_available = ::Gitlab::LetsEncrypt.enabled?
+- auto_ssl_enabled = @domain.auto_ssl_enabled?
+- auto_ssl_available_and_enabled = auto_ssl_available && auto_ssl_enabled
+- has_user_defined_certificate = @domain.certificate && @domain.certificate_user_provided?
+
+- if auto_ssl_available
+ .form-group.border-section
+ .row
+ .col-sm-2
+ = _('Certificate')
+ .col-sm-10.js-auto-ssl-toggle-container
+ %label{ for: "pages_domain_auto_ssl_enabled_button" }
+ - lets_encrypt_link_url = "https://letsencrypt.org/"
+ - lets_encrypt_link_start = "<a href=\"%{lets_encrypt_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { lets_encrypt_link_url: lets_encrypt_link_url }
+ - lets_encrypt_link_end = "</a>".html_safe
+ = _("Automatic certificate management using %{lets_encrypt_link_start}Let's Encrypt%{lets_encrypt_link_end}").html_safe % { lets_encrypt_link_start: lets_encrypt_link_start, lets_encrypt_link_end: lets_encrypt_link_end }
+ %button{ type: "button", id: "pages_domain_auto_ssl_enabled_button",
+ class: "js-project-feature-toggle project-feature-toggle mt-2 #{"is-checked" if auto_ssl_available_and_enabled}",
+ "aria-label": _("Automatic certificate management using Let's Encrypt") }
+ = f.hidden_field :auto_ssl_enabled?, class: "js-project-feature-toggle-input"
+ %span.toggle-icon
+ = sprite_icon("status_success_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-checked")
+ = sprite_icon("status_failed_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-unchecked")
+ %p.text-secondary.mt-3
+ - docs_link_url = help_page_path("user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md")
+ - docs_link_start = "<a href=\"%{docs_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { docs_link_url: docs_link_url }
+ - docs_link_end = "</a>".html_safe
+ = _("Let's Encrypt is a free, automated, and open certificate authority (CA) that gives digital certificates in order to enable HTTPS (SSL/TLS) for websites. Learn more about Let's Encrypt configuration by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}.").html_safe % { docs_link_url: docs_link_url, docs_link_start: docs_link_start, docs_link_end: docs_link_end }
+
+.form-group.border-section.js-shown-unless-auto-ssl{ class: ("d-none" if auto_ssl_available_and_enabled) }
+ - if has_user_defined_certificate
+ .row
+ .col-sm-10.offset-sm-2
+ .card
+ .card-header
+ = _('Certificate')
+ .d-flex.justify-content-between.align-items-center.p-3
+ %span
+ = @domain.subject || _('missing')
+ = link_to _('Remove'),
+ clean_certificate_project_pages_domain_path(@project, @domain),
+ data: { confirm: _('Are you sure?') },
+ class: 'btn btn-remove btn-sm',
+ method: :delete
- else
- .bs-callout.bs-callout-warning
- = _("A Let's Encrypt SSL certificate can not be obtained until your domain is verified.")
-- else
- - if @domain.certificate_text
- %pre
- = @domain.certificate_text
- - else
- .light
- = _("missing")
+ .row
+ .col-sm-10.offset-sm-2
+ = f.label :user_provided_certificate, _("Certificate (PEM)")
+ = f.text_area :user_provided_certificate,
+ rows: 5,
+ class: "form-control js-enabled-unless-auto-ssl",
+ disabled: auto_ssl_available_and_enabled
+ %span.help-inline.text-muted= _("Upload a certificate for your domain with all intermediates")
+ .row
+ .col-sm-10.offset-sm-2
+ = f.label :user_provided_key, _("Key (PEM)")
+ = f.text_area :user_provided_key,
+ rows: 5,
+ class: "form-control js-enabled-unless-auto-ssl",
+ disabled: auto_ssl_available_and_enabled
+ %span.help-inline.text-muted= _("Upload a private key for your certificate")
+
+= render 'lets_encrypt_callout', auto_ssl_available_and_enabled: auto_ssl_available_and_enabled
diff --git a/app/views/projects/pages_domains/_dns.html.haml b/app/views/projects/pages_domains/_dns.html.haml
new file mode 100644
index 00000000000..e4e590f0a98
--- /dev/null
+++ b/app/views/projects/pages_domains/_dns.html.haml
@@ -0,0 +1,33 @@
+- verification_enabled = Gitlab::CurrentSettings.pages_domain_verification_enabled?
+- dns_record = "#{@domain.domain} CNAME #{@domain.project.pages_subdomain}.#{Settings.pages.host}."
+
+.form-group.border-section
+ .row
+ .col-sm-2
+ = _("DNS")
+ .col-sm-10
+ .input-group
+ = text_field_tag :domain_dns, dns_record , class: "monospace js-select-on-focus form-control", readonly: true
+ .input-group-append
+ = clipboard_button(target: '#domain_dns', class: 'btn-default input-group-text d-none d-sm-block')
+ %p.form-text.text-muted
+ = _("To access this domain create a new DNS record")
+- if verification_enabled
+ - verification_record = "#{@domain.verification_domain} TXT #{@domain.keyed_verification_code}"
+ .form-group.border-section
+ .row
+ .col-sm-2
+ = _("Verification status")
+ .col-sm-10
+ .status-badge
+ - text, status = @domain.unverified? ? [_('Unverified'), 'badge-danger'] : [_('Verified'), 'badge-success']
+ .badge{ class: status }
+ = text
+ = link_to sprite_icon("redo"), verify_project_pages_domain_path(@project, @domain), method: :post, class: "btn has-tooltip", title: _("Retry verification")
+ .input-group
+ = text_field_tag :domain_verification, verification_record, class: "monospace js-select-on-focus form-control", readonly: true
+ .input-group-append
+ = clipboard_button(target: '#domain_verification', class: 'btn-default d-none d-sm-block')
+ %p.form-text.text-muted
+ - link_to_help = link_to(_('verify ownership'), help_page_path('user/project/pages/custom_domains_ssl_tls_certification/index.md', anchor: '4-verify-the-domains-ownership'))
+ = _("To %{link_to_help} of your domain, add the above key to a TXT record within to your DNS configuration.").html_safe % { link_to_help: link_to_help }
diff --git a/app/views/projects/pages_domains/_form.html.haml b/app/views/projects/pages_domains/_form.html.haml
index 4aa1e574d93..e06dab9be06 100644
--- a/app/views/projects/pages_domains/_form.html.haml
+++ b/app/views/projects/pages_domains/_form.html.haml
@@ -3,62 +3,25 @@
- @domain.errors.full_messages.each do |msg|
= msg
-.form-group.row
- .col-sm-2.col-form-label
- = f.label :domain, _("Domain")
- .col-sm-10
- = f.text_field :domain, required: true, autocomplete: "off", class: "form-control", disabled: @domain.persisted?
-
-- if Gitlab.config.pages.external_https
-
- - auto_ssl_available = ::Gitlab::LetsEncrypt.enabled?
- - auto_ssl_enabled = @domain.auto_ssl_enabled?
- - auto_ssl_available_and_enabled = auto_ssl_available && auto_ssl_enabled
-
- - if auto_ssl_available
- .form-group.row
- .col-sm-2.col-form-label
- %label{ for: "pages_domain_auto_ssl_enabled_button" }
- - lets_encrypt_link_url = "https://letsencrypt.org/"
- - lets_encrypt_link_start = "<a href=\"%{lets_encrypt_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { lets_encrypt_link_url: lets_encrypt_link_url }
- - lets_encrypt_link_end = "</a>".html_safe
- = _("Automatic certificate management using %{lets_encrypt_link_start}Let's Encrypt%{lets_encrypt_link_end}").html_safe % { lets_encrypt_link_start: lets_encrypt_link_start, lets_encrypt_link_end: lets_encrypt_link_end }
-
- .col-sm-10.js-auto-ssl-toggle-container
- %button{ type: "button", id: "pages_domain_auto_ssl_enabled_button",
- class: "js-project-feature-toggle project-feature-toggle mt-2 #{"is-checked" if auto_ssl_available_and_enabled}",
- "aria-label": _("Automatic certificate management using Let's Encrypt") }
- = f.hidden_field :auto_ssl_enabled?, class: "js-project-feature-toggle-input"
- %span.toggle-icon
- = sprite_icon("status_success_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-checked")
- = sprite_icon("status_failed_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-unchecked")
- %p.text-secondary.mt-3
- - docs_link_url = help_page_path("user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md")
- - docs_link_start = "<a href=\"%{docs_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { docs_link_url: docs_link_url }
- - docs_link_end = "</a>".html_safe
- = _("Let's Encrypt is a free, automated, and open certificate authority (CA) that gives digital certificates in order to enable HTTPS (SSL/TLS) for websites. Learn more about Let's Encrypt configuration by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}.").html_safe % { docs_link_url: docs_link_url, docs_link_start: docs_link_start, docs_link_end: docs_link_end }
-
- .js-shown-unless-auto-ssl{ class: ("d-none" if auto_ssl_available_and_enabled) }
- .form-group.row
- .col-sm-2.col-form-label
- = f.label :user_provided_certificate, _("Certificate (PEM)")
+.form-group.border-section
+ .row
+ - if @domain.persisted?
+ .col-sm-2
+ = _("Domain")
.col-sm-10
- = f.text_area :user_provided_certificate,
- rows: 5,
- class: "form-control js-enabled-unless-auto-ssl",
- disabled: auto_ssl_available_and_enabled
- %span.help-inline.text-muted= _("Upload a certificate for your domain with all intermediates")
-
- .form-group.row
- .col-sm-2.col-form-label
- = f.label :user_provided_key, _("Key (PEM)")
+ = external_link(@domain.url, @domain.url)
+ - else
+ .col-sm-2
+ = f.label :domain, _("Domain")
.col-sm-10
- = f.text_area :user_provided_key,
- rows: 5,
- class: "form-control js-enabled-unless-auto-ssl",
- disabled: auto_ssl_available_and_enabled
- %span.help-inline.text-muted= _("Upload a private key for your certificate")
+ .input-group
+ = f.text_field :domain, required: true, autocomplete: "off", class: "form-control"
+- if @domain.persisted?
+ = render 'dns'
+
+- if Gitlab.config.pages.external_https
+ = render 'certificate', f: f
- else
- .nothing-here-block
+ .border-section.nothing-here-block
= _("Support for custom certificates is disabled. Ask your system's administrator to enable it.")
diff --git a/app/views/projects/pages_domains/_lets_encrypt_callout.html.haml b/app/views/projects/pages_domains/_lets_encrypt_callout.html.haml
new file mode 100644
index 00000000000..d6406a78fca
--- /dev/null
+++ b/app/views/projects/pages_domains/_lets_encrypt_callout.html.haml
@@ -0,0 +1,13 @@
+- if @domain.enabled?
+ - if @domain.auto_ssl_enabled && !@domain.certificate
+ .form-group.border-section.js-shown-if-auto-ssl{ class: ("d-none" unless auto_ssl_available_and_enabled) }
+ .row
+ .col-sm-10.offset-sm-2
+ .bs-callout.bs-callout-info.mt-0
+ = _("GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later.")
+- else
+ .form-group.border-section.js-shown-if-auto-ssl{ class: ("d-none" unless auto_ssl_available_and_enabled) }
+ .row
+ .col-sm-10.offset-sm-2
+ .bs-callout.bs-callout-warning.mt-0
+ = _("A Let's Encrypt SSL certificate can not be obtained until your domain is verified.")
diff --git a/app/views/projects/pages_domains/edit.html.haml b/app/views/projects/pages_domains/edit.html.haml
index 7c0777e5496..a08be65d7e4 100644
--- a/app/views/projects/pages_domains/edit.html.haml
+++ b/app/views/projects/pages_domains/edit.html.haml
@@ -1,12 +1,21 @@
- add_to_breadcrumbs _("Pages"), project_pages_path(@project)
- breadcrumb_title @domain.domain
- page_title @domain.domain
+
+- verification_enabled = Gitlab::CurrentSettings.pages_domain_verification_enabled?
+
+- if verification_enabled && @domain.unverified?
+ = content_for :flash_message do
+ .alert.alert-warning
+ .container-fluid.container-limited
+ = _("This domain is not verified. You will need to verify ownership before access is enabled.")
+
%h3.page-title
- = @domain.domain
+ = _('Pages Domain')
= render 'projects/pages_domains/helper_text'
-%hr.clearfix
%div
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
- .form-actions
+ .form-actions.d-flex.justify-content-between
= f.submit _('Save Changes'), class: "btn btn-success"
+ = link_to _('Cancel'), project_pages_path(@project), class: 'btn btn-default btn-inverse'
diff --git a/app/views/projects/pages_domains/new.html.haml b/app/views/projects/pages_domains/new.html.haml
index e23ccb5d4c6..3210bfe9231 100644
--- a/app/views/projects/pages_domains/new.html.haml
+++ b/app/views/projects/pages_domains/new.html.haml
@@ -3,7 +3,6 @@
%h3.page-title
= _("New Pages Domain")
= render 'projects/pages_domains/helper_text'
-%hr.clearfix
%div
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml
index 33837e21c8d..8eec3d51835 100644
--- a/app/views/projects/pages_domains/show.html.haml
+++ b/app/views/projects/pages_domains/show.html.haml
@@ -58,4 +58,4 @@
%td
= _("Certificate")
%td
- = render 'certificate'
+ = render 'lets_encrypt_callout', auto_ssl_available_and_enabled: false
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index a8aae03aad7..0fb23adc31f 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -14,7 +14,6 @@
= render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
-%hr
.form-group.row
= form.label :title, class: 'col-form-label col-sm-2'
diff --git a/app/views/shared/issuable/form/_branch_chooser.html.haml b/app/views/shared/issuable/form/_branch_chooser.html.haml
index 03eebe7c987..29ac17c43b9 100644
--- a/app/views/shared/issuable/form/_branch_chooser.html.haml
+++ b/app/views/shared/issuable/form/_branch_chooser.html.haml
@@ -20,3 +20,4 @@
= form.hidden_field(:target_branch,
{ class: 'target_branch js-target-branch-select ref-name mw-xl',
data: { placeholder: _('Select branch'), endpoint: refs_project_path(@project, sort: 'updated_desc', find: 'branches') }})
+%hr
diff --git a/changelogs/unreleased/30660-allow-to-enable-disable-auto-ssl-letsencrypt-support-via-api.yml b/changelogs/unreleased/30660-allow-to-enable-disable-auto-ssl-letsencrypt-support-via-api.yml
new file mode 100644
index 00000000000..7ec23301eeb
--- /dev/null
+++ b/changelogs/unreleased/30660-allow-to-enable-disable-auto-ssl-letsencrypt-support-via-api.yml
@@ -0,0 +1,5 @@
+---
+title: Require explicit null parameters to remove pages domain certificate and allow to use Let's Encrypt certificates through API
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/34426-use-new-list-task-icon-in-text-editor.yml b/changelogs/unreleased/34426-use-new-list-task-icon-in-text-editor.yml
new file mode 100644
index 00000000000..2b66bd02e93
--- /dev/null
+++ b/changelogs/unreleased/34426-use-new-list-task-icon-in-text-editor.yml
@@ -0,0 +1,6 @@
+---
+title: Replace task-done icon with list-task icon to better align with other toolbar
+ list icons
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/35547-add-documentation-for-sign-in-application-setting.yml b/changelogs/unreleased/35547-add-documentation-for-sign-in-application-setting.yml
new file mode 100644
index 00000000000..d13fa1d8fc5
--- /dev/null
+++ b/changelogs/unreleased/35547-add-documentation-for-sign-in-application-setting.yml
@@ -0,0 +1,5 @@
+---
+title: Add documentation for sign-in application setting
+merge_request: 19561
+author: Horatiu Eugen Vlad
+type: added
diff --git a/changelogs/unreleased/remove-domain-details.yml b/changelogs/unreleased/remove-domain-details.yml
new file mode 100644
index 00000000000..a9eedd580f6
--- /dev/null
+++ b/changelogs/unreleased/remove-domain-details.yml
@@ -0,0 +1,5 @@
+---
+title: Merge Details Page and Edit Page for Page Domains
+merge_request: 16687
+author:
+type: added
diff --git a/config/application.rb b/config/application.rb
index 1d32ebcaa54..cad5c8bbe76 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -255,8 +255,8 @@ module Gitlab
caching_config_hash[:compress] = false
caching_config_hash[:namespace] = Gitlab::Redis::Cache::CACHE_NAMESPACE
caching_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
- if Sidekiq.server? # threaded context
- caching_config_hash[:pool_size] = Sidekiq.options[:concurrency] + 5
+ if Sidekiq.server? || defined?(::Puma) # threaded context
+ caching_config_hash[:pool_size] = Gitlab::Redis::Cache.pool_size
caching_config_hash[:pool_timeout] = 1
end
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 1bcd8b68ac9..9c7a3f42c97 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -300,6 +300,11 @@ module.exports = {
to: path.join(ROOT_PATH, 'public/assets/webpack/cmaps/'),
},
{
+ from: path.join(ROOT_PATH, 'node_modules/@sourcegraph/code-host-integration/'),
+ to: path.join(ROOT_PATH, 'public/assets/webpack/sourcegraph/'),
+ ignore: ['package.json'],
+ },
+ {
from: path.join(
ROOT_PATH,
'node_modules/@gitlab/visual-review-tools/dist/visual_review_toolbar.js',
diff --git a/danger/commit_messages/Dangerfile b/danger/commit_messages/Dangerfile
index 064b8c94805..da5a63633c3 100644
--- a/danger/commit_messages/Dangerfile
+++ b/danger/commit_messages/Dangerfile
@@ -86,6 +86,12 @@ def unicode_emoji_regex
))x
end
+def count_filtered_commits(commits)
+ commits.count do |commit|
+ !commit.message.start_with?('fixup!', 'squash!')
+ end
+end
+
def lint_commit(commit) # rubocop:disable Metrics/AbcSize
# For now we'll ignore merge commits, as getting rid of those is a problem
# separate from enforcing good commit messages.
@@ -285,7 +291,7 @@ def lint_commits(commits)
end
end
-if git.commits.length > 10 && !ce_upstream?
+if count_filtered_commits(git.commits) > 10 && !ce_upstream?
warn(
'This merge request includes more than 10 commits. ' \
'Please rebase these commits into a smaller number of commits.'
diff --git a/db/migrate/20190827222124_add_sourcegraph_configuration_to_application_settings.rb b/db/migrate/20190827222124_add_sourcegraph_configuration_to_application_settings.rb
new file mode 100644
index 00000000000..e624642c2fc
--- /dev/null
+++ b/db/migrate/20190827222124_add_sourcegraph_configuration_to_application_settings.rb
@@ -0,0 +1,21 @@
+# 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 AddSourcegraphConfigurationToApplicationSettings < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def up
+ add_column(:application_settings, :sourcegraph_enabled, :boolean, default: false, null: false)
+ add_column(:application_settings, :sourcegraph_url, :string, null: true, limit: 255)
+ end
+
+ def down
+ remove_column(:application_settings, :sourcegraph_enabled)
+ remove_column(:application_settings, :sourcegraph_url)
+ end
+end
diff --git a/db/migrate/20191107173446_add_sourcegraph_admin_and_user_preferences.rb b/db/migrate/20191107173446_add_sourcegraph_admin_and_user_preferences.rb
new file mode 100644
index 00000000000..731ed82c999
--- /dev/null
+++ b/db/migrate/20191107173446_add_sourcegraph_admin_and_user_preferences.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddSourcegraphAdminAndUserPreferences < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ add_column(:application_settings, :sourcegraph_public_only, :boolean, default: true, null: false)
+ add_column(:user_preferences, :sourcegraph_enabled, :boolean)
+ end
+
+ def down
+ remove_column(:application_settings, :sourcegraph_public_only)
+ remove_column(:user_preferences, :sourcegraph_enabled)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index ebf1eb41499..f217253db5e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -352,6 +352,9 @@ ActiveRecord::Schema.define(version: 2019_11_14_173624) do
t.string "snowplow_app_id"
t.datetime_with_timezone "productivity_analytics_start_date"
t.string "default_ci_config_path", limit: 255
+ t.boolean "sourcegraph_enabled", default: false, null: false
+ t.string "sourcegraph_url", limit: 255
+ t.boolean "sourcegraph_public_only", default: true, null: false
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id"
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id"
t.index ["instance_administration_project_id"], name: "index_applicationsettings_on_instance_administration_project_id"
@@ -3771,6 +3774,7 @@ ActiveRecord::Schema.define(version: 2019_11_14_173624) do
t.boolean "time_format_in_24h"
t.string "projects_sort", limit: 64
t.boolean "show_whitespace_in_diffs", default: true, null: false
+ t.boolean "sourcegraph_enabled"
t.boolean "setup_for_company"
t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true
end
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 8980cd2d5bf..f51c375860b 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -120,7 +120,7 @@ The Pages daemon doesn't listen to the outside world.
1. Set the external URL for GitLab Pages in `/etc/gitlab/gitlab.rb`:
- ```shell
+ ```ruby
pages_external_url 'http://example.io'
```
@@ -145,7 +145,7 @@ outside world.
1. Place the certificate and key inside `/etc/gitlab/ssl`
1. In `/etc/gitlab/gitlab.rb` specify the following configuration:
- ```shell
+ ```ruby
pages_external_url 'https://example.io'
pages_nginx['redirect_http_to_https'] = true
@@ -167,7 +167,7 @@ behavior:
1. Edit `/etc/gitlab/gitlab.rb`.
1. Set the `inplace_chroot` to `true` for GitLab Pages:
- ```shell
+ ```ruby
gitlab_pages['inplace_chroot'] = true
```
@@ -202,7 +202,7 @@ world. Custom domains are supported, but no TLS.
1. Edit `/etc/gitlab/gitlab.rb`:
- ```shell
+ ```ruby
pages_external_url "http://example.io"
nginx['listen_addresses'] = ['192.0.2.1']
pages_nginx['enable'] = false
@@ -233,7 +233,7 @@ world. Custom domains and TLS are supported.
1. Edit `/etc/gitlab/gitlab.rb`:
- ```shell
+ ```ruby
pages_external_url "https://example.io"
nginx['listen_addresses'] = ['192.0.2.1']
pages_nginx['enable'] = false
@@ -332,7 +332,7 @@ Follow the steps below to configure verbose logging of GitLab Pages daemon.
If you wish to make it log events with level `DEBUG` you must configure this in
`/etc/gitlab/gitlab.rb`:
- ```shell
+ ```ruby
gitlab_pages['log_verbose'] = true
```
@@ -347,7 +347,7 @@ are stored.
If you wish to store them in another location you must set it up in
`/etc/gitlab/gitlab.rb`:
- ```shell
+ ```ruby
gitlab_rails['pages_path'] = "/mnt/storage/pages"
```
@@ -363,14 +363,14 @@ Omnibus GitLab 11.1.
If you wish to disable it you must configure this in
`/etc/gitlab/gitlab.rb`:
- ```shell
+ ```ruby
gitlab_pages['listen_proxy'] = nil
```
If you wish to make it listen on a different port you must configure this also in
`/etc/gitlab/gitlab.rb`:
- ```shell
+ ```ruby
gitlab_pages['listen_proxy'] = "localhost:10080"
```
@@ -382,21 +382,26 @@ The maximum size of the unpacked archive per project can be configured in the
Admin area under the Application settings in the **Maximum size of pages (MB)**.
The default is 100MB.
-## Running GitLab Pages in a separate server
+## Running GitLab Pages on a separate server
-You may want to run GitLab Pages daemon on a separate server in order to decrease the load on your main application server.
-Follow the steps below to configure GitLab Pages in a separate server.
+You can run the GitLab Pages daemon on a separate server in order to decrease the load on your main application server.
-1. Suppose you have the main GitLab application server named `app1`. Prepare
- new Linux server (let's call it `app2`), create NFS share there and configure access to
- this share from `app1`. Let's use the default GitLab Pages folder `/var/opt/gitlab/gitlab-rails/shared/pages`
- as the shared folder on `app2` and mount it to `/mnt/pages` on `app1`.
+To configure GitLab Pages on a separate server:
-1. On `app2` install GitLab omnibus and modify `/etc/gitlab/gitlab.rb` this way:
+1. Set up a new server. This will become the **Pages server**.
- ```shell
+1. Create an NFS share on the new server and configure this share to
+ allow access from your main **GitLab server**. For this example, we use the
+ default GitLab Pages folder `/var/opt/gitlab/gitlab-rails/shared/pages`
+ as the shared folder on the new server and we will mount it to `/mnt/pages`
+ on the **GitLab server**.
+
+1. On the **Pages server**, install Omnibus GitLab and modify `/etc/gitlab/gitlab.rb`
+ to include:
+
+ ```ruby
external_url 'http://<ip-address-of-the-server>'
- pages_external_url "http://<your-pages-domain>"
+ pages_external_url "http://<your-pages-server-URL>"
postgresql['enable'] = false
redis['enable'] = false
prometheus['enable'] = false
@@ -409,20 +414,82 @@ Follow the steps below to configure GitLab Pages in a separate server.
gitlab_rails['auto_migrate'] = false
```
-1. Run `sudo gitlab-ctl reconfigure`.
-1. On `app1` apply the following changes to `/etc/gitlab/gitlab.rb`:
+1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
- ```shell
+1. On the **GitLab server**, make the following changes to `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
gitlab_pages['enable'] = false
- pages_external_url "http://<your-pages-domain>"
+ pages_external_url "http://<your-pages-server-URL>"
gitlab_rails['pages_path'] = "/mnt/pages"
```
-1. Run `sudo gitlab-ctl reconfigure`.
+1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+
+It is possible to run GitLab Pages on multiple servers if you wish to distribute
+the load. You can do this through standard load balancing practices such as
+configuring your DNS server to return multiple IPs for your Pages server,
+configuring a load balancer to work at the IP level, and so on. If you wish to
+set up GitLab Pages on multiple servers, perform the above procedure for each
+Pages server.
+
+### Access control when running GitLab Pages on a separate server
+
+If you are [running GitLab Pages on a separate server](#running-gitlab-pages-on-a-separate-server),
+then you must use the following procedure to configure [access control](#access-control):
+
+1. On the **GitLab server**, add the following to `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_pages['enable'] = true
+ gitlab_pages['access_control'] = true
+ ```
+
+1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the
+ changes to take effect. The `gitlab-secrets.json` file is now updated with the
+ new configuration.
+
+ DANGER: **Danger:**
+ The `gitlab-secrets.json` file contains secrets that control database encryption.
+ Do not edit or replace this file on the **GitLab server** or you might
+ experience permanent data loss. Make a backup copy of this file before proceeding,
+ as explained in the following steps.
+
+1. Create a backup of the secrets file on the **GitLab server**:
+
+ ```shell
+ cp /etc/gitlab/gitlab-secrets.json /etc/gitlab/gitlab-secrets.json.bak
+ ```
+
+1. Create a backup of the secrets file on the **Pages server**:
+
+ ```shell
+ cp /etc/gitlab/gitlab-secrets.json /etc/gitlab/gitlab-secrets.json.bak
+ ```
+
+1. Disable Pages on the **GitLab server** by setting the following in
+ `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_pages['enable'] = false
+ ```
+
+1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+
+1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the **GitLab server**
+ to the **Pages server**.
+
+1. On your **Pages server**, add the following to `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_pages['gitlab_server'] = "https://<your-gitlab-server-URL>"
+ ```
+
+1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
## Backup
-Pages are part of the [regular backup][backup] so there is nothing to configure.
+GitLab Pages are part of the [regular backup][backup], so there is no separate backup to configure.
## Security
diff --git a/doc/api/pages_domains.md b/doc/api/pages_domains.md
index 9678203eb40..9d482781cde 100644
--- a/doc/api/pages_domains.md
+++ b/doc/api/pages_domains.md
@@ -22,6 +22,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/ap
"domain": "ssl.domain.example",
"url": "https://ssl.domain.example",
"project_id": 1337,
+ "auto_ssl_enabled": false,
"certificate": {
"expired": false,
"expiration": "2020-04-12T14:32:00.000Z"
@@ -55,6 +56,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/ap
{
"domain": "ssl.domain.example",
"url": "https://ssl.domain.example",
+ "auto_ssl_enabled": false,
"certificate": {
"subject": "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate",
"expired": false,
@@ -76,7 +78,7 @@ GET /projects/:id/pages/domains/:domain
| Attribute | Type | Required | Description |
| --------- | -------------- | -------- | ---------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `domain` | string | yes | The domain |
+| `domain` | string | yes | The custom domain indicated by the user |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/pages/domains/www.domain.example
@@ -97,6 +99,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/ap
{
"domain": "ssl.domain.example",
"url": "https://ssl.domain.example",
+ "auto_ssl_enabled": false,
"certificate": {
"subject": "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate",
"expired": false,
@@ -114,12 +117,13 @@ Creates a new pages domain. The user must have permissions to create new pages d
POST /projects/:id/pages/domains
```
-| Attribute | Type | Required | Description |
-| ------------- | -------------- | -------- | ---------------------------------------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `domain` | string | yes | The domain |
-| `certificate` | file/string | no | The certificate in PEM format with intermediates following in most specific to least specific order.|
-| `key` | file/string | no | The certificate key in PEM format. |
+| Attribute | Type | Required | Description |
+| -------------------| -------------- | -------- | ---------------------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `domain` | string | yes | The custom domain indicated by the user |
+| `auto_ssl_enabled` | boolean | no | Enables [automatic generation](../user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md) of SSL certificates issued by Let's Encrypt for custom domains. |
+| `certificate` | file/string | no | The certificate in PEM format with intermediates following in most specific to least specific order.|
+| `key` | file/string | no | The certificate key in PEM format. |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "domain=ssl.domain.example" --form "certificate=@/path/to/cert.pem" --form "key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains
@@ -129,10 +133,15 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "domain
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "domain=ssl.domain.example" --form "certificate=$CERT_PEM" --form "key=$KEY_PEM" https://gitlab.example.com/api/v4/projects/5/pages/domains
```
+```bash
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "domain=ssl.domain.example" --form "auto_ssl_enabled=true" https://gitlab.example.com/api/v4/projects/5/pages/domains
+```
+
```json
{
"domain": "ssl.domain.example",
"url": "https://ssl.domain.example",
+ "auto_ssl_enabled": true,
"certificate": {
"subject": "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate",
"expired": false,
@@ -150,12 +159,15 @@ Updates an existing project pages domain. The user must have permissions to chan
PUT /projects/:id/pages/domains/:domain
```
-| Attribute | Type | Required | Description |
-| ------------- | -------------- | -------- | ---------------------------------------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `domain` | string | yes | The domain |
-| `certificate` | file/string | no | The certificate in PEM format with intermediates following in most specific to least specific order.|
-| `key` | file/string | no | The certificate key in PEM format. |
+| Attribute | Type | Required | Description |
+| ------------------ | -------------- | -------- | ---------------------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `domain` | string | yes | The custom domain indicated by the user |
+| `auto_ssl_enabled` | boolean | no | Enables [automatic generation](../user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md) of SSL certificates issued by Let's Encrypt for custom domains. |
+| `certificate` | file/string | no | The certificate in PEM format with intermediates following in most specific to least specific order.|
+| `key` | file/string | no | The certificate key in PEM format. |
+
+### Adding certificate
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --form "certificate=@/path/to/cert.pem" --form "key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
@@ -169,6 +181,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --form "certifi
{
"domain": "ssl.domain.example",
"url": "https://ssl.domain.example",
+ "auto_ssl_enabled": false,
"certificate": {
"subject": "/O=Example, Inc./OU=Example Origin CA/CN=Example Origin Certificate",
"expired": false,
@@ -178,6 +191,36 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --form "certifi
}
```
+### Enabling Let's Encrypt integration for Pages custom domains
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --form "auto_ssl_enabled=true" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
+```
+
+```json
+{
+ "domain": "ssl.domain.example",
+ "url": "https://ssl.domain.example",
+ "auto_ssl_enabled": true
+}
+```
+
+### Removing certificate
+
+To remove the SSL certificate attached to the Pages domain, run:
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --form "certificate=" --form "key=" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
+```
+
+```json
+{
+ "domain": "ssl.domain.example",
+ "url": "https://ssl.domain.example",
+ "auto_ssl_enabled": false
+}
+```
+
## Delete pages domain
Deletes an existing project pages domain.
@@ -189,7 +232,7 @@ DELETE /projects/:id/pages/domains/:domain
| Attribute | Type | Required | Description |
| --------- | -------------- | -------- | ---------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `domain` | string | yes | The domain |
+| `domain` | string | yes | The custom domain indicated by the user |
```bash
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
diff --git a/doc/api/settings.md b/doc/api/settings.md
index f63466298e3..51d5e5f35d7 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -324,6 +324,9 @@ are listed in the descriptions of the relevant settings.
| `snowplow_enabled` | boolean | no | Enable snowplow tracking. |
| `snowplow_app_id` | string | no | The Snowplow site name / application id. (e.g. `gitlab`) |
| `snowplow_iglu_registry_url` | string | no | The Snowplow base Iglu Schema Registry URL to use for custom context and self describing events'|
+| `sourcegraph_enabled` | boolean | no | Enables Sourcegraph integration. Default is `false`. **If enabled, requires** `sourcegraph_url`. |
+| `sourcegraph_url` | string | required by: `sourcegraph_enabled` | The Sourcegraph instance URL for integration. |
+| `sourcegraph_public_only` | boolean | no | Blocks Sourcegraph from being loaded on private and internal projects. Defaul is `true`. |
| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to `0` for unlimited time. |
| `terms` | text | required by: `enforce_terms` | (**Required by:** `enforce_terms`) Markdown content for the ToS. |
| `throttle_authenticated_api_enabled` | boolean | no | (**If enabled, requires:** `throttle_authenticated_api_period_in_seconds` and `throttle_authenticated_api_requests_per_period`) Enable authenticated API request rate limit. Helps reduce request volume (e.g. from crawlers or abusive bots). |
diff --git a/doc/development/feature_flags/development.md b/doc/development/feature_flags/development.md
index 1ce9525d74e..c410c7eae41 100644
--- a/doc/development/feature_flags/development.md
+++ b/doc/development/feature_flags/development.md
@@ -61,7 +61,7 @@ you'd want to explicitly disable that flag until the frontend half is also ready
to be shipped. To make sure this feature is disabled for both GitLab.com and
self-managed instances you'd need to explicitly call `Feature.enabled?` method
before the `feature_available` method. This ensures the feature_flag is defaulting
-to `true`.
+to `false`.
## Feature groups
diff --git a/doc/integration/README.md b/doc/integration/README.md
index 7a2c9b9bc54..3f33aa94cb9 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -54,6 +54,7 @@ GitLab can be integrated with the following enhancements:
- Add GitLab actions to [Gmail actions buttons](gmail_action_buttons_for_gitlab.md).
- Configure [PlantUML](../administration/integration/plantuml.md) to use diagrams in AsciiDoc documents.
- Attach merge requests to [Trello](trello_power_up.md) cards.
+- Enable integrated code intelligence powered by [Sourcegraph](sourcegraph.md).
## Project services
diff --git a/doc/integration/img/sourcegraph_admin_v12_5.png b/doc/integration/img/sourcegraph_admin_v12_5.png
new file mode 100644
index 00000000000..23e38f56619
--- /dev/null
+++ b/doc/integration/img/sourcegraph_admin_v12_5.png
Binary files differ
diff --git a/doc/integration/img/sourcegraph_demo_v12_5.png b/doc/integration/img/sourcegraph_demo_v12_5.png
new file mode 100644
index 00000000000..c70448c0a8a
--- /dev/null
+++ b/doc/integration/img/sourcegraph_demo_v12_5.png
Binary files differ
diff --git a/doc/integration/img/sourcegraph_popover_v12_5.png b/doc/integration/img/sourcegraph_popover_v12_5.png
new file mode 100644
index 00000000000..878d6143646
--- /dev/null
+++ b/doc/integration/img/sourcegraph_popover_v12_5.png
Binary files differ
diff --git a/doc/integration/img/sourcegraph_user_preferences_v12_5.png b/doc/integration/img/sourcegraph_user_preferences_v12_5.png
new file mode 100644
index 00000000000..2c0e138e296
--- /dev/null
+++ b/doc/integration/img/sourcegraph_user_preferences_v12_5.png
Binary files differ
diff --git a/doc/integration/sourcegraph.md b/doc/integration/sourcegraph.md
new file mode 100644
index 00000000000..fc45b270d80
--- /dev/null
+++ b/doc/integration/sourcegraph.md
@@ -0,0 +1,119 @@
+---
+type: reference, how-to
+---
+
+# Sourcegraph integration
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/16556) in GitLab 12.5. Please note that this integration is [behind a feature flag](#enable-the-sourcegraph-feature-flag).
+
+[Sourcegraph](https://sourcegraph.com) provides code intelligence features, natively integrated into the GitLab UI.
+
+For GitLab.com users, see [Sourcegraph for GitLab.com](#sourcegraph-for-gitlabcom).
+
+![Sourcegraph demo](img/sourcegraph_demo_v12_5.png)
+
+NOTE: **Note:**
+This feature requires user opt-in. After Sourcegraph has been enabled for your GitLab instance,
+you can choose to enable Sourcegraph [through your user preferences](#enable-sourcegraph-in-user-preferences).
+
+## Set up for self-managed GitLab instances **(CORE ONLY)**
+
+Before you can enable Sourcegraph code intelligence in GitLab you will need to:
+
+- Enable the `sourcegraph` feature flag for your GitLab instance.
+- Configure a Sourcegraph instance with your GitLab instance as an external service.
+
+### Enable the Sourcegraph feature flag
+
+NOTE: **Note:**
+If you are running a self-managed instance, the Sourcegraph integration will not be available
+unless the feature flag `sourcegraph` is enabled. This can be done from the Rails console
+by instance administrators.
+
+Use these commands to start the Rails console:
+
+```sh
+# Omnibus GitLab
+gitlab-rails console
+
+# Installation from source
+cd /home/git/gitlab
+sudo -u git -H bin/rails console RAILS_ENV=production
+```
+
+Then run the following command to enable the feature flag:
+
+```
+Feature.enable(:sourcegraph)
+```
+
+You can also enable the feature flag only for specific projects with:
+
+```
+Feature.enable(:sourcegraph, Project.find_by_full_path('my_group/my_project'))
+```
+
+### Set up a self-managed Sourcegraph instance
+
+If you are new to Sourcegraph, head over to the [Sourcegraph installation documentation](https://docs.sourcegraph.com/admin) and get your instance up and running.
+
+### Connect your Sourcegraph instance to your GitLab instance
+
+1. Navigate to the site admin area in Sourcegraph.
+1. [Configure your GitLab external service](https://docs.sourcegraph.com/admin/external_service/gitlab).
+You can skip this step if you already have your GitLab repositories searchable in Sourcegraph.
+1. Validate that you can search your repositories from GitLab in your Sourcegraph instance by running a test query.
+1. Add your GitLab instance URL to the [`corsOrigin` setting](https://docs.sourcegraph.com/admin/config/site_config#corsOrigin) in your site configuration.
+
+### Configure your GitLab instance with Sourcegraph
+
+1. In GitLab, go to **Admin Area > Settings > Integrations**.
+1. Expand the **Sourcegraph** configuration section.
+1. Check **Enable Sourcegraph**.
+1. Set the Sourcegraph URL to your Sourcegraph instance, e.g., `https://sourcegraph.example.com`.
+
+![Sourcegraph admin settings](img/sourcegraph_admin_v12_5.png)
+
+## Enable Sourcegraph in user preferences
+
+If a GitLab administrator has enabled Sourcegraph, you can enable this feature in your user preferences.
+
+1. In GitLab, click your avatar in the top-right corner, then click **Settings**. On the left-hand nav, click **Preferences**.
+1. Under **Integrations**, find the **Sourcegraph** section.
+1. Check **Enable Sourcegraph**.
+
+![Sourcegraph user preferences](img/sourcegraph_user_preferences_v12_5.png)
+
+## Using Sourcegraph code intelligence
+
+Once enabled, participating projects will have a code intelligence popover available in
+the following code views:
+
+- Merge request diffs
+- Commit view
+- File view
+
+When visiting one of these views, you can now hover over a code reference to see a popover with:
+
+- Details on how this reference was defined.
+- **Go to definition**, which navigates to the line of code where this reference was defined.
+- **Find references**, which navigates to the configured Sourcegraph instance, showing a list of references to the hilighted code.
+
+![Sourcegraph demo](img/sourcegraph_popover_v12_5.png)
+
+## Sourcegraph for GitLab.com
+
+Sourcegraph powered code intelligence will be incrementally rolled out on GitLab.com. It will eventually be
+available for all public projects, but for now, it is only available for some specific [`gitlab-org` projects](https://gitlab.com/gitlab-org/).
+
+If you have a private or internal project and would like integrated code intelligence, please consider
+setting up a self-managed GitLab instance.
+
+## Sourcegraph and Privacy
+
+From Sourcegraph's [extension documentation](https://docs.sourcegraph.com/integration/browser_extension#privacy) which is the
+engine behind the native GitLab integration:
+
+> Sourcegraph integrations never send any logs, pings, usage statistics, or telemetry to Sourcegraph.com.
+> They will only connect to Sourcegraph.com as required to provide code intelligence or other functionality on public code.
+> As a result, no private code, private repository names, usernames, or any other specific data is sent to Sourcegraph.com.
diff --git a/doc/user/admin_area/settings/img/two_factor_grace_period.png b/doc/user/admin_area/settings/img/two_factor_grace_period.png
new file mode 100644
index 00000000000..e7fb52969aa
--- /dev/null
+++ b/doc/user/admin_area/settings/img/two_factor_grace_period.png
Binary files differ
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
index 4ca91ae5339..42f496bfbfa 100644
--- a/doc/user/admin_area/settings/index.md
+++ b/doc/user/admin_area/settings/index.md
@@ -14,6 +14,7 @@ include:
- [Continuous Integration and Deployment](continuous_integration.md)
- [Email](email.md)
- [Sign up restrictions](sign_up_restrictions.md)
+- [Sign in restrictions](sign_in_restrictions.md)
- [Terms](terms.md)
- [Third party offers](third_party_offers.md)
- [Usage statistics](usage_statistics.md)
diff --git a/doc/user/admin_area/settings/sign_in_restrictions.md b/doc/user/admin_area/settings/sign_in_restrictions.md
new file mode 100644
index 00000000000..0975766400f
--- /dev/null
+++ b/doc/user/admin_area/settings/sign_in_restrictions.md
@@ -0,0 +1,56 @@
+---
+type: reference
+---
+
+# Sign-in restrictions **(CORE ONLY)**
+
+You can use sign-in restrictions to limit the authentication with password
+for web interface and Git over HTTP(S), two-factor authentication enforcing, as well as
+as configuring the home page URL and after sign-out path.
+
+## Password authentication enabled
+
+You can restrict the password authentication for web interface and Git over HTTP(S):
+
+- **Web interface**: When this feature is disabled, an [external authentication provider](../../../administration/auth/README.md) must be used.
+- **Git over HTTP(S)**: When this feature is disabled, a [Personal Access Token](../../profile/personal_access_tokens.md) must be used to authenticate.
+
+## Two-factor authentication
+
+When this feature enabled, all users will have to use the [two-factor authentication](../../profile/account/two_factor_authentication.md).
+
+Once the two-factor authentication is configured as mandatory, the users will be allowed
+to skip forced configuration of two-factor authentication for the configurable grace
+period in hours.
+
+![Two-factor grace period](img/two_factor_grace_period.png)
+
+## Sign-in information
+
+All users that are not logged-in will be redirected to the page represented by the configured
+"Home page URL" if value is not empty.
+
+All users will be redirect to the page represented by the configured "After sign out path"
+after sign out if value is not empty.
+
+If a "Sign in text" in Markdown format is provided, then every user will be presented with
+this message after logging-in.
+
+## Settings
+
+To access this feature:
+
+1. Navigate to the **Settings > General** in the Admin area.
+1. Expand the **Sign-in restrictions** section.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index 9b43f6317d0..b299c74c8f4 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -128,6 +128,19 @@ You can choose one of the following options as the first day of the week:
If you select **System Default**, the system-wide default setting will be used.
+## Integrations
+
+Configure your preferences with third-party services which provide enhancements to your GitLab experience.
+
+### Sourcegraph
+
+NOTE: **Note:**
+This setting is only visible if Sourcegraph has been enabled by a GitLab administrator.
+
+Manage the availability of integrated code intelligence features powered by
+Sourcegraph. View [the Sourcegraph feature documentation](../../integration/sourcegraph.md#enable-sourcegraph-in-user-preferences)
+for more information.
+
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 5aa16b117fd..9617f1a8acf 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1681,6 +1681,7 @@ module API
expose :verified?, as: :verified
expose :verification_code, as: :verification_code
expose :enabled_until
+ expose :auto_ssl_enabled
expose :certificate,
as: :certificate_expiration,
@@ -1696,6 +1697,7 @@ module API
expose :verified?, as: :verified
expose :verification_code, as: :verification_code
expose :enabled_until
+ expose :auto_ssl_enabled
expose :certificate,
if: ->(pages_domain, _) { pages_domain.certificate? },
diff --git a/lib/api/pages_domains.rb b/lib/api/pages_domains.rb
index ec2fe8270b7..2d02a4e624c 100644
--- a/lib/api/pages_domains.rb
+++ b/lib/api/pages_domains.rb
@@ -92,8 +92,10 @@ module API
requires :domain, type: String, desc: 'The domain'
# rubocop:disable Scalability/FileUploads
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
- optional :certificate, allow_blank: false, types: [File, String], desc: 'The certificate', as: :user_provided_certificate
- optional :key, allow_blank: false, types: [File, String], desc: 'The key', as: :user_provided_key
+ optional :certificate, types: [File, String], desc: 'The certificate', as: :user_provided_certificate
+ optional :key, types: [File, String], desc: 'The key', as: :user_provided_key
+ optional :auto_ssl_enabled, allow_blank: false, type: Boolean, default: false,
+ desc: "Enables automatic generation of SSL certificates issued by Let's Encrypt for custom domains."
# rubocop:enable Scalability/FileUploads
all_or_none_of :user_provided_certificate, :user_provided_key
end
@@ -116,14 +118,16 @@ module API
requires :domain, type: String, desc: 'The domain'
# rubocop:disable Scalability/FileUploads
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
- optional :certificate, allow_blank: false, types: [File, String], desc: 'The certificate', as: :user_provided_certificate
- optional :key, allow_blank: false, types: [File, String], desc: 'The key', as: :user_provided_key
+ optional :certificate, types: [File, String], desc: 'The certificate', as: :user_provided_certificate
+ optional :key, types: [File, String], desc: 'The key', as: :user_provided_key
+ optional :auto_ssl_enabled, allow_blank: true, type: Boolean,
+ desc: "Enables automatic generation of SSL certificates issued by Let's Encrypt for custom domains."
# rubocop:enable Scalability/FileUploads
end
put ":id/pages/domains/:domain", requirements: PAGES_DOMAINS_ENDPOINT_REQUIREMENTS do
authorize! :update_pages, user_project
- pages_domain_params = declared(params, include_parent_namespaces: false)
+ pages_domain_params = declared(params, include_parent_namespaces: false, include_missing: false)
# Remove empty private key if certificate is not empty.
if pages_domain_params[:user_provided_certificate] && !pages_domain_params[:user_provided_key]
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 88076614f73..5362b3060c1 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -136,6 +136,11 @@ module API
optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application'
optional :signin_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5
optional :signup_enabled, type: Boolean, desc: 'Flag indicating if sign up is enabled'
+ optional :sourcegraph_enabled, type: Boolean, desc: 'Enable Sourcegraph'
+ optional :sourcegraph_public_only, type: Boolean, desc: 'Only allow public projects to communicate with Sourcegraph'
+ given sourcegraph_enabled: ->(val) { val } do
+ requires :sourcegraph_url, type: String, desc: 'The configured Sourcegraph instance URL'
+ end
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :instance_statistics_visibility_private, type: Boolean, desc: 'When set to `true` Instance statistics will only be available to admins'
diff --git a/lib/gitlab/redis/wrapper.rb b/lib/gitlab/redis/wrapper.rb
index fa1615a5953..412d00c6939 100644
--- a/lib/gitlab/redis/wrapper.rb
+++ b/lib/gitlab/redis/wrapper.rb
@@ -25,6 +25,8 @@ module Gitlab
if Sidekiq.server?
# the pool will be used in a multi-threaded context
size += Sidekiq.options[:concurrency]
+ elsif defined?(::Puma)
+ size += Puma.cli_config.options[:max_threads]
end
size
diff --git a/lib/gitlab/sourcegraph.rb b/lib/gitlab/sourcegraph.rb
new file mode 100644
index 00000000000..d0f12c8364a
--- /dev/null
+++ b/lib/gitlab/sourcegraph.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class Sourcegraph
+ class << self
+ def feature_conditional?
+ feature.conditional?
+ end
+
+ def feature_available?
+ # The sourcegraph_bundle feature could be conditionally applied, so check if `!off?`
+ !feature.off?
+ end
+
+ def feature_enabled?(thing = nil)
+ feature.enabled?(thing)
+ end
+
+ private
+
+ def feature
+ Feature.get(:sourcegraph)
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e98f06864ce..204c138b9d6 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8232,10 +8232,10 @@ msgstr ""
msgid "GitLabPages|Configure pages"
msgstr ""
-msgid "GitLabPages|Details"
+msgid "GitLabPages|Domains"
msgstr ""
-msgid "GitLabPages|Domains"
+msgid "GitLabPages|Edit"
msgstr ""
msgid "GitLabPages|Expired"
@@ -12541,6 +12541,9 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
+msgid "Preferences|Customize integrations with third party services."
+msgstr ""
+
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
@@ -12550,9 +12553,15 @@ msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
+msgid "Preferences|Enable integrated code intelligence on code views"
+msgstr ""
+
msgid "Preferences|For example: 30 mins ago."
msgstr ""
+msgid "Preferences|Integrations"
+msgstr ""
+
msgid "Preferences|Layout width"
msgstr ""
@@ -12565,6 +12574,9 @@ msgstr ""
msgid "Preferences|Show whitespace in diffs"
msgstr ""
+msgid "Preferences|Sourcegraph"
+msgstr ""
+
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -16172,6 +16184,51 @@ msgstr ""
msgid "Source project cannot be found."
msgstr ""
+msgid "Sourcegraph"
+msgstr ""
+
+msgid "SourcegraphAdmin|Block on private and internal projects"
+msgstr ""
+
+msgid "SourcegraphAdmin|Configure the URL to a Sourcegraph instance which can read your GitLab projects."
+msgstr ""
+
+msgid "SourcegraphAdmin|Enable Sourcegraph"
+msgstr ""
+
+msgid "SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance's code views and merge requests."
+msgstr ""
+
+msgid "SourcegraphAdmin|If checked, only public projects will have code intelligence and communicate with Sourcegraph."
+msgstr ""
+
+msgid "SourcegraphAdmin|More information"
+msgstr ""
+
+msgid "SourcegraphAdmin|Save changes"
+msgstr ""
+
+msgid "SourcegraphAdmin|Sourcegraph URL"
+msgstr ""
+
+msgid "SourcegraphAdmin|e.g. https://sourcegraph.example.com"
+msgstr ""
+
+msgid "SourcegraphPreferences|This feature is experimental and currently limited to certain projects."
+msgstr ""
+
+msgid "SourcegraphPreferences|This feature is experimental and limited to public projects."
+msgstr ""
+
+msgid "SourcegraphPreferences|This feature is experimental."
+msgstr ""
+
+msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgstr ""
+
+msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgstr ""
+
msgid "Spam Logs"
msgstr ""
diff --git a/package.json b/package.json
index 2a439be9514..1cf88523c97 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
"@gitlab/svgs": "^1.82.0",
"@gitlab/ui": "7.11.0",
"@gitlab/visual-review-tools": "1.2.0",
+ "@sourcegraph/code-host-integration": "^0.0.13",
"@sentry/browser": "^5.7.1",
"apollo-cache-inmemory": "^1.6.3",
"apollo-client": "^2.6.4",
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index 0842f7871ee..ed872783856 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -1,5 +1,4 @@
[[ "$TRACE" ]] && set -x
-export TILLER_NAMESPACE="$KUBE_NAMESPACE"
function deploy_exists() {
local namespace="${1}"
@@ -14,16 +13,18 @@ function deploy_exists() {
}
function previous_deploy_failed() {
- local deploy="${1}"
+ local namespace="${1}"
+ local deploy="${2}"
+
echoinfo "Checking for previous deployment of ${deploy}" true
- helm status "${deploy}" >/dev/null 2>&1
+ helm status --tiller-namespace "${namespace}" "${deploy}" >/dev/null 2>&1
local status=$?
# if `status` is `0`, deployment exists, has a status
if [ $status -eq 0 ]; then
echoinfo "Previous deployment found, checking status..."
- deployment_status=$(helm status "${deploy}" | grep ^STATUS | cut -d' ' -f2)
+ deployment_status=$(helm status --tiller-namespace "${namespace}" "${deploy}" | grep ^STATUS | cut -d' ' -f2)
echoinfo "Previous deployment state: ${deployment_status}"
if [[ "$deployment_status" == "FAILED" || "$deployment_status" == "PENDING_UPGRADE" || "$deployment_status" == "PENDING_INSTALL" ]]; then
status=0;
@@ -37,16 +38,17 @@ function previous_deploy_failed() {
}
function delete_release() {
- if [ -z "$CI_ENVIRONMENT_SLUG" ]; then
+ local namespace="${KUBE_NAMESPACE}"
+ local deploy="${CI_ENVIRONMENT_SLUG}"
+
+ if [ -z "$deploy" ]; then
echoerr "No release given, aborting the delete!"
return
fi
- local name="$CI_ENVIRONMENT_SLUG"
-
- echoinfo "Deleting release '$name'..." true
+ echoinfo "Deleting release '$deploy'..." true
- helm delete --purge "$name"
+ helm delete --purge --tiller-namespace "${namespace}" "${deploy}"
}
function delete_failed_release() {
@@ -59,7 +61,7 @@ function delete_failed_release() {
echoinfo "No Review App with ${CI_ENVIRONMENT_SLUG} is currently deployed."
else
# Cleanup and previous installs, as FAILED and PENDING_UPGRADE will cause errors with `upgrade`
- if previous_deploy_failed "$CI_ENVIRONMENT_SLUG" ; then
+ if previous_deploy_failed "${KUBE_NAMESPACE}" "$CI_ENVIRONMENT_SLUG" ; then
echoinfo "Review App deployment in bad state, cleaning up $CI_ENVIRONMENT_SLUG"
delete_release
else
@@ -117,6 +119,7 @@ function ensure_namespace() {
}
function install_tiller() {
+ local TILLER_NAMESPACE="$KUBE_NAMESPACE"
echoinfo "Checking deployment/tiller-deploy status in the ${TILLER_NAMESPACE} namespace..." true
echoinfo "Initiating the Helm client..."
@@ -131,11 +134,12 @@ function install_tiller() {
--override "spec.template.spec.tolerations[0].key"="dedicated" \
--override "spec.template.spec.tolerations[0].operator"="Equal" \
--override "spec.template.spec.tolerations[0].value"="helm" \
- --override "spec.template.spec.tolerations[0].effect"="NoSchedule"
+ --override "spec.template.spec.tolerations[0].effect"="NoSchedule" \
+ --tiller-namespace "${TILLER_NAMESPACE}"
kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy"
- if ! helm version --debug; then
+ if ! helm version --debug --tiller-namespace "${TILLER_NAMESPACE}"; then
echo "Failed to init Tiller."
return 1
fi
@@ -147,7 +151,7 @@ function install_external_dns() {
domain=$(echo "${REVIEW_APPS_DOMAIN}" | awk -F. '{printf "%s.%s", $(NF-1), $NF}')
echoinfo "Installing external DNS for domain ${domain}..." true
- if ! deploy_exists "${KUBE_NAMESPACE}" "${release_name}" || previous_deploy_failed "${release_name}" ; then
+ if ! deploy_exists "${KUBE_NAMESPACE}" "${release_name}" || previous_deploy_failed "${KUBE_NAMESPACE}" "${release_name}" ; then
echoinfo "Installing external-dns Helm chart"
helm repo update
# Default requested: CPU => 0, memory => 0
diff --git a/spec/controllers/concerns/sourcegraph_gon_spec.rb b/spec/controllers/concerns/sourcegraph_gon_spec.rb
new file mode 100644
index 00000000000..4fb7e37d148
--- /dev/null
+++ b/spec/controllers/concerns/sourcegraph_gon_spec.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe SourcegraphGon do
+ let_it_be(:enabled_user) { create(:user, sourcegraph_enabled: true) }
+ let_it_be(:disabled_user) { create(:user, sourcegraph_enabled: false) }
+ let_it_be(:public_project) { create(:project, :public) }
+ let_it_be(:internal_project) { create(:project, :internal) }
+
+ let(:sourcegraph_url) { 'http://sourcegraph.gitlab.com' }
+ let(:feature_enabled) { true }
+ let(:sourcegraph_enabled) { true }
+ let(:sourcegraph_public_only) { false }
+ let(:format) { :html }
+ let(:user) { enabled_user }
+ let(:project) { internal_project }
+
+ controller(ApplicationController) do
+ include SourcegraphGon # rubocop:disable RSpec/DescribedClass
+
+ def index
+ head :ok
+ end
+ end
+
+ before do
+ Feature.get(:sourcegraph).enable(feature_enabled)
+
+ stub_application_setting(sourcegraph_url: sourcegraph_url, sourcegraph_enabled: sourcegraph_enabled, sourcegraph_public_only: sourcegraph_public_only)
+
+ allow(controller).to receive(:project).and_return(project)
+
+ Gon.clear
+
+ sign_in user if user
+ end
+
+ after do
+ Feature.get(:sourcegraph).disable
+ end
+
+ subject do
+ get :index, format: format
+
+ Gon.sourcegraph
+ end
+
+ shared_examples 'enabled' do
+ it { is_expected.to eq({ url: sourcegraph_url }) }
+ end
+
+ shared_examples 'disabled' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'with feature enabled, application enabled, and user enabled' do
+ it_behaves_like 'enabled'
+ end
+
+ context 'with feature enabled for specific project' do
+ let(:feature_enabled) { project }
+
+ it_behaves_like 'enabled'
+ end
+
+ context 'with feature enabled for different project' do
+ let(:feature_enabled) { create(:project) }
+
+ it_behaves_like 'disabled'
+ end
+
+ context 'with feature disabled' do
+ let(:feature_enabled) { false }
+
+ it_behaves_like 'disabled'
+ end
+
+ context 'with admin settings disabled' do
+ let(:sourcegraph_enabled) { false }
+
+ it_behaves_like 'disabled'
+ end
+
+ context 'with public only' do
+ let(:sourcegraph_public_only) { true }
+
+ context 'with internal project' do
+ let(:project) { internal_project }
+
+ it_behaves_like 'disabled'
+ end
+
+ context 'with public project' do
+ let(:project) { public_project }
+
+ it_behaves_like 'enabled'
+ end
+ end
+
+ context 'with user disabled' do
+ let(:user) { disabled_user }
+
+ it_behaves_like 'disabled'
+ end
+
+ context 'with no user' do
+ let(:user) { nil }
+
+ it_behaves_like 'disabled'
+ end
+
+ context 'with non-html format' do
+ let(:format) { :json }
+
+ it_behaves_like 'disabled'
+ end
+end
diff --git a/spec/controllers/projects/pages_domains_controller_spec.rb b/spec/controllers/projects/pages_domains_controller_spec.rb
index 1db1fff02d8..3987bebb124 100644
--- a/spec/controllers/projects/pages_domains_controller_spec.rb
+++ b/spec/controllers/projects/pages_domains_controller_spec.rb
@@ -32,10 +32,10 @@ describe Projects::PagesDomainsController do
get(:show, params: request_params.merge(id: pages_domain.domain))
end
- it "displays the 'show' page" do
+ it "redirects to the 'edit' page" do
make_request
- expect(response).to have_gitlab_http_status(200)
- expect(response).to render_template('show')
+
+ expect(response).to redirect_to(edit_project_pages_domain_path(project, pages_domain.domain))
end
context 'when user is developer' do
@@ -69,7 +69,7 @@ describe Projects::PagesDomainsController do
created_domain = PagesDomain.reorder(:id).last
expect(created_domain).to be_present
- expect(response).to redirect_to(project_pages_domain_path(project, created_domain))
+ expect(response).to redirect_to(edit_project_pages_domain_path(project, created_domain))
end
end
@@ -160,7 +160,7 @@ describe Projects::PagesDomainsController do
post :verify, params: params
- expect(response).to redirect_to project_pages_domain_path(project, pages_domain)
+ expect(response).to redirect_to edit_project_pages_domain_path(project, pages_domain)
expect(flash[:notice]).to eq('Successfully verified domain ownership')
end
@@ -169,7 +169,7 @@ describe Projects::PagesDomainsController do
post :verify, params: params
- expect(response).to redirect_to project_pages_domain_path(project, pages_domain)
+ expect(response).to redirect_to edit_project_pages_domain_path(project, pages_domain)
expect(flash[:alert]).to eq('Failed to verify domain ownership')
end
diff --git a/spec/features/projects/pages_lets_encrypt_spec.rb b/spec/features/projects/pages_lets_encrypt_spec.rb
index c33a2896952..19c61487e16 100644
--- a/spec/features/projects/pages_lets_encrypt_spec.rb
+++ b/spec/features/projects/pages_lets_encrypt_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe "Pages with Let's Encrypt", :https_pages_enabled do
include LetsEncryptHelpers
- let(:project) { create(:project) }
+ let(:project) { create(:project, pages_https_only: false) }
let(:user) { create(:user) }
let(:role) { :maintainer }
let(:certificate_pem) { attributes_for(:pages_domain)[:certificate] }
@@ -34,14 +34,14 @@ describe "Pages with Let's Encrypt", :https_pages_enabled do
expect(domain.auto_ssl_enabled).to eq false
expect(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'false'
- expect(page).to have_field 'Certificate (PEM)', type: 'textarea'
- expect(page).to have_field 'Key (PEM)', type: 'textarea'
+ expect(page).to have_selector '.card-header', text: 'Certificate'
+ expect(page).to have_text domain.subject
find('.js-auto-ssl-toggle-container .project-feature-toggle').click
expect(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'true'
- expect(page).not_to have_field 'Certificate (PEM)', type: 'textarea'
- expect(page).not_to have_field 'Key (PEM)', type: 'textarea'
+ expect(page).not_to have_selector '.card-header', text: 'Certificate'
+ expect(page).not_to have_text domain.subject
click_on 'Save Changes'
@@ -67,9 +67,6 @@ describe "Pages with Let's Encrypt", :https_pages_enabled do
expect(page).to have_field 'Certificate (PEM)', type: 'textarea'
expect(page).to have_field 'Key (PEM)', type: 'textarea'
- fill_in 'Certificate (PEM)', with: certificate_pem
- fill_in 'Key (PEM)', with: certificate_key
-
click_on 'Save Changes'
expect(domain.reload.auto_ssl_enabled).to eq false
@@ -81,7 +78,8 @@ describe "Pages with Let's Encrypt", :https_pages_enabled do
it 'user do not see private key' do
visit edit_project_pages_domain_path(project, domain)
- expect(find_field('Key (PEM)', visible: :all, disabled: :all).value).to be_blank
+ expect(page).not_to have_selector '.card-header', text: 'Certificate'
+ expect(page).not_to have_text domain.subject
end
end
@@ -100,10 +98,21 @@ describe "Pages with Let's Encrypt", :https_pages_enabled do
context 'when certificate is provided by user' do
let(:domain) { create(:pages_domain, project: project) }
- it 'user sees private key' do
+ it 'user sees certificate subject' do
+ visit edit_project_pages_domain_path(project, domain)
+
+ expect(page).to have_selector '.card-header', text: 'Certificate'
+ expect(page).to have_text domain.subject
+ end
+
+ it 'user can delete the certificate', :js do
visit edit_project_pages_domain_path(project, domain)
- expect(find_field('Key (PEM)').value).not_to be_blank
+ expect(page).to have_selector '.card-header', text: 'Certificate'
+ expect(page).to have_text domain.subject
+ within('.card') { accept_confirm { click_on 'Remove' } }
+ expect(page).to have_field 'Certificate (PEM)', with: ''
+ expect(page).to have_field 'Key (PEM)', with: ''
end
end
end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 17a2d30e784..eab0aefbe84 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
shared_examples 'pages settings editing' do
- let(:project) { create(:project) }
+ let_it_be(:project) { create(:project, pages_https_only: false) }
let(:user) { create(:user) }
let(:role) { :maintainer }
@@ -185,6 +185,21 @@ shared_examples 'pages settings editing' do
expect(page).to have_content('my.test.domain.com')
end
+ describe 'with dns verification enabled' do
+ before do
+ stub_application_setting(pages_domain_verification_enabled: true)
+ end
+
+ it 'shows the DNS verification record' do
+ domain = create(:pages_domain, project: project)
+
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+ expect(page).to have_field :domain_verification, with: "#{domain.verification_domain} TXT #{domain.keyed_verification_code}"
+ end
+ end
+
describe 'updating the certificate for an existing domain' do
let!(:domain) do
create(:pages_domain, project: project)
@@ -193,19 +208,22 @@ shared_examples 'pages settings editing' do
it 'allows the certificate to be updated' do
visit project_pages_path(project)
- within('#content-body') { click_link 'Details' }
- click_link 'Edit'
+ within('#content-body') { click_link 'Edit' }
click_button 'Save Changes'
expect(page).to have_content('Domain was updated')
end
context 'when the certificate is invalid' do
+ let_it_be(:domain) do
+ create(:pages_domain, :without_certificate, :without_key, project: project)
+ end
+
it 'tells the user what the problem is' do
visit project_pages_path(project)
- within('#content-body') { click_link 'Details' }
- click_link 'Edit'
+ within('#content-body') { click_link 'Edit' }
+
fill_in 'Certificate (PEM)', with: 'invalid data'
click_button 'Save Changes'
@@ -214,6 +232,27 @@ shared_examples 'pages settings editing' do
expect(page).to have_content("Key doesn't match the certificate")
end
end
+
+ it 'allows the certificate to be removed', :js do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+
+ accept_confirm { click_link 'Remove' }
+
+ expect(page).to have_field('Certificate (PEM)', with: '')
+ expect(page).to have_field('Key (PEM)', with: '')
+ domain.reload
+ expect(domain.certificate).to be_nil
+ expect(domain.key).to be_nil
+ end
+
+ it 'shows the DNS CNAME record' do
+ visit project_pages_path(project)
+
+ within('#content-body') { click_link 'Edit' }
+ expect(page).to have_field :domain_dns, with: "#{domain.domain} CNAME #{domain.project.pages_subdomain}.#{Settings.pages.host}."
+ end
end
end
end
@@ -250,7 +289,7 @@ shared_examples 'pages settings editing' do
end
end
- describe 'HTTPS settings', :js, :https_pages_enabled do
+ describe 'HTTPS settings', :https_pages_enabled do
before do
project.namespace.update(owner: user)
@@ -358,18 +397,21 @@ shared_examples 'pages settings editing' do
expect(page).to have_link('Remove pages')
- click_link 'Remove pages'
+ accept_confirm { click_link 'Remove pages' }
- expect(project.pages_deployed?).to be_falsey
+ expect(page).to have_content('Pages were removed')
+ expect(project.reload.pages_deployed?).to be_falsey
end
end
end
end
-describe 'Pages' do
+describe 'Pages', :js do
include LetsEncryptHelpers
- include_examples 'pages settings editing'
+ context 'when editing normally' do
+ include_examples 'pages settings editing'
+ end
context 'when letsencrypt support is enabled' do
before do
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
index ed8ed9085c0..721b8d4641f 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
@@ -7,6 +7,7 @@
"verified": { "type": "boolean" },
"verification_code": { "type": ["string", "null"] },
"enabled_until": { "type": ["date", "null"] },
+ "auto_ssl_enabled": { "type": "boolean" },
"certificate_expiration": {
"type": "object",
"properties": {
@@ -17,6 +18,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url", "project_id", "verified", "verification_code", "enabled_until"],
+ "required": ["domain", "url", "project_id", "verified", "verification_code", "enabled_until", "auto_ssl_enabled"],
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
index b57d544f896..3dd80a6f11b 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
@@ -6,6 +6,7 @@
"verified": { "type": "boolean" },
"verification_code": { "type": ["string", "null"] },
"enabled_until": { "type": ["date", "null"] },
+ "auto_ssl_enabled": { "type": "boolean" },
"certificate": {
"type": "object",
"properties": {
@@ -18,6 +19,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url", "verified", "verification_code", "enabled_until"],
+ "required": ["domain", "url", "verified", "verification_code", "enabled_until", "auto_ssl_enabled"],
"additionalProperties": false
}
diff --git a/spec/frontend/repository/log_tree_spec.js b/spec/frontend/repository/log_tree_spec.js
index 9eee89cd07b..ad42f8b2ffc 100644
--- a/spec/frontend/repository/log_tree_spec.js
+++ b/spec/frontend/repository/log_tree_spec.js
@@ -1,6 +1,6 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import { normalizeData, resolveCommit, fetchLogsTree } from '~/repository/log_tree';
+import { resolveCommit, fetchLogsTree } from '~/repository/log_tree';
const mockData = [
{
@@ -15,22 +15,6 @@ const mockData = [
},
];
-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 = {
diff --git a/spec/frontend/repository/utils/commit_spec.js b/spec/frontend/repository/utils/commit_spec.js
new file mode 100644
index 00000000000..2d75358106c
--- /dev/null
+++ b/spec/frontend/repository/utils/commit_spec.js
@@ -0,0 +1,30 @@
+import { normalizeData } from '~/repository/utils/commit';
+
+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',
+ },
+ ]);
+ });
+});
diff --git a/spec/helpers/sourcegraph_helper_spec.rb b/spec/helpers/sourcegraph_helper_spec.rb
new file mode 100644
index 00000000000..830bbb3129f
--- /dev/null
+++ b/spec/helpers/sourcegraph_helper_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe SourcegraphHelper do
+ describe '#sourcegraph_url_message' do
+ let(:sourcegraph_url) { 'http://sourcegraph.example.com' }
+
+ before do
+ allow(Gitlab::CurrentSettings).to receive(:sourcegraph_url).and_return(sourcegraph_url)
+ allow(Gitlab::CurrentSettings).to receive(:sourcegraph_url_is_com?).and_return(is_com)
+ end
+
+ subject { helper.sourcegraph_url_message }
+
+ context 'with .com sourcegraph url' do
+ let(:is_com) { true }
+
+ it { is_expected.to have_text('Uses Sourcegraph.com') }
+ it { is_expected.to have_link('Sourcegraph.com', href: sourcegraph_url) }
+ end
+
+ context 'with custom sourcegraph url' do
+ let(:is_com) { false }
+
+ it { is_expected.to have_text('Uses a custom Sourcegraph instance') }
+ it { is_expected.to have_link('Sourcegraph instance', href: sourcegraph_url) }
+
+ context 'with unsafe url' do
+ let(:sourcegraph_url) { '\" onload=\"alert(1);\"' }
+
+ it { is_expected.to have_link('Sourcegraph instance', href: sourcegraph_url) }
+ end
+ end
+ end
+
+ context '#sourcegraph_experimental_message' do
+ let(:feature_conditional) { false }
+ let(:public_only) { false }
+
+ before do
+ allow(Gitlab::CurrentSettings).to receive(:sourcegraph_public_only).and_return(public_only)
+ allow(Gitlab::Sourcegraph).to receive(:feature_conditional?).and_return(feature_conditional)
+ end
+
+ subject { helper.sourcegraph_experimental_message }
+
+ context 'when not limited by feature or public only' do
+ it { is_expected.to eq "This feature is experimental." }
+ end
+
+ context 'when limited by feature' do
+ let(:feature_conditional) { true }
+
+ it { is_expected.to eq "This feature is experimental and currently limited to certain projects." }
+ end
+
+ context 'when limited by public only' do
+ let(:public_only) { true }
+
+ it { is_expected.to eq "This feature is experimental and limited to public projects." }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sourcegraph_spec.rb b/spec/lib/gitlab/sourcegraph_spec.rb
new file mode 100644
index 00000000000..e081ae32175
--- /dev/null
+++ b/spec/lib/gitlab/sourcegraph_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Sourcegraph do
+ let_it_be(:user) { create(:user) }
+ let(:feature_scope) { true }
+
+ before do
+ Feature.enable(:sourcegraph, feature_scope)
+ end
+
+ describe '.feature_conditional?' do
+ subject { described_class.feature_conditional? }
+
+ context 'when feature is enabled globally' do
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when feature is enabled only to a resource' do
+ let(:feature_scope) { user }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe '.feature_available?' do
+ subject { described_class.feature_available? }
+
+ context 'when feature is enabled globally' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when feature is enabled only to a resource' do
+ let(:feature_scope) { user }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe '.feature_enabled?' do
+ let(:current_user) { nil }
+
+ subject { described_class.feature_enabled?(current_user) }
+
+ context 'when feature is enabled globally' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when feature is enabled only to a resource' do
+ let(:feature_scope) { user }
+
+ context 'for the same resource' do
+ let(:current_user) { user }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'for a different resource' do
+ let(:current_user) { create(:user) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 4aa8f2d959d..ba3b99f4421 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
describe ApplicationSetting do
+ using RSpec::Parameterized::TableSyntax
+
subject(:setting) { described_class.create_from_defaults }
it { include(CacheableAttributes) }
@@ -495,6 +497,15 @@ describe ApplicationSetting do
it { is_expected.not_to allow_value(nil).for(:static_objects_external_storage_auth_token) }
end
end
+
+ context 'sourcegraph settings' do
+ it 'is invalid if sourcegraph is enabled and no url is provided' do
+ allow(subject).to receive(:sourcegraph_enabled).and_return(true)
+
+ expect(subject.sourcegraph_url).to be_nil
+ is_expected.to be_invalid
+ end
+ end
end
context 'restrict creating duplicates' do
@@ -583,5 +594,24 @@ describe ApplicationSetting do
end
end
+ describe '#sourcegraph_url_is_com?' do
+ where(:url, :is_com) do
+ 'https://sourcegraph.com' | true
+ 'https://sourcegraph.com/' | true
+ 'https://www.sourcegraph.com' | true
+ 'shttps://www.sourcegraph.com' | false
+ 'https://sourcegraph.example.com/' | false
+ 'https://sourcegraph.org/' | false
+ end
+
+ with_them do
+ it 'matches the url with sourcegraph.com' do
+ setting.sourcegraph_url = url
+
+ expect(setting.sourcegraph_url_is_com?).to eq(is_com)
+ end
+ end
+ end
+
it_behaves_like 'application settings examples'
end
diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb
index ee3ee9c4d23..6b774e9335e 100644
--- a/spec/requests/api/pages_domains_spec.rb
+++ b/spec/requests/api/pages_domains_spec.rb
@@ -3,15 +3,20 @@
require 'spec_helper'
describe API::PagesDomains do
- set(:project) { create(:project, path: 'my.project', pages_https_only: false) }
- set(:user) { create(:user) }
- set(:admin) { create(:admin) }
+ let_it_be(:project) { create(:project, path: 'my.project', pages_https_only: false) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:admin) { create(:admin) }
- set(:pages_domain) { create(:pages_domain, :without_key, :without_certificate, domain: 'www.domain.test', project: project) }
- set(:pages_domain_secure) { create(:pages_domain, domain: 'ssl.domain.test', project: project) }
- set(:pages_domain_expired) { create(:pages_domain, :with_expired_certificate, domain: 'expired.domain.test', project: project) }
+ let_it_be(:pages_domain) { create(:pages_domain, :without_key, :without_certificate, domain: 'www.domain.test', project: project) }
+ let_it_be(:pages_domain_secure) { create(:pages_domain, domain: 'ssl.domain.test', project: project) }
+ let_it_be(:pages_domain_with_letsencrypt) { create(:pages_domain, :letsencrypt, domain: 'letsencrypt.domain.test', project: project) }
+ let_it_be(:pages_domain_expired) { create(:pages_domain, :with_expired_certificate, domain: 'expired.domain.test', project: project) }
let(:pages_domain_params) { build(:pages_domain, :without_key, :without_certificate, domain: 'www.other-domain.test').slice(:domain) }
+ let(:pages_domain_with_letsencrypt_params) do
+ build(:pages_domain, :without_key, :without_certificate, domain: 'www.other-domain.test', auto_ssl_enabled: true)
+ .slice(:domain, :auto_ssl_enabled)
+ end
let(:pages_domain_secure_params) { build(:pages_domain, domain: 'ssl.other-domain.test', project: project).slice(:domain, :certificate, :key) }
let(:pages_domain_secure_key_missmatch_params) {build(:pages_domain, :with_trusted_chain, project: project).slice(:domain, :certificate, :key) }
let(:pages_domain_secure_missing_chain_params) {build(:pages_domain, :with_missing_chain, project: project).slice(:certificate) }
@@ -22,6 +27,7 @@ describe API::PagesDomains do
let(:route_secure_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_secure.domain}" }
let(:route_expired_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_expired.domain}" }
let(:route_vacant_domain) { "/projects/#{project.id}/pages/domains/www.vacant-domain.test" }
+ let(:route_letsencrypt_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_with_letsencrypt.domain}" }
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
@@ -47,9 +53,10 @@ describe API::PagesDomains do
expect(response).to match_response_schema('public_api/v4/pages_domain_basics')
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.size).to eq(3)
+ expect(json_response.size).to eq(4)
expect(json_response.last).to have_key('domain')
expect(json_response.last).to have_key('project_id')
+ expect(json_response.last).to have_key('auto_ssl_enabled')
expect(json_response.last).to have_key('certificate_expiration')
expect(json_response.last['certificate_expiration']['expired']).to be true
expect(json_response.first).not_to have_key('certificate_expiration')
@@ -73,7 +80,7 @@ describe API::PagesDomains do
expect(response).to match_response_schema('public_api/v4/pages_domains')
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.size).to eq(3)
+ expect(json_response.size).to eq(4)
expect(json_response.map { |pages_domain| pages_domain['domain'] }).to include(pages_domain.domain)
expect(json_response.last).to have_key('domain')
end
@@ -166,6 +173,7 @@ describe API::PagesDomains do
expect(json_response['url']).to eq(pages_domain_secure.url)
expect(json_response['certificate']['subject']).to eq(pages_domain_secure.subject)
expect(json_response['certificate']['expired']).to be false
+ expect(json_response['auto_ssl_enabled']).to be false
end
it 'returns pages domain with an expired certificate' do
@@ -175,6 +183,18 @@ describe API::PagesDomains do
expect(response).to match_response_schema('public_api/v4/pages_domain/detail')
expect(json_response['certificate']['expired']).to be true
end
+
+ it 'returns pages domain with letsencrypt' do
+ get api(route_letsencrypt_domain, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/pages_domain/detail')
+ expect(json_response['domain']).to eq(pages_domain_with_letsencrypt.domain)
+ expect(json_response['url']).to eq(pages_domain_with_letsencrypt.url)
+ expect(json_response['certificate']['subject']).to eq(pages_domain_with_letsencrypt.subject)
+ expect(json_response['certificate']['expired']).to be false
+ expect(json_response['auto_ssl_enabled']).to be true
+ end
end
context 'when domain is vacant' do
@@ -246,6 +266,7 @@ describe API::PagesDomains do
expect(pages_domain.domain).to eq(params[:domain])
expect(pages_domain.certificate).to be_nil
expect(pages_domain.key).to be_nil
+ expect(pages_domain.auto_ssl_enabled).to be false
end
it 'creates a new secure pages domain' do
@@ -257,6 +278,29 @@ describe API::PagesDomains do
expect(pages_domain.domain).to eq(params_secure[:domain])
expect(pages_domain.certificate).to eq(params_secure[:certificate])
expect(pages_domain.key).to eq(params_secure[:key])
+ expect(pages_domain.auto_ssl_enabled).to be false
+ end
+
+ it 'creates domain with letsencrypt enabled' do
+ post api(route, user), params: pages_domain_with_letsencrypt_params
+ pages_domain = PagesDomain.find_by(domain: json_response['domain'])
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/pages_domain/detail')
+ expect(pages_domain.domain).to eq(pages_domain_with_letsencrypt_params[:domain])
+ expect(pages_domain.auto_ssl_enabled).to be true
+ end
+
+ it 'creates domain with letsencrypt enabled and provided certificate' do
+ post api(route, user), params: params_secure.merge(auto_ssl_enabled: true)
+ pages_domain = PagesDomain.find_by(domain: json_response['domain'])
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(response).to match_response_schema('public_api/v4/pages_domain/detail')
+ expect(pages_domain.domain).to eq(params_secure[:domain])
+ expect(pages_domain.certificate).to eq(params_secure[:certificate])
+ expect(pages_domain.key).to eq(params_secure[:key])
+ expect(pages_domain.auto_ssl_enabled).to be true
end
it 'fails to create pages domain without key' do
@@ -323,13 +367,14 @@ describe API::PagesDomains do
shared_examples_for 'put pages domain' do
it 'updates pages domain removing certificate' do
- put api(route_secure_domain, user)
+ put api(route_secure_domain, user), params: { certificate: nil, key: nil }
pages_domain_secure.reload
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/pages_domain/detail')
expect(pages_domain_secure.certificate).to be_nil
expect(pages_domain_secure.key).to be_nil
+ expect(pages_domain_secure.auto_ssl_enabled).to be false
end
it 'updates pages domain adding certificate' do
@@ -342,6 +387,37 @@ describe API::PagesDomains do
expect(pages_domain.key).to eq(params_secure[:key])
end
+ it 'updates pages domain adding certificate with letsencrypt' do
+ put api(route_domain, user), params: params_secure.merge(auto_ssl_enabled: true)
+ pages_domain.reload
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/pages_domain/detail')
+ expect(pages_domain.certificate).to eq(params_secure[:certificate])
+ expect(pages_domain.key).to eq(params_secure[:key])
+ expect(pages_domain.auto_ssl_enabled).to be true
+ end
+
+ it 'updates pages domain enabling letsencrypt' do
+ put api(route_domain, user), params: { auto_ssl_enabled: true }
+ pages_domain.reload
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/pages_domain/detail')
+ expect(pages_domain.auto_ssl_enabled).to be true
+ end
+
+ it 'updates pages domain disabling letsencrypt while preserving the certificate' do
+ put api(route_letsencrypt_domain, user), params: { auto_ssl_enabled: false }
+ pages_domain_with_letsencrypt.reload
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/pages_domain/detail')
+ expect(pages_domain_with_letsencrypt.auto_ssl_enabled).to be false
+ expect(pages_domain_with_letsencrypt.key).to be
+ expect(pages_domain_with_letsencrypt.certificate).to be
+ end
+
it 'updates pages domain with expired certificate' do
put api(route_expired_domain, user), params: params_secure
pages_domain_expired.reload
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 3190de03d1a..b7586307929 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -19,6 +19,9 @@ describe API::Settings, 'Settings' do
expect(json_response['plantuml_enabled']).to be_falsey
expect(json_response['plantuml_url']).to be_nil
expect(json_response['default_ci_config_path']).to be_nil
+ expect(json_response['sourcegraph_enabled']).to be_falsey
+ expect(json_response['sourcegraph_url']).to be_nil
+ expect(json_response['sourcegraph_public_only']).to be_truthy
expect(json_response['default_project_visibility']).to be_a String
expect(json_response['default_snippet_visibility']).to be_a String
expect(json_response['default_group_visibility']).to be_a String
@@ -45,6 +48,7 @@ describe API::Settings, 'Settings' do
storages = Gitlab.config.repositories.storages
.merge({ 'custom' => 'tmp/tests/custom_repositories' })
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
+ Feature.get(:sourcegraph).enable
end
it "updates application settings" do
@@ -57,6 +61,9 @@ describe API::Settings, 'Settings' do
repository_storages: ['custom'],
plantuml_enabled: true,
plantuml_url: 'http://plantuml.example.com',
+ sourcegraph_enabled: true,
+ sourcegraph_url: 'https://sourcegraph.com',
+ sourcegraph_public_only: false,
default_snippet_visibility: 'internal',
restricted_visibility_levels: ['public'],
default_artifacts_expire_in: '2 days',
@@ -89,6 +96,9 @@ describe API::Settings, 'Settings' do
expect(json_response['repository_storages']).to eq(['custom'])
expect(json_response['plantuml_enabled']).to be_truthy
expect(json_response['plantuml_url']).to eq('http://plantuml.example.com')
+ expect(json_response['sourcegraph_enabled']).to be_truthy
+ expect(json_response['sourcegraph_url']).to eq('https://sourcegraph.com')
+ expect(json_response['sourcegraph_public_only']).to eq(false)
expect(json_response['default_snippet_visibility']).to eq('internal')
expect(json_response['restricted_visibility_levels']).to eq(['public'])
expect(json_response['default_artifacts_expire_in']).to eq('2 days')
@@ -355,5 +365,14 @@ describe API::Settings, 'Settings' do
expect(json_response['domain_blacklist']).to eq(['domain3.com', '*.domain4.com'])
end
end
+
+ context "missing sourcegraph_url value when sourcegraph_enabled is true" do
+ it "returns a blank parameter error message" do
+ put api("/application/settings", admin), params: { sourcegraph_enabled: true }
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['error']).to eq('sourcegraph_url is missing')
+ end
+ end
end
end
diff --git a/spec/views/admin/application_settings/integrations.html.haml_spec.rb b/spec/views/admin/application_settings/integrations.html.haml_spec.rb
new file mode 100644
index 00000000000..392d43ef2d4
--- /dev/null
+++ b/spec/views/admin/application_settings/integrations.html.haml_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'admin/application_settings/integrations.html.haml' do
+ let(:app_settings) { build(:application_setting) }
+
+ describe 'sourcegraph integration' do
+ let(:sourcegraph_flag) { true }
+
+ before do
+ assign(:application_setting, app_settings)
+ allow(Gitlab::Sourcegraph).to receive(:feature_available?).and_return(sourcegraph_flag)
+ end
+
+ context 'when sourcegraph feature is enabled' do
+ it 'show the form' do
+ render
+
+ expect(rendered).to have_field('application_setting_sourcegraph_enabled')
+ end
+ end
+
+ context 'when sourcegraph feature is disabled' do
+ let(:sourcegraph_flag) { false }
+
+ it 'show the form' do
+ render
+
+ expect(rendered).not_to have_field('application_setting_sourcegraph_enabled')
+ end
+ end
+ end
+end
diff --git a/spec/views/profiles/preferences/show.html.haml_spec.rb b/spec/views/profiles/preferences/show.html.haml_spec.rb
new file mode 100644
index 00000000000..52933c42621
--- /dev/null
+++ b/spec/views/profiles/preferences/show.html.haml_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'profiles/preferences/show' do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:user) { build(:user) }
+
+ before do
+ assign(:user, user)
+ allow(controller).to receive(:current_user).and_return(user)
+ end
+
+ context 'sourcegraph' do
+ def have_sourcegraph_field(*args)
+ have_field('user_sourcegraph_enabled', *args)
+ end
+
+ def have_integrations_section
+ have_css('.profile-settings-sidebar', { text: 'Integrations' })
+ end
+
+ before do
+ # Can't use stub_feature_flags because we use Feature.get to check if conditinally applied
+ Feature.get(:sourcegraph).enable sourcegraph_feature
+ stub_application_setting(sourcegraph_enabled: sourcegraph_enabled)
+ end
+
+ context 'when not fully enabled' do
+ where(:feature, :admin_enabled) do
+ false | false
+ false | true
+ true | false
+ end
+
+ with_them do
+ let(:sourcegraph_feature) { feature }
+ let(:sourcegraph_enabled) { admin_enabled }
+
+ before do
+ render
+ end
+
+ it 'does not display sourcegraph field' do
+ expect(rendered).not_to have_sourcegraph_field
+ end
+
+ it 'does not display integrations settings' do
+ expect(rendered).not_to have_integrations_section
+ end
+ end
+ end
+
+ context 'when fully enabled' do
+ let(:sourcegraph_feature) { true }
+ let(:sourcegraph_enabled) { true }
+
+ before do
+ render
+ end
+
+ it 'displays the sourcegraph field' do
+ expect(rendered).to have_sourcegraph_field
+ end
+
+ it 'displays the integrations section' do
+ expect(rendered).to have_integrations_section
+ end
+ end
+ end
+end
diff --git a/spec/views/projects/pages_domains/show.html.haml_spec.rb b/spec/views/projects/pages_domains/show.html.haml_spec.rb
index ba0544a49b0..331bfe63f28 100644
--- a/spec/views/projects/pages_domains/show.html.haml_spec.rb
+++ b/spec/views/projects/pages_domains/show.html.haml_spec.rb
@@ -30,39 +30,5 @@ describe 'projects/pages_domains/show' do
expect(rendered).to have_content("GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later.")
end
end
-
- context 'when certificate is present' do
- let(:domain) { create(:pages_domain, :letsencrypt, project: project) }
-
- it 'shows certificate info' do
- render
-
- # test just a random part of cert represenations(X509v3 Subject Key Identifier:)
- expect(rendered).to have_content("C6:5F:56:4B:10:69:AC:1D:33:D2:26:C9:B3:7A:D7:12:4D:3E:F7:90")
- end
- end
- end
-
- context 'when auto_ssl is disabled' do
- context 'when certificate is present' do
- let(:domain) { create(:pages_domain, project: project) }
-
- it 'shows certificate info' do
- render
-
- # test just a random part of cert represenations(X509v3 Subject Key Identifier:)
- expect(rendered).to have_content("C6:5F:56:4B:10:69:AC:1D:33:D2:26:C9:B3:7A:D7:12:4D:3E:F7:90")
- end
- end
-
- context 'when certificate is absent' do
- let(:domain) { create(:pages_domain, :without_certificate, :without_key, project: project) }
-
- it 'shows missing certificate' do
- render
-
- expect(rendered).to have_content("missing")
- end
- end
end
end
diff --git a/yarn.lock b/yarn.lock
index 61173c11bf3..f50795dec08 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -970,6 +970,11 @@
"@sentry/types" "5.7.1"
tslib "^1.9.3"
+"@sourcegraph/code-host-integration@^0.0.13":
+ version "0.0.13"
+ resolved "https://registry.yarnpkg.com/@sourcegraph/code-host-integration/-/code-host-integration-0.0.13.tgz#4fd5fe1e0088c63b2a26be231c5a2a4ca79b1596"
+ integrity sha512-IjF9gb9e8dG8p12DKg5Z7UMOVQO/ClH3AyMCPfX/qH7DH/0b55WH6stYVqZu6y776quFonO4Z9gWYM8pQZjzKw==
+
"@types/anymatch@*":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.0.tgz#d1d55958d1fccc5527d4aba29fc9c4b942f563ff"