datadog-sdk-testing 0.4.9 → 0.5.0
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 +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
|