Commit e52c401e authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-10-06

# Conflicts:
#	app/assets/javascripts/api.js
#	app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
#	app/finders/license_template_finder.rb
#	app/finders/template_finder.rb
#	app/models/commit_status.rb
#	app/models/issue.rb
#	app/models/note.rb
#	app/models/system_note_metadata.rb
#	app/services/ci/process_build_service.rb
#	db/schema.rb
#	doc/api/README.md
#	spec/features/projects/jobs_spec.rb
#	spec/models/issue_spec.rb

[ci skip]
parents 6dba97cf 1239629c
...@@ -16,3 +16,5 @@ db/ @abrandl @NikolayS ...@@ -16,3 +16,5 @@ db/ @abrandl @NikolayS
/ee/lib/gitlab/code_owners/ @reprazent /ee/lib/gitlab/code_owners/ @reprazent
/ee/lib/ee/gitlab/auth/ldap/ @dblessing @mkozono /ee/lib/ee/gitlab/auth/ldap/ @dblessing @mkozono
/lib/gitlab/auth/ldap/ @dblessing @mkozono /lib/gitlab/auth/ldap/ @dblessing @mkozono
/lib/gitlab/ci/templates/ @nolith @zj
/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah
...@@ -576,6 +576,15 @@ entry. ...@@ -576,6 +576,15 @@ entry.
- Moves help_popover component to a common location. - Moves help_popover component to a common location.
## 11.1.8 (2018-10-05)
### Security (3 changes)
- Filter user sensitive data from discussions JSON. !2539
- Properly filter private references from system notes.
- Markdown API no longer displays confidential title references unless authorized.
## 11.1.7 (2018-09-26) ## 11.1.7 (2018-09-26)
### Security (6 changes) ### Security (6 changes)
......
...@@ -15,7 +15,10 @@ const Api = { ...@@ -15,7 +15,10 @@ const Api = {
mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes', mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions', mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
groupLabelsPath: '/groups/:namespace_path/-/labels', groupLabelsPath: '/groups/:namespace_path/-/labels',
<<<<<<< HEAD
ldapGroupsPath: '/api/:version/ldap/:provider/groups.json', ldapGroupsPath: '/api/:version/ldap/:provider/groups.json',
=======
>>>>>>> upstream/master
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key', issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key', projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key',
projectTemplatesPath: '/api/:version/projects/:id/templates/:type', projectTemplatesPath: '/api/:version/projects/:id/templates/:type',
...@@ -275,6 +278,7 @@ const Api = { ...@@ -275,6 +278,7 @@ const Api = {
}); });
}, },
<<<<<<< HEAD
approverUsers(search, options, callback = $.noop) { approverUsers(search, options, callback = $.noop) {
const url = Api.buildUrl('/autocomplete/users.json'); const url = Api.buildUrl('/autocomplete/users.json');
return axios return axios
...@@ -311,6 +315,8 @@ const Api = { ...@@ -311,6 +315,8 @@ const Api = {
}); });
}, },
=======
>>>>>>> upstream/master
buildUrl(url) { buildUrl(url) {
let urlRoot = ''; let urlRoot = '';
if (gon.relative_url_root != null) { if (gon.relative_url_root != null) {
......
import Visibility from 'visibilityjs'; import Visibility from 'visibilityjs';
import Vue from 'vue'; import Vue from 'vue';
import initDismissableCallout from '~/dismissable_callout'; import PersistentUserCallout from '../persistent_user_callout';
import { s__, sprintf } from '../locale'; import { s__, sprintf } from '../locale';
import Flash from '../flash'; import Flash from '../flash';
import Poll from '../lib/utils/poll'; import Poll from '../lib/utils/poll';
...@@ -62,7 +62,7 @@ export default class Clusters { ...@@ -62,7 +62,7 @@ export default class Clusters {
this.showTokenButton = document.querySelector('.js-show-cluster-token'); this.showTokenButton = document.querySelector('.js-show-cluster-token');
this.tokenField = document.querySelector('.js-cluster-token'); this.tokenField = document.querySelector('.js-cluster-token');
initDismissableCallout('.js-cluster-security-warning'); Clusters.initDismissableCallout();
initSettingsPanels(); initSettingsPanels();
setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area')); setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area'));
this.initApplications(); this.initApplications();
...@@ -105,6 +105,12 @@ export default class Clusters { ...@@ -105,6 +105,12 @@ export default class Clusters {
}); });
} }
static initDismissableCallout() {
const callout = document.querySelector('.js-cluster-security-warning');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
}
addListeners() { addListeners() {
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken); if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication); eventHub.$on('installApplication', this.installApplication);
......
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import setupToggleButtons from '~/toggle_buttons'; import setupToggleButtons from '~/toggle_buttons';
import initDismissableCallout from '~/dismissable_callout'; import PersistentUserCallout from '../persistent_user_callout';
import ClustersService from './services/clusters_service'; import ClustersService from './services/clusters_service';
export default () => { export default () => {
const clusterList = document.querySelector('.js-clusters-list'); const clusterList = document.querySelector('.js-clusters-list');
initDismissableCallout('.gcp-signup-offer'); const callout = document.querySelector('.gcp-signup-offer');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
// The empty state won't have a clusterList // The empty state won't have a clusterList
if (clusterList) { if (clusterList) {
......
...@@ -28,7 +28,7 @@ export default { ...@@ -28,7 +28,7 @@ export default {
return diffModes[diffModeKey] || diffModes.replaced; return diffModes[diffModeKey] || diffModes.replaced;
}, },
isTextFile() { isTextFile() {
return this.diffFile.text; return this.diffFile.viewer.name === 'text';
}, },
}, },
}; };
......
import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import Flash from '~/flash';
export default function initDismissableCallout(alertSelector) {
const alertEl = document.querySelector(alertSelector);
if (!alertEl) {
return;
}
const closeButtonEl = alertEl.getElementsByClassName('close')[0];
const { dismissEndpoint, featureId } = closeButtonEl.dataset;
closeButtonEl.addEventListener('click', () => {
axios
.post(dismissEndpoint, {
feature_name: featureId,
})
.then(() => {
$(alertEl).alert('close');
})
.catch(() => {
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
});
});
}
import initDismissableCallout from '~/dismissable_callout';
import initGkeDropdowns from '~/projects/gke_cluster_dropdowns'; import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
import PersistentUserCallout from '../../persistent_user_callout';
import Project from './project'; import Project from './project';
import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation'; import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
...@@ -12,7 +12,9 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -12,7 +12,9 @@ document.addEventListener('DOMContentLoaded', () => {
]; ];
if (newClusterViews.indexOf(page) > -1) { if (newClusterViews.indexOf(page) > -1) {
initDismissableCallout('.gcp-signup-offer'); const callout = document.querySelector('.gcp-signup-offer');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
initGkeDropdowns(); initGkeDropdowns();
} }
......
...@@ -57,7 +57,21 @@ ...@@ -57,7 +57,21 @@
required: false, required: false,
default: '', default: '',
}, },
<<<<<<< HEAD
packagesHelpPath: { packagesHelpPath: {
=======
pagesAvailable: {
type: Boolean,
required: false,
default: false,
},
pagesAccessControlEnabled: {
type: Boolean,
required: false,
default: false,
},
pagesHelpPath: {
>>>>>>> upstream/master
type: String, type: String,
required: false, required: false,
default: '', default: '',
...@@ -74,6 +88,7 @@ ...@@ -74,6 +88,7 @@
buildsAccessLevel: 20, buildsAccessLevel: 20,
wikiAccessLevel: 20, wikiAccessLevel: 20,
snippetsAccessLevel: 20, snippetsAccessLevel: 20,
pagesAccessLevel: 20,
containerRegistryEnabled: true, containerRegistryEnabled: true,
lfsEnabled: true, lfsEnabled: true,
packagesEnabled: true, packagesEnabled: true,
...@@ -101,6 +116,13 @@ ...@@ -101,6 +116,13 @@
); );
}, },
pagesFeatureAccessLevelOptions() {
if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
return this.featureAccessLevelOptions.concat([[30, 'Everyone']]);
}
return this.featureAccessLevelOptions;
},
repositoryEnabled() { repositoryEnabled() {
return this.repositoryAccessLevel > 0; return this.repositoryAccessLevel > 0;
}, },
...@@ -120,6 +142,10 @@ ...@@ -120,6 +142,10 @@
this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel); this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel); this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel); this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
if (this.pagesAccessLevel === 20) {
// When from Internal->Private narrow access for only members
this.pagesAccessLevel = 10;
}
this.highlightChanges(); this.highlightChanges();
} else if (oldValue === visibilityOptions.PRIVATE) { } else if (oldValue === visibilityOptions.PRIVATE) {
// if changing away from private, make enabled features more permissive // if changing away from private, make enabled features more permissive
...@@ -129,6 +155,7 @@ ...@@ -129,6 +155,7 @@
if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20; if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20; if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20; if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20;
this.highlightChanges(); this.highlightChanges();
} }
}, },
...@@ -348,6 +375,18 @@ ...@@ -348,6 +375,18 @@
name="project[project_feature_attributes][snippets_access_level]" name="project[project_feature_attributes][snippets_access_level]"
/> />
</project-setting-row> </project-setting-row>
<project-setting-row
v-if="pagesAvailable && pagesAccessControlEnabled"
:help-path="pagesHelpPath"
label="Pages"
help-text="Static website for the project."
>
<project-feature-setting
v-model="pagesAccessLevel"
:options="pagesFeatureAccessLevelOptions"
name="project[project_feature_attributes][pages_access_level]"
/>
</project-setting-row>
</div> </div>
</div> </div>
</template> </template>
// if the "projects dashboard" is a user's default dashboard, when they visit the
// instance root index, the dashboard will be served by the root controller instead
// of a dashboard controller. The root index redirects for all other default dashboards.
import '../dashboard/projects/index';
import axios from './lib/utils/axios_utils';
import { __ } from './locale';
import Flash from './flash';
export default class PersistentUserCallout {
constructor(container) {
const { dismissEndpoint, featureId } = container.dataset;
this.container = container;
this.dismissEndpoint = dismissEndpoint;
this.featureId = featureId;
this.init();
}
init() {
const closeButton = this.container.querySelector('.js-close');
closeButton.addEventListener('click', event => this.dismiss(event));
}
dismiss(event) {
event.preventDefault();
axios
.post(this.dismissEndpoint, {
feature_name: this.featureId,
})
.then(() => {
this.container.remove();
})
.catch(() => {
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
});
}
}
...@@ -1000,6 +1000,7 @@ ...@@ -1000,6 +1000,7 @@
} }
.tree-list-holder { .tree-list-holder {
position: -webkit-sticky;
position: sticky; position: sticky;
top: 100px; top: 100px;
max-height: calc(100vh - 100px); max-height: calc(100vh - 100px);
......
...@@ -211,7 +211,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -211,7 +211,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
environments = environments =
begin begin
@merge_request.environments_for(current_user).map do |environment| @merge_request.environments_for(current_user).map do |environment|
project = environment.project project = environment.project
deployment = environment.first_deployment_for(@merge_request.diff_head_sha) deployment = environment.first_deployment_for(@merge_request.diff_head_sha)
stop_url = stop_url =
...@@ -221,7 +221,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -221,7 +221,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
metrics_url = metrics_url =
if can?(current_user, :read_environment, environment) && environment.has_metrics? if can?(current_user, :read_environment, environment) && environment.has_metrics?
metrics_project_environment_deployment_path(environment.project, environment, deployment) metrics_project_environment_deployment_path(project, environment, deployment)
end end
metrics_monitoring_url = metrics_monitoring_url =
......
...@@ -16,10 +16,10 @@ module Projects ...@@ -16,10 +16,10 @@ module Projects
@new_deploy_token = DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute @new_deploy_token = DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute
if @new_deploy_token.persisted? if @new_deploy_token.persisted?
flash[:notice] = s_('DeployTokens|Your new project deploy token has been created.') flash.now[:notice] = s_('DeployTokens|Your new project deploy token has been created.')
end end
redirect_to action: :show render_show
end end
private private
......
...@@ -368,6 +368,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -368,6 +368,7 @@ class ProjectsController < Projects::ApplicationController
repository_access_level repository_access_level
snippets_access_level snippets_access_level
wiki_access_level wiki_access_level
pages_access_level
] ]
] ]
end end
......
...@@ -363,6 +363,7 @@ class IssuableFinder ...@@ -363,6 +363,7 @@ class IssuableFinder
def use_cte_for_search? def use_cte_for_search?
return false unless search return false unless search
return false unless Gitlab::Database.postgresql? return false unless Gitlab::Database.postgresql?
return false unless Feature.enabled?(:use_cte_for_group_issues_search, default_enabled: true)
params[:use_cte_for_search] params[:use_cte_for_search]
end end
......
...@@ -122,9 +122,13 @@ class IssuesFinder < IssuableFinder ...@@ -122,9 +122,13 @@ class IssuesFinder < IssuableFinder
return @user_can_see_all_confidential_issues = true if current_user.full_private_access? return @user_can_see_all_confidential_issues = true if current_user.full_private_access?
@user_can_see_all_confidential_issues = @user_can_see_all_confidential_issues =
project? && if project? && project
project && project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL
project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL elsif group
group.max_member_access_for_user(current_user) >= CONFIDENTIAL_ACCESS_LEVEL
else
false
end
end end
def user_cannot_see_confidential_issues? def user_cannot_see_confidential_issues?
......
...@@ -13,8 +13,11 @@ ...@@ -13,8 +13,11 @@
class LicenseTemplateFinder class LicenseTemplateFinder
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
<<<<<<< HEAD
prepend ::EE::LicenseTemplateFinder prepend ::EE::LicenseTemplateFinder
=======
>>>>>>> upstream/master
attr_reader :project, :params attr_reader :project, :params
def initialize(project, params = {}) def initialize(project, params = {})
......
...@@ -3,8 +3,11 @@ ...@@ -3,8 +3,11 @@
class TemplateFinder class TemplateFinder
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
<<<<<<< HEAD
prepend ::EE::TemplateFinder prepend ::EE::TemplateFinder
=======
>>>>>>> upstream/master
VENDORED_TEMPLATES = HashWithIndifferentAccess.new( VENDORED_TEMPLATES = HashWithIndifferentAccess.new(
dockerfiles: ::Gitlab::Template::DockerfileTemplate, dockerfiles: ::Gitlab::Template::DockerfileTemplate,
gitignores: ::Gitlab::Template::GitignoreTemplate, gitignores: ::Gitlab::Template::GitignoreTemplate,
......
...@@ -16,7 +16,7 @@ module Types ...@@ -16,7 +16,7 @@ module Types
:create_deployment, :push_to_delete_protected_branch, :create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages, :admin_wiki, :admin_project, :update_pages,
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki, :admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
:create_pages, :destroy_pages :create_pages, :destroy_pages, :read_pages_content
end end
end end
end end
...@@ -21,6 +21,29 @@ module DashboardHelper ...@@ -21,6 +21,29 @@ module DashboardHelper
links.any? { |link| dashboard_nav_link?(link) } links.any? { |link| dashboard_nav_link?(link) }
end end
def controller_action_to_child_dashboards(controller = controller_name, action = action_name)
case "#{controller}##{action}"
when 'projects#index', 'root#index', 'projects#starred', 'projects#trending'
%w(projects stars)
when 'dashboard#activity'
%w(starred_project_activity project_activity)
when 'groups#index'
%w(groups)
when 'todos#index'
%w(todos)
when 'dashboard#issues'
%w(issues)
when 'dashboard#merge_requests'
%w(merge_requests)
else
[]
end
end
def user_default_dashboard?(user = current_user)
controller_action_to_child_dashboards.any? {|dashboard| dashboard == user.dashboard }
end
private private
def get_dashboard_nav_links def get_dashboard_nav_links
......
...@@ -456,6 +456,7 @@ module ProjectsHelper ...@@ -456,6 +456,7 @@ module ProjectsHelper
buildsAccessLevel: feature.builds_access_level, buildsAccessLevel: feature.builds_access_level,
wikiAccessLevel: feature.wiki_access_level, wikiAccessLevel: feature.wiki_access_level,
snippetsAccessLevel: feature.snippets_access_level, snippetsAccessLevel: feature.snippets_access_level,
pagesAccessLevel: feature.pages_access_level,
containerRegistryEnabled: !!project.container_registry_enabled, containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled lfsEnabled: !!project.lfs_enabled
} }
...@@ -470,7 +471,10 @@ module ProjectsHelper ...@@ -470,7 +471,10 @@ module ProjectsHelper
registryAvailable: Gitlab.config.registry.enabled, registryAvailable: Gitlab.config.registry.enabled,
registryHelpPath: help_page_path('user/project/container_registry'), registryHelpPath: help_page_path('user/project/container_registry'),
lfsAvailable: Gitlab.config.lfs.enabled, lfsAvailable: Gitlab.config.lfs.enabled,
lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'),
pagesAvailable: Gitlab.config.pages.enabled,
pagesAccessControlEnabled: Gitlab.config.pages.access_control,
pagesHelpPath: help_page_path('user/project/pages/index.md')
} }
end end
......
...@@ -792,6 +792,9 @@ module Ci ...@@ -792,6 +792,9 @@ module Ci
variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(',')) variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(','))
variables.append(key: 'CI_SERVER_NAME', value: 'GitLab') variables.append(key: 'CI_SERVER_NAME', value: 'GitLab')
variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION) variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: gitlab_version_info.major.to_s)
variables.append(key: 'CI_SERVER_VERSION_MINOR', value: gitlab_version_info.minor.to_s)
variables.append(key: 'CI_SERVER_VERSION_PATCH', value: gitlab_version_info.patch.to_s)
variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision) variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision)
variables.append(key: 'CI_JOB_NAME', value: name) variables.append(key: 'CI_JOB_NAME', value: name)
variables.append(key: 'CI_JOB_STAGE', value: stage) variables.append(key: 'CI_JOB_STAGE', value: stage)
...@@ -806,6 +809,10 @@ module Ci ...@@ -806,6 +809,10 @@ module Ci
end end
end end
def gitlab_version_info
@gitlab_version_info ||= Gitlab::VersionInfo.parse(Gitlab::VERSION)
end
def legacy_variables def legacy_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables| Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_BUILD_REF', value: sha) variables.append(key: 'CI_BUILD_REF', value: sha)
......
...@@ -53,7 +53,11 @@ class CommitStatus < ActiveRecord::Base ...@@ -53,7 +53,11 @@ class CommitStatus < ActiveRecord::Base
missing_dependency_failure: 5, missing_dependency_failure: 5,
runner_unsupported: 6, runner_unsupported: 6,
stale_schedule: 7 stale_schedule: 7
<<<<<<< HEAD
}.merge(EE_FAILURE_REASONS) }.merge(EE_FAILURE_REASONS)
=======
}
>>>>>>> upstream/master
## ##
# We still create some CommitStatuses outside of CreatePipelineService. # We still create some CommitStatuses outside of CreatePipelineService.
......
...@@ -177,6 +177,7 @@ class Issue < ActiveRecord::Base ...@@ -177,6 +177,7 @@ class Issue < ActiveRecord::Base
"#{project.to_reference(from, full: full)}#{reference}" "#{project.to_reference(from, full: full)}#{reference}"
end end
<<<<<<< HEAD
def related_issues(current_user, preload: nil) def related_issues(current_user, preload: nil)
related_issues = Issue related_issues = Issue
.select(['issues.*', 'issue_links.id AS issue_link_id']) .select(['issues.*', 'issue_links.id AS issue_link_id'])
...@@ -194,6 +195,8 @@ class Issue < ActiveRecord::Base ...@@ -194,6 +195,8 @@ class Issue < ActiveRecord::Base
) )
end end
=======
>>>>>>> upstream/master
def suggested_branch_name def suggested_branch_name
return to_branch_name unless project.repository.branch_exists?(to_branch_name) return to_branch_name unless project.repository.branch_exists?(to_branch_name)
......
...@@ -41,11 +41,18 @@ class Note < ActiveRecord::Base ...@@ -41,11 +41,18 @@ class Note < ActiveRecord::Base
alias_attribute :last_edited_at, :updated_at alias_attribute :last_edited_at, :updated_at
alias_attribute :last_edited_by, :updated_by alias_attribute :last_edited_by, :updated_by
# Attribute containing rendered and redacted Markdown as generated by # Number of user visible references as generated by Banzai::ObjectRenderer
# Banzai::ObjectRenderer.
attr_accessor :redacted_note_html attr_accessor :redacted_note_html
<<<<<<< HEAD
# Number of user visible references as generated by Banzai::ObjectRenderer # Number of user visible references as generated by Banzai::ObjectRenderer
=======
# Total of all references as generated by Banzai::ObjectRenderer
attr_accessor :total_reference_count
# An Array containing the number of visible references as generated by
# Banzai::ObjectRenderer
>>>>>>> upstream/master
attr_accessor :user_visible_reference_count attr_accessor :user_visible_reference_count
# Total of all references as generated by Banzai::ObjectRenderer # Total of all references as generated by Banzai::ObjectRenderer
......
...@@ -58,8 +58,8 @@ class Project < ActiveRecord::Base ...@@ -58,8 +58,8 @@ class Project < ActiveRecord::Base
cache_markdown_field :description, pipeline: :description cache_markdown_field :description, pipeline: :description
delegate :feature_available?, :builds_enabled?, :wiki_enabled?, delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
:merge_requests_enabled?, :issues_enabled?, to: :project_feature, :merge_requests_enabled?, :issues_enabled?, :pages_enabled?, :public_pages?,
allow_nil: true to: :project_feature, allow_nil: true
delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage
...@@ -361,7 +361,7 @@ class Project < ActiveRecord::Base ...@@ -361,7 +361,7 @@ class Project < ActiveRecord::Base
# "enabled" here means "not disabled". It includes private features! # "enabled" here means "not disabled". It includes private features!
scope :with_feature_enabled, ->(feature) { scope :with_feature_enabled, ->(feature) {
access_level_attribute = ProjectFeature.access_level_attribute(feature) access_level_attribute = ProjectFeature.access_level_attribute(feature)
with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED] }) with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED, ProjectFeature::PUBLIC] })
} }
# Picks a feature where the level is exactly that given. # Picks a feature where the level is exactly that given.
...@@ -423,15 +423,15 @@ class Project < ActiveRecord::Base ...@@ -423,15 +423,15 @@ class Project < ActiveRecord::Base
end end
end end
# project features may be "disabled", "internal" or "enabled". If "internal", # project features may be "disabled", "internal", "enabled" or "public". If "internal",
# they are only available to team members. This scope returns projects where # they are only available to team members. This scope returns projects where
# the feature is either enabled, or internal with permission for the user. # the feature is either public, enabled, or internal with permission for the user.
# #
# This method uses an optimised version of `with_feature_access_level` for # This method uses an optimised version of `with_feature_access_level` for
# logged in users to more efficiently get private projects with the given # logged in users to more efficiently get private projects with the given
# feature. # feature.
def self.with_feature_available_for_user(feature, user) def self.with_feature_available_for_user(feature, user)
visible = [nil, ProjectFeature::ENABLED] visible = [nil, ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
if user&.admin? if user&.admin?
with_feature_enabled(feature) with_feature_enabled(feature)
...@@ -1097,26 +1097,10 @@ class Project < ActiveRecord::Base ...@@ -1097,26 +1097,10 @@ class Project < ActiveRecord::Base
end end
def find_or_initialize_services(exceptions: []) def find_or_initialize_services(exceptions: [])
services_templates = Service.where(template: true)
available_services_names = Service.available_services_names - exceptions available_services_names = Service.available_services_names - exceptions
available_services = available_services_names.map do |service_name| available_services = available_services_names.map do |service_name|
service = find_service(services, service_name) find_or_initialize_service(service_name)
if service
service
else
# We should check if template for the service exists
template = find_service(services_templates, service_name)
if template.nil?
# If no template, we should create an instance. Ex `build_gitlab_ci_service`
public_send("build_#{service_name}_service") # rubocop:disable GitlabSecurity/PublicSend
else
Service.build_from_template(id, template)
end
end
end end
available_services.reject do |service| available_services.reject do |service|
...@@ -1129,7 +1113,18 @@ class Project < ActiveRecord::Base ...@@ -1129,7 +1113,18 @@ class Project < ActiveRecord::Base
end end
def find_or_initialize_service(name) def find_or_initialize_service(name)
find_or_initialize_services.find { |service| service.to_param == name } service = find_service(services, name)
return service if service
# We should check if template for the service exists
template = find_service(services_templates, name)
if template
Service.build_from_template(id, template)
else
# If no template, we should create an instance. Ex `build_gitlab_ci_service`
public_send("build_#{name}_service") # rubocop:disable GitlabSecurity/PublicSend
end
end end
# rubocop: disable CodeReuse/ServiceClass # rubocop: disable CodeReuse/ServiceClass
...@@ -2289,4 +2284,8 @@ class Project < ActiveRecord::Base ...@@ -2289,4 +2284,8 @@ class Project < ActiveRecord::Base
check_access.call check_access.call
end end
end end
def services_templates
@services_templates ||= Service.where(template: true)
end
end end
...@@ -5,7 +5,8 @@ class ProjectAutoDevops < ActiveRecord::Base ...@@ -5,7 +5,8 @@ class ProjectAutoDevops < ActiveRecord::Base
enum deploy_strategy: { enum deploy_strategy: {
continuous: 0, continuous: 0,
manual: 1 manual: 1,
timed_incremental: 2
} }
scope :enabled, -> { where(enabled: true) } scope :enabled, -> { where(enabled: true) }
...@@ -30,10 +31,7 @@ class ProjectAutoDevops < ActiveRecord::Base ...@@ -30,10 +31,7 @@ class ProjectAutoDevops < ActiveRecord::Base
value: domain.presence || instance_domain) value: domain.presence || instance_domain)
end end
if manual? variables.concat(deployment_strategy_default_variables)
variables.append(key: 'STAGING_ENABLED', value: '1')
variables.append(key: 'INCREMENTAL_ROLLOUT_ENABLED', value: '1')
end
end end
end end
...@@ -51,4 +49,16 @@ class ProjectAutoDevops < ActiveRecord::Base ...@@ -51,4 +49,16 @@ class ProjectAutoDevops < ActiveRecord::Base
!project.public? && !project.public? &&
!project.deploy_tokens.find_by(name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME).present? !project.deploy_tokens.find_by(name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME).present?
end end
def deployment_strategy_default_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
if manual?
variables.append(key: 'STAGING_ENABLED', value: '1')
variables.append(key: 'INCREMENTAL_ROLLOUT_ENABLED', value: '1') # deprecated
variables.append(key: 'INCREMENTAL_ROLLOUT_MODE', value: 'manual')
elsif timed_incremental?
variables.append(key: 'INCREMENTAL_ROLLOUT_MODE', value: 'timed')
end
end
end
end end
...@@ -13,14 +13,16 @@ class ProjectFeature < ActiveRecord::Base ...@@ -13,14 +13,16 @@ class ProjectFeature < ActiveRecord::Base
# Disabled: not enabled for anyone # Disabled: not enabled for anyone
# Private: enabled only for team members # Private: enabled only for team members
# Enabled: enabled for everyone able to access the project # Enabled: enabled for everyone able to access the project
# Public: enabled for everyone (only allowed for pages)
# #
# Permission levels # Permission levels
DISABLED = 0 DISABLED = 0
PRIVATE = 10 PRIVATE = 10
ENABLED = 20 ENABLED = 20
PUBLIC = 30
FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze
class << self class << self
def access_level_attribute(feature) def access_level_attribute(feature)
...@@ -46,6 +48,7 @@ class ProjectFeature < ActiveRecord::Base ...@@ -46,6 +48,7 @@ class ProjectFeature < ActiveRecord::Base
validates :project, presence: true validates :project, presence: true
validate :repository_children_level validate :repository_children_level
validate :allowed_access_levels
default_value_for :builds_access_level, value: ENABLED, allows_nil: false default_value_for :builds_access_level, value: ENABLED, allows_nil: false
default_value_for :issues_access_level, value: ENABLED, allows_nil: false default_value_for :issues_access_level, value: ENABLED, allows_nil: false
...@@ -87,6 +90,16 @@ class ProjectFeature < ActiveRecord::Base ...@@ -87,6 +90,16 @@ class ProjectFeature < ActiveRecord::Base
issues_access_level > DISABLED issues_access_level > DISABLED
end end
def pages_enabled?
pages_access_level > DISABLED
end
def public_pages?
return true unless Gitlab.config.pages.access_control
pages_access_level == PUBLIC || pages_access_level == ENABLED && project.public?
end
private private
# Validates builds and merge requests access level # Validates builds and merge requests access level
...@@ -101,6 +114,17 @@ class ProjectFeature < ActiveRecord::Base ...@@ -101,6 +114,17 @@ class ProjectFeature < ActiveRecord::Base
%i(merge_requests_access_level builds_access_level).each(&validator) %i(merge_requests_access_level builds_access_level).each(&validator)
end end
# Validates access level for other than pages cannot be PUBLIC
def allowed_access_levels
validator = lambda do |field|
level = public_send(field) || ProjectFeature::ENABLED # rubocop:disable GitlabSecurity/PublicSend
not_allowed = level > ProjectFeature::ENABLED
self.errors.add(field, "cannot have public visibility level") if not_allowed
end
(FEATURES - %i(pages)).each {|f| validator.call("#{f}_access_level")}
end
def get_permission(user, level) def get_permission(user, level)
case level case level
when DISABLED when DISABLED
...@@ -109,6 +133,8 @@ class ProjectFeature < ActiveRecord::Base ...@@ -109,6 +133,8 @@ class ProjectFeature < ActiveRecord::Base
user && (project.team.member?(user) || user.full_private_access?) user && (project.team.member?(user) || user.full_private_access?)
when ENABLED when ENABLED
true true
when PUBLIC
true
else else
true true
end end
......
...@@ -149,7 +149,7 @@ class HipchatService < Service ...@@ -149,7 +149,7 @@ class HipchatService < Service
context.merge!(options) context.merge!(options)
html = Banzai.post_process(Banzai.render(text, context), context) html = Banzai.render_and_post_process(text, context)
sanitized_html = sanitize(html, tags: HIPCHAT_ALLOWED_TAGS, attributes: %w[href title alt]) sanitized_html = sanitize(html, tags: HIPCHAT_ALLOWED_TAGS, attributes: %w[href title alt])
sanitized_html.truncate(200, separator: ' ', omission: '...') sanitized_html.truncate(200, separator: ' ', omission: '...')
......
...@@ -11,7 +11,10 @@ class SystemNoteMetadata < ActiveRecord::Base ...@@ -11,7 +11,10 @@ class SystemNoteMetadata < ActiveRecord::Base
TYPES_WITH_CROSS_REFERENCES = %w[ TYPES_WITH_CROSS_REFERENCES = %w[
commit cross_reference commit cross_reference
close duplicate close duplicate
<<<<<<< HEAD
relate unrelate relate unrelate
=======
>>>>>>> upstream/master
moved moved
].freeze ].freeze
......
...@@ -6,7 +6,8 @@ class UserCallout < ActiveRecord::Base ...@@ -6,7 +6,8 @@ class UserCallout < ActiveRecord::Base
enum feature_name: { enum feature_name: {
gke_cluster_integration: 1, gke_cluster_integration: 1,
gcp_signup_offer: 2, gcp_signup_offer: 2,
cluster_security_warning: 3 cluster_security_warning: 3,
gold_trial: 4
} }
validates :user, presence: true validates :user, presence: true
......
...@@ -111,6 +111,7 @@ class ProjectPolicy < BasePolicy ...@@ -111,6 +111,7 @@ class ProjectPolicy < BasePolicy
snippets snippets
wiki wiki
builds builds
pages
] ]
features.each do |f| features.each do |f|
...@@ -168,6 +169,7 @@ class ProjectPolicy < BasePolicy ...@@ -168,6 +169,7 @@ class ProjectPolicy < BasePolicy
enable :upload_file enable :upload_file
enable :read_cycle_analytics enable :read_cycle_analytics
enable :award_emoji enable :award_emoji
enable :read_pages_content
end end
# These abilities are not allowed to admins that are not members of the project, # These abilities are not allowed to admins that are not members of the project,
...@@ -287,6 +289,8 @@ class ProjectPolicy < BasePolicy ...@@ -287,6 +289,8 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:merge_request)) prevent(*create_read_update_admin_destroy(:merge_request))
end end
rule { pages_disabled }.prevent :read_pages_content
rule { issues_disabled & merge_requests_disabled }.policy do rule { issues_disabled & merge_requests_disabled }.policy do
prevent(*create_read_update_admin_destroy(:label)) prevent(*create_read_update_admin_destroy(:label))
prevent(*create_read_update_admin_destroy(:milestone)) prevent(*create_read_update_admin_destroy(:milestone))
...@@ -346,6 +350,7 @@ class ProjectPolicy < BasePolicy ...@@ -346,6 +350,7 @@ class ProjectPolicy < BasePolicy
enable :download_code enable :download_code
enable :download_wiki_code enable :download_wiki_code
enable :read_cycle_analytics enable :read_cycle_analytics
enable :read_pages_content
# NOTE: may be overridden by IssuePolicy # NOTE: may be overridden by IssuePolicy
enable :read_issue enable :read_issue
......
...@@ -116,6 +116,10 @@ class DiffFileEntity < Grape::Entity ...@@ -116,6 +116,10 @@ class DiffFileEntity < Grape::Entity
project_blob_path(project, tree_join(diff_file.content_sha, diff_file.new_path)) project_blob_path(project, tree_join(diff_file.content_sha, diff_file.new_path))
end end
expose :viewer, using: DiffViewerEntity do |diff_file|
diff_file.rich_viewer || diff_file.simple_viewer
end
expose :replaced_view_path, if: -> (_, options) { options[:merge_request] } do |diff_file| expose :replaced_view_path, if: -> (_, options) { options[:merge_request] } do |diff_file|
image_diff = diff_file.rich_viewer && diff_file.rich_viewer.partial_name == 'image' image_diff = diff_file.rich_viewer && diff_file.rich_viewer.partial_name == 'image'
image_replaced = diff_file.old_content_sha && diff_file.old_content_sha != diff_file.content_sha image_replaced = diff_file.old_content_sha && diff_file.old_content_sha != diff_file.content_sha
......
# frozen_string_literal: true
class DiffViewerEntity < Grape::Entity
# Partial name refers directly to a Rails feature, let's avoid
# using this on the frontend.
expose :partial_name, as: :name
end
...@@ -27,7 +27,7 @@ class DiscussionEntity < Grape::Entity ...@@ -27,7 +27,7 @@ class DiscussionEntity < Grape::Entity
expose :resolved?, as: :resolved expose :resolved?, as: :resolved
expose :resolved_by_push?, as: :resolved_by_push expose :resolved_by_push?, as: :resolved_by_push
expose :resolved_by expose :resolved_by, using: NoteUserEntity
expose :resolved_at expose :resolved_at
expose :resolve_path, if: -> (d, _) { d.resolvable? } do |discussion| expose :resolve_path, if: -> (d, _) { d.resolvable? } do |discussion|
resolve_project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion.id) resolve_project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion.id)
......
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
module Ci module Ci
class ProcessBuildService < BaseService class ProcessBuildService < BaseService
<<<<<<< HEAD
prepend EE::Ci::ProcessBuildService prepend EE::Ci::ProcessBuildService
=======
>>>>>>> upstream/master
def execute(build, current_status) def execute(build, current_status)
if valid_statuses_for_when(build.when).include?(current_status) if valid_statuses_for_when(build.when).include?(current_status)
if build.schedulable? if build.schedulable?
......
...@@ -21,7 +21,9 @@ module Projects ...@@ -21,7 +21,9 @@ module Projects
def pages_config def pages_config
{ {
domains: pages_domains_config, domains: pages_domains_config,
https_only: project.pages_https_only? https_only: project.pages_https_only?,
id: project.project_id,
access_control: !project.public_pages?
} }
end end
...@@ -31,7 +33,9 @@ module Projects ...@@ -31,7 +33,9 @@ module Projects
domain: domain.domain, domain: domain.domain,
certificate: domain.certificate, certificate: domain.certificate,
key: domain.key, key: domain.key,
https_only: project.pages_https_only? && domain.https? https_only: project.pages_https_only? && domain.https?,
id: project.project_id,
access_control: !project.public_pages?
} }
end end
end end
......
...@@ -74,7 +74,11 @@ module Projects ...@@ -74,7 +74,11 @@ module Projects
system_hook_service.execute_hooks_for(project, :update) system_hook_service.execute_hooks_for(project, :update)
end end
update_pages_config if changing_pages_https_only? update_pages_config if changing_pages_related_config?
end
def changing_pages_related_config?
changing_pages_https_only? || changing_pages_access_level?
end end
def update_failed! def update_failed!
...@@ -104,6 +108,10 @@ module Projects ...@@ -104,6 +108,10 @@ module Projects
params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED
end end
def changing_pages_access_level?
params.dig(:project_feature_attributes, :pages_access_level)
end
def ensure_wiki_exists def ensure_wiki_exists
ProjectWiki.new(project, project.owner).wiki ProjectWiki.new(project, project.owner).wiki
rescue ProjectWiki::CouldNotCreateWikiError rescue ProjectWiki::CouldNotCreateWikiError
......
...@@ -114,6 +114,8 @@ ...@@ -114,6 +114,8 @@
= visibility_level_icon(@project.visibility_level) = visibility_level_icon(@project.visibility_level)
= visibility_level_label(@project.visibility_level) = visibility_level_label(@project.visibility_level)
= render_if_exists 'admin/projects/geo_status_widget', locals: { project: @project }
.card .card
.card-header .card-header
Transfer project Transfer project
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity") = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- page_title "Activity" - page_title "Activity"
- header_title "Activity", activity_dashboard_path - header_title "Activity", activity_dashboard_path
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
- header_title "Groups", dashboard_groups_path - header_title "Groups", dashboard_groups_path
= render 'dashboard/groups_head' = render 'dashboard/groups_head'
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if params[:filter].blank? && @groups.empty? - if params[:filter].blank? && @groups.empty?
= render 'shared/groups/empty_state' = render 'shared/groups/empty_state'
- else - else
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues") = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
.top-area .top-area
= render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set = render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set
.nav-controls .nav-controls
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
- page_title _("Merge Requests") - page_title _("Merge Requests")
- @breadcrumb_link = merge_requests_dashboard_path(assignee_id: current_user.id) - @breadcrumb_link = merge_requests_dashboard_path(assignee_id: current_user.id)
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
.top-area .top-area
= render 'shared/issuable/nav', type: :merge_requests, display_count: !@no_filters_set = render 'shared/issuable/nav', type: :merge_requests, display_count: !@no_filters_set
.nav-controls .nav-controls
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity") = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- page_title "Projects" - page_title "Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
- page_title "Starred Projects" - page_title "Starred Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
%div{ class: container_class } %div{ class: container_class }
= render "projects/last_push" = render "projects/last_push"
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
- page_title "Todos" - page_title "Todos"
- header_title "Todos", dashboard_todos_path - header_title "Todos", dashboard_todos_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user.todos.any? - if current_user.todos.any?
.top-area .top-area
%ul.nav-links.mobile-separator.nav.nav-tabs %ul.nav-links.mobile-separator.nav.nav-tabs
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
- page_title _("Groups") - page_title _("Groups")
- header_title _("Groups"), dashboard_groups_path - header_title _("Groups"), dashboard_groups_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user - if current_user
= render 'dashboard/groups_head' = render 'dashboard/groups_head'
- else - else
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
- page_title _("Projects") - page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path - header_title _("Projects"), dashboard_projects_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user - if current_user
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
- else - else
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
- page_title _("Projects") - page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path - header_title _("Projects"), dashboard_projects_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user - if current_user
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
- else - else
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
- page_title _("Projects") - page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path - header_title _("Projects"), dashboard_projects_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user - if current_user
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
- else - else
......
- container = @no_breadcrumb_container ? 'container-fluid' : container_class - container = @no_breadcrumb_container ? 'container-fluid' : container_class
- hide_top_links = @hide_top_links || false - hide_top_links = @hide_top_links || false
= yield :above_breadcrumbs_content
%nav.breadcrumbs{ role: "navigation", class: [container, @content_class] } %nav.breadcrumbs{ role: "navigation", class: [container, @content_class] }
.breadcrumbs-container .breadcrumbs-container
- if defined?(@left_sidebar) - if defined?(@left_sidebar)
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details") = s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details")
- if show_cluster_security_warning? - if show_cluster_security_warning?
.js-cluster-security-warning.alert.alert-block.alert-dismissable.bs-callout.bs-callout-warning .js-cluster-security-warning.alert.alert-block.alert-dismissable.bs-callout.bs-callout-warning{ data: { feature_id: UserCalloutsHelper::CLUSTER_SECURITY_WARNING, dismiss_endpoint: user_callouts_path } }
%button.close{ type: "button", data: { feature_id: UserCalloutsHelper::CLUSTER_SECURITY_WARNING, dismiss_endpoint: user_callouts_path } } &times; %button.close.js-close{ type: "button" } &times;
= s_("ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application.") = s_("ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application.")
= link_to s_("More information"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications') = link_to s_("More information"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications')
- link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer') - link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert' } .bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert', data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } }
%button.close{ type: "button", data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } } &times; %button.close.js-close{ type: "button" } &times;
.gcp-signup-offer--content .gcp-signup-offer--content
.gcp-signup-offer--icon.append-right-8 .gcp-signup-offer--icon.append-right-8
= sprite_icon("information", size: 16) = sprite_icon("information", size: 16)
......
- page_title _("Metrics") - page_title _("Metrics")
.row .row.empty-state
.col-sm-12 .col-sm-12
.svg-content .svg-content
= image_tag 'illustrations/operations_metrics_empty.svg' = image_tag 'illustrations/operations_metrics_empty.svg'
.row.empty-environments .col-12
.col-sm-12.text-center .text-content
%h4 %h4.text-center
= s_('Metrics|No deployed environments') = s_('Metrics|No deployed environments')
.state-description %p.state-description
= s_('Metrics|Check out the CI/CD documentation on deploying to an environment') = s_('Metrics|Check out the CI/CD documentation on deploying to an environment')
.prepend-top-10 .text-center
= link_to s_("Metrics|Learn about environments"), help_page_path('ci/environments'), class: 'btn btn-success' = link_to s_("Metrics|Learn about environments"), help_page_path('ci/environments'), class: 'btn btn-success'
...@@ -45,10 +45,17 @@ ...@@ -45,10 +45,17 @@
= form.label :deploy_strategy_continuous, class: 'form-check-label' do = form.label :deploy_strategy_continuous, class: 'form-check-label' do
= s_('CICD|Continuous deployment to production') = s_('CICD|Continuous deployment to production')
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-deploy'), target: '_blank' = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-deploy'), target: '_blank'
.form-check
= form.radio_button :deploy_strategy, 'timed_incremental', class: 'form-check-input'
= form.label :deploy_strategy_timed_incremental, class: 'form-check-label' do
= s_('CICD|Continuous deployment to production using timed incremental rollout')
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'timed-incremental-rollout-to-production'), target: '_blank'
.form-check .form-check
= form.radio_button :deploy_strategy, 'manual', class: 'form-check-input' = form.radio_button :deploy_strategy, 'manual', class: 'form-check-input'
= form.label :deploy_strategy_manual, class: 'form-check-label' do = form.label :deploy_strategy_manual, class: 'form-check-label' do
= s_('CICD|Automatic deployment to staging, manual deployment to production') = s_('CICD|Automatic deployment to staging, manual deployment to production')
= link_to icon('question-circle'), help_page_path('ci/environments.md', anchor: 'manually-deploying-to-environments'), target: '_blank' = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'incremental-rollout-to-production'), target: '_blank'
= f.submit _('Save changes'), class: "btn btn-success prepend-top-15" = f.submit _('Save changes'), class: "btn btn-success prepend-top-15"
---
title: Add GitLab version components to CI environment variables
merge_request: 21853
author:
type: added
---
title: Fix sorting by priority or popularity on group issues page, when also searching
issue content
merge_request: 21521
author:
type: fixed
---
title: Fix timeout when running the RemoveRestrictedTodos background migration
merge_request: 21893
author:
type: fixed
---
title: Add installation type to backup information file
merge_request: 22150
author:
type: changed
---
title: Add access control to GitLab pages and make it possible to enable/disable it in project settings
merge_request: 18589
author: Tuomo Ala-Vannesluoma
type: added
---
title: Add timed incremental rollout to Auto DevOps
merge_request: 22023
author:
type: added
---
title: Mitigate N+1 queries when parsing commit references in comments.
merge_request:
author:
type: performance
---
title: Fix LFS uploaded images not being rendered
merge_request: 22092
author:
type: fixed
---
title: 'Rails5: fix artifacts controller download spec Rails5 has params[:file_type]
as '''' if file_type is included as nil in the request'
merge_request: 22123
author: Jasper Maes
type: other
---
title: Markdown API no longer displays confidential title references unless authorized
merge_request:
author:
type: security
---
title: Properly filter private references from system notes
merge_request:
author:
type: security
---
title: Filter user sensitive data from discussions JSON
merge_request: 2536
author:
type: security
---
title: Update operations metrics empty state
merge_request: 21974
author: George Tsiolis
type: other
...@@ -231,6 +231,7 @@ production: &base ...@@ -231,6 +231,7 @@ production: &base
## GitLab Pages ## GitLab Pages
pages: pages:
enabled: false enabled: false
access_control: false
# The location where pages are stored (default: shared/pages). # The location where pages are stored (default: shared/pages).
# path: shared/pages # path: shared/pages
......
...@@ -217,6 +217,7 @@ Settings.registry['path'] = Settings.absolute(Settings.registry['path ...@@ -217,6 +217,7 @@ Settings.registry['path'] = Settings.absolute(Settings.registry['path
# #
Settings['pages'] ||= Settingslogic.new({}) Settings['pages'] ||= Settingslogic.new({})
Settings.pages['enabled'] = false if Settings.pages['enabled'].nil? Settings.pages['enabled'] = false if Settings.pages['enabled'].nil?
Settings.pages['access_control'] = false if Settings.pages['access_control'].nil?
Settings.pages['path'] = Settings.absolute(Settings.pages['path'] || File.join(Settings.shared['path'], "pages")) Settings.pages['path'] = Settings.absolute(Settings.pages['path'] || File.join(Settings.shared['path'], "pages"))
Settings.pages['https'] = false if Settings.pages['https'].nil? Settings.pages['https'] = false if Settings.pages['https'].nil?
Settings.pages['host'] ||= "example.com" Settings.pages['host'] ||= "example.com"
......
class AddPagesAccessLevelToProjectFeature < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
def up
add_column_with_default(:project_features, :pages_access_level, :integer, default: ProjectFeature::PUBLIC, allow_null: false)
change_column_default(:project_features, :pages_access_level, ProjectFeature::ENABLED)
end
def down
remove_column :project_features, :pages_access_level
end
end
# 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.
# rescheduling of the revised RemoveRestrictedTodos background migration
class RemoveRestrictedTodosWithCte < ActiveRecord::Migration
DOWNTIME = false
disable_ddl_transaction!
MIGRATION = 'RemoveRestrictedTodos'.freeze
BATCH_SIZE = 1000
DELAY_INTERVAL = 5.minutes.to_i
class Project < ActiveRecord::Base
include EachBatch
self.table_name = 'projects'
end
def up
Project.where('EXISTS (SELECT 1 FROM todos WHERE todos.project_id = projects.id)')
.each_batch(of: BATCH_SIZE) do |batch, index|
range = batch.pluck('MIN(id)', 'MAX(id)').first
BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, range)
end
end
def down
# nothing to do
end
end
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
<<<<<<< HEAD
ActiveRecord::Schema.define(version: 20181001172651) do ActiveRecord::Schema.define(version: 20181001172651) do
=======
ActiveRecord::Schema.define(version: 20181002172433) do
>>>>>>> upstream/master
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -2141,6 +2145,7 @@ ActiveRecord::Schema.define(version: 20181001172651) do ...@@ -2141,6 +2145,7 @@ ActiveRecord::Schema.define(version: 20181001172651) do
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.integer "repository_access_level", default: 20, null: false t.integer "repository_access_level", default: 20, null: false
t.integer "pages_access_level", default: 20, null: false
end end
add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
......
...@@ -26,7 +26,10 @@ following locations: ...@@ -26,7 +26,10 @@ following locations:
- [Epic Issues](epic_issues.md) **[ULTIMATE]** - [Epic Issues](epic_issues.md) **[ULTIMATE]**
- [Events](events.md) - [Events](events.md)
- [Feature flags](features.md) - [Feature flags](features.md)
<<<<<<< HEAD
- [Geo Nodes](geo_nodes.md) **[PREMIUM]** - [Geo Nodes](geo_nodes.md) **[PREMIUM]**
=======
>>>>>>> upstream/master
- [Gitignore templates](templates/gitignores.md) - [Gitignore templates](templates/gitignores.md)
- [GitLab CI Config templates](templates/gitlab_ci_ymls.md) - [GitLab CI Config templates](templates/gitlab_ci_ymls.md)
- [Groups](groups.md) - [Groups](groups.md)
......
...@@ -96,6 +96,9 @@ future GitLab releases.** ...@@ -96,6 +96,9 @@ future GitLab releases.**
| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs | | **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs |
| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs | | **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs |
| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs | | **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs |
| **CI_SERVER_VERSION_MAJOR** | 11.4 | all | GitLab version major component |
| **CI_SERVER_VERSION_MINOR** | 11.4 | all | GitLab version minor component |
| **CI_SERVER_VERSION_PATCH** | 11.4 | all | GitLab version patch component |
| **CI_SHARED_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. | | **CI_SHARED_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. |
| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job | | **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job |
| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment | | **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment |
...@@ -344,6 +347,12 @@ Running on runner-8a2f473d-project-1796893-concurrent-0 via runner-8a2f473d-mach ...@@ -344,6 +347,12 @@ Running on runner-8a2f473d-project-1796893-concurrent-0 via runner-8a2f473d-mach
++ CI_SERVER_NAME='GitLab CI' ++ CI_SERVER_NAME='GitLab CI'
++ export CI_SERVER_VERSION= ++ export CI_SERVER_VERSION=
++ CI_SERVER_VERSION= ++ CI_SERVER_VERSION=
++ export CI_SERVER_VERSION_MAJOR=
++ CI_SERVER_VERSION_MAJOR=
++ export CI_SERVER_VERSION_MINOR=
++ CI_SERVER_VERSION_MINOR=
++ export CI_SERVER_VERSION_PATCH=
++ CI_SERVER_VERSION_PATCH=
++ export CI_SERVER_REVISION= ++ export CI_SERVER_REVISION=
++ CI_SERVER_REVISION= ++ CI_SERVER_REVISION=
++ export GITLAB_CI=true ++ export GITLAB_CI=true
...@@ -489,6 +498,9 @@ export CI_SERVER="yes" ...@@ -489,6 +498,9 @@ export CI_SERVER="yes"
export CI_SERVER_NAME="GitLab" export CI_SERVER_NAME="GitLab"
export CI_SERVER_REVISION="70606bf" export CI_SERVER_REVISION="70606bf"
export CI_SERVER_VERSION="8.9.0" export CI_SERVER_VERSION="8.9.0"
export CI_SERVER_VERSION_MAJOR="8"
export CI_SERVER_VERSION_MINOR="9"
export CI_SERVER_VERSION_PATCH="0"
export GITLAB_USER_ID="42" export GITLAB_USER_ID="42"
export GITLAB_USER_EMAIL="user@example.com" export GITLAB_USER_EMAIL="user@example.com"
export CI_REGISTRY_USER="gitlab-ci-token" export CI_REGISTRY_USER="gitlab-ci-token"
......
...@@ -17,8 +17,8 @@ There are two places defined variables can be used. On the: ...@@ -17,8 +17,8 @@ There are two places defined variables can be used. On the:
| Definition | Can be expanded? | Expansion place | Description | | Definition | Can be expanded? | Expansion place | Description |
|--------------------------------------|-------------------|-----------------|--------------| |--------------------------------------|-------------------|-----------------|--------------|
| `environment:url` | yes | GitLab | The variable expansion is made by GitLab's [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism).<ul><li>**Supported:** all variables defined for a job (project/group variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules)</li><li>**Not suported:** variables defined in Runner's `config.toml` and variables created in job's `script`</li></ul> | | `environment:url` | yes | GitLab | The variable expansion is made by GitLab's [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism).<ul><li>Supported: all variables defined for a job (project/group variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules)</li><li>Not suported: variables defined in Runner's `config.toml` and variables created in job's `script`</li></ul> |
| `environment:name` | yes | GitLab | Similar to `environment:url`, but the variables expansion **doesn't support**: <ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> | | `environment:name` | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support: <ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
| `variables` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) | | `variables` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `image` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) | | `image` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `services:[]` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) | | `services:[]` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
...@@ -26,7 +26,7 @@ There are two places defined variables can be used. On the: ...@@ -26,7 +26,7 @@ There are two places defined variables can be used. On the:
| `cache:key` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) | | `cache:key` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `artifacts:name` | yes | Runner | The variable expansion is made by GitLab Runner's shell environment | | `artifacts:name` | yes | Runner | The variable expansion is made by GitLab Runner's shell environment |
| `script`, `before_script`, `after_script` | yes | Script execution shell | The variable expansion is made by the [execution shell environment](#execution-shell-environment) | | `script`, `before_script`, `after_script` | yes | Script execution shell | The variable expansion is made by the [execution shell environment](#execution-shell-environment) |
| `only:variables:[]`, `except:variables:[]` | no | n/a | The variable must be in the form of `$variable`.<br/>**Not supported:**<ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> | | `only:variables:[]`, `except:variables:[]` | no | n/a | The variable must be in the form of `$variable`.<br/>Not supported:<ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
### `config.toml` file ### `config.toml` file
...@@ -55,9 +55,9 @@ since the expansion is done in GitLab before any Runner will get the job. ...@@ -55,9 +55,9 @@ since the expansion is done in GitLab before any Runner will get the job.
### GitLab Runner internal variable expansion mechanism ### GitLab Runner internal variable expansion mechanism
- **Supported:** project/group variables, `.gitlab-ci.yml` variables, `config.toml` variables, and - Supported: project/group variables, `.gitlab-ci.yml` variables, `config.toml` variables, and
variables from triggers, pipeline schedules, and manual pipelines. variables from triggers, pipeline schedules, and manual pipelines.
- **Not supported:** variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`). - Not supported: variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`).
The Runner uses Go's `os.Expand()` method for variable expansion. It means that it will handle The Runner uses Go's `os.Expand()` method for variable expansion. It means that it will handle
only variables defined as `$variable` and `${variable}`. What's also important, is that only variables defined as `$variable` and `${variable}`. What's also important, is that
...@@ -73,7 +73,7 @@ by bash/sh (leaving empty strings or some values depending whether the variables ...@@ -73,7 +73,7 @@ by bash/sh (leaving empty strings or some values depending whether the variables
defined or not), but will not work with Windows' cmd/PowerShell, since these shells defined or not), but will not work with Windows' cmd/PowerShell, since these shells
are using a different variables syntax. are using a different variables syntax.
**Supported:** Supported:
- The `script` may use all available variables that are default for the shell (e.g., `$PATH` which - The `script` may use all available variables that are default for the shell (e.g., `$PATH` which
should be present in all bash/sh shells) and all variables defined by GitLab CI/CD (project/group variables, should be present in all bash/sh shells) and all variables defined by GitLab CI/CD (project/group variables,
...@@ -106,9 +106,11 @@ The following variables are known as "persisted": ...@@ -106,9 +106,11 @@ The following variables are known as "persisted":
They are: They are:
- **Supported** for all definitions as [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "Runner". - Supported for definitions where the ["Expansion place"](#gitlab-ci-yml-file) is:
- **Not supported:** - Runner.
- By the definitions [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "GitLab". - Script execution shell.
- Not supported:
- For definitions where the ["Expansion place"](#gitlab-ci-yml-file) is GitLab.
- In the `only` and `except` [variables expressions](README.md#variables-expressions). - In the `only` and `except` [variables expressions](README.md#variables-expressions).
## Variables with an environment scope ## Variables with an environment scope
......
...@@ -239,14 +239,19 @@ project's **Settings > CI/CD > Auto DevOps**. ...@@ -239,14 +239,19 @@ project's **Settings > CI/CD > Auto DevOps**.
The available options are: The available options are:
- **Continuous deployment to production** - enables [Auto Deploy](#auto-deploy) - **Continuous deployment to production**: Enables [Auto Deploy](#auto-deploy)
by setting the [`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and with `master` branch directly deployed to production.
[`INCREMENTAL_ROLLOUT_ENABLED`](#incremental-rollout-to-production) variables - **Continuous deployment to production using timed incremental rollout**: Sets the
to false. [`INCREMENTAL_ROLLOUT_MODE`](#timed-incremental-rollout-to-production) variable
- **Automatic deployment to staging, manual deployment to production** - sets the to `timed`, and production deployment will be executed with a 5 minute delay between
each increment in rollout.
- **Automatic deployment to staging, manual deployment to production**: Sets the
[`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and [`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
[`INCREMENTAL_ROLLOUT_ENABLED`](#incremental-rollout-to-production) variables [`INCREMENTAL_ROLLOUT_MODE`](#incremental-rollout-to-production) variables
to true, and the user is responsible for manually deploying to staging and production. to `1` and `manual`. This means:
- `master` branch is directly deployed to staging.
- Manual actions are provided for incremental rollout to production.
## Stages of Auto DevOps ## Stages of Auto DevOps
...@@ -609,7 +614,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac ...@@ -609,7 +614,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. | | `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. |
| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). | | `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). | | `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
| `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. | | `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment.<br/>Set to: <ul><li>`manual`, for manual deployment jobs.</li><li>`timed`, for automatic rollout deployments with a 5 minute delay each one.</li></ul> |
| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. | | `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. | | `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. | | `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
...@@ -730,9 +735,8 @@ to use an incremental rollout to replace just a few pods with the latest code. ...@@ -730,9 +735,8 @@ to use an incremental rollout to replace just a few pods with the latest code.
This will allow you to first check how the app is behaving, and later manually This will allow you to first check how the app is behaving, and later manually
increasing the rollout up to 100%. increasing the rollout up to 100%.
If `INCREMENTAL_ROLLOUT_ENABLED` is defined in your project (e.g., set If `INCREMENTAL_ROLLOUT_MODE` is set to `manual` in your project, then instead
`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a secret variable), then instead of the of the standard `production` job, 4 different
standard `production` job, 4 different
[manual jobs](../../ci/pipelines.md#manual-actions-from-the-pipeline-graph) [manual jobs](../../ci/pipelines.md#manual-actions-from-the-pipeline-graph)
will be created: will be created:
...@@ -756,21 +760,45 @@ environment page. ...@@ -756,21 +760,45 @@ environment page.
Below, you can see how the pipeline will look if the rollout or staging Below, you can see how the pipeline will look if the rollout or staging
variables are defined. variables are defined.
- **Without `INCREMENTAL_ROLLOUT_ENABLED` and without `STAGING_ENABLED`** Without `INCREMENTAL_ROLLOUT_MODE` and without `STAGING_ENABLED`:
![Staging and rollout disabled](img/rollout_staging_disabled.png)
Without `INCREMENTAL_ROLLOUT_MODE` and with `STAGING_ENABLED`:
![Staging and rollout disabled](img/rollout_staging_disabled.png) ![Staging enabled](img/staging_enabled.png)
- **Without `INCREMENTAL_ROLLOUT_ENABLED` and with `STAGING_ENABLED`** With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and without `STAGING_ENABLED`:
![Staging enabled](img/staging_enabled.png) ![Rollout enabled](img/rollout_enabled.png)
- **With `INCREMENTAL_ROLLOUT_ENABLED` and without `STAGING_ENABLED`** With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and with `STAGING_ENABLED`
![Rollout and staging enabled](img/rollout_staging_enabled.png)
CAUTION: **Caution:**
Before GitLab 11.4 this feature was enabled by the presence of the
`INCREMENTAL_ROLLOUT_ENABLED` environment variable.
This configuration is deprecated and will be removed in the future.
#### Timed incremental rollout to production **[PREMIUM]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7545) in GitLab 11.4.
TIP: **Tip:**
You can also set this inside your [project's settings](#deployment-strategy).
![Rollout enabled](img/rollout_enabled.png) This configuration based on
[incremental rollout to production](#incremental-rollout-to-production).
- **With `INCREMENTAL_ROLLOUT_ENABLED` and with `STAGING_ENABLED`** Everything behaves the same way, except:
![Rollout and staging enabled](img/rollout_staging_enabled.png) - It's enabled by setting the `INCREMENTAL_ROLLOUT_MODE` variable to `timed`.
- Instead of the standard `production` job, the following jobs with a 5 minute delay between each are created:
1. `timed rollout 10%`
1. `timed rollout 25%`
1. `timed rollout 50%`
1. `timed rollout 100%`
## Currently supported languages ## Currently supported languages
......
...@@ -116,6 +116,7 @@ which visibility level you select on project settings. ...@@ -116,6 +116,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone - Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal - Only team members: only team members will see even if your project is public or internal
- Everyone with access: everyone can see depending on your project visibility level - Everyone with access: everyone can see depending on your project visibility level
- Everyone: enabled for everyone (only available for GitLab Pages)
### Protected branches ### Protected branches
...@@ -248,6 +249,7 @@ which visibility level you select on project settings. ...@@ -248,6 +249,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone - Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal - Only team members: only team members will see even if your project is public or internal
- Everyone with access: everyone can see depending on your project visibility level - Everyone with access: everyone can see depending on your project visibility level
- Everyone: enabled for everyone (only available for GitLab Pages)
## GitLab CI/CD permissions ## GitLab CI/CD permissions
......
...@@ -12,7 +12,8 @@ module API ...@@ -12,7 +12,8 @@ module API
detail "This feature was introduced in GitLab 11.0." detail "This feature was introduced in GitLab 11.0."
end end
post do post do
context = { only_path: false } context = { only_path: false, current_user: current_user }
context[:pipeline] = params[:gfm] ? :full : :plain_markdown
if params[:project] if params[:project]
project = Project.find_by_full_path(params[:project]) project = Project.find_by_full_path(params[:project])
...@@ -24,9 +25,7 @@ module API ...@@ -24,9 +25,7 @@ module API
context[:skip_project_check] = true context[:skip_project_check] = true
end end
context[:pipeline] = params[:gfm] ? :full : :plain_markdown { html: Banzai.render_and_post_process(params[:text], context) }
{ html: Banzai.render(params[:text], context) }
end end
end end
end end
......
...@@ -289,6 +289,12 @@ module API ...@@ -289,6 +289,12 @@ module API
present_projects forks present_projects forks
end end
desc 'Check pages access of this project'
get ':id/pages_access' do
authorize! :read_pages_content, user_project unless user_project.public_pages?
status 200
end
desc 'Update an existing project' do desc 'Update an existing project' do
success Entities::Project success Entities::Project
end end
......
...@@ -243,6 +243,7 @@ module Backup ...@@ -243,6 +243,7 @@ module Backup
backup_created_at: Time.now, backup_created_at: Time.now,
gitlab_version: Gitlab::VERSION, gitlab_version: Gitlab::VERSION,
tar_version: tar_version, tar_version: tar_version,
installation_type: Gitlab::INSTALLATION_TYPE,
skipped: ENV["SKIP"] skipped: ENV["SKIP"]
} }
end end
......
module Banzai module Banzai
# if you need to render markdown, then you probably need to post_process as well,
# such as removing references that the current user doesn't have
# permission to make
def self.render_and_post_process(text, context = {})
post_process(render(text, context), context)
end
def self.render(text, context = {}) def self.render(text, context = {})
Renderer.render(text, context) Renderer.render(text, context)
end end
......
...@@ -215,7 +215,7 @@ module Banzai ...@@ -215,7 +215,7 @@ module Banzai
# #
def projects_for_nodes(nodes) def projects_for_nodes(nodes)
@projects_for_nodes ||= @projects_for_nodes ||=
grouped_objects_for_nodes(nodes, Project, 'data-project') grouped_objects_for_nodes(nodes, Project.includes(:project_feature), 'data-project')
end end
def can?(user, permission, subject = :global) def can?(user, permission, subject = :global)
......
# frozen_string_literal: true # frozen_string_literal: true
# rubocop:disable Style/Documentation # rubocop:disable Style/Documentation
# rubocop:disable Metrics/ClassLength
module Gitlab module Gitlab
module BackgroundMigration module BackgroundMigration
...@@ -49,11 +50,14 @@ module Gitlab ...@@ -49,11 +50,14 @@ module Gitlab
private private
def remove_non_members_todos(project_id) def remove_non_members_todos(project_id)
Todo.where(project_id: project_id) if Gitlab::Database.postgresql?
.where('user_id NOT IN (?)', authorized_users(project_id)) batch_remove_todos_cte(project_id)
.each_batch(of: 5000) do |batch| else
batch.delete_all unauthorized_project_todos(project_id)
end .each_batch(of: 5000) do |batch|
batch.delete_all
end
end
end end
def remove_confidential_issue_todos(project_id) def remove_confidential_issue_todos(project_id)
...@@ -86,10 +90,13 @@ module Gitlab ...@@ -86,10 +90,13 @@ module Gitlab
next if target_types.empty? next if target_types.empty?
Todo.where(project_id: project_id) if Gitlab::Database.postgresql?
.where('user_id NOT IN (?)', authorized_users(project_id)) batch_remove_todos_cte(project_id, target_types)
.where(target_type: target_types) else
.delete_all unauthorized_project_todos(project_id)
.where(target_type: target_types)
.delete_all
end
end end
end end
...@@ -100,6 +107,65 @@ module Gitlab ...@@ -100,6 +107,65 @@ module Gitlab
def authorized_users(project_id) def authorized_users(project_id)
ProjectAuthorization.select(:user_id).where(project_id: project_id) ProjectAuthorization.select(:user_id).where(project_id: project_id)
end end
def unauthorized_project_todos(project_id)
Todo.where(project_id: project_id)
.where('user_id NOT IN (?)', authorized_users(project_id))
end
def batch_remove_todos_cte(project_id, target_types = nil)
loop do
count = remove_todos_cte(project_id, target_types)
break if count == 0
end
end
def remove_todos_cte(project_id, target_types = nil)
sql = []
sql << with_all_todos_sql(project_id, target_types)
sql << as_deleted_sql
sql << "SELECT count(*) FROM deleted"
result = Todo.connection.exec_query(sql.join(' '))
result.rows[0][0].to_i
end
def with_all_todos_sql(project_id, target_types = nil)
if target_types
table = Arel::Table.new(:todos)
in_target = table[:target_type].in(target_types)
target_types_sql = " AND #{in_target.to_sql}"
end
<<-SQL
WITH all_todos AS (
SELECT id
FROM "todos"
WHERE "todos"."project_id" = #{project_id}
AND (user_id NOT IN (
SELECT "project_authorizations"."user_id"
FROM "project_authorizations"
WHERE "project_authorizations"."project_id" = #{project_id})
#{target_types_sql}
)
),
SQL
end
def as_deleted_sql
<<-SQL
deleted AS (
DELETE FROM todos
WHERE id IN (
SELECT id
FROM all_todos
LIMIT 5000
)
RETURNING id
)
SQL
end
end end
end end
end end
...@@ -25,8 +25,9 @@ ...@@ -25,8 +25,9 @@
# level, or manually added below. # level, or manually added below.
# #
# Continuous deployment to production is enabled by default. # Continuous deployment to production is enabled by default.
# If you want to deploy to staging first, or enable incremental rollouts, # If you want to deploy to staging first, set STAGING_ENABLED environment variable.
# set STAGING_ENABLED or INCREMENTAL_ROLLOUT_ENABLED environment variables. # If you want to enable incremental rollout, either manual or time based,
# set INCREMENTAL_ROLLOUT_TYPE environment variable to "manual" or "timed".
# If you want to use canary deployments, set CANARY_ENABLED environment variable. # If you want to use canary deployments, set CANARY_ENABLED environment variable.
# #
# If Auto DevOps fails to detect the proper buildpack, or if you want to # If Auto DevOps fails to detect the proper buildpack, or if you want to
...@@ -61,6 +62,10 @@ stages: ...@@ -61,6 +62,10 @@ stages:
- staging - staging
- canary - canary
- production - production
- incremental rollout 10%
- incremental rollout 25%
- incremental rollout 50%
- incremental rollout 100%
- performance - performance
- cleanup - cleanup
...@@ -282,11 +287,6 @@ stop_review: ...@@ -282,11 +287,6 @@ stop_review:
variables: variables:
- $REVIEW_DISABLED - $REVIEW_DISABLED
# Keys that start with a dot (.) will not be processed by GitLab CI.
# Staging and canary jobs are disabled by default, to enable them
# remove the dot (.) before the job name.
# https://docs.gitlab.com/ee/ci/yaml/README.html#hidden-keys
# Staging deploys are disabled by default since # Staging deploys are disabled by default since
# continuous deployment to production is enabled by default # continuous deployment to production is enabled by default
# If you prefer to automatically deploy to staging and # If you prefer to automatically deploy to staging and
...@@ -368,6 +368,7 @@ production: ...@@ -368,6 +368,7 @@ production:
- $STAGING_ENABLED - $STAGING_ENABLED
- $CANARY_ENABLED - $CANARY_ENABLED
- $INCREMENTAL_ROLLOUT_ENABLED - $INCREMENTAL_ROLLOUT_ENABLED
- $INCREMENTAL_ROLLOUT_MODE
production_manual: production_manual:
<<: *production_template <<: *production_template
...@@ -383,11 +384,11 @@ production_manual: ...@@ -383,11 +384,11 @@ production_manual:
except: except:
variables: variables:
- $INCREMENTAL_ROLLOUT_ENABLED - $INCREMENTAL_ROLLOUT_ENABLED
- $INCREMENTAL_ROLLOUT_MODE
# This job implements incremental rollout on for every push to `master`. # This job implements incremental rollout on for every push to `master`.
.rollout: &rollout_template .rollout: &rollout_template
stage: production
script: script:
- check_kube_domain - check_kube_domain
- install_dependencies - install_dependencies
...@@ -405,52 +406,77 @@ production_manual: ...@@ -405,52 +406,77 @@ production_manual:
artifacts: artifacts:
paths: [environment_url.txt] paths: [environment_url.txt]
rollout 10%: .manual_rollout_template: &manual_rollout_template
<<: *rollout_template <<: *rollout_template
variables: stage: production
ROLLOUT_PERCENTAGE: 10
when: manual when: manual
# This selectors are backward compatible mode with $INCREMENTAL_ROLLOUT_ENABLED (before 11.4)
only: only:
refs: refs:
- master - master
kubernetes: active kubernetes: active
variables: variables:
- $INCREMENTAL_ROLLOUT_MODE == "manual"
- $INCREMENTAL_ROLLOUT_ENABLED - $INCREMENTAL_ROLLOUT_ENABLED
except:
variables:
- $INCREMENTAL_ROLLOUT_MODE == "timed"
rollout 25%: .timed_rollout_template: &timed_rollout_template
<<: *rollout_template <<: *rollout_template
variables: when: delayed
ROLLOUT_PERCENTAGE: 25 start_in: 5 minutes
when: manual
only: only:
refs: refs:
- master - master
kubernetes: active kubernetes: active
variables: variables:
- $INCREMENTAL_ROLLOUT_ENABLED - $INCREMENTAL_ROLLOUT_MODE == "timed"
timed rollout 10%:
<<: *timed_rollout_template
stage: incremental rollout 10%
variables:
ROLLOUT_PERCENTAGE: 10
timed rollout 25%:
<<: *timed_rollout_template
stage: incremental rollout 25%
variables:
ROLLOUT_PERCENTAGE: 25
timed rollout 50%:
<<: *timed_rollout_template
stage: incremental rollout 50%
variables:
ROLLOUT_PERCENTAGE: 50
timed rollout 100%:
<<: *timed_rollout_template
<<: *production_template
stage: incremental rollout 100%
variables:
ROLLOUT_PERCENTAGE: 100
rollout 10%:
<<: *manual_rollout_template
variables:
ROLLOUT_PERCENTAGE: 10
rollout 25%:
<<: *manual_rollout_template
variables:
ROLLOUT_PERCENTAGE: 25
rollout 50%: rollout 50%:
<<: *rollout_template <<: *manual_rollout_template
variables: variables:
ROLLOUT_PERCENTAGE: 50 ROLLOUT_PERCENTAGE: 50
when: manual
only:
refs:
- master
kubernetes: active
variables:
- $INCREMENTAL_ROLLOUT_ENABLED
rollout 100%: rollout 100%:
<<: *manual_rollout_template
<<: *production_template <<: *production_template
when: manual
allow_failure: false allow_failure: false
only:
refs:
- master
kubernetes: active
variables:
- $INCREMENTAL_ROLLOUT_ENABLED
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
......
...@@ -1297,6 +1297,9 @@ msgstr "" ...@@ -1297,6 +1297,9 @@ msgstr ""
msgid "CICD|Continuous deployment to production" msgid "CICD|Continuous deployment to production"
msgstr "" msgstr ""
msgid "CICD|Continuous deployment to production using timed incremental rollout"
msgstr ""
msgid "CICD|Default to Auto DevOps pipeline" msgid "CICD|Default to Auto DevOps pipeline"
msgstr "" msgstr ""
......
...@@ -42,4 +42,15 @@ describe Admin::ProjectsController do ...@@ -42,4 +42,15 @@ describe Admin::ProjectsController do
expect { get :index }.not_to exceed_query_limit(control_count) expect { get :index }.not_to exceed_query_limit(control_count)
end end
end end
describe 'GET /projects/:id' do
render_views
it 'renders show page' do
get :show, namespace_id: project.namespace.path, id: project.path
expect(response).to have_gitlab_http_status(200)
expect(response.body).to match(project.name)
end
end
end end
...@@ -234,8 +234,8 @@ describe GroupsController do ...@@ -234,8 +234,8 @@ describe GroupsController do
end end
describe 'GET #issues' do describe 'GET #issues' do
let(:issue_1) { create(:issue, project: project) } let(:issue_1) { create(:issue, project: project, title: 'foo') }
let(:issue_2) { create(:issue, project: project) } let(:issue_2) { create(:issue, project: project, title: 'bar') }
before do before do
create_list(:award_emoji, 3, awardable: issue_2) create_list(:award_emoji, 3, awardable: issue_2)
...@@ -256,6 +256,31 @@ describe GroupsController do ...@@ -256,6 +256,31 @@ describe GroupsController do
expect(assigns(:issues)).to eq [issue_2, issue_1] expect(assigns(:issues)).to eq [issue_2, issue_1]
end end
end end
context 'searching' do
# Remove as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/52271
before do
stub_feature_flags(use_cte_for_group_issues_search: false)
end
it 'works with popularity sort' do
get :issues, id: group.to_param, search: 'foo', sort: 'popularity'
expect(assigns(:issues)).to eq([issue_1])
end
it 'works with priority sort' do
get :issues, id: group.to_param, search: 'foo', sort: 'priority'
expect(assigns(:issues)).to eq([issue_1])
end
it 'works with label priority sort' do
get :issues, id: group.to_param, search: 'foo', sort: 'label_priority'
expect(assigns(:issues)).to eq([issue_1])
end
end
end end
describe 'GET #merge_requests' do describe 'GET #merge_requests' do
......
...@@ -19,15 +19,17 @@ describe Projects::ArtifactsController do ...@@ -19,15 +19,17 @@ describe Projects::ArtifactsController do
end end
describe 'GET download' do describe 'GET download' do
subject { get :download, namespace_id: project.namespace, project_id: project, job_id: job, file_type: file_type } def download_artifact(extra_params = {})
params = { namespace_id: project.namespace, project_id: project, job_id: job }.merge(extra_params)
context 'when no file type is supplied' do get :download, params
let(:file_type) { nil } end
context 'when no file type is supplied' do
it 'sends the artifacts file' do it 'sends the artifacts file' do
expect(controller).to receive(:send_file).with(job.artifacts_file.path, hash_including(disposition: 'attachment')).and_call_original expect(controller).to receive(:send_file).with(job.artifacts_file.path, hash_including(disposition: 'attachment')).and_call_original
subject download_artifact
end end
end end
...@@ -36,7 +38,7 @@ describe Projects::ArtifactsController do ...@@ -36,7 +38,7 @@ describe Projects::ArtifactsController do
let(:file_type) { 'invalid' } let(:file_type) { 'invalid' }
it 'returns 404' do it 'returns 404' do
subject download_artifact(file_type: file_type)
expect(response).to have_gitlab_http_status(404) expect(response).to have_gitlab_http_status(404)
end end
...@@ -52,7 +54,7 @@ describe Projects::ArtifactsController do ...@@ -52,7 +54,7 @@ describe Projects::ArtifactsController do
it 'sends the codequality report' do it 'sends the codequality report' do
expect(controller).to receive(:send_file).with(job.job_artifacts_codequality.file.path, hash_including(disposition: 'attachment')).and_call_original expect(controller).to receive(:send_file).with(job.job_artifacts_codequality.file.path, hash_including(disposition: 'attachment')).and_call_original
subject download_artifact(file_type: file_type)
end end
end end
end end
......
...@@ -763,25 +763,35 @@ describe Projects::MergeRequestsController do ...@@ -763,25 +763,35 @@ describe Projects::MergeRequestsController do
describe 'GET ci_environments_status' do describe 'GET ci_environments_status' do
context 'the environment is from a forked project' do context 'the environment is from a forked project' do
let!(:forked) { fork_project(project, user, repository: true) } let!(:forked) { fork_project(project, user, repository: true) }
let!(:environment) { create(:environment, project: forked) } let!(:environment) { create(:environment, project: forked) }
let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') } let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:merge_request) do let(:merge_request) do
create(:merge_request, source_project: forked, target_project: project) create(:merge_request, source_project: forked, target_project: project)
end end
before do it 'links to the environment on that project' do
get_ci_environments_status
expect(json_response.first['url']).to match /#{forked.full_path}/
end
# we're trying to reduce the overall number of queries for this method.
# set a hard limit for now. https://gitlab.com/gitlab-org/gitlab-ce/issues/52287
it 'keeps queries in check' do
control_count = ActiveRecord::QueryRecorder.new { get_ci_environments_status }.count
expect(control_count).to be <= 137
end
def get_ci_environments_status
get :ci_environments_status, get :ci_environments_status,
namespace_id: merge_request.project.namespace.to_param, namespace_id: merge_request.project.namespace.to_param,
project_id: merge_request.project, project_id: merge_request.project,
id: merge_request.iid, format: 'json' id: merge_request.iid, format: 'json'
end end
it 'links to the environment on that project' do
expect(json_response.first['url']).to match /#{forked.full_path}/
end
end end
end end
......
...@@ -5,8 +5,16 @@ FactoryBot.define do ...@@ -5,8 +5,16 @@ FactoryBot.define do
domain "example.com" domain "example.com"
deploy_strategy :continuous deploy_strategy :continuous
trait :manual do trait :continuous_deployment do
deploy_strategy :manual deploy_strategy ProjectAutoDevops.deploy_strategies[:continuous] # rubocop:disable FactoryBot/DynamicAttributeDefinedStatically
end
trait :manual_deployment do
deploy_strategy ProjectAutoDevops.deploy_strategies[:manual] # rubocop:disable FactoryBot/DynamicAttributeDefinedStatically
end
trait :timed_incremental_deployment do
deploy_strategy ProjectAutoDevops.deploy_strategies[:timed_incremental] # rubocop:disable FactoryBot/DynamicAttributeDefinedStatically
end end
trait :disabled do trait :disabled do
......
require_relative '../support/helpers/test_env' require_relative '../support/helpers/test_env'
FactoryBot.define do FactoryBot.define do
PAGES_ACCESS_LEVEL_SCHEMA_VERSION = 20180423204600
# Project without repository # Project without repository
# #
# Project does not have bare repository. # Project does not have bare repository.
...@@ -23,6 +25,7 @@ FactoryBot.define do ...@@ -23,6 +25,7 @@ FactoryBot.define do
issues_access_level ProjectFeature::ENABLED issues_access_level ProjectFeature::ENABLED
merge_requests_access_level ProjectFeature::ENABLED merge_requests_access_level ProjectFeature::ENABLED
repository_access_level ProjectFeature::ENABLED repository_access_level ProjectFeature::ENABLED
pages_access_level ProjectFeature::ENABLED
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the # we can't assign the delegated `#ci_cd_settings` attributes directly, as the
# `#ci_cd_settings` relation needs to be created first # `#ci_cd_settings` relation needs to be created first
...@@ -34,13 +37,20 @@ FactoryBot.define do ...@@ -34,13 +37,20 @@ FactoryBot.define do
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
project.project_feature.update( hash = {
wiki_access_level: evaluator.wiki_access_level, wiki_access_level: evaluator.wiki_access_level,
builds_access_level: builds_access_level, builds_access_level: builds_access_level,
snippets_access_level: evaluator.snippets_access_level, snippets_access_level: evaluator.snippets_access_level,
issues_access_level: evaluator.issues_access_level, issues_access_level: evaluator.issues_access_level,
merge_requests_access_level: merge_requests_access_level, merge_requests_access_level: merge_requests_access_level,
repository_access_level: evaluator.repository_access_level) repository_access_level: evaluator.repository_access_level
}
if ActiveRecord::Migrator.current_version >= PAGES_ACCESS_LEVEL_SCHEMA_VERSION
hash.store("pages_access_level", evaluator.pages_access_level)
end
project.project_feature.update(hash)
# Normally the class Projects::CreateService is used for creating # Normally the class Projects::CreateService is used for creating
# projects, and this class takes care of making sure the owner and current # projects, and this class takes care of making sure the owner and current
...@@ -287,6 +297,10 @@ FactoryBot.define do ...@@ -287,6 +297,10 @@ FactoryBot.define do
trait(:repository_enabled) { repository_access_level ProjectFeature::ENABLED } trait(:repository_enabled) { repository_access_level ProjectFeature::ENABLED }
trait(:repository_disabled) { repository_access_level ProjectFeature::DISABLED } trait(:repository_disabled) { repository_access_level ProjectFeature::DISABLED }
trait(:repository_private) { repository_access_level ProjectFeature::PRIVATE } trait(:repository_private) { repository_access_level ProjectFeature::PRIVATE }
trait(:pages_public) { pages_access_level ProjectFeature::PUBLIC }
trait(:pages_enabled) { pages_access_level ProjectFeature::ENABLED }
trait(:pages_disabled) { pages_access_level ProjectFeature::DISABLED }
trait(:pages_private) { pages_access_level ProjectFeature::PRIVATE }
trait :auto_devops do trait :auto_devops do
association :auto_devops, factory: :project_auto_devops association :auto_devops, factory: :project_auto_devops
......
...@@ -568,12 +568,17 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do ...@@ -568,12 +568,17 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end end
it 'shows delayed job', :js do it 'shows delayed job', :js do
<<<<<<< HEAD
time_diff = [0, job.scheduled_at - Time.now].max time_diff = [0, job.scheduled_at - Time.now].max
expect(page).to have_content(job.detailed_status(user).illustration[:title]) expect(page).to have_content(job.detailed_status(user).illustration[:title])
expect(page).to have_content('This is a scheduled to run in') expect(page).to have_content('This is a scheduled to run in')
expect(page).to have_content("This job will automatically run after it's timer finishes.") expect(page).to have_content("This job will automatically run after it's timer finishes.")
expect(page).to have_content(Time.at(time_diff).utc.strftime("%H:%M:%S")) expect(page).to have_content(Time.at(time_diff).utc.strftime("%H:%M:%S"))
=======
expect(page).to have_content('This is a scheduled to run in')
expect(page).to have_content("This job will automatically run after it's timer finishes.")
>>>>>>> upstream/master
expect(page).to have_link('Unschedule job') expect(page).to have_link('Unschedule job')
end end
......
{
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": ["string"] }
},
"additionalProperties": false
}
{
"type": "object",
"required": [
"id",
"state",
"avatar_url",
"path",
"name",
"username"
],
"properties": {
"id": { "type": "integer" },
"state": { "type": "string" },
"avatar_url": { "type": "string" },
"path": { "type": "string" },
"name": { "type": "string" },
"username": { "type": "string" },
"status_tooltip_html": { "$ref": "../types/nullable_string.json" }
},
"additionalProperties": false
}
...@@ -10,7 +10,7 @@ describe Types::PermissionTypes::Project do ...@@ -10,7 +10,7 @@ describe Types::PermissionTypes::Project do
:read_commit_status, :request_access, :create_pipeline, :create_pipeline_schedule, :read_commit_status, :request_access, :create_pipeline, :create_pipeline_schedule,
:create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch, :create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label, :admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label,
:update_wiki, :destroy_wiki, :create_pages, :destroy_pages :update_wiki, :destroy_wiki, :create_pages, :destroy_pages, :read_pages_content
] ]
expect(described_class).to have_graphql_fields(expected_permissions) expect(described_class).to have_graphql_fields(expected_permissions)
......
...@@ -8,13 +8,12 @@ import diffFileMockData from '../mock_data/diff_file'; ...@@ -8,13 +8,12 @@ import diffFileMockData from '../mock_data/diff_file';
describe('DiffContent', () => { describe('DiffContent', () => {
const Component = Vue.extend(DiffContentComponent); const Component = Vue.extend(DiffContentComponent);
let vm; let vm;
const getDiffFileMock = () => Object.assign({}, diffFileMockData);
beforeEach(() => { beforeEach(() => {
vm = mountComponentWithStore(Component, { vm = mountComponentWithStore(Component, {
store, store,
props: { props: {
diffFile: getDiffFileMock(), diffFile: JSON.parse(JSON.stringify(diffFileMockData)),
}, },
}); });
}); });
...@@ -43,7 +42,7 @@ describe('DiffContent', () => { ...@@ -43,7 +42,7 @@ describe('DiffContent', () => {
describe('Non-Text diffs', () => { describe('Non-Text diffs', () => {
beforeEach(() => { beforeEach(() => {
vm.diffFile.text = false; vm.diffFile.viewer.name = 'image';
}); });
describe('image diff', () => { describe('image diff', () => {
......
...@@ -6,11 +6,10 @@ import diffFileMockData from '../mock_data/diff_file'; ...@@ -6,11 +6,10 @@ import diffFileMockData from '../mock_data/diff_file';
describe('DiffFile', () => { describe('DiffFile', () => {
let vm; let vm;
const getDiffFileMock = () => Object.assign({}, diffFileMockData);
beforeEach(() => { beforeEach(() => {
vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, { vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, {
file: getDiffFileMock(), file: JSON.parse(JSON.stringify(diffFileMockData)),
canCurrentUserFork: false, canCurrentUserFork: false,
}).$mount(); }).$mount();
}); });
...@@ -18,7 +17,7 @@ describe('DiffFile', () => { ...@@ -18,7 +17,7 @@ describe('DiffFile', () => {
describe('template', () => { describe('template', () => {
it('should render component with file header, file content components', () => { it('should render component with file header, file content components', () => {
const el = vm.$el; const el = vm.$el;
const { fileHash, filePath } = diffFileMockData; const { fileHash, filePath } = vm.file;
expect(el.id).toEqual(fileHash); expect(el.id).toEqual(fileHash);
expect(el.classList.contains('diff-file')).toEqual(true); expect(el.classList.contains('diff-file')).toEqual(true);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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