devops: add source code for flakiness dashboard (#4479)
This adds source code for the azure function that processes flakiness reports
This commit is contained in:
parent
e72d9a4185
commit
06e0aa40eb
44
utils/flakiness-dashboard/.gitignore
vendored
Normal file
44
utils/flakiness-dashboard/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
bin
|
||||
obj
|
||||
csx
|
||||
.vs
|
||||
edge
|
||||
Publish
|
||||
|
||||
*.swp
|
||||
*.user
|
||||
*.suo
|
||||
*.cscfg
|
||||
*.Cache
|
||||
project.lock.json
|
||||
|
||||
/packages
|
||||
/TestResults
|
||||
|
||||
/tools/NuGet.exe
|
||||
/App_Data
|
||||
/secrets
|
||||
/data
|
||||
.secrets
|
||||
appsettings.json
|
||||
local.settings.json
|
||||
|
||||
node_modules
|
||||
dist
|
||||
|
||||
# Local python packages
|
||||
.python_packages/
|
||||
|
||||
# Python Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
4
utils/flakiness-dashboard/README.md
Normal file
4
utils/flakiness-dashboard/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Flakiness Dashboard Backend
|
||||
|
||||
This directory contains source code for the Azure function that we use to aggregate test reports.
|
||||
The data is consumed by https://devops.aslushnikov.com/flakiness.html
|
||||
21
utils/flakiness-dashboard/host.json
Normal file
21
utils/flakiness-dashboard/host.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"version": "2.0",
|
||||
"logging": {
|
||||
"applicationInsights": {
|
||||
"samplingSettings": {
|
||||
"isEnabled": true,
|
||||
"excludedTypes": "Request"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"queues": {
|
||||
"batchSize": 1,
|
||||
"newBatchThreshold": 0
|
||||
}
|
||||
},
|
||||
"extensionBundle": {
|
||||
"id": "Microsoft.Azure.Functions.ExtensionBundle",
|
||||
"version": "[1.*, 2.0.0)"
|
||||
}
|
||||
}
|
||||
315
utils/flakiness-dashboard/package-lock.json
generated
Normal file
315
utils/flakiness-dashboard/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.1.tgz",
|
||||
"integrity": "sha512-wP2Jw6uPp8DEDy0n4KNidvwzDjyVV2xnycEIq7nPzj1rHyb/r+t3OPeNT1INZePP2wy5ZqlwyuyOMTi0ePyY1A==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@azure/core-asynciterator-polyfill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz",
|
||||
"integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg=="
|
||||
},
|
||||
"@azure/core-auth": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.1.3.tgz",
|
||||
"integrity": "sha512-A4xigW0YZZpkj1zK7dKuzbBpGwnhEcRk6WWuIshdHC32raR3EQ1j6VA9XZqE+RFsUgH6OAmIK5BWIz+mZjnd6Q==",
|
||||
"requires": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-tracing": "1.0.0-preview.8",
|
||||
"@opentelemetry/api": "^0.6.1",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/core-tracing": {
|
||||
"version": "1.0.0-preview.8",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.8.tgz",
|
||||
"integrity": "sha512-ZKUpCd7Dlyfn7bdc+/zC/sf0aRIaNQMDuSj2RhYRFe3p70hVAnYGp3TX4cnG2yoEALp/LTj/XnZGQ8Xzf6Ja/Q==",
|
||||
"requires": {
|
||||
"@opencensus/web-types": "0.0.7",
|
||||
"@opentelemetry/api": "^0.6.1",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@opentelemetry/api": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-0.6.1.tgz",
|
||||
"integrity": "sha512-wpufGZa7tTxw7eAsjXJtiyIQ42IWQdX9iUQp7ACJcKo1hCtuhLU+K2Nv1U6oRwT1oAlZTE6m4CgWKZBhOiau3Q==",
|
||||
"requires": {
|
||||
"@opentelemetry/context-base": "^0.6.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@azure/core-http": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-1.1.9.tgz",
|
||||
"integrity": "sha512-wM0HMRNQaE2NtTHb+9FXF7uxUqaAHFTMVu6OzlEll6gUGybcDqM7+9Oklp33BhEfq+ZumpCoqxq3njNbMHuf/w==",
|
||||
"requires": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-auth": "^1.1.3",
|
||||
"@azure/core-tracing": "1.0.0-preview.9",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"@opentelemetry/api": "^0.10.2",
|
||||
"@types/node-fetch": "^2.5.0",
|
||||
"@types/tunnel": "^0.0.1",
|
||||
"form-data": "^3.0.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"process": "^0.11.10",
|
||||
"tough-cookie": "^4.0.0",
|
||||
"tslib": "^2.0.0",
|
||||
"tunnel": "^0.0.6",
|
||||
"uuid": "^8.1.0",
|
||||
"xml2js": "^0.4.19"
|
||||
}
|
||||
},
|
||||
"@azure/core-lro": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-1.0.2.tgz",
|
||||
"integrity": "sha512-Yr0JD7GKryOmbcb5wHCQoQ4KCcH5QJWRNorofid+UvudLaxnbCfvKh/cUfQsGUqRjO9L/Bw4X7FP824DcHdMxw==",
|
||||
"requires": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-http": "^1.1.1",
|
||||
"events": "^3.0.0",
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@azure/core-paging": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.1.3.tgz",
|
||||
"integrity": "sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A==",
|
||||
"requires": {
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@azure/core-tracing": {
|
||||
"version": "1.0.0-preview.9",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.9.tgz",
|
||||
"integrity": "sha512-zczolCLJ5QG42AEPQ+Qg9SRYNUyB+yZ5dzof4YEc+dyWczO9G2sBqbAjLB7IqrsdHN2apkiB2oXeDKCsq48jug==",
|
||||
"requires": {
|
||||
"@opencensus/web-types": "0.0.7",
|
||||
"@opentelemetry/api": "^0.10.2",
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@azure/logger": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.0.tgz",
|
||||
"integrity": "sha512-g2qLDgvmhyIxR3JVS8N67CyIOeFRKQlX/llxYJQr1OSGQqM3HTpVP8MjmjcEKbL/OIt2N9C9UFaNQuKOw1laOA==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@azure/storage-blob": {
|
||||
"version": "12.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.2.1.tgz",
|
||||
"integrity": "sha512-erqCSmDL8b/AHZi94nq+nCE+2whQmvBDkAv4N9uic0MRac/gRyZqnsqkfrun/gr2rZo+qVtnMenwkkE3roXn8Q==",
|
||||
"requires": {
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-http": "^1.1.6",
|
||||
"@azure/core-lro": "^1.0.2",
|
||||
"@azure/core-paging": "^1.1.1",
|
||||
"@azure/core-tracing": "1.0.0-preview.9",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"@opentelemetry/api": "^0.10.2",
|
||||
"events": "^3.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@opencensus/web-types": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@opencensus/web-types/-/web-types-0.0.7.tgz",
|
||||
"integrity": "sha512-xB+w7ZDAu3YBzqH44rCmG9/RlrOmFuDPt/bpf17eJr8eZSrLt7nc7LnWdxM9Mmoj/YKMHpxRg28txu3TcpiL+g=="
|
||||
},
|
||||
"@opentelemetry/api": {
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-0.10.2.tgz",
|
||||
"integrity": "sha512-GtpMGd6vkzDMYcpu2t9LlhEgMy/SzBwRnz48EejlRArYqZzqSzAsKmegUK7zHgl+EOIaK9mKHhnRaQu3qw20cA==",
|
||||
"requires": {
|
||||
"@opentelemetry/context-base": "^0.10.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opentelemetry/context-base": {
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.10.2.tgz",
|
||||
"integrity": "sha512-hZNKjKOYsckoOEgBziGMnBcX0M7EtstnCmwz5jZUOUYwlZ+/xxX6z3jPu1XVO2Jivk0eLfuP9GP+vFD49CMetw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@opentelemetry/context-base": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.6.1.tgz",
|
||||
"integrity": "sha512-5bHhlTBBq82ti3qPT15TRxkYTFPPQWbnkkQkmHPtqiS1XcTB69cEKd3Jm7Cfi/vkPoyxapmePE9tyA7EzLt8SQ=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.1.tgz",
|
||||
"integrity": "sha512-D2/Xwp9c49JhIWq7SIrdvoYyGdq6yXkr5FTldN4rus9XljYFBul6P2epAID8xepOpL4ffcx09C05FZGK/1AIkw=="
|
||||
},
|
||||
"@types/node-fetch": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz",
|
||||
"integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@types/tunnel": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz",
|
||||
"integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"events": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz",
|
||||
"integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
|
||||
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.27",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||
"requires": {
|
||||
"mime-db": "1.44.0"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
||||
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||
"integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
|
||||
"requires": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
},
|
||||
"tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
|
||||
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.4.23",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
|
||||
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
|
||||
"requires": {
|
||||
"sax": ">=0.6.0",
|
||||
"xmlbuilder": "~11.0.0"
|
||||
}
|
||||
},
|
||||
"xmlbuilder": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
12
utils/flakiness-dashboard/package.json
Normal file
12
utils/flakiness-dashboard/package.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "",
|
||||
"version": "",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"test": "echo \"No tests yet...\""
|
||||
},
|
||||
"author": "",
|
||||
"dependencies": {
|
||||
"@azure/storage-blob": "^12.2.1"
|
||||
}
|
||||
}
|
||||
11
utils/flakiness-dashboard/processing/function.json
Normal file
11
utils/flakiness-dashboard/processing/function.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"bindings": [
|
||||
{
|
||||
"type": "blobTrigger",
|
||||
"direction": "in",
|
||||
"name": "newBlob",
|
||||
"path": "uploads/{name}",
|
||||
"connection": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
13
utils/flakiness-dashboard/processing/host.json
Normal file
13
utils/flakiness-dashboard/processing/host.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": "2.0",
|
||||
"extensions": {
|
||||
"queues": {
|
||||
"batchSize": 1,
|
||||
"newBatchThreshold": 0
|
||||
}
|
||||
},
|
||||
"extensionBundle": {
|
||||
"id": "Microsoft.Azure.Functions.ExtensionBundle",
|
||||
"version": "[1.*, 2.0.0)"
|
||||
}
|
||||
}
|
||||
173
utils/flakiness-dashboard/processing/index.js
Normal file
173
utils/flakiness-dashboard/processing/index.js
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const { BlobServiceClient } = require("@azure/storage-blob");
|
||||
const zlib = require('zlib');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
|
||||
const gzipAsync = util.promisify(zlib.gzip);
|
||||
const gunzipAsync = util.promisify(zlib.gunzip);
|
||||
|
||||
const blobServiceClient = BlobServiceClient.fromConnectionString(process.env.AzureWebJobsStorage);
|
||||
|
||||
const DASHBOARD_CONTAINER = 'dashboards';
|
||||
const DASHBOARD_NAME = 'main.json';
|
||||
const DASHBOARD_VERSION = 1;
|
||||
|
||||
class SimpleBlob {
|
||||
static async create(container, blobName) {
|
||||
const dashboardContainerClient = await blobServiceClient.getContainerClient(DASHBOARD_CONTAINER);
|
||||
return new SimpleBlob(dashboardContainerClient);
|
||||
}
|
||||
|
||||
constructor(containerClient) {
|
||||
this._blobClient = containerClient.getBlobClient(DASHBOARD_NAME);
|
||||
this._blockBlobClient = this._blobClient.getBlockBlobClient();
|
||||
}
|
||||
|
||||
async download() {
|
||||
if (!await this._blobClient.exists())
|
||||
return undefined;
|
||||
const response = await this._blobClient.download();
|
||||
const responseStream = response.readableStreamBody;
|
||||
const buffer = await new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
responseStream.on('data', data => chunks.push(data instanceof Buffer ? data : Buffer.from(data)));
|
||||
responseStream.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
responseStream.on('error', reject);
|
||||
});
|
||||
const properties = await this._blobClient.getProperties();
|
||||
const content = properties.contentEncoding.toLowerCase().trim() === 'gzip' ? await gunzipAsync(buffer) : buffer.toString('utf8');
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
async uploadGzipped(data) {
|
||||
const content = JSON.stringify(data);
|
||||
const zipped = await gzipAsync(content);
|
||||
await this._blockBlobClient.upload(zipped, Buffer.byteLength(zipped), {
|
||||
blobHTTPHeaders: {
|
||||
blobContentEncoding: 'gzip',
|
||||
blobContentType: 'application/json; charset=UTF-8',
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteUploadBlob(blobName) {
|
||||
// First we do - delete the blob.
|
||||
const containerClient = await blobServiceClient.getContainerClient('uploads');
|
||||
await containerClient.deleteBlob(blobName, {});
|
||||
}
|
||||
|
||||
function flattenSpecs(suite, result = []) {
|
||||
if (suite.suites) {
|
||||
for (const child of suite.suites)
|
||||
flattenSpecs(child, result);
|
||||
}
|
||||
for (const spec of suite.specs || [])
|
||||
result.push(spec);
|
||||
return result;
|
||||
}
|
||||
|
||||
class Dashboard {
|
||||
constructor() {
|
||||
this._runs = [];
|
||||
}
|
||||
|
||||
initialize(jsonData) {
|
||||
if (jsonData.version !== DASHBOARD_VERSION) {
|
||||
// Run migrations here!
|
||||
}
|
||||
this._runs = jsonData.buildbotRuns;
|
||||
}
|
||||
|
||||
addReport(report) {
|
||||
// We cannot use linenumber to identify specs since line numbers
|
||||
// might be different across commits.
|
||||
const getSpecId = spec => spec.file + ' @@@ ' + spec.title;
|
||||
|
||||
const faultySpecIds = new Set();
|
||||
for (const run of this._runs) {
|
||||
for (const spec of run.specs)
|
||||
faultySpecIds.add(getSpecId(spec));
|
||||
}
|
||||
const specs = [];
|
||||
for (const spec of flattenSpecs(report)) {
|
||||
// Filter out specs that didn't have a single test that was run in the
|
||||
// given shard.
|
||||
if (spec.tests.every(test => test.runs.length === 1 && !test.runs[0].status))
|
||||
continue;
|
||||
const hasFlakyAnnotation = spec.tests.some(test => test.annotations.some(a => a.type === 'flaky'));
|
||||
|
||||
if (!spec.ok || hasFlakyAnnotation || faultySpecIds.has(getSpecId(spec)))
|
||||
specs.push(spec);
|
||||
}
|
||||
if (specs.length) {
|
||||
this._runs.push({
|
||||
metadata: report.metadata,
|
||||
specs,
|
||||
});
|
||||
}
|
||||
return specs.length;
|
||||
}
|
||||
|
||||
serialize(maxCommits = 100) {
|
||||
const shaToTimestamp = new Map();
|
||||
for (const run of this._runs)
|
||||
shaToTimestamp.set(run.metadata.commitSHA, run.metadata.commitTimestamp);
|
||||
const commits = [...shaToTimestamp].sort(([sha1, ts1], [sha2, ts2]) => ts2 - ts1).slice(0, maxCommits);
|
||||
const commitsSet = new Set(commits.map(([sha, ts]) => sha));
|
||||
return {
|
||||
version: DASHBOARD_VERSION,
|
||||
timestamp: Date.now(),
|
||||
buildbotRuns: this._runs.filter(run => commitsSet.has(run.metadata.commitSHA)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = async function(context) {
|
||||
const timestamp = Date.now();
|
||||
const blobName = context.bindingData.name;
|
||||
// First thing we do - delete the blob.
|
||||
await deleteUploadBlob(blobName);
|
||||
|
||||
const dashboardBlob = await SimpleBlob.create();
|
||||
const dashboardData = await dashboardBlob.download();
|
||||
const dashboard = new Dashboard();
|
||||
if (dashboardData)
|
||||
dashboard.initialize(dashboardData);
|
||||
|
||||
try {
|
||||
const data = await gunzipAsync(context.bindings.newBlob);
|
||||
const report = JSON.parse(data.toString('utf8'));
|
||||
const addedSpecs = dashboard.addReport(report);
|
||||
await dashboardBlob.uploadGzipped(dashboard.serialize());
|
||||
context.log(`
|
||||
===== started =====
|
||||
SHA: ${report.metadata.commitSHA}
|
||||
URL: ${report.metadata.runURL}
|
||||
timestamp: ${report.metadata.commitTimestamp}
|
||||
added specs: ${addedSpecs}
|
||||
===== complete in ${Date.now() - timestamp}ms =====
|
||||
`);
|
||||
} catch (e) {
|
||||
context.log(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue