diff options
132 files changed, 1426 insertions, 631 deletions
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml index 5aa1a856405..beab406fab7 100644 --- a/.gitlab/ci/docs.gitlab-ci.yml +++ b/.gitlab/ci/docs.gitlab-ci.yml @@ -66,6 +66,10 @@ docs lint: - scripts/lint-changelog-yaml - mv doc/ /tmp/gitlab-docs/content/$DOCS_GITLAB_REPO_SUFFIX - cd /tmp/gitlab-docs + # Lint Markdown + # https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md + - bundle exec mdl content/$DOCS_GITLAB_REPO_SUFFIX/**/*.md --rules \ + MD032 # Build HTML from Markdown - bundle exec nanoc # Check the internal links diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index 75c79bb213f..cd6953a6ac2 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -72,6 +72,7 @@ gitlab:assets:compile pull-cache: except: - master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ee + - /(^docs[\/-].*|.*-docs$)/ .compile-assets-metadata: extends: .dedicated-runner diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml index b7ef4b31743..eb50f08c1a7 100644 --- a/.gitlab/ci/global.gitlab-ci.yml +++ b/.gitlab/ci/global.gitlab-ci.yml @@ -28,11 +28,15 @@ policy: pull stage: test -.dedicated-no-docs-pull-cache-job: - extends: .dedicated-pull-cache-job +.no-docs: except: - /(^docs[\/-].*|.*-docs$)/ +.dedicated-no-docs-pull-cache-job: + extends: + - .dedicated-pull-cache-job + - .no-docs + .dedicated-no-docs-and-no-qa-pull-cache-job: extends: .dedicated-pull-cache-job except: @@ -45,6 +49,12 @@ variables: SETUP_DB: "false" +# Jobs that need a dedicated runner, with no cache +.dedicated-no-docs: + extends: + - .dedicated-runner + - .no-docs + .single-script-job-dedicated-runner: extends: .dedicated-runner image: ruby:2.6-alpine diff --git a/.gitlab/ci/memory.gitlab-ci.yml b/.gitlab/ci/memory.gitlab-ci.yml index 50b843df585..2f3907a331a 100644 --- a/.gitlab/ci/memory.gitlab-ci.yml +++ b/.gitlab/ci/memory.gitlab-ci.yml @@ -5,15 +5,15 @@ memory-static: # Loads each of gems in the Gemfile and checks how much memory they consume when they are required. # 'derailed_benchmarks' internally uses 'get_process_mem' - - scripts/memory-static 'tmp/memory_static_full_report.txt' 'tmp/memory_static_metrics.txt' + - bundle exec derailed bundle:mem > tmp/memory_bundle_mem.txt + - scripts/generate-gems-size-metrics-static tmp/memory_bundle_mem.txt >> 'tmp/memory_metrics.txt' # Outputs detailed information about objects created while gems are loaded. # 'derailed_benchmarks' internally uses 'memory_profiler' - - scripts/memory-static-objects 'tmp/memory_static_objects_full_report.txt' 'tmp/memory_static_metrics.txt' + - bundle exec derailed bundle:objects > tmp/memory_bundle_objects.txt + - scripts/generate-gems-memory-metrics-static tmp/memory_bundle_objects.txt >> 'tmp/memory_metrics.txt' artifacts: paths: - - tmp/memory_static_full_report.txt - - tmp/memory_static_objects_full_report.txt - - tmp/memory_static_metrics.txt + - tmp/memory_*.txt reports: - metrics: tmp/memory_static_metrics.txt + metrics: tmp/memory_metrics.txt diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml index d0e09dbf2f8..89b5ae38072 100644 --- a/.gitlab/ci/reports.gitlab-ci.yml +++ b/.gitlab/ci/reports.gitlab-ci.yml @@ -1,98 +1,26 @@ include: - template: Code-Quality.gitlab-ci.yml + - template: Security/SAST.gitlab-ci.yml + - template: Security/Dependency-Scanning.gitlab-ci.yml code_quality: - extends: .dedicated-no-docs-no-db-pull-cache-job + extends: .dedicated-no-docs # gitlab-org runners set `privileged: false` but we need to have it set to true # since we're using Docker in Docker tags: [] before_script: [] cache: {} dependencies: [] - variables: - SETUP_DB: "false" sast: - extends: .dedicated-no-docs-no-db-pull-cache-job - image: docker:stable + extends: .dedicated-no-docs + before_script: [] + tags: [] variables: SAST_CONFIDENCE_LEVEL: 2 DOCKER_DRIVER: overlay2 - allow_failure: true - tags: [] - before_script: [] - cache: {} - dependencies: [] - services: - - docker:stable-dind - script: - - | # this is required to avoid undesirable reset of Docker image ENV variables being set on build stage - function propagate_env_vars() { - CURRENT_ENV=$(printenv) - - for VAR_NAME; do - echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME " - done - } - - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - - | - docker run \ - $(propagate_env_vars \ - SAST_ANALYZER_IMAGES \ - SAST_ANALYZER_IMAGE_PREFIX \ - SAST_ANALYZER_IMAGE_TAG \ - SAST_DEFAULT_ANALYZERS \ - SAST_BRAKEMAN_LEVEL \ - SAST_GOSEC_LEVEL \ - SAST_FLAWFINDER_LEVEL \ - SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \ - SAST_PULL_ANALYZER_IMAGE_TIMEOUT \ - SAST_RUN_ANALYZER_TIMEOUT \ - ) \ - --volume "$PWD:/code" \ - --volume /var/run/docker.sock:/var/run/docker.sock \ - "registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code - artifacts: - reports: - sast: gl-sast-report.json dependency_scanning: - extends: .dedicated-no-docs-no-db-pull-cache-job - image: docker:stable - variables: - DOCKER_DRIVER: overlay2 - allow_failure: true - tags: [] before_script: [] - cache: {} - dependencies: [] - services: - - docker:stable-dind - script: - - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - - | # this is required to avoid undesirable reset of Docker image ENV variables being set on build stage - function propagate_env_vars() { - CURRENT_ENV=$(printenv) - - for VAR_NAME; do - echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME " - done - } - - | - docker run \ - $(propagate_env_vars \ - DS_ANALYZER_IMAGES \ - DS_ANALYZER_IMAGE_PREFIX \ - DS_ANALYZER_IMAGE_TAG \ - DS_DEFAULT_ANALYZERS \ - DEP_SCAN_DISABLE_REMOTE_CHECKS \ - DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \ - DS_PULL_ANALYZER_IMAGE_TIMEOUT \ - DS_RUN_ANALYZER_TIMEOUT \ - ) \ - --volume "$PWD:/code" \ - --volume /var/run/docker.sock:/var/run/docker.sock \ - "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code - artifacts: - reports: - dependency_scanning: gl-dependency-scanning-report.json + tags: [] + extends: .dedicated-no-docs diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml index 9b764028be9..933af90c85a 100644 --- a/.gitlab/ci/review.gitlab-ci.yml +++ b/.gitlab/ci/review.gitlab-ci.yml @@ -77,6 +77,7 @@ schedule:review-build-cng: .review-deploy-base: &review-deploy-base <<: *review-base allow_failure: true + retry: 2 stage: review variables: HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}" @@ -95,10 +96,16 @@ schedule:review-build-cng: - install_api_client_dependencies_with_apk - source scripts/review_apps/review-apps.sh script: - - perform_review_app_deployment + - check_kube_domain + - ensure_namespace + - install_tiller + - install_external_dns + - download_chart + - deploy || display_deployment_debug + - wait_for_review_app_to_be_accessible + - add_license artifacts: - paths: - - review_app_url.txt + paths: [review_app_url.txt] expire_in: 2 days when: always @@ -108,8 +115,6 @@ review-deploy: schedule:review-deploy: <<: *review-deploy-base <<: *review-schedules-only - script: - - perform_review_app_deployment review-stop: <<: *review-base @@ -124,11 +129,11 @@ review-stop: script: - source scripts/review_apps/review-apps.sh - delete - - cleanup .review-qa-base: &review-qa-base <<: *review-docker allow_failure: true + retry: 2 stage: qa variables: <<: *review-docker-variables diff --git a/CHANGELOG.md b/CHANGELOG.md index 8922c5b4938..118034867ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 12.0.1 (2019-06-24) + +- No changes. + ## 12.0.0 (2019-06-22) ### Security (10 changes) diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js index c587b276fa3..2ace0060c42 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js +++ b/app/assets/javascripts/boards/components/board_sidebar.js @@ -38,6 +38,7 @@ export default Vue.extend({ issue: {}, list: {}, loadingAssignees: false, + timeTrackingLimitToHours: boardsStore.timeTracking.limitToHours, }; }, computed: { diff --git a/app/assets/javascripts/boards/components/issue_time_estimate.vue b/app/assets/javascripts/boards/components/issue_time_estimate.vue index 98c1d29db16..3385aad5b11 100644 --- a/app/assets/javascripts/boards/components/issue_time_estimate.vue +++ b/app/assets/javascripts/boards/components/issue_time_estimate.vue @@ -2,6 +2,7 @@ import { GlTooltip } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility'; +import boardsStore from '../stores/boards_store'; export default { components: { @@ -14,12 +15,17 @@ export default { required: true, }, }, + data() { + return { + limitToHours: boardsStore.timeTracking.limitToHours, + }; + }, computed: { title() { - return stringifyTime(parseSeconds(this.estimate), true); + return stringifyTime(parseSeconds(this.estimate, { limitToHours: this.limitToHours }), true); }, timeEstimate() { - return stringifyTime(parseSeconds(this.estimate)); + return stringifyTime(parseSeconds(this.estimate, { limitToHours: this.limitToHours })); }, }, }; diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index f2f37d22b97..a020765f335 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -49,6 +49,7 @@ export default () => { } boardsStore.create(); + boardsStore.setTimeTrackingLimitToHours($boardApp.dataset.timeTrackingLimitToHours); issueBoardsApp = new Vue({ el: $boardApp, diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index 4b3b44574a8..4ba4cde6bae 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -12,6 +12,9 @@ import eventHub from '../eventhub'; const boardsStore = { disabled: false, + timeTracking: { + limitToHours: false, + }, scopedLabels: { helpLink: '', enabled: false, @@ -222,6 +225,10 @@ const boardsStore = { setIssueDetail(issueDetail) { this.detail.issue = issueDetail; }, + + setTimeTrackingLimitToHours(limitToHours) { + this.timeTracking.limitToHours = parseBoolean(limitToHours); + }, }; BoardsStoreEE.initEESpecific(boardsStore); diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js index d0cc4897aeb..a4394ab7e92 100644 --- a/app/assets/javascripts/commons/polyfills.js +++ b/app/assets/javascripts/commons/polyfills.js @@ -12,6 +12,7 @@ import 'core-js/es/promise/finally'; import 'core-js/es/string/code-point-at'; import 'core-js/es/string/from-code-point'; import 'core-js/es/string/includes'; +import 'core-js/es/string/starts-with'; import 'core-js/es/symbol'; import 'core-js/es/map'; import 'core-js/es/weak-map'; diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 3b73dd83c9f..b308cd9c236 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -318,6 +318,7 @@ class GfmAutoComplete { } setupLabels($input) { + const instance = this; const fetchData = this.fetchData.bind(this); const LABEL_COMMAND = { LABEL: '/label', UNLABEL: '/unlabel', RELABEL: '/relabel' }; let command = ''; @@ -348,7 +349,6 @@ class GfmAutoComplete { })); }, matcher(flag, subtext) { - const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers); const subtextNodes = subtext .split(/\n+/g) .pop() @@ -366,6 +366,27 @@ class GfmAutoComplete { return null; }); + // If any label matches the inserted text after the last `~`, suggest those labels, + // even if any spaces or funky characters were typed. + // This allows matching labels like "Accepting merge requests". + const labels = instance.cachedData[flag]; + if (labels) { + if (!subtext.includes(flag)) { + // Do not match if there is no `~` before the cursor + return null; + } + const lastCandidate = subtext.split(flag).pop(); + if (labels.find(label => label.title.startsWith(lastCandidate))) { + return lastCandidate; + } + } else { + // Load all labels into the autocompleter. + // This needs to happen if e.g. editing a label in an existing comment, because normally + // label data would only be loaded only once you type `~`. + fetchData(this.$inputor, this.at); + } + + const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers); return match && match.length ? match[1] : null; }, filter(query, data, searchKey) { @@ -563,8 +584,9 @@ class GfmAutoComplete { const accentAChar = decodeURI('%C3%80'); const accentYChar = decodeURI('%C3%BF'); + // Holy regex, batman! const regexp = new RegExp( - `^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`, + `^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-:]|[^\\x00-\\x7a])*)$`, 'gi', ); diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js index 5429b834708..48aabaf9dcf 100644 --- a/app/assets/javascripts/ide/stores/actions.js +++ b/app/assets/javascripts/ide/stores/actions.js @@ -208,10 +208,6 @@ export const deleteEntry = ({ commit, dispatch, state }, path) => { commit(types.DELETE_ENTRY, path); - if (entry.parentPath && state.entries[entry.parentPath].tree.length === 0) { - dispatch('deleteEntry', entry.parentPath); - } - dispatch('triggerFilesChange'); }; diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue index 42a3de62772..b2f9296c68b 100644 --- a/app/assets/javascripts/issue_show/components/edit_actions.vue +++ b/app/assets/javascripts/issue_show/components/edit_actions.vue @@ -73,7 +73,9 @@ export default { Save changes <i v-if="formState.updateLoading" class="fa fa-spinner fa-spin" aria-hidden="true"> </i> </button> - <button class="btn btn-default float-right" type="button" @click="closeForm">Cancel</button> + <button class="btn btn-default float-right" type="button" @click="closeForm"> + {{ __('Cancel') }} + </button> <button v-if="shouldShowDeleteButton" :class="{ disabled: deleteLoading }" diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue index d27dd873125..447d7bf21a5 100644 --- a/app/assets/javascripts/issue_show/components/fields/description.vue +++ b/app/assets/javascripts/issue_show/components/fields/description.vue @@ -39,7 +39,7 @@ export default { <template> <div class="common-note-form"> - <label class="sr-only" for="issue-description"> Description </label> + <label class="sr-only" for="issue-description">{{ __('Description') }}</label> <markdown-field :markdown-preview-path="markdownPreviewPath" :markdown-docs-path="markdownDocsPath" @@ -55,8 +55,8 @@ export default { qa-description-textarea" dir="auto" data-supports-quick-actions="false" - aria-label="Description" - placeholder="Write a comment or drag your files here…" + :aria-label="__('Description')" + :placeholder="__('Write a comment or drag your files here…')" @keydown.meta.enter="updateIssuable" @keydown.ctrl.enter="updateIssuable" > diff --git a/app/assets/javascripts/issue_show/components/fields/description_template.vue b/app/assets/javascripts/issue_show/components/fields/description_template.vue index 14f0acf6540..6f955928d8e 100644 --- a/app/assets/javascripts/issue_show/components/fields/description_template.vue +++ b/app/assets/javascripts/issue_show/components/fields/description_template.vue @@ -56,22 +56,31 @@ export default { data-selected="null" data-toggle="dropdown" > - <span class="dropdown-toggle-text"> Choose a template </span> + <span class="dropdown-toggle-text">{{ __('Choose a template') }}</span> <i aria-hidden="true" class="fa fa-chevron-down"> </i> </button> <div class="dropdown-menu dropdown-select"> <div class="dropdown-title"> Choose a template - <button class="dropdown-title-button dropdown-menu-close" aria-label="Close" type="button"> + <button + class="dropdown-title-button dropdown-menu-close" + :aria-label="__('Close')" + type="button" + > <i aria-hidden="true" class="fa fa-times dropdown-menu-close-icon"> </i> </button> </div> <div class="dropdown-input"> - <input type="search" class="dropdown-input-field" placeholder="Filter" autocomplete="off" /> + <input + type="search" + class="dropdown-input-field" + :placeholder="__('Filter')" + autocomplete="off" + /> <i aria-hidden="true" class="fa fa-search dropdown-input-search"> </i> <i role="button" - aria-label="Clear templates search input" + :aria-label="__('Clear templates search input')" class="fa fa-times dropdown-input-clear js-dropdown-input-clear" > </i> @@ -79,8 +88,12 @@ export default { <div class="dropdown-content"></div> <div class="dropdown-footer"> <ul class="dropdown-footer-list"> - <li><a class="no-template"> No template </a></li> - <li><a class="reset-template"> Reset template </a></li> + <li> + <a class="no-template">{{ __('No template') }}</a> + </li> + <li> + <a class="reset-template">{{ __('Reset template') }}</a> + </li> </ul> </div> </div> diff --git a/app/assets/javascripts/issue_show/components/fields/title.vue b/app/assets/javascripts/issue_show/components/fields/title.vue index ce4baf17d09..34eb0451d53 100644 --- a/app/assets/javascripts/issue_show/components/fields/title.vue +++ b/app/assets/javascripts/issue_show/components/fields/title.vue @@ -14,7 +14,7 @@ export default { <template> <fieldset> - <label class="sr-only" for="issuable-title"> Title </label> + <label class="sr-only" for="issuable-title">{{ __('Title') }}</label> <input id="issuable-title" ref="input" @@ -22,8 +22,8 @@ export default { class="form-control qa-title-input" dir="auto" type="text" - placeholder="Title" - aria-label="Title" + :placeholder="__('Title')" + :aria-label="__('Title')" @keydown.meta.enter="updateIssuable" @keydown.ctrl.enter="updateIssuable" /> diff --git a/app/assets/javascripts/issue_show/components/locked_warning.vue b/app/assets/javascripts/issue_show/components/locked_warning.vue index 639221473b1..2f3e611e089 100644 --- a/app/assets/javascripts/issue_show/components/locked_warning.vue +++ b/app/assets/javascripts/issue_show/components/locked_warning.vue @@ -10,8 +10,9 @@ export default { <template> <div class="alert alert-danger"> - Someone edited the issue at the same time you did. Please check out - <a :href="currentPath" target="_blank" rel="nofollow">the issue</a> and make sure your changes - will not unintentionally remove theirs. + {{ sprintf(__("Someone edited the issue at the same time you did. Please check out + %{linkStart}%the issue%{linkEnd} and make sure your changes will not unintentionally remove + theirs."), { linkStart: `<a href="${currentPath}" target="_blank" rel="nofollow">` linkEnd: '</a + >', }) }} </div> </template> diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index d521c462ad8..062d21ed247 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -479,9 +479,13 @@ export const pikadayToString = date => { * Seconds can be negative or positive, zero or non-zero. Can be configured for any day * or week length. */ -export const parseSeconds = (seconds, { daysPerWeek = 5, hoursPerDay = 8 } = {}) => { +export const parseSeconds = ( + seconds, + { daysPerWeek = 5, hoursPerDay = 8, limitToHours = false } = {}, +) => { const DAYS_PER_WEEK = daysPerWeek; const HOURS_PER_DAY = hoursPerDay; + const SECONDS_PER_MINUTE = 60; const MINUTES_PER_HOUR = 60; const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR; const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR; @@ -493,9 +497,18 @@ export const parseSeconds = (seconds, { daysPerWeek = 5, hoursPerDay = 8 } = {}) minutes: 1, }; - let unorderedMinutes = Math.abs(seconds / MINUTES_PER_HOUR); + if (limitToHours) { + timePeriodConstraints.weeks = 0; + timePeriodConstraints.days = 0; + } + + let unorderedMinutes = Math.abs(seconds / SECONDS_PER_MINUTE); return _.mapObject(timePeriodConstraints, minutesPerPeriod => { + if (minutesPerPeriod === 0) { + return 0; + } + const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod); unorderedMinutes -= periodCount * minutesPerPeriod; diff --git a/app/assets/javascripts/monitoring/components/charts/area.vue b/app/assets/javascripts/monitoring/components/charts/area.vue index 9de4e96e4da..9a3ce5174db 100644 --- a/app/assets/javascripts/monitoring/components/charts/area.vue +++ b/app/assets/javascripts/monitoring/components/charts/area.vue @@ -1,4 +1,5 @@ <script> +import { __ } from '~/locale'; import { GlAreaChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts'; import dateFormat from 'dateformat'; import { debounceByAnimationFrame, roundOffFloat } from '~/lib/utils/common_utils'; @@ -99,7 +100,7 @@ export default { chartOptions() { return { xAxis: { - name: 'Time', + name: __('Time'), type: 'time', axisLabel: { formatter: date => dateFormat(date, 'h:MM TT'), diff --git a/app/assets/javascripts/monitoring/components/empty_state.vue b/app/assets/javascripts/monitoring/components/empty_state.vue index 0e141d02ead..a3c6de14aa4 100644 --- a/app/assets/javascripts/monitoring/components/empty_state.vue +++ b/app/assets/javascripts/monitoring/components/empty_state.vue @@ -1,4 +1,6 @@ <script> +import { __ } from '~/locale'; + export default { props: { documentationPath: { @@ -41,35 +43,35 @@ export default { states: { gettingStarted: { svgUrl: this.emptyGettingStartedSvgPath, - title: 'Get started with performance monitoring', - description: `Stay updated about the performance and health - of your environment by configuring Prometheus to monitor your deployments.`, - buttonText: 'Install on clusters', + title: __('Get started with performance monitoring'), + description: __(`Stay updated about the performance and health + of your environment by configuring Prometheus to monitor your deployments.`), + buttonText: __('Install on clusters'), buttonPath: this.clustersPath, - secondaryButtonText: 'Configure existing installation', + secondaryButtonText: __('Configure existing installation'), secondaryButtonPath: this.settingsPath, }, loading: { svgUrl: this.emptyLoadingSvgPath, - title: 'Waiting for performance data', - description: `Creating graphs uses the data from the Prometheus server. - If this takes a long time, ensure that data is available.`, - buttonText: 'View documentation', + title: __('Waiting for performance data'), + description: __(`Creating graphs uses the data from the Prometheus server. + If this takes a long time, ensure that data is available.`), + buttonText: __('View documentation'), buttonPath: this.documentationPath, }, noData: { svgUrl: this.emptyNoDataSvgPath, - title: 'No data found', - description: `You are connected to the Prometheus server, but there is currently - no data to display.`, - buttonText: 'Configure Prometheus', + title: __('No data found'), + description: __(`You are connected to the Prometheus server, but there is currently + no data to display.`), + buttonText: __('Configure Prometheus'), buttonPath: this.settingsPath, }, unableToConnect: { svgUrl: this.emptyUnableToConnectSvgPath, - title: 'Unable to connect to Prometheus server', + title: __('Unable to connect to Prometheus server'), description: 'Ensure connectivity is available from the GitLab server to the ', - buttonText: 'View documentation', + buttonText: __('View documentation'), buttonPath: this.documentationPath, }, }, @@ -90,7 +92,9 @@ export default { <template> <div class="row empty-state js-empty-state"> <div class="col-12"> - <div class="state-svg svg-content"><img :src="currentState.svgUrl" /></div> + <div class="state-svg svg-content"> + <img :src="currentState.svgUrl" /> + </div> </div> <div class="col-12"> @@ -98,20 +102,22 @@ export default { <h4 class="state-title text-center">{{ currentState.title }}</h4> <p class="state-description"> {{ currentState.description }} - <a v-if="showButtonDescription" :href="settingsPath"> Prometheus server </a> + <a v-if="showButtonDescription" :href="settingsPath">{{ __('Prometheus server') }}</a> </p> <div class="text-center"> - <a v-if="currentState.buttonPath" :href="currentState.buttonPath" class="btn btn-success"> - {{ currentState.buttonText }} - </a> + <a + v-if="currentState.buttonPath" + :href="currentState.buttonPath" + class="btn btn-success" + >{{ currentState.buttonText }}</a + > <a v-if="currentState.secondaryButtonPath" :href="currentState.secondaryButtonPath" class="btn" + >{{ currentState.secondaryButtonText }}</a > - {{ currentState.secondaryButtonText }} - </a> </div> </div> </div> diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue index f4d926cd3ec..13955529cab 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue @@ -28,11 +28,16 @@ export default { type: String, required: true, }, + limitToHours: { + type: Boolean, + required: false, + default: false, + }, }, computed: { parsedTimeRemaining() { const diffSeconds = this.timeEstimate - this.timeSpent; - return parseSeconds(diffSeconds); + return parseSeconds(diffSeconds, { limitToHours: this.limitToHours }); }, timeRemainingHumanReadable() { return stringifyTime(this.parsedTimeRemaining); diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue index 8e8b9f19b6e..018b30d2a67 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue @@ -53,6 +53,7 @@ export default { :time-spent="store.totalTimeSpent" :human-time-estimate="store.humanTimeEstimate" :human-time-spent="store.humanTotalTimeSpent" + :limit-to-hours="store.timeTrackingLimitToHours" :root-path="store.rootPath" /> </div> diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue index d84d5344935..682ca600b6a 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue @@ -37,6 +37,10 @@ export default { required: false, default: '', }, + limitToHours: { + type: Boolean, + default: false, + }, rootPath: { type: String, required: true, @@ -129,6 +133,7 @@ export default { :time-spent="timeSpent" :time-spent-human-readable="humanTimeSpent" :time-estimate-human-readable="humanTimeEstimate" + :limit-to-hours="limitToHours" /> <transition name="help-state-toggle"> <time-tracking-help-state v-if="showHelpState" :root-path="rootPath" /> diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js index 1ebdbec7bc9..d934463382f 100644 --- a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import timeTracker from './components/time_tracking/time_tracker.vue'; +import { parseBoolean } from '~/lib/utils/common_utils'; export default class SidebarMilestone { constructor() { @@ -7,7 +8,7 @@ export default class SidebarMilestone { if (!el) return; - const { timeEstimate, timeSpent, humanTimeEstimate, humanTimeSpent } = el.dataset; + const { timeEstimate, timeSpent, humanTimeEstimate, humanTimeSpent, limitToHours } = el.dataset; // eslint-disable-next-line no-new new Vue({ @@ -22,6 +23,7 @@ export default class SidebarMilestone { timeSpent: parseInt(timeSpent, 10), humanTimeEstimate, humanTimeSpent, + limitToHours: parseBoolean(limitToHours), rootPath: '/', }, }), diff --git a/app/assets/javascripts/sidebar/stores/sidebar_store.js b/app/assets/javascripts/sidebar/stores/sidebar_store.js index 7b8b4c5d856..63c4a2a3f84 100644 --- a/app/assets/javascripts/sidebar/stores/sidebar_store.js +++ b/app/assets/javascripts/sidebar/stores/sidebar_store.js @@ -8,7 +8,7 @@ export default class SidebarStore { } initSingleton(options) { - const { currentUser, rootPath, editable } = options; + const { currentUser, rootPath, editable, timeTrackingLimitToHours } = options; this.currentUser = currentUser; this.rootPath = rootPath; this.editable = editable; @@ -16,6 +16,7 @@ export default class SidebarStore { this.totalTimeSpent = 0; this.humanTimeEstimate = ''; this.humanTimeSpent = ''; + this.timeTrackingLimitToHours = timeTrackingLimitToHours; this.assignees = []; this.isFetching = { assignees: true, diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 4469118f065..d837c42fd68 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -253,6 +253,7 @@ module ApplicationSettingsHelper :throttle_unauthenticated_enabled, :throttle_unauthenticated_period_in_seconds, :throttle_unauthenticated_requests_per_period, + :time_tracking_limit_to_hours, :two_factor_grace_period, :unique_ips_limit_enabled, :unique_ips_limit_per_user, diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index 1640f4fc93f..c5130b430b9 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -14,7 +14,8 @@ module BoardsHelper issue_link_base: build_issue_link_base, root_path: root_path, bulk_update_path: @bulk_issues_path, - default_avatar: image_path(default_avatar) + default_avatar: image_path(default_avatar), + time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s } end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 150f24a5d5b..045de105b77 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -430,7 +430,8 @@ module IssuablesHelper editable: issuable.dig(:current_user, :can_edit), currentUser: issuable[:current_user], rootPath: root_path, - fullPath: issuable[:project_full_path] + fullPath: issuable[:project_full_path], + timeTrackingLimitToHours: Gitlab::CurrentSettings.time_tracking_limit_to_hours } end diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index 904d650ef96..cf328bcd994 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -82,6 +82,7 @@ module ApplicationSettingImplementation throttle_unauthenticated_enabled: false, throttle_unauthenticated_period_in_seconds: 3600, throttle_unauthenticated_requests_per_period: 3600, + time_tracking_limit_to_hours: false, two_factor_grace_period: 48, unique_ips_limit_enabled: false, unique_ips_limit_per_user: 10, diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index db7fd8524c2..f0256ff4d41 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -3,7 +3,7 @@ module Clusters module Applications class Runner < ApplicationRecord - VERSION = '0.5.2'.freeze + VERSION = '0.6.0'.freeze self.table_name = 'clusters_applications_runners' diff --git a/app/models/deployment.rb b/app/models/deployment.rb index ee6e830d3ec..f0fa5974787 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -84,12 +84,9 @@ class Deployment < ApplicationRecord Commit.truncate_sha(sha) end - def cluster - platform = project.deployment_platform(environment: environment.name) - - if platform.present? && platform.respond_to?(:cluster) - platform.cluster - end + # Deprecated - will be replaced by a persisted cluster_id + def deployment_platform_cluster + environment.deployment_platform&.cluster end def execute_hooks diff --git a/app/views/admin/application_settings/_localization.html.haml b/app/views/admin/application_settings/_localization.html.haml index bb4d1fa1241..e01c123d1db 100644 --- a/app/views/admin/application_settings/_localization.html.haml +++ b/app/views/admin/application_settings/_localization.html.haml @@ -8,4 +8,11 @@ .form-text.text-muted = _('Default first day of the week in calendars and date pickers.') + .form-group + = f.label :time_tracking, _('Time tracking'), class: 'label-bold' + .form-check + = f.check_box :time_tracking_limit_to_hours, class: 'form-check-input' + = f.label :time_tracking_limit_to_hours, class: 'form-check-label' do + = _('Limit display of time tracking units to hours.') + = f.submit _('Save changes'), class: "btn btn-success" diff --git a/app/views/projects/pages_domains/_form.html.haml b/app/views/projects/pages_domains/_form.html.haml index e7edb93f05b..5b657966909 100644 --- a/app/views/projects/pages_domains/_form.html.haml +++ b/app/views/projects/pages_domains/_form.html.haml @@ -11,7 +11,7 @@ - if Gitlab.config.pages.external_https - - auto_ssl_available = ::Gitlab::LetsEncrypt::Client.new.enabled? + - auto_ssl_available = ::Gitlab::LetsEncrypt.enabled?(@domain) - auto_ssl_enabled = @domain.auto_ssl_enabled? - auto_ssl_available_and_enabled = auto_ssl_available && auto_ssl_enabled diff --git a/app/views/shared/boards/components/sidebar/_time_tracker.html.haml b/app/views/shared/boards/components/sidebar/_time_tracker.html.haml index b76d44c5907..43081499920 100644 --- a/app/views/shared/boards/components/sidebar/_time_tracker.html.haml +++ b/app/views/shared/boards/components/sidebar/_time_tracker.html.haml @@ -3,4 +3,5 @@ ":time-spent" => "issue.timeSpent || 0", ":human-time-estimate" => "issue.humanTimeEstimate", ":human-time-spent" => "issue.humanTimeSpent", + ":limit-to-hours" => "timeTrackingLimitToHours", "root-path" => "#{root_url}" } diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml index b24075c7849..ced6af50501 100644 --- a/app/views/shared/milestones/_sidebar.html.haml +++ b/app/views/shared/milestones/_sidebar.html.haml @@ -93,7 +93,11 @@ = milestone.issues_visible_to_user(current_user).closed.count .block - #issuable-time-tracker{ data: { time_estimate: @milestone.total_issue_time_estimate, time_spent: @milestone.total_issue_time_spent, human_time_estimate: @milestone.human_total_issue_time_estimate, human_time_spent: @milestone.human_total_issue_time_spent } } + #issuable-time-tracker{ data: { time_estimate: @milestone.total_issue_time_estimate, + time_spent: @milestone.total_issue_time_spent, + human_time_estimate: @milestone.human_total_issue_time_estimate, + human_time_spent: @milestone.human_total_issue_time_spent, + limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s } } // Fallback while content is loading .title.hide-collapsed = _('Time tracking') diff --git a/app/workers/pages_domain_ssl_renewal_cron_worker.rb b/app/workers/pages_domain_ssl_renewal_cron_worker.rb index 4ca9db922b4..40c34d29970 100644 --- a/app/workers/pages_domain_ssl_renewal_cron_worker.rb +++ b/app/workers/pages_domain_ssl_renewal_cron_worker.rb @@ -5,9 +5,9 @@ class PagesDomainSslRenewalCronWorker include CronjobQueue def perform - return unless ::Gitlab::LetsEncrypt::Client.new.enabled? - PagesDomain.need_auto_ssl_renewal.find_each do |domain| + next unless ::Gitlab::LetsEncrypt.enabled?(domain) + PagesDomainSslRenewalWorker.perform_async(domain.id) end end diff --git a/app/workers/pages_domain_ssl_renewal_worker.rb b/app/workers/pages_domain_ssl_renewal_worker.rb index 00c9c4782d8..b32458ca777 100644 --- a/app/workers/pages_domain_ssl_renewal_worker.rb +++ b/app/workers/pages_domain_ssl_renewal_worker.rb @@ -4,11 +4,9 @@ class PagesDomainSslRenewalWorker include ApplicationWorker def perform(domain_id) - return unless ::Gitlab::LetsEncrypt::Client.new.enabled? - domain = PagesDomain.find_by_id(domain_id) - - return unless domain + return unless domain&.enabled? + return unless ::Gitlab::LetsEncrypt.enabled?(domain) ::PagesDomains::ObtainLetsEncryptCertificateService.new(domain).execute end diff --git a/changelogs/unreleased/30355-use-hours-only-for-time-tracking.yml b/changelogs/unreleased/30355-use-hours-only-for-time-tracking.yml new file mode 100644 index 00000000000..b0252f9e81b --- /dev/null +++ b/changelogs/unreleased/30355-use-hours-only-for-time-tracking.yml @@ -0,0 +1,5 @@ +--- +title: Add option to limit time tracking units to hours +merge_request: 29469 +author: Jon Kolb +type: added diff --git a/changelogs/unreleased/50834-change-http-status-code-when-repository-disabled.yml b/changelogs/unreleased/50834-change-http-status-code-when-repository-disabled.yml new file mode 100644 index 00000000000..b51079d5c74 --- /dev/null +++ b/changelogs/unreleased/50834-change-http-status-code-when-repository-disabled.yml @@ -0,0 +1,5 @@ +--- +title: "Changed HTTP Status Code for disabled repository on /branches and /commits to 404" +merge_request: 29585 +author: Sam Battalio +type: changed diff --git a/changelogs/unreleased/60860-keep-empty-folders-in-tree.yml b/changelogs/unreleased/60860-keep-empty-folders-in-tree.yml new file mode 100644 index 00000000000..237d0fd6aef --- /dev/null +++ b/changelogs/unreleased/60860-keep-empty-folders-in-tree.yml @@ -0,0 +1,5 @@ +--- +title: Keep the empty folders in the tree +merge_request: 29196 +author: +type: fixed diff --git a/changelogs/unreleased/60879-fix-reports-timing-out.yml b/changelogs/unreleased/60879-fix-reports-timing-out.yml new file mode 100644 index 00000000000..845162fe10f --- /dev/null +++ b/changelogs/unreleased/60879-fix-reports-timing-out.yml @@ -0,0 +1,5 @@ +--- +title: Fix reports jobs timing out because of cache +merge_request: 29780 +author: +type: fixed diff --git a/changelogs/unreleased/mh-colon-autocomplete.yml b/changelogs/unreleased/mh-colon-autocomplete.yml new file mode 100644 index 00000000000..8b169c22588 --- /dev/null +++ b/changelogs/unreleased/mh-colon-autocomplete.yml @@ -0,0 +1,5 @@ +--- +title: Allow auto-completing scoped labels +merge_request: 29749 +author: +type: added diff --git a/changelogs/unreleased/sh-recover-ee-schema-backport-migration-failure.yml b/changelogs/unreleased/sh-recover-ee-schema-backport-migration-failure.yml new file mode 100644 index 00000000000..1695e2827eb --- /dev/null +++ b/changelogs/unreleased/sh-recover-ee-schema-backport-migration-failure.yml @@ -0,0 +1,5 @@ +--- +title: Prevent EE backport migrations from running if CE is not migrated +merge_request: 30002 +author: +type: fixed diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-6-0.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-6-0.yml new file mode 100644 index 00000000000..6719fa94b19 --- /dev/null +++ b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-6-0.yml @@ -0,0 +1,5 @@ +--- +title: Update GitLab Runner Helm Chart to 0.6.0 +merge_request: 29982 +author: +type: other diff --git a/config/initializers/forbid_sidekiq_in_transactions.rb b/config/initializers/forbid_sidekiq_in_transactions.rb index deb94d7dbce..a69f1ba090e 100644 --- a/config/initializers/forbid_sidekiq_in_transactions.rb +++ b/config/initializers/forbid_sidekiq_in_transactions.rb @@ -17,7 +17,7 @@ module Sidekiq module NoEnqueueingFromTransactions %i(perform_async perform_at perform_in).each do |name| define_method(name) do |*args| - if !Sidekiq::Worker.skip_transaction_check && AfterCommitQueue.inside_transaction? + if !Sidekiq::Worker.skip_transaction_check && Gitlab::Database.inside_transaction? begin raise Sidekiq::Worker::EnqueueFromTransactionError, <<~MSG `#{self}.#{name}` cannot be called inside a transaction as this can lead to diff --git a/db/migrate/20190402150158_backport_enterprise_schema.rb b/db/migrate/20190402150158_backport_enterprise_schema.rb index 610a8808383..8762cc53ed7 100644 --- a/db/migrate/20190402150158_backport_enterprise_schema.rb +++ b/db/migrate/20190402150158_backport_enterprise_schema.rb @@ -117,6 +117,8 @@ class BackportEnterpriseSchema < ActiveRecord::Migration[5.0] end def up + check_schema! + create_missing_tables update_appearances @@ -868,6 +870,52 @@ class BackportEnterpriseSchema < ActiveRecord::Migration[5.0] remove_column_if_exists(:geo_nodes, :internal_url) end + # Some users may have upgraded to EE at some point but downgraded to + # CE v11.11.3. As a result, their EE tables may not be in the right + # state. Here we check for these such cases and attempt to guide the + # user into recovering from this state by upgrading to v11.11.3 EE + # before installing v12.0.0 CE. + def check_schema! + # The following cases will fail later when this migration attempts + # to add a foreign key for non-existent columns. + columns_to_check = [ + [:epics, :parent_id], # Added in GitLab 11.7 + [:geo_event_log, :cache_invalidation_event_id], # Added in GitLab 11.4 + [:vulnerability_feedback, :merge_request_id] # Added in GitLab 11.9 + ].freeze + + columns_to_check.each do |table, column| + check_ee_columns!(table, column) + end + end + + def check_ee_columns!(table, column) + return unless table_exists?(table) + return if column_exists?(table, column) + + raise_ee_migration_error!(table, column) + end + + def raise_ee_migration_error!(table, column) + message = "Your database is missing the '#{column}' column from the '#{table}' table that is present for GitLab EE." + + message += + if ::Gitlab.ee? + "\nUpgrade your GitLab instance to 11.11.3 EE first!" + else + <<~MSG + + Even though it looks like you're running a CE installation, it appears + you may have installed GitLab EE at some point. To migrate to GitLab 12.0: + + 1. Install GitLab 11.11.3 EE + 2. Install GitLab 12.0.x CE + MSG + end + + raise Exception.new(message) + end + def create_missing_tables create_table_if_not_exists "approval_merge_request_rule_sources", id: :bigserial do |t| t.bigint "approval_merge_request_rule_id", null: false diff --git a/db/migrate/20190513174947_enable_create_incident_issues_by_default.rb b/db/migrate/20190513174947_enable_create_incident_issues_by_default.rb new file mode 100644 index 00000000000..ecd466627fe --- /dev/null +++ b/db/migrate/20190513174947_enable_create_incident_issues_by_default.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class EnableCreateIncidentIssuesByDefault < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + change_default_for :create_issue, from: false, to: true + change_default_for :send_email, from: true, to: false + end + + private + + def change_default_for(column, from:, to:) + change_column_default :project_incident_management_settings, + column, from: from, to: to + end +end diff --git a/db/migrate/20190611090827_add_time_tracking_limit_to_hours_to_application_settings.rb b/db/migrate/20190611090827_add_time_tracking_limit_to_hours_to_application_settings.rb new file mode 100644 index 00000000000..a5f8925c1db --- /dev/null +++ b/db/migrate/20190611090827_add_time_tracking_limit_to_hours_to_application_settings.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddTimeTrackingLimitToHoursToApplicationSettings < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_column_with_default :application_settings, :time_tracking_limit_to_hours, :boolean, default: false, allow_null: false + end + + def down + remove_column :application_settings, :time_tracking_limit_to_hours + end +end diff --git a/db/schema.rb b/db/schema.rb index b81558178b9..a94e5142627 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -229,6 +229,7 @@ ActiveRecord::Schema.define(version: 20190620112608) do t.integer "custom_project_templates_group_id" t.boolean "elasticsearch_limit_indexing", default: false, null: false t.string "geo_node_allowed_ips", default: "0.0.0.0/0, ::/0" + t.boolean "time_tracking_limit_to_hours", default: false, null: false t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id", using: :btree t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id", using: :btree t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree @@ -2502,9 +2503,9 @@ ActiveRecord::Schema.define(version: 20190620112608) do t.index ["project_id"], name: "index_project_import_data_on_project_id", using: :btree end - create_table "project_incident_management_settings", primary_key: "project_id", id: :integer, default: nil, force: :cascade do |t| - t.boolean "create_issue", default: false, null: false - t.boolean "send_email", default: true, null: false + create_table "project_incident_management_settings", primary_key: "project_id", id: :serial, force: :cascade do |t| + t.boolean "create_issue", default: true, null: false + t.boolean "send_email", default: false, null: false t.text "issue_template_key" end diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md index 54279897e04..1f2961ea39a 100644 --- a/doc/administration/auth/ldap.md +++ b/doc/administration/auth/ldap.md @@ -396,21 +396,34 @@ omniauth-ldap. ### Escaping special characters -If the `user_filter` DN contains special characters. For example, a comma: +The `user_filter` DN can contain special characters. For example: -``` -OU=GitLab, Inc,DC=gitlab,DC=com -``` +- A comma: -This character needs to be escaped as documented in [RFC 4515](https://tools.ietf.org/search/rfc4515). + ``` + OU=GitLab, Inc,DC=gitlab,DC=com + ``` -Due to the way the string is parsed, the special character needs to be converted -to hex and `\\5C\\` (`5C` = `\` in hex) added before it. -As an example the above DN would look like +- Open and close brackets: -``` -OU=GitLab\\5C\\2C Inc,DC=gitlab,DC=com -``` + ``` + OU=Gitlab (Inc),DC=gitlab,DC=com + ``` + + These characters must be escaped as documented in + [RFC 4515](https://tools.ietf.org/search/rfc4515). + +- Escape commas with `\2C`. For example: + + ``` + OU=GitLab\2C Inc,DC=gitlab,DC=com + ``` + +- Escape open and close brackets with `\28` and `\29`, respectively. For example: + + ``` + OU=Gitlab \28Inc\29,DC=gitlab,DC=com + ``` ## Enabling LDAP sign-in for existing GitLab users diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md index 00422ec347c..6e48add6930 100644 --- a/doc/administration/auth/oidc.md +++ b/doc/administration/auth/oidc.md @@ -144,20 +144,20 @@ for more details: If you're having trouble, here are some tips: 1. Ensure `discovery` is set to `true`. Setting it to `false` requires -specifying all the URLs and keys required to make OpenID work. + specifying all the URLs and keys required to make OpenID work. 1. Check your system clock to ensure the time is synchronized properly. 1. As mentioned in [the -documentation](https://github.com/m0n9oose/omniauth_openid_connect), -make sure `issuer` corresponds to the base URL of the Discovery URL. For -example, `https://accounts.google.com` is used for the URL -`https://accounts.google.com/.well-known/openid-configuration`. + documentation](https://github.com/m0n9oose/omniauth_openid_connect), + make sure `issuer` corresponds to the base URL of the Discovery URL. For + example, `https://accounts.google.com` is used for the URL + `https://accounts.google.com/.well-known/openid-configuration`. 1. The OpenID Connect client uses HTTP Basic Authentication to send the -OAuth2 access token. For example, if you are seeing 401 errors upon -retrieving the `userinfo` endpoint, you may want to check your OpenID -Web server configuration. For example, for -[oauth2-server-php](https://github.com/bshaffer/oauth2-server-php), you -may need to [add a configuration parameter to -Apache](https://github.com/bshaffer/oauth2-server-php/issues/926#issuecomment-387502778). + OAuth2 access token. For example, if you are seeing 401 errors upon + retrieving the `userinfo` endpoint, you may want to check your OpenID + Web server configuration. For example, for + [oauth2-server-php](https://github.com/bshaffer/oauth2-server-php), you + may need to [add a configuration parameter to + Apache](https://github.com/bshaffer/oauth2-server-php/issues/926#issuecomment-387502778). diff --git a/doc/administration/geo/replication/faq.md b/doc/administration/geo/replication/faq.md index dd1af0dbf9c..c527248bc72 100644 --- a/doc/administration/geo/replication/faq.md +++ b/doc/administration/geo/replication/faq.md @@ -6,8 +6,8 @@ The requirements are listed [on the index page](index.md#requirements-for-runnin ## How does Geo know which projects to sync? -On each **secondary** node, there is a read-only replicated copy of the GitLab database. -A **secondary** node also has a tracking database where it stores which projects have been synced. +On each **secondary** node, there is a read-only replicated copy of the GitLab database. +A **secondary** node also has a tracking database where it stores which projects have been synced. Geo compares the two databases to find projects that are not yet tracked. At the start, this tracking database is empty, so Geo will start trying to update from every project that it can see in the GitLab database. @@ -15,19 +15,19 @@ At the start, this tracking database is empty, so Geo will start trying to updat For each project to sync: 1. Geo will issue a `git fetch geo --mirror` to get the latest information from the **primary** node. -If there are no changes, the sync will be fast and end quickly. Otherwise, it will pull the latest commits. + If there are no changes, the sync will be fast and end quickly. Otherwise, it will pull the latest commits. 1. The **secondary** node will update the tracking database to store the fact that it has synced projects A, B, C, etc. 1. Repeat until all projects are synced. -When someone pushes a commit to the **primary** node, it generates an event in the GitLab database that the repository has changed. +When someone pushes a commit to the **primary** node, it generates an event in the GitLab database that the repository has changed. The **secondary** node sees this event, marks the project in question as dirty, and schedules the project to be resynced. To ensure that problems with pipelines (for example, syncs failing too many times or jobs being lost) don't permanently stop projects syncing, Geo also periodically checks the tracking database for projects that are marked as dirty. This check happens when -the number of concurrent syncs falls below `repos_max_capacity` and there are no new projects waiting to be synced. +the number of concurrent syncs falls below `repos_max_capacity` and there are no new projects waiting to be synced. -Geo also has a checksum feature which runs a SHA256 sum across all the Git references to the SHA values. -If the refs don't match between the **primary** node and the **secondary** node, then the **secondary** node will mark that project as dirty and try to resync it. -So even if we have an outdated tracking database, the validation should activate and find discrepancies in the repository state and resync. +Geo also has a checksum feature which runs a SHA256 sum across all the Git references to the SHA values. +If the refs don't match between the **primary** node and the **secondary** node, then the **secondary** node will mark that project as dirty and try to resync it. +So even if we have an outdated tracking database, the validation should activate and find discrepancies in the repository state and resync. ## Can I use Geo in a disaster recovery situation? diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md index 846afd8f5f4..5394e6dd763 100644 --- a/doc/administration/geo/replication/troubleshooting.md +++ b/doc/administration/geo/replication/troubleshooting.md @@ -331,7 +331,7 @@ There are a few key points to remember: 1. The FDW settings are configured on the Geo **tracking** database. 1. The configured foreign server enables a login to the Geo -**secondary**, read-only database. + **secondary**, read-only database. By default, the Geo secondary and tracking database are running on the same host on different ports. That is, 5432 and 5431 respectively. @@ -350,7 +350,7 @@ To check the configuration: ``` 1. Check whether any tables are present. If everything is working, you -should see something like this: + should see something like this: ```sql gitlabhq_geo_production=# SELECT * from information_schema.foreign_tables; diff --git a/doc/administration/high_availability/database.md b/doc/administration/high_availability/database.md index 4db53353218..20bbfdb2603 100644 --- a/doc/administration/high_availability/database.md +++ b/doc/administration/high_availability/database.md @@ -83,7 +83,7 @@ deploy the bundled PostgreSQL. plain text password. These will be necessary when configuring the GitLab application servers later. 1. [Enable monitoring](#enable-monitoring) - + Advanced configuration options are supported and can be added if needed. @@ -204,9 +204,9 @@ Few notes on the service itself: - The service runs under a system account, by default `gitlab-consul`. - If you are using a different username, you will have to specify it. We -will refer to it with `CONSUL_USERNAME`, + will refer to it with `CONSUL_USERNAME`, - There will be a database user created with read only access to the repmgr -database + database - Passwords will be stored in the following locations: - `/etc/gitlab/gitlab.rb`: hashed - `/var/opt/gitlab/pgbouncer/pg_auth`: hashed diff --git a/doc/api/search.md b/doc/api/search.md index da81c8321c9..abb77ae05dc 100644 --- a/doc/api/search.md +++ b/doc/api/search.md @@ -285,7 +285,7 @@ Example response: ### Scope: wiki_blobs **[STARTER]** -This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. +This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. ```bash curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/search?scope=wiki_blobs&search=bye @@ -346,6 +346,7 @@ Example response: This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. Filters are available for this scope: + - filename - path - extension @@ -679,6 +680,7 @@ Example response: This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. Filters are available for this scope: + - filename - path - extension diff --git a/doc/api/settings.md b/doc/api/settings.md index c2a1f7feefd..eb3f39e6670 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -231,6 +231,7 @@ are listed in the descriptions of the relevant settings. | `throttle_unauthenticated_enabled` | boolean | no | (**If enabled, requires:** `throttle_unauthenticated_period_in_seconds` and `throttle_unauthenticated_requests_per_period`) Enable unauthenticated request rate limit. Helps reduce request volume (e.g. from crawlers or abusive bots). | | `throttle_unauthenticated_period_in_seconds` | integer | required by: `throttle_unauthenticated_enabled` | Rate limit period in seconds. | | `throttle_unauthenticated_requests_per_period` | integer | required by: `throttle_unauthenticated_enabled` | Max requests per period per IP. | +| `time_tracking_limit_to_hours` | boolean | no | Limit display of time tracking units to hours. Default is `false`. | | `two_factor_grace_period` | integer | required by: `require_two_factor_authentication` | Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication. | | `unique_ips_limit_enabled` | boolean | no | (**If enabled, requires:** `unique_ips_limit_per_user` and `unique_ips_limit_time_window`) Limit sign in from multiple ips. | | `unique_ips_limit_per_user` | integer | required by: `unique_ips_limit_enabled` | Maximum number of ips per user. | diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index e4dc289dbdb..f752f942e24 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -489,6 +489,7 @@ it's provided as an environment variable. This is because GitLab Runnner uses ** runtime. ### Using statically-defined credentials + As an example, let's assume that you want to use the `registry.example.com:5000/private/image:latest` image which is private and requires you to login into a private container registry. @@ -566,7 +567,6 @@ for the Runner to match the `DOCKER_AUTH_CONFIG`. For example, if then the `DOCKER_AUTH_CONFIG` must also specify `registry.example.com:5000`. Specifying only `registry.example.com` will not work. - ### Using Credentials Store > Support for using Credentials Store was added in GitLab Runner 9.5. @@ -574,7 +574,7 @@ Specifying only `registry.example.com` will not work. To configure credentials store, follow these steps: 1. To use a credentials store, you need an external helper program to interact with a specific keychain or external store. -Make sure helper program is available in GitLab Runner `$PATH`. + Make sure helper program is available in GitLab Runner `$PATH`. 1. Make GitLab Runner use it. There are two ways to accomplish this. Either: - Create a diff --git a/doc/ci/examples/test-scala-application.md b/doc/ci/examples/test-scala-application.md index 0e33a1ba060..bd899240307 100644 --- a/doc/ci/examples/test-scala-application.md +++ b/doc/ci/examples/test-scala-application.md @@ -47,10 +47,10 @@ deploy: In the above configuration: - The `before_script` installs [SBT](http://www.scala-sbt.org/) and -displays the version that is being used. + displays the version that is being used. - The `test` stage executes SBT to compile and test the project. - - [sbt-scoverage](https://github.com/scoverage/sbt-scoverage) is used as an SBT -plugin to measure test coverage. + - [sbt-scoverage](https://github.com/scoverage/sbt-scoverage) is used as an SBT + plugin to measure test coverage. - The `deploy` stage automatically deploys the project to Heroku using dpl. You can use other versions of Scala and SBT by defining them in diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index c8c92002be2..1b50273eca2 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -339,7 +339,7 @@ Group-level variables can be added by: 1. Navigating to your group's **Settings > CI/CD** page. 1. Inputing variable types, keys, and values in the **Variables** section. -Any variables of [subgroups](../../user/group/subgroups/index.md) will be inherited recursively. + Any variables of [subgroups](../../user/group/subgroups/index.md) will be inherited recursively. Once you set them, they will be available for all subsequent pipelines. diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md index 4655eec51de..7dbb9af2869 100644 --- a/doc/ci/variables/predefined_variables.md +++ b/doc/ci/variables/predefined_variables.md @@ -30,7 +30,7 @@ future GitLab releases.** | `CI_BUILDS_DIR` | all | 11.10 | Top-level directory where builds are executed. | | `CI_CONCURRENT_ID` | all | 11.10 | Unique ID of build execution within a single executor. | | `CI_CONCURRENT_PROJECT_ID` | all | 11.10 | Unique ID of build execution within a single executor and project. | -| `CI_COMMIT_BEFORE_SHA` | 11.2 | all | The previous latest commit present on a branch before a push request. | +| `CI_COMMIT_BEFORE_SHA` | 11.2 | all | The previous latest commit present on a branch before a push request. Only populated when there is a merge request associated with the pipeline. | | `CI_COMMIT_DESCRIPTION` | 10.8 | all | The description of the commit: the message without first line, if the title is shorter than 100 characters; full message in other case. | | `CI_COMMIT_MESSAGE` | 10.8 | all | The full commit message. | | `CI_COMMIT_REF_NAME` | 9.0 | all | The branch or tag name for which project is built | diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md index f7f36901fe1..2ed2a905db7 100644 --- a/doc/development/api_graphql_styleguide.md +++ b/doc/development/api_graphql_styleguide.md @@ -198,9 +198,9 @@ abilities as in the Rails app. If the: - Currently authenticated user fails the authorization, the authorized -resource will be returned as `null`. + resource will be returned as `null`. - Resource is part of a collection, the collection will be filtered to -exclude the objects that the user's authorization checks failed against. + exclude the objects that the user's authorization checks failed against. TIP: **Tip:** Try to load only what the currently authenticated user is allowed to @@ -496,4 +496,4 @@ it 'returns a successful response' do expect(response).to have_gitlab_http_status(:success) expect(graphql_mutation_response(:merge_request_set_wip)['errors']).to be_empty end -```
\ No newline at end of file +``` diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md index 8a4aa5dfa7f..6064f59ed10 100644 --- a/doc/development/contributing/merge_request_workflow.md +++ b/doc/development/contributing/merge_request_workflow.md @@ -126,16 +126,16 @@ When writing commit messages, please follow the guidelines below: - The commit subject must contain at least 3 words. - The commit subject should ideally contain up to 50 characters, -and must not be longer than 72 characters. + and must not be longer than 72 characters. - The commit subject must start with a capital letter. - The commit subject must not end with a period. - The commit subject and body must be separated by a blank line. - The commit body must not contain more than 72 characters per line. - Commits that change 30 or more lines across at least 3 files must -describe these changes in the commit body. + describe these changes in the commit body. - The commit subject or body must not contain Emojis. - Use issues and merge requests' full URLs instead of short references, -as they are displayed as plain text outside of GitLab. + as they are displayed as plain text outside of GitLab. - The merge request must not contain more than 10 commit messages. If the guidelines are not met, the MR will not pass the diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index c7fa40af930..418e58b22d5 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -76,10 +76,10 @@ After a given documentation path is aligned across CE and EE, all merge requests affecting that path must be submitted to CE, regardless of the content it has. This means that: -* For **EE-only docs changes**, you only have to submit a CE MR. -* For **EE-only features** that touch both the code and the docs, you have to submit -an EE MR containing all changes, and a CE MR containing only the docs changes -and without a changelog entry. +- For **EE-only docs changes**, you only have to submit a CE MR. +- For **EE-only features** that touch both the code and the docs, you have to submit + an EE MR containing all changes, and a CE MR containing only the docs changes + and without a changelog entry. This might seem like a duplicate effort, but it's only for the short term. A list of the already aligned docs can be found in diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index b8506a72666..23d52a33881 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -165,8 +165,8 @@ The table below shows what kind of documentation goes where. `doc/topics/topic-name/subtopic-name/index.md` when subtopics become necessary. General user- and admin- related documentation, should be placed accordingly. 1. The directories `/workflow/`, `/university/`, and `/articles/` have -been **deprecated** and the majority their docs have been moved to their correct location -in small iterations. + been **deprecated** and the majority their docs have been moved to their correct location + in small iterations. If you are unsure where a document or a content addition should live, this should not stop you from authoring and contributing. You can use your best judgment and diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md index cca52706ddc..6b416cf588c 100644 --- a/doc/development/ee_features.md +++ b/doc/development/ee_features.md @@ -909,11 +909,12 @@ import bundle from 'ee_else_ce/protected_branches/protected_branches_bundle.js'; See the frontend guide [performance section](fe_guide/performance.md) for information on managing page-specific javascript within EE. - ## Vue code in `assets/javascript` + ### script tag #### Child Component only used in EE + To separate Vue template differences we should [async import the components](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components). Doing this allows for us to load the correct component in EE whilst in CE @@ -937,10 +938,12 @@ export default { ``` #### For JS code that is EE only, like props, computed properties, methods, etc, we will keep the current approach - - Since we [can't async load a mixin](https://github.com/vuejs/vue-loader/issues/418#issuecomment-254032223) we will use the [`ee_else_ce`](../development/ee_features.md#javascript-code-in-assetsjavascripts) alias we already have for webpack. + +- Since we [can't async load a mixin](https://github.com/vuejs/vue-loader/issues/418#issuecomment-254032223) we will use the [`ee_else_ce`](../development/ee_features.md#javascript-code-in-assetsjavascripts) alias we already have for webpack. - This means all the EE specific props, computed properties, methods, etc that are EE only should be in a mixin in the `ee/` folder and we need to create a CE counterpart of the mixin ##### Example: + ```javascript import mixin from 'ee_else_ce/path/mixin'; @@ -955,6 +958,7 @@ import mixin from 'ee_else_ce/path/mixin'; - You can see an MR with an example [here](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9762) #### `template` tag + * **EE Child components** - Since we are using the async loading to check which component to load, we'd still use the component's name, check [this example](#child-component-only-used-in-ee). @@ -962,11 +966,12 @@ import mixin from 'ee_else_ce/path/mixin'; - For the templates that have extra HTML in EE we should move it into a new component and use the `ee_else_ce` dynamic import ### Non Vue Files + For regular JS files, the approach is similar. 1. We will keep using the [`ee_else_ce`](../development/ee_features.md#javascript-code-in-assetsjavascripts) helper, this means that EE only code should be inside the `ee/` folder. - 1. An EE file should be created with the EE only code, and it should extend the CE counterpart. - 1. For code inside functions that can't be extended, the code should be moved into a new file and we should use `ee_else_ce` helper: + 1. An EE file should be created with the EE only code, and it should extend the CE counterpart. + 1. For code inside functions that can't be extended, the code should be moved into a new file and we should use `ee_else_ce` helper: ##### Example: @@ -996,6 +1001,7 @@ to isolate such ruleset from rest of CE rules (along with adding comment describ to avoid conflicts during CE to EE merge. #### Bad + ```scss .section-body { .section-title { @@ -1011,6 +1017,7 @@ to avoid conflicts during CE to EE merge. ``` #### Good + ```scss .section-body { .section-title { diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md index c8c70fa7216..94b3796f6e9 100644 --- a/doc/development/elasticsearch.md +++ b/doc/development/elasticsearch.md @@ -64,20 +64,25 @@ All indexing after the initial one is done via `ElasticIndexerWorker` (sidekiq j Search queries are generated by the concerns found in [ee/app/models/concerns/elastic](https://gitlab.com/gitlab-org/gitlab-ee/tree/master/ee/app/models/concerns/elastic). These concerns are also in charge of access control, and have been a historic source of security bugs so please pay close attention to them! ## Existing Analyzers/Tokenizers/Filters + These are all defined in https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/elasticsearch/git/model.rb ### Analyzers + #### `path_analyzer` + Used when indexing blobs' paths. Uses the `path_tokenizer` and the `lowercase` and `asciifolding` filters. Please see the `path_tokenizer` explanation below for an example. #### `sha_analyzer` + Used in blobs and commits. Uses the `sha_tokenizer` and the `lowercase` and `asciifolding` filters. Please see the `sha_tokenizer` explanation later below for an example. #### `code_analyzer` + Used when indexing a blob's filename and content. Uses the `whitespace` tokenizer and the filters: `code`, `edgeNGram_filter`, `lowercase`, and `asciifolding` The `whitespace` tokenizer was selected in order to have more control over how tokens are split. For example the string `Foo::bar(4)` needs to generate tokens like `Foo` and `bar(4)` in order to be properly searched. @@ -85,15 +90,19 @@ The `whitespace` tokenizer was selected in order to have more control over how t Please see the `code` filter for an explanation on how tokens are split. #### `code_search_analyzer` + Not directly used for indexing, but rather used to transform a search input. Uses the `whitespace` tokenizer and the `lowercase` and `asciifolding` filters. ### Tokenizers + #### `sha_tokenizer` + This is a custom tokenizer that uses the [`edgeNGram` tokenizer](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/analysis-edgengram-tokenizer.html) to allow SHAs to be searcheable by any sub-set of it (minimum of 5 chars). -example: +Example: `240c29dc7e` becomes: + - `240c2` - `240c29` - `240c29d` @@ -102,21 +111,26 @@ example: - `240c29dc7e` #### `path_tokenizer` + This is a custom tokenizer that uses the [`path_hierarchy` tokenizer](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/analysis-pathhierarchy-tokenizer.html) with `reverse: true` in order to allow searches to find paths no matter how much or how little of the path is given as input. -example: +Example: `'/some/path/application.js'` becomes: + - `'/some/path/application.js'` - `'some/path/application.js'` - `'path/application.js'` - `'application.js'` ### Filters + #### `code` -Uses a [Pattern Capture token filter](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/analysis-pattern-capture-tokenfilter.html) to split tokens into more easily searched versions of themselves. + +Uses a [Pattern Capture token filter](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/analysis-pattern-capture-tokenfilter.html) to split tokens into more easily searched versions of themselves. Patterns: + - `"(\\p{Ll}+|\\p{Lu}\\p{Ll}+|\\p{Lu}+)"`: captures CamelCased and lowedCameCased strings as separate tokens - `"(\\d+)"`: extracts digits - `"(?=([\\p{Lu}]+[\\p{L}]+))"`: captures CamelCased strings recursively. Ex: `ThisIsATest` => `[ThisIsATest, IsATest, ATest, Test]` @@ -126,6 +140,7 @@ Patterns: - `'\/?([^\/]+)(?=\/|\b)'`: separate path terms `like/this/one` #### `edgeNGram_filter` + Uses an [Edge NGram token filter](https://www.elastic.co/guide/en/elasticsearch/reference/5.5/analysis-edgengram-tokenfilter.html) to allow inputs with only parts of a token to find the token. For example it would turn `glasses` into permutations starting with `gl` and ending with `glasses`, which would allow a search for "`glass`" to find the original token `glasses` ## Gotchas @@ -140,13 +155,13 @@ Uses an [Edge NGram token filter](https://www.elastic.co/guide/en/elasticsearch/ You might get an error such as ``` -[2018-10-31T15:54:19,762][WARN ][o.e.c.r.a.DiskThresholdMonitor] [pval5Ct] - flood stage disk watermark [95%] exceeded on - [pval5Ct7SieH90t5MykM5w][pval5Ct][/usr/local/var/lib/elasticsearch/nodes/0] free: 56.2gb[3%], +[2018-10-31T15:54:19,762][WARN ][o.e.c.r.a.DiskThresholdMonitor] [pval5Ct] + flood stage disk watermark [95%] exceeded on + [pval5Ct7SieH90t5MykM5w][pval5Ct][/usr/local/var/lib/elasticsearch/nodes/0] free: 56.2gb[3%], all indices on this node will be marked read-only ``` -This is because you've exceeded the disk space threshold - it thinks you don't have enough disk space left, based on the default 95% threshold. +This is because you've exceeded the disk space threshold - it thinks you don't have enough disk space left, based on the default 95% threshold. In addition, the `read_only_allow_delete` setting will be set to `true`. It will block indexing, `forcemerge`, etc @@ -158,16 +173,16 @@ Add this to your `elasticsearch.yml` file: ``` # turn off the disk allocator -cluster.routing.allocation.disk.threshold_enabled: false +cluster.routing.allocation.disk.threshold_enabled: false ``` _or_ ``` # set your own limits -cluster.routing.allocation.disk.threshold_enabled: true +cluster.routing.allocation.disk.threshold_enabled: true cluster.routing.allocation.disk.watermark.flood_stage: 5gb # ES 6.x only -cluster.routing.allocation.disk.watermark.low: 15gb +cluster.routing.allocation.disk.watermark.low: 15gb cluster.routing.allocation.disk.watermark.high: 10gb ``` diff --git a/doc/development/geo.md b/doc/development/geo.md index 6e59fab34c7..a10f13b069f 100644 --- a/doc/development/geo.md +++ b/doc/development/geo.md @@ -14,10 +14,10 @@ Geo handles replication for different components: - [Database](#database-replication): includes the entire application, except cache and jobs. - [Git repositories](#repository-replication): includes both projects and wikis. - [Uploaded blobs](#uploads-replication): includes anything from images attached on issues -to raw logs and assets from CI. + to raw logs and assets from CI. With the exception of the Database replication, on a *secondary* node, everything is coordinated -by the [Geo Log Cursor](#geo-log-cursor). +by the [Geo Log Cursor](#geo-log-cursor). ### Geo Log Cursor daemon @@ -31,8 +31,8 @@ picks the event up and schedules a `Geo::ProjectSyncWorker` job which will use the `Geo::RepositorySyncService` and `Geo::WikiSyncService` classes to update the repository and the wiki respectively. -The Geo Log Cursor daemon can operate in High Availability mode automatically. -The daemon will try to acquire a lock from time to time and once acquired, it +The Geo Log Cursor daemon can operate in High Availability mode automatically. +The daemon will try to acquire a lock from time to time and once acquired, it will behave as the *active* daemon. Any additional running daemons on the same node, will be in standby @@ -164,20 +164,20 @@ The Git Push Proxy exists as a functionality built inside the `gitlab-shell` com It is active on a **secondary** node only. It allows the user that has cloned a repository from the secondary node to push to the same URL. -Git `push` requests directed to a **secondary** node will be sent over to the **primary** node, +Git `push` requests directed to a **secondary** node will be sent over to the **primary** node, while `pull` requests will continue to be served by the **secondary** node for maximum efficiency. HTTPS and SSH requests are handled differently: - With HTTPS, we will give the user a `HTTP 302 Redirect` pointing to the project on the **primary** node. -The git client is wise enough to understand that status code and process the redirection. + The git client is wise enough to understand that status code and process the redirection. - With SSH, because there is no equivalent way to perform a redirect, we have to proxy the request. -This is done inside [`gitlab-shell`](https://gitlab.com/gitlab-org/gitlab-shell), by first translating the request -to the HTTP protocol, and then proxying it to the **primary** node. + This is done inside [`gitlab-shell`](https://gitlab.com/gitlab-org/gitlab-shell), by first translating the request + to the HTTP protocol, and then proxying it to the **primary** node. -The [`gitlab-shell`](https://gitlab.com/gitlab-org/gitlab-shell) daemon knows when to proxy based on the response -from `/api/v4/allowed`. A special `HTTP 300` status code is returned and we execute a "custom action", -specified in the response body. The response contains additional data that allows the proxied `push` operation +The [`gitlab-shell`](https://gitlab.com/gitlab-org/gitlab-shell) daemon knows when to proxy based on the response +from `/api/v4/allowed`. A special `HTTP 300` status code is returned and we execute a "custom action", +specified in the response body. The response contains additional data that allows the proxied `push` operation to happen on the **primary** node. ## Using the Tracking Database @@ -229,17 +229,17 @@ named `gitlab_secondary`. This configuration exists within the database's user context only. To access the `gitlab_secondary`, GitLab needs to use the same database user that had previously been configured. -The Geo Tracking Database accesses the readonly database replica via FDW as a regular user, -limited by its own restrictions. The credentials are configured as a -`USER MAPPING` associated with the `SERVER` mapped previously +The Geo Tracking Database accesses the readonly database replica via FDW as a regular user, +limited by its own restrictions. The credentials are configured as a +`USER MAPPING` associated with the `SERVER` mapped previously (`gitlab_secondary`). FDW configuration and credentials definition are managed automatically by the -Omnibus GitLab `gitlab-ctl reconfigure` command. +Omnibus GitLab `gitlab-ctl reconfigure` command. #### Refeshing the Foreign Tables -Whenever a new Geo node is configured or the database schema changes on the +Whenever a new Geo node is configured or the database schema changes on the **primary** node, you must refresh the foreign tables on the **secondary** node by running the following: @@ -279,11 +279,11 @@ on the Tracking Database: SELECT project_registry.* FROM project_registry JOIN gitlab_secondary.projects - ON (project_registry.project_id = gitlab_secondary.projects.id + ON (project_registry.project_id = gitlab_secondary.projects.id AND gitlab_secondary.projects.archived IS FALSE) ``` -At the ActiveRecord level, we have additional Models that represent the +At the ActiveRecord level, we have additional Models that represent the foreign tables. They must be mapped in a slightly different way, and they are read-only. Check the existing FDW models in `ee/app/models/geo/fdw` for reference. diff --git a/doc/development/testing_guide/end_to_end/dynamic_element_validation.md b/doc/development/testing_guide/end_to_end/dynamic_element_validation.md index f7b3ca8bc89..aec0a3ede5a 100644 --- a/doc/development/testing_guide/end_to_end/dynamic_element_validation.md +++ b/doc/development/testing_guide/end_to_end/dynamic_element_validation.md @@ -5,8 +5,8 @@ We devised a solution to solve common test automation problems such as the dread Other problems that dynamic element validations solve are... - When we perform an action with the mouse, we expect something to occur. -- When our test is navigating to (or from) a page, we ensure that we are on the page we expect before -test continuation. +- When our test is navigating to (or from) a page, we ensure that we are on the page we expect before + test continuation. ## How it works @@ -19,7 +19,7 @@ We interpret user actions on the page to have some sort of effect. These actions When a page is navigated to, there are elements that will always appear on the page unconditionally. -Dynamic element validation is instituted when using +Dynamic element validation is instituted when using ```ruby Runtime::Browser.visit(:gitlab, Some::Page) @@ -27,7 +27,7 @@ Runtime::Browser.visit(:gitlab, Some::Page) ### Clicks -When we perform a click within our tests, we expect something to occur. That something could be a component to now +When we perform a click within our tests, we expect something to occur. That something could be a component to now appear on the webpage, or the test to navigate away from the page entirely. Dynamic element validation is instituted when using @@ -71,7 +71,7 @@ class MyPage < Page::Base element :another_element, required: true element :conditional_element end - + def open_layer click_element :my_element, Layer::MyLayer end @@ -95,7 +95,7 @@ execute_stuff ``` will invoke GitLab QA to scan `MyPage` for `my_element` and `another_element` to be on the page before continuing to -`execute_stuff` +`execute_stuff` ### Clicking diff --git a/doc/development/testing_guide/end_to_end/page_objects.md b/doc/development/testing_guide/end_to_end/page_objects.md index 73e1fd862c1..05cb03eb4bd 100644 --- a/doc/development/testing_guide/end_to_end/page_objects.md +++ b/doc/development/testing_guide/end_to_end/page_objects.md @@ -82,7 +82,7 @@ module Page end # ... - end + end end end ``` @@ -134,7 +134,7 @@ for each element defined. In our case, `qa-login-field`, `qa-password-field` and `qa-sign-in-button` -**app/views/my/view.html.haml** +**app/views/my/view.html.haml** ```haml = f.text_field :login, class: "form-control top qa-login-field", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required." @@ -146,7 +146,7 @@ Things to note: - The CSS class must be `kebab-cased` (separated with hyphens "`-`") - If the element appears on the page unconditionally, add `required: true` to the element. See -[Dynamic element validation](dynamic_element_validation.md) + [Dynamic element validation](dynamic_element_validation.md) ## Running the test locally diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md index 5950737b964..e6496ae3a2e 100644 --- a/doc/integration/jenkins.md +++ b/doc/integration/jenkins.md @@ -25,22 +25,22 @@ and [Migrating from Jenkins to GitLab](https://www.youtube.com/watch?v=RlEVGOpYF ## Use cases - Suppose you are new to GitLab, and want to keep using Jenkins until you prepare -your projects to build with [GitLab CI/CD](../ci/README.md). You set up the -integration between GitLab and Jenkins, then you migrate to GitLab CI later. While -you organize yourself and your team to onboard GitLab, you keep your pipelines -running with Jenkins, but view the results in your project's repository in GitLab. + your projects to build with [GitLab CI/CD](../ci/README.md). You set up the + integration between GitLab and Jenkins, then you migrate to GitLab CI later. While + you organize yourself and your team to onboard GitLab, you keep your pipelines + running with Jenkins, but view the results in your project's repository in GitLab. - Your team uses [Jenkins Plugins](https://plugins.jenkins.io/) for other proceedings, -therefore, you opt for keep using Jenkins to build your apps. Show the results of your -pipelines directly in GitLab. + therefore, you opt for keep using Jenkins to build your apps. Show the results of your + pipelines directly in GitLab. For a real use case, read the blog post [Continuous integration: From Jenkins to GitLab using Docker](https://about.gitlab.com/2017/07/27/docker-my-precious/). ## Requirements -* [Jenkins GitLab Plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+Plugin) -* [Jenkins Git Plugin](https://wiki.jenkins.io/display/JENKINS/Git+Plugin) -* Git clone access for Jenkins from the GitLab repository -* GitLab API access to report build status +- [Jenkins GitLab Plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+Plugin) +- [Jenkins Git Plugin](https://wiki.jenkins.io/display/JENKINS/Git+Plugin) +- Git clone access for Jenkins from the GitLab repository +- GitLab API access to report build status ## Configure GitLab users @@ -65,7 +65,7 @@ Go to Manage Jenkins -> Configure System and scroll down to the 'GitLab' section Enter the GitLab server URL in the 'GitLab host URL' field and paste the API token copied earlier in the 'API Token' field. -For more information, see GitLab Plugin documentation about +For more information, see GitLab Plugin documentation about [Jenkins-to-GitLab authentication](https://github.com/jenkinsci/gitlab-plugin#jenkins-to-gitlab-authentication) ![Jenkins GitLab plugin configuration](img/jenkins_gitlab_plugin_config.png) @@ -76,8 +76,8 @@ Follow the GitLab Plugin documentation about [Jenkins Job Configuration](https:/ NOTE: **Note:** Be sure to include the steps about [Build status configuration](https://github.com/jenkinsci/gitlab-plugin#build-status-configuration). -The 'Publish build status to GitLab' post-build step is required to view -Jenkins build status in GitLab Merge Requests. +The 'Publish build status to GitLab' post-build step is required to view +Jenkins build status in GitLab Merge Requests. ## Configure a GitLab project @@ -114,21 +114,21 @@ and storing build status for Commits and Merge Requests. All steps are implemented using AJAX requests on the merge request page. 1. In order to display the build status in a merge request you must create a project service in GitLab. -2. Your project service will do a (JSON) query to a URL of the CI tool with the SHA1 of the commit. -3. The project service builds this URL and payload based on project service settings and knowledge of the CI tool. -4. The response is parsed to give a response in GitLab (success/failed/pending). +1. Your project service will do a (JSON) query to a URL of the CI tool with the SHA1 of the commit. +1. The project service builds this URL and payload based on project service settings and knowledge of the CI tool. +1. The response is parsed to give a response in GitLab (success/failed/pending). ## Troubleshooting ### Error in merge requests - "Could not connect to the CI server" This integration relies on Jenkins reporting the build status back to GitLab via -the [Commit Status API](../api/commits.md#commit-status). +the [Commit Status API](../api/commits.md#commit-status). The error 'Could not connect to the CI server' usually means that GitLab did not receive a build status update via the API. Either Jenkins was not properly -configured or there was an error reporting the status via the API. +configured or there was an error reporting the status via the API. 1. [Configure the Jenkins server](#configure-the-jenkins-server) for GitLab API access -2. [Configure a Jenkins project](#configure-a-jenkins-project), including the - 'Publish build status to GitLab' post-build action. +1. [Configure a Jenkins project](#configure-a-jenkins-project), including the + 'Publish build status to GitLab' post-build action. diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md index 37051f6b10f..1e9c53b9811 100644 --- a/doc/subscriptions/index.md +++ b/doc/subscriptions/index.md @@ -14,7 +14,7 @@ Learn how GitLab helps you in the stages of the DevOps lifecycle by learning mor ### Self-managed: Install GitLab -Take a look at [installing GitLab](https://about.gitlab.com/install/) and our [administrator documentation](../administration/index.md). Then, follow the instructions below under [Your subscription](#your-subscription) to apply your license file. +Take a look at [installing GitLab](https://about.gitlab.com/install/) and our [administrator documentation](../administration/index.md). Then, follow the instructions below under [Your subscription](#your-subscription) to apply your license file. ### GitLab.com: Create a user and group @@ -74,11 +74,11 @@ Please note that you need to be a group owner to associate a group to your subsc To see the status of your GitLab.com subscription, you can click on the Billings section of the relevant namespace: -* For individuals, this is located at https://gitlab.com/profile/billings under -in your Settings, -* For groups, this is located under the group's Settings dropdown, under Billing. +- For individuals, this is located at https://gitlab.com/profile/billings under + in your Settings, +- For groups, this is located under the group's Settings dropdown, under Billing. -For groups, you can see details of your subscription - including your current +For groups, you can see details of your subscription - including your current plan - in the included table: ![Billing table](billing_table.png) @@ -86,11 +86,11 @@ plan - in the included table: | Field | Description | | ------ | ------ | | Seats in subscription | If this is a paid plan, this represents the number of seats you've paid to support in your group. | -| Seats currently in use | The number of active seats currently in use. | -| Max seats used | The highest number of seats you've used. If this exceeds the seats in subscription, you may owe an additional fee for the additional users. | -| Seats owed | If your max seats used exceeds the seats in your subscription, you'll owe an additional fee for the users you've added. | -| Subscription start date | The date your subscription started. If this is for a Free plan, this is the date you transitioned off your group's paid plan. | -| Subscription end date | The date your current subscription will end. This does not apply to Free plans. | +| Seats currently in use | The number of active seats currently in use. | +| Max seats used | The highest number of seats you've used. If this exceeds the seats in subscription, you may owe an additional fee for the additional users. | +| Seats owed | If your max seats used exceeds the seats in your subscription, you'll owe an additional fee for the users you've added. | +| Subscription start date | The date your subscription started. If this is for a Free plan, this is the date you transitioned off your group's paid plan. | +| Subscription end date | The date your current subscription will end. This does not apply to Free plans. | ### Subscription changes and your data diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index add5971e01c..fa60ee96cf7 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -186,7 +186,7 @@ the sort order to *Last Contacted* from the dropdown beside the search field. To search Runners' descriptions: 1. In the **Search or filter results...** field, type the description of the Runner you want to -find. + find. 1. Press Enter. You can also filter Runners by status, type, and tag. To filter: diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md index fde7d1aeaf7..5768a97e727 100644 --- a/doc/user/admin_area/settings/continuous_integration.md +++ b/doc/user/admin_area/settings/continuous_integration.md @@ -94,7 +94,6 @@ a group in the **Usage Quotas** page available to the group page settings list. ![Group pipelines quota](img/group_pipelines_quota.png) - ## Extra Shared Runners pipeline minutes quota NOTE: **Note:** @@ -110,27 +109,27 @@ In order to purchase additional minutes, you should follow these steps: ![Buy additional minutes](img/buy_btn.png) 1. Locate the subscription card that is linked to your group on GitLab.com, -click on **Buy more CI minutes**, and complete the details about the transaction. + click on **Buy more CI minutes**, and complete the details about the transaction. ![Buy additional minutes](img/buy_minutes_card.png) 1. Once we have processed your payment, the extra CI minutes -will be synced to your Group and you can visualize it from the -**Group > Settings > Pipelines quota** page: + will be synced to your Group and you can visualize it from the + **Group > Settings > Pipelines quota** page: ![Additional minutes](img/additional_minutes.png) Be aware that: 1. If you have purchased extra CI minutes before the purchase of a paid plan, -we will calculate a pro-rated charge for your paid plan. That means you may -be charged for less than one year since your subscription was previously -created with the extra CI minutes. + we will calculate a pro-rated charge for your paid plan. That means you may + be charged for less than one year since your subscription was previously + created with the extra CI minutes. 1. Once the extra CI minutes has been assigned to a Group they cannot be transferred -to a different Group. + to a different Group. 1. If you have some minutes used over your default quota, these minutes will -be deducted from your Additional Minutes quota immediately after your purchase of additional -minutes. + be deducted from your Additional Minutes quota immediately after your purchase of additional + minutes. ## What happens when my CI minutes quota run out diff --git a/doc/user/admin_area/settings/terms.md b/doc/user/admin_area/settings/terms.md index a5f8d05f662..a1bce5a6c69 100644 --- a/doc/user/admin_area/settings/terms.md +++ b/doc/user/admin_area/settings/terms.md @@ -17,7 +17,7 @@ To enforce acceptance of a Terms of Service and Privacy Policy: 1. Go to **Admin Area > Settings > General**. 1. Expand the **Terms of Service and Privacy Policy** section. 1. Check the **Require all users to accept Terms of Service and Privacy Policy when they access -GitLab.** checkbox. + GitLab.** checkbox. 1. Input the text of the **Terms of Service and Privacy Policy**. Markdown formatting can be used in this input box. 1. Click **Save changes**. 1. When you are presented with the **Terms of Service** statement, click **Accept terms**. diff --git a/doc/user/application_security/dependency_scanning/analyzers.md b/doc/user/application_security/dependency_scanning/analyzers.md new file mode 100644 index 00000000000..937ded287e5 --- /dev/null +++ b/doc/user/application_security/dependency_scanning/analyzers.md @@ -0,0 +1,133 @@ +# Dependency Scanning Analyzers **[ULTIMATE]** + +Dependency Scanning relies on underlying third party tools that are wrapped into +what we call "Analyzers". An analyzer is a +[dedicated project](https://gitlab.com/gitlab-org/security-products/analyzers) +that wraps a particular tool to: + +- Expose its detection logic. +- Handle its execution. +- Convert its output to the common format. + +This is achieved by implementing the [common API](https://gitlab.com/gitlab-org/security-products/analyzers/common). + +Dependency Scanning supports the following official analyzers: + +- [`bundler-audit`](https://gitlab.com/gitlab-org/security-products/analyzers/bundler-audit) +- [`gemnasium`](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium) +- [`gemnasium-maven`](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-maven) +- [`gemnasium-python`](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-python) +- [`retire.js`](https://gitlab.com/gitlab-org/security-products/analyzers/retire.js) + +The analyzers are published as Docker images that Dependency Scanning will use +to launch dedicated containers for each analysis. + +Dependency Scanning is pre-configured with a set of **default images** that are +maintained by GitLab, but users can also integrate their own **custom images**. + +## Official default analyzers + +Any custom change to the official analyzers can be achieved by using an +[environment variable in your `.gitlab-ci.yml`](index.md#customizing-the-dependency-scanning-settings). + +### Using a custom Docker mirror + +You can switch to a custom Docker registry that provides the official analyzer +images under a different prefix. For instance, the following instructs Dependency +Scanning to pull `my-docker-registry/gl-images/gemnasium` +instead of `registry.gitlab.com/gitlab-org/security-products/analyzers/gemnasium`. +In `.gitlab-ci.yml` define: + +```yaml +include: + template: Dependency-Scanning.gitlab-ci.yml + +variables: + DS_ANALYZER_IMAGE_PREFIX: my-docker-registry/gl-images +``` + +This configuration requires that your custom registry provides images for all +the official analyzers. + +### Selecting specific analyzers + +You can select the official analyzers you want to run. Here's how to enable +`bundler-audit` and `gemnasium` while disabling all the other default ones. +In `.gitlab-ci.yml` define: + +```yaml +include: + template: Dependency-Scanning.gitlab-ci.yml + +variables: + DS_DEFAULT_ANALYZERS: "bundler-audit,gemnasium" +``` + +`bundler-audit` runs first. When merging the reports, Dependency Scanning will +remove the duplicates and will keep the `bundler-audit` entries. + +### Disabling default analyzers + +Setting `DS_DEFAULT_ANALYZERS` to an empty string will disable all the official +default analyzers. In `.gitlab-ci.yml` define: + +```yaml +include: + template: Dependency-Scanning.gitlab-ci.yml + +variables: + DS_DEFAULT_ANALYZERS: "" +``` + +That's needed when one totally relies on [custom analyzers](#custom-analyzers). + +## Custom analyzers + +You can provide your own analyzers as a comma separated list of Docker images. +Here's how to add `analyzers/nugget` and `analyzers/perl` to the default images. +In `.gitlab-ci.yml` define: + +```yaml +include: + template: Dependency-Scanning.gitlab-ci.yml + +variables: + DS_ANALYZER_IMAGES: "my-docker-registry/analyzers/nugget,amy-docker-registry/nalyzers/perl" +``` + +The values must be the full path to the container registry images, +like what you would feed to the `docker pull` command. + +NOTE: **Note:** +This configuration doesn't benefit from the integrated detection step. Dependency +Scanning has to fetch and spawn each Docker image to establish whether the +custom analyzer can scan the source code. + +## Analyzers data + +The following table lists the data available for each official analyzer. + +| Property \ Tool | Gemnasium | bundler-audit | Retire.js | +|---------------------------------------|:------------------:|:------------------:|:------------------:| +| Severity | 𐄂 | ✓ | ✓ | +| Title | ✓ | ✓ | ✓ | +| File | ✓ | ⚠ | ✓ | +| Start line | 𐄂 | 𐄂 | 𐄂 | +| End line | 𐄂 | 𐄂 | 𐄂 | +| External ID (e.g., CVE) | ✓ | ✓ | ⚠ | +| URLs | ✓ | ✓ | ✓ | +| Internal doc/explanation | ✓ | 𐄂 | 𐄂 | +| Solution | ✓ | ✓ | 𐄂 | +| Confidence | 𐄂 | 𐄂 | 𐄂 | +| Affected item (e.g. class or package) | ✓ | ✓ | ✓ | +| Source code extract | 𐄂 | 𐄂 | 𐄂 | +| Internal ID | ✓ | 𐄂 | 𐄂 | +| Date | ✓ | 𐄂 | 𐄂 | +| Credits | ✓ | 𐄂 | 𐄂 | + +- ✓ => we have that data +- ⚠ => we have that data but it's partially reliable, or we need to extract that data from unstructured content +- 𐄂 => we don't have that data or it would need to develop specific or inefficient/unreliable logic to obtain it. + +The values provided by these tools are heterogeneous so they are sometimes +normalized into common values (e.g., `severity`, `confidence`, etc). diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index a4e5b19bdc7..ea8b96eb24d 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -46,17 +46,33 @@ this is enabled by default. The following languages and dependency managers are supported. -| Language (package managers) | Scan tool | -|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| -| JavaScript ([npm](https://www.npmjs.com/), [yarn](https://yarnpkg.com/en/)) | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium/general), [Retire.js](https://retirejs.github.io/retire.js) | -| Python ([pip](https://pip.pypa.io/en/stable/)) (only `requirements.txt` supported) | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium/general) | -| Ruby ([gem](https://rubygems.org/)) | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium/general), [bundler-audit](https://github.com/rubysec/bundler-audit) | -| Java ([Maven](https://maven.apache.org/)) | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium/general) | -| PHP ([Composer](https://getcomposer.org/)) | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium/general) | - -Some scanners require to send a list of project dependencies to GitLab's central -servers to check for vulnerabilities. To learn more about this or to disable it, -refer to the [GitLab Dependency Scanning tool documentation](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks). +| Language (package managers) | Supported | Scan tool(s) | +|----------------------------- | --------- | ------------ | +| JavaScript ([npm](https://www.npmjs.com/), [yarn](https://yarnpkg.com/en/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium), [Retire.js](https://retirejs.github.io/retire.js) | +| Python ([pip](https://pip.pypa.io/en/stable/)) (only `requirements.txt` supported) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | +| Ruby ([gem](https://rubygems.org/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium), [bundler-audit](https://github.com/rubysec/bundler-audit) | +| Java ([Maven](https://maven.apache.org/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | +| PHP ([Composer](https://getcomposer.org/)) | yes | [gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) | +| Python ([poetry](https://poetry.eustace.io/)) | no ([issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/7006 "Support Poetry in Dependency Scanning")) | not available | +| Python ([Pipfile](https://docs.pipenv.org/en/latest/basics/)) | no ([issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/11756 "Pipfile.lock support for Dependency Scanning"))| not available | +| Go ([Golang](https://golang.org/)) | no ([issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/7132 "Dependency Scanning for Go")) | not available | + +## Remote checks + +While some tools pull a local database to check vulnerabilities, some others +like Gemnasium require sending data to GitLab central servers to analyze them: + +1. Gemnasium scans the dependencies of your project locally and sends a list of + packages to GitLab central servers. +1. The servers return the list of known vulnerabilities for all versions of + these packages. +1. The client picks up the relevant vulnerabilities by comparing with the versions + of the packages that are used by the project. + +The Gemnasium client does **NOT** send the exact package versions your project relies on. + +You can disable the remote checks by [using](#customizing-the-dependency-scanning-settings) +the `DS_DISABLE_REMOTE_CHECKS` environment variable and setting it to `true`. ## Configuring Dependency Scanning @@ -97,17 +113,10 @@ The report will be saved as a that you can later download and analyze. Due to implementation limitations, we always take the latest Dependency Scanning artifact available. -Some security scanners require to send a list of project dependencies to GitLab -central servers to check for vulnerabilities. To learn more about this or to -disable it, check the -[GitLab Dependency Scanning tool documentation](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks). - #### Customizing the Dependency Scanning settings -The Dependency Scanning settings can be changed through environment variables by using the +The Dependency Scanning settings can be changed through [environment variables](#available-variables) by using the [`variables`](../../../ci/yaml/README.md#variables) parameter in `.gitlab-ci.yml`. -These variables are documented in the -[Dependency Scanning tool documentation](https://gitlab.com/gitlab-org/security-products/dependency-scanning#settings). For example: @@ -116,7 +125,7 @@ include: template: Dependency-Scanning.gitlab-ci.yml variables: - DEP_SCAN_DISABLE_REMOTE_CHECKS: true + DS_DISABLE_REMOTE_CHECKS: true ``` Because template is [evaluated before](../../../ci/yaml/README.md#include) the pipeline @@ -137,6 +146,24 @@ dependency_scanning: CI_DEBUG_TRACE: "true" ``` +#### Available variables + +Dependency Scanning can be [configured](#customizing-the-dependency-scanning-settings) +using environment variables. + +| Environment variable | Function | +|-------------------------------- |----------| +| `DS_ANALYZER_IMAGES` | Comma separated list of custom images. The official default images are still enabled. Read more about [customizing analyzers](analyzers.md). | +| `DS_ANALYZER_IMAGE_PREFIX` | Override the name of the Docker registry providing the official default images (proxy). Read more about [customizing analyzers](analyzers.md). | +| `DS_ANALYZER_IMAGE_TAG` | Override the Docker tag of the official default images. Read more about [customizing analyzers](analyzers.md). | +| `DS_DEFAULT_ANALYZERS` | Override the names of the official default images. Read more about [customizing analyzers](analyzers.md). | +| `DS_DISABLE_REMOTE_CHECKS` | Do not send any data to GitLab. Used in the [Gemnasium analyzer](#remote-checks). | +| `DS_PULL_ANALYZER_IMAGES` | Pull the images from the Docker registry (set to `0` to disable). | +| `DS_EXCLUDED_PATHS` | Exclude vulnerabilities from output based on the paths. A comma-separated list of patterns. Patterns can be globs, file or folder paths. Parent directories will also match patterns. | +| `DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT` | Time limit for Docker client negotiation. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. | +| `DS_PULL_ANALYZER_IMAGE_TIMEOUT` | Time limit when pulling the image of an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. | +| `DS_RUN_ANALYZER_TIMEOUT` | Time limit when running an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. | + ### Manual job definition for GitLab 11.5 and later For GitLab 11.5 and GitLab Runner 11.5 and later, the following `dependency_scanning` @@ -171,7 +198,7 @@ dependency_scanning: dependency_scanning: gl-dependency-scanning-report.json ``` -You can supply many other [settings variables](https://gitlab.com/gitlab-org/security-products/dependency-scanning#settings) +You can supply many other [settings variables](#available-variables) via `docker run --env` to customize your job execution. ### Manual job definition for GitLab 11.4 and earlier (deprecated) @@ -388,6 +415,10 @@ supported by Gemnasium. To see the generated dependency list, navigate to your project's **Project > Dependency List**. +## Versioning and release process + +Please check the [Release Process documentation](https://gitlab.com/gitlab-org/security-products/release/blob/master/docs/release_process.md). + ## Contributing to the vulnerability database You can search the [gemnasium-db](https://gitlab.com/gitlab-org/security-products/gemnasium-db) project diff --git a/doc/user/group/contribution_analytics/index.md b/doc/user/group/contribution_analytics/index.md index 7e6cb24a51e..0da131ab7bd 100644 --- a/doc/user/group/contribution_analytics/index.md +++ b/doc/user/group/contribution_analytics/index.md @@ -21,9 +21,9 @@ page. ## Use cases - Analyze your team's contributions over a period of time, and offer a bonus for the top -contributors. + contributors. - Identify opportunities for improvement with group members who may benefit from additional -support. + support. ## Using Contribution Analytics diff --git a/doc/user/group/epics/index.md b/doc/user/group/epics/index.md index 2e4106f55e5..f53c1dd95d7 100644 --- a/doc/user/group/epics/index.md +++ b/doc/user/group/epics/index.md @@ -202,8 +202,8 @@ You may also consult the [group permissions table][permissions]. ## Thread - Comments: collaborate on that epic by posting comments in its thread. -These text fields also fully support -[GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm). + These text fields also fully support + [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm). ## Comment, or start a discussion @@ -216,7 +216,7 @@ Once you wrote your comment, you can either: - You can [award an emoji](../../award_emojis.md) to that epic or its comments. -## Notifications +## Notifications - [Receive notifications](../../../workflow/notifications.md) for epic events. diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 4fde45da6c4..183b12b1c73 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -41,12 +41,13 @@ You can create groups for numerous reasons. To name a couple: - Make it easier to `@mention` all of your team at once in issues and merge requests by creating a group and including the appropriate members. For example, you could create a group for your company members, and create a [subgroup](subgroups/index.md) for each individual team. Let's say you create a group called `company-team`, and you create subgroups in this group for the individual teams `backend-team`, `frontend-team`, and `production-team`. - - When you start a new implementation from an issue, you add a comment: - _"`@company-team`, let's do it! `@company-team/backend-team` you're good to go!"_ - - When your backend team needs help from frontend, they add a comment: - _"`@company-team/frontend-team` could you help us here please?"_ - - When the frontend team completes their implementation, they comment: - _"`@company-team/backend-team`, it's done! Let's ship it `@company-team/production-team`!"_ + +- When you start a new implementation from an issue, you add a comment: + _"`@company-team`, let's do it! `@company-team/backend-team` you're good to go!"_ +- When your backend team needs help from frontend, they add a comment: + _"`@company-team/frontend-team` could you help us here please?"_ +- When the frontend team completes their implementation, they comment: + _"`@company-team/backend-team`, it's done! Let's ship it `@company-team/production-team`!"_ ## Namespaces diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index 96cc523f4ec..5aef463d782 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -24,27 +24,27 @@ The following identity providers are supported: ## Requirements -- [Group SSO](index.md) needs to be configured. +- [Group SSO](index.md) needs to be configured. - The `scim_group` feature flag must be enabled: Run the following commands in a Rails console: - + ```sh # Omnibus GitLab gitlab-rails console - + # Installation from source cd /home/git/gitlab sudo -u git -H bin/rails console RAILS_ENV=production ``` - + To enable SCIM for a group named `group_name`: - + ```ruby group = Group.find_by_full_path('group_name') Feature.enable(:group_scim, group) ``` - + ### GitLab configuration Once [Single sign-on](index.md) has been configured, we can: @@ -53,7 +53,7 @@ Once [Single sign-on](index.md) has been configured, we can: 1. Click on the **Generate a SCIM token** button. 1. Save the token and URL so they can be used in the next step. -![SCIM token configuration](img/scim_token.png) +![SCIM token configuration](img/scim_token.png) ## SCIM IdP configuration @@ -63,15 +63,15 @@ In the [Single sign-on](index.md) configuration for the group, make sure that the **Name identifier value** (NameID) points to a unique identifier, such as the `user.objectid`. This will match the `extern_uid` used on GitLab. -The GitLab app in Azure needs to be configured following +The GitLab app in Azure needs to be configured following [Azure's SCIM setup](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/use-scim-to-provision-users-and-groups#getting-started). Note the following: - The `Tenant URL` and `secret token` are the ones retrieved in the -[previous step](#gitlab-configuration). + [previous step](#gitlab-configuration). - Should there be any problems with the availability of GitLab or similar -errors, the notification email set will get those. + errors, the notification email set will get those. - For mappings, we will only leave `Synchronize Azure Active Directory Users to AppName` enabled. You can then test the connection clicking on `Test Connection`. @@ -79,14 +79,14 @@ You can then test the connection clicking on `Test Connection`. ### Synchronize Azure Active Directory users 1. Click on `Synchronize Azure Active Directory Users to AppName`, to configure -the attribute mapping. + the attribute mapping. 1. Select the unique identifier (in the example `objectId`) as the `id` and `externalId`, -and enable the `Create`, `Update`, and `Delete` actions. + and enable the `Create`, `Update`, and `Delete` actions. 1. Map the `userPricipalName` to `emails[type eq "work"].value` and `mailNickname` to -`userName`. + `userName`. Example configuration: - + ![Azure's attribute mapping configuration](img/scim_attribute_mapping.png) 1. Click on **Show advanced options > Edit attribute list for AppName**. @@ -95,11 +95,11 @@ and enable the `Create`, `Update`, and `Delete` actions. NOTE: **Note:** `username` should neither be primary nor required as we don't support that field on GitLab SCIM yet. - + ![Azure's attribute advanced configuration](img/scim_advanced.png) 1. Save all the screens and, in the **Provisioning** step, set -the `Provisioning Status` to `ON`. + the `Provisioning Status` to `ON`. NOTE: **Note:** You can control what is actually synced by selecting the `Scope`. For example, diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index a0fe97f2b9d..97d2dfc0f7e 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -519,8 +519,11 @@ service account of the cluster integration. ### Troubleshooting failed deployment jobs -GitLab will create a namespace and service account specifically for your -deployment jobs. This happens immediately before the deployment job starts. +Before the deployment jobs starts, GitLab creates the following specifically for +the deployment job: + +- A namespace. +- A service account. However, sometimes GitLab can not create them. In such instances, your job will fail with the message: diff --git a/doc/user/project/clusters/kubernetes_pod_logs.md b/doc/user/project/clusters/kubernetes_pod_logs.md index 368031070c1..25d8abebf07 100644 --- a/doc/user/project/clusters/kubernetes_pod_logs.md +++ b/doc/user/project/clusters/kubernetes_pod_logs.md @@ -12,9 +12,9 @@ By displaying the logs directly in GitLab, developers can avoid having to manage 1. Go to **Operations > Environments** and find the environment which contains the desired pod, like `production`. 1. On the **Environments** page, you should see the status of the environment's pods with [Deploy Boards](../deploy_boards.md). 1. When mousing over the list of pods, a tooltip will appear with the exact pod name and status. -![Deploy Boards pod list](img/pod_logs_deploy_board.png) + ![Deploy Boards pod list](img/pod_logs_deploy_board.png) 1. Click on the desired pod to bring up the logs view, which will contain the last 500 lines for that pod. Support for pods with multiple containers is coming [in a future release](https://gitlab.com/gitlab-org/gitlab-ee/issues/6502). -![Deploy Boards pod list](img/kubernetes_pod_logs.png) + ![Deploy Boards pod list](img/kubernetes_pod_logs.png) ## Requirements diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md index 58b7fe33906..fdf9ce3e225 100644 --- a/doc/user/project/container_registry.md +++ b/doc/user/project/container_registry.md @@ -113,6 +113,7 @@ This feature requires GitLab 8.8 and GitLab Runner 1.2. Make sure that your GitLab Runner is configured to allow building Docker images by following the [Using Docker Build](../../ci/docker/using_docker_build.md) and [Using the GitLab Container Registry documentation](../../ci/docker/using_docker_build.md#using-the-gitlab-container-registry). +Alternatively, you can [build images with Kaniko](../../ci/docker/using_kaniko.md) if the Docker builds are not an option for you. ## Using with private projects diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md index ca59fe3cc7d..7520237251a 100644 --- a/doc/user/project/description_templates.md +++ b/doc/user/project/description_templates.md @@ -25,14 +25,14 @@ templates of the default branch will be taken into account. ## Use-cases - Add a template to be used in every issue for a specific project, -giving instructions and guidelines, requiring for information specific to that subject. -For example, if you have a project for tracking new blog posts, you can require the -title, outlines, author name, author social media information, etc. + giving instructions and guidelines, requiring for information specific to that subject. + For example, if you have a project for tracking new blog posts, you can require the + title, outlines, author name, author social media information, etc. - Following the previous example, you can make a template for every MR submitted -with a new blog post, requiring information about the post date, frontmatter data, -images guidelines, link to the related issue, reviewer name, etc. + with a new blog post, requiring information about the post date, frontmatter data, + images guidelines, link to the related issue, reviewer name, etc. - You can also create issues and merge request templates for different -stages of your workflow, e.g., feature proposal, feature improvement, bug report, etc. + stages of your workflow, e.g., feature proposal, feature improvement, bug report, etc. ## Creating issue templates diff --git a/doc/user/project/integrations/hipchat.md b/doc/user/project/integrations/hipchat.md index 0fd847d415f..7a0540aa9e3 100644 --- a/doc/user/project/integrations/hipchat.md +++ b/doc/user/project/integrations/hipchat.md @@ -23,7 +23,7 @@ allow GitLab to send messages only to *one* room. 1. Find "Build Your Own!" and click "Create". 1. Select the desired room, name the integration "GitLab", and click "Create". 1. In the "Send messages to this room by posting this URL" column, you should -see a URL in the format: + see a URL in the format: ``` https://api.hipchat.com/v2/room/<room>/notification?auth_token=<token> diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index 751e8e44e60..aab7131e353 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -160,7 +160,7 @@ receivers: > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4925) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.11. -Alerts can be used to trigger actions, like open an issue automatically. To configure the actions: +Alerts can be used to trigger actions, like open an issue automatically (enabled by default since `12.1`). To configure the actions: 1. Navigate to your project's **Settings > Operations > Incidents**. 1. Enable the option to create issues. diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md index 76dc6e49bce..4acbb4cc3f6 100644 --- a/doc/user/project/issues/index.md +++ b/doc/user/project/issues/index.md @@ -134,4 +134,4 @@ For more information, see [Crosslinking issues](crosslinking_issues.md). - [Export issues](csv_export.md) **[STARTER]** - [Issues API](../../../api/issues.md) - Configure an [external issue tracker](../../../integration/external-issue-tracker.md) such as Jira, Redmine, -or Bugzilla. + or Bugzilla. diff --git a/doc/user/project/merge_requests/merge_request_approvals.md b/doc/user/project/merge_requests/merge_request_approvals.md index fd151a6df45..8e8ec26daf2 100644 --- a/doc/user/project/merge_requests/merge_request_approvals.md +++ b/doc/user/project/merge_requests/merge_request_approvals.md @@ -12,9 +12,9 @@ to approve a merge request before it can be unblocked for merging. ## Use cases 1. Enforcing review of all code that gets merged into a repository. -2. Specifying code maintainers for an entire repository. -3. Specifying reviewers for a given proposed code change. -4. Specifying categories of reviewers, such as BE, FE, QA, DB, etc., for all proposed code changes. +1. Specifying code maintainers for an entire repository. +1. Specifying reviewers for a given proposed code change. +1. Specifying categories of reviewers, such as BE, FE, QA, DB, etc., for all proposed code changes. ## Enabling the new approvals interface @@ -246,7 +246,7 @@ restrictions (compared to [GitLab Starter](#overriding-the-merge-request-approva - Approval rules can be added to an MR with no restriction. - For project sourced approval rules, editing and removing approvers is not allowed. - The approvals required of all approval rules is configurable, but if a rule is backed by a project rule, then it is restricted -to the minimum approvals required set in the project's corresponding rule. + to the minimum approvals required set in the project's corresponding rule. ## Resetting approvals on push diff --git a/doc/user/project/pages/getting_started_part_two.md b/doc/user/project/pages/getting_started_part_two.md index 3e50cd4887c..fe92d19567d 100644 --- a/doc/user/project/pages/getting_started_part_two.md +++ b/doc/user/project/pages/getting_started_part_two.md @@ -77,10 +77,10 @@ containing the most popular SSGs templates to get you started. 1. [Fork](../../../gitlab-basics/fork-project.md) a sample project from the [GitLab Pages examples](https://gitlab.com/pages) group. 1. From the left sidebar, navigate to your project's **CI/CD > Pipelines** -and click **Run pipeline** to trigger GitLab CI/CD to build and deploy your -site to the server. + and click **Run pipeline** to trigger GitLab CI/CD to build and deploy your + site to the server. 1. Once the pipeline has finished successfully, find the link to visit your -website from your project's **Settings > Pages**. + website from your project's **Settings > Pages**. You can also take some **optional** further steps: @@ -89,14 +89,14 @@ You can also take some **optional** further steps: ![remove fork relationship](img/remove_fork_relationship.png) - _Make it a user or group website._ To turn a **project website** forked -from the Pages group into a **user/group** website, you'll need to: + from the Pages group into a **user/group** website, you'll need to: - Rename it to `namespace.gitlab.io`: go to your project's - **Settings > General** and expand **Advanced**. Scroll down to - **Rename repository** and change the path to `namespace.gitlab.io`. + **Settings > General** and expand **Advanced**. Scroll down to + **Rename repository** and change the path to `namespace.gitlab.io`. - Adjust your SSG's [base URL](#urls-and-baseurls) from `"project-name"` to - `""`. This setting will be at a different place for each SSG, as each of them - have their own structure and file tree. Most likely, it will be in the SSG's - config file. + `""`. This setting will be at a different place for each SSG, as each of them + have their own structure and file tree. Most likely, it will be in the SSG's + config file. ### Create a project from scratch diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md index 04bda212128..fa79c393b72 100644 --- a/doc/user/project/pages/index.md +++ b/doc/user/project/pages/index.md @@ -12,7 +12,6 @@ type: index, reference > - Support for subgroup project's websites was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/30548) in GitLab 11.8. > - Bundled project templates were [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/47857) in GitLab 11.8. - **GitLab Pages is a feature that allows you to publish static websites directly from a repository in GitLab.** @@ -105,10 +104,10 @@ To get started with GitLab Pages, you can either: ![Project templates for Pages](img/pages_project_templates_11-8.png) 1. From the left sidebar, navigate to your project's **CI/CD > Pipelines** -and click **Run pipeline** to trigger GitLab CI/CD to build and deploy your -site to the server. + and click **Run pipeline** to trigger GitLab CI/CD to build and deploy your + site to the server. 1. Once the pipeline has finished successfully, find the link to visit your -website from your project's **Settings > Pages**. + website from your project's **Settings > Pages**. Your website is then visible on your domain, and you can modify yourfiles as you wish. For every modification pushed to your repository, GitLab CI/CD diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md index 4fab7f79e0c..4ea3bd9be9b 100644 --- a/doc/user/project/pages/introduction.md +++ b/doc/user/project/pages/introduction.md @@ -13,17 +13,17 @@ To familiarize yourself with GitLab Pages first: - Read an [introduction to GitLab Pages](index.md#overview). - Learn [how to get started with Pages](index.md#getting-started). - Learn how to enable GitLab Pages -across your GitLab instance on the [administrator documentation](../../../administration/pages/index.md). + across your GitLab instance on the [administrator documentation](../../../administration/pages/index.md). ## GitLab Pages requirements In brief, this is what you need to upload your website in GitLab Pages: 1. Domain of the instance: domain name that is used for GitLab Pages -(ask your administrator). + (ask your administrator). 1. GitLab CI/CD: a `.gitlab-ci.yml` file with a specific job named [`pages`][pages] in the root directory of your repository. 1. A directory called `public` in your site's repo containing the content -to be published. + to be published. 1. GitLab Runner enabled for the project. ## GitLab Pages on GitLab.com diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index ba890c5ac01..be4215b1934 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -26,10 +26,10 @@ Set up your project's access, [visibility](../../../public_access/public_access. ![projects sharing permissions](img/sharing_and_permissions_settings.png) -If Issues are disabled, or you can't access Issues because you're not a project member, then Lables and Milestones +If Issues are disabled, or you can't access Issues because you're not a project member, then Lables and Milestones links will be missing from the sidebar UI. -You can still access them with direct links if you can access Merge Requests. This is deliberate, if you can see +You can still access them with direct links if you can access Merge Requests. This is deliberate, if you can see Issues or Merge Requests, both of which use Labels and Milestones, then you shouldn't be denied access to Labels and Milestones pages. ### Issue settings @@ -109,8 +109,8 @@ You can transfer an existing project into a [group](../../group/index.md) if: 1. You have at least **Maintainer** [permissions] to that group. 1. The project is in a subgroup you own. 1. You are at least a **Maintainer** of the project under your personal namespace. -Similarly, if you are an owner of a group, you can transfer any of its projects -under your own user. + Similarly, if you are an owner of a group, you can transfer any of its projects + under your own user. To transfer a project: diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md index b2bf85335c5..7d85f4adfed 100644 --- a/doc/user/project/web_ide/index.md +++ b/doc/user/project/web_ide/index.md @@ -171,13 +171,13 @@ syntax but with some restrictions: - No global blocks can be defined (ie: `before_script` or `after_script`) - Only one job named `terminal` can be added to this file. - Only the keywords `image`, `services`, `tags`, `before_script`, `script`, and -`variables` are allowed to be used to configure the job. + `variables` are allowed to be used to configure the job. - To connect to the interactive terminal, the `terminal` job must be still alive -and running, otherwise the terminal won't be able to connect to the job's session. -By default the `script` keyword has the value `sleep 60` to prevent -the job from ending and giving the Web IDE enough time to connect. This means -that, if you override the default `script` value, you'll have to add a command -which would keep the job running, like `sleep`. + and running, otherwise the terminal won't be able to connect to the job's session. + By default the `script` keyword has the value `sleep 60` to prevent + the job from ending and giving the Web IDE enough time to connect. This means + that, if you override the default `script` value, you'll have to add a command + which would keep the job running, like `sleep`. In the code below there is an example of this configuration file: diff --git a/doc/workflow/time_tracking.md b/doc/workflow/time_tracking.md index c03dffa967d..4286a3625a2 100644 --- a/doc/workflow/time_tracking.md +++ b/doc/workflow/time_tracking.md @@ -73,7 +73,15 @@ The following time units are available: Default conversion rates are 1mo = 4w, 1w = 5d and 1d = 8h. -Other interesting links: +### Limit displayed units to hours + +> Introduced in GitLab 12.0. + +The display of time units can be limited to hours through the option in **Admin Area > Settings > Preferences** under 'Localization'. + +With this option enabled, `75h` is displayed instead of `1w 4d 3h`. + +## Other interesting links - [Time Tracking landing page on about.gitlab.com](https://about.gitlab.com/solutions/time-tracking/) diff --git a/lib/after_commit_queue.rb b/lib/after_commit_queue.rb index 6fb7985f955..6a180fdf338 100644 --- a/lib/after_commit_queue.rb +++ b/lib/after_commit_queue.rb @@ -15,7 +15,7 @@ module AfterCommitQueue end def run_after_commit_or_now(&block) - if AfterCommitQueue.inside_transaction? + if Gitlab::Database.inside_transaction? if ActiveRecord::Base.connection.current_transaction.records.include?(self) run_after_commit(&block) else @@ -32,18 +32,6 @@ module AfterCommitQueue true end - def self.open_transactions_baseline - if ::Rails.env.test? - return DatabaseCleaner.connections.count { |conn| conn.strategy.is_a?(DatabaseCleaner::ActiveRecord::Transaction) } - end - - 0 - end - - def self.inside_transaction? - ActiveRecord::Base.connection.open_transactions > open_transactions_baseline - end - protected def _run_after_commit_queue diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 65d7f68bbf9..c3821630b6b 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -8,7 +8,10 @@ module API BRANCH_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX) - before { authorize! :download_code, user_project } + before do + require_repository_enabled! + authorize! :download_code, user_project + end helpers do params :filter_params do diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 80913f4ca07..eebded87ebc 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -6,7 +6,10 @@ module API class Commits < Grape::API include PaginationParams - before { authorize! :download_code, user_project } + before do + require_repository_enabled! + authorize! :download_code, user_project + end helpers do def user_access diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 6382d295f79..8ae42c6dadd 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -250,6 +250,10 @@ module API authorize! :update_build, user_project end + def require_repository_enabled!(subject = :global) + not_found!("Repository") unless user_project.feature_available?(:repository, current_user) + end + def require_gitlab_workhorse! unless env['HTTP_GITLAB_WORKHORSE'].present? forbidden!('Request should be executed via GitLab Workhorse') diff --git a/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb index e6e0aaab60b..49c680605ea 100644 --- a/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb +++ b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb @@ -20,7 +20,7 @@ module Gitlab private def deployment_cluster - build.deployment&.cluster + build.deployment&.deployment_platform_cluster end def kubernetes_namespace diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index 8da98cc3909..e4d4779ba9a 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -234,6 +234,7 @@ module Gitlab def self.connection ActiveRecord::Base.connection end + private_class_method :connection def self.cached_column_exists?(table_name, column_name) connection.schema_cache.columns_hash(table_name).has_key?(column_name.to_s) @@ -243,8 +244,6 @@ module Gitlab connection.schema_cache.data_source_exists?(table_name) end - private_class_method :connection - def self.database_version row = connection.execute("SELECT VERSION()").first @@ -272,5 +271,20 @@ module Gitlab end end end + + # inside_transaction? will return true if the caller is running within a transaction. Handles special cases + # when running inside a test environment, in which the entire test is running with a DatabaseCleaner transaction + def self.inside_transaction? + ActiveRecord::Base.connection.open_transactions > open_transactions_baseline + end + + def self.open_transactions_baseline + if ::Rails.env.test? + return DatabaseCleaner.connections.count { |conn| conn.strategy.is_a?(DatabaseCleaner::ActiveRecord::Transaction) } + end + + 0 + end + private_class_method :open_transactions_baseline end end diff --git a/lib/gitlab/lets_encrypt.rb b/lib/gitlab/lets_encrypt.rb new file mode 100644 index 00000000000..cdf24f24647 --- /dev/null +++ b/lib/gitlab/lets_encrypt.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Gitlab + module LetsEncrypt + def self.enabled?(pages_domain = nil) + return false unless Gitlab::CurrentSettings.lets_encrypt_terms_of_service_accepted + + return false unless Feature.enabled?(:pages_auto_ssl) + + # If no domain is passed, just check whether we're enabled globally + return true unless pages_domain + + !!pages_domain.project && Feature.enabled?(:pages_auto_ssl_for_project, pages_domain.project) + end + end +end diff --git a/lib/gitlab/lets_encrypt/client.rb b/lib/gitlab/lets_encrypt/client.rb index 66aea137012..ad2921ed555 100644 --- a/lib/gitlab/lets_encrypt/client.rb +++ b/lib/gitlab/lets_encrypt/client.rb @@ -34,14 +34,6 @@ module Gitlab acme_client.terms_of_service end - def enabled? - return false unless Feature.enabled?(:pages_auto_ssl) - - return false unless private_key - - Gitlab::CurrentSettings.lets_encrypt_terms_of_service_accepted - end - private def acme_client @@ -65,7 +57,7 @@ module Gitlab end def ensure_account - raise 'Acme integration is disabled' unless enabled? + raise 'Acme integration is disabled' unless ::Gitlab::LetsEncrypt.enabled? @acme_account ||= acme_client.new_account(contact: contact, terms_of_service_agreed: true) end diff --git a/lib/gitlab/time_tracking_formatter.rb b/lib/gitlab/time_tracking_formatter.rb index cc206010e74..302da91328a 100644 --- a/lib/gitlab/time_tracking_formatter.rb +++ b/lib/gitlab/time_tracking_formatter.rb @@ -6,7 +6,7 @@ module Gitlab def parse(string) with_custom_config do - string.sub!(/\A-/, '') + string = string.sub(/\A-/, '') seconds = ChronicDuration.parse(string, default_unit: 'hours') rescue nil seconds *= -1 if seconds && Regexp.last_match @@ -16,10 +16,12 @@ module Gitlab def output(seconds) with_custom_config do - ChronicDuration.output(seconds, format: :short, limit_to_hours: false, weeks: true) rescue nil + ChronicDuration.output(seconds, format: :short, limit_to_hours: limit_to_hours_setting, weeks: true) rescue nil end end + private + def with_custom_config # We may want to configure it through project settings in a future version. ChronicDuration.hours_per_day = 8 @@ -32,5 +34,9 @@ module Gitlab result end + + def limit_to_hours_setting + Gitlab::CurrentSettings.time_tracking_limit_to_hours + end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 97613a6a920..fd6d3ed624c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2064,6 +2064,9 @@ msgstr "" msgid "Clear search input" msgstr "" +msgid "Clear templates search input" +msgstr "" + msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone." msgstr "" @@ -2789,9 +2792,15 @@ msgstr "" msgid "Configure Let's Encrypt" msgstr "" +msgid "Configure Prometheus" +msgstr "" + msgid "Configure automatic git checks and housekeeping on repositories." msgstr "" +msgid "Configure existing installation" +msgstr "" + msgid "Configure limits for web and API requests." msgstr "" @@ -3146,6 +3155,9 @@ msgstr "" msgid "Creates branch '%{branch_name}' and a merge request to resolve this issue" msgstr "" +msgid "Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available." +msgstr "" + msgid "Cron Timezone" msgstr "" @@ -4656,6 +4668,9 @@ msgstr "" msgid "Get started with error tracking" msgstr "" +msgid "Get started with performance monitoring" +msgstr "" + msgid "Getting started with releases" msgstr "" @@ -5333,6 +5348,9 @@ msgstr "" msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}." msgstr "" +msgid "Install on clusters" +msgstr "" + msgid "Installed" msgstr "" @@ -5839,6 +5857,9 @@ msgstr "" msgid "Let's Encrypt is a free, automated, and open certificate authority (CA) that gives digital certificates in order to enable HTTPS (SSL/TLS) for websites. Learn more about Let's Encrypt configuration by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}." msgstr "" +msgid "Limit display of time tracking units to hours." +msgstr "" + msgid "Limited to showing %d event at most" msgid_plural "Limited to showing %d events at most" msgstr[0] "" @@ -6584,6 +6605,9 @@ msgstr "" msgid "No contributions were found" msgstr "" +msgid "No data found" +msgstr "" + msgid "No details available" msgstr "" @@ -6647,6 +6671,9 @@ msgstr "" msgid "No schedules" msgstr "" +msgid "No template" +msgstr "" + msgid "No, directly import the existing email addresses and usernames." msgstr "" @@ -8071,6 +8098,9 @@ msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Prometheus server" +msgstr "" + msgid "PrometheusService|%{exporters} with %{metrics} were found" msgstr "" @@ -8541,6 +8571,9 @@ msgstr "" msgid "Reset runners registration token" msgstr "" +msgid "Reset template" +msgstr "" + msgid "Resolve all discussions in new issue" msgstr "" @@ -9592,6 +9625,9 @@ msgstr "" msgid "Status:" msgstr "" +msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments." +msgstr "" + msgid "Stop environment" msgstr "" @@ -10503,6 +10539,9 @@ msgstr "" msgid "Thursday" msgstr "" +msgid "Time" +msgstr "" + msgid "Time based: Yes" msgstr "" @@ -10948,6 +10987,9 @@ msgstr "" msgid "U2F only works with HTTPS-enabled websites. Contact your administrator for more details." msgstr "" +msgid "Unable to connect to Prometheus server" +msgstr "" + msgid "Unable to connect to server: %{error}" msgstr "" @@ -11353,6 +11395,9 @@ msgstr "" msgid "View details: %{details_url}" msgstr "" +msgid "View documentation" +msgstr "" + msgid "View file @ " msgstr "" @@ -11428,6 +11473,9 @@ msgstr "" msgid "Wait for the source to load to copy it to the clipboard" msgstr "" +msgid "Waiting for performance data" +msgstr "" + msgid "Want to see the data? Please ask an administrator for access." msgstr "" @@ -11685,6 +11733,9 @@ msgstr "" msgid "You are attempting to update a file that has changed since you started editing it." msgstr "" +msgid "You are connected to the Prometheus server, but there is currently no data to display." +msgstr "" + msgid "You are going to remove %{group_name}, this will also remove all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?" msgstr "" diff --git a/package.json b/package.json index 8e97419bfcb..577967d23b9 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "apollo-upload-client": "^10.0.0", "at.js": "^1.5.4", "autosize": "^4.0.0", - "axios": "^0.17.1", + "axios": "^0.19.0", "babel-loader": "^8.0.5", "bootstrap": "4.3.1", "brace-expansion": "^1.1.8", diff --git a/scripts/generate-gems-memory-metrics-static b/scripts/generate-gems-memory-metrics-static new file mode 100755 index 00000000000..aa7ce3615bf --- /dev/null +++ b/scripts/generate-gems-memory-metrics-static @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby + +abort "usage: #{__FILE__} <memory_bundle_objects_file_name>" unless ARGV.length == 1 +memory_bundle_objects_file_name = ARGV.first + +full_report = File.readlines(memory_bundle_objects_file_name) + +allocated_str = full_report[1] +retained_str = full_report[2] +allocated_stats = /Total allocated: (?<bytes>.*) bytes \((?<objects>.*) objects\)/.match(allocated_str) +retained_stats = /Total retained: (?<bytes>.*) bytes \((?<objects>.*) objects\)/.match(retained_str) + +abort 'failed to process the benchmark output' unless allocated_stats && retained_stats + +puts "memory_static_objects_allocated_mb #{(allocated_stats[:bytes].to_f / (1024 * 1024)).round(1)}" +puts "memory_static_objects_retained_mb #{(retained_stats[:bytes].to_f / (1024 * 1024)).round(1)}" +puts "memory_static_objects_allocated_items #{allocated_stats[:objects]}" +puts "memory_static_objects_retained_items #{retained_stats[:objects]}" diff --git a/scripts/generate-gems-size-metrics-static b/scripts/generate-gems-size-metrics-static new file mode 100755 index 00000000000..ceec8aaccf1 --- /dev/null +++ b/scripts/generate-gems-size-metrics-static @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +abort "usage: #{__FILE__} <memory_bundle_mem_file_name>" unless ARGV.length == 1 +memory_bundle_mem_file_name = ARGV.first + +full_report = File.readlines(memory_bundle_mem_file_name) + +def total_size(memory_bundle_mem_report) + stats = /TOP: (?<total_mibs_str>.*) MiB/.match(memory_bundle_mem_report.first) + abort 'failed to process the benchmark output' unless stats + "gem_total_size_mb #{stats[:total_mibs_str].to_f.round(1)}" +end + +TOP_LEVEL_GEM_LOG_FORMAT = /^ (?<gem_name>\S.*):\s*(?<gem_size>\d[.\d]*)\s*MiB/.freeze +def all_gems(memory_bundle_mem_report) + memory_bundle_mem_report.map do |line| + TOP_LEVEL_GEM_LOG_FORMAT.match(line) + end.compact +end + +def gems_as_metrics(gems_match_data) + gems_match_data.map do |gem| + gem_name = gem[:gem_name] + gem_size_mb = gem[:gem_size].to_f.round(1) + "gem_size_mb{name=\"#{gem_name}\"} #{gem_size_mb}" + end +end + +puts total_size(full_report) +puts gems_as_metrics(all_gems(full_report)).sort(&:casecmp) diff --git a/scripts/memory-static b/scripts/memory-static deleted file mode 100755 index 54f147a7a91..00000000000 --- a/scripts/memory-static +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env ruby - -require_relative '../lib/gitlab/popen' - -full_report_filename, metrics_filename = ARGV -abort 'usage: memory-static <full_report_filename> <metrics_filename>' unless full_report_filename && metrics_filename - -full_report, status = Gitlab::Popen.popen(%w(bundle exec derailed bundle:mem)) -abort 'failed to execute the benchmark' unless status.zero? - -File.open(full_report_filename, 'w') do |f| - f.write(full_report) -end - -stats = /TOP: (?<total_mibs_str>.*) MiB/.match(full_report.lines.first) -abort 'failed to process the benchmark output' unless stats - -File.open(metrics_filename, 'a') do |f| - f.puts "memory_static_total_mb #{stats[:total_mibs_str].to_f.round(1)}" -end diff --git a/scripts/memory-static-objects b/scripts/memory-static-objects deleted file mode 100755 index 2ad38d9717c..00000000000 --- a/scripts/memory-static-objects +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env ruby - -require_relative '../lib/gitlab/popen' - -full_report_filename, metrics_filename = ARGV -abort 'usage: memory-static-objects <full_report_filename> <metrics_filename>' unless full_report_filename && metrics_filename - -full_report, status = Gitlab::Popen.popen(%w(bundle exec derailed bundle:objects)) -abort 'failed to execute the benchmark' unless status.zero? - -File.open(full_report_filename, 'w') do |f| - f.write(full_report) -end - -allocated_str = full_report.lines[1] -retained_str = full_report.lines[2] -allocated_stats = /Total allocated: (?<bytes>.*) bytes \((?<objects>.*) objects\)/.match(allocated_str) -retained_stats = /Total retained: (?<bytes>.*) bytes \((?<objects>.*) objects\)/.match(retained_str) - -abort 'failed to process the benchmark output' unless allocated_stats && retained_stats - -File.open(metrics_filename, 'a') do |f| - f.puts "memory_static_objects_allocated_mb #{(allocated_stats[:bytes].to_f / (1024 * 1024)).round(1)}" - f.puts "memory_static_objects_retained_mb #{(retained_stats[:bytes].to_f / (104 * 1024)).round(1)}" - f.puts "memory_static_objects_allocated_items #{allocated_stats[:objects]}" - f.puts "memory_static_objects_retained_items #{retained_stats[:objects]}" -end diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index 3bae2e08a6f..633ea28e96c 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -1,7 +1,7 @@ [[ "$TRACE" ]] && set -x export TILLER_NAMESPACE="$KUBE_NAMESPACE" -function deployExists() { +function deploy_exists() { local namespace="${1}" local deploy="${2}" echoinfo "Checking if ${deploy} exists in the ${namespace} namespace..." true @@ -13,8 +13,7 @@ function deployExists() { return $deploy_exists } -function previousDeployFailed() { - set +e +function previous_deploy_failed() { local deploy="${1}" echoinfo "Checking for previous deployment of ${deploy}" true @@ -34,7 +33,6 @@ function previousDeployFailed() { else echoerr "Previous deployment NOT found." fi - set -e return $status } @@ -51,49 +49,35 @@ function delete() { helm delete --purge "$name" } -function cleanup() { - if [ -z "$CI_ENVIRONMENT_SLUG" ]; then - echoerr "No release given, aborting the delete!" - return - fi - - echoinfo "Cleaning up '$CI_ENVIRONMENT_SLUG'..." true - - kubectl -n "$KUBE_NAMESPACE" delete \ - ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa \ - --now --ignore-not-found --include-uninitialized \ - -l release="$CI_ENVIRONMENT_SLUG" -} - function get_pod() { local app_name="${1}" local status="${2-Running}" get_pod_cmd="kubectl get pods -n ${KUBE_NAMESPACE} --field-selector=status.phase=${status} -lapp=${app_name},release=${CI_ENVIRONMENT_SLUG} --no-headers -o=custom-columns=NAME:.metadata.name" - echoinfo "Running '${get_pod_cmd}'" true + echoinfo "Waiting till '${app_name}' pod is ready" true + echoinfo "Running '${get_pod_cmd}'" + local interval=5 + local elapsed_seconds=0 + local max_seconds=$((2 * 60)) while true; do local pod_name pod_name="$(eval "${get_pod_cmd}")" [[ "${pod_name}" == "" ]] || break - echoinfo "Waiting till '${app_name}' pod is ready"; - sleep 5; + if [[ "${elapsed_seconds}" -gt "${max_seconds}" ]]; then + echoerr "The pod name couldn't be found after ${elapsed_seconds} seconds, aborting." + break + fi + + printf "." + let "elapsed_seconds+=interval" + sleep ${interval} done echoinfo "The pod name is '${pod_name}'." echo "${pod_name}" } -function perform_review_app_deployment() { - check_kube_domain - ensure_namespace - install_tiller - install_external_dns - time deploy - wait_for_review_app_to_be_accessible - add_license -} - function check_kube_domain() { echoinfo "Checking that Kube domain exists..." true @@ -119,9 +103,16 @@ function install_tiller() { echoinfo "Initiating the Helm client..." helm init --client-only + # Set toleration for Tiller to be installed on a specific node pool helm init \ + --wait \ --upgrade \ - --replicas 2 + --node-selectors "app=helm" \ + --replicas 3 \ + --override "spec.template.spec.tolerations[0].key"="dedicated" \ + --override "spec.template.spec.tolerations[0].operator"="Equal" \ + --override "spec.template.spec.tolerations[0].value"="helm" \ + --override "spec.template.spec.tolerations[0].effect"="NoSchedule" kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy" @@ -137,7 +128,7 @@ function install_external_dns() { domain=$(echo "${REVIEW_APPS_DOMAIN}" | awk -F. '{printf "%s.%s", $(NF-1), $NF}') echoinfo "Installing external DNS for domain ${domain}..." true - if ! deployExists "${KUBE_NAMESPACE}" "${release_name}" || previousDeployFailed "${release_name}" ; then + if ! deploy_exists "${KUBE_NAMESPACE}" "${release_name}" || previous_deploy_failed "${release_name}" ; then echoinfo "Installing external-dns Helm chart" helm repo update helm install stable/external-dns \ @@ -156,7 +147,7 @@ function install_external_dns() { fi } -function create_secret() { +function create_application_secret() { echoinfo "Creating the ${CI_ENVIRONMENT_SLUG}-gitlab-initial-root-password secret in the ${KUBE_NAMESPACE} namespace..." true kubectl create secret generic -n "$KUBE_NAMESPACE" \ @@ -165,7 +156,7 @@ function create_secret() { --dry-run -o json | kubectl apply -f - } -function download_gitlab_chart() { +function download_chart() { echoinfo "Downloading the GitLab chart..." true curl -o gitlab.tar.bz2 "https://gitlab.com/charts/gitlab/-/archive/${GITLAB_HELM_CHART_REF}/gitlab-${GITLAB_HELM_CHART_REF}.tar.bz2" @@ -194,14 +185,12 @@ function deploy() { gitlab_workhorse_image_repository="${IMAGE_REPOSITORY}/gitlab-workhorse-${IMAGE_VERSION}" # Cleanup and previous installs, as FAILED and PENDING_UPGRADE will cause errors with `upgrade` - if [ "$CI_ENVIRONMENT_SLUG" != "production" ] && previousDeployFailed "$CI_ENVIRONMENT_SLUG" ; then + if [ "$CI_ENVIRONMENT_SLUG" != "production" ] && previous_deploy_failed "$CI_ENVIRONMENT_SLUG" ; then echo "Deployment in bad state, cleaning up $CI_ENVIRONMENT_SLUG" delete - cleanup fi - create_secret - download_gitlab_chart + create_application_secret HELM_CMD=$(cat << EOF helm upgrade --install \ @@ -216,7 +205,7 @@ HELM_CMD=$(cat << EOF --set prometheus.install=false \ --set global.ingress.configureCertmanager=false \ --set global.ingress.tls.secretName=tls-cert \ - --set global.ingress.annotations."external-dns\.alpha\.kubernetes\.io/ttl"="10" + --set global.ingress.annotations."external-dns\.alpha\.kubernetes\.io/ttl"="10" \ --set nginx-ingress.controller.service.enableHttp=false \ --set nginx-ingress.defaultBackend.resources.requests.memory=7Mi \ --set nginx-ingress.controller.resources.requests.memory=440M \ @@ -252,14 +241,35 @@ EOF echoinfo "Deploying with:" echoinfo "${HELM_CMD}" - eval $HELM_CMD || true + eval "${HELM_CMD}" +} + +function display_deployment_debug() { + migrations_pod=$(get_pod "migrations"); + if [ -z "${migrations_pod}" ]; then + echoerr "Migrations pod not found." + else + echoinfo "Logs tail of the ${migrations_pod} pod..." + + kubectl logs -n "$KUBE_NAMESPACE" "${migrations_pod}" | sed "s/${REVIEW_APPS_ROOT_PASSWORD}/[REDACTED]/g" + fi + + unicorn_pod=$(get_pod "unicorn"); + if [ -z "${unicorn_pod}" ]; then + echoerr "Unicorn pod not found." + else + echoinfo "Logs tail of the ${unicorn_pod} pod..." + + kubectl logs -n "$KUBE_NAMESPACE" -c unicorn "${unicorn_pod}" | sed "s/${REVIEW_APPS_ROOT_PASSWORD}/[REDACTED]/g" + fi } function wait_for_review_app_to_be_accessible() { - # In case the Review App isn't completely available yet. Keep trying for 5 minutes. + echoinfo "Waiting for the Review App at ${CI_ENVIRONMENT_URL} to be accessible..." true + local interval=5 local elapsed_seconds=0 - local max_seconds=$((5 * 60)) + local max_seconds=$((2 * 60)) while true; do local review_app_http_code review_app_http_code=$(curl --silent --output /dev/null --max-time 5 --write-out "%{http_code}" "${CI_ENVIRONMENT_URL}/users/sign_in") @@ -272,10 +282,10 @@ function wait_for_review_app_to_be_accessible() { sleep ${interval} done - if [[ "${review_app_http_code}" == "200" ]]; then - echoinfo "The Review App at ${CI_ENVIRONMENT_URL} is ready!" + if [[ "${review_app_http_code}" -eq "200" ]]; then + echoinfo "The Review App at ${CI_ENVIRONMENT_URL} is ready after ${elapsed_seconds} seconds!" else - echoerr "The Review App at ${CI_ENVIRONMENT_URL} isn't ready after 5 minutes of polling..." + echoerr "The Review App at ${CI_ENVIRONMENT_URL} isn't ready after ${max_seconds} seconds of polling..." exit 1 fi } diff --git a/scripts/trigger-build b/scripts/trigger-build index 52bc61cac56..4d8110fce10 100755 --- a/scripts/trigger-build +++ b/scripts/trigger-build @@ -122,7 +122,14 @@ module Trigger end def ref - ENV['CNG_BRANCH'] || 'master' + default_ref = + if ENV['CI_COMMIT_REF_NAME'] =~ /^[\d-]+-stable(-ee)?$/ + ENV['CI_COMMIT_REF_NAME'] + else + 'master' + end + + ENV['CNG_BRANCH'] || default_ref end def trigger_token diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index b1798c11361..6c9ae343e01 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -16,7 +16,9 @@ describe 'Issue Boards', :js do let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) } let(:board) { create(:board, project: project) } let!(:list) { create(:list, board: board, label: development, position: 0) } - let(:card) { find('.board:nth-child(2)').first('.board-card') } + let(:card) { find('.board:nth-child(2)').first('.board-card') } + + let(:application_settings) { {} } around do |example| Timecop.freeze { example.run } @@ -27,6 +29,8 @@ describe 'Issue Boards', :js do sign_in(user) + stub_application_setting(application_settings) + visit project_board_path(project, board) wait_for_requests end @@ -223,16 +227,24 @@ describe 'Issue Boards', :js do end context 'time tracking' do + let(:compare_meter_tooltip) { find('.time-tracking .time-tracking-content .compare-meter')['data-original-title'] } + before do issue2.timelogs.create(time_spent: 14400, user: user) - issue2.update!(time_estimate: 28800) + issue2.update!(time_estimate: 128800) + + click_card(card) end it 'shows time tracking progress bar' do - click_card(card) + expect(compare_meter_tooltip).to eq('Time remaining: 3d 7h 46m') + end + + context 'when time_tracking_limit_to_hours is true' do + let(:application_settings) { { time_tracking_limit_to_hours: true } } - page.within('.time-tracking') do - expect(find('.time-tracking-content .compare-meter')['data-original-title']).to eq('Time remaining: 4h') + it 'shows time tracking progress bar' do + expect(compare_meter_tooltip).to eq('Time remaining: 31h 46m') end end end diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index 8eb413bdd8d..40845ec48f9 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -3,14 +3,14 @@ require 'rails_helper' describe 'GFM autocomplete', :js do let(:issue_xss_title) { 'This will execute alert<img src=x onerror=alert(2)<img src=x onerror=alert(1)>' } let(:user_xss_title) { 'eve <img src=x onerror=alert(2)<img src=x onerror=alert(1)>' } - let(:label_xss_title) { 'alert label <img src=x onerror="alert(\'Hello xss\');" a'} + let(:label_xss_title) { 'alert label <img src=x onerror="alert(\'Hello xss\');" a' } let(:milestone_xss_title) { 'alert milestone <img src=x onerror="alert(\'Hello xss\');" a' } let(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') } - let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') } + let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') } let(:project) { create(:project) } let(:label) { create(:label, project: project, title: 'special+') } - let(:issue) { create(:issue, project: project) } + let(:issue) { create(:issue, project: project) } before do project.add_maintainer(user) @@ -293,6 +293,70 @@ describe 'GFM autocomplete', :js do expect(find('.atwho-view-ul').text).to have_content('alert label') end end + + it 'allows colons when autocompleting scoped labels' do + create(:label, project: project, title: 'scoped:label') + + note = find('#note-body') + type(note, '~scoped:') + + wait_for_requests + + page.within '.atwho-container #at-view-labels' do + expect(find('.atwho-view-ul').text).to have_content('scoped:label') + end + end + + it 'allows colons when autocompleting scoped labels with double colons' do + create(:label, project: project, title: 'scoped::label') + + note = find('#note-body') + type(note, '~scoped::') + + wait_for_requests + + page.within '.atwho-container #at-view-labels' do + expect(find('.atwho-view-ul').text).to have_content('scoped::label') + end + end + + it 'allows spaces when autocompleting multi-word labels' do + create(:label, project: project, title: 'Accepting merge requests') + + note = find('#note-body') + type(note, '~Accepting merge') + + wait_for_requests + + page.within '.atwho-container #at-view-labels' do + expect(find('.atwho-view-ul').text).to have_content('Accepting merge requests') + end + end + + it 'only autocompletes the latest label' do + create(:label, project: project, title: 'Accepting merge requests') + create(:label, project: project, title: 'Accepting job applicants') + + note = find('#note-body') + type(note, '~Accepting merge requests foo bar ~Accepting job') + + wait_for_requests + + page.within '.atwho-container #at-view-labels' do + expect(find('.atwho-view-ul').text).to have_content('Accepting job applicants') + end + end + + it 'does not autocomplete labels if no tilde is typed' do + create(:label, project: project, title: 'Accepting merge requests') + + note = find('#note-body') + type(note, 'Accepting merge') + + wait_for_requests + + expect(page).not_to have_css('.atwho-container #at-view-labels') + end end shared_examples 'autocomplete suggestions' do diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js index 9f49e68cfe8..751fb5e1b94 100644 --- a/spec/frontend/lib/utils/datetime_utility_spec.js +++ b/spec/frontend/lib/utils/datetime_utility_spec.js @@ -334,6 +334,12 @@ describe('prettyTime methods', () => { assertTimeUnits(aboveOneDay, 33, 2, 2, 0); assertTimeUnits(aboveOneWeek, 26, 0, 1, 9); }); + + it('should correctly parse values when limitedToHours is true', () => { + const twoDays = datetimeUtility.parseSeconds(173000, { limitToHours: true }); + + assertTimeUnits(twoDays, 3, 48, 0, 0); + }); }); describe('stringifyTime', () => { diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js index e81115e10c9..5266b1bdbfc 100644 --- a/spec/javascripts/boards/boards_store_spec.js +++ b/spec/javascripts/boards/boards_store_spec.js @@ -355,4 +355,14 @@ describe('Store', () => { expect(boardsStore.moving.list).toEqual(dummyList); }); }); + + describe('setTimeTrackingLimitToHours', () => { + it('sets the timeTracking.LimitToHours option', () => { + boardsStore.timeTracking.limitToHours = false; + + boardsStore.setTimeTrackingLimitToHours('true'); + + expect(boardsStore.timeTracking.limitToHours).toEqual(true); + }); + }); }); diff --git a/spec/javascripts/boards/components/issue_time_estimate_spec.js b/spec/javascripts/boards/components/issue_time_estimate_spec.js index ba65d3287da..de48e3f6091 100644 --- a/spec/javascripts/boards/components/issue_time_estimate_spec.js +++ b/spec/javascripts/boards/components/issue_time_estimate_spec.js @@ -1,40 +1,70 @@ import Vue from 'vue'; import IssueTimeEstimate from '~/boards/components/issue_time_estimate.vue'; +import boardsStore from '~/boards/stores/boards_store'; import mountComponent from '../../helpers/vue_mount_component_helper'; -describe('Issue Tine Estimate component', () => { +describe('Issue Time Estimate component', () => { let vm; beforeEach(() => { - const Component = Vue.extend(IssueTimeEstimate); - vm = mountComponent(Component, { - estimate: 374460, - }); + boardsStore.create(); }); afterEach(() => { vm.$destroy(); }); - it('renders the correct time estimate', () => { - expect(vm.$el.querySelector('time').textContent.trim()).toEqual('2w 3d 1m'); - }); + describe('when limitToHours is false', () => { + beforeEach(() => { + boardsStore.timeTracking.limitToHours = false; + + const Component = Vue.extend(IssueTimeEstimate); + vm = mountComponent(Component, { + estimate: 374460, + }); + }); + + it('renders the correct time estimate', () => { + expect(vm.$el.querySelector('time').textContent.trim()).toEqual('2w 3d 1m'); + }); + + it('renders expanded time estimate in tooltip', () => { + expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain( + '2 weeks 3 days 1 minute', + ); + }); + + it('prevents tooltip xss', done => { + const alertSpy = spyOn(window, 'alert'); + vm.estimate = 'Foo <script>alert("XSS")</script>'; - it('renders expanded time estimate in tooltip', () => { - expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain( - '2 weeks 3 days 1 minute', - ); + vm.$nextTick(() => { + expect(alertSpy).not.toHaveBeenCalled(); + expect(vm.$el.querySelector('time').textContent.trim()).toEqual('0m'); + expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain('0m'); + done(); + }); + }); }); - it('prevents tooltip xss', done => { - const alertSpy = spyOn(window, 'alert'); - vm.estimate = 'Foo <script>alert("XSS")</script>'; + describe('when limitToHours is true', () => { + beforeEach(() => { + boardsStore.timeTracking.limitToHours = true; + + const Component = Vue.extend(IssueTimeEstimate); + vm = mountComponent(Component, { + estimate: 374460, + }); + }); + + it('renders the correct time estimate', () => { + expect(vm.$el.querySelector('time').textContent.trim()).toEqual('104h 1m'); + }); - vm.$nextTick(() => { - expect(alertSpy).not.toHaveBeenCalled(); - expect(vm.$el.querySelector('time').textContent.trim()).toEqual('0m'); - expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain('0m'); - done(); + it('renders expanded time estimate in tooltip', () => { + expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain( + '104 hours 1 minute', + ); }); }); }); diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js index 37354283cab..537152f5eed 100644 --- a/spec/javascripts/ide/stores/actions_spec.js +++ b/spec/javascripts/ide/stores/actions_spec.js @@ -492,6 +492,33 @@ describe('Multi-file store actions', () => { done, ); }); + + it('does not delete a folder after it is emptied', done => { + const testFolder = { + type: 'tree', + tree: [], + }; + const testEntry = { + path: 'testFolder/entry-to-delete', + parentPath: 'testFolder', + opened: false, + tree: [], + }; + testFolder.tree.push(testEntry); + store.state.entries = { + testFolder, + 'testFolder/entry-to-delete': testEntry, + }; + + testAction( + deleteEntry, + 'testFolder/entry-to-delete', + store.state, + [{ type: types.DELETE_ENTRY, payload: 'testFolder/entry-to-delete' }], + [{ type: 'burstUnusedSeal' }, { type: 'triggerFilesChange' }], + done, + ); + }); }); describe('renameEntry', () => { diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js index 4c3dd713589..2e1863cff86 100644 --- a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js +++ b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js @@ -13,6 +13,7 @@ describe('Issuable Time Tracker', () => { timeSpent, timeEstimateHumanReadable, timeSpentHumanReadable, + limitToHours, }) => { setFixtures(` <div> @@ -25,6 +26,7 @@ describe('Issuable Time Tracker', () => { timeSpent, humanTimeEstimate: timeEstimateHumanReadable, humanTimeSpent: timeSpentHumanReadable, + limitToHours: Boolean(limitToHours), rootPath: '/', }; @@ -128,6 +130,29 @@ describe('Issuable Time Tracker', () => { }); }); + describe('Comparison pane when limitToHours is true', () => { + beforeEach(() => { + initTimeTrackingComponent({ + timeEstimate: 100000, // 1d 3h + timeSpent: 5000, // 1h 23m + timeEstimateHumanReadable: '', + timeSpentHumanReadable: '', + limitToHours: true, + }); + }); + + it('should show the correct tooltip text', done => { + Vue.nextTick(() => { + expect(vm.showComparisonState).toBe(true); + const $title = vm.$el.querySelector('.time-tracking-content .compare-meter').dataset + .originalTitle; + + expect($title).toBe('Time remaining: 26h 23m'); + done(); + }); + }); + }); + describe('Estimate only pane', () => { beforeEach(() => { initTimeTrackingComponent({ diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb index c5bc81a2b9e..51e16c99688 100644 --- a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb +++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb @@ -23,7 +23,7 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do let(:cluster) { create(:cluster, :group) } before do - allow(build.deployment).to receive(:cluster).and_return(cluster) + allow(build.deployment).to receive(:deployment_platform_cluster).and_return(cluster) end it { is_expected.to be_truthy } @@ -49,7 +49,7 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do context 'and no cluster to deploy to' do before do - expect(deployment.cluster).to be_nil + expect(deployment.deployment_platform_cluster).to be_nil end it { is_expected.to be_falsey } @@ -67,7 +67,7 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do let(:cluster) { create(:cluster, :group) } before do - allow(build.deployment).to receive(:cluster).and_return(cluster) + allow(build.deployment).to receive(:deployment_platform_cluster).and_return(cluster) end it 'creates a kubernetes namespace' do @@ -84,7 +84,7 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do context 'completion is not required' do before do - expect(deployment.cluster).to be_nil + expect(deployment.deployment_platform_cluster).to be_nil end it 'does not create a namespace' do diff --git a/spec/lib/gitlab/lets_encrypt/client_spec.rb b/spec/lib/gitlab/lets_encrypt/client_spec.rb index 5454d9c1af4..cbb862cb0c9 100644 --- a/spec/lib/gitlab/lets_encrypt/client_spec.rb +++ b/spec/lib/gitlab/lets_encrypt/client_spec.rb @@ -116,42 +116,6 @@ describe ::Gitlab::LetsEncrypt::Client do end end - describe '#enabled?' do - subject { client.enabled? } - - context 'when terms of service are accepted' do - it { is_expected.to eq(true) } - - context "when private_key isn't present and database is read only" do - before do - allow(::Gitlab::Database).to receive(:read_only?).and_return(true) - end - - it 'returns false' do - expect(::Gitlab::CurrentSettings.lets_encrypt_private_key).to eq(nil) - - is_expected.to eq(false) - end - end - - context 'when feature flag is disabled' do - before do - stub_feature_flags(pages_auto_ssl: false) - end - - it { is_expected.to eq(false) } - end - end - - context 'when terms of service are not accepted' do - before do - stub_application_setting(lets_encrypt_terms_of_service_accepted: false) - end - - it { is_expected.to eq(false) } - end - end - describe '#terms_of_service_url' do subject { client.terms_of_service_url } diff --git a/spec/lib/gitlab/lets_encrypt_spec.rb b/spec/lib/gitlab/lets_encrypt_spec.rb new file mode 100644 index 00000000000..674b114e9d3 --- /dev/null +++ b/spec/lib/gitlab/lets_encrypt_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ::Gitlab::LetsEncrypt do + include LetsEncryptHelpers + + before do + stub_lets_encrypt_settings + end + + describe '.enabled?' do + let(:project) { create(:project) } + let(:pages_domain) { create(:pages_domain, project: project) } + + subject { described_class.enabled?(pages_domain) } + + context 'when terms of service are accepted' do + it { is_expected.to eq(true) } + + context 'when feature flag is disabled' do + before do + stub_feature_flags(pages_auto_ssl: false) + end + + it { is_expected.to eq(false) } + end + end + + context 'when terms of service are not accepted' do + before do + stub_application_setting(lets_encrypt_terms_of_service_accepted: false) + end + + it { is_expected.to eq(false) } + end + + context 'when feature flag for project is disabled' do + before do + stub_feature_flags(pages_auto_ssl_for_project: false) + end + + it 'returns false' do + is_expected.to eq(false) + end + end + + context 'when domain has not project' do + let(:pages_domain) { create(:pages_domain) } + + it 'returns false' do + is_expected.to eq(false) + end + end + end +end diff --git a/spec/lib/gitlab/time_tracking_formatter_spec.rb b/spec/lib/gitlab/time_tracking_formatter_spec.rb new file mode 100644 index 00000000000..a85d418777f --- /dev/null +++ b/spec/lib/gitlab/time_tracking_formatter_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::TimeTrackingFormatter do + describe '#parse' do + subject { described_class.parse(duration_string) } + + context 'positive durations' do + let(:duration_string) { '3h 20m' } + + it { expect(subject).to eq(12_000) } + end + + context 'negative durations' do + let(:duration_string) { '-3h 20m' } + + it { expect(subject).to eq(-12_000) } + end + end + + describe '#output' do + let(:num_seconds) { 178_800 } + + subject { described_class.output(num_seconds) } + + context 'time_tracking_limit_to_hours setting is true' do + before do + stub_application_setting(time_tracking_limit_to_hours: true) + end + + it { expect(subject).to eq('49h 40m') } + end + + context 'time_tracking_limit_to_hours setting is false' do + before do + stub_application_setting(time_tracking_limit_to_hours: false) + end + + it { expect(subject).to eq('1w 1d 1h 40m') } + end + end +end diff --git a/spec/migrations/backport_enterprise_schema_spec.rb b/spec/migrations/backport_enterprise_schema_spec.rb new file mode 100644 index 00000000000..8d2d9d4953a --- /dev/null +++ b/spec/migrations/backport_enterprise_schema_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require Rails.root.join('db', 'migrate', '20190402150158_backport_enterprise_schema.rb') + +describe BackportEnterpriseSchema, :migration, schema: 20190329085614 do + include MigrationsHelpers + + def drop_if_exists(table) + active_record_base.connection.drop_table(table) if active_record_base.connection.table_exists?(table) + end + + describe '#up' do + it 'creates new EE tables' do + migrate! + + expect(active_record_base.connection.table_exists?(:epics)).to be true + expect(active_record_base.connection.table_exists?(:geo_nodes)).to be true + end + + context 'missing EE columns' do + before do + drop_if_exists(:epics) + + active_record_base.connection.create_table "epics" do |t| + t.integer :group_id, null: false, index: true + t.integer :author_id, null: false, index: true + end + end + + after do + drop_if_exists(:epics) + end + + it 'flags an error' do + expect { migrate! }.to raise_error(/Your database is missing.*that is present for GitLab EE/) + end + end + end +end diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index 1dceef3fc00..a433878f3bc 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -380,12 +380,12 @@ describe Deployment do end end - describe '#cluster' do + describe '#deployment_platform_cluster' do let(:deployment) { create(:deployment) } let(:project) { deployment.project } let(:environment) { deployment.environment } - subject { deployment.cluster } + subject { deployment.deployment_platform_cluster } before do expect(project).to receive(:deployment_platform) diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 8b503777443..f9c8b42afa8 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -65,7 +65,7 @@ describe API::Branches do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { get api(route, current_user) } end end @@ -175,7 +175,7 @@ describe API::Branches do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { get api(route, current_user) } end end @@ -337,7 +337,7 @@ describe API::Branches do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { put api(route, current_user) } end end @@ -471,7 +471,7 @@ describe API::Branches do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { put api(route, current_user) } end end @@ -547,7 +547,7 @@ describe API::Branches do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { post api(route, current_user) } end end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index f104da6ebba..3df5d9412f8 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -736,7 +736,7 @@ describe API::Commits do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { get api(route, current_user) } end end @@ -825,7 +825,7 @@ describe API::Commits do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { get api(route, current_user) } end end @@ -968,7 +968,7 @@ describe API::Commits do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { get api(route, current_user) } end end @@ -1067,7 +1067,7 @@ describe API::Commits do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { get api(route, current_user) } end end @@ -1169,7 +1169,7 @@ describe API::Commits do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { post api(route, current_user), params: { branch: 'master' } } end end @@ -1324,7 +1324,7 @@ describe API::Commits do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { post api(route, current_user), params: { branch: branch } } end end @@ -1435,7 +1435,7 @@ describe API::Commits do context 'when repository is disabled' do include_context 'disabled repository' - it_behaves_like '403 response' do + it_behaves_like '404 response' do let(:request) { post api(route, current_user), params: { note: 'My comment' } } end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 2420817e1f7..30a867fa7ba 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -946,6 +946,18 @@ describe SystemNoteService do expect(subject.note).to eq "changed time estimate to 1w 4d 5h" end + + context 'when time_tracking_limit_to_hours setting is true' do + before do + stub_application_setting(time_tracking_limit_to_hours: true) + end + + it 'sets the note text' do + noteable.update_attribute(:time_estimate, 277200) + + expect(subject.note).to eq "changed time estimate to 77h" + end + end end context 'without a time estimate' do @@ -1022,6 +1034,18 @@ describe SystemNoteService do end end + context 'when time_tracking_limit_to_hours setting is true' do + before do + stub_application_setting(time_tracking_limit_to_hours: true) + end + + it 'sets the note text' do + spend_time!(277200) + + expect(subject.note).to eq "added 77h of time spent" + end + end + def spend_time!(seconds) noteable.spend_time(duration: seconds, user_id: author.id) noteable.save! diff --git a/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb b/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb index 2ae4872f51d..08a3511f70b 100644 --- a/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb +++ b/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb @@ -12,15 +12,18 @@ describe PagesDomainSslRenewalCronWorker do end describe '#perform' do - let!(:domain) { create(:pages_domain) } - let!(:domain_with_enabled_auto_ssl) { create(:pages_domain, auto_ssl_enabled: true) } - let!(:domain_with_obtained_letsencrypt) { create(:pages_domain, :letsencrypt, auto_ssl_enabled: true) } + let(:project) { create :project } + let!(:domain) { create(:pages_domain, project: project) } + let!(:domain_with_enabled_auto_ssl) { create(:pages_domain, project: project, auto_ssl_enabled: true) } + let!(:domain_with_obtained_letsencrypt) do + create(:pages_domain, :letsencrypt, project: project, auto_ssl_enabled: true) + end let!(:domain_without_auto_certificate) do - create(:pages_domain, :without_certificate, :without_key, auto_ssl_enabled: true) + create(:pages_domain, :without_certificate, :without_key, project: project, auto_ssl_enabled: true) end let!(:domain_with_expired_auto_ssl) do - create(:pages_domain, :letsencrypt, :with_expired_certificate) + create(:pages_domain, :letsencrypt, :with_expired_certificate, project: project) end it 'enqueues a PagesDomainSslRenewalWorker for domains needing renewal' do diff --git a/spec/workers/pages_domain_ssl_renewal_worker_spec.rb b/spec/workers/pages_domain_ssl_renewal_worker_spec.rb index a3d33de1b40..3552ff0823a 100644 --- a/spec/workers/pages_domain_ssl_renewal_worker_spec.rb +++ b/spec/workers/pages_domain_ssl_renewal_worker_spec.rb @@ -7,7 +7,8 @@ describe PagesDomainSslRenewalWorker do subject(:worker) { described_class.new } - let(:domain) { create(:pages_domain) } + let(:project) { create(:project) } + let(:domain) { create(:pages_domain, project: project) } before do stub_lets_encrypt_settings @@ -22,14 +23,24 @@ describe PagesDomainSslRenewalWorker do worker.perform(domain.id) end + shared_examples 'does nothing' do + it 'does nothing' do + expect(::PagesDomains::ObtainLetsEncryptCertificateService).not_to receive(:new) + end + end + context 'when domain was deleted' do before do domain.destroy! end - it 'does nothing' do - expect(::PagesDomains::ObtainLetsEncryptCertificateService).not_to receive(:new) - end + include_examples 'does nothing' + end + + context 'when domain is disabled' do + let(:domain) { create(:pages_domain, :disabled) } + + include_examples 'does nothing' end end end diff --git a/yarn.lock b/yarn.lock index c54e7cd460b..bbab157d4c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1686,13 +1686,13 @@ axios-mock-adapter@^1.15.0: dependencies: deep-equal "^1.0.1" -axios@^0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.17.1.tgz#2d8e3e5d0bdbd7327f91bc814f5c57660f81824d" - integrity sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0= +axios@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" + integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== dependencies: - follow-redirects "^1.2.5" - is-buffer "^1.1.5" + follow-redirects "1.5.10" + is-buffer "^2.0.2" babel-code-frame@^6.26.0: version "6.26.0" @@ -3410,6 +3410,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" +debug@=3.1.0, debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + debug@^3.1.0, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -3424,13 +3431,6 @@ debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" -debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - decamelize-keys@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" @@ -4731,12 +4731,12 @@ flush-write-stream@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" -follow-redirects@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.6.tgz#4dcdc7e4ab3dd6765a97ff89c3b4c258117c79bf" - integrity sha512-FrMqZ/FONtHnbqO651UPpfRUVukIEwJhXMfdr/JWAmrDbeYBu773b1J6gdWDyRIj4hvvzQEHoEOTrdR8o6KLYA== +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== dependencies: - debug "^3.1.0" + debug "=3.1.0" for-in@^1.0.2: version "1.0.2" @@ -5734,7 +5734,7 @@ is-buffer@^1.1.5, is-buffer@~1.1.1: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.0: +is-buffer@^2.0.0, is-buffer@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== |