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 +4 -4
- data/README.md +24 -9
- data/lib/travis/rollout.rb +77 -69
- data/lib/travis/rollout/version.rb +1 -1
- data/spec/rollout_spec.rb +22 -26
- metadata +5 -7
- data/exercise.md +0 -25
- data/travis-rollout.gemspec +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a2ad2f94826f209638d29b84780e8cbafd49e0f
|
4
|
+
data.tar.gz: 6960f014a097950b813b30ba1ce789b394eca430
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 = {
|
10
|
-
|
11
|
-
|
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 `
|
18
|
-
* remaining arg values against the env vars `
|
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
|
-
|
21
|
-
|
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(
|
27
|
-
#
|
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.
|
data/lib/travis/rollout.rb
CHANGED
@@ -2,111 +2,119 @@ require 'zlib'
|
|
2
2
|
|
3
3
|
module Travis
|
4
4
|
class Rollout
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
10
|
+
def values(key)
|
11
|
+
ENV["ROLLOUT_#{name.to_s.upcase}_#{key.to_s.upcase}S"].to_s.split(',')
|
12
|
+
end
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
def percent
|
15
|
+
ENV["ROLLOUT_#{name.to_s.upcase}_PERCENT"]
|
16
|
+
end
|
21
17
|
|
22
|
-
|
23
|
-
|
18
|
+
def names
|
19
|
+
ENV['ROLLOUT'].to_s.split(',')
|
20
|
+
end
|
24
21
|
end
|
25
22
|
|
26
|
-
|
27
|
-
|
23
|
+
class RedisNoop
|
24
|
+
def get(*); end
|
25
|
+
def smembers(*); [] end
|
28
26
|
end
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
def production?
|
33
|
-
ENVS.include?(ENV['ENV'])
|
34
|
-
end
|
35
|
-
|
28
|
+
class Redis < Struct.new(:name, :redis)
|
36
29
|
def enabled?
|
37
|
-
|
30
|
+
redis.get(:"#{name}.rollout.enabled") == '1'
|
38
31
|
end
|
39
32
|
|
40
|
-
def
|
41
|
-
|
33
|
+
def percent
|
34
|
+
redis.get(:"#{name}.rollout.percent")
|
42
35
|
end
|
43
36
|
|
44
|
-
def
|
45
|
-
|
37
|
+
def values(key)
|
38
|
+
redis.smembers(:"#{name}.rollout.#{key}s")
|
46
39
|
end
|
47
40
|
|
48
|
-
def
|
49
|
-
|
41
|
+
def redis
|
42
|
+
super || self.redis = RedisNoop.new
|
50
43
|
end
|
44
|
+
end
|
51
45
|
|
52
|
-
|
53
|
-
|
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
|
57
|
-
|
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
|
-
|
61
|
-
|
58
|
+
class ByPercent < Struct.new(:name, :value, :env, :redis)
|
59
|
+
def matches?
|
60
|
+
!!value && value % 100 < percent
|
62
61
|
end
|
63
62
|
|
64
|
-
def
|
65
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
70
|
+
def self.run(*all, &block)
|
71
|
+
rollout = new(*all, &block)
|
72
|
+
rollout.run if rollout.matches?
|
73
|
+
end
|
75
74
|
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
def self.matches?(*all)
|
76
|
+
new(*all).matches?
|
77
|
+
end
|
79
78
|
|
80
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
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
|
91
|
-
|
103
|
+
def by_value?
|
104
|
+
by_values.map(&:matches?).inject(&:|)
|
92
105
|
end
|
93
106
|
|
94
|
-
def
|
95
|
-
|
107
|
+
def by_values
|
108
|
+
args.map { |key, value| ByValue.new(name, key, value, env, redis) }
|
96
109
|
end
|
97
110
|
|
98
|
-
def
|
99
|
-
|
111
|
+
def by_percent?
|
112
|
+
ByPercent.new(name, uid, env, redis).matches?
|
100
113
|
end
|
101
114
|
|
102
|
-
def
|
103
|
-
|
104
|
-
|
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
|
data/spec/rollout_spec.rb
CHANGED
@@ -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
|
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[
|
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[
|
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[
|
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[
|
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[
|
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[
|
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[
|
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[
|
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
|
111
|
-
before
|
112
|
-
|
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
|
117
|
-
before
|
118
|
-
before
|
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
|
123
|
-
before
|
124
|
-
|
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
|
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(
|
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(
|
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(
|
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.
|
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:
|
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.
|
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:
|
data/exercise.md
DELETED
@@ -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`
|
data/travis-rollout.gemspec
DELETED
@@ -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
|