Create a fork network when forking a project
When no fork network exists for the source projects, we create a new one with the correct source
This commit is contained in:
parent
20727db170
commit
d328007214
9 changed files with 160 additions and 6 deletions
|
@ -1,3 +1,11 @@
|
|||
class ForkNetwork < ActiveRecord::Base
|
||||
belongs_to :root_project
|
||||
belongs_to :root_project, class_name: 'Project'
|
||||
has_many :fork_network_members
|
||||
has_many :projects, through: :fork_network_members
|
||||
|
||||
after_create :add_root_as_member, if: :root_project
|
||||
|
||||
def add_root_as_member
|
||||
projects << root_project
|
||||
end
|
||||
end
|
||||
|
|
7
app/models/fork_network_member.rb
Normal file
7
app/models/fork_network_member.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class ForkNetworkMember < ActiveRecord::Base
|
||||
belongs_to :fork_network
|
||||
belongs_to :project
|
||||
belongs_to :forked_from_project, class_name: 'Project'
|
||||
|
||||
validates :fork_network, :project, presence: true
|
||||
end
|
|
@ -118,11 +118,20 @@ class Project < ActiveRecord::Base
|
|||
has_one :mock_monitoring_service
|
||||
has_one :microsoft_teams_service
|
||||
|
||||
# TODO: replace these relations with the fork network versions
|
||||
has_one :forked_project_link, foreign_key: "forked_to_project_id"
|
||||
has_one :forked_from_project, through: :forked_project_link
|
||||
|
||||
has_many :forked_project_links, foreign_key: "forked_from_project_id"
|
||||
has_many :forks, through: :forked_project_links, source: :forked_to_project
|
||||
# TODO: replace these relations with the fork network versions
|
||||
|
||||
has_one :root_of_fork_network,
|
||||
foreign_key: 'root_project_id',
|
||||
inverse_of: :root_project,
|
||||
class_name: 'ForkNetwork'
|
||||
has_one :fork_network_member
|
||||
has_one :fork_network, through: :fork_network_member
|
||||
|
||||
# Merge Requests for target project should be removed with it
|
||||
has_many :merge_requests, foreign_key: 'target_project_id'
|
||||
|
@ -1119,8 +1128,8 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def forked_from?(project)
|
||||
forked? && project == forked_from_project
|
||||
def in_fork_network_of?(project)
|
||||
forked? && project.fork_network == fork_network
|
||||
end
|
||||
|
||||
def origin_merge_requests
|
||||
|
|
|
@ -23,11 +23,31 @@ module Projects
|
|||
|
||||
refresh_forks_count
|
||||
|
||||
link_fork_network(new_project)
|
||||
|
||||
new_project
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fork_network
|
||||
if @project.fork_network
|
||||
@project.fork_network
|
||||
elsif forked_from_project = @project.forked_from_project
|
||||
# TODO: remove this case when all background migrations have completed
|
||||
# this only happens when a project had a `forked_project_link` that was
|
||||
# not migrated to the `fork_network` relation
|
||||
forked_from_project.fork_network || forked_from_project.create_root_of_fork_network
|
||||
else
|
||||
@project.create_root_of_fork_network
|
||||
end
|
||||
end
|
||||
|
||||
def link_fork_network(new_project)
|
||||
fork_network.fork_network_members.create(project: new_project,
|
||||
forked_from_project: @project)
|
||||
end
|
||||
|
||||
def refresh_forks_count
|
||||
Projects::ForksCountService.new(@project).refresh_cache
|
||||
end
|
||||
|
|
26
db/migrate/20170928133643_create_fork_network_members.rb
Normal file
26
db/migrate/20170928133643_create_fork_network_members.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
class CreateForkNetworkMembers < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
create_table :fork_network_members do |t|
|
||||
t.references :fork_network, null: false, index: true, foreign_key: { on_delete: :cascade }
|
||||
t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
|
||||
t.references :forked_from_project, references: :projects
|
||||
end
|
||||
|
||||
add_concurrent_foreign_key :fork_network_members, :projects,
|
||||
column: :forked_from_project_id,
|
||||
on_delete: :nullify
|
||||
end
|
||||
|
||||
def down
|
||||
if foreign_key_exists?(:fork_network_members, column: :forked_from_project_id)
|
||||
remove_foreign_key :fork_network_members, column: :forked_from_project_id
|
||||
end
|
||||
drop_table :fork_network_members
|
||||
end
|
||||
end
|
12
db/schema.rb
12
db/schema.rb
|
@ -591,6 +591,15 @@ ActiveRecord::Schema.define(version: 20171006091000) do
|
|||
|
||||
add_index "features", ["key"], name: "index_features_on_key", unique: true, using: :btree
|
||||
|
||||
create_table "fork_network_members", force: :cascade do |t|
|
||||
t.integer "fork_network_id", null: false
|
||||
t.integer "project_id", null: false
|
||||
t.integer "forked_from_project_id"
|
||||
end
|
||||
|
||||
add_index "fork_network_members", ["fork_network_id"], name: "index_fork_network_members_on_fork_network_id", using: :btree
|
||||
add_index "fork_network_members", ["project_id"], name: "index_fork_network_members_on_project_id", unique: true, using: :btree
|
||||
|
||||
create_table "fork_networks", force: :cascade do |t|
|
||||
t.integer "root_project_id"
|
||||
t.string "deleted_root_project_name"
|
||||
|
@ -1800,6 +1809,9 @@ ActiveRecord::Schema.define(version: 20171006091000) do
|
|||
add_foreign_key "environments", "projects", name: "fk_d1c8c1da6a", on_delete: :cascade
|
||||
add_foreign_key "events", "projects", on_delete: :cascade
|
||||
add_foreign_key "events", "users", column: "author_id", name: "fk_edfd187b6f", on_delete: :cascade
|
||||
add_foreign_key "fork_network_members", "fork_networks", on_delete: :cascade
|
||||
add_foreign_key "fork_network_members", "projects", column: "forked_from_project_id", name: "fk_b01280dae4", on_delete: :nullify
|
||||
add_foreign_key "fork_network_members", "projects", on_delete: :cascade
|
||||
add_foreign_key "fork_networks", "projects", column: "root_project_id", name: "fk_e7b436b2b5", on_delete: :nullify
|
||||
add_foreign_key "forked_project_links", "projects", column: "forked_to_project_id", name: "fk_434510edb0", on_delete: :cascade
|
||||
add_foreign_key "gcp_clusters", "projects", on_delete: :cascade
|
||||
|
|
8
spec/models/fork_network_member_spec.rb
Normal file
8
spec/models/fork_network_member_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ForkNetworkMember do
|
||||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:project) }
|
||||
it { is_expected.to validate_presence_of(:fork_network) }
|
||||
end
|
||||
end
|
|
@ -1,5 +1,42 @@
|
|||
require 'rails_helper'
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ForkNetwork, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
describe ForkNetwork do
|
||||
include ProjectForksHelper
|
||||
|
||||
describe '#add_root_as_member' do
|
||||
it 'adds the root project as a member when creating a new root network' do
|
||||
project = create(:project)
|
||||
fork_network = described_class.create(root_project: project)
|
||||
|
||||
expect(fork_network.projects).to include(project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a deleted project' do
|
||||
it 'keeps the fork network' do
|
||||
project = create(:project, :public)
|
||||
forked = fork_project(project)
|
||||
project.destroy!
|
||||
|
||||
fork_network = forked.reload.fork_network
|
||||
|
||||
expect(fork_network.projects).to contain_exactly(forked)
|
||||
expect(fork_network.root_project).to be_nil
|
||||
end
|
||||
|
||||
it 'allows multiple fork networks where the root project is deleted' do
|
||||
first_project = create(:project)
|
||||
second_project = create(:project)
|
||||
first_fork = fork_project(first_project)
|
||||
second_fork = fork_project(second_project)
|
||||
|
||||
first_project.destroy
|
||||
second_project.destroy
|
||||
|
||||
expect(first_fork.fork_network).not_to be_nil
|
||||
expect(first_fork.fork_network.root_project).to be_nil
|
||||
expect(second_fork.fork_network).not_to be_nil
|
||||
expect(second_fork.fork_network.root_project).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -60,6 +60,33 @@ describe Projects::ForkService do
|
|||
|
||||
expect(@from_project.forks_count).to eq(1)
|
||||
end
|
||||
|
||||
it 'creates a fork network with the new project and the root project set' do
|
||||
to_project
|
||||
fork_network = @from_project.reload.fork_network
|
||||
|
||||
expect(fork_network).not_to be_nil
|
||||
expect(fork_network.root_project).to eq(@from_project)
|
||||
expect(fork_network.projects).to contain_exactly(@from_project, to_project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'creating a fork of a fork' do
|
||||
let(:from_forked_project) { fork_project(@from_project, @to_user) }
|
||||
let(:other_namespace) do
|
||||
group = create(:group)
|
||||
group.add_owner(@to_user)
|
||||
group
|
||||
end
|
||||
let(:to_project) { fork_project(from_forked_project, @to_user, namespace: other_namespace) }
|
||||
|
||||
it 'sets the root of the network to the root project' do
|
||||
expect(to_project.fork_network.root_project).to eq(@from_project)
|
||||
end
|
||||
|
||||
it 'sets the forked_from_project on the membership' do
|
||||
expect(to_project.fork_network_member.forked_from_project).to eq(from_forked_project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue