Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/images/ci/arch.jpgbin25222 -> 0 bytes
-rw-r--r--app/assets/images/ci/favicon.icobin5430 -> 0 bytes
-rw-r--r--app/assets/images/ci/loader.gifbin4405 -> 0 bytes
-rw-r--r--app/assets/images/ci/no_avatar.pngbin1337 -> 0 bytes
-rw-r--r--app/assets/images/ci/rails.pngbin6646 -> 0 bytes
-rw-r--r--app/assets/images/ci/service_sample.pngbin76024 -> 0 bytes
-rw-r--r--app/assets/stylesheets/application.scss1
-rw-r--r--app/assets/stylesheets/framework.scss1
-rw-r--r--app/assets/stylesheets/framework/animations.scss72
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/framework/zen.scss2
-rw-r--r--app/assets/stylesheets/pages/projects.scss2
-rw-r--r--app/controllers/admin/abuse_reports_controller.rb2
-rw-r--r--app/controllers/admin/broadcast_messages_controller.rb2
-rw-r--r--app/controllers/admin/keys_controller.rb2
-rw-r--r--app/controllers/admin/spam_logs_controller.rb2
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/controllers/concerns/toggle_subscription_action.rb2
-rw-r--r--app/controllers/dashboard/todos_controller.rb4
-rw-r--r--app/controllers/groups/group_members_controller.rb2
-rw-r--r--app/controllers/profiles/emails_controller.rb2
-rw-r--r--app/controllers/profiles/keys_controller.rb2
-rw-r--r--app/controllers/projects/container_registry_controller.rb34
-rw-r--r--app/controllers/projects/imports_controller.rb1
-rw-r--r--app/controllers/projects/milestones_controller.rb2
-rw-r--r--app/controllers/projects/notes_controller.rb4
-rw-r--r--app/controllers/projects/project_members_controller.rb4
-rw-r--r--app/controllers/projects/protected_branches_controller.rb2
-rw-r--r--app/controllers/projects/variables_controller.rb30
-rw-r--r--app/helpers/gitlab_routing_helper.rb4
-rw-r--r--app/helpers/projects_helper.rb10
-rw-r--r--app/models/merge_request_diff.rb4
-rw-r--r--app/models/namespace.rb8
-rw-r--r--app/models/project.rb68
-rw-r--r--app/models/project_services/slack_service/issue_message.rb19
-rw-r--r--app/models/user.rb9
-rw-r--r--app/services/auth/container_registry_authentication_service.rb13
-rw-r--r--app/services/issues/update_service.rb10
-rw-r--r--app/services/projects/create_service.rb21
-rw-r--r--app/services/projects/destroy_service.rb10
-rw-r--r--app/services/projects/transfer_service.rb5
-rw-r--r--app/services/system_note_service.rb18
-rw-r--r--app/views/groups/_activities.html.haml2
-rw-r--r--app/views/groups/issues.atom.builder8
-rw-r--r--app/views/layouts/nav/_project.html.haml7
-rw-r--r--app/views/projects/_md_preview.html.haml2
-rw-r--r--app/views/projects/_zen.html.haml2
-rw-r--r--app/views/projects/commits/_commits.html.haml2
-rw-r--r--app/views/projects/container_registry/_header_title.html.haml1
-rw-r--r--app/views/projects/container_registry/_tag.html.haml21
-rw-r--r--app/views/projects/container_registry/index.html.haml40
-rw-r--r--app/views/projects/deploy_keys/index.html.haml4
-rw-r--r--app/views/projects/hooks/index.html.haml2
-rw-r--r--app/views/projects/protected_branches/_branches_list.html.haml2
-rw-r--r--app/views/projects/triggers/index.html.haml2
-rw-r--r--app/views/projects/variables/_content.html.haml8
-rw-r--r--app/views/projects/variables/_form.html.haml10
-rw-r--r--app/views/projects/variables/_table.html.haml25
-rw-r--r--app/views/projects/variables/index.html.haml17
-rw-r--r--app/views/projects/variables/show.html.haml41
-rw-r--r--app/workers/repository_import_worker.rb2
61 files changed, 439 insertions, 137 deletions
diff --git a/app/assets/images/ci/arch.jpg b/app/assets/images/ci/arch.jpg
deleted file mode 100644
index 0e05674e840..00000000000
--- a/app/assets/images/ci/arch.jpg
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci/favicon.ico b/app/assets/images/ci/favicon.ico
deleted file mode 100644
index 9663d4d00b9..00000000000
--- a/app/assets/images/ci/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci/loader.gif b/app/assets/images/ci/loader.gif
deleted file mode 100644
index 2fcb8f2da0d..00000000000
--- a/app/assets/images/ci/loader.gif
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci/no_avatar.png b/app/assets/images/ci/no_avatar.png
deleted file mode 100644
index 752d26adba7..00000000000
--- a/app/assets/images/ci/no_avatar.png
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci/rails.png b/app/assets/images/ci/rails.png
deleted file mode 100644
index d5edc04e65f..00000000000
--- a/app/assets/images/ci/rails.png
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci/service_sample.png b/app/assets/images/ci/service_sample.png
deleted file mode 100644
index 65d29e3fd89..00000000000
--- a/app/assets/images/ci/service_sample.png
+++ /dev/null
Binary files differ
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 69b3b6586de..e2d590f4df4 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -10,7 +10,6 @@
*= require dropzone/basic
*= require cal-heatmap
*= require cropper.css
- *= require animate
*/
/*
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 560de9fc0bd..3cbddc59f11 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -5,6 +5,7 @@
@import 'framework/tw_bootstrap';
@import "framework/layout";
+@import "framework/animations.scss";
@import "framework/avatar.scss";
@import "framework/blocks.scss";
@import "framework/buttons.scss";
diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss
new file mode 100644
index 00000000000..1fec61bdba1
--- /dev/null
+++ b/app/assets/stylesheets/framework/animations.scss
@@ -0,0 +1,72 @@
+// This file is based off animate.css 3.5.1, available here:
+// https://github.com/daneden/animate.css/blob/3.5.1/animate.css
+//
+// animate.css - http://daneden.me/animate
+// Version - 3.5.1
+// Licensed under the MIT license - http://opensource.org/licenses/MIT
+//
+// Copyright (c) 2016 Daniel Eden
+
+.animated {
+ -webkit-animation-duration: 1s;
+ animation-duration: 1s;
+ -webkit-animation-fill-mode: both;
+ animation-fill-mode: both;
+}
+
+.animated.infinite {
+ -webkit-animation-iteration-count: infinite;
+ animation-iteration-count: infinite;
+}
+
+.animated.hinge {
+ -webkit-animation-duration: 2s;
+ animation-duration: 2s;
+}
+
+.animated.flipOutX,
+.animated.flipOutY,
+.animated.bounceIn,
+.animated.bounceOut {
+ -webkit-animation-duration: .75s;
+ animation-duration: .75s;
+}
+
+@-webkit-keyframes pulse {
+ from {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+
+ 50% {
+ -webkit-transform: scale3d(1.05, 1.05, 1.05);
+ transform: scale3d(1.05, 1.05, 1.05);
+ }
+
+ to {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+@keyframes pulse {
+ from {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+
+ 50% {
+ -webkit-transform: scale3d(1.05, 1.05, 1.05);
+ transform: scale3d(1.05, 1.05, 1.05);
+ }
+
+ to {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+.pulse {
+ -webkit-animation-name: pulse;
+ animation-name: pulse;
+}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 5fa4c266607..7360bdbcc82 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -12,7 +12,7 @@ $gutter_inner_width: 258px;
*/
$border-color: #e5e5e5;
$focus-border-color: #3aabf0;
-$table-border-color: #ececec;
+$table-border-color: #f0f0f0;
$background-color: #fafafa;
/*
diff --git a/app/assets/stylesheets/framework/zen.scss b/app/assets/stylesheets/framework/zen.scss
index f870ea0d87f..ff02ebdd34c 100644
--- a/app/assets/stylesheets/framework/zen.scss
+++ b/app/assets/stylesheets/framework/zen.scss
@@ -32,7 +32,7 @@
}
}
-.zen-cotrol {
+.zen-control {
padding: 0;
color: #555;
background: none;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index c20f04653fc..a3690e40e28 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -7,7 +7,7 @@
}
.no-ssh-key-message, .project-limit-message {
background-color: #f28d35;
- margin-bottom: 16px;
+ margin-bottom: 0;
}
.new_project,
.edit_project {
diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb
index e9b0972bdd8..5055c318a5f 100644
--- a/app/controllers/admin/abuse_reports_controller.rb
+++ b/app/controllers/admin/abuse_reports_controller.rb
@@ -9,6 +9,6 @@ class Admin::AbuseReportsController < Admin::ApplicationController
abuse_report.remove_user(deleted_by: current_user) if params[:remove_user]
abuse_report.destroy
- render nothing: true
+ head :ok
end
end
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
index fc342924987..82055006ac0 100644
--- a/app/controllers/admin/broadcast_messages_controller.rb
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -32,7 +32,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
respond_to do |format|
format.html { redirect_back_or_default(default: { action: 'index' }) }
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
diff --git a/app/controllers/admin/keys_controller.rb b/app/controllers/admin/keys_controller.rb
index cb33fdd9763..054bb52b696 100644
--- a/app/controllers/admin/keys_controller.rb
+++ b/app/controllers/admin/keys_controller.rb
@@ -6,7 +6,7 @@ class Admin::KeysController < Admin::ApplicationController
respond_to do |format|
format.html
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb
index 377e9741e5f..3a2f0185315 100644
--- a/app/controllers/admin/spam_logs_controller.rb
+++ b/app/controllers/admin/spam_logs_controller.rb
@@ -11,7 +11,7 @@ class Admin::SpamLogsController < Admin::ApplicationController
redirect_to admin_spam_logs_path, notice: "User #{spam_log.user.username} was successfully removed."
else
spam_log.destroy
- render nothing: true
+ head :ok
end
end
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 6908a3bf946..f35f4a8c811 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -154,7 +154,7 @@ class Admin::UsersController < Admin::ApplicationController
respond_to do |format|
format.html { redirect_back_or_admin_user(notice: "Successfully removed email.") }
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
diff --git a/app/controllers/concerns/toggle_subscription_action.rb b/app/controllers/concerns/toggle_subscription_action.rb
index 8a43c0b93c4..9e3b9be2ff4 100644
--- a/app/controllers/concerns/toggle_subscription_action.rb
+++ b/app/controllers/concerns/toggle_subscription_action.rb
@@ -6,7 +6,7 @@ module ToggleSubscriptionAction
subscribable_resource.toggle_subscription(current_user)
- render nothing: true
+ head :ok
end
private
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 5abf97342c3..f9a1929c117 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -12,7 +12,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: todo_notice }
- format.js { render nothing: true }
+ format.js { head :ok }
format.json do
render json: { count: @todos.size, done_count: current_user.todos.done.count }
end
@@ -24,7 +24,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
- format.js { render nothing: true }
+ format.js { head :ok }
format.json do
find_todos
render json: { count: @todos.size, done_count: current_user.todos.done.count }
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index d5ef33888c6..48dbf656e84 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -40,7 +40,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
respond_to do |format|
format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index 0ede9b8e21b..1c24c4db993 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -24,7 +24,7 @@ class Profiles::EmailsController < Profiles::ApplicationController
respond_to do |format|
format.html { redirect_to profile_emails_url }
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index a12549d6bcb..830e0b9591b 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -32,7 +32,7 @@ class Profiles::KeysController < Profiles::ApplicationController
respond_to do |format|
format.html { redirect_to profile_keys_url }
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
diff --git a/app/controllers/projects/container_registry_controller.rb b/app/controllers/projects/container_registry_controller.rb
new file mode 100644
index 00000000000..d1f46497207
--- /dev/null
+++ b/app/controllers/projects/container_registry_controller.rb
@@ -0,0 +1,34 @@
+class Projects::ContainerRegistryController < Projects::ApplicationController
+ before_action :verify_registry_enabled
+ before_action :authorize_read_container_image!
+ before_action :authorize_update_container_image!, only: [:destroy]
+ layout 'project'
+
+ def index
+ @tags = container_registry_repository.tags
+ end
+
+ def destroy
+ url = namespace_project_container_registry_index_path(project.namespace, project)
+
+ if tag.delete
+ redirect_to url
+ else
+ redirect_to url, alert: 'Failed to remove tag'
+ end
+ end
+
+ private
+
+ def verify_registry_enabled
+ render_404 unless Gitlab.config.registry.enabled
+ end
+
+ def container_registry_repository
+ @container_registry_repository ||= project.container_registry_repository
+ end
+
+ def tag
+ @tag ||= container_registry_repository.tag(params[:id])
+ end
+end
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index 7756f0f0ed3..a1b84afcd91 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -20,6 +20,7 @@ class Projects::ImportsController < Projects::ApplicationController
@project.import_retry
else
@project.import_start
+ @project.add_import_job
end
end
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index f7b6d137bde..da2892bfb3f 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -75,7 +75,7 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to do |format|
format.html { redirect_to namespace_project_milestones_path }
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 4a57cd29a20..40b24d550e0 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -43,7 +43,7 @@ class Projects::NotesController < Projects::ApplicationController
end
respond_to do |format|
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
@@ -52,7 +52,7 @@ class Projects::NotesController < Projects::ApplicationController
note.update_attribute(:attachment, nil)
respond_to do |format|
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 33b2625c0ac..cdea5f0b776 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -55,7 +55,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
format.html do
redirect_to namespace_project_project_members_path(@project.namespace, @project)
end
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
@@ -81,7 +81,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
respond_to do |format|
format.html { redirect_to dashboard_projects_path, notice: "You left the project." }
- format.js { render nothing: true }
+ format.js { head :ok }
end
else
if current_user == @project.owner
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index e49259c34b6..efa7bf14d0f 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -39,7 +39,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
respond_to do |format|
format.html { redirect_to namespace_project_protected_branches_path }
- format.js { render nothing: true }
+ format.js { head :ok }
end
end
diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb
index 00234654578..6f068729390 100644
--- a/app/controllers/projects/variables_controller.rb
+++ b/app/controllers/projects/variables_controller.rb
@@ -3,20 +3,44 @@ class Projects::VariablesController < Projects::ApplicationController
layout 'project_settings'
+ def index
+ @variable = Ci::Variable.new
+ end
+
def show
+ @variable = @project.variables.find(params[:id])
end
def update
- if project.update_attributes(project_params)
+ @variable = @project.variables.find(params[:id])
+
+ if @variable.update_attributes(project_params)
+ redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variable was successfully updated.'
+ else
+ render action: "show"
+ end
+ end
+
+ def create
+ @variable = Ci::Variable.new(project_params)
+
+ if @variable.valid? && @project.variables << @variable
redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variables were successfully updated.'
else
- render action: 'show'
+ render action: "index"
end
end
+ def destroy
+ @key = @project.variables.find(params[:id])
+ @key.destroy
+
+ redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variable was successfully removed.'
+ end
+
private
def project_params
- params.require(:project).permit({ variables_attributes: [:id, :key, :value, :_destroy] })
+ params.require(:variable).permit([:id, :key, :value, :_destroy])
end
end
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index f07eff3fb57..2ce2d4e694f 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -33,6 +33,10 @@ module GitlabRoutingHelper
namespace_project_builds_path(project.namespace, project, *args)
end
+ def project_container_registry_path(project, *args)
+ namespace_project_container_registry_index_path(project.namespace, project, *args)
+ end
+
def activity_project_path(project, *args)
activity_namespace_project_path(project.namespace, project, *args)
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index e1ab78df69e..0825b5b6437 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -124,11 +124,7 @@ module ProjectsHelper
end
def license_short_name(project)
- no_license_key = project.repository.license_key.nil? ||
- # Back-compat if cache contains 'no-license', can be removed in a few weeks
- project.repository.license_key == 'no-license'
-
- return 'LICENSE' if no_license_key
+ return 'LICENSE' if project.repository.license_key.nil?
license = Licensee::License.new(project.repository.license_key)
@@ -152,6 +148,10 @@ module ProjectsHelper
nav_tabs << :builds
end
+ if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project)
+ nav_tabs << :container_registry
+ end
+
if can?(current_user, :admin_project, project)
nav_tabs << :settings
end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 6ad8fc3f034..7d5103748f5 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -98,9 +98,7 @@ class MergeRequestDiff < ActiveRecord::Base
commits = compare.commits
if commits.present?
- commits = Commit.decorate(commits, merge_request.source_project).
- sort_by(&:created_at).
- reverse
+ commits = Commit.decorate(commits, merge_request.source_project).reverse
end
commits
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 9c942a8f4e3..da19462f265 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -110,6 +110,10 @@ class Namespace < ActiveRecord::Base
# Ensure old directory exists before moving it
gitlab_shell.add_namespace(path_was)
+ if any_project_has_container_registry_tags?
+ raise Exception.new('Namespace cannot be moved, because at least one project has tags in container registry')
+ end
+
if gitlab_shell.mv_namespace(path_was, path)
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
@@ -131,6 +135,10 @@ class Namespace < ActiveRecord::Base
end
end
+ def any_project_has_container_registry_tags?
+ projects.any?(&:has_container_registry_tags?)
+ end
+
def send_update_instructions
projects.each do |project|
project.send_move_instructions("#{path_was}/#{project.path}")
diff --git a/app/models/project.rb b/app/models/project.rb
index a3c4f1d8e9b..9a34198e098 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -171,17 +171,17 @@ class Project < ActiveRecord::Base
scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) }
scope :sorted_by_stars, -> { reorder('projects.star_count DESC') }
- scope :sorted_by_names, -> { joins(:namespace).reorder('namespaces.name ASC, projects.name ASC') }
- scope :without_user, ->(user) { where('projects.id NOT IN (:ids)', ids: user.authorized_projects.map(&:id) ) }
- scope :without_team, ->(team) { team.projects.present? ? where('projects.id NOT IN (:ids)', ids: team.projects.map(&:id)) : scoped }
- scope :not_in_group, ->(group) { where('projects.id NOT IN (:ids)', ids: group.project_ids ) }
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
- scope :in_group_namespace, -> { joins(:group) }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
+ scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
scope :non_archived, -> { where(archived: false) }
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
+ scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) }
+
+ scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') }
+ scope :abandoned, -> { where('projects.last_activity_at < ?', 6.months.ago) }
state_machine :import_status, initial: :none do
event :import_start do
@@ -204,23 +204,10 @@ class Project < ActiveRecord::Base
state :finished
state :failed
- after_transition any => :started, do: :schedule_add_import_job
after_transition any => :finished, do: :clear_import_data
end
class << self
- def abandoned
- where('projects.last_activity_at < ?', 6.months.ago)
- end
-
- def with_push
- joins(:events).where('events.action = ?', Event::PUSHED)
- end
-
- def active
- joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC')
- end
-
# Searches for a list of projects based on the query given in `query`.
#
# On PostgreSQL this method uses "ILIKE" to perform a case-insensitive
@@ -282,10 +269,6 @@ class Project < ActiveRecord::Base
projects.iwhere('projects.path' => project_path).take
end
- def find_by_ci_id(id)
- find_by(ci_id: id.to_i)
- end
-
def visibility_levels
Gitlab::VisibilityLevel.options
end
@@ -316,10 +299,6 @@ class Project < ActiveRecord::Base
joins(join_body).reorder('join_note_counts.amount DESC')
end
-
- def visible_to_user(user)
- where(id: user.authorized_projects.select(:id).reorder(nil))
- end
end
def team
@@ -330,12 +309,30 @@ class Project < ActiveRecord::Base
@repository ||= Repository.new(path_with_namespace, self)
end
- def container_registry_url
- if container_registry_enabled? && Gitlab.config.registry.enabled
- "#{Gitlab.config.registry.host_with_port}/#{path_with_namespace}"
+ def container_registry_repository
+ return unless Gitlab.config.registry.enabled
+
+ @container_registry_repository ||= begin
+ token = Auth::ContainerRegistryAuthenticationService.full_access_token(path_with_namespace)
+ url = Gitlab.config.registry.api_url
+ host_port = Gitlab.config.registry.host_port
+ registry = ContainerRegistry::Registry.new(url, token: token, path: host_port)
+ registry.repository(path_with_namespace)
+ end
+ end
+
+ def container_registry_repository_url
+ if Gitlab.config.registry.enabled
+ "#{Gitlab.config.registry.host_port}/#{path_with_namespace}"
end
end
+ def has_container_registry_tags?
+ return unless container_registry_repository
+
+ container_registry_repository.tags.any?
+ end
+
def commit(id = 'HEAD')
repository.commit(id)
end
@@ -349,10 +346,6 @@ class Project < ActiveRecord::Base
id && persisted?
end
- def schedule_add_import_job
- run_after_commit(:add_import_job)
- end
-
def add_import_job
if forked?
job_id = RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path)
@@ -376,14 +369,14 @@ class Project < ActiveRecord::Base
end
def import_url=(value)
- import_url = Gitlab::ImportUrl.new(value)
+ import_url = Gitlab::UrlSanitizer.new(value)
create_or_update_import_data(credentials: import_url.credentials)
super(import_url.sanitized_url)
end
def import_url
if import_data && super
- import_url = Gitlab::ImportUrl.new(super, credentials: import_data.credentials)
+ import_url = Gitlab::UrlSanitizer.new(super, credentials: import_data.credentials)
import_url.full_url
else
super
@@ -751,6 +744,11 @@ class Project < ActiveRecord::Base
expire_caches_before_rename(old_path_with_namespace)
+ if has_container_registry_tags?
+ # we currently doesn't support renaming repository if it contains tags in container registry
+ raise Exception.new('Project cannot be renamed, because tags are present in its container registry')
+ end
+
if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace)
# If repository moved successfully we need to send update instructions to users.
# However we cannot allow rollback since we moved repository
diff --git a/app/models/project_services/slack_service/issue_message.rb b/app/models/project_services/slack_service/issue_message.rb
index 438ff33fdff..88e053ec192 100644
--- a/app/models/project_services/slack_service/issue_message.rb
+++ b/app/models/project_services/slack_service/issue_message.rb
@@ -34,7 +34,12 @@ class SlackService
private
def message
- "#{user_name} #{state} #{issue_link} in #{project_link}: *#{title}*"
+ case state
+ when "opened"
+ "[#{project_link}] Issue #{state} by #{user_name}"
+ else
+ "[#{project_link}] Issue #{issue_link} #{state} by #{user_name}"
+ end
end
def opened_issue?
@@ -42,7 +47,11 @@ class SlackService
end
def description_message
- [{ text: format(description), color: attachment_color }]
+ [{
+ title: issue_title,
+ title_link: issue_url,
+ text: format(description),
+ color: "#C95823" }]
end
def project_link
@@ -50,7 +59,11 @@ class SlackService
end
def issue_link
- "[issue ##{issue_iid}](#{issue_url})"
+ "[#{issue_title}](#{issue_url})"
+ end
+
+ def issue_title
+ "##{issue_iid} #{title}"
end
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 368a3f3cfba..6a09b78455b 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -397,11 +397,6 @@ class User < ActiveRecord::Base
owned_groups.select(:id), namespace.id).joins(:namespace)
end
- # Team membership in authorized projects
- def tm_in_authorized_projects
- ProjectMember.where(source_id: authorized_projects.map(&:id), user_id: self.id)
- end
-
def is_admin?
admin
end
@@ -491,10 +486,6 @@ class User < ActiveRecord::Base
"#{name} (#{username})"
end
- def tm_of(project)
- project.project_member_by_id(self.id)
- end
-
def already_forked?(project)
!!fork_of(project)
end
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index b636f55d031..3144e96ba31 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -6,7 +6,7 @@ module Auth
return error('not found', 404) unless registry.enabled
if params[:offline_token]
- return error('forbidden', 403) unless current_user
+ return error('unauthorized', 401) unless current_user
else
return error('forbidden', 403) unless scope
end
@@ -14,6 +14,17 @@ module Auth
{ token: authorized_token(scope).encoded }
end
+ def self.full_access_token(*names)
+ registry = Gitlab.config.registry
+ token = JSONWebToken::RSAToken.new(registry.key)
+ token.issuer = registry.issuer
+ token.audience = AUDIENCE
+ token[:access] = names.map do |name|
+ { type: 'repository', name: name, actions: %w(pull push) }
+ end
+ token.encoded
+ end
+
private
def authorized_token(*accesses)
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 3563cbaa997..c7d406cc331 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -24,6 +24,10 @@ module Issues
todo_service.reassigned_issue(issue, current_user)
end
+ if issue.previous_changes.include?('confidential')
+ create_confidentiality_note(issue)
+ end
+
added_labels = issue.labels - old_labels
if added_labels.present?
notification_service.relabeled_issue(issue, added_labels, current_user)
@@ -37,5 +41,11 @@ module Issues
def close_service
Issues::CloseService
end
+
+ private
+
+ def create_confidentiality_note(issue)
+ SystemNoteService.change_issue_confidentiality(issue, issue.project, current_user)
+ end
end
end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 501e58c1407..6728fabea1e 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -6,6 +6,7 @@ module Projects
def execute
forked_from_project_id = params.delete(:forked_from_project_id)
+ import_data = params.delete(:import_data)
@project = Project.new(params)
@@ -49,16 +50,14 @@ module Projects
@project.build_forked_project_link(forked_from_project_id: forked_from_project_id)
end
- Project.transaction do
- @project.save
+ save_project_and_import_data(import_data)
- if @project.persisted? && !@project.import?
- raise 'Failed to create repository' unless @project.create_repository
- end
- end
+ @project.import_start if @project.import?
after_create_actions if @project.persisted?
+ @project.add_import_job if @project.import?
+
@project
rescue => e
message = "Unable to save project: #{e.message}"
@@ -93,8 +92,16 @@ module Projects
unless @project.group
@project.team << [current_user, :master, current_user]
end
+ end
- @project.import_start if @project.import?
+ def save_project_and_import_data(import_data)
+ Project.transaction do
+ @project.create_or_update_import_data(data: import_data[:data], credentials: import_data[:credentials]) if import_data
+
+ if @project.save && !@project.import?
+ raise 'Failed to create repository' unless @project.create_repository
+ end
+ end
end
end
end
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 48a6131b444..f09072975c3 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -26,6 +26,10 @@ module Projects
Project.transaction do
project.destroy!
+ unless remove_registry_tags
+ raise_error('Failed to remove project container registry. Please try again or contact administrator')
+ end
+
unless remove_repository(repo_path)
raise_error('Failed to remove project repository. Please try again or contact administrator')
end
@@ -59,6 +63,12 @@ module Projects
end
end
+ def remove_registry_tags
+ return true unless Gitlab.config.registry.enabled
+
+ project.container_registry_repository.delete_tags
+ end
+
def raise_error(message)
raise DestroyError.new(message)
end
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 111b3ec05ea..03b57dea51e 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -34,6 +34,11 @@ module Projects
raise TransferError.new("Project with same path in target namespace already exists")
end
+ if project.has_container_registry_tags?
+ # we currently doesn't support renaming repository if it contains tags in container registry
+ raise TransferError.new('Project cannot be transferred, because tags are present in its container registry')
+ end
+
project.expire_caches_before_rename(old_path)
# Apply new namespace id and visibility level
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 4bdb1b0c074..972f8b2012d 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -169,12 +169,26 @@ class SystemNoteService
#
# Returns the created Note object
def self.change_title(noteable, project, author, old_title)
- return unless noteable.respond_to?(:title)
-
body = "Title changed from **#{old_title}** to **#{noteable.title}**"
create_note(noteable: noteable, project: project, author: author, note: body)
end
+ # Called when the confidentiality changes
+ #
+ # issue - Issue object
+ # project - Project owning the issue
+ # author - User performing the change
+ #
+ # Example Note text:
+ #
+ # "Made the issue confidential"
+ #
+ # Returns the created Note object
+ def self.change_issue_confidentiality(issue, project, author)
+ body = issue.confidential ? 'Made the issue confidential' : 'Made the issue visible'
+ create_note(noteable: issue, project: project, author: author, note: body)
+ end
+
# Called when a branch in Noteable is changed
#
# noteable - Noteable object
diff --git a/app/views/groups/_activities.html.haml b/app/views/groups/_activities.html.haml
index dc76599b776..71cc4d87b1f 100644
--- a/app/views/groups/_activities.html.haml
+++ b/app/views/groups/_activities.html.haml
@@ -4,7 +4,7 @@
.nav-block
- if current_user
.controls
- = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn' do
+ = link_to group_path(@group, format: :atom, private_token: current_user.private_token), class: 'btn rss-btn' do
%i.fa.fa-rss
= render 'shared/event_filter'
diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder
index 486d1d8587a..a6eb9abada6 100644
--- a/app/views/groups/issues.atom.builder
+++ b/app/views/groups/issues.atom.builder
@@ -1,9 +1,9 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
- xml.title "#{@user.name} issues"
- xml.link href: issues_dashboard_url(format: :atom, private_token: @user.private_token), rel: "self", type: "application/atom+xml"
- xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html"
- xml.id issues_dashboard_url
+ xml.title "#{@group.name} issues"
+ xml.link href: issues_group_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+ xml.link href: issues_group_url, rel: "alternate", type: "text/html"
+ xml.id issues_group_url
xml.updated @issues.first.created_at.xmlschema if @issues.any?
@issues.each do |issue|
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 8f5739cf92e..a97fefcfb46 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -53,6 +53,13 @@
Builds
%span.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all))
+ - if project_nav_tab? :container_registry
+ = nav_link(controller: %w(container_registry)) do
+ = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
+ = icon('hdd-o fw')
+ %span
+ Container Registry
+
- if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do
= link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 8de44a6c914..81afea2c60a 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -8,7 +8,7 @@
%a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
Preview
%li.pull-right
- %button.zen-cotrol.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 }
+ %button.zen-control.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 }
Go full screen
.md-write-holder
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index e1e35013968..413477a2d3a 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -4,5 +4,5 @@
= f.text_area attr, class: classes, placeholder: placeholder
- else
= text_area_tag attr, nil, class: classes, placeholder: placeholder
- %a.zen-cotrol.zen-control-leave.js-zen-leave{ href: "#" }
+ %a.zen-control.zen-control-leave.js-zen-leave{ href: "#" }
= icon('compress')
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index 82f39e59284..7283a78a64e 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -3,7 +3,7 @@
- commits, hidden = limited_commits(@commits)
-- commits.group_by { |c| c.committed_date.in_time_zone.to_date }.sort.reverse.each do |day, commits|
+- commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits|
.row.commits-row
.col-md-2.hidden-xs.hidden-sm
%h5.commits-row-date
diff --git a/app/views/projects/container_registry/_header_title.html.haml b/app/views/projects/container_registry/_header_title.html.haml
new file mode 100644
index 00000000000..f1863c52a3e
--- /dev/null
+++ b/app/views/projects/container_registry/_header_title.html.haml
@@ -0,0 +1 @@
+- header_title project_title(@project, "Container Registry", project_container_registry_path(@project))
diff --git a/app/views/projects/container_registry/_tag.html.haml b/app/views/projects/container_registry/_tag.html.haml
new file mode 100644
index 00000000000..4e9f936539b
--- /dev/null
+++ b/app/views/projects/container_registry/_tag.html.haml
@@ -0,0 +1,21 @@
+%tr.tag
+ %td
+ = escape_once(tag.name)
+ = clipboard_button(clipboard_text: "docker pull #{tag.path}")
+ %td
+ - if layer = tag.layers.first
+ %span.has-tooltip{ title: "#{layer.revision}" }
+ = layer.short_revision
+ - else
+ \-
+ %td
+ = number_to_human_size(tag.total_size)
+ &middot;
+ = pluralize(tag.layers.size, "layer")
+ %td
+ = time_ago_in_words(tag.created_at)
+ - if can?(current_user, :update_container_image, @project)
+ %td.content
+ .controls.hidden-xs.pull-right
+ = link_to namespace_project_container_registry_path(@project.namespace, @project, tag.name), class: 'btn btn-remove has-tooltip', title: "Remove", data: { confirm: "Are you sure?" }, method: :delete do
+ = icon("trash cred")
diff --git a/app/views/projects/container_registry/index.html.haml b/app/views/projects/container_registry/index.html.haml
new file mode 100644
index 00000000000..40957993b22
--- /dev/null
+++ b/app/views/projects/container_registry/index.html.haml
@@ -0,0 +1,40 @@
+- page_title "Container Registry"
+= render "header_title"
+
+%hr
+
+%ul.content-list
+ .light.prepend-top-default
+ %p
+ A 'container image' is a snapshot of a container.
+ You can host your container images with GitLab.
+ %br
+ To start using container images hosted on GitLab you first need to login:
+ %pre
+ %code
+ docker login #{Gitlab.config.registry.host_port}
+ %br
+ Then you are free to create and upload a container image with build and push commands:
+ %pre
+ docker build -t #{escape_once(@project.container_registry_repository_url)} .
+ %br
+ docker push #{escape_once(@project.container_registry_repository_url)}
+
+ - if @tags.blank?
+ %li
+ .nothing-here-block No images in Container Registry for this project.
+
+ - else
+ .table-holder
+ %table.table.tags
+ %thead
+ %tr
+ %th Name
+ %th Image ID
+ %th Size
+ %th Created
+ - if can?(current_user, :update_container_image, @project)
+ %th
+
+ - @tags.each do |tag|
+ = render 'tag', tag: tag \ No newline at end of file
diff --git a/app/views/projects/deploy_keys/index.html.haml b/app/views/projects/deploy_keys/index.html.haml
index e230834e8ba..04fbb37d93f 100644
--- a/app/views/projects/deploy_keys/index.html.haml
+++ b/app/views/projects/deploy_keys/index.html.haml
@@ -19,7 +19,7 @@
%ul.well-list
= render @enabled_keys
- else
- .profile-settings-message.text-center
+ .settings-message.text-center
No deploy keys found. Create one with the form above or add existing one below.
%h5.prepend-top-default
Deploy keys from projects you have access to (#{@available_project_keys.size})
@@ -27,7 +27,7 @@
%ul.well-list
= render @available_project_keys
- else
- .profile-settings-message.text-center
+ .settings-message.text-center
No deploy keys from your projects could be found. Create one with the form above or add existing one below.
- if @available_public_keys.any?
%h5.prepend-top-default
diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml
index 36c1d69f060..cffe9a01a96 100644
--- a/app/views/projects/hooks/index.html.haml
+++ b/app/views/projects/hooks/index.html.haml
@@ -80,5 +80,5 @@
- @hooks.each do |hook|
= render "project_hook", hook: hook
- else
- %p.profile-settings-message.text-center.append-bottom-0
+ %p.settings-message.text-center.append-bottom-0
No webhooks found, add one in the form above.
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
index b9e9dd8aaea..565905cbe7b 100644
--- a/app/views/projects/protected_branches/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/_branches_list.html.haml
@@ -1,7 +1,7 @@
%h5.prepend-top-0
Already Protected (#{@branches.size})
- if @branches.empty?
- %p.profile-settings-message.text-center
+ %p.settings-message.text-center
No branches are protected, protect a branch with the form above.
- else
- can_admin_project = can?(current_user, :admin_project, @project)
diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml
index f91885b216d..d73ac987161 100644
--- a/app/views/projects/triggers/index.html.haml
+++ b/app/views/projects/triggers/index.html.haml
@@ -18,7 +18,7 @@
%th
= render partial: 'trigger', collection: @triggers, as: :trigger
- else
- %p.profile-settings-message.text-center.append-bottom-default
+ %p.settings-message.text-center.append-bottom-default
There are no triggers to use, add one by the button below.
= form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create') do |f|
diff --git a/app/views/projects/variables/_content.html.haml b/app/views/projects/variables/_content.html.haml
new file mode 100644
index 00000000000..0249e0c1bf1
--- /dev/null
+++ b/app/views/projects/variables/_content.html.haml
@@ -0,0 +1,8 @@
+%h4.prepend-top-0
+ Secret Variables
+%p
+ These variables will be set to environment by the runner.
+%p
+ So you can use them for passwords, secret keys or whatever you want.
+%p
+ The value of the variable can be visible in build log if explicitly asked to do so.
diff --git a/app/views/projects/variables/_form.html.haml b/app/views/projects/variables/_form.html.haml
new file mode 100644
index 00000000000..a5bae83e0ce
--- /dev/null
+++ b/app/views/projects/variables/_form.html.haml
@@ -0,0 +1,10 @@
+= form_for [@project.namespace.becomes(Namespace), @project, @variable] do |f|
+ = form_errors(@variable)
+
+ .form-group
+ = f.label :key, "Key", class: "label-light"
+ = f.text_field :key, class: "form-control", placeholder: "PROJECT_VARIABLE", required: true
+ .form-group
+ = f.label :value, "Value", class: "label-light"
+ = f.text_area :value, class: "form-control", placeholder: "PROJECT_VARIABLE", required: true
+ = f.submit btn_text, class: "btn btn-save"
diff --git a/app/views/projects/variables/_table.html.haml b/app/views/projects/variables/_table.html.haml
new file mode 100644
index 00000000000..6c43f822db4
--- /dev/null
+++ b/app/views/projects/variables/_table.html.haml
@@ -0,0 +1,25 @@
+.table-responsive.variables-table
+ %table.table
+ %colgroup
+ %col
+ %col
+ %col{ width: 100 }
+ %thead
+ %th Key
+ %th Value
+ %th
+ %tbody
+ - @project.variables.each do |variable|
+ - if variable.id?
+ %tr
+ %td= variable.key
+ %td= variable.value
+ %td
+ = link_to namespace_project_variable_path(@project.namespace, @project, variable), class: "btn btn-transparent btn-variable-edit" do
+ %span.sr-only
+ Update
+ = icon("pencil")
+ = link_to namespace_project_variable_path(@project.namespace, @project, variable), class: "btn btn-transparent btn-variable-delete", method: :delete, data: { confirm: "Are you sure?" } do
+ %span.sr-only
+ Remove
+ = icon("trash")
diff --git a/app/views/projects/variables/index.html.haml b/app/views/projects/variables/index.html.haml
new file mode 100644
index 00000000000..09bb54600af
--- /dev/null
+++ b/app/views/projects/variables/index.html.haml
@@ -0,0 +1,17 @@
+- page_title "Variables"
+
+.row.prepend-top-default.append-bottom-default
+ .col-lg-3
+ = render "content"
+ .col-lg-9
+ %h5.prepend-top-0
+ Add a variable
+ = render "form", btn_text: "Add new variable"
+ %hr
+ %h5.prepend-top-0
+ Your variables (#{@project.variables.size})
+ - if @project.variables.empty?
+ %p.settings-message.text-center.append-bottom-0
+ No variables found, add one with the form above.
+ - else
+ = render "table"
diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml
index ca284b84d39..297a53ca98c 100644
--- a/app/views/projects/variables/show.html.haml
+++ b/app/views/projects/variables/show.html.haml
@@ -1,36 +1,9 @@
- page_title "Variables"
-%h3.page-title
- Secret Variables
-%p.light
- These variables will be set to environment by the runner.
- %br
- So you can use them for passwords, secret keys or whatever you want.
- %br
- The value of the variable can be visible in build log if explicitly asked to do so.
-
-%hr
-
-
-= nested_form_for @project, url: url_for(controller: 'projects/variables', action: 'update'), html: { class: 'form-horizontal' } do |f|
- = form_errors(@project)
-
- = f.fields_for :variables do |variable_form|
- .form-group
- = variable_form.label :key, 'Key', class: 'control-label'
- .col-sm-10
- = variable_form.text_field :key, class: 'form-control', placeholder: "PROJECT_VARIABLE"
-
- .form-group
- = variable_form.label :value, 'Value', class: 'control-label'
- .col-sm-10
- = variable_form.text_area :value, class: 'form-control', rows: 2, placeholder: ""
-
- = variable_form.link_to_remove "Remove this variable", class: 'btn btn-danger pull-right prepend-top-10'
- %hr
- %p
- .clearfix
- = f.link_to_add "Add a variable", :variables, class: 'btn btn-success pull-right'
-
- .form-actions
- = f.submit 'Save changes', class: 'btn btn-save', return_to: request.original_url
+.row.prepend-top-default.append-bottom-default
+ .col-lg-3
+ = render "content"
+ .col-lg-9
+ %h5.prepend-top-0
+ Update variable
+ = render "form", btn_text: "Save variable"
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 2937493c614..fbc7ed63c6a 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -13,7 +13,7 @@ class RepositoryImportWorker
result = Projects::ImportService.new(project, current_user).execute
if result[:status] == :error
- project.update(import_error: result[:message])
+ project.update(import_error: Gitlab::UrlSanitizer.sanitize(result[:message]))
project.import_fail
return
end