Commit 687a6c08 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into ce-to-ee-tue

parents 8ae9b485 19dd138c
...@@ -13,9 +13,11 @@ ...@@ -13,9 +13,11 @@
}, },
"plugins": [ "plugins": [
"filenames", "filenames",
"import" "import",
"html"
], ],
"settings": { "settings": {
"html/html-extensions": [".html", ".html.raw", ".vue"],
"import/resolver": { "import/resolver": {
"webpack": { "webpack": {
"config": "./config/webpack.config.js" "config": "./config/webpack.config.js"
......
...@@ -90,6 +90,8 @@ window.Build = (function () { ...@@ -90,6 +90,8 @@ window.Build = (function () {
success: ((log) => { success: ((log) => {
const $buildContainer = $('.js-build-output'); const $buildContainer = $('.js-build-output');
gl.utils.setCiStatusFavicon(`${this.pageUrl}/status.json`);
if (log.state) { if (log.state) {
this.state = log.state; this.state = log.state;
} }
......
...@@ -4,8 +4,8 @@ import PipelinesTableComponent from '../../vue_shared/components/pipelines_table ...@@ -4,8 +4,8 @@ import PipelinesTableComponent from '../../vue_shared/components/pipelines_table
import PipelinesService from '../../vue_pipelines_index/services/pipelines_service'; import PipelinesService from '../../vue_pipelines_index/services/pipelines_service';
import PipelineStore from '../../vue_pipelines_index/stores/pipelines_store'; import PipelineStore from '../../vue_pipelines_index/stores/pipelines_store';
import eventHub from '../../vue_pipelines_index/event_hub'; import eventHub from '../../vue_pipelines_index/event_hub';
import EmptyState from '../../vue_pipelines_index/components/empty_state'; import EmptyState from '../../vue_pipelines_index/components/empty_state.vue';
import ErrorState from '../../vue_pipelines_index/components/error_state'; import ErrorState from '../../vue_pipelines_index/components/error_state.vue';
import '../../lib/utils/common_utils'; import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor'; import '../../vue_shared/vue_resource_interceptor';
import Poll from '../../lib/utils/poll'; import Poll from '../../lib/utils/poll';
......
<script>
/* eslint-disable no-new, no-alert */ /* eslint-disable no-new, no-alert */
/* global Flash */ /* global Flash */
import '~/flash'; import '~/flash';
...@@ -65,29 +66,31 @@ export default { ...@@ -65,29 +66,31 @@ export default {
this.isLoading = true; this.isLoading = true;
this.service.postAction(this.endpoint) this.service.postAction(this.endpoint)
.then(() => { .then(() => {
this.isLoading = false; this.isLoading = false;
eventHub.$emit('refreshPipelines'); eventHub.$emit('refreshPipelines');
}) })
.catch(() => { .catch(() => {
this.isLoading = false; this.isLoading = false;
new Flash('An error occured while making the request.'); new Flash('An error occured while making the request.');
}); });
}, },
}, },
template: `
<button
type="button"
@click="onClick"
:class="buttonClass"
:title="title"
:aria-label="title"
data-container="body"
data-placement="top"
:disabled="isLoading">
<i :class="iconClass" aria-hidden="true"/>
<i class="fa fa-spinner fa-spin" aria-hidden="true" v-if="isLoading" />
</button>
`,
}; };
</script>
<template>
<button
type="button"
@click="onClick"
:class="buttonClass"
:title="title"
:aria-label="title"
data-container="body"
data-placement="top"
:disabled="isLoading"
>
<i :class="iconClass" aria-hidden="true"></i>
<i class="fa fa-spinner fa-spin" aria-hidden="true" v-if="isLoading"></i>
</button>
</template>
import pipelinesEmptyStateSVG from 'empty_states/icons/_pipelines_empty.svg';
export default {
props: {
helpPagePath: {
type: String,
required: true,
},
},
template: `
<div class="row empty-state">
<div class="col-xs-12">
<div class="svg-content">
${pipelinesEmptyStateSVG}
</div>
</div>
<div class="col-xs-12 text-center">
<div class="text-content">
<h4>Build with confidence</h4>
<p>
Continous Integration can help catch bugs by running your tests automatically,
while Continuous Deployment can help you deliver code to your product environment.
</p>
<a :href="helpPagePath" class="btn btn-info">
Get started with Pipelines
</a>
</div>
</div>
</div>
`,
};
<script>
import pipelinesEmptyStateSVG from 'empty_states/icons/_pipelines_empty.svg';
export default {
props: {
helpPagePath: {
type: String,
required: true,
},
},
data: () => ({ pipelinesEmptyStateSVG }),
};
</script>
<template>
<div class="row empty-state">
<div class="col-xs-12">
<div class="svg-content" v-html="pipelinesEmptyStateSVG" />
</div>
<div class="col-xs-12 text-center">
<div class="text-content">
<h4>Build with confidence</h4>
<p>
Continous Integration can help catch bugs by running your tests automatically,
while Continuous Deployment can help you deliver code to your product environment.
</p>
<a :href="helpPagePath" class="btn btn-info">
Get started with Pipelines
</a>
</div>
</div>
</div>
</template>
import pipelinesErrorStateSVG from 'empty_states/icons/_pipelines_failed.svg';
export default {
template: `
<div class="row empty-state js-pipelines-error-state">
<div class="col-xs-12">
<div class="svg-content">
${pipelinesErrorStateSVG}
</div>
</div>
<div class="col-xs-12 text-center">
<div class="text-content">
<h4>The API failed to fetch the pipelines.</h4>
</div>
</div>
</div>
`,
};
<script>
import pipelinesErrorStateSVG from 'empty_states/icons/_pipelines_failed.svg';
export default {
data: () => ({ pipelinesErrorStateSVG }),
};
</script>
<template>
<div class="row empty-state js-pipelines-error-state">
<div class="col-xs-12">
<div class="svg-content" v-html="pipelinesErrorStateSVG" />
</div>
<div class="col-xs-12 text-center">
<div class="text-content">
<h4>The API failed to fetch the pipelines.</h4>
</div>
</div>
</div>
</template>
...@@ -4,8 +4,8 @@ import PipelinesService from './services/pipelines_service'; ...@@ -4,8 +4,8 @@ import PipelinesService from './services/pipelines_service';
import eventHub from './event_hub'; import eventHub from './event_hub';
import PipelinesTableComponent from '../vue_shared/components/pipelines_table'; import PipelinesTableComponent from '../vue_shared/components/pipelines_table';
import TablePaginationComponent from '../vue_shared/components/table_pagination'; import TablePaginationComponent from '../vue_shared/components/table_pagination';
import EmptyState from './components/empty_state'; import EmptyState from './components/empty_state.vue';
import ErrorState from './components/error_state'; import ErrorState from './components/error_state.vue';
import NavigationTabs from './components/navigation_tabs'; import NavigationTabs from './components/navigation_tabs';
import NavigationControls from './components/nav_controls'; import NavigationControls from './components/nav_controls';
import Poll from '../lib/utils/poll'; import Poll from '../lib/utils/poll';
......
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import AsyncButtonComponent from '../../vue_pipelines_index/components/async_button'; import AsyncButtonComponent from '../../vue_pipelines_index/components/async_button.vue';
import PipelinesActionsComponent from '../../vue_pipelines_index/components/pipelines_actions'; import PipelinesActionsComponent from '../../vue_pipelines_index/components/pipelines_actions';
import PipelinesArtifactsComponent from '../../vue_pipelines_index/components/pipelines_artifacts'; import PipelinesArtifactsComponent from '../../vue_pipelines_index/components/pipelines_artifacts';
import PipelinesStatusComponent from '../../vue_pipelines_index/components/status'; import PipelinesStatusComponent from '../../vue_pipelines_index/components/status';
......
...@@ -118,6 +118,10 @@ module BlobHelper ...@@ -118,6 +118,10 @@ module BlobHelper
blob && blob.text? && !blob.lfs_pointer? && !blob.only_display_raw? blob && blob.text? && !blob.lfs_pointer? && !blob.only_display_raw?
end end
def blob_rendered_as_text?(blob)
blob_text_viewable?(blob) && blob.to_partial_path(@project) == 'text'
end
def blob_size(blob) def blob_size(blob)
if blob.lfs_pointer? if blob.lfs_pointer?
blob.lfs_size blob.lfs_size
......
...@@ -42,12 +42,16 @@ class Blob < SimpleDelegator ...@@ -42,12 +42,16 @@ class Blob < SimpleDelegator
size && truncated? size && truncated?
end end
def extension
extname.downcase.delete('.')
end
def svg? def svg?
text? && language && language.name == 'SVG' text? && language && language.name == 'SVG'
end end
def pdf? def pdf?
name && File.extname(name) == '.pdf' extension == 'pdf'
end end
def ipython_notebook? def ipython_notebook?
...@@ -55,11 +59,15 @@ class Blob < SimpleDelegator ...@@ -55,11 +59,15 @@ class Blob < SimpleDelegator
end end
def sketch? def sketch?
binary? && extname.downcase.delete('.') == 'sketch' binary? && extension == 'sketch'
end end
def stl? def stl?
extname.downcase.delete('.') == 'stl' extension == 'stl'
end
def markup?
text? && Gitlab::MarkupHelper.markup?(name)
end end
def size_within_svg_limits? def size_within_svg_limits?
...@@ -77,8 +85,10 @@ class Blob < SimpleDelegator ...@@ -77,8 +85,10 @@ class Blob < SimpleDelegator
else else
'text' 'text'
end end
elsif image? || svg? elsif image?
'image' 'image'
elsif svg?
'svg'
elsif pdf? elsif pdf?
'pdf' 'pdf'
elsif ipython_notebook? elsif ipython_notebook?
...@@ -87,8 +97,18 @@ class Blob < SimpleDelegator ...@@ -87,8 +97,18 @@ class Blob < SimpleDelegator
'sketch' 'sketch'
elsif stl? elsif stl?
'stl' 'stl'
elsif markup?
if only_display_raw?
'too_large'
else
'markup'
end
elsif text? elsif text?
'text' if only_display_raw?
'too_large'
else
'text'
end
else else
'download' 'download'
end end
......
...@@ -414,8 +414,6 @@ class Repository ...@@ -414,8 +414,6 @@ class Repository
# Runs code after a repository has been forked/imported. # Runs code after a repository has been forked/imported.
def after_import def after_import
expire_content_cache expire_content_cache
expire_tags_cache
expire_branches_cache
end end
# Runs code after a new commit has been pushed. # Runs code after a new commit has been pushed.
......
...@@ -662,6 +662,7 @@ ...@@ -662,6 +662,7 @@
The multiplier can also have a decimal value. The multiplier can also have a decimal value.
The default value (1) is a reasonable choice for the majority of GitLab The default value (1) is a reasonable choice for the majority of GitLab
installations. Set to 0 to completely disable polling. installations. Set to 0 to completely disable polling.
= link_to icon('question-circle'), help_page_path('administration/polling')
- if Gitlab::Geo.license_allows? - if Gitlab::Geo.license_allows?
%fieldset %fieldset
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Project #{@project.name} was exported successfully. Project #{@project.name} was exported successfully.
%p %p
The project export can be downloaded from: The project export can be downloaded from:
= link_to download_export_namespace_project_url(@project.namespace, @project), rel: 'nofollow', download: '', do = link_to download_export_namespace_project_url(@project.namespace, @project), rel: 'nofollow', download: '' do
= @project.name_with_namespace + " export" = @project.name_with_namespace + " export"
%p %p
The download link will expire in 24 hours. The download link will expire in 24 hours.
...@@ -33,4 +33,10 @@ ...@@ -33,4 +33,10 @@
= link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new' = link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' } %button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' }
Cancel Cancel
= render blob.to_partial_path(@project), blob: blob
- if blob.empty?
.file-content.code
.nothing-here-block
Empty file
- else
= render blob.to_partial_path(@project), blob: blob
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
.file-actions.hidden-xs .file-actions.hidden-xs
.btn-group{ role: "group" }< .btn-group{ role: "group" }<
= copy_blob_content_button(blob) if !blame && blob_text_viewable?(blob) = copy_blob_content_button(blob) if !blame && blob_rendered_as_text?(blob)
= open_raw_file_button(namespace_project_raw_path(@project.namespace, @project, @id)) = open_raw_file_button(namespace_project_raw_path(@project.namespace, @project, @id))
= view_on_environment_button(@commit.sha, @path, @environment) if @environment = view_on_environment_button(@commit.sha, @path, @environment) if @environment
......
.file-content.image_file .file-content.image_file
- if blob.svg? %img{ src: namespace_project_raw_path(@project.namespace, @project, @id), alt: blob.name }
- if blob.size_within_svg_limits?
-# We need to scrub SVG but we cannot do so in the RawController: it would
-# be wrong/strange if RawController modified the data.
- blob.load_all_data!(@repository)
- blob = sanitize_svg(blob)
%img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: "#{blob.name}" }
- else
.nothing-here-block
The SVG could not be displayed as it is too large, you can
#{link_to('view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank', rel: 'noopener noreferrer')}
instead.
- else
%img{ src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path)), alt: "#{blob.name}" }
- blob.load_all_data!(@repository)
.file-content.wiki
= render_markup(blob.name, blob.data)
- if blob.size_within_svg_limits?
-# We need to scrub SVG but we cannot do so in the RawController: it would
-# be wrong/strange if RawController modified the data.
- blob.load_all_data!(@repository)
- blob = sanitize_svg(blob)
.file-content.image_file
%img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: blob.name }
- else
= render 'too_large'
- if blob.only_display_raw? - blob.load_all_data!(@repository)
.file-content.code = render 'shared/file_highlight', blob: blob, repository: @repository
.nothing-here-block
File too large, you can
= succeed '.' do
= link_to 'view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank', rel: 'noopener noreferrer'
- else
- blob.load_all_data!(@repository)
- if blob.empty?
.file-content.code
.nothing-here-block Empty file
- else
- if markup?(blob.name)
.file-content.wiki
= render_markup(blob.name, blob.data)
- else
= render 'shared/file_highlight', blob: blob, repository: @repository
.file-content.code
.nothing-here-block
The file could not be displayed as it is too large, you can
#{link_to('view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank', rel: 'noopener noreferrer')}
instead.
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
= render "home_panel" = render "home_panel"
- if current_user && can?(current_user, :download_code, @project) - if current_user && can?(current_user, :download_code, @project)
%nav.project-stats.limit-container-width{ class: container_class } %nav.project-stats{ class: container_class }
%ul.nav %ul.nav
%li %li
= link_to project_files_path(@project) do = link_to project_files_path(@project) do
...@@ -77,11 +77,11 @@ ...@@ -77,11 +77,11 @@
Set up auto deploy Set up auto deploy
- if @repository.commit - if @repository.commit
.limit-container-width{ class: container_class } %div{ class: container_class }
.project-last-commit .project-last-commit
= render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project = render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project
.limit-container-width{ class: container_class } %div{ class: container_class }
- if @project.archived? - if @project.archived?
.text-warning.center.prepend-top-20 .text-warning.center.prepend-top-20
%p %p
......
---
title: Disable invalid service templates
merge_request:
author:
---
title: Keep webpack-dev-server process functional across branch changes
merge_request: 10581
author:
---
title: add support for .vue templates
merge_request: 10517
author:
---
title: Fix redundant cache expiration in Repository
merge_request: 10575
author: blackst0ne
---
title: Add spec for schema.rb
merge_request: 10580
author: blackst0ne
...@@ -344,3 +344,57 @@ ...@@ -344,3 +344,57 @@
:why: https://github.com/nodeca/pako/blob/master/LICENSE :why: https://github.com/nodeca/pako/blob/master/LICENSE
:versions: [] :versions: []
:when: 2017-04-05 10:43:45.897720000 Z :when: 2017-04-05 10:43:45.897720000 Z
- - :approve
- caniuse-db
- :who: Mike Greiling
:why: https://github.com/Fyrd/caniuse/blob/master/LICENSE
:versions: []
:when: 2017-04-07 16:05:14.185549000 Z
- - :approve
- domelementtype
- :who: Mike Greiling
:why: https://github.com/fb55/domelementtype/blob/master/LICENSE
:versions: []
:when: 2017-04-07 16:19:17.992640000 Z
- - :approve
- domhandler
- :who: Mike Greiling
:why: https://github.com/fb55/domhandler/blob/master/LICENSE
:versions: []
:when: 2017-04-07 16:19:19.628953000 Z
- - :approve
- domutils
- :who: Mike Greiling
:why: https://github.com/fb55/domutils/blob/master/LICENSE
:versions: []
:when: 2017-04-07 16:19:21.159356000 Z
- - :approve
- entities
- :who: Mike Greiling
:why: https://github.com/fb55/entities/blob/master/LICENSE
:versions: []
:when: 2017-04-07 16:19:23.900571000 Z
- - :approve
- ansi-html
- :who: Mike Greiling
:why: https://github.com/Tjatse/ansi-html/blob/master/LICENSE
:versions: []
:when: 2017-04-10 05:42:12.898178000 Z
- - :approve
- map-stream
- :who: Mike Greiling
:why: https://github.com/dominictarr/map-stream/blob/master/LICENCE
:versions: []
:when: 2017-04-10 06:27:52.269085000 Z
- - :approve
- pause-stream
- :who: Mike Greiling
:why: https://github.com/dominictarr/pause-stream/blob/master/LICENSE
:versions: []
:when: 2017-04-10 06:28:39.825894000 Z
- - :approve
- undefsafe
- :who: Mike Greiling
:why: https://github.com/remy/undefsafe/blob/master/LICENSE
:versions: []
:when: 2017-04-10 06:30:00.002555000 Z
...@@ -6,6 +6,7 @@ var webpack = require('webpack'); ...@@ -6,6 +6,7 @@ var webpack = require('webpack');
var StatsPlugin = require('stats-webpack-plugin'); var StatsPlugin = require('stats-webpack-plugin');
var CompressionPlugin = require('compression-webpack-plugin'); var CompressionPlugin = require('compression-webpack-plugin');
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
var ROOT_PATH = path.resolve(__dirname, '..'); var ROOT_PATH = path.resolve(__dirname, '..');
var IS_PRODUCTION = process.env.NODE_ENV === 'production'; var IS_PRODUCTION = process.env.NODE_ENV === 'production';
...@@ -68,13 +69,18 @@ var config = { ...@@ -68,13 +69,18 @@ var config = {
{ {
test: /\.js$/, test: /\.js$/,
exclude: /(node_modules|vendor\/assets)/, exclude: /(node_modules|vendor\/assets)/,
loader: 'babel-loader' loader: 'babel-loader',
},
{
test: /\.vue$/,
loader: 'vue-loader',
}, },
{ {
test: /\.svg$/, test: /\.svg$/,
use: 'raw-loader' loader: 'raw-loader',
}, { },
test: /\.(worker.js|pdf)$/, {
test: /\.(worker\.js|pdf)$/,
exclude: /node_modules/, exclude: /node_modules/,
loader: 'file-loader', loader: 'file-loader',
}, },
...@@ -187,6 +193,10 @@ if (IS_DEV_SERVER) { ...@@ -187,6 +193,10 @@ if (IS_DEV_SERVER) {
inline: DEV_SERVER_LIVERELOAD inline: DEV_SERVER_LIVERELOAD
}; };
config.output.publicPath = '//localhost:' + DEV_SERVER_PORT + config.output.publicPath; config.output.publicPath = '//localhost:' + DEV_SERVER_PORT + config.output.publicPath;
config.plugins.push(
// watch node_modules for changes if we encounter a missing module compile error
new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules'))
);
} }
if (WEBPACK_REPORT) { if (WEBPACK_REPORT) {
......
...@@ -69,6 +69,7 @@ All technical content published by GitLab lives in the documentation, including: ...@@ -69,6 +69,7 @@ All technical content published by GitLab lives in the documentation, including:
- [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE. - [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
- [Monitoring uptime](user/admin_area/monitoring/health_check.md) Check the server status using the health check endpoint. - [Monitoring uptime](user/admin_area/monitoring/health_check.md) Check the server status using the health check endpoint.
- [Operations](administration/operations.md) Keeping GitLab up and running. - [Operations](administration/operations.md) Keeping GitLab up and running.
- [Polling](administration/polling.md) Configure how often the GitLab UI polls for updates
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects. - [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects.
- [Reply by email](administration/reply_by_email.md) Allow users to comment on issues and merge requests by replying to notification emails. - [Reply by email](administration/reply_by_email.md) Allow users to comment on issues and merge requests by replying to notification emails.
- [Repository checks](administration/repository_checks.md) Periodic Git repository checks. - [Repository checks](administration/repository_checks.md) Periodic Git repository checks.
......
# Polling configuration
The GitLab UI polls for updates for different resources (issue notes, issue
titles, pipeline statuses, etc.) on a schedule appropriate to the resource.
In "Application settings -> Real-time features" you can configure "Polling
interval multiplier". This multiplier is applied to all resources at once,
and decimal values are supported. For the sake of the examples below, we will
say that issue notes poll every 2 seconds, and issue titles poll every 5
seconds; these are _not_ the actual values.
- 1 is the default, and recommended for most installations. (Issue notes poll
every 2 seconds, and issue titles poll every 5 seconds.)
- 0 will disable UI polling completely. (On the next poll, clients will stop
polling for updates.)
- A value greater than 1 will slow polling down. If you see issues with
database load from lots of clients polling for updates, increasing the
multiplier from 1 can be a good compromise, rather than disabling polling
completely. (For example: If this is set to 2, then issue notes poll every 4
seconds, and issue titles poll every 10 seconds.)
- A value between 0 and 1 will make the UI poll more frequently (so updates
will show in other sessions faster), but is **not recommended**. 1 should be
fast enough. (For example, if this is set to 0.5, then issue notes poll every
1 second, and issue titles poll every 2.5 seconds.)
...@@ -12,8 +12,8 @@ Thus, we must strike a balance between sending requests and the feeling of realt ...@@ -12,8 +12,8 @@ Thus, we must strike a balance between sending requests and the feeling of realt
Use the following rules when creating realtime solutions. Use the following rules when creating realtime solutions.
1. The server will tell you how much to poll by sending `Poll-Interval` in the header. 1. The server will tell you how much to poll by sending `Poll-Interval` in the header.
Use that as your polling interval. This way it is easy for system administrators to change the Use that as your polling interval. This way it is [easy for system administrators to change the
polling rate. polling rate](../../administration/polling.md).
A `Poll-Interval: -1` means you should disable polling, and this must be implemented. A `Poll-Interval: -1` means you should disable polling, and this must be implemented.
1. A response with HTTP status `4XX` or `5XX` should disable polling as well. 1. A response with HTTP status `4XX` or `5XX` should disable polling as well.
1. Use a common library for polling. 1. Use a common library for polling.
......
...@@ -51,5 +51,6 @@ request path. By doing this we avoid query parameter ordering problems and make ...@@ -51,5 +51,6 @@ request path. By doing this we avoid query parameter ordering problems and make
route matching easier. route matching easier.
For more information see: For more information see:
- [`Poll-Interval` header](fe_guide/performance.md#realtime-components)
- [RFC 7232](https://tools.ietf.org/html/rfc7232) - [RFC 7232](https://tools.ietf.org/html/rfc7232)
- [ETag proposal](https://gitlab.com/gitlab-org/gitlab-ce/issues/26926) - [ETag proposal](https://gitlab.com/gitlab-org/gitlab-ce/issues/26926)
...@@ -20,8 +20,8 @@ the hardware requirements. ...@@ -20,8 +20,8 @@ the hardware requirements.
- [Docker](https://docs.gitlab.com/omnibus/docker/) - Install GitLab using Docker. - [Docker](https://docs.gitlab.com/omnibus/docker/) - Install GitLab using Docker.
- [Installation on Google Cloud Platform](google_cloud_platform/index.md) - Install - [Installation on Google Cloud Platform](google_cloud_platform/index.md) - Install
GitLab on Google Cloud Platform using our official image. GitLab on Google Cloud Platform using our official image.
- [Digital Ocean and Docker](digitaloceandocker.md) - Install GitLab quickly - Testing only! [DigitalOcean and Docker Machine](digitaloceandocker.md) -
on DigitalOcean using Docker. Quickly test any version of GitLab on DigitalOcean using Docker Machine.
## Database ## Database
......
# Digital Ocean and Docker # Digital Ocean and Docker Machine test environment
## Warning. This guide is for quickly testing different versions of GitLab and
## not recommended for ease of future upgrades or keeping the data you create.
## Initial setup ## Initial setup
......
...@@ -143,7 +143,7 @@ into the password field. ...@@ -143,7 +143,7 @@ into the password field.
To disable two-factor authentication on your account (for example, if you To disable two-factor authentication on your account (for example, if you
have lost your code generation device) you can: have lost your code generation device) you can:
* [Use a saved recovery code](#use-a-saved-recovery-code) * [Use a saved recovery code](#use-a-saved-recovery-code)
* [Generate new recovery codes using SSH](#generate-new-recovery-codes-using-SSH) * [Generate new recovery codes using SSH](#generate-new-recovery-codes-using-ssh)
* [Ask a GitLab administrator to disable two-factor authentication on your account](#ask-a-gitlab-administrator-to-disable-two-factor-authentication-on-your-account) * [Ask a GitLab administrator to disable two-factor authentication on your account](#ask-a-gitlab-administrator-to-disable-two-factor-authentication-on-your-account)
### Use a saved recovery code ### Use a saved recovery code
......
...@@ -323,6 +323,5 @@ Merging only when needed prevents creating merge commits in your feature branch ...@@ -323,6 +323,5 @@ Merging only when needed prevents creating merge commits in your feature branch
## References ## References
- [Sketch file](https://www.dropbox.com/s/58dvsj5votbwrzv/git_flows.sketch?dl=0) with vectors of images in this article
- [Git Flow by Vincent Driessen](http://nvie.com/posts/a-successful-git-branching-model/) - [Git Flow by Vincent Driessen](http://nvie.com/posts/a-successful-git-branching-model/)
- [Blog post with responses](https://about.gitlab.com/2014/09/29/gitlab-flow/) - [Blog post with responses](https://about.gitlab.com/2014/09/29/gitlab-flow/)
\ No newline at end of file
...@@ -111,7 +111,7 @@ There are four kinds of filters you can use on your Todos dashboard. ...@@ -111,7 +111,7 @@ There are four kinds of filters you can use on your Todos dashboard.
| Type | Filter by issue or merge request | | Type | Filter by issue or merge request |
| Action | Filter by the action that triggered the Todo | | Action | Filter by the action that triggered the Todo |
You can also filter by more than one of these at the same time. You can also filter by more than one of these at the same time. The possible Actions are `Any Action`, `Assigned`, `Mentioned`, `Added`, `Pipelines`, and `Directly Addressed`, [as described above](#what-triggers-a-todo).
[ce-2817]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2817 [ce-2817]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2817
[ce-7926]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7926 [ce-7926]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7926
...@@ -53,6 +53,7 @@ module API ...@@ -53,6 +53,7 @@ module API
] ]
end end
<<<<<<< HEAD
def log_user_activity(actor) def log_user_activity(actor)
commands = Gitlab::GitAccess::DOWNLOAD_COMMANDS commands = Gitlab::GitAccess::DOWNLOAD_COMMANDS
...@@ -62,6 +63,11 @@ module API ...@@ -62,6 +63,11 @@ module API
def parse_env def parse_env
return {} if params[:env].blank? return {} if params[:env].blank?
=======
def parse_env
return {} if params[:env].blank?
>>>>>>> 19dd138cdd4e551759a2163f5357ae2fc9f73b99
JSON.parse(params[:env]) JSON.parse(params[:env])
rescue JSON::ParserError rescue JSON::ParserError
{} {}
......
...@@ -3,23 +3,42 @@ module Banzai ...@@ -3,23 +3,42 @@ module Banzai
class MergeRequestParser < BaseParser class MergeRequestParser < BaseParser
self.reference_type = :merge_request self.reference_type = :merge_request
def nodes_visible_to_user(user, nodes)
merge_requests = merge_requests_for_nodes(nodes)
nodes.select do |node|
merge_request = merge_requests[node]
merge_request && can?(user, :read_merge_request, merge_request.project)
end
end
def referenced_by(nodes)
merge_requests = merge_requests_for_nodes(nodes)
nodes.map { |node| merge_requests[node] }.compact.uniq
end
def merge_requests_for_nodes(nodes) def merge_requests_for_nodes(nodes)
@merge_requests_for_nodes ||= grouped_objects_for_nodes( @merge_requests_for_nodes ||= grouped_objects_for_nodes(
nodes, nodes,
MergeRequest.all, MergeRequest.includes(
:author,
:assignee,
{
# These associations are primarily used for checking permissions.
# Eager loading these ensures we don't end up running dozens of
# queries in this process.
target_project: [
{ namespace: :owner },
{ group: [:owners, :group_members] },
:invited_groups,
:project_members
]
}),
self.class.data_attribute self.class.data_attribute
) )
end end
def references_relation
MergeRequest.includes(:author, :assignee, :target_project)
end
private
def can_read_reference?(user, ref_project)
can?(user, :read_merge_request, ref_project)
end
end end
end end
end end
module Gitlab module Gitlab
module EtagCaching module EtagCaching
class Middleware class Middleware
RESERVED_WORDS = NamespaceValidator::WILDCARD_ROUTES.map { |word| "/#{word}/" }.join('|')
ROUTES = [
{
regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/noteable/issue/\d+/notes\z),
name: 'issue_notes'
},
{
regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/issues/\d+/rendered_title\z),
name: 'issue_title'
},
{
regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/pipelines\.json\z),
name: 'project_pipelines'
},
{
regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/commit/\s+/pipelines\.json\z),
name: 'commit_pipelines'
},
{
regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/merge_requests/new\.json\z),
name: 'new_merge_request_pipelines'
},
{
regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/merge_requests/\d+/pipelines\.json\z),
name: 'merge_request_pipelines'
}
].freeze
def initialize(app) def initialize(app)
@app = app @app = app
end end
def call(env) def call(env)
route = match_current_route(env) route = Gitlab::EtagCaching::Router.match(env)
return @app.call(env) unless route return @app.call(env) unless route
track_event(:etag_caching_middleware_used, route) track_event(:etag_caching_middleware_used, route)
...@@ -55,10 +27,6 @@ module Gitlab ...@@ -55,10 +27,6 @@ module Gitlab
private private
def match_current_route(env)
ROUTES.find { |route| route[:regexp].match(env['PATH_INFO']) }
end
def get_etag(env) def get_etag(env)
cache_key = env['PATH_INFO'] cache_key = env['PATH_INFO']
store = Gitlab::EtagCaching::Store.new store = Gitlab::EtagCaching::Store.new
...@@ -95,7 +63,7 @@ module Gitlab ...@@ -95,7 +63,7 @@ module Gitlab
end end
def track_event(name, route) def track_event(name, route)
Gitlab::Metrics.add_event(name, endpoint: route[:name]) Gitlab::Metrics.add_event(name, endpoint: route.name)
end end
end end
end end
......
module Gitlab
module EtagCaching
class Router
Route = Struct.new(:regexp, :name)
RESERVED_WORDS = NamespaceValidator::WILDCARD_ROUTES.map { |word| "/#{word}/" }.join('|')
ROUTES = [
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS})).*/noteable/issue/\d+/notes\z),
'issue_notes'
),
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS})).*/issues/\d+/rendered_title\z),
'issue_title'
),
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS})).*/commit/\S+/pipelines\.json\z),
'commit_pipelines'
),
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS})).*/merge_requests/new\.json\z),
'new_merge_request_pipelines'
),
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS})).*/merge_requests/\d+/pipelines\.json\z),
'merge_request_pipelines'
),
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS})).*/pipelines\.json\z),
'project_pipelines'
)
].freeze
def self.match(env)
ROUTES.find { |route| route.regexp.match(env['PATH_INFO']) }
end
end
end
end
{ {
"private": true, "private": true,
"scripts": { "scripts": {
"dev-server": "webpack-dev-server --config config/webpack.config.js", "dev-server": "nodemon --watch config/webpack.config.js -- ./node_modules/.bin/webpack-dev-server --config config/webpack.config.js",
"eslint": "eslint --max-warnings 0 --ext .js .", "eslint": "eslint --max-warnings 0 --ext .js,.vue .",
"eslint-fix": "eslint --max-warnings 0 --ext .js --fix .", "eslint-fix": "eslint --max-warnings 0 --ext .js,.vue --fix .",
"eslint-report": "eslint --max-warnings 0 --ext .js --format html --output-file ./eslint-report.html .", "eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html .",
"karma": "karma start config/karma.config.js --single-run", "karma": "karma start config/karma.config.js --single-run",
"karma-coverage": "BABEL_ENV=coverage karma start config/karma.config.js --single-run", "karma-coverage": "BABEL_ENV=coverage karma start config/karma.config.js --single-run",
"karma-start": "karma start config/karma.config.js", "karma-start": "karma start config/karma.config.js",
...@@ -21,10 +21,12 @@ ...@@ -21,10 +21,12 @@
"clipboard": "^1.6.1", "clipboard": "^1.6.1",
"compression-webpack-plugin": "^0.3.2", "compression-webpack-plugin": "^0.3.2",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"css-loader": "^0.28.0",
"d3": "^3.5.11", "d3": "^3.5.11",
"document-register-element": "^1.3.0", "document-register-element": "^1.3.0",
"dropzone": "^4.2.0", "dropzone": "^4.2.0",
"emoji-unicode-version": "^0.2.1", "emoji-unicode-version": "^0.2.1",
"eslint-plugin-html": "^2.0.1",
"file-loader": "^0.11.1", "file-loader": "^0.11.1",
"jquery": "^2.2.1", "jquery": "^2.2.1",
"jquery-ujs": "^1.2.1", "jquery-ujs": "^1.2.1",
...@@ -35,6 +37,7 @@ ...@@ -35,6 +37,7 @@
"pikaday": "^1.5.1", "pikaday": "^1.5.1",
"raphael": "^2.2.7", "raphael": "^2.2.7",
"raw-loader": "^0.5.1", "raw-loader": "^0.5.1",
"react-dev-utils": "^0.5.2",
"select2": "3.5.2-browserify", "select2": "3.5.2-browserify",
"stats-webpack-plugin": "^0.4.3", "stats-webpack-plugin": "^0.4.3",
"three": "^0.84.0", "three": "^0.84.0",
...@@ -43,8 +46,10 @@ ...@@ -43,8 +46,10 @@
"timeago.js": "^2.0.5", "timeago.js": "^2.0.5",
"underscore": "^1.8.3", "underscore": "^1.8.3",
"visibilityjs": "^1.2.4", "visibilityjs": "^1.2.4",
"vue": "^2.2.4", "vue": "^2.2.6",
"vue-loader": "^11.3.4",
"vue-resource": "^0.9.3", "vue-resource": "^0.9.3",
"vue-template-compiler": "^2.2.6",
"webpack": "^2.3.3", "webpack": "^2.3.3",
"webpack-bundle-analyzer": "^2.3.0" "webpack-bundle-analyzer": "^2.3.0"
}, },
...@@ -66,6 +71,7 @@ ...@@ -66,6 +71,7 @@
"karma-phantomjs-launcher": "^1.0.2", "karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7", "karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.2", "karma-webpack": "^2.0.2",
"nodemon": "^1.11.0",
"webpack-dev-server": "^2.4.2" "webpack-dev-server": "^2.4.2"
} }
} }
\ No newline at end of file
...@@ -98,9 +98,9 @@ describe('Build', () => { ...@@ -98,9 +98,9 @@ describe('Build', () => {
jasmine.clock().tick(4001); jasmine.clock().tick(4001);
expect($.ajax.calls.count()).toBe(2); expect($.ajax.calls.count()).toBe(3);
args = $.ajax.calls.argsFor(1)[0]; args = $.ajax.calls.argsFor(2)[0];
expect(args.url).toBe(`${BUILD_URL}/trace.json`); expect(args.url).toBe(`${BUILD_URL}/trace.json`);
expect(args.dataType).toBe('json'); expect(args.dataType).toBe('json');
expect(args.data.state).toBe('newstate'); expect(args.data.state).toBe('newstate');
...@@ -133,7 +133,7 @@ describe('Build', () => { ...@@ -133,7 +133,7 @@ describe('Build', () => {
expect($('#build-trace .js-build-output').text()).toMatch(/Update/); expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
jasmine.clock().tick(4001); jasmine.clock().tick(4001);
args = $.ajax.calls.argsFor(1)[0]; args = $.ajax.calls.argsFor(2)[0];
args.success.call($, { args.success.call($, {
html: '<span>Different</span>', html: '<span>Different</span>',
status: 'running', status: 'running',
......
import Vue from 'vue'; import Vue from 'vue';
import asyncButtonComp from '~/vue_pipelines_index/components/async_button'; import asyncButtonComp from '~/vue_pipelines_index/components/async_button.vue';
describe('Pipelines Async Button', () => { describe('Pipelines Async Button', () => {
let component; let component;
......
import Vue from 'vue'; import Vue from 'vue';
import emptyStateComp from '~/vue_pipelines_index/components/empty_state'; import emptyStateComp from '~/vue_pipelines_index/components/empty_state.vue';
describe('Pipelines Empty State', () => { describe('Pipelines Empty State', () => {
let component; let component;
......
import Vue from 'vue'; import Vue from 'vue';
import errorStateComp from '~/vue_pipelines_index/components/error_state'; import errorStateComp from '~/vue_pipelines_index/components/error_state.vue';
describe('Pipelines Error State', () => { describe('Pipelines Error State', () => {
let component; let component;
......
require 'spec_helper'
describe Gitlab::EtagCaching::Router do
it 'matches issue notes endpoint' do
env = build_env(
'/my-group/and-subgroup/here-comes-the-project/noteable/issue/1/notes'
)
result = described_class.match(env)
expect(result).to be_present
expect(result.name).to eq 'issue_notes'
end
it 'matches issue title endpoint' do
env = build_env(
'/my-group/my-project/issues/123/rendered_title'
)
result = described_class.match(env)
expect(result).to be_present
expect(result.name).to eq 'issue_title'
end
it 'matches project pipelines endpoint' do
env = build_env(
'/my-group/my-project/pipelines.json'
)
result = described_class.match(env)
expect(result).to be_present
expect(result.name).to eq 'project_pipelines'
end
it 'matches commit pipelines endpoint' do
env = build_env(
'/my-group/my-project/commit/aa8260d253a53f73f6c26c734c72fdd600f6e6d4/pipelines.json'
)
result = described_class.match(env)
expect(result).to be_present
expect(result.name).to eq 'commit_pipelines'
end
it 'matches new merge request pipelines endpoint' do
env = build_env(
'/my-group/my-project/merge_requests/new.json'
)
result = described_class.match(env)
expect(result).to be_present
expect(result.name).to eq 'new_merge_request_pipelines'
end
it 'matches merge request pipelines endpoint' do
env = build_env(
'/my-group/my-project/merge_requests/234/pipelines.json'
)
result = described_class.match(env)
expect(result).to be_present
expect(result.name).to eq 'merge_request_pipelines'
end
it 'does not match blob with confusing name' do
env = build_env(
'/my-group/my-project/blob/master/pipelines.json'
)
result = described_class.match(env)
expect(result).to be_blank
end
def build_env(path)
{ 'PATH_INFO' => path }
end
end
require 'spec_helper'
# Check consistency of db/schema.rb version, migrations' timestamps, and the latest migration timestamp
# stored in the database's schema_migrations table.
describe ActiveRecord::Schema do
let(:latest_migration_timestamp) do
migrations = Dir[Rails.root.join('db', 'migrate', '*'), Rails.root.join('db', 'post_migrate', '*')]
migrations.map { |migration| File.basename(migration).split('_').first.to_i }.max
end
it '> schema version equals last migration timestamp' do
defined_schema_version = File.open(Rails.root.join('db', 'schema.rb')) do |file|
file.find { |line| line =~ /ActiveRecord::Schema.define/ }
end.match(/(\d+)/)[0].to_i
expect(defined_schema_version).to eq(latest_migration_timestamp)
end
it '> schema version should equal the latest migration timestamp stored in schema_migrations table' do
expect(latest_migration_timestamp).to eq(ActiveRecord::Migrator.current_version.to_i)
end
end
...@@ -55,13 +55,13 @@ describe Blob do ...@@ -55,13 +55,13 @@ describe Blob do
describe '#pdf?' do describe '#pdf?' do
it 'is falsey when file extension is not .pdf' do it 'is falsey when file extension is not .pdf' do
git_blob = double(name: 'git_blob.txt') git_blob = Gitlab::Git::Blob.new(name: 'git_blob.txt')
expect(described_class.decorate(git_blob)).not_to be_pdf expect(described_class.decorate(git_blob)).not_to be_pdf
end end
it 'is truthy when file extension is .pdf' do it 'is truthy when file extension is .pdf' do
git_blob = double(name: 'git_blob.pdf') git_blob = Gitlab::Git::Blob.new(name: 'git_blob.pdf')
expect(described_class.decorate(git_blob)).to be_pdf expect(described_class.decorate(git_blob)).to be_pdf
end end
...@@ -140,7 +140,7 @@ describe Blob do ...@@ -140,7 +140,7 @@ describe Blob do
stl?: false stl?: false
) )
described_class.decorate(double).tap do |blob| described_class.decorate(Gitlab::Git::Blob.new({})).tap do |blob|
allow(blob).to receive_messages(overrides) allow(blob).to receive_messages(overrides)
end end
end end
...@@ -158,7 +158,7 @@ describe Blob do ...@@ -158,7 +158,7 @@ describe Blob do
it 'handles SVGs' do it 'handles SVGs' do
blob = stubbed_blob(text?: true, svg?: true) blob = stubbed_blob(text?: true, svg?: true)
expect(blob.to_partial_path(project)).to eq 'image' expect(blob.to_partial_path(project)).to eq 'svg'
end end
it 'handles images' do it 'handles images' do
...@@ -167,7 +167,7 @@ describe Blob do ...@@ -167,7 +167,7 @@ describe Blob do
end end
it 'handles text' do it 'handles text' do
blob = stubbed_blob(text?: true) blob = stubbed_blob(text?: true, name: 'test.txt')
expect(blob.to_partial_path(project)).to eq 'text' expect(blob.to_partial_path(project)).to eq 'text'
end end
......
...@@ -1309,8 +1309,6 @@ describe Repository, models: true do ...@@ -1309,8 +1309,6 @@ describe Repository, models: true do
describe '#after_import' do describe '#after_import' do
it 'flushes and builds the cache' do it 'flushes and builds the cache' do
expect(repository).to receive(:expire_content_cache) expect(repository).to receive(:expire_content_cache)
expect(repository).to receive(:expire_tags_cache)
expect(repository).to receive(:expire_branches_cache)
repository.after_import repository.after_import
end end
......
...@@ -222,6 +222,22 @@ describe API::Internal, api: true do ...@@ -222,6 +222,22 @@ describe API::Internal, api: true do
end end
end end
context 'with env passed as a JSON' do
it 'sets env in RequestStore' do
expect(Gitlab::Git::Env).to receive(:set).with({
'GIT_OBJECT_DIRECTORY' => 'foo',
'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
})
push(key, project.wiki, env: {
GIT_OBJECT_DIRECTORY: 'foo',
GIT_ALTERNATE_OBJECT_DIRECTORIES: 'bar'
}.to_json)
expect(response).to have_http_status(200)
end
end
context "git push with project.wiki" do context "git push with project.wiki" do
it 'responds with success' do it 'responds with success' do
push(key, project.wiki) push(key, project.wiki)
......
...@@ -1216,6 +1216,22 @@ describe NotificationService, services: true do ...@@ -1216,6 +1216,22 @@ describe NotificationService, services: true do
should_not_email(@u_disabled) should_not_email(@u_disabled)
end end
end end
describe '#project_exported' do
it do
notification.project_exported(project, @u_disabled)
should_only_email(@u_disabled)
end
end
describe '#project_not_exported' do
it do
notification.project_not_exported(project, @u_disabled, ['error'])
should_only_email(@u_disabled)
end
end
end end
describe 'GroupMember' do describe 'GroupMember' do
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment