datadog-sdk-testing 0.4.9 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/lib/config/CHANGELOG.md +8 -0
- data/lib/tasks/ci/common.rb +24 -1
- data/lib/tasks/ci/hooks/pre-commit.py +183 -49
- data/lib/tasks/sdk.rake +7 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e44e65983bc61091e09056f2a4c0915ef278bd26
|
4
|
+
data.tar.gz: ac179c9a1273e47e0224c21caa1de6762cd431fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d763f040338c25d0db5ad817055906dfff23f2103428b6ae322ef4b9b43829acadbab321fc29ef7dc3cb8f0a7494e6c171ee0efa9edf34795da45b7d83f13402
|
7
|
+
data.tar.gz: aacc3d99aeb93c3badb7564f10afbf47cd9352c7eb73fb2bdfa8d3541a30ce11c60c61fd1d5eeda1da8c86ba0d8bdba4ea96ce4bdf10540d91b32852d159ffd3
|
data/README.md
CHANGED
@@ -30,3 +30,9 @@ ENV['SDK_HOME'] = File.dirname(__FILE__)
|
|
30
30
|
spec = Gem::Specification.find_by_name 'datadog-sdk-testing'
|
31
31
|
load "#{spec.gem_dir}/lib/tasks/sdk.rake"
|
32
32
|
```
|
33
|
+
|
34
|
+
if you wish to enable the remote facilities for the requirement conflict checking [tool](https://github.com/DataDog/datadog-sdk-testing/blob/master/lib/tasks/ci/hooks/pre-commit.py) you will have to install [github3.py](https://github.com/sigmavirus24/github3.p://github.com/sigmavirus24/github3.py) (pre-release):
|
35
|
+
```
|
36
|
+
pip install --pre github3.py
|
37
|
+
```
|
38
|
+
By default this tool is designed to be used as a pre-commit hook and runs only on local repos. Due to the duration of the validation runs it's not recommended to enable remote repos when using as a pre-commit hook. We perform requirement validation in our continuous integration environment, so it's definitely not mandatory.
|
data/lib/tasks/ci/common.rb
CHANGED
@@ -175,6 +175,7 @@ def generate_skeleton(integration)
|
|
175
175
|
copy_skeleton('lib/config/metadata.csv', 'metadata.csv', integration)
|
176
176
|
copy_skeleton('lib/config/requirements.txt', 'requirements.txt', integration)
|
177
177
|
copy_skeleton('lib/config/README.md', 'README.md', integration)
|
178
|
+
copy_skeleton('lib/config/CHANGELOG.md', 'CHANGELOG.md', integration)
|
178
179
|
copy_skeleton('lib/config/conf.yaml.example', 'conf.yaml.example', integration)
|
179
180
|
end
|
180
181
|
|
@@ -260,6 +261,28 @@ class Wait
|
|
260
261
|
end
|
261
262
|
end
|
262
263
|
|
264
|
+
def travis_pr?
|
265
|
+
!ENV['TRAVIS'].nil? && ENV['TRAVIS_EVENT_TYPE'] == 'pull_request'
|
266
|
+
end
|
267
|
+
|
268
|
+
def can_skip?
|
269
|
+
return false, [] unless travis_pr?
|
270
|
+
|
271
|
+
modified_checks = []
|
272
|
+
puts "Comparing #{ENV['TRAVIS_PULL_REQUEST_SHA']} with #{ENV['TRAVIS_BRANCH']}"
|
273
|
+
git_output = `git diff --name-only #{ENV['TRAVIS_BRANCH']}...#{ENV['TRAVIS_PULL_REQUEST_SHA']}`
|
274
|
+
puts "Git diff: \n#{git_output}"
|
275
|
+
git_output.each_line do |filename|
|
276
|
+
filename.strip!
|
277
|
+
puts filename
|
278
|
+
return false, [] if filename.split('/').length < 2
|
279
|
+
|
280
|
+
check_name = filename.split('/')[0]
|
281
|
+
modified_checks << check_name unless modified_checks.include? check_name
|
282
|
+
end
|
283
|
+
[true, modified_checks]
|
284
|
+
end
|
285
|
+
|
263
286
|
namespace :ci do
|
264
287
|
namespace :common do
|
265
288
|
task :before_install do |t|
|
@@ -323,8 +346,8 @@ namespace :ci do
|
|
323
346
|
flavors = attr[:flavor]
|
324
347
|
sdkhome = ENV['SDK_HOME'] || Dir.pwd
|
325
348
|
filter = ENV['NOSE_FILTER'] || '1'
|
326
|
-
nose_command = in_venv ? 'venv/bin/nosetests' : 'nosetests'
|
327
349
|
|
350
|
+
nose_command = in_venv ? 'venv/bin/nosetests' : 'nosetests'
|
328
351
|
nose = if flavors.include?('default')
|
329
352
|
"(not requires) and #{filter}"
|
330
353
|
else
|
@@ -4,62 +4,196 @@ import fnmatch
|
|
4
4
|
import os
|
5
5
|
import sys
|
6
6
|
|
7
|
-
|
7
|
+
try:
|
8
|
+
import github3
|
9
|
+
except ImportError:
|
10
|
+
github3 = None
|
11
|
+
|
12
|
+
|
8
13
|
ERR = 1
|
9
14
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
+
ENV_TOKEN = 'GITHUB_TOKEN'
|
16
|
+
ENV_USER = 'GITHUB_USER'
|
17
|
+
ENV_PASS = 'GITHUB_PASS'
|
18
|
+
|
19
|
+
|
20
|
+
class BadGithubRepo(Exception):
|
21
|
+
pass
|
22
|
+
|
15
23
|
|
16
|
-
|
24
|
+
def two_fa():
|
25
|
+
code = ''
|
26
|
+
while not code:
|
27
|
+
code = raw_input('Enter Github 2FA Code: ')
|
17
28
|
|
29
|
+
return code
|
18
30
|
|
19
|
-
|
31
|
+
|
32
|
+
class RequirementsAnalyzer(object):
|
20
33
|
SPECIFIERS = ['==', '!=' '<=', '>=', '<', '>']
|
21
34
|
|
22
|
-
|
23
|
-
|
24
|
-
|
35
|
+
def __init__(self, remote, local, patterns=['requirements.txt'], verbose=False):
|
36
|
+
self.api = None
|
37
|
+
self.remote_sources = remote
|
38
|
+
self.local_sources = local
|
39
|
+
self.req_patterns = patterns
|
40
|
+
self.verbose = verbose
|
41
|
+
|
42
|
+
if github3:
|
43
|
+
if os.environ.get(ENV_TOKEN):
|
44
|
+
self.api = github3.login(
|
45
|
+
token=os.environ.get(ENV_TOKEN)
|
46
|
+
)
|
47
|
+
elif os.environ.get(ENV_USER) and os.environ.get(ENV_PASS):
|
48
|
+
self.api = github3.login(
|
49
|
+
os.environ.get(ENV_USER),
|
50
|
+
os.environ(ENV_PASS),
|
51
|
+
two_factor_callback=two_fa
|
52
|
+
)
|
53
|
+
|
54
|
+
def get_repo_requirements(self, repo):
|
55
|
+
reqs = {}
|
56
|
+
|
57
|
+
_repo = repo.split('/')
|
58
|
+
if len(_repo) is not 2:
|
59
|
+
raise BadGithubRepo
|
25
60
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
61
|
+
org = _repo[0]
|
62
|
+
repository = _repo[1]
|
63
|
+
gh_repo = self.api.repository(org, repository)
|
64
|
+
|
65
|
+
contents = gh_repo.directory_contents('/', return_as=dict)
|
66
|
+
files = {}
|
67
|
+
for entry, content in contents.iteritems():
|
68
|
+
if content.type != "dir":
|
69
|
+
files[content.name] = content
|
31
70
|
continue
|
32
71
|
|
33
|
-
req =
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
for
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
72
|
+
req = gh_repo.file_contents("/{}/requirements.txt".format(entry))
|
73
|
+
reqs[entry] = req.decoded
|
74
|
+
|
75
|
+
fmatches = []
|
76
|
+
for pattern in self.req_patterns:
|
77
|
+
fmatches.extend(fnmatch.filter(files.keys(), pattern))
|
78
|
+
|
79
|
+
for match in fmatches:
|
80
|
+
req = gh_repo.file_contents(files[match].path)
|
81
|
+
reqs[match] = req.decoded
|
82
|
+
|
83
|
+
return reqs
|
84
|
+
|
85
|
+
def get_local_files(self):
|
86
|
+
matches = []
|
87
|
+
for src in self.local_sources:
|
88
|
+
for pattern in self.req_patterns:
|
89
|
+
for root, dirnames, filenames in os.walk(src):
|
90
|
+
for filename in fnmatch.filter(filenames, pattern):
|
91
|
+
matches.append(os.path.join(root, filename))
|
92
|
+
|
93
|
+
return matches
|
94
|
+
|
95
|
+
def get_local_contents(self, files):
|
96
|
+
local_reqs = {}
|
97
|
+
for fname in files:
|
98
|
+
with open(fname) as f:
|
99
|
+
content = f.read()
|
100
|
+
|
101
|
+
local_reqs[fname] = content
|
102
|
+
|
103
|
+
return local_reqs
|
104
|
+
|
105
|
+
def get_all_requirements(self):
|
106
|
+
reqs = {}
|
107
|
+
if self.api:
|
108
|
+
for repo in self.remote_sources:
|
109
|
+
try:
|
110
|
+
requirements = self.get_repo_requirements(repo)
|
111
|
+
if requirements:
|
112
|
+
reqs[repo] = requirements
|
113
|
+
except BadGithubRepo:
|
114
|
+
print 'Unable to get repo requirements for {} - skipping.'.format(repo)
|
115
|
+
else:
|
116
|
+
print 'No Github API set (missing creds?) cant crawl remotes.'
|
117
|
+
|
118
|
+
for local in self.local_sources:
|
119
|
+
requirements = self.get_local_contents(self.get_local_files())
|
120
|
+
reqs[local] = requirements
|
121
|
+
|
122
|
+
return reqs
|
123
|
+
|
124
|
+
def process_requirements(self, sources):
|
125
|
+
err = 0
|
126
|
+
reqs = {}
|
127
|
+
|
128
|
+
for source, requirements in sources.iteritems():
|
129
|
+
for integration, content in requirements.iteritems():
|
130
|
+
if self.verbose:
|
131
|
+
print "processing... {}/{}".format(source, integration)
|
132
|
+
mycontent = content.splitlines()
|
133
|
+
|
134
|
+
for line in mycontent:
|
135
|
+
line = "".join(line.split())
|
136
|
+
for specifier in self.SPECIFIERS:
|
137
|
+
idx = line.find(specifier)
|
138
|
+
if idx < 0:
|
139
|
+
continue
|
140
|
+
|
141
|
+
req = line[:idx]
|
142
|
+
specifier = line[idx:]
|
143
|
+
|
144
|
+
if req in reqs and reqs[req][0] != specifier:
|
145
|
+
# version mismatch
|
146
|
+
print "There's a version mismatch with {req} " \
|
147
|
+
" {spec} and {prev_spec} defined in {src} " \
|
148
|
+
"@ {repo}.".format(
|
149
|
+
req=req,
|
150
|
+
spec=specifier,
|
151
|
+
prev_spec=reqs[req][0],
|
152
|
+
src=reqs[req][1],
|
153
|
+
repo=reqs[req][2]
|
154
|
+
)
|
155
|
+
err = ERR
|
156
|
+
break
|
157
|
+
elif req not in reqs:
|
158
|
+
reqs[req] = (specifier, integration, source)
|
159
|
+
break
|
160
|
+
|
161
|
+
return err, reqs
|
162
|
+
|
163
|
+
|
164
|
+
def str2bool(v):
|
165
|
+
return v.lower() in ("yes", "true", "t", "1")
|
166
|
+
|
167
|
+
def main(args):
|
168
|
+
remote = local = []
|
169
|
+
verbose = str2bool(os.environ.get('VERBOSE', 'false'))
|
170
|
+
if not len(args):
|
171
|
+
remote = [repo.strip() for repo in os.environ.get('REQ_REMOTES', '').split(',')]
|
172
|
+
local = [repo.strip() for repo in os.environ.get('REQ_LOCALS', '').split(',')]
|
173
|
+
elif len(args) == 1:
|
174
|
+
local = [repo.strip() for repo in args[0].split(',')]
|
175
|
+
elif len(args) == 2:
|
176
|
+
local = [repo.strip() for repo in args[0].split(',')]
|
177
|
+
remote = [repo.strip() for repo in args[1].split(',')]
|
178
|
+
else:
|
179
|
+
local = ['.']
|
180
|
+
|
181
|
+
analyzer = RequirementsAnalyzer(
|
182
|
+
remote=remote, local=local, patterns=['requirements*.txt'], verbose=verbose)
|
183
|
+
|
184
|
+
err, reqs = analyzer.process_requirements(analyzer.get_all_requirements())
|
185
|
+
if not err:
|
186
|
+
print "No requirement version conflicts found. Looking good... ;)"
|
187
|
+
if verbose:
|
188
|
+
for requirement, spec in reqs.iteritems():
|
189
|
+
print "{req}{spec} first found in {fname} @ {source}".format(
|
190
|
+
req=requirement,
|
191
|
+
spec=spec[0],
|
192
|
+
fname=spec[1],
|
193
|
+
source=spec[2]
|
194
|
+
)
|
195
|
+
|
196
|
+
sys.exit(err)
|
197
|
+
|
198
|
+
if __name__ == "__main__":
|
199
|
+
main(sys.argv[1:])
|
data/lib/tasks/sdk.rake
CHANGED
@@ -147,8 +147,15 @@ namespace :ci do
|
|
147
147
|
check_env
|
148
148
|
puts 'Assuming you are running these tests locally' unless ENV['TRAVIS']
|
149
149
|
flavor = args[:flavor] || ENV['TRAVIS_FLAVOR'] || 'default'
|
150
|
+
can_skip, checks = can_skip?
|
151
|
+
can_skip &&= !%w(default).include?(flavor)
|
152
|
+
|
150
153
|
flavors = flavor.split(',')
|
151
154
|
flavors.each do |f|
|
155
|
+
if can_skip && !checks.include?(flavor)
|
156
|
+
puts "skipping #{flavor} tests, not affected by the change".yellow
|
157
|
+
next
|
158
|
+
end
|
152
159
|
Rake::Task["ci:#{f}:execute"].invoke
|
153
160
|
end
|
154
161
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: datadog-sdk-testing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jaime Fullaondo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Datadog Integration SDK testing/scaffolding gem
|
14
14
|
email: jaime.fullaondo@datadoghq.com
|
@@ -18,6 +18,7 @@ extra_rdoc_files: []
|
|
18
18
|
files:
|
19
19
|
- LICENSE
|
20
20
|
- README.md
|
21
|
+
- lib/config/CHANGELOG.md
|
21
22
|
- lib/config/README.md
|
22
23
|
- lib/config/check.py
|
23
24
|
- lib/config/ci/skeleton.rake
|