diff options
42 files changed, 382 insertions, 131 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dadce073309..f27d809af3c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -448,6 +448,7 @@ db:migrate:reset-mysql: .migration-paths: &migration-paths <<: *dedicated-runner <<: *pull-cache + <<: *except-docs stage: test variables: SETUP_DB: "false" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6fb2c6bd1dc..1f25171e8a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -421,7 +421,7 @@ request is as follows: 1. Fork the project into your personal space on GitLab.com 1. Create a feature branch, branch away from `master` -1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code +1. Write [tests](https://docs.gitlab.com/ee/development/rake_tasks.html#run-tests) and code 1. [Generate a changelog entry with `bin/changelog`][changelog] 1. If you are writing documentation, make sure to follow the [documentation styleguide][doc-styleguide] diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 038239bf466..9178fec085a 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -332,7 +332,14 @@ class FilteredSearchManager { const removeElements = []; [].forEach.call(this.tokensContainer.children, (t) => { - if (t.classList.contains('js-visual-token')) { + let canClearToken = t.classList.contains('js-visual-token'); + + if (canClearToken) { + const tokenKey = t.querySelector('.name').textContent.trim(); + canClearToken = this.canEdit && this.canEdit(tokenKey); + } + + if (canClearToken) { removeElements.push(t); } }); @@ -411,8 +418,14 @@ class FilteredSearchManager { }); } + // allows for modifying params array when a param can't be included in the URL (e.g. Service Desk) + getAllParams(urlParams) { + return this.modifyUrlParams ? this.modifyUrlParams(urlParams) : urlParams; + } + loadSearchParamsFromURL() { - const params = gl.utils.getUrlParamsArray(); + const urlParams = gl.utils.getUrlParamsArray(); + const params = this.getAllParams(urlParams); const usernameParams = this.getUsernameParams(); let hasFilteredSearch = false; diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js index 57394097944..917a45eb06b 100644 --- a/app/assets/javascripts/lib/utils/number_utils.js +++ b/app/assets/javascripts/lib/utils/number_utils.js @@ -13,7 +13,7 @@ export function formatRelevantDigits(number) { let relevantDigits = 0; let formattedNumber = ''; if (!isNaN(Number(number))) { - digitsLeft = number.split('.')[0]; + digitsLeft = number.toString().split('.')[0]; switch (digitsLeft.length) { case 1: relevantDigits = 3; diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue index cde2ff7ca2a..6b3e341f936 100644 --- a/app/assets/javascripts/monitoring/components/graph.vue +++ b/app/assets/javascripts/monitoring/components/graph.vue @@ -3,7 +3,7 @@ import GraphLegend from './graph/legend.vue'; import GraphFlag from './graph/flag.vue'; import GraphDeployment from './graph/deployment.vue'; - import monitoringPaths from './monitoring_paths.vue'; + import GraphPath from './graph_path.vue'; import MonitoringMixin from '../mixins/monitoring_mixins'; import eventHub from '../event_hub'; import measurements from '../utils/measurements'; @@ -40,8 +40,6 @@ graphHeightOffset: 120, margin: {}, unitOfDisplay: '', - areaColorRgb: '#8fbce8', - lineColorRgb: '#1f78d1', yAxisLabel: '', legendTitle: '', reducedDeploymentData: [], @@ -63,7 +61,7 @@ GraphLegend, GraphFlag, GraphDeployment, - monitoringPaths, + GraphPath, }, computed: { @@ -143,7 +141,7 @@ }, renderAxesPaths() { - this.timeSeries = createTimeSeries(this.graphData.queries[0].result, + this.timeSeries = createTimeSeries(this.graphData.queries[0], this.graphWidth, this.graphHeight, this.graphHeightOffset); @@ -162,7 +160,7 @@ const xAxis = d3.svg.axis() .scale(axisXScale) - .ticks(measurements.xTicks) + .ticks(d3.time.minute, 60) .tickFormat(timeScaleFormat) .orient('bottom'); @@ -238,7 +236,7 @@ class="graph-data" :viewBox="innerViewBox" ref="graphData"> - <monitoring-paths + <graph-path v-for="(path, index) in timeSeries" :key="index" :generated-line-path="path.linePath" @@ -246,7 +244,7 @@ :line-color="path.lineColor" :area-color="path.areaColor" /> - <monitoring-deployment + <graph-deployment :show-deploy-info="showDeployInfo" :deployment-data="reducedDeploymentData" :graph-height="graphHeight" diff --git a/app/assets/javascripts/monitoring/components/graph/legend.vue b/app/assets/javascripts/monitoring/components/graph/legend.vue index a43dad8e601..dbc48c63747 100644 --- a/app/assets/javascripts/monitoring/components/graph/legend.vue +++ b/app/assets/javascripts/monitoring/components/graph/legend.vue @@ -81,6 +81,13 @@ formatMetricUsage(series) { return `${formatRelevantDigits(series.values[this.currentDataIndex].value)} ${this.unitOfDisplay}`; }, + + createSeriesString(index, series) { + if (series.metricTag) { + return `${series.metricTag} ${this.formatMetricUsage(series)}`; + } + return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(series)}`; + }, }, mounted() { this.$nextTick(() => { @@ -164,7 +171,7 @@ ref="legendTitleSvg" x="38" :y="graphHeight - 30"> - {{legendTitle}} Series {{index + 1}} {{formatMetricUsage(series)}} + {{createSeriesString(index, series)}} </text> <text v-else diff --git a/app/assets/javascripts/monitoring/components/monitoring_paths.vue b/app/assets/javascripts/monitoring/components/graph_path.vue index 043f1bf66bb..043f1bf66bb 100644 --- a/app/assets/javascripts/monitoring/components/monitoring_paths.vue +++ b/app/assets/javascripts/monitoring/components/graph_path.vue diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js index 05d551e917c..3cbe06d8fd6 100644 --- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js +++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js @@ -1,8 +1,37 @@ import d3 from 'd3'; import _ from 'underscore'; -export default function createTimeSeries(seriesData, graphWidth, graphHeight, graphHeightOffset) { - const maxValues = seriesData.map((timeSeries, index) => { +const defaultColorPalette = { + blue: ['#1f78d1', '#8fbce8'], + orange: ['#fc9403', '#feca81'], + red: ['#db3b21', '#ed9d90'], + green: ['#1aaa55', '#8dd5aa'], + purple: ['#6666c4', '#d1d1f0'], +}; + +const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple']; + +export default function createTimeSeries(queryData, graphWidth, graphHeight, graphHeightOffset) { + let usedColors = []; + + function pickColor(name) { + let pick; + if (name && defaultColorPalette[name]) { + pick = name; + } else { + const unusedColors = _.difference(defaultColorOrder, usedColors); + if (unusedColors.length > 0) { + pick = unusedColors[0]; + } else { + usedColors = []; + pick = defaultColorOrder[0]; + } + } + usedColors.push(pick); + return defaultColorPalette[pick]; + } + + const maxValues = queryData.result.map((timeSeries, index) => { const maxValue = d3.max(timeSeries.values.map(d => d.value)); return { maxValue, @@ -12,10 +41,11 @@ export default function createTimeSeries(seriesData, graphWidth, graphHeight, gr const maxValueFromSeries = _.max(maxValues, val => val.maxValue); - let timeSeriesNumber = 1; - let lineColor = '#1f78d1'; - let areaColor = '#8fbce8'; - return seriesData.map((timeSeries) => { + return queryData.result.map((timeSeries, timeSeriesNumber) => { + let metricTag = ''; + let lineColor = ''; + let areaColor = ''; + const timeSeriesScaleX = d3.time.scale() .range([0, graphWidth - 70]); @@ -23,49 +53,30 @@ export default function createTimeSeries(seriesData, graphWidth, graphHeight, gr .range([graphHeight - graphHeightOffset, 0]); timeSeriesScaleX.domain(d3.extent(timeSeries.values, d => d.time)); + timeSeriesScaleX.ticks(d3.time.minute, 60); timeSeriesScaleY.domain([0, maxValueFromSeries.maxValue]); const lineFunction = d3.svg.line() + .interpolate('linear') .x(d => timeSeriesScaleX(d.time)) .y(d => timeSeriesScaleY(d.value)); const areaFunction = d3.svg.area() + .interpolate('linear') .x(d => timeSeriesScaleX(d.time)) .y0(graphHeight - graphHeightOffset) - .y1(d => timeSeriesScaleY(d.value)) - .interpolate('linear'); - - switch (timeSeriesNumber) { - case 1: - lineColor = '#1f78d1'; - areaColor = '#8fbce8'; - break; - case 2: - lineColor = '#fc9403'; - areaColor = '#feca81'; - break; - case 3: - lineColor = '#db3b21'; - areaColor = '#ed9d90'; - break; - case 4: - lineColor = '#1aaa55'; - areaColor = '#8dd5aa'; - break; - case 5: - lineColor = '#6666c4'; - areaColor = '#d1d1f0'; - break; - default: - lineColor = '#1f78d1'; - areaColor = '#8fbce8'; - break; - } + .y1(d => timeSeriesScaleY(d.value)); - if (timeSeriesNumber <= 5) { - timeSeriesNumber = timeSeriesNumber += 1; + const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]]; + const seriesCustomizationData = queryData.series != null && + _.findWhere(queryData.series[0].when, + { value: timeSeriesMetricLabel }); + if (seriesCustomizationData != null) { + metricTag = seriesCustomizationData.value || timeSeriesMetricLabel; + [lineColor, areaColor] = pickColor(seriesCustomizationData.color); } else { - timeSeriesNumber = 1; + metricTag = timeSeriesMetricLabel || `series ${timeSeriesNumber + 1}`; + [lineColor, areaColor] = pickColor(); } return { @@ -75,6 +86,7 @@ export default function createTimeSeries(seriesData, graphWidth, graphHeight, gr values: timeSeries.values, lineColor, areaColor, + metricTag, }; }); } diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 23909bd2d39..0d0e53d4b76 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -10,6 +10,22 @@ module IssuableCollections private + def set_issues_index + @collection_type = "Issue" + @issues = issues_collection + @issues = @issues.page(params[:page]) + @issuable_meta_data = issuable_meta_data(@issues, @collection_type) + @total_pages = issues_page_count(@issues) + + return if redirect_out_of_range(@issues, @total_pages) + + if params[:label_name].present? + @labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute + end + + @users = [] + end + def issues_collection issues_finder.execute.preload(:project, :author, :assignees, :labels, :milestone, project: :namespace) end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index ab9f132b502..8990c919ca0 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -10,6 +10,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action :check_issues_available! before_action :issue, except: [:index, :new, :create, :bulk_update] + before_action :set_issues_index, only: [:index] # Allow write(create) issue before_action :authorize_create_issue!, only: [:new, :create] @@ -23,20 +24,6 @@ class Projects::IssuesController < Projects::ApplicationController respond_to :html def index - @collection_type = "Issue" - @issues = issues_collection - @issues = @issues.page(params[:page]) - @issuable_meta_data = issuable_meta_data(@issues, @collection_type) - @total_pages = issues_page_count(@issues) - - return if redirect_out_of_range(@issues, @total_pages) - - if params[:label_name].present? - @labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute - end - - @users = [] - if params[:assignee_id].present? assignee = User.find_by_id(params[:assignee_id]) @users.push(assignee) if assignee diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb index c53ea4519da..f7e17f5cc01 100644 --- a/app/helpers/graph_helper.rb +++ b/app/helpers/graph_helper.rb @@ -7,7 +7,8 @@ module GraphHelper refs << commit_refs.join(' ') # append note count - refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0 + notes_count = @graph.notes[commit.id] + refs << "[#{notes_count} #{pluralize(notes_count, 'note')}]" if notes_count > 0 refs end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 95dbdc8ec46..c4ea0f5ac53 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -104,7 +104,7 @@ module TreeHelper subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path) if subtree.count == 1 && subtree.first.dir? - return tree_join(tree.name, flatten_tree(subtree.first)) + return tree_join(tree.name, flatten_tree(root_path, subtree.first)) else return tree.name end diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 27fadc1d952..9589e81c750 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -108,7 +108,7 @@ %span.badge.count.issue_counter.fly-out-badge = number_with_delimiter(@project.open_issues_count) %li.divider.fly-out-top-item - = nav_link(controller: :issues) do + = nav_link(controller: :issues, action: :index) do = link_to project_issues_path(@project), title: 'Issues' do %span List diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index 6fb5aa45166..6f7713124ac 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -1,7 +1,9 @@ +- empty_state_path = local_assigns.fetch(:empty_state_path, 'shared/empty_states/issues') + %ul.content-list.issues-list.issuable-list = render partial: "projects/issues/issue", collection: @issues - if @issues.blank? - = render 'shared/empty_states/issues' + = render empty_state_path - if @issues.present? = paginate @issues, theme: "gitlab", total_pages: @total_pages diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml index ce0aa72ab00..c5a8b32c772 100644 --- a/app/views/shared/boards/components/_board.html.haml +++ b/app/views/shared/boards/components/_board.html.haml @@ -8,7 +8,7 @@ "aria-hidden": "true" } %span.board-title-text.has-tooltip{ "v-if": "list.type !== \"label\"", - ":title" => '(list.label ? list.label.description : "")' } + ":title" => '(list.label ? list.label.description : "")', data: { container: "body" } } {{ list.title }} %span.has-tooltip{ "v-if": "list.type === \"label\"", diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml index 093b2d82813..79e8f8d0e89 100644 --- a/app/views/u2f/_register.html.haml +++ b/app/views/u2f/_register.html.haml @@ -6,15 +6,15 @@ %script#js-register-u2f-setup{ type: "text/template" } - if current_user.two_factor_otp_enabled? .row.append-bottom-10 - .col-md-3 - %button#js-setup-u2f-device.btn.btn-info Setup new U2F device - .col-md-9 + .col-md-4 + %button#js-setup-u2f-device.btn.btn-info.btn-block Setup new U2F device + .col-md-8 %p Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left. - else .row.append-bottom-10 - .col-md-3 - %button#js-setup-u2f-device.btn.btn-info{ disabled: true } Setup new U2F device - .col-md-9 + .col-md-4 + %button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true } Setup new U2F device + .col-md-8 %p.text-warning You need to register a two-factor authentication app before you can set up a U2F device. %script#js-register-u2f-in-progress{ type: "text/template" } diff --git a/changelogs/unreleased/bugfix-graph-friendly-notes-number.yml b/changelogs/unreleased/bugfix-graph-friendly-notes-number.yml new file mode 100644 index 00000000000..3a99729fb48 --- /dev/null +++ b/changelogs/unreleased/bugfix-graph-friendly-notes-number.yml @@ -0,0 +1,5 @@ +--- +title: Show notes number more user-friendly in the graph +merge_request: 13949 +author: Vladislav Kaverin +type: changed diff --git a/changelogs/unreleased/fix-tooltip-width-issue-board.yml b/changelogs/unreleased/fix-tooltip-width-issue-board.yml new file mode 100644 index 00000000000..a648953c5bd --- /dev/null +++ b/changelogs/unreleased/fix-tooltip-width-issue-board.yml @@ -0,0 +1,5 @@ +--- +title: Issue board tooltips are now the correct width when the column is collapsed +merge_request: +author: Jedidiah Broadbent +type: fixed diff --git a/changelogs/unreleased/support-additional-colors.yml b/changelogs/unreleased/support-additional-colors.yml new file mode 100644 index 00000000000..5178e159dcf --- /dev/null +++ b/changelogs/unreleased/support-additional-colors.yml @@ -0,0 +1,5 @@ +--- +title: Added support for specific labels and colors +merge_request: +author: +type: changed diff --git a/changelogs/unreleased/uipolish-fix-2factor-warning.yml b/changelogs/unreleased/uipolish-fix-2factor-warning.yml new file mode 100644 index 00000000000..9f55207d309 --- /dev/null +++ b/changelogs/unreleased/uipolish-fix-2factor-warning.yml @@ -0,0 +1,5 @@ +--- +title: Two factor auth messages in settings no longer overlap the button +merge_request: +author: Jedidiah Broadbent +type: fixed diff --git a/doc/README.md b/doc/README.md index b3e7c9bd0bf..0d82a09d1f4 100644 --- a/doc/README.md +++ b/doc/README.md @@ -68,17 +68,18 @@ Shortcuts to GitLab's most visited docs: Manage your [repositories](user/project/repository/index.md) from the UI (user interface): -- Files +- [Files](user/project/repository/index.md#files) - [Create a file](user/project/repository/web_editor.md#create-a-file) - [Upload a file](user/project/repository/web_editor.md#upload-a-file) - [File templates](user/project/repository/web_editor.md#template-dropdowns) - [Create a directory](user/project/repository/web_editor.md#create-a-directory) - [Start a merge request](user/project/repository/web_editor.md#tips) (when committing via UI) -- Branches +- [Branches](user/project/repository/branches/index.md) + - [Default branch](user/project/repository/branches/index.md#default-branch) - [Create a branch](user/project/repository/web_editor.md#create-a-new-branch) - [Protected branches](user/project/protected_branches.md#protected-branches) - [Delete merged branches](user/project/repository/branches/index.md#delete-merged-branches) -- Commits +- [Commits](user/project/repository/index.md#commits) - [Signing commits](user/project/repository/gpg_signed_commits/index.md): use GPG to sign your commits. ### Issues and Merge Requests (MRs) diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index f5d3b524d6e..bac8e972754 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -228,7 +228,8 @@ To make a Runner pick tagged/untagged jobs: ### Be careful with sensitive information -If you can run a job on a Runner, you can get access to any code it runs +With some [Runner Executors](https://docs.gitlab.com/runner/executors/README.html), +if you can run a job on the Runner, you can get access to any code it runs and get the token of the Runner. With shared Runners, this means that anyone that runs jobs on the Runner, can access anyone else's code that runs on the Runner. @@ -237,7 +238,8 @@ In addition, because you can get access to the Runner token, it is possible to create a clone of a Runner and submit false jobs, for example. The above is easily avoided by restricting the usage of shared Runners -on large public GitLab instances and controlling access to your GitLab instance. +on large public GitLab instances, controlling access to your GitLab instance, +and using more secure [Runner Executors](https://docs.gitlab.com/runner/executors/README.html). ### Forks diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index d0ac3ec6163..78733b9cc4b 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1366,25 +1366,31 @@ variables: GIT_DEPTH: "3" ``` -## Hidden keys +## Hidden keys (jobs) > Introduced in GitLab 8.6 and GitLab Runner v1.1.1. -Keys that start with a dot (`.`) will be not processed by GitLab CI. You can -use this feature to ignore jobs, or use the -[special YAML features](#special-yaml-features) and transform the hidden keys -into templates. +If you want to temporarily 'disable' a job, rather than commenting out all the +lines where the job is defined: + +``` +#hidden_job: +# script: +# - run test +``` -In the following example, `.key_name` will be ignored: +you can instead start its name with a dot (`.`) and it will not be processed by +GitLab CI. In the following example, `.hidden_job` will be ignored: ```yaml -.key_name: +.hidden_job: script: - - rake spec + - run test ``` -Hidden keys can be hashes like normal CI jobs, but you are also allowed to use -different types of structures to leverage special YAML features. +Use this feature to ignore jobs, or use the +[special YAML features](#special-yaml-features) and transform the hidden keys +into templates. ## Special YAML features @@ -1400,7 +1406,7 @@ Read more about the various [YAML features](https://learnxinyminutes.com/docs/ya YAML has a handy feature called 'anchors', which lets you easily duplicate content across your document. Anchors can be used to duplicate/inherit -properties, and is a perfect example to be used with [hidden keys](#hidden-keys) +properties, and is a perfect example to be used with [hidden keys](#hidden-keys-jobs) to provide templates for your jobs. The following example uses anchors and map merging. It will create two jobs, diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md index 4f20aa070de..c8d23609280 100644 --- a/doc/development/fe_guide/style_guide_js.md +++ b/doc/development/fe_guide/style_guide_js.md @@ -311,6 +311,7 @@ A forEach will cause side effects, it will be mutating the array being iterated. #### Alignment 1. Follow these alignment styles for the template method: + 1. With more than one attribute, all attributes should be on a new line: ```javascript // bad <component v-if="bar" @@ -327,9 +328,16 @@ A forEach will cause side effects, it will be mutating the array being iterated. <button class="btn"> Click me </button> + ``` + 1. The tag can be inline if there is only one attribute: + ```javascript + // good + <component bar="bar" /> - // if props fit in one line then keep it on the same line - <component bar="bar" /> + // good + <component + bar="bar" + /> ``` #### Quotes @@ -381,9 +389,12 @@ A forEach will cause side effects, it will be mutating the array being iterated. } ``` -1. Default key should always be provided if the prop is not required: +1. Default key should be provided if the prop is not required. +_Note:_ There are some scenarios where we need to check for the existence of the property. +On those a default key should not be provided. + ```javascript - // bad + // good props: { foo: { type: String, @@ -512,11 +523,11 @@ A forEach will cause side effects, it will be mutating the array being iterated. ``` ### The Javascript/Vue Accord -The goal of this accord is to make sure we are all on the same page. +The goal of this accord is to make sure we are all on the same page. -1. When writing Vue, you may not use jQuery in your application. +1. When writing Vue, you may not use jQuery in your application. 1. If you need to grab data from the DOM, you may query the DOM 1 time while bootstrapping your application to grab data attributes using `dataset`. You can do this without jQuery. - 1. You may use a jQuery dependency in Vue.js following [this example from the docs](https://vuejs.org/v2/examples/select2.html). + 1. You may use a jQuery dependency in Vue.js following [this example from the docs](https://vuejs.org/v2/examples/select2.html). 1. If an outside jQuery Event needs to be listen to inside the Vue application, you may use jQuery event listeners. 1. We will avoid adding new jQuery events when they are not required. Instead of adding new jQuery events take a look at [different methods to do the same task](https://vuejs.org/v2/api/#vm-emit). 1. You may query the `window` object 1 time, while bootstrapping your application for application specific data (e.g. `scrollTo` is ok to access anytime). Do this access during the bootstrapping of your application. diff --git a/doc/user/project/index.md b/doc/user/project/index.md index d6b3d59d407..a56b3744480 100644 --- a/doc/user/project/index.md +++ b/doc/user/project/index.md @@ -20,6 +20,8 @@ When you create a project in GitLab, you'll have access to a large number of - [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards) (**EES/EEP**): Allow your teams to create their own workflows (Issue Boards) for the same project - [Repositories](repository/index.md): Host your code in a fully integrated platform + - [Branches](repository/branches/index.md): use Git branching strategies to + collaborate on code - [Protected branches](protected_branches.md): Prevent collaborators from messing with history or pushing code without review - [Protected tags](protected_tags.md): Control over who has diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md index 1948627ee79..e1d3aebb8b3 100644 --- a/doc/user/project/repository/branches/index.md +++ b/doc/user/project/repository/branches/index.md @@ -1,5 +1,32 @@ # Branches +Read through GiLab's branching documentation: + +- [Create a branch](../web_editor.md#create-a-new-branch) +- [Default branch](#default-branch) +- [Protected branches](../../protected_branches.md#protected-branches) +- [Delete merged branches](#delete-merged-branches) + +See also: + +- [GitLab Flow](../../../../university/training/gitlab_flow.md#gitlab-flow): use the best of GitLab for your branching strategies +- [Getting started with Git](../../../../topics/git/index.md) and GitLab + +## Default branch + +When you create a new [project](../../index.md), GitLab sets `master` as the default +branch for your project. You can choose another branch to be your project's +default under your project's **Settings > General**. + +The default branch is the branched affected by the +[issue closing pattern](../../issues/automatic_issue_closing.md), +which means that _an issue will be closed when a merge request is merged to +the **default branch**_. + +The default branch is also protected against accidental deletion. Read through +the documentation on [protected branches](../../protected_branches.md#protected-branches) +to learn more. + ## Delete merged branches > [Introduced][ce-6449] in GitLab 8.14. diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md index 20aadb8f7ff..dfe43c6b691 100644 --- a/doc/user/project/repository/gpg_signed_commits/index.md +++ b/doc/user/project/repository/gpg_signed_commits/index.md @@ -113,7 +113,7 @@ started: 1. Use the following command to list the private GPG key you just created: ``` - gpg --list-secret-keys mr@robot.sh + gpg --list-secret-keys --keyid-format 0xLONG mr@robot.sh ``` Replace `mr@robot.sh` with the email address you entered above. @@ -167,7 +167,7 @@ key to use. 1. Use the following command to list the private GPG key you just created: ``` - gpg --list-secret-keys mr@robot.sh + gpg --list-secret-keys --keyid-format 0xLONG mr@robot.sh ``` Replace `mr@robot.sh` with the email address you entered above. diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md index 235af83353d..9501db88f57 100644 --- a/doc/user/project/repository/index.md +++ b/doc/user/project/repository/index.md @@ -55,7 +55,7 @@ Use GitLab's [file finder](../../../workflow/file_finder.md) to search for files ## Branches -When you submit changes in a new branch, you create a new version +When you submit changes in a new [branch](branches/index.md), you create a new version of that project's file tree. Your branch contains all the changes you are presenting, which are detected by Git line by line. @@ -70,8 +70,9 @@ With [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) subscriptions, you can also request [approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html#merge-request-approvals) from your managers. -To create, delete, and branches via GitLab's UI: +To create, delete, and [branches](branches/index.md) via GitLab's UI: +- [Default branches](branches/index.md#default-branch) - [Create a branch](web_editor.md#create-a-new-branch) - [Protected branches](../protected_branches.md#protected-branches) - [Delete merged branches](branches/index.md#delete-merged-branches) diff --git a/doc/user/project/settings/img/general_settings.png b/doc/user/project/settings/img/general_settings.png Binary files differnew file mode 100755 index 00000000000..96f5b84871f --- /dev/null +++ b/doc/user/project/settings/img/general_settings.png diff --git a/doc/user/project/settings/img/merge_requests_settings.png b/doc/user/project/settings/img/merge_requests_settings.png Binary files differnew file mode 100755 index 00000000000..b1f2dfa7376 --- /dev/null +++ b/doc/user/project/settings/img/merge_requests_settings.png diff --git a/doc/user/project/settings/img/sharing_and_permissions_settings.png b/doc/user/project/settings/img/sharing_and_permissions_settings.png Binary files differnew file mode 100755 index 00000000000..7767a3d7187 --- /dev/null +++ b/doc/user/project/settings/img/sharing_and_permissions_settings.png diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md new file mode 100644 index 00000000000..cd73c0687d6 --- /dev/null +++ b/doc/user/project/settings/index.md @@ -0,0 +1,44 @@ +# Project settings + +You can adjust your [project](../index.md) settings by navigating +to your project's homepage and clicking **Settings**. + +## General settings + +Adjust your project's path and name, description, avatar, [default branch](../repository/branches/index.md#default-branches), and tags: + +![general project settings](img/general_settings.png) + +### Sharing and permissions + +Set up your project's access, [visibility](../../../public_access/public_access.md), and enable [Container Registry](../container_registry.md) for your projects: + +![projects sharing permissions](img/sharing_and_permissions_settings.png) + +### Issue settings + +Add an [issue description template](../description_templates.md#description-templates) to your project, so that every new issue will start with a custom template. + +### Merge request settings + +Set up your project's merge request settings: + +- Set up the merge request method (merge commit, [fast-forward merge](https://docs.gitlab.com/ee/user/project/merge_requests/fast_forward_merge.html#fast-forward-merge-requests)). Fast-forward is available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/). +- Merge request [description templates](../description_templates.md#description-templates) +- Enable [merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html#merge-request-approvals), available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/). +- Enable [merge only of pipeline succeeds](../merge_requests/merge_when_pipeline_succeeds.md) +- Enable [merge only when all discussions are resolved](../../discussions/index.md#only-allow-merge-requests-to-be-merged-if-all-discussions-are-resolved) + +![project's merge request settings](img/merge_requests_settings.png) + +### Service Desk + +Enable [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html) for your project to offer customer support. Service Desk is available in [GitLab Enterprise Edition Premium](https://about.gitlab.com/gitlab-ee/). + +### Export project + +Learn how to [export a project](import_export.md#importing-the-project) in GitLab. + +### Advanced settings + +Here you can run housekeeping, archive, rename, transfer, or remove a project. diff --git a/spec/features/projects/user_browses_a_tree_with_a_folder_containing_only_a_folder.rb b/spec/features/projects/user_browses_a_tree_with_a_folder_containing_only_a_folder.rb new file mode 100644 index 00000000000..a17e65cc5b9 --- /dev/null +++ b/spec/features/projects/user_browses_a_tree_with_a_folder_containing_only_a_folder.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +# This is a regression test for https://gitlab.com/gitlab-org/gitlab-ce/issues/37569 +describe 'User browses a tree with a folder containing only a folder' do + let(:project) { create(:project, :empty_repo) } + let(:user) { project.creator } + + before do + # We need to disable the tree.flat_path provided by Gitaly to reproduce the issue + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) + + project.repository.create_dir(user, 'foo/bar', branch_name: 'master', message: 'Add the foo/bar folder') + sign_in(user) + visit(project_tree_path(project, project.repository.root_ref)) + end + + it 'shows the nested folder on a single row' do + expect(page).to have_content('foo/bar') + end +end diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js index 16ae649ee60..f209328dee1 100644 --- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js @@ -411,4 +411,26 @@ describe('Filtered Search Manager', () => { expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(false); }); }); + + describe('getAllParams', () => { + beforeEach(() => { + this.paramsArr = ['key=value', 'otherkey=othervalue']; + + initializeManager(); + }); + + it('correctly modifies params when custom modifier is passed', () => { + const modifedParams = manager.getAllParams.call({ + modifyUrlParams: paramsArr => paramsArr.reverse(), + }, [].concat(this.paramsArr)); + + expect(modifedParams[0]).toBe(this.paramsArr[1]); + }); + + it('does not modify params when no custom modifier is passed', () => { + const modifedParams = manager.getAllParams.call({}, this.paramsArr); + + expect(modifedParams[1]).toBe(this.paramsArr[1]); + }); + }); }); diff --git a/spec/javascripts/monitoring/graph/legend_spec.js b/spec/javascripts/monitoring/graph/legend_spec.js index da2fbd26e23..2571b7ef869 100644 --- a/spec/javascripts/monitoring/graph/legend_spec.js +++ b/spec/javascripts/monitoring/graph/legend_spec.js @@ -28,7 +28,7 @@ const defaultValuesComponent = { currentDataIndex: 0, }; -const timeSeries = createTimeSeries(convertedMetrics[0].queries[0].result, +const timeSeries = createTimeSeries(convertedMetrics[0].queries[0], defaultValuesComponent.graphWidth, defaultValuesComponent.graphHeight, defaultValuesComponent.graphHeightOffset); @@ -89,13 +89,12 @@ describe('GraphLegend', () => { expect(component.$el.querySelectorAll('.rect-axis-text').length).toEqual(2); }); - it('contains text to signal the usage, title and time', () => { + it('contains text to signal the usage, title and time with multiple time series', () => { const component = createComponent(defaultValuesComponent); const titles = component.$el.querySelectorAll('.legend-metric-title'); - expect(getTextFromNode(component, '.legend-metric-title').indexOf(component.legendTitle)).not.toEqual(-1); - expect(titles[0].textContent.indexOf('Title')).not.toEqual(-1); - expect(titles[1].textContent.indexOf('Series')).not.toEqual(-1); + expect(titles[0].textContent.indexOf('1xx')).not.toEqual(-1); + expect(titles[1].textContent.indexOf('2xx')).not.toEqual(-1); expect(getTextFromNode(component, '.y-label-text')).toEqual(component.yAxisLabel); }); diff --git a/spec/javascripts/monitoring/monitoring_paths_spec.js b/spec/javascripts/monitoring/graph_path_spec.js index d39db945e17..a4844636d09 100644 --- a/spec/javascripts/monitoring/monitoring_paths_spec.js +++ b/spec/javascripts/monitoring/graph_path_spec.js @@ -1,10 +1,10 @@ import Vue from 'vue'; -import MonitoringPaths from '~/monitoring/components/monitoring_paths.vue'; +import GraphPath from '~/monitoring/components/graph_path.vue'; import createTimeSeries from '~/monitoring/utils/multiple_time_series'; import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from './mock_data'; const createComponent = (propsData) => { - const Component = Vue.extend(MonitoringPaths); + const Component = Vue.extend(GraphPath); return new Component({ propsData, @@ -13,22 +13,23 @@ const createComponent = (propsData) => { const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); -const timeSeries = createTimeSeries(convertedMetrics[0].queries[0].result, 428, 272, 120); +const timeSeries = createTimeSeries(convertedMetrics[0].queries[0], 428, 272, 120); +const firstTimeSeries = timeSeries[0]; describe('Monitoring Paths', () => { it('renders two paths to represent a line and the area underneath it', () => { const component = createComponent({ - generatedLinePath: timeSeries[0].linePath, - generatedAreaPath: timeSeries[0].areaPath, - lineColor: '#ccc', - areaColor: '#fff', + generatedLinePath: firstTimeSeries.linePath, + generatedAreaPath: firstTimeSeries.areaPath, + lineColor: firstTimeSeries.lineColor, + areaColor: firstTimeSeries.areaColor, }); const metricArea = component.$el.querySelector('.metric-area'); const metricLine = component.$el.querySelector('.metric-line'); - expect(metricArea.getAttribute('fill')).toBe('#fff'); - expect(metricArea.getAttribute('d')).toBe(timeSeries[0].areaPath); - expect(metricLine.getAttribute('stroke')).toBe('#ccc'); - expect(metricLine.getAttribute('d')).toBe(timeSeries[0].linePath); + expect(metricArea.getAttribute('fill')).toBe('#8fbce8'); + expect(metricArea.getAttribute('d')).toBe(firstTimeSeries.areaPath); + expect(metricLine.getAttribute('stroke')).toBe('#1f78d1'); + expect(metricLine.getAttribute('d')).toBe(firstTimeSeries.linePath); }); }); diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js index 3d399f2bb95..7ceab657464 100644 --- a/spec/javascripts/monitoring/mock_data.js +++ b/spec/javascripts/monitoring/mock_data.js @@ -6346,7 +6346,13 @@ export const singleRowMetricsMultipleSeries = [ } ] }, - ] + ], + 'when': [ + { + 'value': 'hundred(s)', + 'color': 'green', + }, + ], } ] }, diff --git a/spec/javascripts/monitoring/utils/multiple_time_series_spec.js b/spec/javascripts/monitoring/utils/multiple_time_series_spec.js index 3daf6bf82df..7e44a9ade9e 100644 --- a/spec/javascripts/monitoring/utils/multiple_time_series_spec.js +++ b/spec/javascripts/monitoring/utils/multiple_time_series_spec.js @@ -2,16 +2,17 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series'; import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data'; const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); -const timeSeries = createTimeSeries(convertedMetrics[0].queries[0].result, 428, 272, 120); +const timeSeries = createTimeSeries(convertedMetrics[0].queries[0], 428, 272, 120); +const firstTimeSeries = timeSeries[0]; describe('Multiple time series', () => { it('createTimeSeries returned array contains an object for each element', () => { - expect(typeof timeSeries[0].linePath).toEqual('string'); - expect(typeof timeSeries[0].areaPath).toEqual('string'); - expect(typeof timeSeries[0].timeSeriesScaleX).toEqual('function'); - expect(typeof timeSeries[0].areaColor).toEqual('string'); - expect(typeof timeSeries[0].lineColor).toEqual('string'); - expect(timeSeries[0].values instanceof Array).toEqual(true); + expect(typeof firstTimeSeries.linePath).toEqual('string'); + expect(typeof firstTimeSeries.areaPath).toEqual('string'); + expect(typeof firstTimeSeries.timeSeriesScaleX).toEqual('function'); + expect(typeof firstTimeSeries.areaColor).toEqual('string'); + expect(typeof firstTimeSeries.lineColor).toEqual('string'); + expect(firstTimeSeries.values instanceof Array).toEqual(true); }); it('createTimeSeries returns an array', () => { diff --git a/vendor/gitlab-ci-yml/CONTRIBUTING.md b/vendor/gitlab-ci-yml/CONTRIBUTING.md index 6e5160a2487..d4c057bf9dc 100644 --- a/vendor/gitlab-ci-yml/CONTRIBUTING.md +++ b/vendor/gitlab-ci-yml/CONTRIBUTING.md @@ -1,5 +1,47 @@ -The canonical repository for `.gitlab-ci.yml` templates is -https://gitlab.com/gitlab-org/gitlab-ci-yml. +## Contributing + +Thank you for your interest in contributing to this GitLab project! We welcome +all contributions. By participating in this project, you agree to abide by the +[code of conduct](#code-of-conduct). + +## Contributor license agreement + +By submitting code as an individual you agree to the [individual contributor +license agreement][individual-agreement]. + +By submitting code as an entity you agree to the [corporate contributor license +agreement][corporate-agreement]. + +## Code of conduct + +As contributors and maintainers of this project, we pledge to respect all people +who contribute through reporting issues, posting feature requests, updating +documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free +experience for everyone, regardless of level of experience, gender, gender +identity and expression, sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual +language or imagery, derogatory comments or personal attacks, trolling, public +or private harassment, insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct. Project maintainers who do not follow the +Code of Conduct may be removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior can be +reported by emailing contact@gitlab.com. + +This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0, +available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/). + +[contributor-covenant]: http://contributor-covenant.org +[individual-agreement]: https://docs.gitlab.com/ee/legal/individual_contributor_license_agreement.html +[corporate-agreement]: https://docs.gitlab.com/ee/legal/corporate_contributor_license_agreement.html -GitLab only mirrors the templates. Please submit your merge requests to -https://gitlab.com/gitlab-org/gitlab-ci-yml. diff --git a/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml index 06b0c84e516..6e5fe97cf6d 100644 --- a/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml @@ -1,3 +1,6 @@ +# This template has been DEPRECATED. Consider using Auto DevOps instead: +# https://docs.gitlab.com/ee/topics/autodevops + # Explanation on the scripts: # https://gitlab.com/gitlab-examples/kubernetes-deploy/blob/master/README.md image: registry.gitlab.com/gitlab-examples/kubernetes-deploy diff --git a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml index 722934b7981..019a4d4cd7d 100644 --- a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml @@ -1,3 +1,6 @@ +# This template has been DEPRECATED. Consider using Auto DevOps instead: +# https://docs.gitlab.com/ee/topics/autodevops + # Explanation on the scripts: # https://gitlab.com/gitlab-examples/kubernetes-deploy/blob/master/README.md image: registry.gitlab.com/gitlab-examples/kubernetes-deploy diff --git a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml index acba718ebe4..60a9430a839 100644 --- a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml @@ -1,3 +1,6 @@ +# This template has been DEPRECATED. Consider using Auto DevOps instead: +# https://docs.gitlab.com/ee/topics/autodevops + # Explanation on the scripts: # https://gitlab.com/gitlab-examples/openshift-deploy/blob/master/README.md image: registry.gitlab.com/gitlab-examples/openshift-deploy |