diff --git a/Gemfile b/Gemfile index ddffa5eff..75bcf07fb 100644 --- a/Gemfile +++ b/Gemfile @@ -27,6 +27,7 @@ end group :development, :test do gem 'micro_migrations', git: 'https://gist.github.com/2087829.git' + gem 'foreigner', require: nil gem 'data_migrations', '~> 0.0.1' end diff --git a/Rakefile b/Rakefile index 1b5ec63d2..463727fa4 100644 --- a/Rakefile +++ b/Rakefile @@ -2,9 +2,14 @@ require 'rspec/core/rake_task' require 'bundler/setup' +require 'foreigner' require 'micro_migrations' require 'travis' +task :environment do + Foreigner.load +end + desc 'Run specs' RSpec::Core::RakeTask.new do |t| t.pattern = './spec/**/*_spec.rb' diff --git a/db/migrate/20131227155535_create_branches.rb b/db/migrate/20131227155535_create_branches.rb new file mode 100644 index 000000000..d0cfb3225 --- /dev/null +++ b/db/migrate/20131227155535_create_branches.rb @@ -0,0 +1,16 @@ +class CreateBranches < ActiveRecord::Migration + def change + create_table :branches do |t| + t.string :name, null: false + t.references :repository, null: false + t.references :last_build + t.foreign_key :repositories + t.foreign_key :builds, column: :last_build_id + + t.timestamps + end + + add_index :branches, :repository_id + add_index :branches, [:name, :repository_id], unique: true + end +end diff --git a/db/migrate/20131227160256_create_builds_branches.rb b/db/migrate/20131227160256_create_builds_branches.rb new file mode 100644 index 000000000..f78658c88 --- /dev/null +++ b/db/migrate/20131227160256_create_builds_branches.rb @@ -0,0 +1,16 @@ +class CreateBuildsBranches < ActiveRecord::Migration + def change + create_table :builds_branches do |t| + t.references :build, null: false + t.references :branch, null: false + t.foreign_key :builds, dependent: :delete + t.foreign_key :branches, dependent: :delete + + t.timestamps + end + + add_index :builds_branches, :build_id + add_index :builds_branches, :branch_id + add_index :builds_branches, [:build_id, :branch_id], unique: true + end +end diff --git a/db/migrate/20131230093907_create_tags.rb b/db/migrate/20131230093907_create_tags.rb new file mode 100644 index 000000000..963ef4732 --- /dev/null +++ b/db/migrate/20131230093907_create_tags.rb @@ -0,0 +1,16 @@ +class CreateTags < ActiveRecord::Migration + def change + create_table :tags do |t| + t.string :name, null: false + t.references :last_build + t.references :repository, null: false + t.foreign_key :repositories + t.foreign_key :builds, column: :last_build_id + + t.timestamps + end + + add_index :tags, :repository_id + add_index :tags, [:name, :repository_id], unique: true + end +end diff --git a/db/migrate/20131230094201_create_builds_tags.rb b/db/migrate/20131230094201_create_builds_tags.rb new file mode 100644 index 000000000..db15c4a47 --- /dev/null +++ b/db/migrate/20131230094201_create_builds_tags.rb @@ -0,0 +1,16 @@ +class CreateBuildsTags < ActiveRecord::Migration + def change + create_table :builds_tags do |t| + t.references :build, null: false + t.references :tag, null: false + t.foreign_key :builds, dependent: :delete + t.foreign_key :tags, dependent: :delete + + t.timestamps + end + + add_index :builds_tags, :build_id + add_index :builds_tags, :tag_id + add_index :builds_tags, [:build_id, :tag_id], unique: true + end +end diff --git a/db/migrate/20140116125536_create_commits_tags.rb b/db/migrate/20140116125536_create_commits_tags.rb new file mode 100644 index 000000000..f59eee981 --- /dev/null +++ b/db/migrate/20140116125536_create_commits_tags.rb @@ -0,0 +1,17 @@ +class CreateCommitsTags < ActiveRecord::Migration + def change + create_table :commits_tags do |t| + t.references :commit + t.references :tag + t.references :request + + t.foreign_key :commits, dependent: :delete + t.foreign_key :tags, dependent: :delete + t.foreign_key :requests, dependent: :delete + + t.timestamps + end + + add_index :commits_tags, [:commit_id, :tag_id], unique: true + end +end diff --git a/db/migrate/20140116125602_create_commits_branches.rb b/db/migrate/20140116125602_create_commits_branches.rb new file mode 100644 index 000000000..b56c1537c --- /dev/null +++ b/db/migrate/20140116125602_create_commits_branches.rb @@ -0,0 +1,17 @@ +class CreateCommitsBranches < ActiveRecord::Migration + def change + create_table :commits_branches do |t| + t.references :commit + t.references :branch + t.references :request + + t.foreign_key :commits, dependent: :delete + t.foreign_key :branches, dependent: :delete + t.foreign_key :requests, dependent: :delete + + t.timestamps + end + + add_index :commits_branches, [:commit_id, :branch_id], unique: true + end +end diff --git a/db/structure.sql b/db/structure.sql index 9deaa8e2b..d7ad3d2ed 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -3,7 +3,6 @@ -- SET statement_timeout = 0; -SET lock_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; SET check_function_bodies = false; @@ -189,6 +188,44 @@ CREATE SEQUENCE annotations_id_seq ALTER SEQUENCE annotations_id_seq OWNED BY annotations.id; +-- +-- Name: branches; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE branches ( + id integer NOT NULL, + name character varying(255) NOT NULL, + repository_id integer NOT NULL, + last_build_id integer, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + last_build_number character varying(255), + last_build_state integer, + last_build_started_at timestamp without time zone, + last_build_finished_at timestamp without time zone, + last_build_duration integer +); + + +-- +-- Name: branches_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE branches_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: branches_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE branches_id_seq OWNED BY branches.id; + + -- -- Name: broadcasts; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -265,6 +302,38 @@ CREATE TABLE builds ( ); +-- +-- Name: builds_branches; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE builds_branches ( + id integer NOT NULL, + build_id integer NOT NULL, + branch_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: builds_branches_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE builds_branches_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: builds_branches_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE builds_branches_id_seq OWNED BY builds_branches.id; + + -- -- Name: builds_id_seq; Type: SEQUENCE; Schema: public; Owner: - -- @@ -284,6 +353,38 @@ CREATE SEQUENCE builds_id_seq ALTER SEQUENCE builds_id_seq OWNED BY builds.id; +-- +-- Name: builds_tags; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE builds_tags ( + id integer NOT NULL, + build_id integer NOT NULL, + tag_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: builds_tags_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE builds_tags_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: builds_tags_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE builds_tags_id_seq OWNED BY builds_tags.id; + + -- -- Name: commits; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -306,6 +407,39 @@ CREATE TABLE commits ( ); +-- +-- Name: commits_branches; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE commits_branches ( + id integer NOT NULL, + commit_id integer, + branch_id integer, + request_id integer, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: commits_branches_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE commits_branches_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: commits_branches_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE commits_branches_id_seq OWNED BY commits_branches.id; + + -- -- Name: commits_id_seq; Type: SEQUENCE; Schema: public; Owner: - -- @@ -325,6 +459,39 @@ CREATE SEQUENCE commits_id_seq ALTER SEQUENCE commits_id_seq OWNED BY commits.id; +-- +-- Name: commits_tags; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE commits_tags ( + id integer NOT NULL, + commit_id integer, + tag_id integer, + request_id integer, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: commits_tags_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE commits_tags_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: commits_tags_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE commits_tags_id_seq OWNED BY commits_tags.id; + + -- -- Name: emails; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -715,6 +882,44 @@ CREATE SEQUENCE ssl_keys_id_seq ALTER SEQUENCE ssl_keys_id_seq OWNED BY ssl_keys.id; +-- +-- Name: tags; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE tags ( + id integer NOT NULL, + name character varying(255) NOT NULL, + last_build_id integer, + repository_id integer NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + last_build_number character varying(255), + last_build_state integer, + last_build_started_at timestamp without time zone, + last_build_finished_at timestamp without time zone, + last_build_duration integer +); + + +-- +-- Name: tags_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE tags_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: tags_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE tags_id_seq OWNED BY tags.id; + + -- -- Name: tokens; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -834,6 +1039,13 @@ ALTER TABLE ONLY annotation_providers ALTER COLUMN id SET DEFAULT nextval('annot ALTER TABLE ONLY annotations ALTER COLUMN id SET DEFAULT nextval('annotations_id_seq'::regclass); +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY branches ALTER COLUMN id SET DEFAULT nextval('branches_id_seq'::regclass); + + -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -841,6 +1053,20 @@ ALTER TABLE ONLY annotations ALTER COLUMN id SET DEFAULT nextval('annotations_id ALTER TABLE ONLY broadcasts ALTER COLUMN id SET DEFAULT nextval('broadcasts_id_seq'::regclass); +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY builds_branches ALTER COLUMN id SET DEFAULT nextval('builds_branches_id_seq'::regclass); + + +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY builds_tags ALTER COLUMN id SET DEFAULT nextval('builds_tags_id_seq'::regclass); + + -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -848,6 +1074,20 @@ ALTER TABLE ONLY broadcasts ALTER COLUMN id SET DEFAULT nextval('broadcasts_id_s ALTER TABLE ONLY commits ALTER COLUMN id SET DEFAULT nextval('commits_id_seq'::regclass); +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY commits_branches ALTER COLUMN id SET DEFAULT nextval('commits_branches_id_seq'::regclass); + + +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY commits_tags ALTER COLUMN id SET DEFAULT nextval('commits_tags_id_seq'::regclass); + + -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -911,6 +1151,13 @@ ALTER TABLE ONLY requests ALTER COLUMN id SET DEFAULT nextval('requests_id_seq': ALTER TABLE ONLY ssl_keys ALTER COLUMN id SET DEFAULT nextval('ssl_keys_id_seq'::regclass); +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY tags ALTER COLUMN id SET DEFAULT nextval('tags_id_seq'::regclass); + + -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -948,6 +1195,14 @@ ALTER TABLE ONLY annotations ADD CONSTRAINT annotations_pkey PRIMARY KEY (id); +-- +-- Name: branches_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY branches + ADD CONSTRAINT branches_pkey PRIMARY KEY (id); + + -- -- Name: broadcasts_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -956,6 +1211,14 @@ ALTER TABLE ONLY broadcasts ADD CONSTRAINT broadcasts_pkey PRIMARY KEY (id); +-- +-- Name: builds_branches_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY builds_branches + ADD CONSTRAINT builds_branches_pkey PRIMARY KEY (id); + + -- -- Name: builds_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -964,6 +1227,22 @@ ALTER TABLE ONLY builds ADD CONSTRAINT builds_pkey PRIMARY KEY (id); +-- +-- Name: builds_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY builds_tags + ADD CONSTRAINT builds_tags_pkey PRIMARY KEY (id); + + +-- +-- Name: commits_branches_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY commits_branches + ADD CONSTRAINT commits_branches_pkey PRIMARY KEY (id); + + -- -- Name: commits_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -972,6 +1251,14 @@ ALTER TABLE ONLY commits ADD CONSTRAINT commits_pkey PRIMARY KEY (id); +-- +-- Name: commits_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY commits_tags + ADD CONSTRAINT commits_tags_pkey PRIMARY KEY (id); + + -- -- Name: emails_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -1044,6 +1331,14 @@ ALTER TABLE ONLY ssl_keys ADD CONSTRAINT ssl_keys_pkey PRIMARY KEY (id); +-- +-- Name: tags_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY tags + ADD CONSTRAINT tags_pkey PRIMARY KEY (id); + + -- -- Name: tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -1076,6 +1371,41 @@ ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id); +-- +-- Name: index_branches_on_name_and_repository_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE UNIQUE INDEX index_branches_on_name_and_repository_id ON branches USING btree (name, repository_id); + + +-- +-- Name: index_branches_on_repository_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_branches_on_repository_id ON branches USING btree (repository_id); + + +-- +-- Name: index_builds_branches_on_branch_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_builds_branches_on_branch_id ON builds_branches USING btree (branch_id); + + +-- +-- Name: index_builds_branches_on_build_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_builds_branches_on_build_id ON builds_branches USING btree (build_id); + + +-- +-- Name: index_builds_branches_on_build_id_and_branch_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE UNIQUE INDEX index_builds_branches_on_build_id_and_branch_id ON builds_branches USING btree (build_id, branch_id); + + -- -- Name: index_builds_on_id_repository_id_and_event_type_desc; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -1111,6 +1441,48 @@ CREATE INDEX index_builds_on_repository_id_and_state ON builds USING btree (repo CREATE INDEX index_builds_on_request_id ON builds USING btree (request_id); +-- +-- Name: index_builds_tags_on_build_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_builds_tags_on_build_id ON builds_tags USING btree (build_id); + + +-- +-- Name: index_builds_tags_on_build_id_and_tag_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE UNIQUE INDEX index_builds_tags_on_build_id_and_tag_id ON builds_tags USING btree (build_id, tag_id); + + +-- +-- Name: index_builds_tags_on_tag_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_builds_tags_on_tag_id ON builds_tags USING btree (tag_id); + + +-- +-- Name: index_commits_branches_on_commit_id_and_branch_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE UNIQUE INDEX index_commits_branches_on_commit_id_and_branch_id ON commits_branches USING btree (commit_id, branch_id); + + +-- +-- Name: index_commits_on_commit; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_commits_on_commit ON commits USING btree (commit); + + +-- +-- Name: index_commits_tags_on_commit_id_and_tag_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE UNIQUE INDEX index_commits_tags_on_commit_id_and_tag_id ON commits_tags USING btree (commit_id, tag_id); + + -- -- Name: index_emails_on_email; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -1258,6 +1630,20 @@ CREATE INDEX index_requests_on_repository_id ON requests USING btree (repository CREATE INDEX index_ssl_key_on_repository_id ON ssl_keys USING btree (repository_id); +-- +-- Name: index_tags_on_name_and_repository_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE UNIQUE INDEX index_tags_on_name_and_repository_id ON tags USING btree (name, repository_id); + + +-- +-- Name: index_tags_on_repository_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_tags_on_repository_id ON tags USING btree (repository_id); + + -- -- Name: index_users_on_github_id; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -1279,6 +1665,102 @@ CREATE INDEX index_users_on_login ON users USING btree (login); CREATE UNIQUE INDEX unique_schema_migrations ON schema_migrations USING btree (version); +-- +-- Name: branches_last_build_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY branches + ADD CONSTRAINT branches_last_build_id_fk FOREIGN KEY (last_build_id) REFERENCES builds(id); + + +-- +-- Name: branches_repository_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY branches + ADD CONSTRAINT branches_repository_id_fk FOREIGN KEY (repository_id) REFERENCES repositories(id); + + +-- +-- Name: builds_branches_branch_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY builds_branches + ADD CONSTRAINT builds_branches_branch_id_fk FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE; + + +-- +-- Name: builds_branches_build_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY builds_branches + ADD CONSTRAINT builds_branches_build_id_fk FOREIGN KEY (build_id) REFERENCES builds(id) ON DELETE CASCADE; + + +-- +-- Name: builds_tags_build_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY builds_tags + ADD CONSTRAINT builds_tags_build_id_fk FOREIGN KEY (build_id) REFERENCES builds(id) ON DELETE CASCADE; + + +-- +-- Name: builds_tags_tag_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY builds_tags + ADD CONSTRAINT builds_tags_tag_id_fk FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE; + + +-- +-- Name: commits_branches_branch_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY commits_branches + ADD CONSTRAINT commits_branches_branch_id_fk FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE; + + +-- +-- Name: commits_branches_commit_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY commits_branches + ADD CONSTRAINT commits_branches_commit_id_fk FOREIGN KEY (commit_id) REFERENCES commits(id) ON DELETE CASCADE; + + +-- +-- Name: commits_branches_request_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY commits_branches + ADD CONSTRAINT commits_branches_request_id_fk FOREIGN KEY (request_id) REFERENCES requests(id) ON DELETE CASCADE; + + +-- +-- Name: commits_tags_commit_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY commits_tags + ADD CONSTRAINT commits_tags_commit_id_fk FOREIGN KEY (commit_id) REFERENCES commits(id) ON DELETE CASCADE; + + +-- +-- Name: commits_tags_request_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY commits_tags + ADD CONSTRAINT commits_tags_request_id_fk FOREIGN KEY (request_id) REFERENCES requests(id) ON DELETE CASCADE; + + +-- +-- Name: commits_tags_tag_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY commits_tags + ADD CONSTRAINT commits_tags_tag_id_fk FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE; + + -- -- Name: log_parts_log_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -1287,6 +1769,22 @@ ALTER TABLE ONLY log_parts ADD CONSTRAINT log_parts_log_id_fk FOREIGN KEY (log_id) REFERENCES logs(id); +-- +-- Name: tags_last_build_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY tags + ADD CONSTRAINT tags_last_build_id_fk FOREIGN KEY (last_build_id) REFERENCES builds(id); + + +-- +-- Name: tags_repository_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY tags + ADD CONSTRAINT tags_repository_id_fk FOREIGN KEY (repository_id) REFERENCES repositories(id); + + -- -- PostgreSQL database dump complete -- @@ -1545,6 +2043,18 @@ INSERT INTO schema_migrations (version) VALUES ('20131104101056'); INSERT INTO schema_migrations (version) VALUES ('20131109101056'); +INSERT INTO schema_migrations (version) VALUES ('20131227155535'); + +INSERT INTO schema_migrations (version) VALUES ('20131227160256'); + +INSERT INTO schema_migrations (version) VALUES ('20131230093907'); + +INSERT INTO schema_migrations (version) VALUES ('20131230094201'); + +INSERT INTO schema_migrations (version) VALUES ('20140116125536'); + +INSERT INTO schema_migrations (version) VALUES ('20140116125602'); + INSERT INTO schema_migrations (version) VALUES ('20140120225125'); INSERT INTO schema_migrations (version) VALUES ('20140121003026'); @@ -1553,4 +2063,6 @@ INSERT INTO schema_migrations (version) VALUES ('20140204220926'); INSERT INTO schema_migrations (version) VALUES ('20140210003014'); -INSERT INTO schema_migrations (version) VALUES ('20140210012509'); \ No newline at end of file +INSERT INTO schema_migrations (version) VALUES ('20140210012509'); + +INSERT INTO schema_migrations (version) VALUES ('20140414121001'); \ No newline at end of file diff --git a/lib/travis/model.rb b/lib/travis/model.rb index 7ff7678b3..77a809d90 100644 --- a/lib/travis/model.rb +++ b/lib/travis/model.rb @@ -10,8 +10,13 @@ class Model < ActiveRecord::Base require 'travis/model/annotation' require 'travis/model/annotation_provider' require 'travis/model/broadcast' + require 'travis/model/branch' require 'travis/model/build' + require 'travis/model/builds_branch' + require 'travis/model/builds_tag' require 'travis/model/commit' + require 'travis/model/commits_branch' + require 'travis/model/commits_tag' require 'travis/model/email' require 'travis/model/env_helpers' require 'travis/model/job' @@ -23,6 +28,7 @@ class Model < ActiveRecord::Base require 'travis/model/request' require 'travis/model/ssl_key' require 'travis/model/token' + require 'travis/model/tag' require 'travis/model/user' require 'travis/model/url' diff --git a/lib/travis/model/branch.rb b/lib/travis/model/branch.rb new file mode 100644 index 000000000..2b3cf853e --- /dev/null +++ b/lib/travis/model/branch.rb @@ -0,0 +1,9 @@ +class Branch < Travis::Model + has_many :builds_branches + has_many :builds, through: :builds_branches + + belongs_to :repository + belongs_to :last_build + + validates :name, uniqueness: { scope: :repository_id } +end diff --git a/lib/travis/model/build.rb b/lib/travis/model/build.rb index 53f052a3c..9bb7b3506 100644 --- a/lib/travis/model/build.rb +++ b/lib/travis/model/build.rb @@ -2,6 +2,7 @@ require 'core_ext/active_record/base' require 'core_ext/hash/deep_symbolize_keys' require 'simple_states' +require 'travis/retry_on' # Build currently models a central but rather abstract domain entity: the thing # that is triggered by a Github request (service hook ping). @@ -46,7 +47,7 @@ class Build < Travis::Model require 'travis/model/build/states' require 'travis/model/env_helpers' - include Matrix, States, SimpleStates + include Matrix, States, SimpleStates, Travis::RetryOn belongs_to :commit belongs_to :request @@ -54,6 +55,10 @@ class Build < Travis::Model belongs_to :owner, polymorphic: true has_many :matrix, as: :source, order: :id, class_name: 'Job::Test', dependent: :destroy has_many :events, as: :source + has_many :builds_branches + has_many :branches, through: :builds_branches + has_many :builds_tags + has_many :tags, through: :builds_tags validates :repository_id, :commit_id, :request_id, presence: true @@ -79,7 +84,7 @@ def on_state(state) end def on_branch(branch) - pushes.where(branch.present? ? ['branch IN (?)', normalize_to_array(branch)] : []) + pushes.joins(:branches).where(branch.present? ? { branches: { name: normalize_to_array(branch) } } : []) end def by_event_type(event_type) @@ -150,10 +155,19 @@ def per_page self.event_type = request.event_type self.pull_request_title = request.pull_request_title self.pull_request_number = request.pull_request_number - self.branch = commit.branch + self.branch = request.branch_name expand_matrix end + after_create do + if ActiveRecord::Base.connection.table_exists? 'branches' + add_branch(request.branch_name) if request.branch_name && !request.tag_name + end + if ActiveRecord::Base.connection.table_exists? 'tags' + add_tag(request.tag_name) if request.tag_name + end + end + after_save do unless cached_matrix_ids update_column(:cached_matrix_ids, to_postgres_array(matrix_ids)) @@ -203,6 +217,30 @@ def on_default_branch? branch == repository.default_branch end + def add_branch(branch_name) + retry_on(ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid) do + branch = repository.branches.where(name: branch_name).first + if branch + branches.push branch unless branches.include?(branch) + branch + else + branches.create!(name: branch_name, repository_id: repository_id) + end + end + end + + def add_tag(tag_name) + retry_on(ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid) do + tag = repository.tags.where(name: tag_name).first + if tag + tags.push tag unless tags.include?(tag) + tag + else + tags.create!(name: tag_name, repository_id: repository_id) + end + end + end + private def multi_os_enabled? @@ -210,7 +248,7 @@ def multi_os_enabled? end def last_finished_state_on_branch - repository.builds.finished.last_state_on(branch: commit.branch) + repository.builds.finished.last_state_on(branch: request.branch_name) end def to_postgres_array(ids) diff --git a/lib/travis/model/build/denormalize.rb b/lib/travis/model/build/denormalize.rb index d01a51275..9c1fc9d27 100644 --- a/lib/travis/model/build/denormalize.rb +++ b/lib/travis/model/build/denormalize.rb @@ -11,7 +11,15 @@ class Build # These attributes are used in the repositories list and thus read frequently. module Denormalize def denormalize(event, *args) - repository.update_attributes!(denormalize_attributes_for(event)) if denormalize?(event) + if denormalize?(event) + repository.update_attributes!(denormalize_attributes_for(event)) + branches.each { |branch| + branch.update_attributes!(denormalize_attributes_for(event)) + } + tags.each { |tag| + tag.update_attributes!(denormalize_attributes_for(event)) + } + end end DENORMALIZE = { diff --git a/lib/travis/model/builds_branch.rb b/lib/travis/model/builds_branch.rb new file mode 100644 index 000000000..41148c22f --- /dev/null +++ b/lib/travis/model/builds_branch.rb @@ -0,0 +1,6 @@ +class BuildsBranch < Travis::Model + belongs_to :build + belongs_to :branch + + validates :build_id, uniqueness: { scope: :branch_id } +end diff --git a/lib/travis/model/builds_tag.rb b/lib/travis/model/builds_tag.rb new file mode 100644 index 000000000..2af954f43 --- /dev/null +++ b/lib/travis/model/builds_tag.rb @@ -0,0 +1,6 @@ +class BuildsTag < ActiveRecord::Base + belongs_to :build + belongs_to :tag + + validates :build_id, uniqueness: { scope: :tag_id } +end diff --git a/lib/travis/model/commit.rb b/lib/travis/model/commit.rb index 7cf9a61f3..3277d0f39 100644 --- a/lib/travis/model/commit.rb +++ b/lib/travis/model/commit.rb @@ -1,4 +1,5 @@ require 'active_record' +require 'travis/retry_on' # Encapsulates a commit that a Build belongs to (and that a Github Request # referred to). @@ -6,7 +7,26 @@ class Commit < Travis::Model has_one :request belongs_to :repository - validates :commit, :branch, :committed_at, :presence => true + has_many :commits_tags + has_many :tags, through: :commits_tags + has_many :commits_branches + has_many :branches, through: :commits_branches + + validates :commit, :committed_at, :presence => true + + include Travis::RetryOn + + def branch + ActiveSupport::Deprecation.warn("Commit#branch should not be used as commit can contain multiple branches", caller) + super + end + + def tag_name + ActiveSupport::Deprecation.warn("Commit#tag_name should not be used as commit can contain multiple tags", caller) + if ref + ref.scan(%r{refs/tags/(.*?)$}).flatten.first + end + end def pull_request? ref =~ %r(^refs/pull/\d+/merge$) @@ -23,4 +43,34 @@ def range $1 end end + + def add_branch(branch_name, request) + retry_on(ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid) do + branch = repository.branches.where(name: branch_name).first + unless branch + branch = repository.branches.create!(name: branch_name, repository_id: repository_id) + end + + unless branches.include?(branch) + commits_branches.create!(branch_id: branch.id, commit_id: self.id, request_id: request.id) + end + + branch + end + end + + def add_tag(tag_name, request) + retry_on(ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid) do + tag = repository.tags.where(name: tag_name).first + unless tag + tag = repository.tags.create!(name: tag_name, repository_id: repository_id) + end + + unless tags.include?(tag) + commits_tags.create!(tag_id: tag.id, commit_id: self.id, request_id: request.id) + end + + tag + end + end end diff --git a/lib/travis/model/repository.rb b/lib/travis/model/repository.rb index 0245afb27..06acf6fb5 100644 --- a/lib/travis/model/repository.rb +++ b/lib/travis/model/repository.rb @@ -20,6 +20,8 @@ class Repository < Travis::Model has_many :events has_many :permissions has_many :users, through: :permissions + has_many :branches + has_many :tags has_one :last_build, class_name: 'Build', order: 'id DESC' has_one :key, class_name: 'SslKey' @@ -114,14 +116,8 @@ def source_url private? ? "git@github.com:#{slug}.git": "git://github.com/#{slug}.git" end - def branches - self.class.connection.select_values %( - SELECT DISTINCT ON (branch) branch - FROM builds - WHERE builds.repository_id = #{id} - ORDER BY branch DESC - LIMIT 25 - ) + def branch_names + branches.map(&:name) end def last_completed_build(branch = nil) @@ -137,14 +133,16 @@ def build_status(branch) end def last_finished_builds_by_branches(limit = 50) - Build.joins(%( - inner join ( - select distinct on (branch) builds.id - from builds - where builds.repository_id = #{id} and builds.event_type = 'push' - order by branch, finished_at desc - ) as last_builds on builds.id = last_builds.id - )).limit(limit).order('finished_at DESC') + Build.pushes.joins(" + INNER JOIN builds_branches ON builds_branches.build_id = builds.id + LEFT OUTER JOIN ( + SELECT builds.finished_at, builds_branches.branch_id, builds.id FROM builds + INNER JOIN builds_branches ON builds_branches.build_id = builds.id + WHERE repository_id = #{id} + ) b1 ON b1.branch_id = builds_branches.branch_id AND ( + (builds.finished_at < b1.finished_at OR (b1.finished_at = builds.finished_at AND builds.id < b1.id)) + ) + ").where("repository_id = #{id} AND b1.id IS NULL").limit(limit).order('finished_at DESC') end def regenerate_key! diff --git a/lib/travis/model/repository/status_image.rb b/lib/travis/model/repository/status_image.rb index 426c4220f..38197b523 100644 --- a/lib/travis/model/repository/status_image.rb +++ b/lib/travis/model/repository/status_image.rb @@ -39,7 +39,11 @@ def state_from_database build = repo.last_completed_build(branch) if build - cache.write(repo.id, build.branch, build) if cache_enabled? + if cache_enabled? + build.branches.each do |branch| + cache.write(repo.id, branch.name, build) + end + end build.state.to_sym end end diff --git a/lib/travis/model/request/approval.rb b/lib/travis/model/request/approval.rb index b2be170b8..946f1b77a 100644 --- a/lib/travis/model/request/approval.rb +++ b/lib/travis/model/request/approval.rb @@ -91,7 +91,7 @@ def github_pages_explicitly_enabled? end def github_pages? - commit.branch =~ /gh[-_]pages/i + request.branch_name =~ /gh[-_]pages/i end def excluded_repository? @@ -111,7 +111,7 @@ def exclude_rules end def branch_approved? - branches.included?(commit.branch) && !branches.excluded?(commit.branch) + branches.included?(request.branch_name) && !branches.excluded?(request.branch_name) end def branches diff --git a/lib/travis/model/tag.rb b/lib/travis/model/tag.rb new file mode 100644 index 000000000..167a1023e --- /dev/null +++ b/lib/travis/model/tag.rb @@ -0,0 +1,9 @@ +class Tag < ActiveRecord::Base + has_many :builds_tags + has_many :builds, through: :builds_tags + + belongs_to :repository + belongs_to :last_build + + validates :name, uniqueness: { scope: :repository_id } +end diff --git a/lib/travis/requests/services/receive.rb b/lib/travis/requests/services/receive.rb index d81ec5d81..3daaed4b1 100644 --- a/lib/travis/requests/services/receive.rb +++ b/lib/travis/requests/services/receive.rb @@ -63,6 +63,11 @@ def create :owner => owner, :token => params[:token] )) + + commit.add_branch(payload.branch_name, @request) if payload.branch_name + commit.add_tag(payload.tag_name, @request) if payload.tag_name + + @request end def start @@ -93,7 +98,12 @@ def repo end def commit - @commit ||= repo.commits.create!(payload.commit) if payload.commit + @commit ||= find_or_create_commit(payload.commit) if payload.commit + end + + def find_or_create_commit(commit_data) + scope = repo.commits + scope.find_by_commit(commit_data[:commit]) || scope.create!(commit_data) end class Instrument < Notification::Instrument diff --git a/lib/travis/requests/services/receive/pull_request.rb b/lib/travis/requests/services/receive/pull_request.rb index 9e5385adc..8210debb9 100644 --- a/lib/travis/requests/services/receive/pull_request.rb +++ b/lib/travis/requests/services/receive/pull_request.rb @@ -112,6 +112,14 @@ def repo def repo_owner @repo_owner ||= repo['owner'] end + + def branch_name + pull_request['base']['ref'] + end + + def tag_name + nil + end end end end diff --git a/lib/travis/requests/services/receive/push.rb b/lib/travis/requests/services/receive/push.rb index cab16c3d8..671662880 100644 --- a/lib/travis/requests/services/receive/push.rb +++ b/lib/travis/requests/services/receive/push.rb @@ -68,6 +68,14 @@ def commit end end + def branch_name + event['ref'].scan(%r{refs/heads/(.*?)$}).flatten.first + end + + def tag_name + event['ref'].scan(%r{refs/tags/(.*?)$}).flatten.first + end + private def last_unskipped_commit(commits) diff --git a/lib/travis/testing/factories.rb b/lib/travis/testing/factories.rb index 73df23909..08a2d6203 100644 --- a/lib/travis/testing/factories.rb +++ b/lib/travis/testing/factories.rb @@ -1,10 +1,20 @@ require 'factory_girl' FactoryGirl.define do + factory :tag do + name 'deploy.1' + repository { Repository.first || Factory(:repository) } + end + + factory :branch do + name 'master' + repository { Repository.first || Factory(:repository) } + end + factory :build do owner { User.first || Factory(:user) } repository { Repository.first || Factory(:repository) } - association :request + request { Factory(:request, payload: { 'ref' => 'refs/heads/master' }) } association :commit started_at { Time.now.utc } finished_at { Time.now.utc } @@ -14,7 +24,6 @@ factory :commit do commit '62aae5f70ceee39123ef' - branch 'master' message 'the commit message' committed_at '2011-11-11T11:11:11Z' committer_name 'Sven Fuchs' diff --git a/spec/travis/model/build_spec.rb b/spec/travis/model/build_spec.rb index 865df0cc5..c1bf63bae 100644 --- a/spec/travis/model/build_spec.rb +++ b/spec/travis/model/build_spec.rb @@ -79,16 +79,16 @@ describe 'on_branch' do it 'returns builds that are on any of the given branches' do - Factory(:build, commit: Factory(:commit, branch: 'master')) - Factory(:build, commit: Factory(:commit, branch: 'develop')) - Factory(:build, commit: Factory(:commit, branch: 'feature')) + master = Factory(:build, request: create_request(branch_name: 'master')) + develop = Factory(:build, request: create_request(branch_name: 'develop')) + feature = Factory(:build, request: create_request(branch_name: 'feature')) - Build.on_branch('master,develop').map(&:commit).map(&:branch).sort.should == ['develop', 'master'] + Build.on_branch('master,develop').order('id ASC').should == [master, develop] end it 'does not include pull requests' do - Factory(:build, commit: Factory(:commit, branch: 'no-pull'), request: Factory(:request, event_type: 'pull_request')) - Factory(:build, commit: Factory(:commit, branch: 'no-pull'), request: Factory(:request, event_type: 'push')) + Factory(:build, request: create_request(branch_name: 'no-pull', event_type: 'pull_request')) + Factory(:build, request: create_request(branch_name: 'no-pull', event_type: 'push')) Build.on_branch('no-pull').count.should be == 1 end end @@ -144,7 +144,7 @@ 3.times { |i| Factory(:build) } Build.stubs(:per_page).returns(1) - builds = Build.paged({page: 2}) + builds = Build.order('id DESC').paged({page: 2}) builds.should have(1).item builds.first.number.should == '2' end @@ -196,11 +196,31 @@ end it 'is set to the last finished build state on the same branch (disregards other branches)' do - Factory(:build, state: 'failed') - Factory(:build, state: 'passed', commit: Factory(:commit, branch: 'something')) - Factory(:build).reload.previous_state.should == 'failed' + Factory(:build, state: 'failed', request: create_request(branch_name: 'master')) + Factory(:build, state: 'passed', request: create_request(branch_name: 'something')) + Factory(:build, request: create_request(branch_name: 'master')).reload.previous_state.should == 'failed' end end + + it 'adds branch' do + build = Factory(:build, request: create_request(branch_name: 'something')) + build2 = Factory(:build, request: create_request(branch_name: 'something'), repository: build.repository) + + build.branches.map(&:name).should == ['something'] + branch = Branch.where(name: 'something', repository_id: build.repository_id).first + branch.builds.order('id ASC').should == [build, build2] + end + + it 'adds tag' do + build = Factory(:build, request: Factory(:request, payload: { 'ref' => 'refs/tags/something' })) + build2 = Factory(:build, request: Factory(:request, payload: { 'ref' => 'refs/tags/something' }, repository: build.repository)) + + build.tags.map(&:name).should == ['something'] + tag = Tag.where(name: 'something', repository_id: build.repository_id).first + tag.builds.should == [build, build2] + + Branch.count.should == 0 + end end describe 'instance methods' do @@ -287,9 +307,111 @@ build.pull_request_title.should == 'A pull request' end - it 'saves branch before create' do - build = Factory(:build, commit: Factory(:commit, branch: 'development')) - build.branch.should == 'development' + describe '#add_branch' do + it 'adds branch to a build' do + build = Factory(:build) + + build.add_branch('foo') + + build.branches.map(&:name).should include('foo') + build.branches.where(name: 'foo').first.builds.should == [build] + end + + it 'adds build to a branch if a branch aleady exists' do + branch = Factory(:branch, name: 'foo') + build = Factory(:build, repository: branch.repository) + + lambda { + build.add_branch('foo') + }.should_not change { Branch.count } + + build.branches.map(&:name).should include('foo') + branch.builds.should == [build] + end + + it 'does not add a branch if it is already added' do + build = Factory(:build) + + build.add_branch('foo') + build.add_branch('foo') + + build.branches.map(&:name).should include('foo') + build.branches.where(name: 'foo').first.builds.should == [build] + end + + it 'retries on ActiveRecord::RecordNotUnique' do + branch = Factory(:branch) + build = Factory(:build, repository: branch.repository) + + build.branches.stubs(:create!).raises(ActiveRecord::RecordNotUnique). + then.returns(branch) + + build.add_branch('master').should == branch + end + + it 'retries on ActiveRecord::RecordInvalid' do + branch = Factory(:branch) + build = Factory(:build, repository: branch.repository) + + build.branches.stubs(:create!).raises(ActiveRecord::RecordInvalid). + then.returns(branch) + + build.add_branch('master').should == branch + end + end + + describe '#add_tag' do + it 'adds tag to a build' do + build = Factory(:build) + + build.add_tag('foo') + + build.tags.map(&:name).should == ['foo'] + build.tags.where(name: 'foo').first.builds.should == [build] + end + + it 'adds build to a tag if a tag aleady exists' do + tag = Factory(:tag, name: 'foo') + build = Factory(:build, repository: tag.repository) + + lambda { + build.add_tag('foo') + }.should_not change { Tag.count } + + build.tags.map(&:name).should == ['foo'] + tag.builds.should == [build] + end + + it 'does not add a tag if it is already added' do + build = Factory(:build) + build.tags.should be_empty + + build.add_tag('foo') + build.add_tag('foo') + + build.tags.map(&:name).should == ['foo'] + build.tags.first.builds.should == [build] + end + + it 'retries on ActiveRecord::RecordNotUnique' do + tag = Factory(:tag, name: 'deploy.1') + build = Factory(:build, repository: tag.repository) + + build.tags.stubs(:create!).raises(ActiveRecord::RecordNotUnique). + then.returns(tag) + + build.add_tag('deploy.1').should == tag + end + + it 'retries on ActiveRecord::RecordInvalid' do + tag = Factory(:tag, name: 'deploy.1') + build = Factory(:build, repository: tag.repository) + + build.tags.stubs(:create!).raises(ActiveRecord::RecordInvalid). + then.returns(tag) + + build.add_tag('deploy.1').should == tag + end end describe 'reset' do diff --git a/spec/travis/model/commit_spec.rb b/spec/travis/model/commit_spec.rb index 6b37ff06e..f58e549ec 100644 --- a/spec/travis/model/commit_spec.rb +++ b/spec/travis/model/commit_spec.rb @@ -5,6 +5,24 @@ let(:commit) { Commit.new(:commit => '12345678') } + describe '#tag_name' do + describe 'with tag name available' do + before { commit.ref = 'refs/tags/foobar' } + + it 'returns tag name' do + commit.tag_name.should == 'foobar' + end + end + + describe 'with tag name unavailable' do + before { commit.ref = 'refs/heads/foobar' } + + it 'returns nil' do + commit.tag_name.should be_nil + end + end + end + describe 'pull_request_number' do context 'when commit is from pull request' do before { commit.ref = 'refs/pull/180/merge' } diff --git a/spec/travis/model/repository/status_image_spec.rb b/spec/travis/model/repository/status_image_spec.rb index 13865ed08..27da07ac4 100644 --- a/spec/travis/model/repository/status_image_spec.rb +++ b/spec/travis/model/repository/status_image_spec.rb @@ -65,21 +65,33 @@ describe 'given a branch' do it 'returns :passed if the last build on that branch has passed' do - build.update_attributes(state: :passed, branch: 'master') + build.update_attributes(state: :passed) + build.add_branch('master') + build.add_branch('staging') image = described_class.new(repo, 'master') image.result.should == :passing + image = described_class.new(repo, 'staging') + image.result.should == :passing end it 'returns :failed if the last build on that branch has failed' do - build.update_attributes(state: :failed, branch: 'develop') + build.update_attributes(state: :failed) + build.add_branch('develop') + build.add_branch('staging') image = described_class.new(repo, 'develop') image.result.should == :failing + image = described_class.new(repo, 'staging') + image.result.should == :failing end it 'returns :error if the last build on that branch has errored' do - build.update_attributes(state: :errored, branch: 'develop') + build.update_attributes(state: :errored) + build.add_branch('develop') + build.add_branch('staging') image = described_class.new(repo, 'develop') image.result.should == :error + image = described_class.new(repo, 'staging') + image.result.should == :error end end end diff --git a/spec/travis/model/repository_spec.rb b/spec/travis/model/repository_spec.rb index b9123c43d..d34132c59 100644 --- a/spec/travis/model/repository_spec.rb +++ b/spec/travis/model/repository_spec.rb @@ -3,14 +3,28 @@ describe Repository do include Support::ActiveRecord + def create_request(attrs = {}) + branch_name = attrs.fetch(:branch_name) + attrs.delete(:branch_name) + request = Factory.build(:request, attrs) + if branch_name + request.payload ||= {} + request.payload['ref'] = "refs/heads/#{branch_name}" + end + request.save! + request + end + describe '#last_completed_build' do let(:repo) { Factory(:repository, name: 'foobarbaz', builds: [build1, build2]) } let(:build1) { Factory(:build, finished_at: 1.hour.ago, state: :passed) } let(:build2) { Factory(:build, finished_at: Time.now, state: :failed) } before do - build1.update_attributes(branch: 'master') - build2.update_attributes(branch: 'development') + build1.branches.destroy_all + build1.add_branch('master') + build2.branches.destroy_all + build2.add_branch('development') end it 'returns last completed build' do @@ -203,18 +217,21 @@ end end - describe 'branches' do + describe 'branch_names' do let(:repo) { Factory(:repository) } it 'returns branches for the given repository' do %w(master production).each do |branch| - 2.times { Factory(:build, repository: repo, commit: Factory(:commit, branch: branch)) } + 2.times { + build = Factory(:build, repository: repo) + build.add_branch(branch) + } end - repo.branches.sort.should == %w(master production) + repo.branch_names.sort.should == %w(master production) end it 'is empty for empty repository' do - repo.branches.should eql [] + repo.branch_names.should eql [] end end @@ -257,8 +274,8 @@ it 'properly orders branches by last build' do Build.delete_all - one = Factory(:build, repository: repo, finished_at: 2.hours.ago, state: 'finished', commit: Factory(:commit, branch: '1one')) - two = Factory(:build, repository: repo, finished_at: 1.hours.ago, state: 'finished', commit: Factory(:commit, branch: '2two')) + one = Factory(:build, repository: repo, finished_at: 2.hours.ago, state: 'finished', request: create_request(branch_name: '1one')) + two = Factory(:build, repository: repo, finished_at: 1.hours.ago, state: 'finished', request: create_request(branch_name: '2two')) builds = repo.last_finished_builds_by_branches(1) builds.should == [two] @@ -266,12 +283,19 @@ it 'retrieves last builds on all branches' do Build.delete_all - old = Factory(:build, repository: repo, finished_at: 1.hour.ago, state: 'finished', commit: Factory(:commit, branch: 'one')) - one = Factory(:build, repository: repo, finished_at: 1.hour.from_now, state: 'finished', commit: Factory(:commit, branch: 'one')) - two = Factory(:build, repository: repo, finished_at: 1.hour.from_now, state: 'finished', commit: Factory(:commit, branch: 'two')) - three = Factory(:build, repository: repo, finished_at: 1.hour.from_now, state: 'finished', commit: Factory(:commit, branch: 'three')) + Branch.delete_all + old = Factory(:build, repository: repo, finished_at: 1.hour.ago, state: 'finished') + old.add_branch('one') + one = Factory(:build, repository: repo, finished_at: 1.hour.from_now, state: 'finished') + one.add_branch('one') + two = Factory(:build, repository: repo, finished_at: 1.hour.from_now, state: 'finished') + two.add_branch('two') + three = Factory(:build, repository: repo, finished_at: 1.hour.from_now, state: 'finished') + three.add_branch('three') three.update_attribute(:event_type, 'pull_request') + repo.branches.where(name: 'master').destroy_all + builds = repo.last_finished_builds_by_branches builds.size.should == 2 builds.should include(one) diff --git a/spec/travis/model/request/approval_spec.rb b/spec/travis/model/request/approval_spec.rb index 2e67870b1..5e1909337 100644 --- a/spec/travis/model/request/approval_spec.rb +++ b/spec/travis/model/request/approval_spec.rb @@ -32,12 +32,12 @@ describe 'branch_accepted?' do it 'does not accept a request that belongs to the github_pages branch' do - request.commit.stubs(:branch).returns('gh_pages') + request.stubs(:branch_name).returns('gh_pages') approval.branch_accepted?.should be_false end it 'accepts a request that belongs to the gh-pages branch if it\'s specified in branches:only' do - request.commit.stubs(:branch).returns('gh_pages') + request.stubs(:branch_name).returns('gh_pages') request.config['branches'] = { 'only' => ['gh-pages'] } approval.branch_accepted?.should be_true end @@ -74,13 +74,13 @@ end it 'accepts a request that belongs to the github_pages branch and is explicitly set to build that branch (String)' do - request.commit.stubs(:branch).returns('gh_pages') + request.stubs(:branch_name).returns('gh_pages') request.stubs(:config).returns('branches' => { 'only' => 'gh_pages' }) approval.should be_accepted end it 'accepts a request that belongs to the github_pages branch and is explicitly set to build that branch (Array)' do - request.commit.stubs(:branch).returns('gh_pages') + request.stubs(:branch_name).returns('gh_pages') request.stubs(:config).returns('branches' => { 'only' => ['gh_pages'] }) approval.should be_accepted end @@ -131,7 +131,7 @@ end it 'returns "github pages branch" if the branch is a github pages branch' do - request.commit.stubs(:branch).returns('gh-pages') + request.stubs(:branch_name).returns('gh-pages') approval.message.should == 'github pages branch' end @@ -141,7 +141,7 @@ end it 'returns "branch not included or excluded" if the branch was not approved' do - request.commit.stubs(:branch).returns('feature') + request.stubs(:branch_name).returns('feature') request.stubs(:config).returns('branches' => { 'only' => 'master' }) approval.message.should == 'branch not included or excluded' end @@ -156,23 +156,23 @@ describe 'github_pages?' do it 'returns true for a branch named gh-pages' do - request.commit.stubs(:branch).returns 'gh-pages' + request.stubs(:branch_name).returns 'gh-pages' approval.send(:github_pages?).should be_true end it 'returns true for a branch named gh_pages' do - request.commit.stubs(:branch).returns 'gh_pages' + request.stubs(:branch_name).returns 'gh_pages' approval.send(:github_pages?).should be_true end it 'returns true when a PR is for gh_pages' do request.commit.stubs(:ref).returns 'refs/pulls/1/merge' - request.commit.stubs(:branch).returns 'gh_pages' + request.stubs(:branch_name).returns 'gh_pages' approval.send(:github_pages?).should be_true end it 'returns false for a branch named master' do - commit.stubs(:branch).returns 'master' + request.stubs(:branch_name).returns 'master' approval.send(:github_pages?).should be_false end end diff --git a/spec/travis/requests/services/receive/pull_request_spec.rb b/spec/travis/requests/services/receive/pull_request_spec.rb index 9a2765f11..5f2ea5eb0 100644 --- a/spec/travis/requests/services/receive/pull_request_spec.rb +++ b/spec/travis/requests/services/receive/pull_request_spec.rb @@ -102,6 +102,12 @@ end end + describe '#branch_name' do + it 'returns branch name of a base' do + payload.branch_name.should == 'master' + end + end + describe 'commit' do it 'returns all attributes required for a Commit' do payload.commit.should == { diff --git a/spec/travis/requests/services/receive/push_spec.rb b/spec/travis/requests/services/receive/push_spec.rb index c61a91e87..b1a6d0b7d 100644 --- a/spec/travis/requests/services/receive/push_spec.rb +++ b/spec/travis/requests/services/receive/push_spec.rb @@ -51,17 +51,17 @@ payload.commit[:commit].should == '586374eac43853e5542a2e2faafd48047127e4be' end - it 'returns master when ref is ref/heads/master' do + it 'returns master when ref is refs/heads/master' do payload.commit[:branch].should == 'master' end - it 'returns travis when ref is ref/heads/travis' do - payload.event.data['ref'] = "ref/heads/travis" + it 'returns travis when ref is refs/heads/travis' do + payload.event.data['ref'] = "refs/heads/travis" payload.commit[:branch].should == 'travis' end - it 'returns features/travis-ci when ref is ref/heads/features/travis-ci' do - payload.event.data['ref'] = "ref/heads/features/travis-ci" + it 'returns features/travis-ci when ref is refs/heads/features/travis-ci' do + payload.event.data['ref'] = "refs/heads/features/travis-ci" payload.commit[:branch].should == 'features/travis-ci' end end diff --git a/spec/travis/requests/services/receive_spec.rb b/spec/travis/requests/services/receive_spec.rb index bd1b82206..b28cfab15 100644 --- a/spec/travis/requests/services/receive_spec.rb +++ b/spec/travis/requests/services/receive_spec.rb @@ -109,6 +109,20 @@ it_should_behave_like 'sets the owner for the request and repository to the expected type and login', type, login end + shared_examples_for 'adds a tag to a commit' do + it 'adds a tag to a commit' do + payload['ref'] = 'refs/tags/release-44' + request.commit.tags.map(&:name).should == ['release-44'] + end + end + + shared_examples_for 'adds a branch to a commit' do + it 'adds branch to a commit' do + payload['ref'] = 'refs/heads/development' + request.commit.branches.map(&:name).should == ['development'] + end + end + describe 'a github push event' do let(:params) { { :event_type => 'push', :payload => payload } } @@ -125,6 +139,40 @@ it_should_behave_like 'does not create a user' end + describe 'without existing commit' do + it 'creates a commit' do + expect { request }.to change(Commit, :count).by(1) + end + + it_should_behave_like 'adds a tag to a commit' + it_should_behave_like 'adds a branch to a commit' + end + + describe 'with an existing commit' do + it_should_behave_like 'adds a tag to a commit' + it_should_behave_like 'adds a branch to a commit' + + it 'reuses the existing commit' do + expect { request }.to change(Commit, :count).by(1) + + additional_request = nil + expect { + additional_request = described_class.new(nil, params).run + }.to_not change(Commit, :count) + additional_request.commit.should == request.commit + end + + it 'does not reuse existing commit if it belongs to the other repository' do + expect { request }.to change(Commit, :count).by(1) + + params[:payload]['repository']['id'] = params[:payload]['repository']['id'] + 1 + params[:payload]['repository']['name'] = 'new-repo' + expect { + described_class.new(nil, params).run + }.to change(Commit, :count).by(1) + end + end + describe 'if the user does not exist' do before(:each) { User.delete_all } it_should_behave_like 'a created request', type, login diff --git a/spec/travis/services/find_branch_spec.rb b/spec/travis/services/find_branch_spec.rb index 0394e0749..ca5d170d4 100644 --- a/spec/travis/services/find_branch_spec.rb +++ b/spec/travis/services/find_branch_spec.rb @@ -4,7 +4,7 @@ include Support::ActiveRecord let(:repo) { Factory(:repository, :owner_name => 'travis-ci', :name => 'travis-core') } - let!(:build) { Factory(:build, :repository => repo, :state => :finished) } + let!(:build) { Factory.create(:build, :repository => repo, :state => :finished) } let(:service) { described_class.new(stub('user'), params) } attr_reader :params