From 18b604429a44d55a475c5c1f48f8cd9ceb285eb5 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 30 Dec 2013 14:12:05 +0100 Subject: [PATCH 1/6] Add models and tables for branches and tags --- Gemfile | 1 + Rakefile | 5 + db/migrate/20131227155535_create_branches.rb | 16 + .../20131227160256_create_builds_branches.rb | 16 + db/migrate/20131230093907_create_tags.rb | 16 + .../20131230094201_create_builds_tags.rb | 16 + db/structure.sql | 495 +++++++++++++++++- lib/travis/model.rb | 4 + lib/travis/model/branch.rb | 9 + lib/travis/model/build.rb | 4 + lib/travis/model/builds_branch.rb | 6 + lib/travis/model/builds_tag.rb | 6 + lib/travis/model/repository.rb | 2 + lib/travis/model/tag.rb | 9 + lib/travis/testing/factories.rb | 10 + 15 files changed, 614 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20131227155535_create_branches.rb create mode 100644 db/migrate/20131227160256_create_builds_branches.rb create mode 100644 db/migrate/20131230093907_create_tags.rb create mode 100644 db/migrate/20131230094201_create_builds_tags.rb create mode 100644 lib/travis/model/branch.rb create mode 100644 lib/travis/model/builds_branch.rb create mode 100644 lib/travis/model/builds_tag.rb create mode 100644 lib/travis/model/tag.rb 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/structure.sql b/db/structure.sql index 9deaa8e2b..5939129f2 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,39 @@ 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 +); + + +-- +-- 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 +297,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 +348,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 +402,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 +454,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 +877,39 @@ 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 +); + + +-- +-- 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 +1029,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 +1043,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 +1064,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 +1141,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 +1185,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 +1201,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 +1217,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 +1241,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 +1321,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 +1361,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 +1431,41 @@ 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_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 +1613,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 +1648,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 +1752,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 +2026,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'); diff --git a/lib/travis/model.rb b/lib/travis/model.rb index 7ff7678b3..bdf5af817 100644 --- a/lib/travis/model.rb +++ b/lib/travis/model.rb @@ -10,7 +10,10 @@ 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/email' require 'travis/model/env_helpers' @@ -23,6 +26,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..a1c4c48da 100644 --- a/lib/travis/model/build.rb +++ b/lib/travis/model/build.rb @@ -54,6 +54,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 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/repository.rb b/lib/travis/model/repository.rb index 0245afb27..ac02a2845 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' 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/testing/factories.rb b/lib/travis/testing/factories.rb index 73df23909..a85a620a5 100644 --- a/lib/travis/testing/factories.rb +++ b/lib/travis/testing/factories.rb @@ -1,6 +1,16 @@ 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) } From 827450bfe962fa6a8cced7cff0ce01668f5d12be Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 30 Dec 2013 15:59:16 +0100 Subject: [PATCH 2/6] Properly save branch and tag info This commits adds proper saving of tag and branch names after build creation. The information is not used anywhere for now and thus commit.branch and build.branch is still populated for backwards compatibility. --- lib/travis/model/build.rb | 43 ++++++ lib/travis/model/commit.rb | 6 + lib/travis/model/repository.rb | 2 +- spec/travis/model/build_spec.rb | 129 +++++++++++++++++- spec/travis/model/commit_spec.rb | 18 +++ spec/travis/model/repository_spec.rb | 6 +- .../requests/services/receive/push_spec.rb | 10 +- 7 files changed, 204 insertions(+), 10 deletions(-) diff --git a/lib/travis/model/build.rb b/lib/travis/model/build.rb index a1c4c48da..ad92789eb 100644 --- a/lib/travis/model/build.rb +++ b/lib/travis/model/build.rb @@ -158,6 +158,15 @@ def per_page expand_matrix end + after_create do + if ActiveRecord::Base.connection.table_exists? 'branches' + add_branch(commit.branch) if commit.branch && !commit.tag_name + end + if ActiveRecord::Base.connection.table_exists? 'tags' + add_tag(commit.tag_name) if commit.tag_name + end + end + after_save do unless cached_matrix_ids update_column(:cached_matrix_ids, to_postgres_array(matrix_ids)) @@ -207,6 +216,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? @@ -221,4 +254,14 @@ def to_postgres_array(ids) ids = ids.compact.uniq "{#{ids.map { |id| id.to_i.to_s }.join(',')}}" unless ids.empty? end + + def retry_on(*errors) + times = 0 + begin + yield + rescue *errors + times += 1 + times < 3 ? retry : raise + end + end end diff --git a/lib/travis/model/commit.rb b/lib/travis/model/commit.rb index 7cf9a61f3..2a416d6cb 100644 --- a/lib/travis/model/commit.rb +++ b/lib/travis/model/commit.rb @@ -8,6 +8,12 @@ class Commit < Travis::Model validates :commit, :branch, :committed_at, :presence => true + def tag_name + if ref + ref.scan(%r{refs/tags/(.*?)$}).flatten.first + end + end + def pull_request? ref =~ %r(^refs/pull/\d+/merge$) end diff --git a/lib/travis/model/repository.rb b/lib/travis/model/repository.rb index ac02a2845..5ecf01570 100644 --- a/lib/travis/model/repository.rb +++ b/lib/travis/model/repository.rb @@ -116,7 +116,7 @@ def source_url private? ? "git@github.com:#{slug}.git": "git://github.com/#{slug}.git" end - def branches + def branch_names self.class.connection.select_values %( SELECT DISTINCT ON (branch) branch FROM builds diff --git a/spec/travis/model/build_spec.rb b/spec/travis/model/build_spec.rb index 865df0cc5..446fa169e 100644 --- a/spec/travis/model/build_spec.rb +++ b/spec/travis/model/build_spec.rb @@ -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 @@ -201,6 +201,26 @@ Factory(:build).reload.previous_state.should == 'failed' end end + + it 'adds branch' do + build = Factory(:build, commit: Factory(:commit, branch: 'something')) + build2 = Factory(:build, commit: Factory(:commit, branch: 'something'), repository: build.repository) + + build.branches.map(&:name).should == ['something'] + branch = Branch.where(name: 'something', repository_id: build.repository_id).first + branch.builds.should == [build, build2] + end + + it 'adds tag' do + build = Factory(:build, commit: Factory(:commit, ref: 'refs/tags/something')) + build2 = Factory(:build, commit: Factory(:commit, 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 @@ -292,6 +312,113 @@ build.branch.should == 'development' end + 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 let(:build) { Factory(:build, state: 'finished') } 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_spec.rb b/spec/travis/model/repository_spec.rb index b9123c43d..13dd1191a 100644 --- a/spec/travis/model/repository_spec.rb +++ b/spec/travis/model/repository_spec.rb @@ -203,18 +203,18 @@ 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)) } 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 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 From b9eb61f96b742f681781f50f71610e08b441a9ef Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 10 Apr 2014 15:40:21 +0200 Subject: [PATCH 3/6] Don't create more than one commit for the same sha --- .../20140116125536_create_commits_tags.rb | 17 +++++++ .../20140116125602_create_commits_branches.rb | 17 +++++++ lib/travis/model.rb | 2 + lib/travis/model/commit.rb | 45 ++++++++++++++++- lib/travis/requests/services/receive.rb | 12 ++++- .../requests/services/receive/pull_request.rb | 8 ++++ lib/travis/requests/services/receive/push.rb | 8 ++++ spec/travis/requests/services/receive_spec.rb | 48 +++++++++++++++++++ 8 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20140116125536_create_commits_tags.rb create mode 100644 db/migrate/20140116125602_create_commits_branches.rb 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/lib/travis/model.rb b/lib/travis/model.rb index bdf5af817..77a809d90 100644 --- a/lib/travis/model.rb +++ b/lib/travis/model.rb @@ -15,6 +15,8 @@ class Model < ActiveRecord::Base 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' diff --git a/lib/travis/model/commit.rb b/lib/travis/model/commit.rb index 2a416d6cb..20adb4dd0 100644 --- a/lib/travis/model/commit.rb +++ b/lib/travis/model/commit.rb @@ -6,9 +6,22 @@ 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") + super + end def tag_name + ActiveSupport::Deprecation.warn("Commit#tag_name should not be used as commit can contain multiple tags") if ref ref.scan(%r{refs/tags/(.*?)$}).flatten.first end @@ -29,4 +42,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/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..d8fa02f33 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 + nil + 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/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 From fcf35c8353218ee0c604a89e7a9093e5e6566f7a Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 14 Apr 2014 10:52:11 +0200 Subject: [PATCH 4/6] Use Travis::RetryOn --- lib/travis/model/build.rb | 13 ++----------- lib/travis/model/commit.rb | 1 + 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/travis/model/build.rb b/lib/travis/model/build.rb index ad92789eb..704ff3d4e 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 @@ -254,14 +255,4 @@ def to_postgres_array(ids) ids = ids.compact.uniq "{#{ids.map { |id| id.to_i.to_s }.join(',')}}" unless ids.empty? end - - def retry_on(*errors) - times = 0 - begin - yield - rescue *errors - times += 1 - times < 3 ? retry : raise - end - end end diff --git a/lib/travis/model/commit.rb b/lib/travis/model/commit.rb index 20adb4dd0..d1c1f4e1e 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). From 0295df6b30df6e29cb9ed8e1b47e8851273161c8 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 14 Apr 2014 11:01:28 +0200 Subject: [PATCH 5/6] Add caller to deprecation warnings Without caller, deprecation warning will not properly report the place where deprecation happend. --- lib/travis/model/commit.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/travis/model/commit.rb b/lib/travis/model/commit.rb index d1c1f4e1e..3277d0f39 100644 --- a/lib/travis/model/commit.rb +++ b/lib/travis/model/commit.rb @@ -17,12 +17,12 @@ class Commit < Travis::Model include Travis::RetryOn def branch - ActiveSupport::Deprecation.warn("Commit#branch should not be used as commit can contain multiple branches") + 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") + 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 From a0f62b7c6e897eccb09c9974a2caa25da08e3fd6 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 14 Apr 2014 19:07:09 +0200 Subject: [PATCH 6/6] WIP --- db/structure.sql | 25 +++++++++-- lib/travis/model/build.rb | 10 ++--- lib/travis/model/build/denormalize.rb | 10 ++++- lib/travis/model/repository.rb | 26 +++++------- lib/travis/model/repository/status_image.rb | 6 ++- lib/travis/model/request/approval.rb | 4 +- .../requests/services/receive/pull_request.rb | 2 +- lib/travis/testing/factories.rb | 3 +- spec/travis/model/build_spec.rb | 33 +++++++-------- .../model/repository/status_image_spec.rb | 18 ++++++-- spec/travis/model/repository_spec.rb | 42 +++++++++++++++---- spec/travis/model/request/approval_spec.rb | 20 ++++----- .../services/receive/pull_request_spec.rb | 6 +++ spec/travis/services/find_branch_spec.rb | 2 +- 14 files changed, 135 insertions(+), 72 deletions(-) diff --git a/db/structure.sql b/db/structure.sql index 5939129f2..d7ad3d2ed 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -198,7 +198,12 @@ CREATE TABLE branches ( 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 + 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 ); @@ -887,7 +892,12 @@ CREATE TABLE tags ( 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 + 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 ); @@ -1459,6 +1469,13 @@ CREATE INDEX index_builds_tags_on_tag_id ON builds_tags USING btree (tag_id); 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: -- @@ -2046,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/build.rb b/lib/travis/model/build.rb index 704ff3d4e..9bb7b3506 100644 --- a/lib/travis/model/build.rb +++ b/lib/travis/model/build.rb @@ -84,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) @@ -155,16 +155,16 @@ 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(commit.branch) if commit.branch && !commit.tag_name + add_branch(request.branch_name) if request.branch_name && !request.tag_name end if ActiveRecord::Base.connection.table_exists? 'tags' - add_tag(commit.tag_name) if commit.tag_name + add_tag(request.tag_name) if request.tag_name end end @@ -248,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/repository.rb b/lib/travis/model/repository.rb index 5ecf01570..06acf6fb5 100644 --- a/lib/travis/model/repository.rb +++ b/lib/travis/model/repository.rb @@ -117,13 +117,7 @@ def source_url end def branch_names - self.class.connection.select_values %( - SELECT DISTINCT ON (branch) branch - FROM builds - WHERE builds.repository_id = #{id} - ORDER BY branch DESC - LIMIT 25 - ) + branches.map(&:name) end def last_completed_build(branch = nil) @@ -139,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/requests/services/receive/pull_request.rb b/lib/travis/requests/services/receive/pull_request.rb index d8fa02f33..8210debb9 100644 --- a/lib/travis/requests/services/receive/pull_request.rb +++ b/lib/travis/requests/services/receive/pull_request.rb @@ -114,7 +114,7 @@ def repo_owner end def branch_name - nil + pull_request['base']['ref'] end def tag_name diff --git a/lib/travis/testing/factories.rb b/lib/travis/testing/factories.rb index a85a620a5..08a2d6203 100644 --- a/lib/travis/testing/factories.rb +++ b/lib/travis/testing/factories.rb @@ -14,7 +14,7 @@ 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 } @@ -24,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 446fa169e..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 @@ -196,24 +196,24 @@ 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, commit: Factory(:commit, branch: 'something')) - build2 = Factory(:build, commit: Factory(:commit, branch: 'something'), repository: build.repository) + 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.should == [build, build2] + branch.builds.order('id ASC').should == [build, build2] end it 'adds tag' do - build = Factory(:build, commit: Factory(:commit, ref: 'refs/tags/something')) - build2 = Factory(:build, commit: Factory(:commit, ref: 'refs/tags/something'), repository: build.repository) + 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 @@ -307,11 +307,6 @@ 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' - end - describe '#add_branch' do it 'adds branch to a build' do build = Factory(:build) 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 13dd1191a..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 @@ -208,7 +222,10 @@ 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.branch_names.sort.should == %w(master production) 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/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