Merge remote-tracking branch 'origin/master' into 22191-delete-dynamic-envs-mr
This commit is contained in:
commit
19300a1a3d
|
@ -2,6 +2,8 @@ Please view this file on the master branch, on stable branches it's out of date.
|
|||
|
||||
## 8.13.0 (2016-10-22)
|
||||
|
||||
- Fix save button on project pipeline settings page. (!6955)
|
||||
- Avoid race condition when asynchronously removing expired artifacts. (!6881)
|
||||
- Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675)
|
||||
- Respond with 404 Not Found for non-existent tags (Linus Thiel)
|
||||
- Truncate long labels with ellipsis in labels page
|
||||
|
@ -16,6 +18,7 @@ Please view this file on the master branch, on stable branches it's out of date.
|
|||
- Fix centering of custom header logos (Ashley Dumaine)
|
||||
- ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup
|
||||
- Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun)
|
||||
- Cancelled pipelines could be retried. !6927
|
||||
- Updating verbiage on git basics to be more intuitive
|
||||
- Clarify documentation for Runners API (Gennady Trafimenkov)
|
||||
- The instrumentation for Banzai::Renderer has been restored
|
||||
|
@ -53,6 +56,7 @@ Please view this file on the master branch, on stable branches it's out of date.
|
|||
- Add Issue Board API support (andrebsguedes)
|
||||
- Allow the Koding integration to be configured through the API
|
||||
- Add new issue button to each list on Issues Board
|
||||
- Execute specific named route method from toggle_award_url helper method
|
||||
- Added soft wrap button to repository file/blob editor
|
||||
- Update namespace validation to forbid reserved names (.git and .atom) (Will Starms)
|
||||
- Show the time ago a merge request was deployed to an environment
|
||||
|
@ -62,6 +66,7 @@ Please view this file on the master branch, on stable branches it's out of date.
|
|||
- Fix inconsistent highlighting of already selected activity nav-links (ClemMakesApps)
|
||||
- Remove redundant mixins (ClemMakesApps)
|
||||
- Added 'Download' button to the Snippets page (Justin DiPierro)
|
||||
- Add visibility level to project repository
|
||||
- Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison)
|
||||
- Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska)
|
||||
- Fix that manual jobs would no longer block jobs in the next stage. !6604
|
||||
|
@ -104,6 +109,7 @@ Please view this file on the master branch, on stable branches it's out of date.
|
|||
- Reduce queries needed to find users using their SSH keys when pushing commits
|
||||
- Prevent rendering the link to all when the author has no access (Katarzyna Kobierska Ula Budziszewska)
|
||||
- Fix broken repository 500 errors in project list
|
||||
- Fix the diff in the merge request view when converting a symlink to a regular file
|
||||
- Fix Pipeline list commit column width should be adjusted
|
||||
- Close todos when accepting merge requests via the API !6486 (tonygambone)
|
||||
- Ability to batch assign issues relating to a merge request to the author. !5725 (jamedjo)
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
this.ProjectNew = (function() {
|
||||
function ProjectNew() {
|
||||
this.toggleSettings = bind(this.toggleSettings, this);
|
||||
this.$selects = $('.features select').filter(function () {
|
||||
return $(this).data('field');
|
||||
});
|
||||
this.$selects = $('.features select');
|
||||
this.$repoSelects = this.$selects.filter('.js-repo-select');
|
||||
|
||||
$('.project-edit-container').on('ajax:before', (function(_this) {
|
||||
return function() {
|
||||
|
@ -16,6 +15,7 @@
|
|||
})(this));
|
||||
this.toggleSettings();
|
||||
this.toggleSettingsOnclick();
|
||||
this.toggleRepoVisibility();
|
||||
}
|
||||
|
||||
ProjectNew.prototype.toggleSettings = function() {
|
||||
|
@ -43,6 +43,38 @@
|
|||
}
|
||||
};
|
||||
|
||||
ProjectNew.prototype.toggleRepoVisibility = function () {
|
||||
var $repoAccessLevel = $('.js-repo-access-level select');
|
||||
|
||||
this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']")
|
||||
.nextAll()
|
||||
.hide();
|
||||
|
||||
$repoAccessLevel.off('change')
|
||||
.on('change', function () {
|
||||
var selectedVal = parseInt($repoAccessLevel.val());
|
||||
|
||||
this.$repoSelects.each(function () {
|
||||
var $this = $(this),
|
||||
repoSelectVal = parseInt($this.val());
|
||||
|
||||
$this.find('option').show();
|
||||
|
||||
if (selectedVal < repoSelectVal) {
|
||||
$this.val(selectedVal);
|
||||
}
|
||||
|
||||
$this.find("option[value='" + selectedVal + "']").nextAll().hide();
|
||||
});
|
||||
|
||||
if (selectedVal) {
|
||||
this.$repoSelects.removeClass('disabled');
|
||||
} else {
|
||||
this.$repoSelects.addClass('disabled');
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
return ProjectNew;
|
||||
|
||||
})();
|
||||
|
|
|
@ -761,62 +761,6 @@ pre.light-well {
|
|||
.dropdown-menu {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
&.from .compare-dropdown-toggle {
|
||||
width: 237px;
|
||||
}
|
||||
|
||||
&.to .compare-dropdown-toggle {
|
||||
width: 254px;
|
||||
}
|
||||
|
||||
.dropdown-toggle-text {
|
||||
display: block;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.compare-ellipsis {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.compare-form-group {
|
||||
.input-group {
|
||||
width: 100%;
|
||||
|
||||
& > .compare-dropdown-toggle {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.compare-switch-container {
|
||||
text-align: center;
|
||||
padding: 0 0 $gl-padding;
|
||||
|
||||
.commits-compare-switch {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
.compare-ellipsis {
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding: 0 0 $gl-padding;
|
||||
}
|
||||
|
||||
.commits-compare-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.clearable-input {
|
||||
|
@ -855,3 +799,30 @@ pre.light-well {
|
|||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.project-home-empty {
|
||||
border-top: 0;
|
||||
|
||||
.container-fluid {
|
||||
background: none;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 650px;
|
||||
}
|
||||
}
|
||||
|
||||
.project-feature-nested {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
padding-left: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
.project-repo-select {
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
class ProjectsController < Projects::ApplicationController
|
||||
include IssuableCollections
|
||||
include ExtractsPath
|
||||
|
||||
before_action :authenticate_user!, except: [:show, :activity, :refs]
|
||||
|
@ -103,16 +104,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
respond_to do |format|
|
||||
format.html do
|
||||
@notification_setting = current_user.notification_settings_for(@project) if current_user
|
||||
|
||||
if @project.repository_exists?
|
||||
if @project.empty_repo?
|
||||
render 'projects/empty'
|
||||
else
|
||||
render :show
|
||||
end
|
||||
else
|
||||
render 'projects/no_repo'
|
||||
end
|
||||
render_landing_page
|
||||
end
|
||||
|
||||
format.atom do
|
||||
|
@ -285,6 +277,26 @@ class ProjectsController < Projects::ApplicationController
|
|||
|
||||
private
|
||||
|
||||
# Render project landing depending of which features are available
|
||||
# So if page is not availble in the list it renders the next page
|
||||
#
|
||||
# pages list order: repository readme, wiki home, issues list, customize workflow
|
||||
def render_landing_page
|
||||
if @project.feature_available?(:repository, current_user)
|
||||
return render 'projects/no_repo' unless @project.repository_exists?
|
||||
render 'projects/empty' if @project.empty_repo?
|
||||
else
|
||||
if @project.wiki_enabled?
|
||||
@wiki_home = @project.wiki.find_page('home', params[:version_id])
|
||||
elsif @project.feature_available?(:issues, current_user)
|
||||
@issues = issues_collection
|
||||
@issues = @issues.page(params[:page])
|
||||
end
|
||||
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
def determine_layout
|
||||
if [:new, :create].include?(action_name.to_sym)
|
||||
'application'
|
||||
|
@ -308,7 +320,8 @@ class ProjectsController < Projects::ApplicationController
|
|||
project_feature_attributes:
|
||||
[
|
||||
:issues_access_level, :builds_access_level,
|
||||
:wiki_access_level, :merge_requests_access_level, :snippets_access_level
|
||||
:wiki_access_level, :merge_requests_access_level,
|
||||
:snippets_access_level, :repository_access_level
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
module AwardEmojiHelper
|
||||
def toggle_award_url(awardable)
|
||||
if @project
|
||||
url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable])
|
||||
return url_for([:toggle_award_emoji, awardable]) unless @project
|
||||
|
||||
if awardable.is_a?(Note)
|
||||
# We render a list of notes very frequently and calling the specific method is a lot faster than the generic one (6.5x)
|
||||
toggle_award_emoji_namespace_project_note_url(namespace_id: @project.namespace_id, project_id: @project.id, id: awardable.id)
|
||||
else
|
||||
url_for([:toggle_award_emoji, awardable])
|
||||
url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,6 +50,20 @@ module PreferencesHelper
|
|||
end
|
||||
|
||||
def default_project_view
|
||||
current_user ? current_user.project_view : 'readme'
|
||||
return 'readme' unless current_user
|
||||
|
||||
user_view = current_user.project_view
|
||||
|
||||
if @project.feature_available?(:repository, current_user)
|
||||
user_view
|
||||
elsif user_view == "activity"
|
||||
"activity"
|
||||
elsif @project.wiki_enabled?
|
||||
"wiki"
|
||||
elsif @project.feature_available?(:issues, current_user)
|
||||
"projects/issues/issues"
|
||||
else
|
||||
"customize_workflow"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -134,16 +134,35 @@ module ProjectsHelper
|
|||
options = project_feature_options
|
||||
|
||||
if @project.private?
|
||||
level = @project.project_feature.send(field)
|
||||
options.delete('Everyone with access')
|
||||
highest_available_option = options.values.max if @project.project_feature.send(field) == ProjectFeature::ENABLED
|
||||
highest_available_option = options.values.max if level == ProjectFeature::ENABLED
|
||||
end
|
||||
|
||||
options = options_for_select(options, selected: highest_available_option || @project.project_feature.public_send(field))
|
||||
content_tag(:select, options, name: "project[project_feature_attributes][#{field}]", id: "project_project_feature_attributes_#{field}", class: "pull-right form-control", data: { field: field }).html_safe
|
||||
|
||||
content_tag(
|
||||
:select,
|
||||
options,
|
||||
name: "project[project_feature_attributes][#{field}]",
|
||||
id: "project_project_feature_attributes_#{field}",
|
||||
class: "pull-right form-control #{repo_children_classes(field)}",
|
||||
data: { field: field }
|
||||
).html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def repo_children_classes(field)
|
||||
needs_repo_check = [:merge_requests_access_level, :builds_access_level]
|
||||
return unless needs_repo_check.include?(field)
|
||||
|
||||
classes = "project-repo-select js-repo-select"
|
||||
classes << " disabled" unless @project.feature_available?(:repository, current_user)
|
||||
|
||||
classes
|
||||
end
|
||||
|
||||
def get_project_nav_tabs(project, current_user)
|
||||
nav_tabs = [:home]
|
||||
|
||||
|
@ -155,12 +174,8 @@ module ProjectsHelper
|
|||
nav_tabs << :merge_requests
|
||||
end
|
||||
|
||||
if can?(current_user, :read_pipeline, project)
|
||||
nav_tabs << :pipelines
|
||||
end
|
||||
|
||||
if can?(current_user, :read_build, project)
|
||||
nav_tabs << :builds
|
||||
nav_tabs << :pipelines
|
||||
end
|
||||
|
||||
if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project)
|
||||
|
@ -435,4 +450,8 @@ module ProjectsHelper
|
|||
'Everyone with access' => ProjectFeature::ENABLED
|
||||
}
|
||||
end
|
||||
|
||||
def project_child_container_class(view_path)
|
||||
view_path == "projects/issues/issues" ? "prepend-top-default" : "project-show-#{view_path}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -154,7 +154,7 @@ module Ci
|
|||
|
||||
def retryable?
|
||||
builds.latest.any? do |build|
|
||||
build.failed? && build.retryable?
|
||||
(build.failed? || build.canceled?) && build.retryable?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -13,23 +13,26 @@ class ProjectFeature < ActiveRecord::Base
|
|||
# Enabled: enabled for everyone able to access the project
|
||||
#
|
||||
|
||||
# Permision levels
|
||||
# Permission levels
|
||||
DISABLED = 0
|
||||
PRIVATE = 10
|
||||
ENABLED = 20
|
||||
|
||||
FEATURES = %i(issues merge_requests wiki snippets builds)
|
||||
FEATURES = %i(issues merge_requests wiki snippets builds repository)
|
||||
|
||||
# Default scopes force us to unscope here since a service may need to check
|
||||
# permissions for a project in pending_delete
|
||||
# http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to
|
||||
belongs_to :project, -> { unscope(where: :pending_delete) }
|
||||
|
||||
validate :repository_children_level
|
||||
|
||||
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 :merge_requests_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
|
||||
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
|
||||
|
||||
def feature_available?(feature, user)
|
||||
raise ArgumentError, 'invalid project feature' unless FEATURES.include?(feature)
|
||||
|
@ -57,6 +60,18 @@ class ProjectFeature < ActiveRecord::Base
|
|||
|
||||
private
|
||||
|
||||
# Validates builds and merge requests access level
|
||||
# which cannot be higher than repository access level
|
||||
def repository_children_level
|
||||
validator = lambda do |field|
|
||||
level = public_send(field) || ProjectFeature::ENABLED
|
||||
not_allowed = level > repository_access_level
|
||||
self.errors.add(field, "cannot have higher visibility level than repository access level") if not_allowed
|
||||
end
|
||||
|
||||
%i(merge_requests_access_level builds_access_level).each(&validator)
|
||||
end
|
||||
|
||||
def get_permission(user, level)
|
||||
case level
|
||||
when DISABLED
|
||||
|
|
|
@ -162,11 +162,13 @@ class ProjectPolicy < BasePolicy
|
|||
end
|
||||
|
||||
def disabled_features!
|
||||
repository_enabled = project.feature_available?(:repository, user)
|
||||
|
||||
unless project.feature_available?(:issues, user)
|
||||
cannot!(*named_abilities(:issue))
|
||||
end
|
||||
|
||||
unless project.feature_available?(:merge_requests, user)
|
||||
unless project.feature_available?(:merge_requests, user) && repository_enabled
|
||||
cannot!(*named_abilities(:merge_request))
|
||||
end
|
||||
|
||||
|
@ -183,13 +185,21 @@ class ProjectPolicy < BasePolicy
|
|||
cannot!(*named_abilities(:wiki))
|
||||
end
|
||||
|
||||
unless project.feature_available?(:builds, user)
|
||||
unless project.feature_available?(:builds, user) && repository_enabled
|
||||
cannot!(*named_abilities(:build))
|
||||
cannot!(*named_abilities(:pipeline))
|
||||
cannot!(*named_abilities(:environment))
|
||||
cannot!(*named_abilities(:deployment))
|
||||
end
|
||||
|
||||
unless repository_enabled
|
||||
cannot! :push_code
|
||||
cannot! :push_code_to_protected_branches
|
||||
cannot! :download_code
|
||||
cannot! :fork_project
|
||||
cannot! :read_commit_status
|
||||
end
|
||||
|
||||
unless project.container_registry_enabled
|
||||
cannot!(*named_abilities(:container_image))
|
||||
end
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.row-content-block.project-home-empty
|
||||
%div.text-center{ class: container_class }
|
||||
%h4
|
||||
Customize your workflow!
|
||||
%p
|
||||
Get started with GitLab by enabling features that work best for your project. From issues and wikis, to merge requests and builds, GitLab can help manage your workflow from idea to production!
|
||||
- if can?(current_user, :admin_project, @project)
|
||||
= link_to "Get started", edit_project_path(@project), class: "btn btn-success"
|
|
@ -22,5 +22,6 @@
|
|||
= render 'projects/buttons/star'
|
||||
= render 'projects/buttons/fork'
|
||||
|
||||
.project-clone-holder
|
||||
= render "shared/clone_panel"
|
||||
- if @project.feature_available?(:repository, current_user)
|
||||
.project-clone-holder
|
||||
= render "shared/clone_panel"
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
- if @wiki_home.present?
|
||||
%div{ class: container_class }
|
||||
.wiki-holder.prepend-top-default.append-bottom-default
|
||||
.wiki
|
||||
= preserve do
|
||||
= render_wiki_content(@wiki_home)
|
||||
- else
|
||||
- can_create_wiki = can?(current_user, :create_wiki, @project)
|
||||
.project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] }
|
||||
%div.text-center{ class: container_class }
|
||||
%h4
|
||||
This project does not have a wiki homepage yet
|
||||
- if can_create_wiki
|
||||
%p
|
||||
Add a homepage to your wiki that contains information about your project
|
||||
%p
|
||||
We recommend you
|
||||
= link_to "add a homepage", namespace_project_wiki_path(@project.namespace, @project, :home)
|
||||
to your project's wiki and GitLab will show it here instead of this message.
|
|
@ -50,31 +50,25 @@
|
|||
.form_group.prepend-top-20
|
||||
.row
|
||||
.col-md-9
|
||||
= feature_fields.label :issues_access_level, "Issues", class: 'label-light'
|
||||
%span.help-block Lightweight issue tracking system for this project
|
||||
.col-md-3
|
||||
= project_feature_access_select(:issues_access_level)
|
||||
= feature_fields.label :repository_access_level, "Repository", class: 'label-light'
|
||||
%span.help-block Push files to be stored in this project
|
||||
.col-md-3.js-repo-access-level
|
||||
= project_feature_access_select(:repository_access_level)
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
= feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light'
|
||||
%span.help-block Submit changes to be merged upstream
|
||||
.col-md-3
|
||||
= project_feature_access_select(:merge_requests_access_level)
|
||||
.col-sm-12
|
||||
.row
|
||||
.col-md-9.project-feature-nested
|
||||
= feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light'
|
||||
%span.help-block Submit changes to be merged upstream
|
||||
.col-md-3
|
||||
= project_feature_access_select(:merge_requests_access_level)
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
= feature_fields.label :builds_access_level, "Builds", class: 'label-light'
|
||||
%span.help-block Submit Test and deploy your changes before merge
|
||||
.col-md-3
|
||||
= project_feature_access_select(:builds_access_level)
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
= feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
|
||||
%span.help-block Pages for project documentation
|
||||
.col-md-3
|
||||
= project_feature_access_select(:wiki_access_level)
|
||||
.row
|
||||
.col-md-9.project-feature-nested
|
||||
= feature_fields.label :builds_access_level, "Builds", class: 'label-light'
|
||||
%span.help-block Submit, test and deploy your changes before merge
|
||||
.col-md-3
|
||||
= project_feature_access_select(:builds_access_level)
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
|
@ -83,17 +77,31 @@
|
|||
.col-md-3
|
||||
= project_feature_access_select(:snippets_access_level)
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
= feature_fields.label :issues_access_level, "Issues", class: 'label-light'
|
||||
%span.help-block Lightweight issue tracking system for this project
|
||||
.col-md-3
|
||||
= project_feature_access_select(:issues_access_level)
|
||||
|
||||
.row
|
||||
.col-md-9
|
||||
= feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
|
||||
%span.help-block Pages for project documentation
|
||||
.col-md-3
|
||||
= project_feature_access_select(:wiki_access_level)
|
||||
|
||||
- if Gitlab.config.lfs.enabled && current_user.admin?
|
||||
.row
|
||||
.col-md-9
|
||||
= f.label :lfs_enabled, 'LFS', class: 'label-light'
|
||||
%span.help-block
|
||||
.checkbox
|
||||
= f.label :lfs_enabled do
|
||||
= f.check_box :lfs_enabled
|
||||
%strong LFS
|
||||
%br
|
||||
%span.descr
|
||||
Git Large File Storage
|
||||
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
|
||||
.col-md-3
|
||||
= f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control', data: { field: 'lfs_enabled' }
|
||||
|
||||
- if Gitlab.config.registry.enabled
|
||||
- if Gitlab.config.lfs.enabled && current_user.admin?
|
||||
.form-group
|
||||
.checkbox
|
||||
= f.label :container_registry_enabled do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
%ul.content-list.issues-list.issuable-list
|
||||
= render @issues
|
||||
= render partial: "projects/issues/issue", collection: @issues
|
||||
- if @issues.blank?
|
||||
%li
|
||||
.nothing-here-block No issues to show
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
.col-lg-9
|
||||
%h5.prepend-top-0
|
||||
Pipelines
|
||||
= form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project), remote: true, authenticity_token: true do |f|
|
||||
= form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project) do |f|
|
||||
%fieldset.builds-feature
|
||||
- unless @repository.gitlab_ci_yml
|
||||
.form-group
|
||||
|
|
|
@ -12,72 +12,74 @@
|
|||
= render 'projects/last_push'
|
||||
= render "home_panel"
|
||||
|
||||
%nav.project-stats{ class: (container_class) }
|
||||
%ul.nav
|
||||
%li
|
||||
= link_to project_files_path(@project) do
|
||||
Files (#{repository_size})
|
||||
%li
|
||||
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
|
||||
#{'Commit'.pluralize(@project.commit_count)} (#{number_with_delimiter(@project.commit_count)})
|
||||
%li
|
||||
= link_to namespace_project_branches_path(@project.namespace, @project) do
|
||||
#{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)})
|
||||
%li
|
||||
= link_to namespace_project_tags_path(@project.namespace, @project) do
|
||||
#{'Tag'.pluralize(@repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
|
||||
|
||||
- if default_project_view != 'readme' && @repository.readme
|
||||
- if @project.feature_available?(:repository, current_user)
|
||||
%nav.project-stats{ class: container_class }
|
||||
%ul.nav
|
||||
%li
|
||||
= link_to 'Readme', readme_path(@project)
|
||||
|
||||
- if @repository.changelog
|
||||
= link_to project_files_path(@project) do
|
||||
Files (#{repository_size})
|
||||
%li
|
||||
= link_to 'Changelog', changelog_path(@project)
|
||||
|
||||
- if @repository.license_blob
|
||||
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
|
||||
#{'Commit'.pluralize(@project.commit_count)} (#{number_with_delimiter(@project.commit_count)})
|
||||
%li
|
||||
= link_to license_short_name(@project), license_path(@project)
|
||||
|
||||
- if @repository.contribution_guide
|
||||
= link_to namespace_project_branches_path(@project.namespace, @project) do
|
||||
#{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)})
|
||||
%li
|
||||
= link_to 'Contribution guide', contribution_guide_path(@project)
|
||||
= link_to namespace_project_tags_path(@project.namespace, @project) do
|
||||
#{'Tag'.pluralize(@repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
|
||||
|
||||
- if @repository.gitlab_ci_yml
|
||||
%li
|
||||
= link_to 'CI configuration', ci_configuration_path(@project)
|
||||
- if default_project_view != 'readme' && @repository.readme
|
||||
%li
|
||||
= link_to 'Readme', readme_path(@project)
|
||||
|
||||
- if current_user && can_push_branch?(@project, @project.default_branch)
|
||||
- unless @repository.changelog
|
||||
%li.missing
|
||||
= link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
|
||||
Add Changelog
|
||||
- unless @repository.license_blob
|
||||
%li.missing
|
||||
= link_to add_special_file_path(@project, file_name: 'LICENSE') do
|
||||
Add License
|
||||
- unless @repository.contribution_guide
|
||||
%li.missing
|
||||
= link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
|
||||
Add Contribution guide
|
||||
- unless @repository.gitlab_ci_yml
|
||||
%li.missing
|
||||
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
|
||||
Set Up CI
|
||||
- if @repository.changelog
|
||||
%li
|
||||
= link_to 'Changelog', changelog_path(@project)
|
||||
|
||||
%li.project-repo-buttons-right
|
||||
.project-repo-buttons.project-right-buttons
|
||||
- if current_user
|
||||
= render 'shared/members/access_request_buttons', source: @project
|
||||
= render "projects/buttons/koding"
|
||||
- if @repository.license_blob
|
||||
%li
|
||||
= link_to license_short_name(@project), license_path(@project)
|
||||
|
||||
= render 'projects/buttons/download', project: @project, ref: @ref
|
||||
= render 'projects/buttons/dropdown'
|
||||
- if @repository.contribution_guide
|
||||
%li
|
||||
= link_to 'Contribution guide', contribution_guide_path(@project)
|
||||
|
||||
= render 'shared/notifications/button', notification_setting: @notification_setting
|
||||
- if @repository.commit
|
||||
.project-last-commit{ class: container_class }
|
||||
= render 'projects/last_commit', commit: @repository.commit, project: @project
|
||||
- if @repository.gitlab_ci_yml
|
||||
%li
|
||||
= link_to 'CI configuration', ci_configuration_path(@project)
|
||||
|
||||
- if current_user && can_push_branch?(@project, @project.default_branch)
|
||||
- unless @repository.changelog
|
||||
%li.missing
|
||||
= link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
|
||||
Add Changelog
|
||||
- unless @repository.license_blob
|
||||
%li.missing
|
||||
= link_to add_special_file_path(@project, file_name: 'LICENSE') do
|
||||
Add License
|
||||
- unless @repository.contribution_guide
|
||||
%li.missing
|
||||
= link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
|
||||
Add Contribution guide
|
||||
- unless @repository.gitlab_ci_yml
|
||||
%li.missing
|
||||
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
|
||||
Set Up CI
|
||||
|
||||
%li.project-repo-buttons-right
|
||||
.project-repo-buttons.project-right-buttons
|
||||
- if current_user
|
||||
= render 'shared/members/access_request_buttons', source: @project
|
||||
= render "projects/buttons/koding"
|
||||
|
||||
.btn-group.project-repo-btn-group
|
||||
= render 'projects/buttons/download', project: @project, ref: @ref
|
||||
= render 'projects/buttons/dropdown'
|
||||
|
||||
= render 'shared/notifications/button', notification_setting: @notification_setting
|
||||
- if @repository.commit
|
||||
.project-last-commit{ class: container_class }
|
||||
= render 'projects/last_commit', commit: @repository.commit, project: @project
|
||||
|
||||
%div{ class: container_class }
|
||||
- if @project.archived?
|
||||
|
@ -86,5 +88,7 @@
|
|||
= icon("exclamation-triangle fw")
|
||||
Archived project! Repository is read-only
|
||||
|
||||
%div{class: "project-show-#{default_project_view}"}
|
||||
= render default_project_view
|
||||
- view_path = default_project_view
|
||||
|
||||
%div{ class: project_child_container_class(view_path) }
|
||||
= render view_path
|
||||
|
|
|
@ -2,10 +2,14 @@ class ExpireBuildInstanceArtifactsWorker
|
|||
include Sidekiq::Worker
|
||||
|
||||
def perform(build_id)
|
||||
build = Ci::Build.with_expired_artifacts.reorder(nil).find_by(id: build_id)
|
||||
return unless build
|
||||
build = Ci::Build
|
||||
.with_expired_artifacts
|
||||
.reorder(nil)
|
||||
.find_by(id: build_id)
|
||||
|
||||
Rails.logger.info "Removing artifacts build #{build.id}..."
|
||||
return unless build.try(:project)
|
||||
|
||||
Rails.logger.info "Removing artifacts for build #{build.id}..."
|
||||
build.erase_artifacts!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
class AddRepositoryAccessLevelToProjectFeature < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
disable_ddl_transaction!
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
add_column_with_default(:project_features, :repository_access_level, :integer, default: ProjectFeature::ENABLED)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :project_features, :repository_access_level
|
||||
end
|
||||
end
|
|
@ -832,6 +832,7 @@ ActiveRecord::Schema.define(version: 20161017095000) do
|
|||
t.integer "builds_access_level"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "repository_access_level", default: 20, null: false
|
||||
end
|
||||
|
||||
add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree
|
||||
|
|
|
@ -125,6 +125,10 @@ module Gitlab
|
|||
|
||||
repository.blob_at(commit.id, file_path)
|
||||
end
|
||||
|
||||
def cache_key
|
||||
"#{file_path}-#{new_file}-#{deleted_file}-#{renamed_file}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,16 +35,16 @@ module Gitlab
|
|||
# for the highlighted ones, so we just skip their execution.
|
||||
# If the highlighted diff files lines are not cached we calculate and cache them.
|
||||
#
|
||||
# The content of the cache is a Hash where the key correspond to the file_path and the values are Arrays of
|
||||
# The content of the cache is a Hash where the key identifies the file and the values are Arrays of
|
||||
# hashes that represent serialized diff lines.
|
||||
#
|
||||
def cache_highlight!(diff_file)
|
||||
file_path = diff_file.file_path
|
||||
item_key = diff_file.cache_key
|
||||
|
||||
if highlight_cache[file_path]
|
||||
highlight_diff_file_from_cache!(diff_file, highlight_cache[file_path])
|
||||
if highlight_cache[item_key]
|
||||
highlight_diff_file_from_cache!(diff_file, highlight_cache[item_key])
|
||||
else
|
||||
highlight_cache[file_path] = diff_file.highlighted_diff_lines.map(&:to_hash)
|
||||
highlight_cache[item_key] = diff_file.highlighted_diff_lines.map(&:to_hash)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -41,6 +41,46 @@ describe ProjectsController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "when project repository is disabled" do
|
||||
render_views
|
||||
|
||||
before do
|
||||
project.team << [user, :developer]
|
||||
project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED)
|
||||
end
|
||||
|
||||
it 'shows wiki homepage' do
|
||||
get :show, namespace_id: project.namespace.path, id: project.path
|
||||
|
||||
expect(response).to render_template('projects/_wiki')
|
||||
end
|
||||
|
||||
it 'shows issues list page if wiki is disabled' do
|
||||
project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED)
|
||||
|
||||
get :show, namespace_id: project.namespace.path, id: project.path
|
||||
|
||||
expect(response).to render_template('projects/issues/_issues')
|
||||
end
|
||||
|
||||
it 'shows customize workflow page if wiki and issues are disabled' do
|
||||
project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED)
|
||||
project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
|
||||
|
||||
get :show, namespace_id: project.namespace.path, id: project.path
|
||||
|
||||
expect(response).to render_template("projects/_customize_workflow")
|
||||
end
|
||||
|
||||
it 'shows activity if enabled by user' do
|
||||
user.update_attribute(:project_view, 'activity')
|
||||
|
||||
get :show, namespace_id: project.namespace.path, id: project.path
|
||||
|
||||
expect(response).to render_template("projects/_activity")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "project with empty repo" do
|
||||
|
|
|
@ -45,6 +45,7 @@ FactoryGirl.define do
|
|||
snippets_access_level ProjectFeature::ENABLED
|
||||
issues_access_level ProjectFeature::ENABLED
|
||||
merge_requests_access_level ProjectFeature::ENABLED
|
||||
repository_access_level ProjectFeature::ENABLED
|
||||
end
|
||||
|
||||
after(:create) do |project, evaluator|
|
||||
|
@ -55,6 +56,7 @@ FactoryGirl.define do
|
|||
snippets_access_level: evaluator.snippets_access_level,
|
||||
issues_access_level: evaluator.issues_access_level,
|
||||
merge_requests_access_level: evaluator.merge_requests_access_level,
|
||||
repository_access_level: evaluator.repository_access_level
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,8 +2,11 @@ require 'spec_helper'
|
|||
include WaitForAjax
|
||||
|
||||
describe 'Edit Project Settings', feature: true do
|
||||
include WaitForAjax
|
||||
|
||||
let(:member) { create(:user) }
|
||||
let!(:project) { create(:project, :public, path: 'gitlab', name: 'sample') }
|
||||
let!(:issue) { create(:issue, project: project) }
|
||||
let(:non_member) { create(:user) }
|
||||
|
||||
describe 'project features visibility selectors', js: true do
|
||||
|
@ -119,4 +122,31 @@ describe 'Edit Project Settings', feature: true do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'repository visibility', js: true do
|
||||
before do
|
||||
project.team << [member, :master]
|
||||
login_as(member)
|
||||
visit edit_namespace_project_path(project.namespace, project)
|
||||
end
|
||||
|
||||
it "disables repository related features" do
|
||||
select "Disabled", from: "project_project_feature_attributes_repository_access_level"
|
||||
|
||||
expect(find(".edit-project")).to have_selector("select.disabled", count: 2)
|
||||
end
|
||||
|
||||
it "shows empty features project homepage" do
|
||||
select "Disabled", from: "project_project_feature_attributes_repository_access_level"
|
||||
select "Disabled", from: "project_project_feature_attributes_issues_access_level"
|
||||
select "Disabled", from: "project_project_feature_attributes_wiki_access_level"
|
||||
|
||||
click_button "Save changes"
|
||||
wait_for_ajax
|
||||
|
||||
visit namespace_project_path(project.namespace, project)
|
||||
|
||||
expect(page).to have_content "Customize your workflow!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,11 +24,12 @@ feature "Pipelines settings", feature: true do
|
|||
context 'for master' do
|
||||
given(:role) { :master }
|
||||
|
||||
scenario 'be allowed to change' do
|
||||
scenario 'be allowed to change', js: true do
|
||||
fill_in('Test coverage parsing', with: 'coverage_regex')
|
||||
click_on 'Save changes'
|
||||
|
||||
expect(page.status_code).to eq(200)
|
||||
expect(page).to have_button('Save changes', disabled: false)
|
||||
expect(page).to have_field('Test coverage parsing', with: 'coverage_regex')
|
||||
end
|
||||
end
|
|
@ -307,6 +307,7 @@ ProjectFeature:
|
|||
- wiki_access_level
|
||||
- snippets_access_level
|
||||
- builds_access_level
|
||||
- repository_access_level
|
||||
- created_at
|
||||
- updated_at
|
||||
ProtectedBranch::MergeAccessLevel:
|
||||
|
|
|
@ -88,24 +88,38 @@ describe Ci::Pipeline, models: true do
|
|||
|
||||
context 'no failed builds' do
|
||||
before do
|
||||
FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'success'
|
||||
create_build('rspec', 'success')
|
||||
end
|
||||
|
||||
it 'be not retryable' do
|
||||
it 'is not retryable' do
|
||||
is_expected.to be_falsey
|
||||
end
|
||||
|
||||
context 'one canceled job' do
|
||||
before do
|
||||
create_build('rubocop', 'canceled')
|
||||
end
|
||||
|
||||
it 'is retryable' do
|
||||
is_expected.to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with failed builds' do
|
||||
before do
|
||||
FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'running'
|
||||
FactoryGirl.create :ci_build, name: "rubocop", pipeline: pipeline, status: 'failed'
|
||||
create_build('rspec', 'running')
|
||||
create_build('rubocop', 'failed')
|
||||
end
|
||||
|
||||
it 'be retryable' do
|
||||
it 'is retryable' do
|
||||
is_expected.to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
def create_build(name, status)
|
||||
create(:ci_build, name: name, status: status, pipeline: pipeline)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#stages' do
|
||||
|
|
|
@ -13,7 +13,7 @@ describe Issue::Metrics, models: true do
|
|||
metrics = subject.metrics
|
||||
|
||||
expect(metrics).to be_present
|
||||
expect(metrics.first_associated_with_milestone_at).to be_within(1.second).of(time)
|
||||
expect(metrics.first_associated_with_milestone_at).to be_like_time(time)
|
||||
end
|
||||
|
||||
it "does not record the second time an issue is associated with a milestone" do
|
||||
|
@ -24,7 +24,7 @@ describe Issue::Metrics, models: true do
|
|||
metrics = subject.metrics
|
||||
|
||||
expect(metrics).to be_present
|
||||
expect(metrics.first_associated_with_milestone_at).to be_within(1.second).of(time)
|
||||
expect(metrics.first_associated_with_milestone_at).to be_like_time(time)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -36,7 +36,7 @@ describe Issue::Metrics, models: true do
|
|||
metrics = subject.metrics
|
||||
|
||||
expect(metrics).to be_present
|
||||
expect(metrics.first_added_to_board_at).to be_within(1.second).of(time)
|
||||
expect(metrics.first_added_to_board_at).to be_like_time(time)
|
||||
end
|
||||
|
||||
it "does not record the second time an issue is associated with a list label" do
|
||||
|
@ -48,7 +48,7 @@ describe Issue::Metrics, models: true do
|
|||
metrics = subject.metrics
|
||||
|
||||
expect(metrics).to be_present
|
||||
expect(metrics.first_added_to_board_at).to be_within(1.second).of(time)
|
||||
expect(metrics.first_added_to_board_at).to be_like_time(time)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ describe MergeRequest::Metrics, models: true do
|
|||
metrics = subject.metrics
|
||||
|
||||
expect(metrics).to be_present
|
||||
expect(metrics.merged_at).to be_within(1.second).of(time)
|
||||
expect(metrics.merged_at).to be_like_time(time)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ describe ProjectFeature do
|
|||
let(:user) { create(:user) }
|
||||
|
||||
describe '#feature_available?' do
|
||||
let(:features) { %w(issues wiki builds merge_requests snippets) }
|
||||
let(:features) { %w(issues wiki builds merge_requests snippets repository) }
|
||||
|
||||
context 'when features are disabled' do
|
||||
it "returns false" do
|
||||
|
@ -64,6 +64,27 @@ describe ProjectFeature do
|
|||
end
|
||||
end
|
||||
|
||||
context 'repository related features' do
|
||||
before do
|
||||
project.project_feature.update_attributes(
|
||||
merge_requests_access_level: ProjectFeature::DISABLED,
|
||||
builds_access_level: ProjectFeature::DISABLED,
|
||||
repository_access_level: ProjectFeature::PRIVATE
|
||||
)
|
||||
end
|
||||
|
||||
it "does not allow repository related features have higher level" do
|
||||
features = %w(builds merge_requests)
|
||||
project_feature = project.project_feature
|
||||
|
||||
features.each do |feature|
|
||||
field = "#{feature}_access_level".to_sym
|
||||
project_feature.update_attribute(field, ProjectFeature::ENABLED)
|
||||
expect(project_feature.valid?).to be_falsy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#*_enabled?' do
|
||||
let(:features) { %w(wiki builds merge_requests) }
|
||||
|
||||
|
|
|
@ -694,7 +694,7 @@ describe API::API, api: true do
|
|||
title: 'new issue', labels: 'label, label2', created_at: creation_time
|
||||
|
||||
expect(response).to have_http_status(201)
|
||||
expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time)
|
||||
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -895,7 +895,7 @@ describe API::API, api: true do
|
|||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(json_response['labels']).to include 'label3'
|
||||
expect(Time.parse(json_response['updated_at'])).to be_within(1.second).of(update_time)
|
||||
expect(Time.parse(json_response['updated_at'])).to be_like_time(update_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -217,7 +217,7 @@ describe API::API, api: true do
|
|||
expect(response).to have_http_status(201)
|
||||
expect(json_response['body']).to eq('hi!')
|
||||
expect(json_response['author']['username']).to eq(user.username)
|
||||
expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time)
|
||||
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -262,7 +262,7 @@ describe CreateDeploymentService, services: true do
|
|||
time = Time.now
|
||||
Timecop.freeze(time) { service.execute }
|
||||
|
||||
expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_within(1.second).of(time)
|
||||
expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(time)
|
||||
end
|
||||
|
||||
it "doesn't set the time if the deploy's environment is not 'production'" do
|
||||
|
@ -288,13 +288,13 @@ describe CreateDeploymentService, services: true do
|
|||
time = Time.now
|
||||
Timecop.freeze(time) { service.execute }
|
||||
|
||||
expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_within(1.second).of(time)
|
||||
expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(time)
|
||||
|
||||
# Current deploy
|
||||
service = described_class.new(project, user, params)
|
||||
Timecop.freeze(time + 12.hours) { service.execute }
|
||||
|
||||
expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_within(1.second).of(time)
|
||||
expect(merge_request.reload.metrics.first_deployed_to_production_at).to be_like_time(time)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -364,7 +364,7 @@ describe GitPushService, services: true do
|
|||
it 'sets the metric for referenced issues' do
|
||||
execute_service(project, user, @oldrev, @newrev, @ref)
|
||||
|
||||
expect(issue.reload.metrics.first_mentioned_in_commit_at).to be_within(1.second).of(commit_time)
|
||||
expect(issue.reload.metrics.first_mentioned_in_commit_at).to be_like_time(commit_time)
|
||||
end
|
||||
|
||||
it 'does not set the metric for non-referenced issues' do
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
RSpec::Matchers.define :be_like_time do |expected|
|
||||
match do |actual|
|
||||
expect(actual).to be_within(1.second).of(expected)
|
||||
end
|
||||
|
||||
description do
|
||||
"within one second of #{expected}"
|
||||
end
|
||||
|
||||
failure_message do |actual|
|
||||
"expected #{actual} to be within one second of #{expected}"
|
||||
end
|
||||
end
|
|
@ -6,28 +6,48 @@ describe ExpireBuildInstanceArtifactsWorker do
|
|||
let(:worker) { described_class.new }
|
||||
|
||||
describe '#perform' do
|
||||
before { build }
|
||||
|
||||
subject! { worker.perform(build.id) }
|
||||
before do
|
||||
worker.perform(build.id)
|
||||
end
|
||||
|
||||
context 'with expired artifacts' do
|
||||
let(:build) { create(:ci_build, :artifacts, artifacts_expire_at: Time.now - 7.days) }
|
||||
let(:artifacts_expiry) { { artifacts_expire_at: Time.now - 7.days } }
|
||||
|
||||
it 'does expire' do
|
||||
expect(build.reload.artifacts_expired?).to be_truthy
|
||||
context 'when associated project is valid' do
|
||||
let(:build) do
|
||||
create(:ci_build, :artifacts, artifacts_expiry)
|
||||
end
|
||||
|
||||
it 'does expire' do
|
||||
expect(build.reload.artifacts_expired?).to be_truthy
|
||||
end
|
||||
|
||||
it 'does remove files' do
|
||||
expect(build.reload.artifacts_file.exists?).to be_falsey
|
||||
end
|
||||
|
||||
it 'does nullify artifacts_file column' do
|
||||
expect(build.reload.artifacts_file_identifier).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it 'does remove files' do
|
||||
expect(build.reload.artifacts_file.exists?).to be_falsey
|
||||
end
|
||||
context 'when associated project was removed' do
|
||||
let(:build) do
|
||||
create(:ci_build, :artifacts, artifacts_expiry) do |build|
|
||||
build.project.delete
|
||||
end
|
||||
end
|
||||
|
||||
it 'does nullify artifacts_file column' do
|
||||
expect(build.reload.artifacts_file_identifier).to be_nil
|
||||
it 'does not remove artifacts' do
|
||||
expect(build.reload.artifacts_file.exists?).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with not yet expired artifacts' do
|
||||
let(:build) { create(:ci_build, :artifacts, artifacts_expire_at: Time.now + 7.days) }
|
||||
let(:build) do
|
||||
create(:ci_build, :artifacts, artifacts_expire_at: Time.now + 7.days)
|
||||
end
|
||||
|
||||
it 'does not expire' do
|
||||
expect(build.reload.artifacts_expired?).to be_falsey
|
||||
|
|
|
@ -23,7 +23,7 @@ describe PipelineMetricsWorker do
|
|||
it 'records the build start time' do
|
||||
subject
|
||||
|
||||
expect(merge_request.reload.metrics.latest_build_started_at).to be_within(1.second).of(pipeline.started_at)
|
||||
expect(merge_request.reload.metrics.latest_build_started_at).to be_like_time(pipeline.started_at)
|
||||
end
|
||||
|
||||
it 'clears the build end time' do
|
||||
|
@ -39,7 +39,7 @@ describe PipelineMetricsWorker do
|
|||
it 'records the build end time' do
|
||||
subject
|
||||
|
||||
expect(merge_request.reload.metrics.latest_build_finished_at).to be_within(1.second).of(pipeline.finished_at)
|
||||
expect(merge_request.reload.metrics.latest_build_finished_at).to be_like_time(pipeline.finished_at)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue