Commit cd836d31 authored by Stan Hu's avatar Stan Hu

Merge branch '31397-go-get-meta-source' into 'master'

Support go-source meta tag for godoc.org

Closes #31397

See merge request gitlab-org/gitlab!19888
parents 5e60ca15 0e6f639a
---
title: Support go-source meta tag for godoc.org
merge_request: 19888
author: Ethan Reesor (@firelizzard)
type: changed
...@@ -200,9 +200,14 @@ When [renaming a user](../profile/index.md#changing-your-username), ...@@ -200,9 +200,14 @@ When [renaming a user](../profile/index.md#changing-your-username),
## Use your project as a Go package ## Use your project as a Go package
Any project can be used as a Go package including private projects in subgroups. To use packages Any project can be used as a Go package including private projects in subgroups.
hosted in private projects with the `go get` command, use a [`.netrc` file](https://ec.haxx.se/usingcurl-netrc.html) GitLab responds correctly to `go get` and `godoc.org` discovery requests,
and a [personal access token](../profile/personal_access_tokens.md) in the password field. including the [`go-import`](https://golang.org/cmd/go/#hdr-Remote_import_paths)
and [`go-source`](https://github.com/golang/gddo/wiki/Source-Code-Links) meta
tags, respectively. To use packages hosted in private projects with the `go get`
command, use a [`.netrc` file](https://ec.haxx.se/usingcurl-netrc.html) and a
[personal access token](../profile/personal_access_tokens.md) in the password
field.
For example: For example:
......
...@@ -25,13 +25,13 @@ module Gitlab ...@@ -25,13 +25,13 @@ module Gitlab
def render_go_doc(request) def render_go_doc(request)
return unless go_request?(request) return unless go_request?(request)
path = project_path(request) path, branch = project_path(request)
return unless path return unless path
body = go_body(path) body, code = go_response(path, branch)
return unless body return unless body
response = Rack::Response.new(body, 200, { 'Content-Type' => 'text/html' }) response = Rack::Response.new(body, code, { 'Content-Type' => 'text/html' })
response.finish response.finish
end end
...@@ -39,8 +39,15 @@ module Gitlab ...@@ -39,8 +39,15 @@ module Gitlab
request["go-get"].to_i == 1 && request.env["PATH_INFO"].present? request["go-get"].to_i == 1 && request.env["PATH_INFO"].present?
end end
def go_body(path) def go_response(path, branch)
config = Gitlab.config config = Gitlab.config
body_tag = content_tag :body, "go get #{config.gitlab.url}/#{path}"
unless branch
html_tag = content_tag :html, body_tag
return html_tag, 404
end
project_url = Gitlab::Utils.append_path(config.gitlab.url, path) project_url = Gitlab::Utils.append_path(config.gitlab.url, path)
import_prefix = strip_url(project_url.to_s) import_prefix = strip_url(project_url.to_s)
...@@ -52,9 +59,11 @@ module Gitlab ...@@ -52,9 +59,11 @@ module Gitlab
"#{project_url}.git" "#{project_url}.git"
end end
meta_tag = tag :meta, name: 'go-import', content: "#{import_prefix} git #{repository_url}" meta_import_tag = tag :meta, name: 'go-import', content: "#{import_prefix} git #{repository_url}"
head_tag = content_tag :head, meta_tag meta_source_tag = tag :meta, name: 'go-source', content: "#{import_prefix} #{project_url} #{project_url}/tree/#{branch}{/dir} #{project_url}/blob/#{branch}{/dir}/{file}#L{line}"
content_tag :html, head_tag head_tag = content_tag :head, meta_import_tag + meta_source_tag
html_tag = content_tag :html, head_tag + body_tag
[html_tag, 200]
end end
def strip_url(url) def strip_url(url)
...@@ -80,9 +89,6 @@ module Gitlab ...@@ -80,9 +89,6 @@ module Gitlab
path_segments = path.split('/') path_segments = path.split('/')
simple_project_path = path_segments.first(2).join('/') simple_project_path = path_segments.first(2).join('/')
# If the path is at most 2 segments long, it is a simple `namespace/project` path and we're done
return simple_project_path if path_segments.length <= 2
project_paths = [] project_paths = []
begin begin
project_paths << path_segments.join('/') project_paths << path_segments.join('/')
...@@ -94,7 +100,7 @@ module Gitlab ...@@ -94,7 +100,7 @@ module Gitlab
if project if project
# If a project is found and the user has access, we return the full project path # If a project is found and the user has access, we return the full project path
project.full_path return project.full_path, project.default_branch
else else
# If not, we return the first two components as if it were a simple `namespace/project` path, # If not, we return the first two components as if it were a simple `namespace/project` path,
# so that we don't reveal the existence of a nested project the user doesn't have access to. # so that we don't reveal the existence of a nested project the user doesn't have access to.
...@@ -105,7 +111,7 @@ module Gitlab ...@@ -105,7 +111,7 @@ module Gitlab
# `go get gitlab.com/group/subgroup/project/subpackage` will not work for private projects. # `go get gitlab.com/group/subgroup/project/subpackage` will not work for private projects.
# `go get gitlab.com/group/subgroup/project.git/subpackage` will work, since Go is smart enough # `go get gitlab.com/group/subgroup/project.git/subpackage` will work, since Go is smart enough
# to figure that out. `import 'gitlab.com/...'` behaves the same as `go get`. # to figure that out. `import 'gitlab.com/...'` behaves the same as `go get`.
simple_project_path return simple_project_path, 'master'
end end
end end
......
...@@ -30,13 +30,13 @@ describe Gitlab::Middleware::Go do ...@@ -30,13 +30,13 @@ describe Gitlab::Middleware::Go do
shared_examples 'go-get=1' do |enabled_protocol:| shared_examples 'go-get=1' do |enabled_protocol:|
context 'with simple 2-segment project path' do context 'with simple 2-segment project path' do
let!(:project) { create(:project, :private) } let!(:project) { create(:project, :private, :repository) }
context 'with subpackages' do context 'with subpackages' do
let(:path) { "#{project.full_path}/subpackage" } let(:path) { "#{project.full_path}/subpackage" }
it 'returns the full project path' do it 'returns the full project path' do
expect_response_with_path(go, enabled_protocol, project.full_path) expect_response_with_path(go, enabled_protocol, project.full_path, project.default_branch)
end end
end end
...@@ -44,19 +44,19 @@ describe Gitlab::Middleware::Go do ...@@ -44,19 +44,19 @@ describe Gitlab::Middleware::Go do
let(:path) { project.full_path } let(:path) { project.full_path }
it 'returns the full project path' do it 'returns the full project path' do
expect_response_with_path(go, enabled_protocol, project.full_path) expect_response_with_path(go, enabled_protocol, project.full_path, project.default_branch)
end end
end end
end end
context 'with a nested project path' do context 'with a nested project path' do
let(:group) { create(:group, :nested) } let(:group) { create(:group, :nested) }
let!(:project) { create(:project, :public, namespace: group) } let!(:project) { create(:project, :public, :repository, namespace: group) }
shared_examples 'a nested project' do shared_examples 'a nested project' do
context 'when the project is public' do context 'when the project is public' do
it 'returns the full project path' do it 'returns the full project path' do
expect_response_with_path(go, enabled_protocol, project.full_path) expect_response_with_path(go, enabled_protocol, project.full_path, project.default_branch)
end end
end end
...@@ -67,7 +67,7 @@ describe Gitlab::Middleware::Go do ...@@ -67,7 +67,7 @@ describe Gitlab::Middleware::Go do
shared_examples 'unauthorized' do shared_examples 'unauthorized' do
it 'returns the 2-segment group path' do it 'returns the 2-segment group path' do
expect_response_with_path(go, enabled_protocol, group.full_path) expect_response_with_path(go, enabled_protocol, group.full_path, project.default_branch)
end end
end end
...@@ -85,7 +85,7 @@ describe Gitlab::Middleware::Go do ...@@ -85,7 +85,7 @@ describe Gitlab::Middleware::Go do
shared_examples 'authenticated' do shared_examples 'authenticated' do
context 'with access to the project' do context 'with access to the project' do
it 'returns the full project path' do it 'returns the full project path' do
expect_response_with_path(go, enabled_protocol, project.full_path) expect_response_with_path(go, enabled_protocol, project.full_path, project.default_branch)
end end
end end
...@@ -160,6 +160,36 @@ describe Gitlab::Middleware::Go do ...@@ -160,6 +160,36 @@ describe Gitlab::Middleware::Go do
go go
end end
end end
context 'with a public project without a repository' do
let!(:project) { create(:project, :public) }
let(:path) { project.full_path }
it 'returns 404' do
response = go
expect(response[0]).to eq(404)
expect(response[1]['Content-Type']).to eq('text/html')
expected_body = %{<html><body>go get #{Gitlab.config.gitlab.url}/#{project.full_path}</body></html>}
expect(response[2].body).to eq([expected_body])
end
end
context 'with a non-standard head' do
let(:user) { create(:user) }
let!(:project) { create(:project, :public, :repository) }
let(:path) { project.full_path }
let(:default_branch) { 'default_branch' }
before do
project.add_maintainer(user)
project.repository.add_branch(user, default_branch, 'master')
project.change_head(default_branch)
end
it 'returns the full project path' do
expect_response_with_path(go, enabled_protocol, project.full_path, default_branch)
end
end
end end
context 'with SSH disabled' do context 'with SSH disabled' do
...@@ -199,16 +229,17 @@ describe Gitlab::Middleware::Go do ...@@ -199,16 +229,17 @@ describe Gitlab::Middleware::Go do
middleware.call(env) middleware.call(env)
end end
def expect_response_with_path(response, protocol, path) def expect_response_with_path(response, protocol, path, branch)
repository_url = case protocol repository_url = case protocol
when :ssh when :ssh
"ssh://#{Gitlab.config.gitlab.user}@#{Gitlab.config.gitlab.host}/#{path}.git" "ssh://#{Gitlab.config.gitlab.user}@#{Gitlab.config.gitlab.host}/#{path}.git"
when :http, nil when :http, nil
"http://#{Gitlab.config.gitlab.host}/#{path}.git" "http://#{Gitlab.config.gitlab.host}/#{path}.git"
end end
project_url = "http://#{Gitlab.config.gitlab.host}/#{path}"
expect(response[0]).to eq(200) expect(response[0]).to eq(200)
expect(response[1]['Content-Type']).to eq('text/html') expect(response[1]['Content-Type']).to eq('text/html')
expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git #{repository_url}" /></head></html>} expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git #{repository_url}" /><meta name="go-source" content="#{Gitlab.config.gitlab.host}/#{path} #{project_url} #{project_url}/tree/#{branch}{/dir} #{project_url}/blob/#{branch}{/dir}/{file}#L{line}" /></head><body>go get #{Gitlab.config.gitlab.url}/#{path}</body></html>}
expect(response[2].body).to eq([expected_body]) expect(response[2].body).to eq([expected_body])
end end
end end
......
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