summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue2
-rw-r--r--app/assets/javascripts/importer_status.js2
-rw-r--r--app/assets/javascripts/jobs/components/job_log_controllers.vue139
-rw-r--r--app/assets/javascripts/locale/ensure_single_line.js25
-rw-r--r--app/assets/javascripts/locale/index.js13
-rw-r--r--app/assets/javascripts/pages/instance_statistics/cohorts/index.js (renamed from app/assets/javascripts/pages/admin/cohorts/index.js)0
-rw-r--r--app/assets/javascripts/pages/instance_statistics/cohorts/usage_ping.js (renamed from app/assets/javascripts/pages/admin/cohorts/usage_ping.js)0
-rw-r--r--app/assets/javascripts/pages/instance_statistics/conversational_development_index/index.js (renamed from app/assets/javascripts/pages/admin/conversational_development_index/show/index.js)0
-rw-r--r--app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue3
-rw-r--r--app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue10
-rw-r--r--app/assets/stylesheets/pages/notes.scss3
-rw-r--r--app/models/ci/build.rb5
-rw-r--r--app/models/project.rb8
-rw-r--r--app/models/project_auto_devops.rb6
-rw-r--r--app/services/groups/destroy_service.rb7
-rw-r--r--app/services/projects/update_service.rb2
-rw-r--r--app/views/admin/application_settings/_ci_cd.html.haml2
-rw-r--r--app/views/admin/users/_access_levels.html.haml12
-rw-r--r--app/views/admin/users/_form.html.haml33
-rw-r--r--app/views/instance_statistics/cohorts/_cohorts_table.html.haml2
-rw-r--r--app/views/instance_statistics/conversational_development_index/_no_data.html.haml2
-rw-r--r--app/views/instance_statistics/conversational_development_index/index.html.haml2
-rw-r--r--app/views/projects/show.html.haml3
-rw-r--r--app/workers/detect_repository_languages_worker.rb2
-rw-r--r--changelogs/unreleased/41738-fix-sorting-issues-is-wrong-in-list-with-pagination.yml5
-rw-r--r--changelogs/unreleased/49770-fixes-input-alignment-on-user-admin-form-with-errors.yml5
-rw-r--r--changelogs/unreleased/49796-project-deletion-may-not-log-audit-events-during-group-deletion.yml5
-rw-r--r--changelogs/unreleased/50101-truncated-job-information.yml5
-rw-r--r--changelogs/unreleased/50312-instance-statistics-convdev-index-intro-banner-is-not-dismissable.yml5
-rw-r--r--changelogs/unreleased/add-ci_archive_traces_cron_worker-to-gitlab-yml.yml5
-rw-r--r--changelogs/unreleased/add-rake-command-to-migrate-locally-persisted-archived-traces.yml5
-rw-r--r--changelogs/unreleased/emoji-cutoff-1px.yml5
-rw-r--r--changelogs/unreleased/rails5-verbose-query-logs.yml5
-rw-r--r--changelogs/unreleased/rouge_3-2-1.yml5
-rw-r--r--config/gitlab.yml.example3
-rw-r--r--config/initializers/active_record_verbose_query_logs.rb4
-rw-r--r--config/initializers/gettext_rails_i18n_patch.rb15
-rw-r--r--doc/administration/index.md2
-rw-r--r--doc/administration/job_traces.md54
-rw-r--r--doc/ci/docker/README.md1
-rw-r--r--doc/ci/docker/using_kaniko.md60
-rw-r--r--doc/ci/yaml/README.md4
-rw-r--r--doc/topics/autodevops/index.md5
-rw-r--r--doc/user/admin_area/img/cohorts.pngbin439635 -> 0 bytes
-rw-r--r--doc/user/admin_area/monitoring/convdev.md32
-rw-r--r--doc/user/admin_area/monitoring/img/convdev_index.pngbin119743 -> 0 bytes
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md5
-rw-r--r--doc/user/admin_area/settings/usage_statistics.md13
-rw-r--r--doc/user/admin_area/user_cohorts.md40
-rw-r--r--doc/user/index.md4
-rw-r--r--doc/user/instance_statistics/convdev.md26
-rw-r--r--doc/user/instance_statistics/img/cohorts.pngbin0 -> 59494 bytes
-rw-r--r--doc/user/instance_statistics/img/convdev_index.pngbin0 -> 316893 bytes
-rw-r--r--doc/user/instance_statistics/img/instance_statistics_button.pngbin0 -> 9462 bytes
-rw-r--r--doc/user/instance_statistics/index.md19
-rw-r--r--doc/user/instance_statistics/user_cohorts.md27
-rw-r--r--doc/user/markdown.md15
-rw-r--r--doc/user/project/import/img/manifest_upload.pngbin12079 -> 0 bytes
-rw-r--r--doc/user/project/import/index.md2
-rw-r--r--doc/user/project/import/manifest.md64
-rw-r--r--doc/user/project/integrations/hangouts_chat.md2
-rw-r--r--doc/user/project/pages/getting_started_part_three.md40
-rw-r--r--doc/user/project/pages/img/dns_add_new_a_record_example_updated.pngbin10578 -> 0 bytes
-rw-r--r--doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.pngbin0 -> 7704 bytes
-rw-r--r--doc/user/project/repository/img/repository_languages.pngbin0 -> 88244 bytes
-rw-r--r--doc/user/project/repository/index.md10
-rw-r--r--lib/tasks/gitlab/traces.rake17
-rw-r--r--locale/gitlab.pot203
-rw-r--r--package.json2
-rw-r--r--scripts/frontend/extract_gettext_all.js72
-rw-r--r--spec/features/instance_statistics/cohorts_spec.rb8
-rw-r--r--spec/features/instance_statistics/conversational_development_index_spec.rb10
-rw-r--r--spec/javascripts/boards/board_list_spec.js9
-rw-r--r--spec/javascripts/jobs/components/job_log_controllers_spec.js217
-rw-r--r--spec/javascripts/locale/ensure_single_line_spec.js35
-rw-r--r--spec/javascripts/notes/mock_data.js2
-rw-r--r--spec/javascripts/vue_shared/translate_spec.js213
-rw-r--r--spec/models/project_auto_devops_spec.rb6
-rw-r--r--spec/models/project_spec.rb48
-rw-r--r--spec/services/groups/destroy_service_spec.rb11
-rw-r--r--spec/services/projects/detect_repository_languages_service_spec.rb4
-rw-r--r--spec/spec_helper.rb7
-rw-r--r--spec/tasks/gitlab/traces_rake_spec.rb112
-rw-r--r--yarn.lock80
85 files changed, 1579 insertions, 259 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index b33dd75c278..1aadc3fd0b6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -743,7 +743,7 @@ GEM
retriable (3.1.1)
rinku (2.0.0)
rotp (2.1.2)
- rouge (3.2.0)
+ rouge (3.2.1)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 3e610a4088c..bfc8d9b03ad 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -203,7 +203,7 @@ export default {
this.showIssueForm = !this.showIssueForm;
},
onScroll() {
- if (!this.loadingMore && (this.scrollTop() > this.scrollHeight() - this.scrollOffset)) {
+ if (!this.list.loadingMore && (this.scrollTop() > this.scrollHeight() - this.scrollOffset)) {
this.loadNextPage();
}
},
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js
index 0035d809062..eda8cdad908 100644
--- a/app/assets/javascripts/importer_status.js
+++ b/app/assets/javascripts/importer_status.js
@@ -87,7 +87,7 @@ class ImporterStatus {
details = error.response.data.errors;
}
- flash(__(`An error occurred while importing project: ${details}`));
+ flash(sprintf(__('An error occurred while importing project: %{details}'), { details }));
});
}
diff --git a/app/assets/javascripts/jobs/components/job_log_controllers.vue b/app/assets/javascripts/jobs/components/job_log_controllers.vue
new file mode 100644
index 00000000000..513851e376f
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job_log_controllers.vue
@@ -0,0 +1,139 @@
+<script>
+ import Icon from '~/vue_shared/components/icon.vue';
+ import tooltip from '~/vue_shared/directives/tooltip';
+ import { numberToHumanSize } from '~/lib/utils/number_utils';
+ import { s__, sprintf } from '~/locale';
+
+ export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ canEraseJob: {
+ type: Boolean,
+ required: true,
+ },
+ size: {
+ type: Number,
+ required: true,
+ },
+ rawTracePath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ canScrollToTop: {
+ type: Boolean,
+ required: true,
+ },
+ canScrollToBottom: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ jobLogSize() {
+ return sprintf('Showing last %{startSpanTag} %{size} %{endSpanTag} of log -', {
+ startSpanTag: '<span class="s-truncated-info-size truncated-info-size">',
+ endSpanTag: '</span>',
+ size: numberToHumanSize(this.size),
+ });
+ },
+ },
+ methods: {
+ handleEraseJobClick() {
+ // eslint-disable-next-line no-alert
+ if (window.confirm(s__('Job|Are you sure you want to erase this job?'))) {
+ this.$emit('eraseJob');
+ }
+ },
+ handleScrollToTop() {
+ this.$emit('scrollJobLogTop');
+ },
+ handleScrollToBottom() {
+ this.$emit('scrollJobLogBottom');
+ },
+ },
+ };
+</script>
+<template>
+ <div class="top-bar">
+ <!-- truncate information -->
+ <div class="js-truncated-info truncated-info d-none d-sm-block float-left">
+ <p v-html="jobLogSize"></p>
+
+ <a
+ v-if="rawTracePath"
+ :href="rawTracePath"
+ class="js-raw-link raw-link"
+ >
+ {{ s__("Job|Complete Raw") }}
+ </a>
+ </div>
+ <!-- eo truncate information -->
+
+ <div class="controllers float-right">
+ <!-- links -->
+ <a
+ v-tooltip
+ v-if="rawTracePath"
+ :title="s__('Job|Show complete raw')"
+ :href="rawTracePath"
+ class="js-raw-link-controller controllers-buttons"
+ data-container="body"
+ >
+ <icon name="doc-text" />
+ </a>
+
+ <button
+ v-tooltip
+ v-if="canEraseJob"
+ :title="s__('Job|Erase job log')"
+ type="button"
+ class="js-erase-link controllers-buttons"
+ data-container="body"
+ @click="handleEraseJobClick"
+ >
+ <icon name="remove" />
+ </button>
+ <!-- eo links -->
+
+ <!-- scroll buttons -->
+ <div
+ v-tooltip
+ :title="s__('Job|Scroll to top')"
+ class="controllers-buttons"
+ data-container="body"
+ >
+ <button
+ :disabled="!canScrollToTop"
+ type="button"
+ class="js-scroll-top btn-scroll btn-transparent btn-blank"
+ @click="handleScrollToTop"
+ >
+ <icon name="scroll_up"/>
+ </button>
+ </div>
+
+ <div
+ v-tooltip
+ :title="s__('Job|Scroll to bottom')"
+ class="controllers-buttons"
+ data-container="body"
+ >
+ <button
+ :disabled="!canScrollToBottom"
+ type="button"
+ class="js-scroll-bottom btn-scroll btn-transparent btn-blank"
+ @click="handleScrollToBottom"
+ >
+ <icon name="scroll_down"/>
+ </button>
+ </div>
+ <!-- eo scroll buttons -->
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/locale/ensure_single_line.js b/app/assets/javascripts/locale/ensure_single_line.js
new file mode 100644
index 00000000000..47c52fe6c50
--- /dev/null
+++ b/app/assets/javascripts/locale/ensure_single_line.js
@@ -0,0 +1,25 @@
+/* eslint-disable import/no-commonjs */
+
+const SPLIT_REGEX = /\s*[\r\n]+\s*/;
+
+/**
+ *
+ * strips newlines from strings and replaces them with a single space
+ *
+ * @example
+ *
+ * ensureSingleLine('foo \n bar') === 'foo bar'
+ *
+ * @param {String} str
+ * @returns {String}
+ */
+module.exports = function ensureSingleLine(str) {
+ // This guard makes the function significantly faster
+ if (str.includes('\n') || str.includes('\r')) {
+ return str
+ .split(SPLIT_REGEX)
+ .filter(s => s !== '')
+ .join(' ');
+ }
+ return str;
+};
diff --git a/app/assets/javascripts/locale/index.js b/app/assets/javascripts/locale/index.js
index 2cc5fb10027..1ae3362c4bc 100644
--- a/app/assets/javascripts/locale/index.js
+++ b/app/assets/javascripts/locale/index.js
@@ -1,4 +1,5 @@
import Jed from 'jed';
+import ensureSingleLine from './ensure_single_line';
import sprintf from './sprintf';
const languageCode = () => document.querySelector('html').getAttribute('lang') || 'en';
@@ -10,7 +11,7 @@ delete window.translations;
@param text The text to be translated
@returns {String} The translated text
*/
-const gettext = locale.gettext.bind(locale);
+const gettext = text => locale.gettext.bind(locale)(ensureSingleLine(text));
/**
Translate the text with a number
@@ -23,7 +24,10 @@ const gettext = locale.gettext.bind(locale);
@returns {String} Translated text with the number replaced (eg. '2 days')
*/
const ngettext = (text, pluralText, count) => {
- const translated = locale.ngettext(text, pluralText, count).replace(/%d/g, count).split('|');
+ const translated = locale
+ .ngettext(ensureSingleLine(text), ensureSingleLine(pluralText), count)
+ .replace(/%d/g, count)
+ .split('|');
return translated[translated.length - 1];
};
@@ -40,7 +44,7 @@ const ngettext = (text, pluralText, count) => {
@returns {String} Translated context based text
*/
const pgettext = (keyOrContext, key) => {
- const normalizedKey = key ? `${keyOrContext}|${key}` : keyOrContext;
+ const normalizedKey = ensureSingleLine(key ? `${keyOrContext}|${key}` : keyOrContext);
const translated = gettext(normalizedKey).split('|');
return translated[translated.length - 1];
@@ -52,8 +56,7 @@ const pgettext = (keyOrContext, key) => {
@param formatOptions for available options, please see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
@returns {Intl.DateTimeFormat}
*/
-const createDateTimeFormat =
- formatOptions => Intl.DateTimeFormat(languageCode(), formatOptions);
+const createDateTimeFormat = formatOptions => Intl.DateTimeFormat(languageCode(), formatOptions);
export { languageCode };
export { gettext as __ };
diff --git a/app/assets/javascripts/pages/admin/cohorts/index.js b/app/assets/javascripts/pages/instance_statistics/cohorts/index.js
index 2d5020dbef4..2d5020dbef4 100644
--- a/app/assets/javascripts/pages/admin/cohorts/index.js
+++ b/app/assets/javascripts/pages/instance_statistics/cohorts/index.js
diff --git a/app/assets/javascripts/pages/admin/cohorts/usage_ping.js b/app/assets/javascripts/pages/instance_statistics/cohorts/usage_ping.js
index 914a9661c27..914a9661c27 100644
--- a/app/assets/javascripts/pages/admin/cohorts/usage_ping.js
+++ b/app/assets/javascripts/pages/instance_statistics/cohorts/usage_ping.js
diff --git a/app/assets/javascripts/pages/admin/conversational_development_index/show/index.js b/app/assets/javascripts/pages/instance_statistics/conversational_development_index/index.js
index c1056537f90..c1056537f90 100644
--- a/app/assets/javascripts/pages/admin/conversational_development_index/show/index.js
+++ b/app/assets/javascripts/pages/instance_statistics/conversational_development_index/index.js
diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
index 5e7b8f9698f..63082654101 100644
--- a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
+++ b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
@@ -1,4 +1,5 @@
<script>
+import { __ } from '~/locale';
import $ from 'jquery';
import eventHub from '../../event_hub';
@@ -17,7 +18,7 @@ export default {
computed: {
buttonText() {
- return this.isLocked ? this.__('Unlock') : this.__('Lock');
+ return this.isLocked ? __('Unlock') : __('Lock');
},
toggleLock() {
diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
index 8bbc59f623a..ab7fab7e5ca 100644
--- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
@@ -1,5 +1,5 @@
<script>
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
import Flash from '~/flash';
import tooltip from '~/vue_shared/directives/tooltip';
import issuableMixin from '~/vue_shared/mixins/issuable';
@@ -79,11 +79,9 @@ export default {
.then(() => window.location.reload())
.catch(() =>
Flash(
- this.__(
- `Something went wrong trying to change the locked state of this ${
- this.issuableDisplayName
- }`,
- ),
+ sprintf(__('Something went wrong trying to change the locked state of this %{issuableDisplayName}'), {
+ issuableDisplayName: this.issuableDisplayName,
+ }),
),
);
},
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 8d28daac750..2e1b2126887 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -141,9 +141,6 @@ ul.notes {
}
.note-body {
- overflow-x: auto;
- overflow-y: hidden;
-
.note-text {
@include md-typography;
// Reset ul style types since we're nested inside a ul already
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 3c69677baf0..faa160ad6ba 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -67,6 +67,10 @@ module Ci
'', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').archive)
end
+ scope :with_archived_trace, ->() do
+ where('EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').trace)
+ end
+
scope :without_archived_trace, ->() do
where('NOT EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').trace)
end
@@ -77,6 +81,7 @@ module Ci
end
scope :with_artifacts_stored_locally, -> { with_artifacts_archive.where(artifacts_file_store: [nil, LegacyArtifactUploader::Store::LOCAL]) }
+ scope :with_archived_trace_stored_locally, -> { with_archived_trace.where(artifacts_file_store: [nil, LegacyArtifactUploader::Store::LOCAL]) }
scope :with_artifacts_not_expired, ->() { with_artifacts_archive.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts_archive.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
diff --git a/app/models/project.rb b/app/models/project.rb
index 7735f23cb9e..94c1d60f071 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -27,6 +27,7 @@ class Project < ActiveRecord::Base
include FastDestroyAll::Helpers
include WithUploads
include BatchDestroyDependentAssociations
+ include FeatureGate
extend Gitlab::Cache::RequestCache
extend Gitlab::ConfigHelper
@@ -519,18 +520,19 @@ class Project < ActiveRecord::Base
def auto_devops_enabled?
if auto_devops&.enabled.nil?
- Gitlab::CurrentSettings.auto_devops_enabled?
+ has_auto_devops_implicitly_enabled?
else
auto_devops.enabled?
end
end
def has_auto_devops_implicitly_enabled?
- auto_devops&.enabled.nil? && Gitlab::CurrentSettings.auto_devops_enabled?
+ auto_devops&.enabled.nil? &&
+ (Gitlab::CurrentSettings.auto_devops_enabled? || Feature.enabled?(:force_autodevops_on_by_default, self))
end
def has_auto_devops_implicitly_disabled?
- auto_devops&.enabled.nil? && !Gitlab::CurrentSettings.auto_devops_enabled?
+ auto_devops&.enabled.nil? && !(Gitlab::CurrentSettings.auto_devops_enabled? || Feature.enabled?(:force_autodevops_on_by_default, self))
end
def empty_repo?
diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb
index 155400d1a43..dc6736dd9cd 100644
--- a/app/models/project_auto_devops.rb
+++ b/app/models/project_auto_devops.rb
@@ -47,12 +47,8 @@ class ProjectAutoDevops < ActiveRecord::Base
end
def needs_to_create_deploy_token?
- auto_devops_enabled? &&
+ project.auto_devops_enabled? &&
!project.public? &&
!project.deploy_tokens.find_by(name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME).present?
end
-
- def auto_devops_enabled?
- Gitlab::CurrentSettings.auto_devops_enabled? || enabled?
- end
end
diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb
index c4554ce45fb..12aeba4af71 100644
--- a/app/services/groups/destroy_service.rb
+++ b/app/services/groups/destroy_service.rb
@@ -2,6 +2,8 @@
module Groups
class DestroyService < Groups::BaseService
+ DestroyError = Class.new(StandardError)
+
def async_execute
job_id = GroupDestroyWorker.perform_async(group.id, current_user.id)
Rails.logger.info("User #{current_user.id} scheduled a deletion of group ID #{group.id} with job ID #{job_id}")
@@ -12,9 +14,8 @@ module Groups
group.projects.each do |project|
# Execute the destruction of the models immediately to ensure atomic cleanup.
- # Skip repository removal because we remove directory with namespace
- # that contain all these repositories
- ::Projects::DestroyService.new(project, current_user, skip_repo: project.legacy_storage?).execute
+ success = ::Projects::DestroyService.new(project, current_user).execute
+ raise DestroyError, "Project #{project.id} can't be deleted" unless success
end
group.children.each do |group|
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index 97f181ccea8..e390d7a04c3 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -30,7 +30,7 @@ module Projects
def run_auto_devops_pipeline?
return false if project.repository.gitlab_ci_yml || !project.auto_devops&.previous_changes&.include?('enabled')
- project.auto_devops.enabled? || (project.auto_devops.enabled.nil? && Gitlab::CurrentSettings.auto_devops_enabled?)
+ project.auto_devops_enabled?
end
private
diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml
index 5037017e38a..97be658cd34 100644
--- a/app/views/admin/application_settings/_ci_cd.html.haml
+++ b/app/views/admin/application_settings/_ci_cd.html.haml
@@ -38,6 +38,8 @@
.form-text.text-muted
Set the default expiration time for each job's artifacts.
0 for unlimited.
+ The default unit is in seconds, but you can define an alternative. For example:
+ <code>4 mins 2 sec</code>, <code>2h42min</code>.
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/users/_access_levels.html.haml b/app/views/admin/users/_access_levels.html.haml
index 04acc5f8423..5f68163163e 100644
--- a/app/views/admin/users/_access_levels.html.haml
+++ b/app/views/admin/users/_access_levels.html.haml
@@ -1,15 +1,18 @@
%fieldset
%legend Access
.form-group.row
- = f.label :projects_limit, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :projects_limit, class: 'col-form-label'
.col-sm-10= f.number_field :projects_limit, min: 0, max: Gitlab::Database::MAX_INT_VALUE, class: 'form-control'
.form-group.row
- = f.label :can_create_group, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :can_create_group, class: 'col-form-label'
.col-sm-10= f.check_box :can_create_group
.form-group.row
- = f.label :access_level, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :access_level, class: 'col-form-label'
.col-sm-10
- editing_current_user = (current_user == @user)
@@ -29,7 +32,8 @@
You cannot remove your own admin rights.
.form-group.row
- = f.label :external, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :external, class: 'col-form-label'
.col-sm-10
= f.check_box :external do
External
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index 58be07fc83e..7f21bdb91c8 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -5,17 +5,20 @@
%fieldset
%legend Account
.form-group.row
- = f.label :name, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :name, class: 'col-form-label'
.col-sm-10
= f.text_field :name, required: true, autocomplete: 'off', class: 'form-control'
%span.help-inline * required
.form-group.row
- = f.label :username, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :username, class: 'col-form-label'
.col-sm-10
= f.text_field :username, required: true, autocomplete: 'off', autocorrect: 'off', autocapitalize: 'off', spellcheck: false, class: 'form-control'
%span.help-inline * required
.form-group.row
- = f.label :email, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :email, class: 'col-form-label'
.col-sm-10
= f.text_field :email, required: true, autocomplete: 'off', class: 'form-control'
%span.help-inline * required
@@ -24,7 +27,8 @@
%fieldset
%legend Password
.form-group.row
- = f.label :password, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :password, class: 'col-form-label'
.col-sm-10
%strong
Reset link will be generated and sent to the user.
@@ -34,10 +38,12 @@
%fieldset
%legend Password
.form-group.row
- = f.label :password, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :password, class: 'col-form-label'
.col-sm-10= f.password_field :password, disabled: f.object.force_random_password, class: 'form-control'
.form-group.row
- = f.label :password_confirmation, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :password_confirmation, class: 'col-form-label'
.col-sm-10= f.password_field :password_confirmation, disabled: f.object.force_random_password, class: 'form-control'
= render partial: 'access_levels', locals: { f: f }
@@ -45,21 +51,26 @@
%fieldset
%legend Profile
.form-group.row
- = f.label :avatar, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :avatar, class: 'col-form-label'
.col-sm-10
= f.file_field :avatar
.form-group.row
- = f.label :skype, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :skype, class: 'col-form-label'
.col-sm-10= f.text_field :skype, class: 'form-control'
.form-group.row
- = f.label :linkedin, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :linkedin, class: 'col-form-label'
.col-sm-10= f.text_field :linkedin, class: 'form-control'
.form-group.row
- = f.label :twitter, class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :twitter, class: 'col-form-label'
.col-sm-10= f.text_field :twitter, class: 'form-control'
.form-group.row
- = f.label :website_url, 'Website', class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right
+ = f.label :website_url, 'Website', class: 'col-form-label'
.col-sm-10= f.text_field :website_url, class: 'form-control'
.form-actions
diff --git a/app/views/instance_statistics/cohorts/_cohorts_table.html.haml b/app/views/instance_statistics/cohorts/_cohorts_table.html.haml
index 701a4e62b39..6a7c999bff3 100644
--- a/app/views/instance_statistics/cohorts/_cohorts_table.html.haml
+++ b/app/views/instance_statistics/cohorts/_cohorts_table.html.haml
@@ -3,7 +3,7 @@
User cohorts are shown for the last #{@cohorts[:months_included]}
months. Only users with activity are counted in the cohort total; inactive
users are counted separately.
- = link_to icon('question-circle'), help_page_path('user/admin_area/user_cohorts', anchor: 'cohorts'), title: 'About this feature', target: '_blank'
+ = link_to icon('question-circle'), help_page_path('user/instance_statistics/user_cohorts', anchor: 'cohorts'), title: 'About this feature', target: '_blank'
.table-holder
%table.table
diff --git a/app/views/instance_statistics/conversational_development_index/_no_data.html.haml b/app/views/instance_statistics/conversational_development_index/_no_data.html.haml
index d69c46194b4..dd795aee135 100644
--- a/app/views/instance_statistics/conversational_development_index/_no_data.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/_no_data.html.haml
@@ -4,4 +4,4 @@
%h4 Data is still calculating...
%p
In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index.
- = link_to 'Learn more', help_page_path('user/admin_area/monitoring/convdev'), target: '_blank'
+ = link_to 'Learn more', help_page_path('user/instance_statistics/convdev'), target: '_blank'
diff --git a/app/views/instance_statistics/conversational_development_index/index.html.haml b/app/views/instance_statistics/conversational_development_index/index.html.haml
index e3d1aa31dc2..dd63b98376f 100644
--- a/app/views/instance_statistics/conversational_development_index/index.html.haml
+++ b/app/views/instance_statistics/conversational_development_index/index.html.haml
@@ -19,7 +19,7 @@
index
%br
score
- = link_to icon('question-circle', 'aria-hidden' => 'true'), help_page_path('user/admin_area/monitoring/convdev')
+ = link_to icon('question-circle', 'aria-hidden' => 'true'), help_page_path('user/instance_statistics/convdev')
.convdev-cards.board-card-container
- @metric.cards.each do |card|
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 8a5abb64515..df8a5742450 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -21,8 +21,7 @@
%nav.project-stats{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
- - if Feature.enabled?(:repository_languages, @project.namespace.becomes(Namespace))
- = repository_languages_bar(@project.repository_languages)
+ = repository_languages_bar(@project.repository_languages)
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
- if @project.archived?
diff --git a/app/workers/detect_repository_languages_worker.rb b/app/workers/detect_repository_languages_worker.rb
index cfbbbffc8a6..854b74b884a 100644
--- a/app/workers/detect_repository_languages_worker.rb
+++ b/app/workers/detect_repository_languages_worker.rb
@@ -16,8 +16,6 @@ class DetectRepositoryLanguagesWorker
user = User.find_by(id: user_id)
return unless project && user
- return if Feature.disabled?(:repository_languages, project.namespace)
-
try_obtain_lease do
::Projects::DetectRepositoryLanguagesService.new(project, user).execute
end
diff --git a/changelogs/unreleased/41738-fix-sorting-issues-is-wrong-in-list-with-pagination.yml b/changelogs/unreleased/41738-fix-sorting-issues-is-wrong-in-list-with-pagination.yml
new file mode 100644
index 00000000000..bc0150c6586
--- /dev/null
+++ b/changelogs/unreleased/41738-fix-sorting-issues-is-wrong-in-list-with-pagination.yml
@@ -0,0 +1,5 @@
+---
+title: "Fix If-Check the result that a function was executed several times"
+merge_request: 20640
+author: Max Dicker
+type: fixed
diff --git a/changelogs/unreleased/49770-fixes-input-alignment-on-user-admin-form-with-errors.yml b/changelogs/unreleased/49770-fixes-input-alignment-on-user-admin-form-with-errors.yml
new file mode 100644
index 00000000000..00e1f6e638a
--- /dev/null
+++ b/changelogs/unreleased/49770-fixes-input-alignment-on-user-admin-form-with-errors.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes input alignment in user admin form with errors
+merge_request: 21108
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/49796-project-deletion-may-not-log-audit-events-during-group-deletion.yml b/changelogs/unreleased/49796-project-deletion-may-not-log-audit-events-during-group-deletion.yml
new file mode 100644
index 00000000000..bb7633abdb1
--- /dev/null
+++ b/changelogs/unreleased/49796-project-deletion-may-not-log-audit-events-during-group-deletion.yml
@@ -0,0 +1,5 @@
+---
+title: 'Fix: Project deletion may not log audit events during group deletion'
+merge_request: 21162
+author:
+type: fixed
diff --git a/changelogs/unreleased/50101-truncated-job-information.yml b/changelogs/unreleased/50101-truncated-job-information.yml
new file mode 100644
index 00000000000..b873b8b7bf6
--- /dev/null
+++ b/changelogs/unreleased/50101-truncated-job-information.yml
@@ -0,0 +1,5 @@
+---
+title: Creates vue component for job log top bar with controllers
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/50312-instance-statistics-convdev-index-intro-banner-is-not-dismissable.yml b/changelogs/unreleased/50312-instance-statistics-convdev-index-intro-banner-is-not-dismissable.yml
new file mode 100644
index 00000000000..50a3b9c9aff
--- /dev/null
+++ b/changelogs/unreleased/50312-instance-statistics-convdev-index-intro-banner-is-not-dismissable.yml
@@ -0,0 +1,5 @@
+---
+title: Fix issue stopping Instance Statistics javascript to be executed
+merge_request: 21211
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-ci_archive_traces_cron_worker-to-gitlab-yml.yml b/changelogs/unreleased/add-ci_archive_traces_cron_worker-to-gitlab-yml.yml
new file mode 100644
index 00000000000..d963dc5bac3
--- /dev/null
+++ b/changelogs/unreleased/add-ci_archive_traces_cron_worker-to-gitlab-yml.yml
@@ -0,0 +1,5 @@
+---
+title: Add an example of the configuration of archive trace cron worker in gitlab.yml.example
+merge_request: 20583
+author:
+type: other
diff --git a/changelogs/unreleased/add-rake-command-to-migrate-locally-persisted-archived-traces.yml b/changelogs/unreleased/add-rake-command-to-migrate-locally-persisted-archived-traces.yml
new file mode 100644
index 00000000000..b82344e3c9c
--- /dev/null
+++ b/changelogs/unreleased/add-rake-command-to-migrate-locally-persisted-archived-traces.yml
@@ -0,0 +1,5 @@
+---
+title: Add rake command to migrate archived traces from local storage to object storage
+merge_request: 21193
+author:
+type: added
diff --git a/changelogs/unreleased/emoji-cutoff-1px.yml b/changelogs/unreleased/emoji-cutoff-1px.yml
new file mode 100644
index 00000000000..815d9c177e8
--- /dev/null
+++ b/changelogs/unreleased/emoji-cutoff-1px.yml
@@ -0,0 +1,5 @@
+---
+title: Fix 1px cutoff of emojis
+merge_request: 21180
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/rails5-verbose-query-logs.yml b/changelogs/unreleased/rails5-verbose-query-logs.yml
new file mode 100644
index 00000000000..7585e75d30b
--- /dev/null
+++ b/changelogs/unreleased/rails5-verbose-query-logs.yml
@@ -0,0 +1,5 @@
+---
+title: 'Rails5: Enable verbose query logs'
+merge_request: 21231
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/rouge_3-2-1.yml b/changelogs/unreleased/rouge_3-2-1.yml
new file mode 100644
index 00000000000..b281a4f0e95
--- /dev/null
+++ b/changelogs/unreleased/rouge_3-2-1.yml
@@ -0,0 +1,5 @@
+---
+title: Update to Rouge 3.2.1, which includes a critical fix to the Perl Lexer
+merge_request: 21263
+author:
+type: changed
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 561ff57c9fb..4847a82236b 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -261,6 +261,9 @@ production: &base
# once per hour you will have concurrent 'git fsck' jobs.
repository_check_worker:
cron: "20 * * * *"
+ # Archive live traces which have not been archived yet
+ ci_archive_traces_cron_worker:
+ cron: "17 * * * *"
# Send admin emails once a week
admin_email_worker:
cron: "0 0 * * 0"
diff --git a/config/initializers/active_record_verbose_query_logs.rb b/config/initializers/active_record_verbose_query_logs.rb
index 44f86fec7e0..1c5fbc8e830 100644
--- a/config/initializers/active_record_verbose_query_logs.rb
+++ b/config/initializers/active_record_verbose_query_logs.rb
@@ -47,7 +47,9 @@ module ActiveRecord
end
end
- unless Gitlab.rails5?
+ if Rails.version.start_with?("5.2")
+ raise "Remove this monkey patch: #{__FILE__}"
+ else
prepend(VerboseQueryLogs) unless Rails.env.production?
end
end
diff --git a/config/initializers/gettext_rails_i18n_patch.rb b/config/initializers/gettext_rails_i18n_patch.rb
index 49551319435..c1342f48ebd 100644
--- a/config/initializers/gettext_rails_i18n_patch.rb
+++ b/config/initializers/gettext_rails_i18n_patch.rb
@@ -1,5 +1,6 @@
require 'gettext_i18n_rails/haml_parser'
require 'gettext_i18n_rails_js/parser/javascript'
+require 'json'
VUE_TRANSLATE_REGEX = /((%[\w.-]+)(?:\s))?{{ (N|n|s)?__\((.*)\) }}/
@@ -36,6 +37,20 @@ module GettextI18nRailsJs
".vue"
].include? ::File.extname(file)
end
+
+ def collect_for(file)
+ gettext_messages_by_file[file] || []
+ end
+
+ private
+
+ def gettext_messages_by_file
+ @gettext_messages_by_file ||= JSON.parse(load_messages)
+ end
+
+ def load_messages
+ `node scripts/frontend/extract_gettext_all.js --all`
+ end
end
end
end
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 112d14652af..030a2f95e23 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -93,7 +93,6 @@ created in snippets, wikis, and repos.
- [Postfix for incoming email](reply_by_email_postfix_setup.md): Set up a
basic Postfix mail server with IMAP authentication on Ubuntu for incoming
emails.
-- [User Cohorts](../user/admin_area/user_cohorts.md): Display the monthly cohorts of new users and their activities over time.
[reply by email]: reply_by_email.md
[issues by email]: ../user/project/issues/create_new_issue.md#new-issue-via-email
@@ -137,7 +136,6 @@ created in snippets, wikis, and repos.
- [Monitoring uptime](../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint.
- [IP whitelist](monitoring/ip_whitelist.md): Monitor endpoints that provide health check information when probed.
- [Monitoring GitHub imports](monitoring/github_imports.md): GitLab's GitHub Importer displays Prometheus metrics to monitor the health and progress of the importer.
-- [Conversational Development (ConvDev) Index](../user/admin_area/monitoring/convdev.md): Provides an overview of your entire instance's feature usage.
### Performance Monitoring
diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md
index 24d1a3fd151..6e2f67f61bc 100644
--- a/doc/administration/job_traces.md
+++ b/doc/administration/job_traces.md
@@ -3,10 +3,6 @@
Job traces are sent by GitLab Runner while it's processing a job. You can see
traces in job pages, pipelines, email notifications, etc.
-There isn't a way to automatically expire old job logs, but it's safe to remove
-them if they're taking up too much space. If you remove the logs manually, the
-job output in the UI will be empty.
-
## Data flow
In general, there are two states in job traces: "live trace" and "archived trace".
@@ -57,11 +53,55 @@ To change the location where the job logs will be stored, follow the steps below
## Uploading traces to object storage
-An archived trace is considered as a [job artifact](job_artifacts.md).
-Therefore, when you [set up an object storage](job_artifacts.md#object-storage-settings),
+Archived traces are considered as [job artifacts](job_artifacts.md).
+Therefore, when you [set up the object storage integration](job_artifacts.md#object-storage-settings),
job traces are automatically migrated to it along with the other job artifacts.
-See [Data flow](#data-flow) to learn about the process.
+See "Phase 4: uploading" in [Data flow](#data-flow) to learn about the process.
+
+## How to archive legacy job trace files
+
+Legacy job traces, which were created before GitLab 10.5, were not archived regularly.
+It's the same state with the "2: overwriting" in the above [Data flow](#data-flow).
+To archive those legacy job traces, please follow the instruction below.
+
+1. Execute the following command
+
+ ```bash
+ gitlab-rake gitlab:traces:archive
+ ```
+
+ After you executed this task, GitLab instance queues up Sidekiq jobs (asynchronous processes)
+ for migrating job trace files from local storage to object storage.
+ It could take time to complete the all migration jobs. You can check the progress by the following command
+
+ ```bash
+ sudo gitlab-rails console
+ ```
+
+ ```bash
+ [1] pry(main)> Sidekiq::Stats.new.queues['pipeline_background:archive_trace']
+ => 100
+ ```
+
+ If the count becomes zero, the archiving processes are done
+
+## How to migrate archived job traces to object storage
+
+If job traces have already been archived into local storage, and you want to migrate those traces to object storage, please follow the instruction below.
+
+1. Ensure [Object storage integration for Job Artifacts](job_artifacts.md#object-storage-settings) is enabled
+1. Execute the following command
+
+ ```bash
+ gitlab-rake gitlab:traces:migrate
+ ```
+
+## How to remove job traces
+
+There isn't a way to automatically expire old job logs, but it's safe to remove
+them if they're taking up too much space. If you remove the logs manually, the
+job output in the UI will be empty.
## New live trace architecture
diff --git a/doc/ci/docker/README.md b/doc/ci/docker/README.md
index b0e01d74f7e..8ae80b2bc02 100644
--- a/doc/ci/docker/README.md
+++ b/doc/ci/docker/README.md
@@ -6,3 +6,4 @@ comments: false
- [Using Docker Images](using_docker_images.md)
- [Using Docker Build](using_docker_build.md)
+- [Using kaniko](using_kaniko.md)
diff --git a/doc/ci/docker/using_kaniko.md b/doc/ci/docker/using_kaniko.md
new file mode 100644
index 00000000000..7d4f28e1f47
--- /dev/null
+++ b/doc/ci/docker/using_kaniko.md
@@ -0,0 +1,60 @@
+# Building images with kaniko and GitLab CI/CD
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/45512) in GitLab 11.2.
+Requires GitLab Runner 11.2 and above.
+
+[kaniko](https://github.com/GoogleContainerTools/kaniko) is a tool to build
+container images from a Dockerfile, inside a container or Kubernetes cluster.
+
+kaniko solves two problems with using the
+[docker-in-docker build](using_docker_build.md#use-docker-in-docker-executor) method:
+
+1. Docker-in-docker requires [privileged mode](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities)
+ in order to function, which is a significant security concern.
+1. Docker-in-docker generally incurs a performance penalty and can be quite slow.
+
+## Requirements
+
+In order to utilize kaniko with GitLab, a [GitLab Runner](https://docs.gitlab.com/runner/)
+using either the [Kubernetes](https://docs.gitlab.com/runner/executors/kubernetes.html),
+[Docker](https://docs.gitlab.com/runner/executors/docker.html), or
+[Docker Machine](https://docs.gitlab.com/runner/executors/docker_machine.html)
+executors is required.
+
+## Building a Docker image with kaniko
+
+When building an image with kaniko and GitLab CI/CD, you should be aware of a
+few important details:
+
+- The kaniko debug image is recommended (`gcr.io/kaniko-project/executor:debug`)
+ because it has a shell, and a shell is required for an image to be used with
+ GitLab CI/CD.
+- The entrypoint will need to be [overridden](using_docker_images.md#overriding-the-entrypoint-of-an-image),
+ otherwise the build script will not run.
+- A Docker `config.json` file needs to be created with the authentication
+ information for the desired container registry.
+
+---
+
+In the following example, kaniko is used to build a Docker image and then push
+it to [GitLab Container Registry](../../user/project/container_registry.md).
+The job will run only when a tag is pushed. A `config.json` file is created under
+`/root/.docker` with the needed GitLab Container Registry credentials taken from the
+[environment variables](../variables/README.md#predefined-variables-environment-variables)
+GitLab CI/CD provides. In the last step, kaniko uses the `Dockerfile` under the
+root directory of the project, builds the Docker image and pushes it to the
+project's Container Registry while tagging it with the Git tag:
+
+```yaml
+build:
+ stage: build
+ image:
+ name: gcr.io/kaniko-project/executor:debug
+ entrypoint: [""]
+ script:
+ - mkdir -p /root/.docker
+ - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /root/.docker/config.json
+ - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
+ only:
+ - tags
+```
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index ef740ab1c5e..abba748db8b 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1075,8 +1075,10 @@ keep artifacts forever.
After their expiry, artifacts are deleted hourly by default (via a cron job),
and are not accessible anymore.
-The value of `expire_in` is an elapsed time. Examples of parsable values:
+The value of `expire_in` is an elapsed time in seconds, unless a unit is
+provided. Examples of parsable values:
+- '42'
- '3 mins 4 sec'
- '2 hrs 20 min'
- '2h20min'
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index f5574506595..0474182e324 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -233,6 +233,11 @@ in **Admin Area > Settings > Continuous Integration and Deployment**. Doing that
all the projects that haven't explicitly set an option will have Auto DevOps
enabled by default.
+NOTE: **Note:**
+There is also a feature flag to enable Auto DevOps to a percentage of projects
+which can be enabled from the console with
+`Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(10)`.
+
### Deployment strategy
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/38542) in GitLab 11.0.
diff --git a/doc/user/admin_area/img/cohorts.png b/doc/user/admin_area/img/cohorts.png
deleted file mode 100644
index 8bae7faff07..00000000000
--- a/doc/user/admin_area/img/cohorts.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/admin_area/monitoring/convdev.md b/doc/user/admin_area/monitoring/convdev.md
index a98602c4d70..6ad8a5a7ff0 100644
--- a/doc/user/admin_area/monitoring/convdev.md
+++ b/doc/user/admin_area/monitoring/convdev.md
@@ -1,29 +1,5 @@
-# Conversational Development Index
+---
+redirect_to: '../../instance_statistics/convdev.md'
+---
-> [Introduced][ce-30469] in GitLab 9.3.
-
-Conversational Development Index (ConvDev) gives you an overview of your entire
-instance's feature usage, from idea to production. It looks at your usage in the
-past 30 days, averaged over the number of active users in that time period. It also
-provides a lead score per feature, which is calculated based on GitLab's analysis
-of top performing instances, based on [usage ping data][ping] that GitLab has
-collected. Your score is compared to the lead score, expressed as a percentage.
-The overall index score is an average over all your feature scores.
-
-![ConvDev index](img/convdev_index.png)
-
-The page also provides helpful links to articles and GitLab docs, to help you
-improve your scores.
-
-Your GitLab instance's usage ping must be activated in order to use this feature.
-Usage ping data is aggregated on GitLab's servers for analysis. Your usage
-information is **not sent** to any other GitLab instances.
-
-If you have just started using GitLab, it may take a few weeks for data to be
-collected before this feature is available.
-
-This feature is accessible only to a system admin, at
-**Admin area > Overview > ConvDev Index**.
-
-[ce-30469]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30469
-[ping]: ../settings/usage_statistics.md#usage-ping
+This document was moved to [another location](../../instance_statistics/convdev.md).
diff --git a/doc/user/admin_area/monitoring/img/convdev_index.png b/doc/user/admin_area/monitoring/img/convdev_index.png
deleted file mode 100644
index 1bf1d6a83c9..00000000000
--- a/doc/user/admin_area/monitoring/img/convdev_index.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index eb6f915f3f4..76d9a4ceb03 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -21,8 +21,9 @@ that this setting is set for each job.
The default expiration time of the [job artifacts][art-yml] can be set in
the Admin area of your GitLab instance. The syntax of duration is described
in [artifacts:expire_in][duration-syntax]. The default is `30 days`. Note that
-this setting is set for each job. Set it to 0 if you don't want default
-expiration.
+this setting is set for each job. Set it to `0` if you don't want default
+expiration. The default unit is in seconds.
+
1. Go to **Admin area > Settings** (`/admin/application_settings`).
diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md
index 381efdf5d67..b7427592e10 100644
--- a/doc/user/admin_area/settings/usage_statistics.md
+++ b/doc/user/admin_area/settings/usage_statistics.md
@@ -6,7 +6,7 @@ to perform various actions.
All statistics are opt-out, you can enable/disable them from the admin panel
under **Admin area > Settings > Usage statistics**.
-## Version check
+## Version check **[CORE ONLY]**
If enabled, version check will inform you if a new version is available and the
importance of it through a status. This is shown on the help page (i.e. `/help`)
@@ -29,7 +29,7 @@ secure.
If you disable version check, this information will not be collected. Enable or
disable the version check at **Admin area > Settings > Usage statistics**.
-## Usage ping
+## Usage ping **[CORE ONLY]**
> [Introduced][ee-557] in GitLab Enterprise Edition 8.10. More statistics
[were added][ee-735] in GitLab Enterprise Edition
@@ -67,6 +67,15 @@ production: &base
usage_ping_enabled: false
```
+## Instance statistics visibility **[CORE ONLY]**
+
+Once usage ping is enabled, GitLab will gather data from other instances and
+will be able to show [usage statistics](../../instance_statistics/index.md)
+of your instance to your users.
+
+This can be restricted to admins by selecting "Only admins" in the Instance
+Statistics visibility section under **Admin area > Settings > Usage statistics**.
+
[ee-557]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/557
[ee-735]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/735
[ce-23361]: https://gitlab.com/gitlab-org/gitlab-ce/issues/23361
diff --git a/doc/user/admin_area/user_cohorts.md b/doc/user/admin_area/user_cohorts.md
index e25e7a8bbc3..21e61e2ec44 100644
--- a/doc/user/admin_area/user_cohorts.md
+++ b/doc/user/admin_area/user_cohorts.md
@@ -1,37 +1,5 @@
-# Cohorts
+---
+redirect_to: '../instance_statistics/user_cohorts.md'
+---
-> **Notes:**
-> [Introduced][ce-23361] in GitLab 9.1.
-
-As a benefit of having the [usage ping active](settings/usage_statistics.md),
-GitLab lets you analyze the users' activities of your GitLab installation.
-Under `/admin/cohorts`, when the usage ping is active, GitLab will show the
-monthly cohorts of new users and their activities over time.
-
-## Overview
-
-How do we read the user cohorts table? Let's take an example with the following
-user cohorts.
-
-![User cohort example](img/cohorts.png)
-
-For the cohort of June 2016, 163 users have been added on this server and have
-been active since this month. One month later, in July 2016, out of
-these 163 users, 155 users (or 95% of the June cohort) are still active. Two
-months later, 139 users (or 85%) are still active. 9 months later, we can see
-that only 6% of this cohort are still active.
-
-The Inactive users column shows the number of users who have been added during
-the month, but who have never actually had any activity in the instance.
-
-How do we measure the activity of users? GitLab considers a user active if:
-
-* the user signs in
-* the user has Git activity (whether push or pull).
-
-## Setup
-
-1. [Activate the usage ping](settings/usage_statistics.md)
-2. Go to `/admin/cohorts` to see the user cohorts of the server
-
-[ce-23361]: https://gitlab.com/gitlab-org/gitlab-ce/issues/23361
+This document was moved to [another location](../instance_statistics/user_cohorts.md).
diff --git a/doc/user/index.md b/doc/user/index.md
index 90f0e2285c3..649c0b664a5 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -172,3 +172,7 @@ Automate GitLab via [API](../api/README.md).
## Git and GitLab
Learn what is [Git](../topics/git/index.md) and its best practices.
+
+## Instance statistics
+
+See [various statistics](instance_statistics/index.md) of your GitLab instance.
diff --git a/doc/user/instance_statistics/convdev.md b/doc/user/instance_statistics/convdev.md
new file mode 100644
index 00000000000..d2795e092fc
--- /dev/null
+++ b/doc/user/instance_statistics/convdev.md
@@ -0,0 +1,26 @@
+# Conversational Development Index
+
+> [Introduced][ce-30469] in GitLab 9.3.
+
+Conversational Development Index (ConvDev) gives you an overview of your entire
+instance's feature usage, from idea to production. It looks at your usage in the
+past 30 days, averaged over the number of active users in that time period. It also
+provides a lead score per feature, which is calculated based on GitLab's analysis
+of top performing instances, based on [usage ping data][ping] that GitLab has
+collected. Your score is compared to the lead score, expressed as a percentage.
+The overall index score is an average over all your feature scores.
+
+![ConvDev index](img/convdev_index.png)
+
+The page also provides helpful links to articles and GitLab docs, to help you
+improve your scores.
+
+Your GitLab instance's usage ping must be activated in order to use this feature.
+Usage ping data is aggregated on GitLab's servers for analysis. Your usage
+information is **not sent** to any other GitLab instances.
+
+If you have just started using GitLab, it may take a few weeks for data to be
+collected before this feature is available.
+
+[ce-30469]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30469
+[ping]: ../admin_area/settings/usage_statistics.md#usage-ping
diff --git a/doc/user/instance_statistics/img/cohorts.png b/doc/user/instance_statistics/img/cohorts.png
new file mode 100644
index 00000000000..12e839e7cd2
--- /dev/null
+++ b/doc/user/instance_statistics/img/cohorts.png
Binary files differ
diff --git a/doc/user/instance_statistics/img/convdev_index.png b/doc/user/instance_statistics/img/convdev_index.png
new file mode 100644
index 00000000000..191295c918b
--- /dev/null
+++ b/doc/user/instance_statistics/img/convdev_index.png
Binary files differ
diff --git a/doc/user/instance_statistics/img/instance_statistics_button.png b/doc/user/instance_statistics/img/instance_statistics_button.png
new file mode 100644
index 00000000000..6104321b1a6
--- /dev/null
+++ b/doc/user/instance_statistics/img/instance_statistics_button.png
Binary files differ
diff --git a/doc/user/instance_statistics/index.md b/doc/user/instance_statistics/index.md
new file mode 100644
index 00000000000..a4eca89b7fe
--- /dev/null
+++ b/doc/user/instance_statistics/index.md
@@ -0,0 +1,19 @@
+# Instance statistics
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/41416)
+in GitLab 11.2.
+
+Instance statistics gives users or admins access to instance-wide analytics.
+They are accessible to all users by default (GitLab admins can restrict its
+visibility in the [admin area](../admin_area/settings/usage_statistics.md)),
+and can be accessed via the top bar.
+
+![Instance Statistics button](img/instance_statistics_button.png)
+
+For the statistics to show up, [usage ping must be enabled](../admin_area/settings/usage_statistics.md#usage-ping)
+by an admin in the admin settings area.
+
+There are two kinds of statistics:
+
+- [Conversational Development (ConvDev) Index](convdev.md): Provides an overview of your entire instance's feature usage.
+- [User Cohorts](user_cohorts.md): Display the monthly cohorts of new users and their activities over time.
diff --git a/doc/user/instance_statistics/user_cohorts.md b/doc/user/instance_statistics/user_cohorts.md
new file mode 100644
index 00000000000..70d5912dc4e
--- /dev/null
+++ b/doc/user/instance_statistics/user_cohorts.md
@@ -0,0 +1,27 @@
+# Cohorts
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/23361)
+in GitLab 9.1.
+
+As a benefit of having the [usage ping active](../admin_area/settings/usage_statistics.md),
+GitLab lets you analyze the users' activities over time of your GitLab installation.
+
+## Overview
+
+How do we read the user cohorts table? Let's take an example with the following
+user cohorts.
+
+![User cohort example](img/cohorts.png)
+
+For the cohort of Jan 2018, 15 users have been added on this server and have
+been active since this month. One month later, in Feb 2018, all 15 users are
+still active. 6 months later (Month 6, July), we can see 10 users from this cohort
+are active, or 66% of the original cohort of 15 that joined in January.
+
+The Inactive users column shows the number of users who have been added during
+the month, but who have never actually had any activity in the instance.
+
+How do we measure the activity of users? GitLab considers a user active if:
+
+* the user signs in
+* the user has Git activity (whether push or pull).
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 6856544ae1b..6203561265b 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -234,7 +234,12 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji
Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
- Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+ Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
+
+ On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+
+ Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
+
Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
@@ -246,7 +251,13 @@ If you are new to this, don't be :fearful:. You can easily join the emoji :famil
Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
-Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
+
+On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+
+Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
+
+
### Special GitLab References
diff --git a/doc/user/project/import/img/manifest_upload.png b/doc/user/project/import/img/manifest_upload.png
deleted file mode 100644
index d6bf4b157dd..00000000000
--- a/doc/user/project/import/img/manifest_upload.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index b55435e5b4f..4ea35a30bbf 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -11,7 +11,7 @@
1. [From SVN](svn.md)
1. [From TFS](tfs.md)
1. [From repo by URL](repo_by_url.md)
-1. [By uploading a manifest file](manifest.md)
+1. [By uploading a manifest file (AOSP)](manifest.md)
In addition to the specific migration documentation above, you can import any
Git repository via HTTP from the New Project page. Be aware that if the
diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md
index 06171f11e12..24bf6541a9d 100644
--- a/doc/user/project/import/manifest.md
+++ b/doc/user/project/import/manifest.md
@@ -1,36 +1,31 @@
# Import multiple repositories by uploading a manifest file
-GitLab allows you to import all the required git repositories
-based a manifest file like the one used by the [Android repository](https://android.googlesource.com/platform/manifest/+/2d6f081a3b05d8ef7a2b1b52b0d536b2b74feab4/default.xml).
-This feature can be very handy when you need to import a project with many repositories like Android Open Source Project (AOSP).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28811) in
+GitLab 11.2.
+GitLab allows you to import all the required Git repositories
+based on a manifest file like the one used by the
+[Android repository](https://android.googlesource.com/platform/manifest/+/2d6f081a3b05d8ef7a2b1b52b0d536b2b74feab4/default.xml).
+This feature can be very handy when you need to import a project with many
+repositories like the Android Open Source Project (AOSP).
->**Note:**
-This feature requires [subgroups](../../group/subgroups/index.md) to be supported by your database.
+## Requirements
-You can do it by following next steps:
+GitLab must be using PostgreSQL for its database, since
+[subgroups](../../group/subgroups/index.md) are needed for the manifest import
+to work.
-1. From your GitLab dashboard click **New project**
-1. Switch to the **Import project** tab
-1. Click on the **Manifest file** button
-1. Provide GitLab with a manifest xml file
-1. Select a group you want to import to (you need to create a group first if you don't have one)
-1. Click **List available repositories**
-1. You will be redirected to the import status page with projects list based on manifest file
-1. Check the list and click 'Import all repositories' to start import.
-
-![Manifest upload](img/manifest_upload.png)
+Read more about the [database requirements](../../../install/requirements.md#database).
-![Manifest status](img/manifest_status.png)
+## Manifest format
-### Manifest format
+A manifest must be an XML file. There must be one `remote` tag with a `review`
+attribute that contains a URL to a Git server, and each `project` tag must have
+a `name` and `path` attribute. GitLab will then build the URL to the repository
+by combining the URL from the `remote` tag with a project name.
+A path attribute will be used to represent the project path in GitLab.
-A manifest must be an XML file. There must be one `remote` tag with `review` attribute
-that contains a URL to a git server. Each `project` tag must have `name` and `path` attribute.
-GitLab will build URL to the repository by combining URL from `remote` tag with a project name.
-A path attribute will be used to represent project path in GitLab system.
-
-Below is a valid example of manifest file.
+Below is a valid example of a manifest file:
```xml
<manifest>
@@ -41,9 +36,24 @@ Below is a valid example of manifest file.
</manifest>
```
-As result next projects will be created:
+As a result, the following projects will be created:
| GitLab | Import URL |
|---|---|
-| https://gitlab/YOUR_GROUP/build/make | https://android-review.googlesource.com/platform/build |
-| https://gitlab/YOUR_GROUP/build/blueprint | https://android-review.googlesource.com/platform/build/blueprint |
+| https://gitlab.com/YOUR_GROUP/build/make | https://android-review.googlesource.com/platform/build |
+| https://gitlab.com/YOUR_GROUP/build/blueprint | https://android-review.googlesource.com/platform/build/blueprint |
+
+## Importing the repositories
+
+You can start the import with:
+
+1. From your GitLab dashboard click **New project**
+1. Switch to the **Import project** tab
+1. Click on the **Manifest file** button
+1. Provide GitLab with a manifest xml file
+1. Select a group you want to import to (you need to create a group first if you don't have one)
+1. Click **List available repositories**. At this point, you will be redirected
+ to the import status page with projects list based on the manifest file.
+1. Check the list and click **Import all repositories** to start the import.
+
+ ![Manifest status](img/manifest_status.png)
diff --git a/doc/user/project/integrations/hangouts_chat.md b/doc/user/project/integrations/hangouts_chat.md
index 6ab44420a10..47525617d95 100644
--- a/doc/user/project/integrations/hangouts_chat.md
+++ b/doc/user/project/integrations/hangouts_chat.md
@@ -1,5 +1,7 @@
# Hangouts Chat service
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/43756) in GitLab 11.2.
+
The Hangouts Chat service sends notifications from GitLab to the room for which the webhook was created.
## On Hangouts Chat
diff --git a/doc/user/project/pages/getting_started_part_three.md b/doc/user/project/pages/getting_started_part_three.md
index 61af1d2ab27..e1eede8bbed 100644
--- a/doc/user/project/pages/getting_started_part_three.md
+++ b/doc/user/project/pages/getting_started_part_three.md
@@ -1,5 +1,5 @@
---
-last_updated: 2018-02-16
+last_updated: 2018-08-16
author: Marcia Ramos
author_gitlab: marcia
level: beginner
@@ -28,7 +28,7 @@ Let's start from the beginning with [DNS records](#dns-records).
If you already know how they work and want to skip the introduction to DNS,
you may be interested in skipping it until the [TL;DR](#tl-dr) section below.
-## DNS Records
+### DNS Records
A Domain Name System (DNS) web service routes visitors to websites
by translating domain names (such as `www.example.com`) into the
@@ -64,22 +64,28 @@ for the most popular hosting services:
If your hosting service is not listed above, you can just try to
search the web for `how to add dns record on <my hosting service>`.
-### DNS A record
+#### DNS A record
In case you want to point a root domain (`example.com`) to your
GitLab Pages site, deployed to `namespace.gitlab.io`, you need to
log into your domain's admin control panel and add a DNS `A` record
pointing your domain to Pages' server IP address. For projects on
-GitLab.com, this IP is `52.167.214.135`. For projects living in
+GitLab.com, this IP is `35.185.44.232`. For projects living in
other GitLab instances (CE or EE), please contact your sysadmin
asking for this information (which IP address is Pages server
running on your instance).
**Practical Example:**
-![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated.png)
+![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated_2018.png)
-### DNS CNAME record
+NOTE: **Note:**
+Note that if you use your root domain for your GitLab Pages website **only**, and if
+your domain registrar supports this feature, you can add a DNS apex `CNAME`
+record instead of an `A` record. The main advantage of doing so is that when GitLab Pages
+IP on GitLab.com changes for whatever reason, you don't need to update your `A` record.
+
+#### DNS CNAME record
In case you want to point a subdomain (`hello-world.example.com`)
to your GitLab Pages site initially deployed to `namespace.gitlab.io`,
@@ -112,14 +118,14 @@ If the domain has multiple uses (e.g., you host email on it as well):
| From | DNS Record | To |
| ---- | ---------- | -- |
-| domain.com | A | 52.167.214.135 |
+| domain.com | A | 35.185.44.232 |
| domain.com | TXT | gitlab-pages-verification-code=00112233445566778899aabbccddeeff |
If the domain is dedicated to GitLab Pages use and no other services run on it:
| From | DNS Record | To |
| ---- | ---------- | -- |
-| subdomain.domain.com | CNAME | gitlab.io |
+| subdomain.domain.com | CNAME | namespace.gitlab.io |
| _gitlab-pages-verification-code.subdomain.domain.com | TXT | gitlab-pages-verification-code=00112233445566778899aabbccddeeff |
> **Notes**:
@@ -129,9 +135,11 @@ If the domain is dedicated to GitLab Pages use and no other services run on it:
> - **Do not** add any special chars after the default Pages
domain. E.g., **do not** point your `subdomain.domain.com` to
`namespace.gitlab.io.` or `namespace.gitlab.io/`.
-> - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) from `104.208.235.32` to `52.167.214.135`.
+> - GitLab Pages IP on GitLab.com [was changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) in 2017
+> - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2018/07/19/gcp-move-update/#gitlab-pages-and-custom-domains)
+from `52.167.214.135` to `35.185.44.232` in 2018
-## Add your custom domain to GitLab Pages settings
+### Add your custom domain to GitLab Pages settings
Once you've set the DNS record, you'll need navigate to your project's
**Setting > Pages** and click **+ New domain** to add your custom domain to
@@ -165,6 +173,18 @@ will fail and attempts to visit your domain will respond with a 404.
Read through the [general documentation on GitLab Pages](introduction.md#add-a-custom-domain-to-your-pages-website) to learn more about adding
custom domains to GitLab Pages sites.
+### Redirecting `www.domain.com` to `domain.com` with Cloudflare
+
+If you use Cloudflare, you can redirect `www` to `domain.com` without the need of adding both
+`www.domain.com` and `domain.com` to GitLab. This happens due to a [Cloudflare feature that creates
+a 301 redirect as a "page rule"](https://gitlab.com/gitlab-org/gitlab-ce/issues/48848#note_87314849) for redirecting `www.domain.com` to `domain.com`. In this case,
+you can use the following setup:
+
+- In Cloudflare, create a DNS `A` record pointing `domain.com` to `35.185.44.232`
+- In GitLab, add the domain to GitLab Pages
+- In Cloudflare, create a DNS `TXT` record to verify your domain
+- In Cloudflare, create a DNS `CNAME` record poiting `www` to `domain.com`
+
## SSL/TLS Certificates
Every GitLab Pages project on GitLab.com will be available under
diff --git a/doc/user/project/pages/img/dns_add_new_a_record_example_updated.png b/doc/user/project/pages/img/dns_add_new_a_record_example_updated.png
deleted file mode 100644
index 2661a497b91..00000000000
--- a/doc/user/project/pages/img/dns_add_new_a_record_example_updated.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png b/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png
new file mode 100644
index 00000000000..fa72df66587
--- /dev/null
+++ b/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png
Binary files differ
diff --git a/doc/user/project/repository/img/repository_languages.png b/doc/user/project/repository/img/repository_languages.png
new file mode 100644
index 00000000000..d9fb1278e06
--- /dev/null
+++ b/doc/user/project/repository/img/repository_languages.png
Binary files differ
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 704c1777e62..1c3915a5fdd 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -155,6 +155,16 @@ The repository graph displays visually the Git flow strategy used in that reposi
Find it under your project's **Repository > Graph**.
+## Repository Languages
+
+For the default branch of each repository, GitLab will determine what programming languages
+were used and display this on the projects pages.
+
+![Repository Languages bar](img/repository_languages.png)
+
+Not all files are detected, among others; documentation,
+vendored code, and most markup languages are excluded.
+
## Compare
Select branches to compare and view the changes inline:
diff --git a/lib/tasks/gitlab/traces.rake b/lib/tasks/gitlab/traces.rake
index ddcca69711f..5a232091a7e 100644
--- a/lib/tasks/gitlab/traces.rake
+++ b/lib/tasks/gitlab/traces.rake
@@ -18,5 +18,22 @@ namespace :gitlab do
logger.info("Scheduled #{job_ids.count} jobs. From #{job_ids.min} to #{job_ids.max}")
end
end
+
+ task migrate: :environment do
+ logger = Logger.new(STDOUT)
+ logger.info('Starting transfer of job traces')
+
+ Ci::Build.joins(:project)
+ .with_archived_trace_stored_locally
+ .find_each(batch_size: 10) do |build|
+ begin
+ build.job_artifacts_trace.file.migrate!(ObjectStorage::Store::REMOTE)
+
+ logger.info("Transferred job trace of #{build.id} to object storage")
+ rescue => e
+ logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
+ end
+ end
+ end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f00dce06bac..1f61ac872f8 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -235,6 +235,9 @@ msgstr ""
msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
msgstr ""
+msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes"
+msgstr ""
+
msgid "<strong>%{group_name}</strong> group members"
msgstr ""
@@ -346,6 +349,12 @@ msgstr ""
msgid "Admin area"
msgstr ""
+msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "AdminArea| You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -364,6 +373,9 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
+msgstr ""
+
msgid "AdminProjects|Delete"
msgstr ""
@@ -499,7 +511,7 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: ${details}"
+msgid "An error occurred while importing project: %{details}"
msgstr ""
msgid "An error occurred while loading commit signatures"
@@ -805,6 +817,9 @@ msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
+msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored."
+msgstr ""
+
msgid "Badges|Your badges"
msgstr ""
@@ -1248,6 +1263,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|API URL"
msgstr ""
@@ -1257,12 +1275,21 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
+msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
+msgstr ""
+
msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occured while trying to fetch your projects: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occured while trying to fetch zone machine types: %{error}"
+msgstr ""
+
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgstr ""
+
msgid "ClusterIntegration|Applications"
msgstr ""
@@ -1329,6 +1356,9 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
+msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgstr ""
+
msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
@@ -1341,6 +1371,9 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
+msgid "ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. Tiller runs inside of your Kubernetes Cluster, and manages releases of your charts."
+msgstr ""
+
msgid "ClusterIntegration|Hide"
msgstr ""
@@ -1350,9 +1383,15 @@ msgstr ""
msgid "ClusterIntegration|Ingress IP Address"
msgstr ""
+msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
+msgstr ""
+
msgid "ClusterIntegration|Install"
msgstr ""
+msgid "ClusterIntegration|Install applications on your Kubernetes cluster. Read more about %{helpLink}"
+msgstr ""
+
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -1371,6 +1410,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub"
msgstr ""
+msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1458,6 +1500,9 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
+msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgstr ""
+
msgid "ClusterIntegration|Project namespace"
msgstr ""
@@ -1467,6 +1512,9 @@ msgstr ""
msgid "ClusterIntegration|Prometheus"
msgstr ""
+msgid "ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications."
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
@@ -1479,6 +1527,9 @@ msgstr ""
msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
+msgid "ClusterIntegration|Replace this with your own hostname if you want. If you do so, point hostname to Ingress IP Address from above."
+msgstr ""
+
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
@@ -1533,6 +1584,9 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
+msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
+
msgid "ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application."
msgstr ""
@@ -1551,6 +1605,9 @@ msgstr ""
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
+msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
@@ -1963,6 +2020,9 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr ""
+
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -2361,6 +2421,9 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
+msgid "Environments|Environments are places where code gets deployed, such as staging or production."
+msgstr ""
+
msgid "Environments|Job"
msgstr ""
@@ -2373,6 +2436,9 @@ msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
+msgstr ""
+
msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
msgstr ""
@@ -3139,12 +3205,21 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Job|Are you sure you want to erase this job?"
+msgstr ""
+
msgid "Job|Browse"
msgstr ""
+msgid "Job|Complete Raw"
+msgstr ""
+
msgid "Job|Download"
msgstr ""
+msgid "Job|Erase job log"
+msgstr ""
+
msgid "Job|Job artifacts"
msgstr ""
@@ -3157,12 +3232,24 @@ msgstr ""
msgid "Job|Keep"
msgstr ""
+msgid "Job|Scroll to bottom"
+msgstr ""
+
+msgid "Job|Scroll to top"
+msgstr ""
+
+msgid "Job|Show complete raw"
+msgstr ""
+
msgid "Job|The artifacts were removed"
msgstr ""
msgid "Job|The artifacts will be removed"
msgstr ""
+msgid "Job|This job is stuck, because the project doesn't have any runners online assigned to it."
+msgstr ""
+
msgid "Jul"
msgstr ""
@@ -3244,6 +3331,9 @@ msgstr ""
msgid "Labels|Promote Label"
msgstr ""
+msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -3303,6 +3393,11 @@ msgstr ""
msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
msgstr ""
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "LinkedIn"
msgstr ""
@@ -3339,6 +3434,9 @@ msgstr ""
msgid "Lock not found"
msgstr ""
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Lock to current projects"
msgstr ""
@@ -3465,6 +3563,9 @@ msgstr ""
msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr ""
+msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -3513,6 +3614,12 @@ msgstr ""
msgid "Milestones"
msgstr ""
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} from this project and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} from this project. %{milestoneTitle} is not currently used in any issues or merge requests."
+msgstr ""
+
msgid "Milestones|Delete milestone"
msgstr ""
@@ -3531,6 +3638,9 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}. Existing project milestones with the same title will be merged. This action cannot be reversed."
+msgstr ""
+
msgid "Mirror a repository"
msgstr ""
@@ -3701,6 +3811,9 @@ msgstr ""
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No container images stored for this project. Add one by following the instructions above."
+msgstr ""
+
msgid "No due date"
msgstr ""
@@ -3746,6 +3859,12 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not all comments are displayed because you're comparing two versions of the diff."
+msgstr ""
+
+msgid "Not all comments are displayed because you're viewing an old version of the diff."
+msgstr ""
+
msgid "Not allowed to merge"
msgstr ""
@@ -3857,6 +3976,11 @@ msgstr ""
msgid "OfSearchInADropdown|Filter"
msgstr ""
+msgid "One more item"
+msgid_plural "%d more items"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
@@ -3965,6 +4089,9 @@ msgstr ""
msgid "Pending"
msgstr ""
+msgid "People without permission will never get a notification and won't be able to comment."
+msgstr ""
+
msgid "Per job. If a job passes this threshold, it will be marked as failed"
msgstr ""
@@ -4067,6 +4194,9 @@ msgstr ""
msgid "Pipelines|Clear Runner Caches"
msgstr ""
+msgid "Pipelines|Continuous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment."
+msgstr ""
+
msgid "Pipelines|Get started with Pipelines"
msgstr ""
@@ -4088,6 +4218,9 @@ msgstr ""
msgid "Pipelines|There are currently no pipelines."
msgstr ""
+msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
@@ -4202,6 +4335,12 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
+msgstr ""
+
+msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
@@ -4376,6 +4515,9 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
+msgid "ProjectsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "PrometheusDashboard|Time"
msgstr ""
@@ -4502,6 +4644,11 @@ msgstr ""
msgid "Reference:"
msgstr ""
+msgid "Refreshing in a second to show the updated status..."
+msgid_plural "Refreshing in %d seconds to show the updated status..."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Register / Sign In"
msgstr ""
@@ -4968,6 +5115,12 @@ msgstr ""
msgid "Something went wrong on our end. Please try again!"
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
+msgid "Something went wrong trying to change the locked state of this %{issuableDisplayName}"
+msgstr ""
+
msgid "Something went wrong when toggling the button"
msgstr ""
@@ -5306,6 +5459,9 @@ msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
+msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
+msgstr ""
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
@@ -5336,6 +5492,9 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -5438,6 +5597,9 @@ msgstr ""
msgid "This application will be able to:"
msgstr ""
+msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -5489,6 +5651,12 @@ msgstr ""
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is stuck, because you don't have any active runners online with any of these tags assigned to them:"
+msgstr ""
+
+msgid "This job is stuck, because you don't have any active runners that can run this job."
+msgstr ""
+
msgid "This job requires a manual action"
msgstr ""
@@ -5498,6 +5666,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This option is disabled as you don't have write permissions for the current branch"
+msgstr ""
+
msgid "This option is disabled while you still have unstaged changes"
msgstr ""
@@ -5513,6 +5684,9 @@ msgstr ""
msgid "This project does not belong to a group and can therefore not make use of group Runners."
msgstr ""
+msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
+msgstr ""
+
msgid "This repository"
msgstr ""
@@ -5808,6 +5982,9 @@ msgstr ""
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
@@ -6321,6 +6498,12 @@ msgstr ""
msgid "command line instructions"
msgstr ""
+msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
+msgstr ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "connecting"
msgstr ""
@@ -6424,6 +6607,9 @@ msgstr ""
msgid "mrWidget|Failed to load deployment statistics"
msgstr ""
+msgid "mrWidget|Fast-forward merge is not possible. To merge this request, first rebase locally."
+msgstr ""
+
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
@@ -6451,9 +6637,15 @@ msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr ""
+msgid "mrWidget|Pipeline blocked. The pipeline for this merge request requires a manual action to proceed"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
+msgid "mrWidget|Ready to be merged automatically. Ask someone with write access to this repository to merge this request"
+msgstr ""
+
msgid "mrWidget|Refresh"
msgstr ""
@@ -6475,6 +6667,9 @@ msgstr ""
msgid "mrWidget|Resolve conflicts"
msgstr ""
+msgid "mrWidget|Resolve these conflicts or ask someone with write access to this repository to merge it locally"
+msgstr ""
+
msgid "mrWidget|Revert"
msgstr ""
@@ -6493,6 +6688,12 @@ msgstr ""
msgid "mrWidget|The changes will be merged into"
msgstr ""
+msgid "mrWidget|The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure"
+msgstr ""
+
+msgid "mrWidget|The source branch HEAD has recently changed. Please reload the page and review the changes before merging"
+msgstr ""
+
msgid "mrWidget|The source branch has been removed"
msgstr ""
diff --git a/package.json b/package.json
index 975dd2619d7..f5fc759f76e 100644
--- a/package.json
+++ b/package.json
@@ -126,6 +126,8 @@
"eslint-plugin-jasmine": "^2.1.0",
"eslint-plugin-promise": "^3.8.0",
"eslint-plugin-vue": "^4.5.0",
+ "gettext-extractor": "^3.3.2",
+ "gettext-extractor-vue": "^4.0.1",
"ignore": "^3.3.7",
"istanbul": "^0.4.5",
"jasmine-core": "^2.9.0",
diff --git a/scripts/frontend/extract_gettext_all.js b/scripts/frontend/extract_gettext_all.js
new file mode 100644
index 00000000000..af8cc4c3341
--- /dev/null
+++ b/scripts/frontend/extract_gettext_all.js
@@ -0,0 +1,72 @@
+const argumentsParser = require('commander');
+
+const { GettextExtractor, JsExtractors } = require('gettext-extractor');
+const {
+ decorateJSParserWithVueSupport,
+ decorateExtractorWithHelpers,
+} = require('gettext-extractor-vue');
+const ensureSingleLine = require('../../app/assets/javascripts/locale/ensure_single_line.js');
+
+const arguments = argumentsParser
+ .option('-f, --file <file>', 'Extract message from one single file')
+ .option('-a, --all', 'Extract message from all js/vue files')
+ .parse(process.argv);
+
+const extractor = decorateExtractorWithHelpers(new GettextExtractor());
+
+extractor.addMessageTransformFunction(ensureSingleLine);
+
+const jsParser = extractor.createJsParser([
+ // Place all the possible expressions to extract here:
+ JsExtractors.callExpression('__', {
+ arguments: {
+ text: 0,
+ },
+ }),
+ JsExtractors.callExpression('n__', {
+ arguments: {
+ text: 0,
+ textPlural: 1,
+ },
+ }),
+ JsExtractors.callExpression('s__', {
+ arguments: {
+ text: 0,
+ },
+ }),
+]);
+
+const vueParser = decorateJSParserWithVueSupport(jsParser);
+
+function printJson() {
+ const messages = extractor.getMessages().reduce((result, message) => {
+ let text = message.text;
+ if (message.textPlural) {
+ text += `\u0000${message.textPlural}`;
+ }
+
+ message.references.forEach(reference => {
+ const filename = reference.replace(/:\d+$/, '');
+
+ if (!Array.isArray(result[filename])) {
+ result[filename] = [];
+ }
+
+ result[filename].push([text, reference]);
+ });
+
+ return result;
+ }, {});
+
+ console.log(JSON.stringify(messages));
+}
+
+if (arguments.file) {
+ vueParser.parseFile(arguments.file).then(() => printJson());
+} else if (arguments.all) {
+ vueParser.parseFilesGlob('{ee/app,app}/assets/javascripts/**/*.{js,vue}').then(() => printJson());
+} else {
+ console.warn('ERROR: Please use the script correctly:');
+ arguments.outputHelp();
+ process.exit(1);
+}
diff --git a/spec/features/instance_statistics/cohorts_spec.rb b/spec/features/instance_statistics/cohorts_spec.rb
index 81fc5eff980..573f8600be1 100644
--- a/spec/features/instance_statistics/cohorts_spec.rb
+++ b/spec/features/instance_statistics/cohorts_spec.rb
@@ -12,4 +12,12 @@ describe 'Cohorts page' do
expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0")
end
+
+ it 'shows usage data', :js do
+ visit instance_statistics_cohorts_path
+
+ wait_for_requests
+
+ expect(find('.js-syntax-highlight').text).not_to eq('')
+ end
end
diff --git a/spec/features/instance_statistics/conversational_development_index_spec.rb b/spec/features/instance_statistics/conversational_development_index_spec.rb
index d441a7a5af9..a6c16b6a2a3 100644
--- a/spec/features/instance_statistics/conversational_development_index_spec.rb
+++ b/spec/features/instance_statistics/conversational_development_index_spec.rb
@@ -5,6 +5,16 @@ describe 'Conversational Development Index' do
sign_in(create(:admin))
end
+ it 'has dismissable intro callout', :js do
+ visit instance_statistics_conversational_development_index_index_path
+
+ expect(page).to have_content 'Introducing Your Conversational Development Index'
+
+ find('.js-close-callout').click
+
+ expect(page).not_to have_content 'Introducing Your Conversational Development Index'
+ end
+
context 'when usage ping is disabled' do
it 'shows empty state' do
stub_application_setting(usage_ping_enabled: false)
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index de261d36c61..290600cf995 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -200,6 +200,15 @@ describe('Board list component', () => {
});
});
+ it('does not load issues if already loading', () => {
+ component.list.nextPage = spyOn(component.list, 'nextPage').and.returnValue(new Promise(() => {}));
+
+ component.onScroll();
+ component.onScroll();
+
+ expect(component.list.nextPage).toHaveBeenCalledTimes(1);
+ });
+
it('shows loading more spinner', (done) => {
component.showCount = true;
component.list.loadingMore = true;
diff --git a/spec/javascripts/jobs/components/job_log_controllers_spec.js b/spec/javascripts/jobs/components/job_log_controllers_spec.js
new file mode 100644
index 00000000000..416dfab8a48
--- /dev/null
+++ b/spec/javascripts/jobs/components/job_log_controllers_spec.js
@@ -0,0 +1,217 @@
+import Vue from 'vue';
+import component from '~/jobs/components/job_log_controllers.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Job log controllers', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('Truncate information', () => {
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ rawTracePath: '/raw',
+ canEraseJob: true,
+ size: 511952,
+ canScrollToTop: true,
+ canScrollToBottom: true,
+ });
+ });
+
+ it('renders size information', () => {
+ expect(vm.$el.querySelector('.js-truncated-info').textContent).toContain('499.95 KiB');
+ });
+
+ it('renders link to raw trace', () => {
+ expect(vm.$el.querySelector('.js-raw-link').getAttribute('href')).toEqual('/raw');
+ });
+
+ });
+
+ describe('links section', () => {
+ describe('with raw trace path', () => {
+ it('renders raw trace link', () => {
+ vm = mountComponent(Component, {
+ rawTracePath: '/raw',
+ canEraseJob: true,
+ size: 511952,
+ canScrollToTop: true,
+ canScrollToBottom: true,
+ });
+
+ expect(vm.$el.querySelector('.js-raw-link-controller').getAttribute('href')).toEqual('/raw');
+ });
+ });
+
+ describe('without raw trace path', () => {
+ it('does not render raw trace link', () => {
+ vm = mountComponent(Component, {
+ canEraseJob: true,
+ size: 511952,
+ canScrollToTop: true,
+ canScrollToBottom: true,
+ });
+
+ expect(vm.$el.querySelector('.js-raw-link-controller')).toBeNull();
+ });
+ });
+
+ describe('when is erasable', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ rawTracePath: '/raw',
+ canEraseJob: true,
+ size: 511952,
+ canScrollToTop: true,
+ canScrollToBottom: true,
+ });
+ });
+
+ it('renders erase job button', () => {
+ expect(vm.$el.querySelector('.js-erase-link')).not.toBeNull();
+ });
+
+ describe('on click', () => {
+ describe('when user confirms action', () => {
+ it('emits eraseJob event', () => {
+ spyOn(window, 'confirm').and.returnValue(true);
+ spyOn(vm, '$emit');
+
+ vm.$el.querySelector('.js-erase-link').click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('eraseJob');
+ });
+ });
+
+ describe('when user does not confirm action', () => {
+ it('does not emit eraseJob event', () => {
+ spyOn(window, 'confirm').and.returnValue(false);
+ spyOn(vm, '$emit');
+
+ vm.$el.querySelector('.js-erase-link').click();
+
+ expect(vm.$emit).not.toHaveBeenCalledWith('eraseJob');
+ });
+ });
+ });
+ });
+
+ describe('when it is not erasable', () => {
+ it('does not render erase button', () => {
+ vm = mountComponent(Component, {
+ rawTracePath: '/raw',
+ canEraseJob: false,
+ size: 511952,
+ canScrollToTop: true,
+ canScrollToBottom: true,
+ });
+
+ expect(vm.$el.querySelector('.js-erase-link')).toBeNull();
+ });
+ });
+ });
+
+ describe('scroll buttons', () => {
+ describe('scroll top button', () => {
+ describe('when user can scroll top', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ rawTracePath: '/raw',
+ canEraseJob: true,
+ size: 511952,
+ canScrollToTop: true,
+ canScrollToBottom: true,
+ });
+ });
+
+ it('renders enabled scroll top button', () => {
+ expect(vm.$el.querySelector('.js-scroll-top').getAttribute('disabled')).toBeNull();
+ });
+
+ it('emits scrollJobLogTop event on click', () => {
+ spyOn(vm, '$emit');
+ vm.$el.querySelector('.js-scroll-top').click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('scrollJobLogTop');
+ });
+ });
+
+ describe('when user can not scroll top', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ rawTracePath: '/raw',
+ canEraseJob: true,
+ size: 511952,
+ canScrollToTop: false,
+ canScrollToBottom: true,
+ });
+ });
+
+ it('renders disabled scroll top button', () => {
+ expect(vm.$el.querySelector('.js-scroll-top').getAttribute('disabled')).toEqual('disabled');
+ });
+
+ it('does not emit scrollJobLogTop event on click', () => {
+ spyOn(vm, '$emit');
+ vm.$el.querySelector('.js-scroll-top').click();
+
+ expect(vm.$emit).not.toHaveBeenCalledWith('scrollJobLogTop');
+ });
+ });
+ });
+
+ describe('scroll bottom button', () => {
+ describe('when user can scroll bottom', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ rawTracePath: '/raw',
+ canEraseJob: true,
+ size: 511952,
+ canScrollToTop: true,
+ canScrollToBottom: true,
+ });
+ });
+
+ it('renders enabled scroll bottom button', () => {
+ expect(vm.$el.querySelector('.js-scroll-bottom').getAttribute('disabled')).toBeNull();
+ });
+
+ it('emits scrollJobLogBottom event on click', () => {
+ spyOn(vm, '$emit');
+ vm.$el.querySelector('.js-scroll-bottom').click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('scrollJobLogBottom');
+ });
+ });
+
+ describe('when user can not scroll bottom', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ rawTracePath: '/raw',
+ canEraseJob: true,
+ size: 511952,
+ canScrollToTop: true,
+ canScrollToBottom: false,
+ });
+ });
+
+ it('renders disabled scroll bottom button', () => {
+ expect(vm.$el.querySelector('.js-scroll-bottom').getAttribute('disabled')).toEqual('disabled');
+
+ });
+
+ it('does not emit scrollJobLogBottom event on click', () => {
+ spyOn(vm, '$emit');
+ vm.$el.querySelector('.js-scroll-bottom').click();
+
+ expect(vm.$emit).not.toHaveBeenCalledWith('scrollJobLogBottom');
+ });
+ });
+ });
+ });
+});
+
diff --git a/spec/javascripts/locale/ensure_single_line_spec.js b/spec/javascripts/locale/ensure_single_line_spec.js
new file mode 100644
index 00000000000..bbefa8f40f3
--- /dev/null
+++ b/spec/javascripts/locale/ensure_single_line_spec.js
@@ -0,0 +1,35 @@
+import ensureSingleLine from '~/locale/ensure_single_line';
+
+describe('locale', () => {
+ describe('ensureSingleLine', () => {
+ it('should remove newlines at the start of the string', () => {
+ const result = 'Test';
+ expect(ensureSingleLine(`\n${result}`)).toBe(result);
+ expect(ensureSingleLine(`\t\n\t${result}`)).toBe(result);
+ expect(ensureSingleLine(`\r\n${result}`)).toBe(result);
+ expect(ensureSingleLine(`\r\n ${result}`)).toBe(result);
+ expect(ensureSingleLine(`\r ${result}`)).toBe(result);
+ expect(ensureSingleLine(` \n ${result}`)).toBe(result);
+ });
+
+ it('should remove newlines at the end of the string', () => {
+ const result = 'Test';
+ expect(ensureSingleLine(`${result}\n`)).toBe(result);
+ expect(ensureSingleLine(`${result}\t\n\t`)).toBe(result);
+ expect(ensureSingleLine(`${result}\r\n`)).toBe(result);
+ expect(ensureSingleLine(`${result}\r`)).toBe(result);
+ expect(ensureSingleLine(`${result} \r`)).toBe(result);
+ expect(ensureSingleLine(`${result} \r\n `)).toBe(result);
+ });
+
+ it('should replace newlines in the middle of the string with a single space', () => {
+ const result = 'Test';
+ expect(ensureSingleLine(`${result}\n${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result}\t\n\t${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result}\r\n${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result}\r${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result} \r${result}`)).toBe(`${result} ${result}`);
+ expect(ensureSingleLine(`${result} \r\n ${result}`)).toBe(`${result} ${result}`);
+ });
+ });
+});
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index 67f6a9629d9..0423fcb6ec4 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -1104,7 +1104,7 @@ export const collapsedSystemNotes = [
resolvable: false,
noteable_iid: 12,
note: 'changed the description',
- note_html: '\n <p dir="auto">changed the description 2 times within 1 minute </p>',
+ note_html: ' <p dir="auto">changed the description 2 times within 1 minute </p>',
current_user: { can_edit: false, can_award_emoji: true },
resolved: false,
resolved_by: null,
diff --git a/spec/javascripts/vue_shared/translate_spec.js b/spec/javascripts/vue_shared/translate_spec.js
index cbb3cbdff46..adb5ff682f0 100644
--- a/spec/javascripts/vue_shared/translate_spec.js
+++ b/spec/javascripts/vue_shared/translate_spec.js
@@ -1,88 +1,249 @@
import Vue from 'vue';
-import Translate from '~/vue_shared/translate';
+import Jed from 'jed';
-Vue.use(Translate);
+import locale from '~/locale';
+import Translate from '~/vue_shared/translate';
+import { trimText } from 'spec/helpers/vue_component_helper';
describe('Vue translate filter', () => {
let el;
+ const createTranslationMock = (key, ...translations) => {
+ const fakeLocale = new Jed({
+ domain: 'app',
+ locale_data: {
+ app: {
+ '': {
+ domain: 'app',
+ lang: 'vo',
+ plural_forms: 'nplurals=2; plural=(n != 1);',
+ },
+ [key]: translations,
+ },
+ },
+ });
+
+ // eslint-disable-next-line no-underscore-dangle
+ locale.__Rewire__('locale', fakeLocale);
+ };
+
+ afterEach(() => {
+ // eslint-disable-next-line no-underscore-dangle
+ locale.__ResetDependency__('locale');
+ });
+
beforeEach(() => {
+ Vue.use(Translate);
+
el = document.createElement('div');
document.body.appendChild(el);
});
- it('translate single text', (done) => {
- const comp = new Vue({
+ it('translate singular text (`__`)', done => {
+ const key = 'singular';
+ const translation = 'singular_translated';
+ createTranslationMock(key, translation);
+
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ __('${key}') }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe(translation);
+
+ done();
+ });
+ });
+
+ it('translate plural text (`n__`) without any substituting text', done => {
+ const key = 'plural';
+ const translationPlural = 'plural_multiple translation';
+ createTranslationMock(key, 'plural_singular translation', translationPlural);
+
+ const vm = new Vue({
el,
template: `
<span>
- {{ __('testing') }}
+ {{ n__('${key}', 'plurals', 2) }}
</span>
`,
}).$mount();
Vue.nextTick(() => {
- expect(
- comp.$el.textContent.trim(),
- ).toBe('testing');
+ expect(trimText(vm.$el.textContent)).toBe(translationPlural);
done();
});
});
- it('translate plural text with single count', (done) => {
- const comp = new Vue({
+ describe('translate plural text (`n__`) with substituting %d', () => {
+ const key = '%d day';
+
+ beforeEach(() => {
+ createTranslationMock(key, '%d singular translated', '%d plural translated');
+ });
+
+ it('and n === 1', done => {
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ n__('${key}', '%d days', 1) }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe('1 singular translated');
+
+ done();
+ });
+ });
+
+ it('and n > 1', done => {
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ n__('${key}', '%d days', 2) }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe('2 plural translated');
+
+ done();
+ });
+ });
+ });
+
+ describe('translates text with context `s__`', () => {
+ const key = 'Context|Foobar';
+ const translation = 'Context|Foobar translated';
+ const expectation = 'Foobar translated';
+
+ beforeEach(() => {
+ createTranslationMock(key, translation);
+ });
+
+ it('and using two parameters', done => {
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ s__('Context', 'Foobar') }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe(expectation);
+
+ done();
+ });
+ });
+
+ it('and using the pipe syntax', done => {
+ const vm = new Vue({
+ el,
+ template: `
+ <span>
+ {{ s__('${key}') }}
+ </span>
+ `,
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(trimText(vm.$el.textContent)).toBe(expectation);
+
+ done();
+ });
+ });
+ });
+
+ it('translate multi line text', done => {
+ const translation = 'multiline string translated';
+ createTranslationMock('multiline string', translation);
+
+ const vm = new Vue({
el,
template: `
<span>
- {{ n__('%d day', '%d days', 1) }}
+ {{ __(\`
+ multiline
+ string
+ \`) }}
</span>
`,
}).$mount();
Vue.nextTick(() => {
- expect(
- comp.$el.textContent.trim(),
- ).toBe('1 day');
+ expect(trimText(vm.$el.textContent)).toBe(translation);
done();
});
});
- it('translate plural text with multiple count', (done) => {
- const comp = new Vue({
+ it('translate pluralized multi line text', done => {
+ const translation = 'multiline string plural';
+
+ createTranslationMock('multiline string', 'multiline string singular', translation);
+
+ const vm = new Vue({
el,
template: `
<span>
- {{ n__('%d day', '%d days', 2) }}
+ {{ n__(
+ \`
+ multiline
+ string
+ \`,
+ \`
+ multiline
+ strings
+ \`,
+ 2
+ ) }}
</span>
`,
}).$mount();
Vue.nextTick(() => {
- expect(
- comp.$el.textContent.trim(),
- ).toBe('2 days');
+ expect(trimText(vm.$el.textContent)).toBe(translation);
done();
});
});
- it('translate plural without replacing any text', (done) => {
- const comp = new Vue({
+ it('translate pluralized multi line text with context', done => {
+ const translation = 'multiline string with context';
+
+ createTranslationMock('Context| multiline string', translation);
+
+ const vm = new Vue({
el,
template: `
<span>
- {{ n__('day', 'days', 2) }}
+ {{ s__(
+ \`
+ Context|
+ multiline
+ string
+ \`
+ ) }}
</span>
`,
}).$mount();
Vue.nextTick(() => {
- expect(
- comp.$el.textContent.trim(),
- ).toBe('days');
+ expect(trimText(vm.$el.textContent)).toBe(translation);
done();
});
diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb
index 749b2094787..797d767465a 100644
--- a/spec/models/project_auto_devops_spec.rb
+++ b/spec/models/project_auto_devops_spec.rb
@@ -100,7 +100,7 @@ describe ProjectAutoDevops do
end
end
- describe '#set_gitlab_deploy_token' do
+ describe '#create_gitlab_deploy_token' do
let(:auto_devops) { build(:project_auto_devops, project: project) }
context 'when the project is public' do
@@ -144,9 +144,9 @@ describe ProjectAutoDevops do
end
end
- context 'when autodevops is enabled at instancel level' do
+ context 'when autodevops is enabled at instance level' do
let(:project) { create(:project, :repository, :internal) }
- let(:auto_devops) { build(:project_auto_devops, :disabled, project: project) }
+ let(:auto_devops) { build(:project_auto_devops, enabled: nil, project: project) }
it 'should create a deploy token' do
allow(Gitlab::CurrentSettings).to receive(:auto_devops_enabled?).and_return(true)
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 076de06cf99..d8a5e5f6869 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -3259,6 +3259,11 @@ describe Project do
end
describe '#auto_devops_enabled?' do
+ before do
+ allow(Feature).to receive(:enabled?).and_call_original
+ Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
+ end
+
set(:project) { create(:project) }
subject { project.auto_devops_enabled? }
@@ -3268,19 +3273,14 @@ describe Project do
stub_application_setting(auto_devops_enabled: true)
end
- it 'auto devops is implicitly enabled' do
- expect(project.auto_devops).to be_nil
- expect(project).to be_auto_devops_enabled
- end
+ it { is_expected.to be_truthy }
context 'when explicitly enabled' do
before do
create(:project_auto_devops, project: project)
end
- it "auto devops is enabled" do
- expect(project).to be_auto_devops_enabled
- end
+ it { is_expected.to be_truthy }
end
context 'when explicitly disabled' do
@@ -3288,9 +3288,7 @@ describe Project do
create(:project_auto_devops, project: project, enabled: false)
end
- it "auto devops is disabled" do
- expect(project).not_to be_auto_devops_enabled
- end
+ it { is_expected.to be_falsey }
end
end
@@ -3299,19 +3297,22 @@ describe Project do
stub_application_setting(auto_devops_enabled: false)
end
- it 'auto devops is implicitly disabled' do
- expect(project.auto_devops).to be_nil
- expect(project).not_to be_auto_devops_enabled
- end
+ it { is_expected.to be_falsey }
context 'when explicitly enabled' do
before do
create(:project_auto_devops, project: project)
end
- it "auto devops is enabled" do
- expect(project).to be_auto_devops_enabled
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when force_autodevops_on_by_default is enabled for the project' do
+ before do
+ Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(100)
end
+
+ it { is_expected.to be_truthy }
end
end
end
@@ -3361,6 +3362,11 @@ describe Project do
end
describe '#has_auto_devops_implicitly_disabled?' do
+ before do
+ allow(Feature).to receive(:enabled?).and_call_original
+ Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
+ end
+
set(:project) { create(:project) }
context 'when enabled in settings' do
@@ -3382,6 +3388,16 @@ describe Project do
expect(project).to have_auto_devops_implicitly_disabled
end
+ context 'when force_autodevops_on_by_default is enabled for the project' do
+ before do
+ Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(100)
+ end
+
+ it 'does not have auto devops implicitly disabled' do
+ expect(project).not_to have_auto_devops_implicitly_disabled
+ end
+ end
+
context 'when explicitly disabled' do
before do
create(:project_auto_devops, project: project, enabled: false)
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index b54491cf5f9..d80d0f5a8a8 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -135,6 +135,17 @@ describe Groups::DestroyService do
it_behaves_like 'group destruction', false
end
+ context 'repository removal status is taken into account' do
+ it 'raises exception' do
+ expect_next_instance_of(::Projects::DestroyService) do |destroy_service|
+ expect(destroy_service).to receive(:execute).and_return(false)
+ end
+
+ expect { destroy_group(group, user, false) }
+ .to raise_error(Groups::DestroyService::DestroyError, "Project #{project.id} can't be deleted" )
+ end
+ end
+
describe 'repository removal' do
before do
destroy_group(group, user, false)
diff --git a/spec/services/projects/detect_repository_languages_service_spec.rb b/spec/services/projects/detect_repository_languages_service_spec.rb
index f90d558938f..deea1189cdf 100644
--- a/spec/services/projects/detect_repository_languages_service_spec.rb
+++ b/spec/services/projects/detect_repository_languages_service_spec.rb
@@ -5,10 +5,6 @@ describe Projects::DetectRepositoryLanguagesService, :clean_gitlab_redis_shared_
subject { described_class.new(project, project.owner) }
- before do
- allow(Feature).to receive(:disabled?).and_return(false)
- end
-
describe '#execute' do
context 'without previous detection' do
it 'inserts new programming languages in the database' do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index bd564cc60a6..f4441a6b700 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -111,6 +111,13 @@ RSpec.configure do |config|
config.before(:example) do
# Enable all features by default for testing
allow(Feature).to receive(:enabled?) { true }
+
+ # The following can be removed when we remove the staged rollout strategy
+ # and we can just enable it using instance wide settings
+ # (ie. ApplicationSetting#auto_devops_enabled)
+ allow(Feature).to receive(:enabled?)
+ .with(:force_autodevops_on_by_default, anything)
+ .and_return(false)
end
config.before(:example, :request_store) do
diff --git a/spec/tasks/gitlab/traces_rake_spec.rb b/spec/tasks/gitlab/traces_rake_spec.rb
index bd18e8ffc1e..aaf0d7242dd 100644
--- a/spec/tasks/gitlab/traces_rake_spec.rb
+++ b/spec/tasks/gitlab/traces_rake_spec.rb
@@ -5,51 +5,109 @@ describe 'gitlab:traces rake tasks' do
Rake.application.rake_require 'tasks/gitlab/traces'
end
- shared_examples 'passes the job id to worker' do
- it do
- expect(ArchiveTraceWorker).to receive(:bulk_perform_async).with([[job.id]])
+ describe 'gitlab:traces:archive' do
+ shared_examples 'passes the job id to worker' do
+ it do
+ expect(ArchiveTraceWorker).to receive(:bulk_perform_async).with([[job.id]])
- run_rake_task('gitlab:traces:archive')
+ run_rake_task('gitlab:traces:archive')
+ end
end
- end
- shared_examples 'does not pass the job id to worker' do
- it do
- expect(ArchiveTraceWorker).not_to receive(:bulk_perform_async)
+ shared_examples 'does not pass the job id to worker' do
+ it do
+ expect(ArchiveTraceWorker).not_to receive(:bulk_perform_async)
- run_rake_task('gitlab:traces:archive')
+ run_rake_task('gitlab:traces:archive')
+ end
end
- end
- context 'when trace file stored in default path' do
- let!(:job) { create(:ci_build, :success, :trace_live) }
+ context 'when trace file stored in default path' do
+ let!(:job) { create(:ci_build, :success, :trace_live) }
- it_behaves_like 'passes the job id to worker'
- end
+ it_behaves_like 'passes the job id to worker'
+ end
- context 'when trace is stored in database' do
- let!(:job) { create(:ci_build, :success) }
+ context 'when trace is stored in database' do
+ let!(:job) { create(:ci_build, :success) }
- before do
- job.update_column(:trace, 'trace in db')
+ before do
+ job.update_column(:trace, 'trace in db')
+ end
+
+ it_behaves_like 'passes the job id to worker'
end
- it_behaves_like 'passes the job id to worker'
+ context 'when job has trace artifact' do
+ let!(:job) { create(:ci_build, :success) }
+
+ before do
+ create(:ci_job_artifact, :trace, job: job)
+ end
+
+ it_behaves_like 'does not pass the job id to worker'
+ end
+
+ context 'when job is not finished yet' do
+ let!(:build) { create(:ci_build, :running, :trace_live) }
+
+ it_behaves_like 'does not pass the job id to worker'
+ end
end
- context 'when job has trace artifact' do
- let!(:job) { create(:ci_build, :success) }
+ describe 'gitlab:traces:migrate' do
+ let(:object_storage_enabled) { false }
before do
- create(:ci_job_artifact, :trace, job: job)
+ stub_artifacts_object_storage(enabled: object_storage_enabled)
end
- it_behaves_like 'does not pass the job id to worker'
- end
+ subject { run_rake_task('gitlab:traces:migrate') }
- context 'when job is not finished yet' do
- let!(:build) { create(:ci_build, :running, :trace_live) }
+ let!(:job_trace) { create(:ci_job_artifact, :trace, file_store: store) }
- it_behaves_like 'does not pass the job id to worker'
+ context 'when local storage is used' do
+ let(:store) { ObjectStorage::Store::LOCAL }
+
+ context 'and job does not have file store defined' do
+ let(:object_storage_enabled) { true }
+ let(:store) { nil }
+
+ it "migrates file to remote storage" do
+ subject
+
+ expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE)
+ end
+ end
+
+ context 'and remote storage is defined' do
+ let(:object_storage_enabled) { true }
+
+ it "migrates file to remote storage" do
+ subject
+
+ expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE)
+ end
+ end
+
+ context 'and remote storage is not defined' do
+ it "fails to migrate to remote storage" do
+ subject
+
+ expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::LOCAL)
+ end
+ end
+ end
+
+ context 'when remote storage is used' do
+ let(:object_storage_enabled) { true }
+ let(:store) { ObjectStorage::Store::REMOTE }
+
+ it "file stays on remote storage" do
+ subject
+
+ expect(job_trace.reload.file_store).to eq(ObjectStorage::Store::REMOTE)
+ end
+ end
end
end
diff --git a/yarn.lock b/yarn.lock
index c1e9d0ab73e..4326245d2ac 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -94,10 +94,34 @@
version "0.7.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
+"@types/events@*":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86"
+
+"@types/glob@^5":
+ version "5.0.35"
+ resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a"
+ dependencies:
+ "@types/events" "*"
+ "@types/minimatch" "*"
+ "@types/node" "*"
+
"@types/jquery@^2.0.40":
version "2.0.48"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.48.tgz#3e90d8cde2d29015e5583017f7830cb3975b2eef"
+"@types/minimatch@*":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
+
+"@types/node@*":
+ version "10.5.2"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.2.tgz#f19f05314d5421fe37e74153254201a7bf00a707"
+
+"@types/parse5@^5":
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.0.tgz#9ae2106efc443d7c1e26570aa8247828c9c80f11"
+
"@vue/component-compiler-utils@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-1.2.1.tgz#3d543baa75cfe5dab96e29415b78366450156ef6"
@@ -2099,6 +2123,10 @@ css-loader@^1.0.0:
postcss-value-parser "^3.3.0"
source-list-map "^2.0.0"
+css-selector-parser@^1.3:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.3.0.tgz#5f1ad43e2d8eefbfdc304fcd39a521664943e3eb"
+
css-selector-tokenizer@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86"
@@ -3520,6 +3548,26 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
+gettext-extractor-vue@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/gettext-extractor-vue/-/gettext-extractor-vue-4.0.1.tgz#69d2737eb8f1938803ffcf9317133ed59fb2372f"
+ dependencies:
+ bluebird "^3.5.1"
+ glob "^7.1.2"
+ vue-template-compiler "^2.5.0"
+
+gettext-extractor@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/gettext-extractor/-/gettext-extractor-3.3.2.tgz#d5172ba8d175678bd40a5abe7f908fa2a9d9473b"
+ dependencies:
+ "@types/glob" "^5"
+ "@types/parse5" "^5"
+ css-selector-parser "^1.3"
+ glob "5 - 7"
+ parse5 "^5"
+ pofile "^1"
+ typescript "^2"
+
glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@@ -3527,24 +3575,24 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
-glob@^5.0.15:
- version "5.0.15"
- resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
+"glob@5 - 7", glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
+ fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
- minimatch "2 || 3"
+ minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
- version "7.1.2"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+glob@^5.0.15:
+ version "5.0.15"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
dependencies:
- fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
- minimatch "^3.0.4"
+ minimatch "2 || 3"
once "^1.3.0"
path-is-absolute "^1.0.0"
@@ -5750,6 +5798,10 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
+parse5@^5:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.0.0.tgz#4d02710d44f3c3846197a11e205d4ef17842b81a"
+
parseqs@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
@@ -5888,6 +5940,10 @@ pluralize@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
+pofile@^1:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.0.11.tgz#35aff58c17491d127a07336d5522ebc9df57c954"
+
popper.js@^1.12.9, popper.js@^1.14.3:
version "1.14.3"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.3.tgz#1438f98d046acf7b4d78cd502bf418ac64d4f095"
@@ -7460,6 +7516,10 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+typescript@^2:
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
+
uglify-es@^3.3.4:
version "3.3.9"
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
@@ -7756,7 +7816,7 @@ vue-style-loader@^4.1.0:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
-vue-template-compiler@^2.5.16:
+vue-template-compiler@^2.5.0, vue-template-compiler@^2.5.16:
version "2.5.16"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.16.tgz#93b48570e56c720cdf3f051cc15287c26fbd04cb"
dependencies: