checkmate5 4.0.67__py3-none-any.whl

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.
Files changed (116) hide show
  1. checkmate/__init__.py +21 -0
  2. checkmate/__main__.py +25 -0
  3. checkmate/contrib/__init__.py +21 -0
  4. checkmate/contrib/plugins/__init__.py +0 -0
  5. checkmate/contrib/plugins/all/gptanalyzer/__init__.py +0 -0
  6. checkmate/contrib/plugins/all/gptanalyzer/analyzer.py +99 -0
  7. checkmate/contrib/plugins/all/gptanalyzer/issues_data.py +6 -0
  8. checkmate/contrib/plugins/all/gptanalyzer/setup.py +13 -0
  9. checkmate/contrib/plugins/cve/__init__.py +0 -0
  10. checkmate/contrib/plugins/cve/text4shell/__init__.py +0 -0
  11. checkmate/contrib/plugins/cve/text4shell/analyzer.py +64 -0
  12. checkmate/contrib/plugins/cve/text4shell/issues_data.py +8 -0
  13. checkmate/contrib/plugins/cve/text4shell/setup.py +13 -0
  14. checkmate/contrib/plugins/git/__init__.py +0 -0
  15. checkmate/contrib/plugins/git/commands/__init__.py +6 -0
  16. checkmate/contrib/plugins/git/commands/analyze.py +364 -0
  17. checkmate/contrib/plugins/git/commands/base.py +16 -0
  18. checkmate/contrib/plugins/git/commands/diff.py +199 -0
  19. checkmate/contrib/plugins/git/commands/init.py +59 -0
  20. checkmate/contrib/plugins/git/commands/update_stats.py +41 -0
  21. checkmate/contrib/plugins/git/hooks/__init__.py +0 -0
  22. checkmate/contrib/plugins/git/hooks/project.py +19 -0
  23. checkmate/contrib/plugins/git/lib/__init__.py +1 -0
  24. checkmate/contrib/plugins/git/lib/repository.py +557 -0
  25. checkmate/contrib/plugins/git/lib/repository_pygit2.py +531 -0
  26. checkmate/contrib/plugins/git/models.py +178 -0
  27. checkmate/contrib/plugins/git/setup.py +27 -0
  28. checkmate/contrib/plugins/golang/__init__.py +0 -0
  29. checkmate/contrib/plugins/golang/gostaticcheck/__init__.py +0 -0
  30. checkmate/contrib/plugins/golang/gostaticcheck/analyzer.py +94 -0
  31. checkmate/contrib/plugins/golang/gostaticcheck/issues_data.py +1246 -0
  32. checkmate/contrib/plugins/golang/gostaticcheck/setup.py +13 -0
  33. checkmate/contrib/plugins/iac/__init__.py +0 -0
  34. checkmate/contrib/plugins/iac/kubescape/__init__.py +0 -0
  35. checkmate/contrib/plugins/iac/kubescape/analyzer.py +115 -0
  36. checkmate/contrib/plugins/iac/kubescape/issues_data.py +636 -0
  37. checkmate/contrib/plugins/iac/kubescape/setup.py +14 -0
  38. checkmate/contrib/plugins/iac/tfsec/__init__.py +0 -0
  39. checkmate/contrib/plugins/iac/tfsec/analyzer.py +92 -0
  40. checkmate/contrib/plugins/iac/tfsec/issues_data.py +1917 -0
  41. checkmate/contrib/plugins/iac/tfsec/setup.py +13 -0
  42. checkmate/contrib/plugins/java/__init__.py +0 -0
  43. checkmate/contrib/plugins/java/semgrepjava/__init__.py +0 -0
  44. checkmate/contrib/plugins/java/semgrepjava/analyzer.py +96 -0
  45. checkmate/contrib/plugins/java/semgrepjava/issues_data.py +5 -0
  46. checkmate/contrib/plugins/java/semgrepjava/setup.py +13 -0
  47. checkmate/contrib/plugins/javascript/__init__.py +0 -0
  48. checkmate/contrib/plugins/javascript/semgrepeslint/__init__.py +0 -0
  49. checkmate/contrib/plugins/javascript/semgrepeslint/analyzer.py +95 -0
  50. checkmate/contrib/plugins/javascript/semgrepeslint/issues_data.py +6 -0
  51. checkmate/contrib/plugins/javascript/semgrepeslint/setup.py +13 -0
  52. checkmate/contrib/plugins/perl/__init__.py +0 -0
  53. checkmate/contrib/plugins/perl/graudit/__init__.py +0 -0
  54. checkmate/contrib/plugins/perl/graudit/analyzer.py +70 -0
  55. checkmate/contrib/plugins/perl/graudit/issues_data.py +8 -0
  56. checkmate/contrib/plugins/perl/graudit/setup.py +13 -0
  57. checkmate/contrib/plugins/python/__init__.py +0 -0
  58. checkmate/contrib/plugins/python/bandit/__init__.py +0 -0
  59. checkmate/contrib/plugins/python/bandit/analyzer.py +74 -0
  60. checkmate/contrib/plugins/python/bandit/issues_data.py +426 -0
  61. checkmate/contrib/plugins/python/bandit/setup.py +13 -0
  62. checkmate/contrib/plugins/ruby/__init__.py +0 -0
  63. checkmate/contrib/plugins/ruby/brakeman/__init__.py +0 -0
  64. checkmate/contrib/plugins/ruby/brakeman/analyzer.py +96 -0
  65. checkmate/contrib/plugins/ruby/brakeman/issues_data.py +518 -0
  66. checkmate/contrib/plugins/ruby/brakeman/setup.py +13 -0
  67. checkmate/helpers/__init__.py +0 -0
  68. checkmate/helpers/facts.py +26 -0
  69. checkmate/helpers/hashing.py +68 -0
  70. checkmate/helpers/issue.py +101 -0
  71. checkmate/helpers/settings.py +14 -0
  72. checkmate/lib/__init__.py +1 -0
  73. checkmate/lib/analysis/__init__.py +3 -0
  74. checkmate/lib/analysis/base.py +103 -0
  75. checkmate/lib/code/__init__.py +3 -0
  76. checkmate/lib/code/environment.py +809 -0
  77. checkmate/lib/models.py +515 -0
  78. checkmate/lib/stats/__init__.py +1 -0
  79. checkmate/lib/stats/helpers.py +19 -0
  80. checkmate/lib/stats/mapreduce.py +29 -0
  81. checkmate/management/__init__.py +1 -0
  82. checkmate/management/commands/__init__.py +18 -0
  83. checkmate/management/commands/alembic.py +32 -0
  84. checkmate/management/commands/analyze.py +42 -0
  85. checkmate/management/commands/analyzers.py +1 -0
  86. checkmate/management/commands/base.py +66 -0
  87. checkmate/management/commands/compare.py +0 -0
  88. checkmate/management/commands/export.py +0 -0
  89. checkmate/management/commands/info.py +0 -0
  90. checkmate/management/commands/init.py +103 -0
  91. checkmate/management/commands/issues.py +478 -0
  92. checkmate/management/commands/props/__init__.py +1 -0
  93. checkmate/management/commands/props/delete.py +29 -0
  94. checkmate/management/commands/props/get.py +30 -0
  95. checkmate/management/commands/props/set.py +29 -0
  96. checkmate/management/commands/reset.py +53 -0
  97. checkmate/management/commands/shell.py +19 -0
  98. checkmate/management/commands/snapshots.py +22 -0
  99. checkmate/management/commands/stats.py +21 -0
  100. checkmate/management/commands/summary.py +19 -0
  101. checkmate/management/commands/sync.py +63 -0
  102. checkmate/management/commands/trend.py +1 -0
  103. checkmate/management/commands/watch.py +27 -0
  104. checkmate/management/decorators.py +1 -0
  105. checkmate/management/helpers.py +140 -0
  106. checkmate/scripts/__init__.py +18 -0
  107. checkmate/scripts/manage.py +121 -0
  108. checkmate/settings/__init__.py +2 -0
  109. checkmate/settings/base.py +127 -0
  110. checkmate/settings/defaults.py +133 -0
  111. checkmate5-4.0.67.dist-info/LICENSE.txt +4095 -0
  112. checkmate5-4.0.67.dist-info/METADATA +15 -0
  113. checkmate5-4.0.67.dist-info/RECORD +116 -0
  114. checkmate5-4.0.67.dist-info/WHEEL +5 -0
  115. checkmate5-4.0.67.dist-info/entry_points.txt +2 -0
  116. checkmate5-4.0.67.dist-info/top_level.txt +1 -0
checkmate/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2014 Andreas Dewes
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ # this software and associated documentation files (the "Software"), to deal in
7
+ # the Software without restriction, including without limitation the rights to
8
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ # the Software, and to permit persons to whom the Software is furnished to do so,
10
+ # subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
checkmate/__main__.py ADDED
@@ -0,0 +1,25 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2014 Andreas Dewes
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ # this software and associated documentation files (the "Software"), to deal in
7
+ # the Software without restriction, including without limitation the rights to
8
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ # the Software, and to permit persons to whom the Software is furnished to do so,
10
+ # subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ import sys
23
+
24
+ if __name__ == '__main__':
25
+ print((sys.argv))
@@ -0,0 +1,21 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2014 Andreas Dewes
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ # this software and associated documentation files (the "Software"), to deal in
7
+ # the Software without restriction, including without limitation the rights to
8
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ # the Software, and to permit persons to whom the Software is furnished to do so,
10
+ # subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
File without changes
File without changes
@@ -0,0 +1,99 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ from checkmate.lib.analysis.base import BaseAnalyzer
5
+
6
+ import logging
7
+ import os
8
+ import tempfile
9
+ import json
10
+ import subprocess
11
+ import re
12
+
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class GptAnalyzer(BaseAnalyzer):
18
+
19
+ def __init__(self, *args, **kwargs):
20
+ super(GptAnalyzer, self).__init__(*args, **kwargs)
21
+
22
+ def summarize(self, items):
23
+ pass
24
+
25
+ def analyze(self, file_revision):
26
+ issues = []
27
+ tmpdir = "/tmp/"+file_revision.project.pk
28
+
29
+ if not os.path.exists(os.path.dirname(tmpdir+"/"+file_revision.path)):
30
+ try:
31
+ os.makedirs(os.path.dirname(tmpdir+"/"+file_revision.path))
32
+ except OSError as exc: # Guard against race condition
33
+ if exc.errno != errno.EEXIST:
34
+ raise
35
+
36
+ #result = subprocess.check_output(["rsync -r . "+tmpdir+" --exclude .git"],shell=True).strip()
37
+
38
+ f = open(tmpdir+"/"+file_revision.path, "wb")
39
+
40
+ result = {}
41
+ try:
42
+ with f:
43
+ try:
44
+ f.write(file_revision.get_file_content())
45
+ except UnicodeDecodeError:
46
+ pass
47
+ os.chdir(tmpdir)
48
+ os.environ["PATH"] = "/root/.go/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/:/usr/local/go/bin/"
49
+
50
+ try:
51
+ result = subprocess.check_output(["/root/bin/ptpt",
52
+ "run",
53
+ "scr",
54
+ f.name],
55
+ stderr=subprocess.DEVNULL).strip()
56
+ except subprocess.CalledProcessError as e:
57
+ if e.returncode == 2:
58
+ result = e.output
59
+ elif e.returncode == 1:
60
+ result = e.output
61
+ pass
62
+ else:
63
+ result = []
64
+
65
+ try:
66
+ json_result = json.loads(result)
67
+ except ValueError:
68
+ json_result = []
69
+ pass
70
+ try:
71
+ for issue in json_result:
72
+ value = issue['line']
73
+
74
+ location = (((value,None),
75
+ (value,None)),)
76
+
77
+ string = issue["finding"]
78
+ string = string.replace("'","")
79
+ string = string.replace("`","")
80
+ string = string.replace("\"","")
81
+ string = string.strip()
82
+ string = re.sub('[^A-Za-z0-9 ]+', '', string)
83
+
84
+ issues.append({
85
+ 'code': "I001",
86
+ 'location': location,
87
+ 'data': string,
88
+ 'file': file_revision.path,
89
+ 'line': value,
90
+ 'fingerprint': self.get_fingerprint_from_code(file_revision, location, extra_data=string)
91
+ })
92
+
93
+ except:
94
+ pass
95
+
96
+ finally:
97
+ pass
98
+ return {'issues': issues}
99
+
@@ -0,0 +1,6 @@
1
+ issues_data = { 'I001': { 'categories': [],
2
+ 'description': '%(issue.data)s',
3
+ 'file': '%(issue.file)s',
4
+ 'line': '%(issue.line)s',
5
+ 'severity': 3,
6
+ 'title': 'Openai'}}
@@ -0,0 +1,13 @@
1
+ from .analyzer import GptAnalyzer
2
+ from .issues_data import issues_data
3
+
4
+ analyzers = {
5
+ 'gptanalyzer':
6
+ {
7
+ 'name': 'gptanalyzer',
8
+ 'title': 'gptanalyzer',
9
+ 'class': GptAnalyzer,
10
+ 'language': 'all',
11
+ 'issues_data': issues_data,
12
+ },
13
+ }
File without changes
File without changes
@@ -0,0 +1,64 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ from checkmate.lib.analysis.base import BaseAnalyzer
5
+
6
+ import logging
7
+ import os
8
+ import tempfile
9
+ import json
10
+ import subprocess
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class Text4shellAnalyzer(BaseAnalyzer):
16
+
17
+ def __init__(self, *args, **kwargs):
18
+ super(Text4shellAnalyzer, self).__init__(*args, **kwargs)
19
+
20
+ def summarize(self, items):
21
+ pass
22
+
23
+ def analyze(self, file_revision):
24
+ issues = []
25
+ tmpdir = "/tmp/"+file_revision.project.pk
26
+ f = open(tmpdir+"/"+file_revision.path, "wb")
27
+ try:
28
+ with f:
29
+ f.write(file_revision.get_file_content())
30
+ try:
31
+ result = subprocess.check_output(["python3","/root/text4shell-ce/scan_commons_text_versions.py",
32
+ f.name,
33
+ "-quiet"]
34
+ )
35
+ except subprocess.CalledProcessError as e:
36
+ pass
37
+
38
+ try:
39
+ json_result = json.loads(result)
40
+ except ValueError:
41
+ json_result = {}
42
+ pass
43
+
44
+ try:
45
+ line = "1"
46
+ line = int(line)
47
+ location = (((line, line),
48
+ (line, None)),)
49
+
50
+ issues.append({
51
+ 'code': "I001",
52
+ 'location': location,
53
+ 'data': json_result["I001"],
54
+ 'file': file_revision.path,
55
+ 'line': line,
56
+ 'fingerprint': self.get_fingerprint_from_code(file_revision, location, extra_data=json_result["I001"])
57
+ })
58
+
59
+ except KeyError:
60
+ pass
61
+
62
+ finally:
63
+ os.unlink(f.name)
64
+ return {'issues': issues}
@@ -0,0 +1,8 @@
1
+ issues_data = {
2
+ "I001": {
3
+ "title": "Text4Shell",
4
+ "description": "%(issue.data)s",
5
+ "severity": 3,
6
+ "categories": []
7
+ }
8
+ }
@@ -0,0 +1,13 @@
1
+ from .analyzer import Text4shellAnalyzer
2
+ from .issues_data import issues_data
3
+
4
+ analyzers = {
5
+ 'text4shell':
6
+ {
7
+ 'name': 'text4shell',
8
+ 'title': 'text4shell',
9
+ 'class': Text4shellAnalyzer,
10
+ 'language': 'cve',
11
+ 'issues_data': issues_data,
12
+ },
13
+ }
File without changes
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .init import Command as InitCommand
4
+ from .analyze import Command as AnalyzeCommand
5
+ from .diff import Command as DiffCommand
6
+ from .update_stats import Command as UpdateStatsCommand
@@ -0,0 +1,364 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ from checkmate.management.commands.analyze import Command as AnalyzeCommand
5
+ from .base import Command as BaseCommand
6
+ from ..lib.repository import group_snapshots_by_date, get_first_date_for_group
7
+ from ..models import GitBranch
8
+ from checkmate.contrib.plugins.git.models import GitSnapshot
9
+ from checkmate.lib.models import Diff, Snapshot
10
+ from checkmate.helpers.hashing import Hasher
11
+ from checkmate.helpers.settings import update
12
+ from checkmate.lib.models import (Snapshot,
13
+ Project,
14
+ BaseDocument,
15
+ FileRevision,
16
+ Diff,
17
+ IssueOccurrence)
18
+
19
+ import time
20
+ import datetime
21
+ import traceback
22
+ import logging
23
+
24
+ from checkmate.lib.code import CodeEnvironment
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class Command(BaseCommand, AnalyzeCommand):
30
+
31
+ options = AnalyzeCommand.options + [
32
+ {
33
+ 'name': '--n',
34
+ 'action': 'store',
35
+ 'dest': 'n',
36
+ 'type': int,
37
+ 'default': 1,
38
+ 'help': 'The number of snapshots to analyze.'
39
+ },
40
+ {
41
+ 'name': '--offset',
42
+ 'action': 'store',
43
+ 'dest': 'offset',
44
+ 'type': int,
45
+ 'default': 0,
46
+ 'help': 'The offset from the latest snapshot.'
47
+ },
48
+ {
49
+ 'name': '--shas',
50
+ 'action': 'store',
51
+ 'dest': 'shas',
52
+ 'type': str,
53
+ 'default': '',
54
+ 'help': 'A comma-separated list of commit to analyze, identified by their SHAs.'
55
+ },
56
+ {
57
+ 'name': '--branch',
58
+ 'action': 'store',
59
+ 'dest': 'branch',
60
+ 'type': str,
61
+ 'default': 'master',
62
+ 'help': 'The branch to analyze.'
63
+ },
64
+ {
65
+ 'name': '--from',
66
+ 'action': 'store',
67
+ 'dest': 'from',
68
+ 'type': str,
69
+ 'default': '',
70
+ 'help': 'The date for which to perform the analysis.'
71
+ },
72
+ {
73
+ 'name': '--type',
74
+ 'action': 'store',
75
+ 'dest': 'type',
76
+ 'type': str,
77
+ 'default': 'latest',
78
+ 'help': 'The type of analysis (latest, monthly, weekly, daily).'
79
+ },
80
+ ]
81
+
82
+ def analyze_grouped_snapshots(self, branch, group, grouped_snapshots):
83
+ for key, snapshots in list(grouped_snapshots.items()):
84
+ last_snp, first_snp = snapshots[0], snapshots[-1]
85
+ self.analyze_and_generate_diffs(branch,
86
+ [first_snp, last_snp],
87
+ [{'snapshot_a': first_snp.sha,
88
+ 'snapshot_b': last_snp.sha}])
89
+
90
+ def analyze_and_generate_diffs(self, branch, snapshots, diff_list):
91
+
92
+ analyzed_snapshots = {}
93
+ new_analyzed_snapshots = {}
94
+
95
+ if 'project_settings' in self.opts:
96
+ project_settings = self.opts['project_settings']
97
+ else:
98
+ project_settings = self.project.settings
99
+
100
+ # we look for a .checkmate.yml file in the main branch of the project
101
+ # if we find it, we either replace the project settings, update them
102
+ # or do nothing, depending on the desired configuration.
103
+ try:
104
+ git_settings = self.project.git.get_settings()
105
+ if git_settings is not None:
106
+ if git_settings.get('overwrite_project_settings', False):
107
+ project_settings = git_settings
108
+ elif not project_settings.get('ignore_git_settings', False):
109
+ project_settings.update(git_settings)
110
+ except ValueError:
111
+ logger.warning("Error when loading checkmate settings from git...")
112
+ git_settings = {}
113
+
114
+ code_environment = CodeEnvironment(self.project,
115
+ global_settings=self.settings,
116
+ project_settings=project_settings)
117
+
118
+ code_environment.env['branch'] = branch
119
+ code_environment.env['project'] = self.project
120
+
121
+ for git_snapshot in snapshots:
122
+ try:
123
+ query = {'sha': git_snapshot.sha,
124
+ 'project': self.project}
125
+ git_snapshot = self.backend.get(
126
+ GitSnapshot, query, include=('snapshot',))
127
+ snapshot = git_snapshot.snapshot
128
+ # we check if the snapshot is analyzed and if the analysis configuration matches
129
+ if snapshot.analyzed and snapshot.configuration == self.project.configuration:
130
+ analyze_snapshot = False
131
+ else:
132
+ analyze_snapshot = True
133
+ snapshot.configuration = self.project.configuration
134
+ except GitSnapshot.DoesNotExist:
135
+ analyze_snapshot = True
136
+ snapshot = Snapshot()
137
+ snapshot.configuration = self.project.configuration
138
+ except GitSnapshot.MultipleDocumentsReturned:
139
+ logger.error("Multiple snapshots returned, deleting...")
140
+ with self.backend.transaction():
141
+ self.backend.filter(GitSnapshot, query).delete()
142
+ analyze_snapshot = True
143
+ if analyze_snapshot:
144
+
145
+ try:
146
+ file_revisions = self.project.git.get_file_revisions(
147
+ git_snapshot.sha)
148
+ except:
149
+ logger.error("Cannot fetch file revisions for snapshot %s in project %s" % (
150
+ git_snapshot.sha, self.project.pk))
151
+ continue
152
+
153
+
154
+ try:
155
+ file_revisions = self.project.git.get_file_revisions(
156
+ git_snapshot.sha)
157
+ except:
158
+ logger.error("Cannot fetch file revisions for snapshot %s in project %s" % (
159
+ git_snapshot.sha, self.project.pk))
160
+ continue
161
+
162
+
163
+
164
+ code_environment.file_revisions = file_revisions
165
+ git_snapshot.snapshot = code_environment.analyze(
166
+ file_revisions, save_if_empty=True, snapshot=snapshot)
167
+
168
+ new_analyzed_snapshots[git_snapshot.sha] = git_snapshot
169
+
170
+ with self.backend.transaction():
171
+ git_snapshot.snapshot.hash = git_snapshot.sha
172
+ self.backend.save(git_snapshot.snapshot)
173
+ git_snapshot.project = self.project
174
+ self.backend.save(git_snapshot)
175
+
176
+ analyzed_snapshots[git_snapshot.sha] = git_snapshot
177
+
178
+ snapshot_pairs = []
179
+ for diff_params in diff_list:
180
+ try:
181
+ snapshot_pairs.append(
182
+ [analyzed_snapshots[diff_params['snapshot_a']], analyzed_snapshots[diff_params['snapshot_b']]])
183
+ except KeyError:
184
+ continue
185
+
186
+ diffs = []
187
+
188
+ for git_snapshot_a, git_snapshot_b in snapshot_pairs:
189
+ diff = None
190
+ try:
191
+ query = {'snapshot_a': git_snapshot_a.snapshot,
192
+ 'snapshot_b': git_snapshot_b.snapshot}
193
+ diff = self.backend.get(Diff, query)
194
+ except Diff.DoesNotExist:
195
+ logger.debug("Generating a diff between snapshots %s and %s" % (git_snapshot_a.sha,
196
+ git_snapshot_b.sha))
197
+ # if the configuration of the diff does not match the project configuration, we regenerate it
198
+ if diff is None or diff.configuration != self.project.configuration:
199
+ if diff is not None:
200
+ diff.configuration = self.project.configuration
201
+ diff, diff_file_revisions, diff_issue_occurrences = code_environment.diff_snapshots(
202
+ git_snapshot_a.snapshot,
203
+ git_snapshot_b.snapshot,
204
+ save=True,
205
+ diff=diff)
206
+
207
+ diffs.append(diff)
208
+
209
+ return analyzed_snapshots, diffs
210
+
211
+ def update_branch(self, branch_name, head_snapshot=None):
212
+
213
+ logger.debug("Updating info for branch %s" % branch_name)
214
+
215
+ hasher = Hasher()
216
+ hasher.add(branch_name)
217
+
218
+ branch = GitBranch({'project': self.project,
219
+ 'name': branch_name,
220
+ 'hash': hasher.digest.hexdigest(),
221
+ 'snapshots': []})
222
+
223
+ try:
224
+ branch = self.backend.get(GitBranch,
225
+ {'project.pk': self.project.pk, 'name': branch_name})
226
+ except GitBranch.DoesNotExist:
227
+ logger.debug("Creating branch....")
228
+ # we save the new branch instead...
229
+ with self.backend.transaction():
230
+ self.backend.save(branch)
231
+
232
+ if head_snapshot:
233
+ if branch.get('head_snapshot'):
234
+ if head_snapshot.committer_date_ts > branch.head_snapshot.eager.committer_date_ts:
235
+ branch.last_analyzed_snapshot = branch.head_snapshot
236
+ branch.head_snapshot = head_snapshot
237
+ else:
238
+ branch.head_snapshot = head_snapshot
239
+ with self.backend.transaction():
240
+ self.backend.update(
241
+ branch, ['last_analyzed_snapshot', 'head_snapshot'])
242
+
243
+ def run(self):
244
+ # to do: replace this with actual configuration information (in the future)
245
+ hasher = Hasher()
246
+ hasher.add("bars")
247
+ self.project.configuration = hasher.digest.hexdigest()
248
+ with self.backend.transaction():
249
+ self.backend.update(self.project, ['configuration'])
250
+ if not 'branch' in self.opts:
251
+ branch_name = self.project.git.get_default_branch()
252
+ else:
253
+ branch_name = self.opts.get('branch')
254
+
255
+ if self.opts['type'] in ('monthly', 'weekly', 'daily'):
256
+
257
+ if not branch_name:
258
+ raise ValueError(
259
+ "Branch cannot be None for %s analysis!" % self.opts['type'])
260
+
261
+ logger.info("Analyzing %d %s commits in branch %s (offset: %d)" %
262
+ (self.opts['n'], self.opts['type'], branch_name, self.opts['offset']))
263
+
264
+ if self.opts['from']:
265
+ if isinstance(self.opts['from'], str):
266
+ try:
267
+ from_date = datetime.datetime.strptime(
268
+ self.opts['from'], "%Y-%m-%d")
269
+ except ValueError:
270
+ raise self.CommandException(
271
+ "Invalid date format or value for `from` parameter (use YYYY-mm-dd)")
272
+ else:
273
+ try:
274
+ from_date = self.project.git.get_snapshots(branch=branch_name, limit=1)[0]\
275
+ .committer_date + datetime.timedelta(days=1)
276
+ except IndexError:
277
+ logger.info("No snapshots found in branch %s" %
278
+ branch_name)
279
+ return
280
+
281
+ dt = get_first_date_for_group(from_date, self.opts['type'], self.opts['n'] +
282
+ self.opts['offset'])
283
+
284
+ snapshots = self.project.git.get_snapshots(branch=branch_name,
285
+ since=dt.ctime())[:-self.opts['offset']
286
+ if self.opts['offset'] != 0 else None]
287
+
288
+ grouped_snapshots = group_snapshots_by_date(
289
+ snapshots, self.opts['type'])
290
+
291
+ snapshots_to_analyze = []
292
+ diffs_to_generate = []
293
+ for key, snapshots in list(grouped_snapshots.items()):
294
+ last_snp, first_snp = snapshots[0], snapshots[-1]
295
+ snapshots_to_analyze.append(first_snp)
296
+ snapshots_to_analyze.append(last_snp)
297
+ diffs_to_generate.append(
298
+ {'snapshot_a': first_snp.sha, 'snapshot_b': last_snp.sha})
299
+
300
+ else:
301
+ if self.opts['shas']:
302
+ logger.debug("Analyzing %d snapshots" %
303
+ len(self.opts['shas'].split(",")))
304
+ if isinstance(self.opts['shas'], str):
305
+ shas = self.opts['shas'].split(",")
306
+ else:
307
+ shas = self.opts['shas']
308
+ snapshots_to_analyze = self.project.git.get_snapshots(
309
+ shas=shas)
310
+ else:
311
+ if branch_name is None:
312
+ logger.error("No branch specified!")
313
+ return
314
+
315
+ # we perform a sequential analysis
316
+ branch = None
317
+ try:
318
+ branch = self.backend.get(GitBranch,
319
+ {'project.pk': self.project.pk,
320
+ 'name': branch_name})
321
+ except GitBranch.DoesNotExist:
322
+ pass
323
+
324
+ logger.info("Analyzing the %d most recent commits in branch %s (offset: %d)" %
325
+ (self.opts['n'], branch_name, self.opts['offset']))
326
+
327
+ if not self.opts['n']:
328
+ snapshots_to_analyze = self.project.git.get_snapshots(
329
+ branch=branch_name)
330
+ else:
331
+ snapshots_to_analyze = self.project.git.get_snapshots(branch=branch_name,
332
+ limit=self.opts['n'], offset=self.opts['offset'])
333
+
334
+ def timestamp(dt):
335
+ return (dt-datetime.datetime(1970, 1, 1)).total_seconds()
336
+
337
+ diffs_to_generate = [{'snapshot_a': snapshots_to_analyze[i]['sha'],
338
+ 'snapshot_b':snapshots_to_analyze[i+1]['sha']}
339
+ for i in range(len(snapshots_to_analyze)-1)]
340
+
341
+ if branch and branch.get('last_analyzed_snapshot', None):
342
+ last_analyzed_snapshot = branch.last_analyzed_snapshot.eager
343
+ if last_analyzed_snapshot and snapshots_to_analyze and last_analyzed_snapshot['sha'] != snapshots_to_analyze[-1]['sha']:
344
+ snapshots_to_analyze.append(last_analyzed_snapshot)
345
+ last_diff = {
346
+ 'snapshot_a': last_analyzed_snapshot['sha'],
347
+ 'snapshot_b': snapshots_to_analyze[-1]['sha']
348
+ }
349
+ diffs_to_generate.append(last_diff)
350
+
351
+ if 'diffs' in self.opts and self.opts['diffs'] is not None:
352
+ diffs_to_generate = self.opts['diffs']
353
+ try:
354
+ analyzed_snapshots, diffs = self.analyze_and_generate_diffs(branch_name,
355
+ snapshots_to_analyze, diffs_to_generate)
356
+ head_snapshot = None
357
+ if analyzed_snapshots:
358
+ head_snapshot = sorted(
359
+ list(analyzed_snapshots.values()), key=lambda snp: snp['committer_date_ts'])[-1]
360
+ if branch_name:
361
+ self.update_branch(
362
+ branch_name, head_snapshot=head_snapshot)
363
+ except KeyboardInterrupt:
364
+ raise