travis-rollout 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6e7b70d440a7f1c5df1b7cb5f8f5b9f7ebed5396
4
- data.tar.gz: 9f1ba41d70a47bd4d76083463b6cac812606fc1e
3
+ metadata.gz: 3a2ad2f94826f209638d29b84780e8cbafd49e0f
4
+ data.tar.gz: 6960f014a097950b813b30ba1ce789b394eca430
5
5
  SHA512:
6
- metadata.gz: 7e9ea82639e6dd032c22c86ec80ca9d333e256ad244b41c15d09edaecd14a7cf5eee6e591e593f77a105df4eb2bdfc1bdbdc2d5245bb1e639616650db8949cea
7
- data.tar.gz: ad70ac68701d6c5440660309fea0c3403a510fdc569cc8ca5364ba4e4c8ff6ce6d4bca11337504332738d859c1a193b909f7955a9c01631eb39234cccfcb64ad
6
+ metadata.gz: b810a7a3203d644250271eaffd687477cd1cdfe2b4312aef4cf0d0f6b155a256fdb68eaa1958c5d99c0df528addf9d7dd420760826bf950642a16a2abe6434a8
7
+ data.tar.gz: 6636bbe4fdfa9a650e61b01a4c1409a7095094ef682227d4c80b5f85cae6222cde7a1dec8607ef8e2cb56517bf9310963e116b49a93c23ae0a185217f07f7b30
data/README.md CHANGED
@@ -6,25 +6,38 @@ Enable by setting an env var `ROLLOUT` and setting `ENV` to `production` or
6
6
  ## Usage
7
7
 
8
8
  ```ruby
9
- args = { uid: 1, user: 'svenfuchs', repo: 'travis-hub' }
10
- Travis::Rollout.run(args) do
11
- # reroute the message
9
+ args = {
10
+ uid: 1
11
+ user: 'svenfuchs',
12
+ repo: 'travis-hub'
13
+ }
14
+
15
+ Rollout.run(:feature, args) do
16
+ # only runs when active
12
17
  end
13
18
  ```
14
19
 
15
20
  This will match:
16
21
 
17
- * uid against the percentage `ROLLOUT_PERCENT`
18
- * remaining arg values against the env vars `ROLLOUT_USERS` and `ROLLOUT_REPOS` (as comma separated values)
22
+ * uid against the percentage `ROLLOUT_FEATURE_PERCENT`
23
+ * remaining arg values against the env vars `ROLLOUT_FEATURE_USERS` and `ROLLOUT_FEATURE_REPOS` (as comma separated values)
19
24
 
20
- `uid` can be a string or integer. For a string it will calculate the crc32 to
21
- turn it into an integer.
25
+ For example:
26
+
27
+ ```
28
+ ROLLOUT=feature_foo
29
+ ROLLOUT_FEATURE_FOO_OWNERS=joecorcoran,svenfuchs
30
+ ROLLOUT_FEATURE_FOO_PERCENT=5
31
+ ```
32
+
33
+ The `uid` passed can be a string or integer. For a string it will calculate the
34
+ crc32 to turn it into an integer.
22
35
 
23
36
  If a redis instance is passed as an option it will additionally check redis:
24
37
 
25
38
  ```ruby
26
- Travis::Rollout.run(args, redis: redis) do
27
- # reroute the message
39
+ Travis::Rollout.run(feature, args.merge(redis: redis)) do
40
+ # only runs when active
28
41
  end
29
42
  ```
30
43
 
@@ -33,3 +46,5 @@ It will use the value of the env var `ROLLOUT` as a namespace (e.g. `sync`), and
33
46
  * enabling: `sync.rollout.enabled`
34
47
  * args: `sync.rollout.users`, `sync.rollout.repos`, `sync.rollout.owners`
35
48
  * percentage: `sync.rollout.percent`
49
+
50
+ Values stored in Redis will take precedence over values stored in the ENV.
@@ -2,111 +2,119 @@ require 'zlib'
2
2
 
3
3
  module Travis
4
4
  class Rollout
5
- ENVS = %w(production staging)
6
-
7
- def self.run(*all, &block)
8
- rollout = new(*all, &block)
9
- rollout.run if rollout.matches?
10
- end
11
-
12
- def self.matches?(*all)
13
- new(*all).matches?
14
- end
5
+ class Env < Struct.new(:name)
6
+ def enabled?
7
+ names.include?(name.to_s)
8
+ end
15
9
 
16
- attr_reader :args, :options, :block
10
+ def values(key)
11
+ ENV["ROLLOUT_#{name.to_s.upcase}_#{key.to_s.upcase}S"].to_s.split(',')
12
+ end
17
13
 
18
- def initialize(args = {}, options = {}, &block)
19
- @args, @options, @block = args, options, block
20
- end
14
+ def percent
15
+ ENV["ROLLOUT_#{name.to_s.upcase}_PERCENT"]
16
+ end
21
17
 
22
- def run
23
- block.call || true
18
+ def names
19
+ ENV['ROLLOUT'].to_s.split(',')
20
+ end
24
21
  end
25
22
 
26
- def matches?
27
- production? and enabled? and (by_owner? or by_repo? or by_user? or by_percent?)
23
+ class RedisNoop
24
+ def get(*); end
25
+ def smembers(*); [] end
28
26
  end
29
27
 
30
- private
31
-
32
- def production?
33
- ENVS.include?(ENV['ENV'])
34
- end
35
-
28
+ class Redis < Struct.new(:name, :redis)
36
29
  def enabled?
37
- !!ENV['ROLLOUT'] && (!redis || redis.get(:"#{name}.rollout.enabled") == '1')
30
+ redis.get(:"#{name}.rollout.enabled") == '1'
38
31
  end
39
32
 
40
- def by_owner?
41
- !!owner && owners.include?(owner)
33
+ def percent
34
+ redis.get(:"#{name}.rollout.percent")
42
35
  end
43
36
 
44
- def owner
45
- args[:owner]
37
+ def values(key)
38
+ redis.smembers(:"#{name}.rollout.#{key}s")
46
39
  end
47
40
 
48
- def owners
49
- read_collection('rollout', 'owners')
41
+ def redis
42
+ super || self.redis = RedisNoop.new
50
43
  end
44
+ end
51
45
 
52
- def by_repo?
53
- !!repo && repos.include?(repo)
46
+ class ByValue < Struct.new(:name, :key, :value, :env, :redis)
47
+ def matches?
48
+ !!value && values.include?(value)
54
49
  end
55
50
 
56
- def repo
57
- args[:repo]
51
+ def values
52
+ values = redis.values(key)
53
+ values = env.values(key) unless values.any?
54
+ values
58
55
  end
56
+ end
59
57
 
60
- def repos
61
- read_collection('rollout', 'repos')
58
+ class ByPercent < Struct.new(:name, :value, :env, :redis)
59
+ def matches?
60
+ !!value && value % 100 < percent
62
61
  end
63
62
 
64
- def by_user?
65
- !!user && users.include?(user)
63
+ def percent
64
+ percent = env.percent || redis.percent || -1
65
+ percent.to_i
66
66
  end
67
+ end
67
68
 
68
- def user
69
- args[:user]
70
- end
71
69
 
72
- def users
73
- read_collection('rollout', 'users')
74
- end
70
+ def self.run(*all, &block)
71
+ rollout = new(*all, &block)
72
+ rollout.run if rollout.matches?
73
+ end
75
74
 
76
- def by_percent?
77
- !!uid && uid % 100 < percent
78
- end
75
+ def self.matches?(*all)
76
+ new(*all).matches?
77
+ end
79
78
 
80
- def uid
81
- uid = args[:uid]
82
- uid.is_a?(String) ? Zlib.crc32(uid).to_i & 0x7fffffff : uid
83
- end
79
+ attr_reader :name, :args, :block, :redis, :env
84
80
 
85
- def percent
86
- percent = ENV['ROLLOUT_PERCENT'] || redis && redis.get(:"#{name}.rollout.percent") || -1
87
- percent.to_i
81
+ def initialize(name, args, &block)
82
+ @name = name
83
+ @args = args
84
+ @block = block
85
+ @redis = Redis.new(name, args.delete(:redis) )
86
+ @env = Env.new(name)
87
+ end
88
+
89
+ def run
90
+ block.call || true
91
+ end
92
+
93
+ def matches?
94
+ enabled? and (by_value? or by_percent?)
95
+ end
96
+
97
+ private
98
+
99
+ def enabled?
100
+ env.enabled? || redis.enabled?
88
101
  end
89
102
 
90
- def name
91
- ENV['ROLLOUT'].to_s.split('.').first
103
+ def by_value?
104
+ by_values.map(&:matches?).inject(&:|)
92
105
  end
93
106
 
94
- def redis
95
- options[:redis]
107
+ def by_values
108
+ args.map { |key, value| ByValue.new(name, key, value, env, redis) }
96
109
  end
97
110
 
98
- def camelize(string)
99
- string.to_s.sub(/./) { |char| char.upcase }
111
+ def by_percent?
112
+ ByPercent.new(name, uid, env, redis).matches?
100
113
  end
101
114
 
102
- def read_collection(*path)
103
- redis_path = path.map(&:downcase).join('.')
104
- env_key = path.map(&:upcase).join('_')
105
- if redis
106
- redis_collection = redis.smembers(:"#{name}.#{redis_path}")
107
- return redis_collection if redis_collection.any?
108
- end
109
- ENV.fetch(env_key, '').to_s.split(',')
115
+ def uid
116
+ uid = args[:uid]
117
+ uid.is_a?(String) ? Zlib.crc32(uid).to_i & 0x7fffffff : uid
110
118
  end
111
119
  end
112
120
  end
@@ -2,6 +2,6 @@ require 'travis/rollout'
2
2
 
3
3
  module Travis
4
4
  class Rollout
5
- VERSION = "0.0.1"
5
+ VERSION = "0.0.2"
6
6
  end
7
7
  end
@@ -3,16 +3,15 @@ require 'travis/rollout'
3
3
 
4
4
  describe Travis::Rollout do
5
5
  let(:redis) { Redis.new }
6
- let(:env) { %w(ENV ROLLOUT ROLLOUT_OWNERS ROLLOUT_REPOS ROLLOUT_USERS ROLLOUT_PERCENT) }
6
+ let(:env) { %w(ENV ROLLOUT ROLLOUT_#{name.upcase}_OWNERS ROLLOUT_#{name.upcase}_REPOS ROLLOUT_#{name.upcase}_USERS ROLLOUT_#{name.upcase}_PERCENT) }
7
7
 
8
- before { redis.set("#{name}.rollout.enabled", '1') }
9
8
  after { redis.flushall }
10
9
  after { env.each { |key| ENV.delete(key) } }
11
10
  subject { rollout.matches? }
12
11
 
13
12
  shared_examples_for 'matches by owner name' do
14
13
  context 'matches if the given owner name matches the OWNERS env var' do
15
- before { ENV['ROLLOUT_OWNERS'] = owner }
14
+ before { ENV["ROLLOUT_#{name.upcase}_OWNERS"] = owner }
16
15
  it { should eq true }
17
16
  end
18
17
 
@@ -24,7 +23,7 @@ describe Travis::Rollout do
24
23
 
25
24
  shared_examples_for 'does not match by owner name' do
26
25
  context 'does not match even if the given owner name matches the OWNERS env var' do
27
- before { ENV['ROLLOUT_OWNERS'] = owner }
26
+ before { ENV["ROLLOUT_#{name.upcase}_OWNERS"] = owner }
28
27
  it { should eq false }
29
28
  end
30
29
 
@@ -36,7 +35,7 @@ describe Travis::Rollout do
36
35
 
37
36
  shared_examples_for 'matches by repo slug' do
38
37
  context 'matches if the given repo slug matches the REPOS env var' do
39
- before { ENV['ROLLOUT_REPOS'] = repo }
38
+ before { ENV["ROLLOUT_#{name.upcase}_REPOS"] = repo }
40
39
  it { should eq true }
41
40
  end
42
41
 
@@ -48,7 +47,7 @@ describe Travis::Rollout do
48
47
 
49
48
  shared_examples_for 'does not match by repo slug' do
50
49
  context 'does not match even if the given repo slug matches the REPOS env var' do
51
- before { ENV['ROLLOUT_REPOS'] = repo }
50
+ before { ENV["ROLLOUT_#{name.upcase}_REPOS"] = repo }
52
51
  it { should eq false }
53
52
  end
54
53
 
@@ -60,7 +59,7 @@ describe Travis::Rollout do
60
59
 
61
60
  shared_examples_for 'matches by user name' do
62
61
  context 'matches if the given user name matches the REPOS env var' do
63
- before { ENV['ROLLOUT_USERS'] = user }
62
+ before { ENV["ROLLOUT_#{name.upcase}_USERS"] = user }
64
63
  it { should eq true }
65
64
  end
66
65
 
@@ -72,7 +71,7 @@ describe Travis::Rollout do
72
71
 
73
72
  shared_examples_for 'does not match by user name' do
74
73
  context 'does not match even if the given user slug matches the REPOS env var' do
75
- before { ENV['ROLLOUT_USERS'] = user }
74
+ before { ENV["ROLLOUT_#{name.upcase}_USERS"] = user }
76
75
  it { should eq false }
77
76
  end
78
77
 
@@ -84,7 +83,7 @@ describe Travis::Rollout do
84
83
 
85
84
  shared_examples_for 'matches by percentage' do
86
85
  context 'matches if the given id matches the ROLLOUT_PERCENT env var' do
87
- before { ENV['ROLLOUT_PERCENT'] = '100' }
86
+ before { ENV["ROLLOUT_#{name.upcase}_PERCENT"] = '100' }
88
87
  it { should eq true }
89
88
  end
90
89
 
@@ -96,7 +95,7 @@ describe Travis::Rollout do
96
95
 
97
96
  shared_examples_for 'does not match by percentage' do
98
97
  context 'does not match even if the given id matches the ROLLOUT_PERCENT env var' do
99
- before { ENV['ROLLOUT_PERCENT'] = '100' }
98
+ before { ENV["ROLLOUT_#{name.upcase}_PERCENT"] = '100' }
100
99
  it { should eq false }
101
100
  end
102
101
 
@@ -107,26 +106,23 @@ describe Travis::Rollout do
107
106
  end
108
107
 
109
108
  shared_examples_for 'matches by' do |type|
110
- context 'with ROLLOUT being set in production' do
111
- before { ENV['ENV'] = 'production' }
112
- before { ENV['ROLLOUT'] = name }
113
- include_examples "matches by #{type}"
109
+ context 'with ROLLOUT being set to another name' do
110
+ before { ENV['ROLLOUT'] = 'something_else' }
111
+ include_examples "does not match by #{type}"
114
112
  end
115
113
 
116
- context 'with ROLLOUT being set in staging' do
117
- before { ENV['ENV'] = 'production' }
118
- before { ENV['ROLLOUT'] = name }
114
+ context 'with ROLLOUT being set to another name, but [name].rollout.enabled being set in redis' do
115
+ before { ENV['ROLLOUT'] = 'something_else' }
116
+ before { redis.set("#{name}.rollout.enabled", '1') }
119
117
  include_examples "matches by #{type}"
120
118
  end
121
119
 
122
- context 'with ROLLOUT being set in development' do
123
- before { ENV['ENV'] = 'development' }
124
- before { ENV['ROLLOUT'] = name }
125
- include_examples "does not match by #{type}"
120
+ context 'with ROLLOUT being set' do
121
+ before { ENV['ROLLOUT'] = name }
122
+ include_examples "matches by #{type}"
126
123
  end
127
124
 
128
- context 'with ROLLOUT not set in production' do
129
- before { ENV['ENV'] = 'production' }
125
+ context 'with ROLLOUT not being set' do
130
126
  include_examples "does not match by #{type}"
131
127
  end
132
128
  end
@@ -137,7 +133,7 @@ describe Travis::Rollout do
137
133
  let(:id) { '517be336-f16d-45cf-aa9b-a429547af6ad' }
138
134
  let(:owner) { 'carlad' }
139
135
  let(:repo) { 'travis-ci/travis-hub' }
140
- let(:rollout) { described_class.new({ uid: id, owner: owner, repo: repo }, redis: redis) }
136
+ let(:rollout) { described_class.new(name, uid: id, owner: owner, repo: repo, redis: redis) }
141
137
 
142
138
  include_examples 'matches by', 'owner name'
143
139
  include_examples 'matches by', 'repo slug'
@@ -151,7 +147,7 @@ describe Travis::Rollout do
151
147
  describe 'in the user sync worker' do
152
148
  let(:id) { 1 }
153
149
  let(:user) { 'carlad' }
154
- let(:rollout) { described_class.new({ uid: id, user: user }, redis: redis) }
150
+ let(:rollout) { described_class.new(name, uid: id, user: user, redis: redis) }
155
151
 
156
152
  include_examples 'matches by', 'user name'
157
153
  include_examples 'matches by', 'percentage'
@@ -160,7 +156,7 @@ describe Travis::Rollout do
160
156
  describe 'in the repo branches sync worker' do
161
157
  let(:id) { 1 } # user id
162
158
  let(:owner) { 'carlad' }
163
- let(:rollout) { described_class.new({ uid: id, owner: owner }, redis: redis) }
159
+ let(:rollout) { described_class.new(name, uid: id, owner: owner, redis: redis) }
164
160
 
165
161
  include_examples 'matches by', 'owner name'
166
162
  include_examples 'matches by', 'percentage'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: travis-rollout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Travis CI
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-27 00:00:00.000000000 Z
11
+ date: 2017-07-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Small helper class for rolling out apps.
14
14
  email: contact@travis-ci.org
@@ -20,14 +20,13 @@ files:
20
20
  - Gemfile.lock
21
21
  - LICENSE
22
22
  - README.md
23
- - exercise.md
24
23
  - lib/travis/rollout.rb
25
24
  - lib/travis/rollout/version.rb
26
25
  - spec/rollout_spec.rb
27
26
  - spec/spec_helper.rb
28
- - travis-rollout.gemspec
29
27
  homepage: https://github.com/travis-ci/travis-rollout
30
- licenses: []
28
+ licenses:
29
+ - MIT
31
30
  metadata: {}
32
31
  post_install_message:
33
32
  rdoc_options: []
@@ -45,9 +44,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
45
44
  version: '0'
46
45
  requirements: []
47
46
  rubyforge_project:
48
- rubygems_version: 2.4.5
47
+ rubygems_version: 2.6.11
49
48
  signing_key:
50
49
  specification_version: 4
51
50
  summary: Small helper class for rolling out apps
52
51
  test_files: []
53
- has_rdoc:
@@ -1,25 +0,0 @@
1
- Refactor the class `Travis::Rollout` and remove the duplication of the methods
2
- `by_[owner|repo|user]?`, `[owner|repo|user]s` etc.
3
-
4
- Doing so also allow arbirary `args` hash to be passed:
5
-
6
- ```
7
- args = {
8
- uid: 1
9
- user: 'carlad',
10
- repo: 'travis-hub'
11
- foo: 'bar'
12
- }
13
-
14
- Rollout.reroute(args) do
15
- # reroute the message
16
- end
17
-
18
- ```
19
-
20
- This would:
21
-
22
- * Match the `uid` against the percentage.
23
- * Not match the `uid` against the env var `ROLLOUT_UID` (i.e. ignore this case)
24
- * Match the strings `carlad`, `travis-hub` and `bar` against the respective env
25
- vars `ROLLOUT_USERS`, `ROLLOUT_REPOS`, and `ROLLOUT_FOOS`
@@ -1,18 +0,0 @@
1
- # encoding: utf-8
2
-
3
- $:.unshift File.expand_path('../lib', __FILE__)
4
- require 'travis/rollout/version'
5
-
6
- Gem::Specification.new do |s|
7
- s.name = 'travis-rollout'
8
- s.version = Travis::Rollout::VERSION
9
- s.authors = ['Travis CI']
10
- s.email = 'contact@travis-ci.org'
11
- s.homepage = 'https://github.com/travis-ci/travis-rollout'
12
- s.summary = 'Small helper class for rolling out apps'
13
- s.description = "#{s.summary}."
14
-
15
- s.files = Dir['{lib/**/*,spec/**/*,[A-Z]*}']
16
- s.platform = Gem::Platform::RUBY
17
- s.require_paths = ['lib']
18
- end