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
1c75e4e6
Commit
1c75e4e6
authored
May 11, 2017
by
Sean McGivern
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dm-auxiliary-viewers' into 'master'
Implement auxiliary blob viewers See merge request !11195
parents
4067dd4f
90997505
Changes
32
Show whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
616 additions
and
140 deletions
+616
-140
app/assets/javascripts/blob/viewer/index.js
app/assets/javascripts/blob/viewer/index.js
+62
-37
app/controllers/concerns/renders_blob.rb
app/controllers/concerns/renders_blob.rb
+4
-1
app/helpers/projects_helper.rb
app/helpers/projects_helper.rb
+2
-5
app/models/blob.rb
app/models/blob.rb
+22
-12
app/models/blob_viewer/auxiliary.rb
app/models/blob_viewer/auxiliary.rb
+12
-0
app/models/blob_viewer/base.rb
app/models/blob_viewer/base.rb
+22
-8
app/models/blob_viewer/gitlab_ci_yml.rb
app/models/blob_viewer/gitlab_ci_yml.rb
+23
-0
app/models/blob_viewer/license.rb
app/models/blob_viewer/license.rb
+23
-0
app/models/blob_viewer/route_map.rb
app/models/blob_viewer/route_map.rb
+30
-0
app/models/blob_viewer/server_side.rb
app/models/blob_viewer/server_side.rb
+6
-0
app/models/repository.rb
app/models/repository.rb
+20
-7
app/views/projects/blob/_blob.html.haml
app/views/projects/blob/_blob.html.haml
+5
-0
app/views/projects/blob/_viewer.html.haml
app/views/projects/blob/_viewer.html.haml
+1
-2
app/views/projects/blob/viewers/_gitlab_ci_yml.html.haml
app/views/projects/blob/viewers/_gitlab_ci_yml.html.haml
+9
-0
app/views/projects/blob/viewers/_gitlab_ci_yml_loading.html.haml
...ws/projects/blob/viewers/_gitlab_ci_yml_loading.html.haml
+4
-0
app/views/projects/blob/viewers/_license.html.haml
app/views/projects/blob/viewers/_license.html.haml
+8
-0
app/views/projects/blob/viewers/_loading.html.haml
app/views/projects/blob/viewers/_loading.html.haml
+2
-0
app/views/projects/blob/viewers/_loading_auxiliary.html.haml
app/views/projects/blob/viewers/_loading_auxiliary.html.haml
+2
-0
app/views/projects/blob/viewers/_route_map.html.haml
app/views/projects/blob/viewers/_route_map.html.haml
+9
-0
app/views/projects/blob/viewers/_route_map_loading.html.haml
app/views/projects/blob/viewers/_route_map_loading.html.haml
+4
-0
changelogs/unreleased/dm-auxiliary-viewers.yml
changelogs/unreleased/dm-auxiliary-viewers.yml
+5
-0
doc/ci/environments.md
doc/ci/environments.md
+2
-1
lib/gitlab/file_detector.rb
lib/gitlab/file_detector.rb
+2
-1
spec/features/projects/blobs/blob_show_spec.rb
spec/features/projects/blobs/blob_show_spec.rb
+78
-20
spec/models/blob_spec.rb
spec/models/blob_spec.rb
+46
-0
spec/models/blob_viewer/base_spec.rb
spec/models/blob_viewer/base_spec.rb
+38
-34
spec/models/blob_viewer/gitlab_ci_yml_spec.rb
spec/models/blob_viewer/gitlab_ci_yml_spec.rb
+32
-0
spec/models/blob_viewer/license_spec.rb
spec/models/blob_viewer/license_spec.rb
+34
-0
spec/models/blob_viewer/route_map_spec.rb
spec/models/blob_viewer/route_map_spec.rb
+38
-0
spec/models/blob_viewer/server_side_spec.rb
spec/models/blob_viewer/server_side_spec.rb
+25
-0
spec/models/repository_spec.rb
spec/models/repository_spec.rb
+44
-10
spec/views/projects/blob/_viewer.html.haml_spec.rb
spec/views/projects/blob/_viewer.html.haml_spec.rb
+2
-2
No files found.
app/assets/javascripts/blob/viewer/index.js
View file @
1c75e4e6
/* global Flash */
export
default
class
BlobViewer
{
constructor
()
{
BlobViewer
.
initAuxiliaryViewer
();
this
.
initMainViewers
();
}
static
initAuxiliaryViewer
()
{
const
auxiliaryViewer
=
document
.
querySelector
(
'
.blob-viewer[data-type="auxiliary"]
'
);
if
(
!
auxiliaryViewer
)
return
;
BlobViewer
.
loadViewer
(
auxiliaryViewer
);
}
initMainViewers
()
{
this
.
$fileHolder
=
$
(
'
.file-holder
'
);
if
(
!
this
.
$fileHolder
.
length
)
return
;
this
.
switcher
=
document
.
querySelector
(
'
.js-blob-viewer-switcher
'
);
this
.
switcherBtns
=
document
.
querySelectorAll
(
'
.js-blob-viewer-switch-btn
'
);
this
.
copySourceBtn
=
document
.
querySelector
(
'
.js-copy-blob-source-btn
'
);
this
.
simpleViewer
=
document
.
querySelector
(
'
.blob-viewer[data-type="simple"]
'
);
this
.
richViewer
=
document
.
querySelector
(
'
.blob-viewer[data-type="rich"]
'
);
this
.
$fileHolder
=
$
(
'
.file-holder
'
);
const
initialViewer
=
document
.
querySelector
(
'
.blob-viewer:not(.hidden)
'
);
if
(
!
initialViewer
)
return
;
let
initialViewerName
=
initialViewer
.
getAttribute
(
'
data-type
'
);
this
.
simpleViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
'
.blob-viewer[data-type="simple"]
'
);
this
.
richViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
'
.blob-viewer[data-type="rich"]
'
);
this
.
initBindings
();
this
.
switchToInitialViewer
();
}
switchToInitialViewer
()
{
const
initialViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
'
.blob-viewer:not(.hidden)
'
);
let
initialViewerName
=
initialViewer
.
getAttribute
(
'
data-type
'
);
if
(
this
.
switcher
&&
location
.
hash
.
indexOf
(
'
#L
'
)
===
0
)
{
initialViewerName
=
'
simple
'
;
}
...
...
@@ -64,40 +82,13 @@ export default class BlobViewer {
$
(
this
.
copySourceBtn
).
tooltip
(
'
fixTitle
'
);
}
loadViewer
(
viewerParam
)
{
const
viewer
=
viewerParam
;
const
url
=
viewer
.
getAttribute
(
'
data-url
'
);
if
(
!
url
||
viewer
.
getAttribute
(
'
data-loaded
'
)
||
viewer
.
getAttribute
(
'
data-loading
'
))
{
return
;
}
viewer
.
setAttribute
(
'
data-loading
'
,
'
true
'
);
$
.
ajax
({
url
,
dataType
:
'
JSON
'
,
})
.
fail
(()
=>
new
Flash
(
'
Error loading source view
'
))
.
done
((
data
)
=>
{
viewer
.
innerHTML
=
data
.
html
;
$
(
viewer
).
syntaxHighlight
();
viewer
.
setAttribute
(
'
data-loaded
'
,
'
true
'
);
this
.
$fileHolder
.
trigger
(
'
highlight:line
'
);
this
.
toggleCopyButtonState
();
});
}
switchToViewer
(
name
)
{
const
newViewer
=
document
.
querySelector
(
`.blob-viewer[data-type='
${
name
}
']`
);
const
newViewer
=
this
.
$fileHolder
[
0
]
.
querySelector
(
`.blob-viewer[data-type='
${
name
}
']`
);
if
(
this
.
activeViewer
===
newViewer
)
return
;
const
oldButton
=
document
.
querySelector
(
'
.js-blob-viewer-switch-btn.active
'
);
const
newButton
=
document
.
querySelector
(
`.js-blob-viewer-switch-btn[data-viewer='
${
name
}
']`
);
const
oldViewer
=
document
.
querySelector
(
`.blob-viewer:not([data-type='
${
name
}
'])`
);
const
oldViewer
=
this
.
$fileHolder
[
0
]
.
querySelector
(
`.blob-viewer:not([data-type='
${
name
}
'])`
);
if
(
oldButton
)
{
oldButton
.
classList
.
remove
(
'
active
'
);
...
...
@@ -118,6 +109,40 @@ export default class BlobViewer {
this
.
toggleCopyButtonState
();
this
.
loadViewer
(
newViewer
);
BlobViewer
.
loadViewer
(
newViewer
)
.
then
((
viewer
)
=>
{
$
(
viewer
).
syntaxHighlight
();
this
.
$fileHolder
.
trigger
(
'
highlight:line
'
);
this
.
toggleCopyButtonState
();
})
.
catch
(()
=>
new
Flash
(
'
Error loading viewer
'
));
}
static
loadViewer
(
viewerParam
)
{
const
viewer
=
viewerParam
;
const
url
=
viewer
.
getAttribute
(
'
data-url
'
);
return
new
Promise
((
resolve
,
reject
)
=>
{
if
(
!
url
||
viewer
.
getAttribute
(
'
data-loaded
'
)
||
viewer
.
getAttribute
(
'
data-loading
'
))
{
resolve
(
viewer
);
return
;
}
viewer
.
setAttribute
(
'
data-loading
'
,
'
true
'
);
$
.
ajax
({
url
,
dataType
:
'
JSON
'
,
})
.
fail
(
reject
)
.
done
((
data
)
=>
{
viewer
.
innerHTML
=
data
.
html
;
viewer
.
setAttribute
(
'
data-loaded
'
,
'
true
'
);
resolve
(
viewer
);
});
});
}
}
app/controllers/concerns/renders_blob.rb
View file @
1c75e4e6
...
...
@@ -3,8 +3,11 @@ module RendersBlob
def
render_blob_json
(
blob
)
viewer
=
if
params
[
:viewer
]
==
'rich'
case
params
[
:viewer
]
when
'rich'
blob
.
rich_viewer
when
'auxiliary'
blob
.
auxiliary_viewer
else
blob
.
simple_viewer
end
...
...
app/helpers/projects_helper.rb
View file @
1c75e4e6
...
...
@@ -110,11 +110,8 @@ module ProjectsHelper
end
def
license_short_name
(
project
)
return
'LICENSE'
if
project
.
repository
.
license_key
.
nil?
license
=
Licensee
::
License
.
new
(
project
.
repository
.
license_key
)
license
.
nickname
||
license
.
name
license
=
project
.
repository
.
license
license
&
.
nickname
||
license
&
.
name
||
'LICENSE'
end
def
last_push_event
...
...
app/models/blob.rb
View file @
1c75e4e6
...
...
@@ -34,10 +34,13 @@ class Blob < SimpleDelegator
BlobViewer
::
BinarySTL
,
BlobViewer
::
TextSTL
].
freeze
].
sort_by
{
|
v
|
v
.
binary?
?
0
:
1
}.
freeze
BINARY_VIEWERS
=
RICH_VIEWERS
.
select
(
&
:binary?
).
freeze
TEXT_VIEWERS
=
RICH_VIEWERS
.
select
(
&
:text?
).
freeze
AUXILIARY_VIEWERS
=
[
BlobViewer
::
GitlabCiYml
,
BlobViewer
::
RouteMap
,
BlobViewer
::
License
].
freeze
attr_reader
:project
...
...
@@ -154,6 +157,12 @@ class Blob < SimpleDelegator
@rich_viewer
=
rich_viewer_class
&
.
new
(
self
)
end
def
auxiliary_viewer
return
@auxiliary_viewer
if
defined?
(
@auxiliary_viewer
)
@auxiliary_viewer
=
auxiliary_viewer_class
&
.
new
(
self
)
end
def
rendered_as_text?
(
ignore_errors:
true
)
simple_viewer
.
text?
&&
(
ignore_errors
||
simple_viewer
.
render_error
.
nil?
)
end
...
...
@@ -180,17 +189,18 @@ class Blob < SimpleDelegator
end
def
rich_viewer_class
return
if
empty?
||
external_storage_error?
viewer_class_from
(
RICH_VIEWERS
)
end
classes
=
if
stored_externally?
BINARY_VIEWERS
+
TEXT_VIEWERS
elsif
binary?
BINARY_VIEWERS
else
# text
TEXT_VIEWERS
def
auxiliary_viewer_class
viewer_class_from
(
AUXILIARY_VIEWERS
)
end
classes
.
find
{
|
viewer_class
|
viewer_class
.
can_render?
(
self
)
}
def
viewer_class_from
(
classes
)
return
if
empty?
||
external_storage_error?
verify_binary
=
!
stored_externally?
classes
.
find
{
|
viewer_class
|
viewer_class
.
can_render?
(
self
,
verify_binary:
verify_binary
)
}
end
end
app/models/blob_viewer/auxiliary.rb
0 → 100644
View file @
1c75e4e6
module
BlobViewer
module
Auxiliary
extend
ActiveSupport
::
Concern
included
do
self
.
loading_partial_name
=
'loading_auxiliary'
self
.
type
=
:auxiliary
self
.
max_size
=
100
.
kilobytes
self
.
absolute_max_size
=
100
.
kilobytes
end
end
end
app/models/blob_viewer/base.rb
View file @
1c75e4e6
module
BlobViewer
class
Base
class_attribute
:partial_name
,
:type
,
:extensions
,
:client_side
,
:binary
,
:switcher_icon
,
:switcher_title
,
:max_size
,
:absolute_max_si
ze
PARTIAL_PATH_PREFIX
=
'projects/blob/viewers'
.
free
ze
delegate
:partial_path
,
:rich?
,
:simple?
,
:client_side?
,
:server_side?
,
:text?
,
:binary?
,
to: :class
class_attribute
:partial_name
,
:loading_partial_name
,
:type
,
:extensions
,
:file_type
,
:client_side
,
:binary
,
:switcher_icon
,
:switcher_title
,
:max_size
,
:absolute_max_size
self
.
loading_partial_name
=
'loading'
delegate
:partial_path
,
:loading_partial_path
,
:rich?
,
:simple?
,
:client_side?
,
:server_side?
,
:text?
,
:binary?
,
to: :class
attr_reader
:blob
attr_accessor
:override_max_size
...
...
@@ -12,7 +16,11 @@ module BlobViewer
end
def
self
.
partial_path
"projects/blob/viewers/
#{
partial_name
}
"
File
.
join
(
PARTIAL_PATH_PREFIX
,
partial_name
)
end
def
self
.
loading_partial_path
File
.
join
(
PARTIAL_PATH_PREFIX
,
loading_partial_name
)
end
def
self
.
rich?
...
...
@@ -23,6 +31,10 @@ module BlobViewer
type
==
:simple
end
def
self
.
auxiliary?
type
==
:auxiliary
end
def
self
.
client_side?
client_side
end
...
...
@@ -39,8 +51,12 @@ module BlobViewer
!
binary?
end
def
self
.
can_render?
(
blob
)
!
extensions
||
extensions
.
include?
(
blob
.
extension
)
def
self
.
can_render?
(
blob
,
verify_binary:
true
)
return
false
if
verify_binary
&&
binary?
!=
blob
.
binary?
return
true
if
extensions
&
.
include?
(
blob
.
extension
)
return
true
if
file_type
&&
Gitlab
::
FileDetector
.
type_of
(
blob
.
path
)
==
file_type
false
end
def
too_large?
...
...
@@ -83,9 +99,7 @@ module BlobViewer
end
def
prepare!
if
server_side?
&&
blob
.
project
blob
.
load_all_data!
(
blob
.
project
.
repository
)
end
# To be overridden by subclasses
end
private
...
...
app/models/blob_viewer/gitlab_ci_yml.rb
0 → 100644
View file @
1c75e4e6
module
BlobViewer
class
GitlabCiYml
<
Base
include
ServerSide
include
Auxiliary
self
.
partial_name
=
'gitlab_ci_yml'
self
.
loading_partial_name
=
'gitlab_ci_yml_loading'
self
.
file_type
=
:gitlab_ci
self
.
binary
=
false
def
validation_message
return
@validation_message
if
defined?
(
@validation_message
)
prepare!
@validation_message
=
Ci
::
GitlabCiYamlProcessor
.
validation_message
(
blob
.
data
)
end
def
valid?
validation_message
.
blank?
end
end
end
app/models/blob_viewer/license.rb
0 → 100644
View file @
1c75e4e6
module
BlobViewer
class
License
<
Base
# We treat the License viewer as if it renders the content client-side,
# so that it doesn't attempt to load the entire blob contents and is
# rendered synchronously instead of loaded asynchronously.
include
ClientSide
include
Auxiliary
self
.
partial_name
=
'license'
self
.
file_type
=
:license
self
.
binary
=
false
def
license
blob
.
project
.
repository
.
license
end
def
render_error
return
if
license
:unknown_license
end
end
end
app/models/blob_viewer/route_map.rb
0 → 100644
View file @
1c75e4e6
module
BlobViewer
class
RouteMap
<
Base
include
ServerSide
include
Auxiliary
self
.
partial_name
=
'route_map'
self
.
loading_partial_name
=
'route_map_loading'
self
.
file_type
=
:route_map
self
.
binary
=
false
def
validation_message
return
@validation_message
if
defined?
(
@validation_message
)
prepare!
@validation_message
=
begin
Gitlab
::
RouteMap
.
new
(
blob
.
data
)
nil
rescue
Gitlab
::
RouteMap
::
FormatError
=>
e
e
.
message
end
end
def
valid?
validation_message
.
blank?
end
end
end
app/models/blob_viewer/server_side.rb
View file @
1c75e4e6
...
...
@@ -7,5 +7,11 @@ module BlobViewer
self
.
max_size
=
2
.
megabytes
self
.
absolute_max_size
=
5
.
megabytes
end
def
prepare!
if
blob
.
project
blob
.
load_all_data!
(
blob
.
project
.
repository
)
end
end
end
end
app/models/repository.rb
View file @
1c75e4e6
...
...
@@ -30,7 +30,7 @@ class Repository
METHOD_CACHES_FOR_FILE_TYPES
=
{
readme: :rendered_readme
,
changelog: :changelog
,
license:
%i(license_blob license_key)
,
license:
%i(license_blob license_key
license
)
,
contributing: :contribution_guide
,
gitignore: :gitignore
,
koding: :koding_yml
,
...
...
@@ -42,13 +42,13 @@ class Repository
# variable.
#
# This only works for methods that do not take any arguments.
def
self
.
cache_method
(
name
,
fallback:
nil
)
def
self
.
cache_method
(
name
,
fallback:
nil
,
memoize_only:
false
)
original
=
:"_uncached_
#{
name
}
"
alias_method
(
original
,
name
)
define_method
(
name
)
do
cache_method_output
(
name
,
fallback:
fallback
)
{
__send__
(
original
)
}
cache_method_output
(
name
,
fallback:
fallback
,
memoize_only:
memoize_only
)
{
__send__
(
original
)
}
end
end
...
...
@@ -549,6 +549,13 @@ class Repository
end
cache_method
:license_key
def
license
return
unless
license_key
Licensee
::
License
.
new
(
license_key
)
end
cache_method
:license
,
memoize_only:
true
def
gitignore
file_on_head
(
:gitignore
)
end
...
...
@@ -1061,14 +1068,20 @@ class Repository
#
# key - The name of the key to cache the data in.
# fallback - A value to fall back to in the event of a Git error.
def
cache_method_output
(
key
,
fallback:
nil
,
&
block
)
def
cache_method_output
(
key
,
fallback:
nil
,
memoize_only:
false
,
&
block
)
ivar
=
cache_instance_variable_name
(
key
)
if
instance_variable_defined?
(
ivar
)
instance_variable_get
(
ivar
)
else
begin
instance_variable_set
(
ivar
,
cache
.
fetch
(
key
,
&
block
))
value
=
if
memoize_only
yield
else
cache
.
fetch
(
key
,
&
block
)
end
instance_variable_set
(
ivar
,
value
)
rescue
Rugged
::
ReferenceError
,
Gitlab
::
Git
::
Repository
::
NoRepository
# if e.g. HEAD or the entire repository doesn't exist we want to
# gracefully handle this and not cache anything.
...
...
@@ -1083,8 +1096,8 @@ class Repository
def
file_on_head
(
type
)
if
head
=
tree
(
:head
)
head
.
blobs
.
find
do
|
file
|
Gitlab
::
FileDetector
.
type_of
(
file
.
name
)
==
type
head
.
blobs
.
find
do
|
blob
|
Gitlab
::
FileDetector
.
type_of
(
blob
.
path
)
==
type
end
end
end
...
...
app/views/projects/blob/_blob.html.haml
View file @
1c75e4e6
...
...
@@ -6,6 +6,11 @@
-
blob_commit
=
@repository
.
last_commit_for_path
(
@commit
.
id
,
blob
.
path
)
=
render
blob_commit
,
project:
@project
,
ref:
@ref
-
auxiliary_viewer
=
blob
.
auxiliary_viewer
-
if
auxiliary_viewer
&&
!
auxiliary_viewer
.
render_error
.well-segment.blob-auxiliary-viewer
=
render
'projects/blob/viewer'
,
viewer:
auxiliary_viewer
#blob-content-holder
.blob-content-holder
%article
.file-holder
=
render
"projects/blob/header"
,
blob:
blob
...
...
app/views/projects/blob/_viewer.html.haml
View file @
1c75e4e6
...
...
@@ -5,8 +5,7 @@
-
viewer_url
=
local_assigns
.
fetch
(
:viewer_url
)
{
url_for
(
params
.
merge
(
viewer:
viewer
.
type
,
format: :json
))
}
if
load_asynchronously
.blob-viewer
{
data:
{
type:
viewer
.
type
,
url:
viewer_url
},
class:
(
'hidden'
if
hidden
)
}
-
if
load_asynchronously
.text-center.prepend-top-default.append-bottom-default
=
icon
(
'spinner spin 2x'
,
'aria-hidden'
=>
'true'
,
'aria-label'
=>
'Loading content'
)
=
render
viewer
.
loading_partial_path
,
viewer:
viewer
-
elsif
render_error
=
render
'projects/blob/render_error'
,
viewer:
viewer
-
else
...
...
app/views/projects/blob/viewers/_gitlab_ci_yml.html.haml
0 → 100644
View file @
1c75e4e6
-
if
viewer
.
valid?
=
icon
(
'check fw'
)
This GitLab CI configuration is valid.
-
else
=
icon
(
'warning fw'
)
This GitLab CI configuration is invalid:
=
viewer
.
validation_message
=
link_to
'Learn more'
,
help_page_path
(
'ci/yaml/README'
)
app/views/projects/blob/viewers/_gitlab_ci_yml_loading.html.haml
0 → 100644
View file @
1c75e4e6
=
icon
(
'spinner spin fw'
)
Validating GitLab CI configuration…
=
link_to
'Learn more'
,
help_page_path
(
'ci/yaml/README'
)
app/views/projects/blob/viewers/_license.html.haml
0 → 100644
View file @
1c75e4e6
-
license
=
viewer
.
license
=
icon
(
'balance-scale fw'
)
This project is licensed under the
=
succeed
'.'
do
%strong
=
license
.
name
=
link_to
'Learn more about this license'
,
license
.
url
,
target:
'_blank'
,
rel:
'noopener noreferrer'
app/views/projects/blob/viewers/_loading.html.haml
0 → 100644
View file @
1c75e4e6
.text-center.prepend-top-default.append-bottom-default
=
icon
(
'spinner spin 2x'
,
'aria-hidden'
=>
'true'
,
'aria-label'
=>
'Loading content…'
)
app/views/projects/blob/viewers/_loading_auxiliary.html.haml
0 → 100644
View file @
1c75e4e6
=
icon
(
'spinner spin fw'
)
Loading…
app/views/projects/blob/viewers/_route_map.html.haml
0 → 100644
View file @
1c75e4e6
-
if
viewer
.
valid?
=
icon
(
'check fw'
)
This Route Map is valid.
-
else
=
icon
(
'warning fw'
)
This Route Map is invalid:
=
viewer
.
validation_message
=
link_to
'Learn more'
,
help_page_path
(
'ci/environments'
,
anchor:
'route-map'
)
app/views/projects/blob/viewers/_route_map_loading.html.haml
0 → 100644
View file @
1c75e4e6
=
icon
(
'spinner spin fw'
)
Validating Route Map…
=
link_to
'Learn more'
,
help_page_path
(
'ci/environments'
,
anchor:
'route-map'
)
changelogs/unreleased/dm-auxiliary-viewers.yml
0 → 100644
View file @
1c75e4e6
---
title
:
Display extra info about files on .gitlab-ci.yml, .gitlab/route-map.yml and
LICENSE blob pages
merge_request
:
author
:
doc/ci/environments.md
View file @
1c75e4e6
...
...
@@ -442,7 +442,8 @@ and/or `production`) you can see this information in the merge request itself.
![
Environment URLs in merge request
](
img/environments_link_url_mr.png
)
### Go directly from source files to public pages on the environment
### <a name="route-map"></a>Go directly from source files to public pages on the environment
> Introduced in GitLab 8.17.
...
...
lib/gitlab/file_detector.rb
View file @
1c75e4e6
...
...
@@ -13,7 +13,8 @@ module Gitlab
gitignore:
'.gitignore'
,
koding:
'.koding.yml'
,
gitlab_ci:
'.gitlab-ci.yml'
,
avatar:
/\Alogo\.(png|jpg|gif)\z/
avatar:
/\Alogo\.(png|jpg|gif)\z/
,
route_map:
'route-map.yml'
}.
freeze
# Returns an Array of file types based on the given paths.
...
...
spec/features/projects/blobs/blob_show_spec.rb
View file @
1c75e4e6
...
...
@@ -5,13 +5,13 @@ feature 'File blob', :js, feature: true do
def
visit_blob
(
path
,
fragment
=
nil
)
visit
namespace_project_blob_path
(
project
.
namespace
,
project
,
File
.
join
(
'master'
,
path
),
anchor:
fragment
)
wait_for_ajax
end
context
'Ruby file'
do
before
do
visit_blob
(
'files/ruby/popen.rb'
)
wait_for_ajax
end
it
'displays the blob'
do
...
...
@@ -35,8 +35,6 @@ feature 'File blob', :js, feature: true do
context
'visiting directly'
do
before
do
visit_blob
(
'files/markdown/ruby-style-guide.md'
)
wait_for_ajax
end
it
'displays the blob using the rich viewer'
do
...
...
@@ -104,8 +102,6 @@ feature 'File blob', :js, feature: true do
context
'visiting with a line number anchor'
do
before
do
visit_blob
(
'files/markdown/ruby-style-guide.md'
,
'L1'
)
wait_for_ajax
end
it
'displays the blob using the simple viewer'
do
...
...
@@ -148,8 +144,6 @@ feature 'File blob', :js, feature: true do
project
.
update_attribute
(
:lfs_enabled
,
true
)
visit_blob
(
'files/lfs/file.md'
)
wait_for_ajax
end
it
'displays an error'
do
...
...
@@ -198,8 +192,6 @@ feature 'File blob', :js, feature: true do
context
'when LFS is disabled on the project'
do
before
do
visit_blob
(
'files/lfs/file.md'
)
wait_for_ajax
end
it
'displays the blob'
do
...
...
@@ -235,8 +227,6 @@ feature 'File blob', :js, feature: true do
).
execute
visit_blob
(
'files/test.pdf'
)
wait_for_ajax
end
it
'displays the blob'
do
...
...
@@ -263,8 +253,6 @@ feature 'File blob', :js, feature: true do
project
.
update_attribute
(
:lfs_enabled
,
true
)
visit_blob
(
'files/lfs/lfs_object.iso'
)
wait_for_ajax
end
it
'displays the blob'
do
...
...
@@ -287,8 +275,6 @@ feature 'File blob', :js, feature: true do
context
'when LFS is disabled on the project'
do
before
do
visit_blob
(
'files/lfs/lfs_object.iso'
)
wait_for_ajax
end
it
'displays the blob'
do
...
...
@@ -312,8 +298,6 @@ feature 'File blob', :js, feature: true do
context
'ZIP file'
do
before
do
visit_blob
(
'Gemfile.zip'
)
wait_for_ajax
end
it
'displays the blob'
do
...
...
@@ -348,8 +332,6 @@ feature 'File blob', :js, feature: true do
).
execute
visit_blob
(
'files/empty.md'
)
wait_for_ajax
end
it
'displays an error'
do
...
...
@@ -369,4 +351,80 @@ feature 'File blob', :js, feature: true do
end
end
end
context
'.gitlab-ci.yml'
do
before
do
project
.
add_master
(
project
.
creator
)
Files
::
CreateService
.
new
(
project
,
project
.
creator
,
start_branch:
'master'
,
branch_name:
'master'
,
commit_message:
"Add .gitlab-ci.yml"
,
file_path:
'.gitlab-ci.yml'
,
file_content:
File
.
read
(
Rails
.
root
.
join
(
'spec/support/gitlab_stubs/gitlab_ci.yml'
))
).
execute
visit_blob
(
'.gitlab-ci.yml'
)
end
it
'displays an auxiliary viewer'
do
aggregate_failures
do
# shows that configuration is valid
expect
(
page
).
to
have_content
(
'This GitLab CI configuration is valid.'
)
# shows a learn more link
expect
(
page
).
to
have_link
(
'Learn more'
)
end
end
end
context
'.gitlab/route-map.yml'
do
before
do
project
.
add_master
(
project
.
creator
)
Files
::
CreateService
.
new
(
project
,
project
.
creator
,
start_branch:
'master'
,
branch_name:
'master'
,
commit_message:
"Add .gitlab/route-map.yml"
,
file_path:
'.gitlab/route-map.yml'
,
file_content:
<<-
MAP
.
strip_heredoc
# Team data
- source: 'data/team.yml'
public: 'team/'
MAP
).
execute
visit_blob
(
'.gitlab/route-map.yml'
)
end
it
'displays an auxiliary viewer'
do
aggregate_failures
do
# shows that map is valid
expect
(
page
).
to
have_content
(
'This Route Map is valid.'
)
# shows a learn more link
expect
(
page
).
to
have_link
(
'Learn more'
)
end
end
end
context
'LICENSE'
do
before
do
visit_blob
(
'LICENSE'
)
end
it
'displays an auxiliary viewer'
do
aggregate_failures
do
# shows license
expect
(
page
).
to
have_content
(
'This project is licensed under the MIT License.'
)
# shows a learn more link
expect
(
page
).
to
have_link
(
'Learn more about this license'
,
'http://choosealicense.com/licenses/mit/'
)
end
end
end
end
spec/models/blob_spec.rb
View file @
1c75e4e6
...
...
@@ -271,6 +271,52 @@ describe Blob do
end
end
describe
'#auxiliary_viewer'
do
context
'when the blob has an external storage error'
do
before
do
project
.
lfs_enabled
=
false
end
it
'returns nil'
do
blob
=
fake_blob
(
path:
'LICENSE'
,
lfs:
true
)
expect
(
blob
.
auxiliary_viewer
).
to
be_nil
end
end
context
'when the blob is empty'
do
it
'returns nil'
do
blob
=
fake_blob
(
data:
''
)
expect
(
blob
.
auxiliary_viewer
).
to
be_nil
end
end
context
'when the blob is stored externally'
do
it
'returns a matching viewer'
do
blob
=
fake_blob
(
path:
'LICENSE'
,
lfs:
true
)
expect
(
blob
.
auxiliary_viewer
).
to
be_a
(
BlobViewer
::
License
)
end
end
context
'when the blob is binary'
do
it
'returns nil'
do
blob
=
fake_blob
(
path:
'LICENSE'
,
binary:
true
)
expect
(
blob
.
auxiliary_viewer
).
to
be_nil
end
end
context
'when the blob is text-based'
do
it
'returns a matching text-based viewer'
do
blob
=
fake_blob
(
path:
'LICENSE'
)
expect
(
blob
.
auxiliary_viewer
).
to
be_a
(
BlobViewer
::
License
)
end
end
end
describe
'#rendered_as_text?'
do
context
'when ignoring errors'
do
context
'when the simple viewer is text-based'
do
...
...
spec/models/blob_viewer/base_spec.rb
View file @
1c75e4e6
...
...
@@ -8,6 +8,7 @@ describe BlobViewer::Base, model: true do
let
(
:viewer_class
)
do
Class
.
new
(
described_class
)
do
self
.
extensions
=
%w(pdf)
self
.
binary
=
true
self
.
max_size
=
1
.
megabyte
self
.
absolute_max_size
=
5
.
megabytes
self
.
client_side
=
false
...
...
@@ -18,14 +19,47 @@ describe BlobViewer::Base, model: true do
describe
'.can_render?'
do
context
'when the extension is supported'
do
let
(
:blob
)
{
fake_blob
(
path:
'file.pdf'
)
}
context
'when the binaryness matches'
do
let
(
:blob
)
{
fake_blob
(
path:
'file.pdf'
,
binary:
true
)
}
it
'returns true'
do
expect
(
viewer_class
.
can_render?
(
blob
)).
to
be_truthy
end
end
context
'when the extension is not supported'
do
context
'when the binaryness does not match'
do
let
(
:blob
)
{
fake_blob
(
path:
'file.pdf'
,
binary:
false
)
}
it
'returns false'
do
expect
(
viewer_class
.
can_render?
(
blob
)).
to
be_falsey
end
end
end
context
'when the file type is supported'
do
before
do
viewer_class
.
file_type
=
:license
viewer_class
.
binary
=
false
end
context
'when the binaryness matches'
do
let
(
:blob
)
{
fake_blob
(
path:
'LICENSE'
,
binary:
false
)
}
it
'returns true'
do
expect
(
viewer_class
.
can_render?
(
blob
)).
to
be_truthy
end
end
context
'when the binaryness does not match'
do
let
(
:blob
)
{
fake_blob
(
path:
'LICENSE'
,
binary:
true
)
}
it
'returns false'
do
expect
(
viewer_class
.
can_render?
(
blob
)).
to
be_falsey
end
end
end
context
'when the extension and file type are not supported'
do
let
(
:blob
)
{
fake_blob
(
path:
'file.txt'
)
}
it
'returns false'
do
...
...
@@ -153,34 +187,4 @@ describe BlobViewer::Base, model: true do
end
end
end
describe
'#prepare!'
do
context
'when the viewer is server side'
do
let
(
:blob
)
{
fake_blob
(
path:
'file.md'
)
}
before
do
viewer_class
.
client_side
=
false
end
it
'loads all blob data'
do
expect
(
blob
).
to
receive
(
:load_all_data!
)
viewer
.
prepare!
end
end
context
'when the viewer is client side'
do
let
(
:blob
)
{
fake_blob
(
path:
'file.md'
)
}
before
do
viewer_class
.
client_side
=
true
end
it
"doesn't load all blob data"
do
expect
(
blob
).
not_to
receive
(
:load_all_data!
)
viewer
.
prepare!
end
end
end
end
spec/models/blob_viewer/gitlab_ci_yml_spec.rb
0 → 100644
View file @
1c75e4e6
require
'spec_helper'
describe
BlobViewer
::
GitlabCiYml
,
model:
true
do
include
FakeBlobHelpers
let
(
:project
)
{
build
(
:project
)
}
let
(
:data
)
{
File
.
read
(
Rails
.
root
.
join
(
'spec/support/gitlab_stubs/gitlab_ci.yml'
))
}
let
(
:blob
)
{
fake_blob
(
path:
'.gitlab-ci.yml'
,
data:
data
)
}
subject
{
described_class
.
new
(
blob
)
}
describe
'#validation_message'
do
it
'calls prepare! on the viewer'
do
expect
(
subject
).
to
receive
(
:prepare!
)
subject
.
validation_message
end
context
'when the configuration is valid'
do
it
'returns nil'
do
expect
(
subject
.
validation_message
).
to
be_nil
end
end
context
'when the configuration is invalid'
do
let
(
:data
)
{
'oof'
}
it
'returns the error message'
do
expect
(
subject
.
validation_message
).
to
eq
(
'Invalid configuration format'
)
end
end
end
end
spec/models/blob_viewer/license_spec.rb
0 → 100644
View file @
1c75e4e6
require
'spec_helper'
describe
BlobViewer
::
License
,
model:
true
do
include
FakeBlobHelpers
let
(
:project
)
{
create
(
:project
,
:repository
)
}
let
(
:blob
)
{
fake_blob
(
path:
'LICENSE'
)
}
subject
{
described_class
.
new
(
blob
)
}
describe
'#license'
do
it
'returns the blob project repository license'
do
expect
(
subject
.
license
).
not_to
be_nil
expect
(
subject
.
license
).
to
eq
(
project
.
repository
.
license
)
end
end
describe
'#render_error'
do
context
'when there is no license'
do
before
do
allow
(
project
.
repository
).
to
receive
(
:license
).
and_return
(
nil
)
end
it
'returns :unknown_license'
do
expect
(
subject
.
render_error
).
to
eq
(
:unknown_license
)
end
end
context
'when there is a license'
do
it
'returns nil'
do
expect
(
subject
.
render_error
).
to
be_nil
end
end
end
end
spec/models/blob_viewer/route_map_spec.rb
0 → 100644
View file @
1c75e4e6
require
'spec_helper'
describe
BlobViewer
::
RouteMap
,
model:
true
do
include
FakeBlobHelpers
let
(
:project
)
{
build
(
:project
)
}
let
(
:data
)
do
<<-
MAP
.
strip_heredoc
# Team data
- source: 'data/team.yml'
public: 'team/'
MAP
end
let
(
:blob
)
{
fake_blob
(
path:
'.gitlab/route-map.yml'
,
data:
data
)
}
subject
{
described_class
.
new
(
blob
)
}
describe
'#validation_message'
do
it
'calls prepare! on the viewer'
do
expect
(
subject
).
to
receive
(
:prepare!
)
subject
.
validation_message
end
context
'when the configuration is valid'
do
it
'returns nil'
do
expect
(
subject
.
validation_message
).
to
be_nil
end
end
context
'when the configuration is invalid'
do
let
(
:data
)
{
'oof'
}
it
'returns the error message'
do
expect
(
subject
.
validation_message
).
to
eq
(
'Route map is not an array'
)
end
end
end
end
spec/models/blob_viewer/server_side_spec.rb
0 → 100644
View file @
1c75e4e6
require
'spec_helper'
describe
BlobViewer
::
ServerSide
,
model:
true
do
include
FakeBlobHelpers
let
(
:project
)
{
build
(
:empty_project
)
}
let
(
:viewer_class
)
do
Class
.
new
(
BlobViewer
::
Base
)
do
include
BlobViewer
::
ServerSide
end
end
subject
{
viewer_class
.
new
(
blob
)
}
describe
'#prepare!'
do
let
(
:blob
)
{
fake_blob
(
path:
'file.txt'
)
}
it
'loads all blob data'
do
expect
(
blob
).
to
receive
(
:load_all_data!
)
subject
.
prepare!
end
end
end
spec/models/repository_spec.rb
View file @
1c75e4e6
...
...
@@ -2,7 +2,7 @@ require 'spec_helper'
describe
Repository
,
models:
true
do
include
RepoHelpers
TestBlob
=
Struct
.
new
(
:
name
)
TestBlob
=
Struct
.
new
(
:
path
)
let
(
:project
)
{
create
(
:project
,
:repository
)
}
let
(
:repository
)
{
project
.
repository
}
...
...
@@ -565,31 +565,31 @@ describe Repository, models: true do
it
'accepts changelog'
do
expect
(
repository
.
tree
).
to
receive
(
:blobs
).
and_return
([
TestBlob
.
new
(
'changelog'
)])
expect
(
repository
.
changelog
.
name
).
to
eq
(
'changelog'
)
expect
(
repository
.
changelog
.
path
).
to
eq
(
'changelog'
)
end
it
'accepts news instead of changelog'
do
expect
(
repository
.
tree
).
to
receive
(
:blobs
).
and_return
([
TestBlob
.
new
(
'news'
)])
expect
(
repository
.
changelog
.
name
).
to
eq
(
'news'
)
expect
(
repository
.
changelog
.
path
).
to
eq
(
'news'
)
end
it
'accepts history instead of changelog'
do
expect
(
repository
.
tree
).
to
receive
(
:blobs
).
and_return
([
TestBlob
.
new
(
'history'
)])
expect
(
repository
.
changelog
.
name
).
to
eq
(
'history'
)
expect
(
repository
.
changelog
.
path
).
to
eq
(
'history'
)
end
it
'accepts changes instead of changelog'
do
expect
(
repository
.
tree
).
to
receive
(
:blobs
).
and_return
([
TestBlob
.
new
(
'changes'
)])
expect
(
repository
.
changelog
.
name
).
to
eq
(
'changes'
)
expect
(
repository
.
changelog
.
path
).
to
eq
(
'changes'
)
end
it
'is case-insensitive'
do
expect
(
repository
.
tree
).
to
receive
(
:blobs
).
and_return
([
TestBlob
.
new
(
'CHANGELOG'
)])
expect
(
repository
.
changelog
.
name
).
to
eq
(
'CHANGELOG'
)
expect
(
repository
.
changelog
.
path
).
to
eq
(
'CHANGELOG'
)
end
end
...
...
@@ -624,7 +624,7 @@ describe Repository, models: true do
repository
.
create_file
(
user
,
'LICENSE'
,
'Copyright!'
,
message:
'Add LICENSE'
,
branch_name:
'master'
)
expect
(
repository
.
license_blob
.
name
).
to
eq
(
'LICENSE'
)
expect
(
repository
.
license_blob
.
path
).
to
eq
(
'LICENSE'
)
end
%w[LICENSE LICENCE LiCensE LICENSE.md LICENSE.foo COPYING COPYING.md]
.
each
do
|
filename
|
...
...
@@ -654,7 +654,7 @@ describe Repository, models: true do
expect
(
repository
.
license_key
).
to
be_nil
end
it
'
detects license file with no recognizable open-source license content
'
do
it
'
returns nil when the content is not recognizable
'
do
repository
.
create_file
(
user
,
'LICENSE'
,
'Copyright!'
,
message:
'Add LICENSE'
,
branch_name:
'master'
)
...
...
@@ -670,12 +670,45 @@ describe Repository, models: true do
end
end
describe
'#license'
do
before
do
repository
.
delete_file
(
user
,
'LICENSE'
,
message:
'Remove LICENSE'
,
branch_name:
'master'
)
end
it
'returns nil when no license is detected'
do
expect
(
repository
.
license
).
to
be_nil
end
it
'returns nil when the repository does not exist'
do
expect
(
repository
).
to
receive
(
:exists?
).
and_return
(
false
)
expect
(
repository
.
license
).
to
be_nil
end
it
'returns nil when the content is not recognizable'
do
repository
.
create_file
(
user
,
'LICENSE'
,
'Copyright!'
,
message:
'Add LICENSE'
,
branch_name:
'master'
)
expect
(
repository
.
license
).
to
be_nil
end
it
'returns the license'
do
license
=
Licensee
::
License
.
new
(
'mit'
)
repository
.
create_file
(
user
,
'LICENSE'
,
license
.
content
,
message:
'Add LICENSE'
,
branch_name:
'master'
)
expect
(
repository
.
license
).
to
eq
(
license
)
end
end
describe
"#gitlab_ci_yml"
,
caching:
true
do
it
'returns valid file'
do
files
=
[
TestBlob
.
new
(
'file'
),
TestBlob
.
new
(
'.gitlab-ci.yml'
),
TestBlob
.
new
(
'copying'
)]
expect
(
repository
.
tree
).
to
receive
(
:blobs
).
and_return
(
files
)
expect
(
repository
.
gitlab_ci_yml
.
name
).
to
eq
(
'.gitlab-ci.yml'
)
expect
(
repository
.
gitlab_ci_yml
.
path
).
to
eq
(
'.gitlab-ci.yml'
)
end
it
'returns nil if not exists'
do
...
...
@@ -1825,11 +1858,12 @@ describe Repository, models: true do
describe
'#refresh_method_caches'
do
it
'refreshes the caches of the given types'
do
expect
(
repository
).
to
receive
(
:expire_method_caches
).
with
(
%i(rendered_readme license_blob license_key)
)
with
(
%i(rendered_readme license_blob license_key
license
)
)
expect
(
repository
).
to
receive
(
:rendered_readme
)
expect
(
repository
).
to
receive
(
:license_blob
)
expect
(
repository
).
to
receive
(
:license_key
)
expect
(
repository
).
to
receive
(
:license
)
repository
.
refresh_method_caches
(
%i(readme license)
)
end
...
...
spec/views/projects/blob/_viewer.html.haml_spec.rb
View file @
1c75e4e6
...
...
@@ -47,10 +47,10 @@ describe 'projects/blob/_viewer.html.haml', :view do
expect
(
rendered
).
to
have_css
(
'.blob-viewer[data-url]'
)
end
it
'
displays a spinne
r'
do
it
'
renders the loading indicato
r'
do
render_view
expect
(
rendered
).
to
have_css
(
'i[aria-label="Loading content"]
'
)
expect
(
view
).
to
render_template
(
'projects/blob/viewers/_loading
'
)
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