summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/api/groups.rb10
-rw-r--r--lib/api/milestones.rb111
-rw-r--r--lib/api/runners.rb117
-rw-r--r--lib/api/session.rb19
-rw-r--r--lib/gitlab/ee_compat_check.rb152
-rw-r--r--lib/tasks/gitlab/dev.rake26
6 files changed, 236 insertions, 199 deletions
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index a13e353b7f5..40644fc2adf 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -26,6 +26,16 @@ module API
present @groups, with: Entities::Group
end
+ # Get list of owned groups for authenticated user
+ #
+ # Example Request:
+ # GET /groups/owned
+ get '/owned' do
+ @groups = current_user.owned_groups
+ @groups = paginate @groups
+ present @groups, with: Entities::Group, user: current_user
+ end
+
# Create group. Available only for users who can create groups.
#
# Parameters:
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index 9b73f6826cf..8984cf8cdcd 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -11,19 +11,25 @@ module API
else milestones
end
end
+
+ params :optional_params do
+ optional :description, type: String, desc: 'The description of the milestone'
+ optional :due_date, type: String, desc: 'The due date of the milestone'
+ end
end
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
resource :projects do
- # Get a list of project milestones
- #
- # Parameters:
- # id (required) - The ID of a project
- # state (optional) - Return "active" or "closed" milestones
- # Example Request:
- # GET /projects/:id/milestones
- # GET /projects/:id/milestones?iid=42
- # GET /projects/:id/milestones?state=active
- # GET /projects/:id/milestones?state=closed
+ desc 'Get a list of project milestones' do
+ success Entities::Milestone
+ end
+ params do
+ optional :state, type: String, values: %w[active closed all], default: 'all',
+ desc: 'Return "active", "closed", or "all" milestones'
+ optional :iid, type: Integer, desc: 'The IID of the milestone'
+ end
get ":id/milestones" do
authorize! :read_milestone, user_project
@@ -34,34 +40,31 @@ module API
present paginate(milestones), with: Entities::Milestone
end
- # Get a single project milestone
- #
- # Parameters:
- # id (required) - The ID of a project
- # milestone_id (required) - The ID of a project milestone
- # Example Request:
- # GET /projects/:id/milestones/:milestone_id
+ desc 'Get a single project milestone' do
+ success Entities::Milestone
+ end
+ params do
+ requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
+ end
get ":id/milestones/:milestone_id" do
authorize! :read_milestone, user_project
- @milestone = user_project.milestones.find(params[:milestone_id])
- present @milestone, with: Entities::Milestone
+ milestone = user_project.milestones.find(params[:milestone_id])
+ present milestone, with: Entities::Milestone
end
- # Create a new project milestone
- #
- # Parameters:
- # id (required) - The ID of the project
- # title (required) - The title of the milestone
- # description (optional) - The description of the milestone
- # due_date (optional) - The due date of the milestone
- # Example Request:
- # POST /projects/:id/milestones
+ desc 'Create a new project milestone' do
+ success Entities::Milestone
+ end
+ params do
+ requires :title, type: String, desc: 'The title of the milestone'
+ use :optional_params
+ end
post ":id/milestones" do
authorize! :admin_milestone, user_project
- required_attributes! [:title]
- attrs = attributes_for_keys [:title, :description, :due_date]
- milestone = ::Milestones::CreateService.new(user_project, current_user, attrs).execute
+ milestone_params = declared(params, include_parent_namespaces: false)
+
+ milestone = ::Milestones::CreateService.new(user_project, current_user, milestone_params).execute
if milestone.valid?
present milestone, with: Entities::Milestone
@@ -70,22 +73,23 @@ module API
end
end
- # Update an existing project milestone
- #
- # Parameters:
- # id (required) - The ID of a project
- # milestone_id (required) - The ID of a project milestone
- # title (optional) - The title of a milestone
- # description (optional) - The description of a milestone
- # due_date (optional) - The due date of a milestone
- # state_event (optional) - The state event of the milestone (close|activate)
- # Example Request:
- # PUT /projects/:id/milestones/:milestone_id
+ desc 'Update an existing project milestone' do
+ success Entities::Milestone
+ end
+ params do
+ requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
+ optional :title, type: String, desc: 'The title of the milestone'
+ optional :state_event, type: String, values: %w[close activate],
+ desc: 'The state event of the milestone '
+ use :optional_params
+ at_least_one_of :title, :description, :due_date, :state_event
+ end
put ":id/milestones/:milestone_id" do
authorize! :admin_milestone, user_project
- attrs = attributes_for_keys [:title, :description, :due_date, :state_event]
- milestone = user_project.milestones.find(params[:milestone_id])
- milestone = ::Milestones::UpdateService.new(user_project, current_user, attrs).execute(milestone)
+ milestone_params = declared(params, include_parent_namespaces: false, include_missing: false)
+
+ milestone = user_project.milestones.find(milestone_params.delete(:milestone_id))
+ milestone = ::Milestones::UpdateService.new(user_project, current_user, milestone_params).execute(milestone)
if milestone.valid?
present milestone, with: Entities::Milestone
@@ -94,21 +98,20 @@ module API
end
end
- # Get all issues for a single project milestone
- #
- # Parameters:
- # id (required) - The ID of a project
- # milestone_id (required) - The ID of a project milestone
- # Example Request:
- # GET /projects/:id/milestones/:milestone_id/issues
+ desc 'Get all issues for a single project milestone' do
+ success Entities::Issue
+ end
+ params do
+ requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
+ end
get ":id/milestones/:milestone_id/issues" do
authorize! :read_milestone, user_project
- @milestone = user_project.milestones.find(params[:milestone_id])
+ milestone = user_project.milestones.find(params[:milestone_id])
finder_params = {
project_id: user_project.id,
- milestone_title: @milestone.title
+ milestone_title: milestone.title
}
issues = IssuesFinder.new(current_user, finder_params).execute
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index ecc8f2fc5a2..84c19c432b0 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -1,34 +1,39 @@
module API
- # Runners API
class Runners < Grape::API
before { authenticate! }
resource :runners do
- # Get runners available for user
- #
- # Example Request:
- # GET /runners
+ desc 'Get runners available for user' do
+ success Entities::Runner
+ end
+ params do
+ optional :scope, type: String, values: %w[active paused online],
+ desc: 'The scope of specific runners to show'
+ end
get do
runners = filter_runners(current_user.ci_authorized_runners, params[:scope], without: ['specific', 'shared'])
present paginate(runners), with: Entities::Runner
end
- # Get all runners - shared and specific
- #
- # Example Request:
- # GET /runners/all
+ desc 'Get all runners - shared and specific' do
+ success Entities::Runner
+ end
+ params do
+ optional :scope, type: String, values: %w[active paused online specific shared],
+ desc: 'The scope of specific runners to show'
+ end
get 'all' do
authenticated_as_admin!
runners = filter_runners(Ci::Runner.all, params[:scope])
present paginate(runners), with: Entities::Runner
end
- # Get runner's details
- #
- # Parameters:
- # id (required) - The ID of ther runner
- # Example Request:
- # GET /runners/:id
+ desc "Get runner's details" do
+ success Entities::RunnerDetails
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the runner'
+ end
get ':id' do
runner = get_runner(params[:id])
authenticate_show_runner!(runner)
@@ -36,33 +41,37 @@ module API
present runner, with: Entities::RunnerDetails, current_user: current_user
end
- # Update runner's details
- #
- # Parameters:
- # id (required) - The ID of ther runner
- # description (optional) - Runner's description
- # active (optional) - Runner's status
- # tag_list (optional) - Array of tags for runner
- # Example Request:
- # PUT /runners/:id
+ desc "Update runner's details" do
+ success Entities::RunnerDetails
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the runner'
+ optional :description, type: String, desc: 'The description of the runner'
+ optional :active, type: Boolean, desc: 'The state of a runner'
+ optional :tag_list, type: Array[String], desc: 'The list of tags for a runner'
+ optional :run_untagged, type: Boolean, desc: 'Flag indicating the runner can execute untagged jobs'
+ optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
+ at_least_one_of :description, :active, :tag_list, :run_untagged, :locked
+ end
put ':id' do
- runner = get_runner(params[:id])
+ runner = get_runner(params.delete(:id))
authenticate_update_runner!(runner)
- attrs = attributes_for_keys [:description, :active, :tag_list, :run_untagged, :locked]
- if runner.update(attrs)
+ runner_params = declared(params, include_missing: false)
+
+ if runner.update(runner_params)
present runner, with: Entities::RunnerDetails, current_user: current_user
else
render_validation_error!(runner)
end
end
- # Remove runner
- #
- # Parameters:
- # id (required) - The ID of ther runner
- # Example Request:
- # DELETE /runners/:id
+ desc 'Remove a runner' do
+ success Entities::Runner
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the runner'
+ end
delete ':id' do
runner = get_runner(params[:id])
authenticate_delete_runner!(runner)
@@ -72,28 +81,31 @@ module API
end
end
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
resource :projects do
before { authorize_admin_project }
- # Get runners available for project
- #
- # Example Request:
- # GET /projects/:id/runners
+ desc 'Get runners available for project' do
+ success Entities::Runner
+ end
+ params do
+ optional :scope, type: String, values: %w[active paused online specific shared],
+ desc: 'The scope of specific runners to show'
+ end
get ':id/runners' do
runners = filter_runners(Ci::Runner.owned_or_shared(user_project.id), params[:scope])
present paginate(runners), with: Entities::Runner
end
- # Enable runner for project
- #
- # Parameters:
- # id (required) - The ID of the project
- # runner_id (required) - The ID of the runner
- # Example Request:
- # POST /projects/:id/runners/:runner_id
+ desc 'Enable a runner for a project' do
+ success Entities::Runner
+ end
+ params do
+ requires :runner_id, type: Integer, desc: 'The ID of the runner'
+ end
post ':id/runners' do
- required_attributes! [:runner_id]
-
runner = get_runner(params[:runner_id])
authenticate_enable_runner!(runner)
@@ -106,13 +118,12 @@ module API
end
end
- # Disable project's runner
- #
- # Parameters:
- # id (required) - The ID of the project
- # runner_id (required) - The ID of the runner
- # Example Request:
- # DELETE /projects/:id/runners/:runner_id
+ desc "Disable project's runner" do
+ success Entities::Runner
+ end
+ params do
+ requires :runner_id, type: Integer, desc: 'The ID of the runner'
+ end
delete ':id/runners/:runner_id' do
runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
not_found!('Runner') unless runner_project
diff --git a/lib/api/session.rb b/lib/api/session.rb
index 55ec66a6d67..d09400b81f5 100644
--- a/lib/api/session.rb
+++ b/lib/api/session.rb
@@ -1,15 +1,14 @@
module API
- # Users API
class Session < Grape::API
- # Login to get token
- #
- # Parameters:
- # login (*required) - user login
- # email (*required) - user email
- # password (required) - user password
- #
- # Example Request:
- # POST /session
+ desc 'Login to get token' do
+ success Entities::UserLogin
+ end
+ params do
+ optional :login, type: String, desc: 'The username'
+ optional :email, type: String, desc: 'The email of the user'
+ requires :password, type: String, desc: 'The password of the user'
+ at_least_one_of :login, :email
+ end
post "/session" do
user = Gitlab::Auth.find_with_user_password(params[:email] || params[:login], params[:password])
diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb
index b1a6d5fe0f6..f4d1505ea91 100644
--- a/lib/gitlab/ee_compat_check.rb
+++ b/lib/gitlab/ee_compat_check.rb
@@ -2,39 +2,38 @@
module Gitlab
# Checks if a set of migrations requires downtime or not.
class EeCompatCheck
+ CE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ce.git'.freeze
EE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze
+ CHECK_DIR = Rails.root.join('ee_compat_check')
+ MAX_FETCH_DEPTH = 500
+ IGNORED_FILES_REGEX = /(VERSION|CHANGELOG\.md:\d+)/.freeze
- attr_reader :ce_branch, :check_dir, :ce_repo
+ attr_reader :repo_dir, :patches_dir, :ce_repo, :ce_branch
- def initialize(branch:, check_dir:, ce_repo: nil)
+ def initialize(branch:, ce_repo: CE_REPO)
+ @repo_dir = CHECK_DIR.join('repo')
+ @patches_dir = CHECK_DIR.join('patches')
@ce_branch = branch
- @check_dir = check_dir
- @ce_repo = ce_repo || 'https://gitlab.com/gitlab-org/gitlab-ce.git'
+ @ce_repo = ce_repo
end
def check
ensure_ee_repo
- delete_patches
+ ensure_patches_dir
generate_patch(ce_branch, ce_patch_full_path)
- Dir.chdir(check_dir) do
- step("In the #{check_dir} directory")
-
- step("Pulling latest master", %w[git pull --ff-only origin master])
+ Dir.chdir(repo_dir) do
+ step("In the #{repo_dir} directory")
status = catch(:halt_check) do
ce_branch_compat_check!
-
- delete_ee_branch_locally
-
+ delete_ee_branch_locally!
ee_branch_presence_check!
-
ee_branch_compat_check!
end
- delete_ee_branch_locally
- delete_patches
+ delete_ee_branch_locally!
if status.nil?
true
@@ -47,20 +46,43 @@ module Gitlab
private
def ensure_ee_repo
- if Dir.exist?(check_dir)
- step("#{check_dir} already exists")
+ if Dir.exist?(repo_dir)
+ step("#{repo_dir} already exists")
else
- cmd = %W[git clone --branch master --single-branch --depth 1 #{EE_REPO} #{check_dir}]
- step("Cloning #{EE_REPO} into #{check_dir}", cmd)
+ cmd = %W[git clone --branch master --single-branch --depth 200 #{EE_REPO} #{repo_dir}]
+ step("Cloning #{EE_REPO} into #{repo_dir}", cmd)
end
end
- def ce_branch_compat_check!
- cmd = %W[git apply --check #{ce_patch_full_path}]
- status = step("Checking if #{ce_patch_name} applies cleanly to EE/master", cmd)
+ def ensure_patches_dir
+ FileUtils.mkdir_p(patches_dir)
+ end
+
+ def generate_patch(branch, patch_path)
+ FileUtils.rm(patch_path, force: true)
+
+ depth = 0
+ loop do
+ depth += 50
+ cmd = %W[git fetch --depth #{depth} origin --prune +refs/heads/master:refs/remotes/origin/master]
+ Gitlab::Popen.popen(cmd)
+ _, status = Gitlab::Popen.popen(%w[git merge-base FETCH_HEAD HEAD])
+
+ raise "#{branch} is too far behind master, please rebase it!" if depth >= MAX_FETCH_DEPTH
+ break if status.zero?
+ end
- if status.zero?
- puts ce_applies_cleanly_msg(ce_branch)
+ step("Generating the patch against master in #{patch_path}")
+ output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout])
+ throw(:halt_check, :ko) unless status.zero?
+
+ File.write(patch_path, output)
+ throw(:halt_check, :ko) unless File.exist?(patch_path)
+ end
+
+ def ce_branch_compat_check!
+ if check_patch(ce_patch_full_path).zero?
+ puts applies_cleanly_msg(ce_branch)
throw(:halt_check)
end
end
@@ -80,10 +102,8 @@ module Gitlab
step("Checking out origin/#{ee_branch}", %W[git checkout -b #{ee_branch} FETCH_HEAD])
generate_patch(ee_branch, ee_patch_full_path)
- cmd = %W[git apply --check #{ee_patch_full_path}]
- status = step("Checking if #{ee_patch_name} applies cleanly to EE/master", cmd)
- unless status.zero?
+ unless check_patch(ee_patch_full_path).zero?
puts
puts ee_branch_doesnt_apply_cleanly_msg
@@ -91,50 +111,49 @@ module Gitlab
end
puts
- puts ee_applies_cleanly_msg
+ puts applies_cleanly_msg(ee_branch)
end
- def generate_patch(branch, filepath)
- FileUtils.rm(filepath, force: true)
+ def check_patch(patch_path)
+ step("Checking out master", %w[git checkout master])
+ step("Reseting to latest master", %w[git reset --hard origin/master])
- depth = 0
- loop do
- depth += 10
- step("Fetching origin/master", %W[git fetch origin master --depth=#{depth}])
- status = step("Finding merge base with master", %W[git merge-base FETCH_HEAD #{branch}])
-
- break if status.zero? || depth > 500
- end
+ step("Checking if #{patch_path} applies cleanly to EE/master")
+ output, status = Gitlab::Popen.popen(%W[git apply --check #{patch_path}])
- raise "#{branch} is too far behind master, please rebase it!" if depth > 500
+ unless status.zero?
+ failed_files = output.lines.reduce([]) do |memo, line|
+ if line.start_with?('error: patch failed:')
+ file = line.sub(/\Aerror: patch failed: /, '')
+ memo << file unless file =~ IGNORED_FILES_REGEX
+ end
+ memo
+ end
- step("Generating the patch against master")
- output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout])
- throw(:halt_check, :ko) unless status.zero?
+ if failed_files.empty?
+ status = 0
+ else
+ puts "\nConflicting files:"
+ failed_files.each do |file|
+ puts " - #{file}"
+ end
+ end
+ end
- File.write(filepath, output)
- throw(:halt_check, :ko) unless File.exist?(filepath)
+ status
end
- def delete_ee_branch_locally
+ def delete_ee_branch_locally!
command(%w[git checkout master])
step("Deleting the local #{ee_branch} branch", %W[git branch -D #{ee_branch}])
end
- def delete_patches
- step("Deleting #{ce_patch_full_path}")
- FileUtils.rm(ce_patch_full_path, force: true)
-
- step("Deleting #{ee_patch_full_path}")
- FileUtils.rm(ee_patch_full_path, force: true)
- end
-
def ce_patch_name
@ce_patch_name ||= "#{ce_branch}.patch"
end
def ce_patch_full_path
- @ce_patch_full_path ||= File.expand_path(ce_patch_name, check_dir)
+ @ce_patch_full_path ||= patches_dir.join(ce_patch_name)
end
def ee_branch
@@ -146,15 +165,18 @@ module Gitlab
end
def ee_patch_full_path
- @ee_patch_full_path ||= File.expand_path(ee_patch_name, check_dir)
+ @ee_patch_full_path ||= patches_dir.join(ee_patch_name)
end
def step(desc, cmd = nil)
puts "\n=> #{desc}\n"
if cmd
+ start = Time.now
puts "\n$ #{cmd.join(' ')}"
- command(cmd)
+ status = command(cmd)
+ puts "\nFinished in #{Time.now - start} seconds"
+ status
end
end
@@ -165,12 +187,12 @@ module Gitlab
status
end
- def ce_applies_cleanly_msg(ce_branch)
+ def applies_cleanly_msg(branch)
<<-MSG.strip_heredoc
=================================================================
🎉 Congratulations!! 🎉
- The #{ce_branch} branch applies cleanly to EE/master!
+ The #{branch} branch applies cleanly to EE/master!
Much ❤️!!
=================================================================\n
@@ -211,7 +233,7 @@ module Gitlab
# In the EE repo
$ git fetch origin
- $ git checkout -b #{ee_branch} FETCH_HEAD
+ $ git checkout -b #{ee_branch} origin/master
$ git fetch #{ce_repo} #{ce_branch}
$ git cherry-pick SHA # Repeat for all the commits you want to pick
@@ -245,17 +267,5 @@ module Gitlab
=================================================================\n
MSG
end
-
- def ee_applies_cleanly_msg
- <<-MSG.strip_heredoc
- =================================================================
- 🎉 Congratulations!! 🎉
-
- The #{ee_branch} branch applies cleanly to EE/master!
-
- Much ❤️!!
- =================================================================\n
- MSG
- end
end
end
diff --git a/lib/tasks/gitlab/dev.rake b/lib/tasks/gitlab/dev.rake
index 5ee99dfc810..3117075b08b 100644
--- a/lib/tasks/gitlab/dev.rake
+++ b/lib/tasks/gitlab/dev.rake
@@ -1,18 +1,22 @@
namespace :gitlab do
namespace :dev do
desc 'Checks if the branch would apply cleanly to EE'
- task ee_compat_check: :environment do
- return if defined?(Gitlab::License)
- return unless ENV['CI']
+ task :ee_compat_check, [:branch] => :environment do |_, args|
+ opts =
+ if ENV['CI']
+ {
+ branch: ENV['CI_BUILD_REF_NAME'],
+ ce_repo: ENV['CI_BUILD_REPO']
+ }
+ else
+ unless args[:branch]
+ puts "Must specify a branch as an argument".color(:red)
+ exit 1
+ end
+ args
+ end
- success =
- Gitlab::EeCompatCheck.new(
- branch: ENV['CI_BUILD_REF_NAME'],
- check_dir: File.expand_path('ee-compat-check', __dir__),
- ce_repo: ENV['CI_BUILD_REPO']
- ).check
-
- if success
+ if Gitlab::EeCompatCheck.new(opts || {}).check
exit 0
else
exit 1