diff options
| -rw-r--r-- | CHANGELOG | 10 | ||||
| -rw-r--r-- | CONTRIBUTING.md | 2 | ||||
| -rw-r--r-- | Gemfile | 9 | ||||
| -rw-r--r-- | Gemfile.lock | 28 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | VERSION | 2 | ||||
| -rw-r--r-- | app/assets/stylesheets/gitlab_bootstrap/nav.scss | 11 | ||||
| -rw-r--r-- | app/controllers/profiles_controller.rb | 15 | ||||
| -rw-r--r-- | app/models/user.rb | 10 | ||||
| -rw-r--r-- | app/views/admin/teams/projects/new.html.haml | 2 | ||||
| -rw-r--r-- | app/views/help/index.html.haml | 2 | ||||
| -rw-r--r-- | app/views/projects/teams/available.html.haml | 2 | ||||
| -rw-r--r-- | config/puma.rb.example | 7 | ||||
| -rw-r--r-- | config/unicorn.rb.example | 102 | ||||
| -rw-r--r-- | doc/api/README.md | 24 | ||||
| -rw-r--r-- | doc/api/projects.md | 25 | ||||
| -rw-r--r-- | doc/install/installation.md | 22 | ||||
| -rw-r--r-- | lib/api/entities.rb | 7 | ||||
| -rw-r--r-- | lib/api/helpers.rb | 6 | ||||
| -rw-r--r-- | lib/api/projects.rb | 36 | ||||
| -rw-r--r-- | spec/requests/api/projects_spec.rb | 67 |
21 files changed, 342 insertions, 49 deletions
diff --git a/CHANGELOG b/CHANGELOG index 7d1ada97c57..c20f27ad7db 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,13 @@ +v 5.4.0 + - Ability to edit own comments + - Documentation improvements + - Improve dashboard projects page + - Fixed nav for empty repos + - GitLab Markdown help page + - Misspelling fixes + - Added suppoort of unicorn and fog gems + - Added client list to API doc + v 5.3.0 - Refactored services - Campfire service added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 761cfe3e261..2a6eb71b654 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obv Do not use the issue tracker for feature requests. We have a specific [feedback and suggestions forum](http://feedback.gitlab.com) for this purpose. -Please send a pull request with a tested solution or a pull request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there. +Please send a pull request with a tested solution or a pull request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there. ### Issue tracker guidelines @@ -59,8 +59,9 @@ gem "haml-rails" # Files attachments gem "carrierwave" + # for aws storage -# gem "fog", "~> 1.3.1" +gem "fog", "~> 1.3.1", group: :aws # Authorization gem "six" @@ -76,7 +77,8 @@ gem "github-markup", "~> 0.7.4", require: 'github/markup' gem "asciidoctor" # Servers -gem "puma", '~> 2.0.1' +gem "puma", '~> 2.3.1', group: :puma +gem "unicorn", '~> 4.6.3', group: :unicorn # State machine gem "state_machine" @@ -116,6 +118,9 @@ gem "d3_rails", "~> 3.1.4" # underscore-rails gem "underscore-rails", "~> 1.4.4" +# Sanitize user input +gem "sanitize" + group :assets do gem "sass-rails" gem "coffee-rails" diff --git a/Gemfile.lock b/Gemfile.lock index 313d2ffbca5..c26eeede69f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,6 +116,7 @@ GEM erubis (2.7.0) escape_utils (0.2.4) eventmachine (1.0.3) + excon (0.13.4) execjs (1.4.0) multi_json (~> 1.0) factory_girl (4.2.0) @@ -131,6 +132,16 @@ GEM eventmachine (>= 0.12.0) ffaker (1.16.0) ffi (1.8.1) + fog (1.3.1) + builder + excon (~> 0.13.0) + formatador (~> 0.2.0) + mime-types + multi_json (~> 1.0) + net-scp (~> 1.0.4) + net-ssh (>= 2.1.3) + nokogiri (~> 1.5.0) + ruby-hmac font-awesome-rails (3.1.1.3) railties (>= 3.2, < 5.0) foreman (0.63.0) @@ -243,6 +254,7 @@ GEM kaminari (0.14.1) actionpack (>= 3.0.0) activesupport (>= 3.0.0) + kgio (2.8.0) launchy (2.2.0) addressable (~> 2.3) letter_opener (1.1.0) @@ -267,6 +279,9 @@ GEM multipart-post (1.2.0) mysql2 (0.3.11) net-ldap (0.3.1) + net-scp (1.0.4) + net-ssh (>= 1.99.1) + net-ssh (2.6.8) nokogiri (1.5.9) oauth (0.4.7) oauth2 (0.8.1) @@ -305,7 +320,7 @@ GEM coderay (~> 1.0.5) method_source (~> 0.8) slop (~> 3.4) - puma (2.0.1) + puma (2.3.1) rack (>= 1.1, < 2.0) pygments.rb (0.4.2) posix-spawn (~> 0.3.6) @@ -354,6 +369,7 @@ GEM rake (>= 0.8.7) rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) + raindrops (0.11.0) rake (10.0.4) rb-fsevent (0.9.3) rb-inotify (0.9.0) @@ -400,6 +416,7 @@ GEM rspec-core (~> 2.13.0) rspec-expectations (~> 2.13.0) rspec-mocks (~> 2.13.0) + ruby-hmac (0.4.0) ruby-progressbar (1.0.2) rubyntlm (0.1.1) rubyzip (0.9.9) @@ -499,6 +516,10 @@ GEM execjs (>= 0.3.0) multi_json (~> 1.0, >= 1.0.2) underscore-rails (1.4.4) + unicorn (4.6.3) + kgio (~> 2.6) + rack + raindrops (~> 0.7) virtus (0.5.4) backports (~> 2.6.1) descendants_tracker (~> 0.0.1) @@ -536,6 +557,7 @@ DEPENDENCIES enumerize factory_girl_rails ffaker + fog (~> 1.3.1) font-awesome-rails (~> 3.1.1) foreman gemoji (~> 1.2.1) @@ -574,7 +596,7 @@ DEPENDENCIES pg poltergeist (~> 1.3.0) pry - puma (~> 2.0.1) + puma (~> 2.3.1) quiet_assets (~> 1.0.1) rack-mini-profiler rails (= 3.2.13) @@ -586,6 +608,7 @@ DEPENDENCIES redcarpet (~> 2.2.2) redis-rails rspec-rails + sanitize sass-rails sdoc seed-fu @@ -608,4 +631,5 @@ DEPENDENCIES turbolinks uglifier underscore-rails (~> 1.4.4) + unicorn (~> 4.6.3) webmock diff --git a/README.md b/README.md index b5b39648a01..de773cce0eb 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ or start each component separately * [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems. -* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix. +* [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix. * [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab. @@ -1 +1 @@ -5.3.0 +5.4.0.pre diff --git a/app/assets/stylesheets/gitlab_bootstrap/nav.scss b/app/assets/stylesheets/gitlab_bootstrap/nav.scss index db0023cff92..847c7180ce2 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/nav.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/nav.scss @@ -64,3 +64,14 @@ &.nav-small-tabs > li > a { padding: 6px 9px; } } + + + +/** + * fix to keep tooltips position in top navigation bar + * + */ +.navbar .nav > li { + position: relative; + white-space: nowrap; +} diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 686edd8af80..6fa635d0e36 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -17,7 +17,7 @@ class ProfilesController < ApplicationController end def update - if @user.update_attributes(user_attributes) + if @user.update_attributes(params[:user]) flash[:notice] = "Profile was successfully updated" else flash[:alert] = "Failed to update profile" @@ -69,19 +69,6 @@ class ProfilesController < ApplicationController @user = current_user end - def user_attributes - user_attributes = params[:user] - - # Sanitize user input because we dont have strict - # validation for this fields - %w(name skype linkedin twitter bio).each do |attr| - value = user_attributes[attr] - user_attributes[attr] = sanitize(strip_tags(value)) if value.present? - end - - user_attributes - end - def authorize_change_password! return render_404 if @user.ldap_user? end diff --git a/app/models/user.rb b/app/models/user.rb index 6de8d2d4c39..ddbdec8acfc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -116,7 +116,10 @@ class User < ActiveRecord::Base validate :namespace_uniq, if: ->(user) { user.username_changed? } before_validation :generate_password, on: :create + before_validation :sanitize_attrs + before_save :ensure_authentication_token + alias_attribute :private_token, :authentication_token delegate :path, to: :namespace, allow_nil: true, prefix: true @@ -371,4 +374,11 @@ class User < ActiveRecord::Base def created_by User.find_by_id(created_by_id) if created_by_id end + + def sanitize_attrs + %w(name username skype linkedin twitter bio).each do |attr| + value = self.send(attr) + self.send("#{attr}=", Sanitize.clean(value)) if value.present? + end + end end diff --git a/app/views/admin/teams/projects/new.html.haml b/app/views/admin/teams/projects/new.html.haml index dcb3dbbc433..21bf65f9c3d 100644 --- a/app/views/admin/teams/projects/new.html.haml +++ b/app/views/admin/teams/projects/new.html.haml @@ -8,7 +8,7 @@ .input = select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' - %h6 Choose greatest user acces for your team in this projects: + %h6 Choose greatest user access for your team in these projects: .clearfix = label_tag :greatest_project_access, "Greatest Access" .input diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 1685c6eec53..80ddc05b503 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -26,7 +26,7 @@ on the top of this page %li Ask in our - = link_to "support forum", "https://groups.google.com/forum/#!forum/gitlabhq" + = link_to "mailing list", "https://groups.google.com/forum/#!forum/gitlabhq" or on = link_to "Stack Overflow", "http://stackoverflow.com/questions/tagged/gitlab" %li diff --git a/app/views/projects/teams/available.html.haml b/app/views/projects/teams/available.html.haml index 29fe8ed25cd..880a02aeaf5 100644 --- a/app/views/projects/teams/available.html.haml +++ b/app/views/projects/teams/available.html.haml @@ -10,7 +10,7 @@ .padded = label_tag :team_id, "Team" .input= select_tag(:team_id, options_from_collection_for_select(@teams, :id, :name), prompt: "Select team", class: "chosen xxlarge", required: true) - %p.slead Choose greatest user acces in team you want to assign: + %p.slead Choose greatest user access for your team in this project: .padded = label_tag :team_ids, "Permission" .input= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles), {class: "project-access-select chosen span3" } diff --git a/config/puma.rb.example b/config/puma.rb.example index ad5e3e23501..08eace369fd 100644 --- a/config/puma.rb.example +++ b/config/puma.rb.example @@ -93,6 +93,13 @@ bind "unix://#{application_path}/tmp/sockets/gitlab.socket" # # workers 2 +# GitLab cluster mode recommendations +# If you have more than 1 GB RAM, uncomment one of the following lines: +# +# workers 2 # if you have at least 1.5 GB RAM +# workers 3 # if you have at least 2 GB RAM +# workers 4 # if you have at least 2.5 GB RAM + # Code to run when a worker boots to setup the process before booting # the app. # diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example new file mode 100644 index 00000000000..00c509dd829 --- /dev/null +++ b/config/unicorn.rb.example @@ -0,0 +1,102 @@ +# Sample verbose configuration file for Unicorn (not Rack) +# +# This configuration file documents many features of Unicorn +# that may not be needed for some applications. See +# http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb +# for a much simpler configuration file. +# +# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete +# documentation. + +# Use at least one worker per core if you're on a dedicated server, +# more will usually help for _short_ waits on databases/caches. +worker_processes 2 + +# Since Unicorn is never exposed to outside clients, it does not need to +# run on the standard HTTP port (80), there is no reason to start Unicorn +# as root unless it's from system init scripts. +# If running the master process as root and the workers as an unprivileged +# user, do this to switch euid/egid in the workers (also chowns logs): +# user "unprivileged_user", "unprivileged_group" + +# Help ensure your application will always spawn in the symlinked +# "current" directory that Capistrano sets up. +working_directory "/home/git/gitlab/current" # available in 0.94.0+ + +# listen on both a Unix domain socket and a TCP port, +# we use a shorter backlog for quicker failover when busy +listen "/home/git/gitlab/tmp/sockets/gitlab.socket", :backlog => 64 +listen 8080, :tcp_nopush => true + +# nuke workers after 30 seconds instead of 60 seconds (the default) +timeout 30 + +# feel free to point this anywhere accessible on the filesystem +pid "/home/git/gitlab/tmp/pids/unicorn.pid" + +# By default, the Unicorn logger will write to stderr. +# Additionally, ome applications/frameworks log to stderr or stdout, +# so prevent them from going to /dev/null when daemonized here: +stderr_path "/home/git/gitlab/log/unicorn.stderr.log" +stdout_path "/home/git/gitlab/log/unicorn.stdout.log" + +# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings +# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow +preload_app true +GC.respond_to?(:copy_on_write_friendly=) and + GC.copy_on_write_friendly = true + +# Enable this flag to have unicorn test client connections by writing the +# beginning of the HTTP headers before calling the application. This +# prevents calling the application for connections that have disconnected +# while queued. This is only guaranteed to detect clients on the same +# host unicorn runs on, and unlikely to detect disconnects even on a +# fast LAN. +check_client_connection false + +before_fork do |server, worker| + # the following is highly recomended for Rails + "preload_app true" + # as there's no need for the master process to hold a connection + defined?(ActiveRecord::Base) and + ActiveRecord::Base.connection.disconnect! + + # The following is only recommended for memory/DB-constrained + # installations. It is not needed if your system can house + # twice as many worker_processes as you have configured. + # + # # This allows a new master process to incrementally + # # phase out the old master process with SIGTTOU to avoid a + # # thundering herd (especially in the "preload_app false" case) + # # when doing a transparent upgrade. The last worker spawned + # # will then kill off the old master process with a SIGQUIT. + # old_pid = "#{server.config[:pid]}.oldbin" + # if old_pid != server.pid + # begin + # sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU + # Process.kill(sig, File.read(old_pid).to_i) + # rescue Errno::ENOENT, Errno::ESRCH + # end + # end + # + # Throttle the master from forking too quickly by sleeping. Due + # to the implementation of standard Unix signal handlers, this + # helps (but does not completely) prevent identical, repeated signals + # from being lost when the receiving process is busy. + # sleep 1 +end + +after_fork do |server, worker| + # per-process listener ports for debugging/admin/migrations + # addr = "127.0.0.1:#{9293 + worker.nr}" + # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true) + + # the following is *required* for Rails + "preload_app true", + defined?(ActiveRecord::Base) and + ActiveRecord::Base.establish_connection + + # if preload_app is true, then you may also want to check and + # restart any other shared sockets/descriptors such as Memcached, + # and Redis. TokyoCabinet file handles are safe to reuse + # between any number of forked children (assuming your kernel + # correctly implements pread()/pwrite() system calls) +end diff --git a/doc/api/README.md b/doc/api/README.md index 4bcd89949df..9d6e229e4cb 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -69,18 +69,18 @@ When listing resources you can pass the following parameters: ## Contents -+ [Users](users.md) -+ [Session](session.md) -+ [Projects](projects.md) -+ [Project Snippets](project_snippets.md) -+ [Repositories](repositories.md) -+ [Issues](issues.md) -+ [Milestones](milestones.md) -+ [Notes](notes.md) -+ [Deploy Keys](deploy_keys.md) -+ [System Hooks](system_hooks.md) -+ [Groups](groups.md) -+ [User Teams](user_teams.md) ++ [Users](api/users.md) ++ [Session](api/session.md) ++ [Projects](api/projects.md) ++ [Project Snippets](api/project_snippets.md) ++ [Repositories](api/repositories.md) ++ [Issues](api/issues.md) ++ [Milestones](api/milestones.md) ++ [Notes](api/notes.md) ++ [Deploy Keys](api/deploy_keys.md) ++ [System Hooks](api/system_hooks.md) ++ [Groups](api/groups.md) ++ [User Teams](api/user_teams.md) ## Clients diff --git a/doc/api/projects.md b/doc/api/projects.md index 323c0be63a4..41b6b6add39 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -453,3 +453,28 @@ Parameters: + `id` (required) - The ID of the project. + `branch` (required) - The name of the branch. + +## Admin fork relation + +Allows modification of the forked relationship between existing projects. . Available only for admins. + +### Create a forked from/to relation between existing projects. + +``` +POST /projects/:id/fork/:forked_from_id +``` + +Parameters: + ++ `id` (required) - The ID of the project ++ `forked_from_id:` (required) - The ID of the project that was forked from + +### Delete an existing forked from relationship + +``` +DELETE /projects/:id/fork +``` + +Parameter: + ++ `id` (required) - The ID of the project
\ No newline at end of file diff --git a/doc/install/installation.md b/doc/install/installation.md index 146f45f7cd6..6cad280acaf 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -42,11 +42,13 @@ up-to-date and install it. apt-get install sudo -y **Note:** -Vim is an editor that is used here whenever there are files that need to be -edited by hand. But, you can use any editor you like instead. +During this installation some files will need to be edited manually. +If you are familiar with vim set it as default editor with the commands below. +If you are not familiar with vim please skip this and keep using the default editor. - # Install vim + # Install vim and set as default editor sudo apt-get install -y vim + sudo update-alternatives --set editor /usr/bin/vim.basic Install the required packages: @@ -123,7 +125,7 @@ GitLab Shell is a ssh access and repository management software developed specia # Edit config and replace gitlab_url # with something like 'http://domain.com/' - sudo -u git -H vim config.yml + sudo -u git -H editor config.yml # Do setup sudo -u git -H ./bin/install @@ -162,7 +164,7 @@ You can change `5-3-stable` to `master` if you want the *bleeding edge* version, # Make sure to change "localhost" to the fully-qualified domain name of your # host serving GitLab where necessary - sudo -u git -H vim config/gitlab.yml + sudo -u git -H editor config/gitlab.yml # Make sure GitLab can write to the log/ and tmp/ directories sudo chown -R git log/ @@ -188,7 +190,7 @@ You can change `5-3-stable` to `master` if you want the *bleeding edge* version, # Enable cluster mode if you expect to have a high load instance # Ex. change amount of workers to 3 for 2GB RAM server - sudo -u git -H vim config/puma.rb + sudo -u git -H editor config/puma.rb # Configure Git global settings for git user, useful when editing via web # Edit user.email according to what is set in gitlab.yml @@ -214,7 +216,7 @@ Make sure to edit both `gitlab.yml` and `puma.rb` to match your setup. # Change 'root' to 'gitlab' # Change 'secure password' with the value you have given to $password # You can keep the double quotes around the password - sudo -u git -H vim config/database.yml + sudo -u git -H editor config/database.yml # Make config/database.yml readable to git only sudo -u git -H chmod o-rwx config/database.yml @@ -226,10 +228,10 @@ Make sure to edit both `gitlab.yml` and `puma.rb` to match your setup. sudo gem install charlock_holmes --version '0.6.9.4' # For MySQL (note, the option says "without ... postgres") - sudo -u git -H bundle install --deployment --without development test postgres + sudo -u git -H bundle install --deployment --without development test postgres unicorn aws # Or for PostgreSQL (note, the option says "without ... mysql") - sudo -u git -H bundle install --deployment --without development test mysql + sudo -u git -H bundle install --deployment --without development test mysql unicorn aws ## Initialize Database and Activate Advanced Features @@ -295,7 +297,7 @@ Make sure to edit the config file to match your setup: # Change YOUR_SERVER_FQDN to the fully-qualified # domain name of your host serving GitLab. - sudo vim /etc/nginx/sites-available/gitlab + sudo editor /etc/nginx/sites-available/gitlab ## Restart diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 0d8cac5c8fd..dea5771d6b6 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -25,6 +25,12 @@ module API expose :id, :url, :created_at end + class ForkedFromProject < Grape::Entity + expose :id + expose :name, :name_with_namespace + expose :path, :path_with_namespace + end + class Project < Grape::Entity expose :id, :description, :default_branch, :public, :ssh_url_to_repo, :http_url_to_repo, :web_url expose :owner, using: Entities::UserBasic @@ -32,6 +38,7 @@ module API expose :path, :path_with_namespace expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at, :last_activity_at expose :namespace + expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? } end class ProjectMember < UserBasic diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 94cf4f2e69f..f857d4133b2 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -5,12 +5,12 @@ module API end def user_project - @project ||= find_project + @project ||= find_project(params[:id]) @project || not_found! end - def find_project - project = Project.find_by_id(params[:id]) || Project.find_with_namespace(params[:id]) + def find_project(id) + project = Project.find_by_id(id) || Project.find_with_namespace(id) if project && can?(current_user, :read_project, project) project diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 6dc051e4ba2..d5709f5cb59 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -121,6 +121,42 @@ module API end + # Mark this project as forked from another + # + # Parameters: + # id: (required) - The ID of the project being marked as a fork + # forked_from_id: (required) - The ID of the project it was forked from + # Example Request: + # POST /projects/:id/fork/:forked_from_id + post ":id/fork/:forked_from_id" do + authenticated_as_admin! + forked_from_project = find_project(params[:forked_from_id]) + unless forked_from_project.nil? + if user_project.forked_from_project.nil? + user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id) + else + render_api_error!("Project already forked", 409) + end + else + not_found! + end + + end + + # Remove a forked_from relationship + # + # Parameters: + # id: (required) - The ID of the project being marked as a fork + # Example Request: + # DELETE /projects/:id/fork + delete ":id/fork" do + authenticated_as_admin! + unless user_project.forked_project_link.nil? + user_project.forked_project_link.destroy + end + end + + # Get a project team members # # Parameters: diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 31075149647..a6612af83eb 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -595,4 +595,71 @@ describe API::API do end end end + + describe :fork_admin do + let(:project_fork_target) { create(:project) } + let(:project_fork_source) { create(:project, public: true) } + + describe "POST /projects/:id/fork/:forked_from_id" do + let(:new_project_fork_source) { create(:project, public: true) } + + it "shouldn't available for non admin users" do + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) + response.status.should == 403 + end + + it "should allow project to be forked from an existing project" do + project_fork_target.forked?.should_not be_true + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) + response.status.should == 201 + project_fork_target.reload + project_fork_target.forked_from_project.id.should == project_fork_source.id + project_fork_target.forked_project_link.should_not be_nil + project_fork_target.forked?.should be_true + end + + it "should fail if forked_from project which does not exist" do + post api("/projects/#{project_fork_target.id}/fork/9999", admin) + response.status.should == 404 + end + + it "should fail with 409 if already forked" do + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) + project_fork_target.reload + project_fork_target.forked_from_project.id.should == project_fork_source.id + post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin) + response.status.should == 409 + project_fork_target.reload + project_fork_target.forked_from_project.id.should == project_fork_source.id + project_fork_target.forked?.should be_true + end + end + + describe "DELETE /projects/:id/fork" do + + it "shouldn't available for non admin users" do + delete api("/projects/#{project_fork_target.id}/fork", user) + response.status.should == 403 + end + + it "should make forked project unforked" do + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) + project_fork_target.reload + project_fork_target.forked_from_project.should_not be_nil + project_fork_target.forked?.should be_true + delete api("/projects/#{project_fork_target.id}/fork", admin) + response.status.should == 200 + project_fork_target.reload + project_fork_target.forked_from_project.should be_nil + project_fork_target.forked?.should_not be_true + end + + it "should be idempotent if not forked" do + project_fork_target.forked_from_project.should be_nil + delete api("/projects/#{project_fork_target.id}/fork", admin) + response.status.should == 200 + project_fork_target.reload.forked_from_project.should be_nil + end + end + end end |
