travis-rollout 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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