Commit 27f2ca94 authored by Douwe Maan's avatar Douwe Maan

Add 'View on [env]' link to blobs and individual files in diffs

parent d147688a
...@@ -30,6 +30,9 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -30,6 +30,9 @@ class Projects::BlobController < Projects::ApplicationController
end end
def show def show
branch_name = @ref if @repository.branch_exists?(@ref)
@environment = @project.latest_environment_for(@commit, ref: branch_name)
@environment = nil unless can?(current_user, :read_environment, @environment)
end end
def edit def edit
......
...@@ -95,6 +95,9 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -95,6 +95,9 @@ class Projects::CommitController < Projects::ApplicationController
@diffs = commit.diffs(opts) @diffs = commit.diffs(opts)
@notes_count = commit.notes.count @notes_count = commit.notes.count
@environment = @project.latest_environment_for(@commit)
@environment = nil unless can?(current_user, :read_environment, @environment)
end end
def define_note_vars def define_note_vars
......
...@@ -57,6 +57,10 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -57,6 +57,10 @@ class Projects::CompareController < Projects::ApplicationController
@diffs = @compare.diffs(diff_options) @diffs = @compare.diffs(diff_options)
branch_name = @head_ref if @repository.branch_exists?(@head_ref)
@environment = @project.latest_environment_for(@commit, ref: branch_name)
@environment = nil unless can?(current_user, :read_environment, @environment)
@diff_notes_disabled = true @diff_notes_disabled = true
@grouped_diff_discussions = {} @grouped_diff_discussions = {}
end end
......
...@@ -103,6 +103,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -103,6 +103,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
end end
@environment = @merge_request.latest_environment
@environment = nil unless can?(current_user, :read_environment, @environment)
respond_to do |format| respond_to do |format|
format.html { define_discussion_vars } format.html { define_discussion_vars }
format.json do format.json do
...@@ -245,7 +248,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -245,7 +248,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
@diff_notes_disabled = true @diff_notes_disabled = true
render json: { html: view_to_html_string('projects/merge_requests/_new_diffs', diffs: @diffs) } @environment = @merge_request.latest_environment
@environment = nil unless can?(current_user, :read_environment, @environment)
render json: { html: view_to_html_string('projects/merge_requests/_new_diffs', diffs: @diffs, environment: @environment) }
end end
end end
end end
......
...@@ -205,6 +205,17 @@ module CommitsHelper ...@@ -205,6 +205,17 @@ module CommitsHelper
end end
end end
def view_on_environment_btn(commit_sha, diff_new_path, environment)
return unless environment && commit_sha
external_url = environment.external_url_for(diff_new_path, commit_sha)
return unless external_url
link_to(external_url, class: 'btn btn-file-option has-tooltip', target: '_blank', title: "View on #{environment.formatted_external_url}", data: { container: 'body' }) do
icon('external-link')
end
end
def truncate_sha(sha) def truncate_sha(sha)
Commit.truncate_sha(sha) Commit.truncate_sha(sha)
end end
......
...@@ -283,13 +283,7 @@ module Ci ...@@ -283,13 +283,7 @@ module Ci
def ci_yaml_file def ci_yaml_file
return @ci_yaml_file if defined?(@ci_yaml_file) return @ci_yaml_file if defined?(@ci_yaml_file)
@ci_yaml_file ||= begin @ci_yaml_file ||= project.repository.ci_yaml_file(sha)
blob = project.repository.blob_at(sha, '.gitlab-ci.yml')
blob.load_all_data!(project.repository)
blob.data
rescue
nil
end
end end
def has_yaml_errors? def has_yaml_errors?
......
...@@ -51,6 +51,14 @@ class Environment < ActiveRecord::Base ...@@ -51,6 +51,14 @@ class Environment < ActiveRecord::Base
state :stopped state :stopped
end end
def self.latest_for_commit(environments, commit)
environments.sort_by do |environment|
deployment = environment.first_deployment_for(commit)
deployment.try(:created_at) || DateTime.parse('1970-01-01')
end.last
end
def predefined_variables def predefined_variables
[ [
{ key: 'CI_ENVIRONMENT_NAME', value: name, public: true }, { key: 'CI_ENVIRONMENT_NAME', value: name, public: true },
...@@ -171,6 +179,16 @@ class Environment < ActiveRecord::Base ...@@ -171,6 +179,16 @@ class Environment < ActiveRecord::Base
self.slug = slugified self.slug = slugified
end end
def external_url_for(path, commit_sha)
return unless self.external_url
public_path = project.public_path_for_source_path(path, commit_sha)
return unless public_path
# TODO: Verify this can't be used for XSS
URI.join(external_url, public_path).to_s
end
private private
# Slugifying a name may remove the uniqueness guarantee afforded by it being # Slugifying a name may remove the uniqueness guarantee afforded by it being
......
...@@ -729,6 +729,12 @@ class MergeRequest < ActiveRecord::Base ...@@ -729,6 +729,12 @@ class MergeRequest < ActiveRecord::Base
end end
end end
def latest_environment
return @latest_environment if defined?(@latest_environment)
@latest_environment = Environment.latest_for_commit(environments, diff_head_commit)
end
def state_human_name def state_human_name
if merged? if merged?
"Merged" "Merged"
......
...@@ -1307,10 +1307,17 @@ class Project < ActiveRecord::Base ...@@ -1307,10 +1307,17 @@ class Project < ActiveRecord::Base
end end
def environments_for(ref, commit: nil, with_tags: false) def environments_for(ref, commit: nil, with_tags: false)
deps =
if ref
deployments_query = with_tags ? 'ref = ? OR tag IS TRUE' : 'ref = ?' deployments_query = with_tags ? 'ref = ? OR tag IS TRUE' : 'ref = ?'
deployments.where(deployments_query, ref.to_s)
elsif commit
deps = deployments.where(sha: commit.sha)
else
Deployment.none
end
environment_ids = deployments environment_ids = deps
.where(deployments_query, ref.to_s)
.group(:environment_id) .group(:environment_id)
.select(:environment_id) .select(:environment_id)
...@@ -1324,12 +1331,46 @@ class Project < ActiveRecord::Base ...@@ -1324,12 +1331,46 @@ class Project < ActiveRecord::Base
end end
end end
def latest_environment_for(commit, ref: nil)
environments = environments_for(ref, commit: commit)
Environment.latest_for_commit(environments, commit)
end
def environments_recently_updated_on_branch(branch) def environments_recently_updated_on_branch(branch)
environments_for(branch).select do |environment| environments_for(branch).select do |environment|
environment.recently_updated_on_branch?(branch) environment.recently_updated_on_branch?(branch)
end end
end end
def route_map_for_commit(commit_sha)
@route_maps_by_commit ||= Hash.new do |h, sha|
h[sha] = begin
data = repository.route_map_file(sha)
next unless data
# TODO: Validate
YAML.safe_load(data).map do |mapping|
{
source: Regexp.new("^#{mapping['source'][1...-1]}$"),
public: mapping['public']
}
end
end
end
@route_maps_by_commit[commit_sha]
end
def public_path_for_source_path(path, commit_sha)
map = route_map_for_commit(commit_sha)
return unless map
mapping = map.find { |mapping| path =~ mapping[:source] }
return unless mapping
path.sub(mapping[:source], mapping[:public])
end
private private
def cross_namespace_reference?(from) def cross_namespace_reference?(from)
......
...@@ -35,6 +35,9 @@ class Repository ...@@ -35,6 +35,9 @@ class Repository
avatar: :avatar avatar: :avatar
} }
ROUTE_MAP_PATH = '.gitlab/route-map.yml'
GITLAB_CI_YML_PATH = '.gitlab-ci.yml'
# Wraps around the given method and caches its output in Redis and an instance # Wraps around the given method and caches its output in Redis and an instance
# variable. # variable.
# #
...@@ -1184,6 +1187,20 @@ class Repository ...@@ -1184,6 +1187,20 @@ class Repository
end end
end end
def route_map_file(sha)
blob = blob_at(sha, ROUTE_MAP_PATH)
return unless blob
blob.load_all_data!(self)
blob.data
end
def ci_yaml_file(sha)
blob = blob_at(sha, GITLAB_CI_YML_PATH)
return unless blob
blob.load_all_data!(self)
blob.data
end
private private
def git_action(index, action) def git_action(index, action)
......
.btn-group
= view_on_environment_btn(@commit.sha, @path, @environment) if @environment
.btn-group.tree-btn-group .btn-group.tree-btn-group
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id), = link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
class: 'btn btn-sm', target: '_blank' class: 'btn btn-sm', target: '_blank'
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= render "ci_menu" = render "ci_menu"
- else - else
.block-connector .block-connector
= render "projects/diffs/diffs", diffs: @diffs = render "projects/diffs/diffs", diffs: @diffs, environment: @environment
= render "projects/notes/notes_with_form" = render "projects/notes/notes_with_form"
- if can_collaborate_with_project? - if can_collaborate_with_project?
- %w(revert cherry-pick).each do |type| - %w(revert cherry-pick).each do |type|
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- if @commits.present? - if @commits.present?
= render "projects/commits/commit_list" = render "projects/commits/commit_list"
= render "projects/diffs/diffs", diffs: @diffs = render "projects/diffs/diffs", diffs: @diffs, environment: @environment
- else - else
.light-well .light-well
.center .center
......
- environment = local_assigns.fetch(:environment, nil)
- show_whitespace_toggle = local_assigns.fetch(:show_whitespace_toggle, true) - show_whitespace_toggle = local_assigns.fetch(:show_whitespace_toggle, true)
- can_create_note = !@diff_notes_disabled && can?(current_user, :create_note, diffs.project) - can_create_note = !@diff_notes_disabled && can?(current_user, :create_note, diffs.project)
- diff_files = diffs.diff_files - diff_files = diffs.diff_files
...@@ -30,4 +31,4 @@ ...@@ -30,4 +31,4 @@
- file_hash = hexdigest(diff_file.file_path) - file_hash = hexdigest(diff_file.file_path)
= render 'projects/diffs/file', file_hash: file_hash, project: diffs.project, = render 'projects/diffs/file', file_hash: file_hash, project: diffs.project,
diff_file: diff_file, diff_commit: diff_commit, blob: blob diff_file: diff_file, diff_commit: diff_commit, blob: blob, environment: environment
- environment = local_assigns.fetch(:environment, nil)
.diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id) } .diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id) }
.file-title .file-title
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}" = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}"
...@@ -14,5 +15,6 @@ ...@@ -14,5 +15,6 @@
blob: blob, link_opts: link_opts) blob: blob, link_opts: link_opts)
= view_file_btn(diff_commit.id, diff_file.new_path, project) = view_file_btn(diff_commit.id, diff_file.new_path, project)
= view_on_environment_btn(diff_commit.id, diff_file.new_path, environment) if environment
= render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, blob: blob, project: project = render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, blob: blob, project: project
= render "projects/diffs/diffs", diffs: @diffs, show_whitespace_toggle: false = render "projects/diffs/diffs", diffs: @diffs, environment: @environment, show_whitespace_toggle: false
- if @merge_request_diff.collected? || @merge_request_diff.overflow? - if @merge_request_diff.collected? || @merge_request_diff.overflow?
= render 'projects/merge_requests/show/versions' = render 'projects/merge_requests/show/versions'
= render "projects/diffs/diffs", diffs: @diffs = render "projects/diffs/diffs", diffs: @diffs, environment: @environment
- elsif @merge_request_diff.empty? - elsif @merge_request_diff.empty?
.nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch} .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch}
---
title: Add 'View on [env]' link to blobs and individual files in diffs
merge_request:
author:
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment