Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Jérome Perrin
gitlab-ce
Commits
19300a1a
Commit
19300a1a
authored
Oct 18, 2016
by
Kamil Trzcinski
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master' into 22191-delete-dynamic-envs-mr
parents
3032a0ca
146e0cbc
Changes
38
Show whitespace changes
Inline
Side-by-side
Showing
38 changed files
with
506 additions
and
218 deletions
+506
-218
CHANGELOG.md
CHANGELOG.md
+6
-0
app/assets/javascripts/project_new.js
app/assets/javascripts/project_new.js
+35
-3
app/assets/stylesheets/pages/projects.scss
app/assets/stylesheets/pages/projects.scss
+27
-56
app/controllers/projects_controller.rb
app/controllers/projects_controller.rb
+24
-11
app/helpers/award_emoji_helper.rb
app/helpers/award_emoji_helper.rb
+6
-3
app/helpers/preferences_helper.rb
app/helpers/preferences_helper.rb
+15
-1
app/helpers/projects_helper.rb
app/helpers/projects_helper.rb
+26
-7
app/models/ci/pipeline.rb
app/models/ci/pipeline.rb
+1
-1
app/models/project_feature.rb
app/models/project_feature.rb
+17
-2
app/policies/project_policy.rb
app/policies/project_policy.rb
+12
-2
app/views/projects/_customize_workflow.html.haml
app/views/projects/_customize_workflow.html.haml
+8
-0
app/views/projects/_home_panel.html.haml
app/views/projects/_home_panel.html.haml
+3
-2
app/views/projects/_wiki.html.haml
app/views/projects/_wiki.html.haml
+19
-0
app/views/projects/edit.html.haml
app/views/projects/edit.html.haml
+32
-24
app/views/projects/issues/_issues.html.haml
app/views/projects/issues/_issues.html.haml
+1
-1
app/views/projects/pipelines_settings/show.html.haml
app/views/projects/pipelines_settings/show.html.haml
+1
-1
app/views/projects/show.html.haml
app/views/projects/show.html.haml
+67
-63
app/workers/expire_build_instance_artifacts_worker.rb
app/workers/expire_build_instance_artifacts_worker.rb
+7
-3
db/migrate/20161012180455_add_repository_access_level_to_project_feature.rb
...2180455_add_repository_access_level_to_project_feature.rb
+14
-0
db/schema.rb
db/schema.rb
+1
-0
lib/gitlab/diff/file.rb
lib/gitlab/diff/file.rb
+4
-0
lib/gitlab/diff/file_collection/merge_request_diff.rb
lib/gitlab/diff/file_collection/merge_request_diff.rb
+5
-5
spec/controllers/projects_controller_spec.rb
spec/controllers/projects_controller_spec.rb
+40
-0
spec/factories/projects.rb
spec/factories/projects.rb
+2
-0
spec/features/projects/features_visibility_spec.rb
spec/features/projects/features_visibility_spec.rb
+30
-0
spec/features/projects/settings/pipelines_settings_spec.rb
spec/features/projects/settings/pipelines_settings_spec.rb
+2
-1
spec/lib/gitlab/import_export/safe_model_attributes.yml
spec/lib/gitlab/import_export/safe_model_attributes.yml
+1
-0
spec/models/ci/pipeline_spec.rb
spec/models/ci/pipeline_spec.rb
+19
-5
spec/models/issue/metrics_spec.rb
spec/models/issue/metrics_spec.rb
+4
-4
spec/models/merge_request/metrics_spec.rb
spec/models/merge_request/metrics_spec.rb
+1
-1
spec/models/project_feature_spec.rb
spec/models/project_feature_spec.rb
+22
-1
spec/requests/api/issues_spec.rb
spec/requests/api/issues_spec.rb
+2
-2
spec/requests/api/notes_spec.rb
spec/requests/api/notes_spec.rb
+1
-1
spec/services/create_deployment_service_spec.rb
spec/services/create_deployment_service_spec.rb
+3
-3
spec/services/git_push_service_spec.rb
spec/services/git_push_service_spec.rb
+1
-1
spec/support/matchers/be_like_time.rb
spec/support/matchers/be_like_time.rb
+13
-0
spec/workers/expire_build_instance_artifacts_worker_spec.rb
spec/workers/expire_build_instance_artifacts_worker_spec.rb
+32
-12
spec/workers/pipeline_metrics_worker_spec.rb
spec/workers/pipeline_metrics_worker_spec.rb
+2
-2
No files found.
CHANGELOG.md
View file @
19300a1a
...
...
@@ -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)
...
...
app/assets/javascripts/project_new.js
View file @
19300a1a
...
...
@@ -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
;
})();
...
...
app/assets/stylesheets/pages/projects.scss
View file @
19300a1a
...
...
@@ -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
;
}
}
app/controllers/projects_controller.rb
View file @
19300a1a
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
]
}
...
...
app/helpers/award_emoji_helper.rb
View file @
19300a1a
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
app/helpers/preferences_helper.rb
View file @
19300a1a
...
...
@@ -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
app/helpers/projects_helper.rb
View file @
19300a1a
...
...
@@ -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
<<
:
build
s
nav_tabs
<<
:
pipeline
s
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
app/models/ci/pipeline.rb
View file @
19300a1a
...
...
@@ -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
...
...
app/models/project_feature.rb
View file @
19300a1a
...
...
@@ -13,23 +13,26 @@ class ProjectFeature < ActiveRecord::Base
# Enabled: enabled for everyone able to access the project
#
# Permision levels
# Permis
s
ion 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
...
...
app/policies/project_policy.rb
View file @
19300a1a
...
...
@@ -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
...
...
app/views/projects/_customize_workflow.html.haml
0 → 100644
View file @
19300a1a
.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"
app/views/projects/_home_panel.html.haml
View file @
19300a1a
...
...
@@ -22,5 +22,6 @@
=
render
'projects/buttons/star'
=
render
'projects/buttons/fork'
-
if
@project
.
feature_available?
(
:repository
,
current_user
)
.project-clone-holder
=
render
"shared/clone_panel"
app/views/projects/_wiki.html.haml
0 → 100644
View file @
19300a1a
-
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.
app/views/projects/edit.html.haml
View file @
19300a1a
...
...
@@ -50,50 +50,58 @@
.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
)
.col-sm-12
.row
.col-md-9
.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
.col-md-9.project-feature-nested
=
feature_fields
.
label
:builds_access_level
,
"Builds"
,
class:
'label-light'
%span
.help-block
Submit T
est and deploy your changes before merge
%span
.help-block
Submit, t
est 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
=
feature_fields
.
label
:
snippets_access_level
,
"Snippets
"
,
class:
'label-light'
%span
.help-block
Share code pastes with others out of Git repository
.col-md-3
=
project_feature_access_select
(
:
wiki
_access_level
)
=
project_feature_access_select
(
:
snippets
_access_level
)
.row
.col-md-9
=
feature_fields
.
label
:
snippets_access_level
,
"Snippet
s"
,
class:
'label-light'
%span
.help-block
Share code pastes with others out of Git repository
=
feature_fields
.
label
:
issues_access_level
,
"Issue
s"
,
class:
'label-light'
%span
.help-block
Lightweight issue tracking system for this project
.col-md-3
=
project_feature_access_select
(
:
snippet
s_access_level
)
=
project_feature_access_select
(
:
issue
s_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
=
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?
.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
...
...
app/views/projects/issues/_issues.html.haml
View file @
19300a1a
%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
...
...
app/views/projects/pipelines_settings/show.html.haml
View file @
19300a1a
...
...
@@ -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
...
...
app/views/projects/show.html.haml
View file @
19300a1a
...
...
@@ -12,7 +12,8 @@
=
render
'projects/last_push'
=
render
"home_panel"
%nav
.project-stats
{
class:
(
container_class
)
}
-
if
@project
.
feature_available?
(
:repository
,
current_user
)
%nav
.project-stats
{
class:
container_class
}
%ul
.nav
%li
=
link_to
project_files_path
(
@project
)
do
...
...
@@ -71,11 +72,12 @@
=
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
-
if
@repository
.
commit
.project-last-commit
{
class:
container_class
}
=
render
'projects/last_commit'
,
commit:
@repository
.
commit
,
project:
@project
...
...
@@ -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
app/workers/expire_build_instance_artifacts_worker.rb
View file @
19300a1a
...
...
@@ -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
db/migrate/20161012180455_add_repository_access_level_to_project_feature.rb
0 → 100644
View file @
19300a1a
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
db/schema.rb
View file @
19300a1a
...
...
@@ -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
...
...
lib/gitlab/diff/file.rb
View file @
19300a1a
...
...
@@ -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
lib/gitlab/diff/file_collection/merge_request_diff.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/controllers/projects_controller_spec.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/factories/projects.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/features/projects/features_visibility_spec.rb
View file @
19300a1a
...
...
@@ -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
spec/features/pipelines_settings_spec.rb
→
spec/features/p
rojects/settings/p
ipelines_settings_spec.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/lib/gitlab/import_export/safe_model_attributes.yml
View file @
19300a1a
...
...
@@ -307,6 +307,7 @@ ProjectFeature:
-
wiki_access_level
-
snippets_access_level
-
builds_access_level
-
repository_access_level
-
created_at
-
updated_at
ProtectedBranch::MergeAccessLevel:
...
...
spec/models/ci/pipeline_spec.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/models/issue/metrics_spec.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/models/merge_request/metrics_spec.rb
View file @
19300a1a
...
...
@@ -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
spec/models/project_feature_spec.rb
View file @
19300a1a
...
...
@@ -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)
}
...
...
spec/requests/api/issues_spec.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/requests/api/notes_spec.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/services/create_deployment_service_spec.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/services/git_push_service_spec.rb
View file @
19300a1a
...
...
@@ -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
...
...
spec/support/matchers/be_like_time.rb
0 → 100644
View file @
19300a1a
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
spec/workers/expire_build_instance_artifacts_worker_spec.rb
View file @
19300a1a
...
...
@@ -6,12 +6,17 @@ 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
}
}
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
...
...
@@ -26,8 +31,23 @@ describe ExpireBuildInstanceArtifactsWorker do
end
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 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
...
...
spec/workers/pipeline_metrics_worker_spec.rb
View file @
19300a1a
...
...
@@ -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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment