aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjonathanmetzman <31354670+jonathanmetzman@users.noreply.github.com>2021-10-05 09:16:54 -0400
committerGitHub <noreply@github.com>2021-10-05 09:16:54 -0400
commit6d23f29f29349a13bcc846fb14dac4a4ad99d066 (patch)
tree8a0a8d98deb41d16a6faf9696f2d5c1eae815e8c
parent26a0eab439cbb05a89e5e4bedcfb9de22b42754f (diff)
downloadoss-fuzz-6d23f29f29349a13bcc846fb14dac4a4ad99d066.tar.gz
[cifuzz] Use javascript actions library for uploading artifacts (#6552)
Delete our python implementation which appears buggy and will be annoying to maintain. Fixes: https://github.com/google/oss-fuzz/issues/6526
-rw-r--r--infra/cifuzz/cifuzz-base/Dockerfile1
-rw-r--r--infra/cifuzz/filestore/github_actions/__init__.py20
-rw-r--r--infra/cifuzz/filestore/github_actions/github_actions_test.py12
-rwxr-xr-xinfra/cifuzz/filestore/github_actions/upload.js33
-rw-r--r--infra/cifuzz/package-lock.json316
-rw-r--r--infra/cifuzz/package.json10
-rw-r--r--infra/cifuzz/run_fuzzers_test.py4
-rw-r--r--infra/cifuzz/third_party/github_actions_toolkit/LICENSE.md9
-rw-r--r--infra/cifuzz/third_party/github_actions_toolkit/README.md2
-rw-r--r--infra/cifuzz/third_party/github_actions_toolkit/artifact/artifact_client.py45
-rw-r--r--infra/cifuzz/third_party/github_actions_toolkit/artifact/config_variables.py40
-rw-r--r--infra/cifuzz/third_party/github_actions_toolkit/artifact/upload_http_client.py216
-rw-r--r--infra/cifuzz/third_party/github_actions_toolkit/artifact/upload_specification.py42
-rw-r--r--infra/cifuzz/third_party/github_actions_toolkit/artifact/utils.py118
-rw-r--r--infra/cifuzz/third_party/github_actions_toolkit/http_client/__init__.py11
-rw-r--r--infra/utils.py28
-rw-r--r--infra/utils_test.py16
17 files changed, 418 insertions, 505 deletions
diff --git a/infra/cifuzz/cifuzz-base/Dockerfile b/infra/cifuzz/cifuzz-base/Dockerfile
index 8fc25502f..67b4436a4 100644
--- a/infra/cifuzz/cifuzz-base/Dockerfile
+++ b/infra/cifuzz/cifuzz-base/Dockerfile
@@ -31,6 +31,7 @@ RUN ldconfig
ENV OSS_FUZZ_ROOT=/opt/oss-fuzz
ADD . ${OSS_FUZZ_ROOT}
RUN python3 -m pip install -r ${OSS_FUZZ_ROOT}/infra/cifuzz/requirements.txt
+RUN npm install ${OSS_FUZZ_ROOT}/infra/cifuzz
# Python file to execute when the docker container starts up.
# We can't use the env var $OSS_FUZZ_ROOT here. Since it's a constant env var,
diff --git a/infra/cifuzz/filestore/github_actions/__init__.py b/infra/cifuzz/filestore/github_actions/__init__.py
index 8b9b5986e..3b03f9c0b 100644
--- a/infra/cifuzz/filestore/github_actions/__init__.py
+++ b/infra/cifuzz/filestore/github_actions/__init__.py
@@ -15,13 +15,21 @@
import logging
import os
import shutil
+import sys
import tarfile
import tempfile
+# pylint: disable=wrong-import-position,import-error
+sys.path.append(
+ os.path.join(os.path.pardir, os.path.pardir, os.path.pardir,
+ os.path.dirname(os.path.abspath(__file__))))
+
+import utils
import http_utils
import filestore
from filestore.github_actions import github_api
-from third_party.github_actions_toolkit.artifact import artifact_client
+
+UPLOAD_JS = os.path.join(os.path.dirname(__file__), 'upload.js')
def tar_directory(directory, archive_path):
@@ -149,6 +157,14 @@ class GithubActionsFilestore(filestore.BaseFilestore):
return self._download_artifact(self.COVERAGE_PREFIX + name, dst_directory)
+def _upload_artifact_with_upload_js(name, artifact_paths, directory):
+ """Uploads the artifacts in |artifact_paths| that are located in |directory|
+ to |name|, using the upload.js script."""
+ command = [UPLOAD_JS, name, directory] + artifact_paths
+ _, _, retcode = utils.execute(command)
+ return retcode == 0
+
+
def _raw_upload_directory(name, directory):
"""Uploads the artifacts located in |directory| to |name|. Does not do any
tarring or adding prefixes to |name|."""
@@ -158,4 +174,4 @@ def _raw_upload_directory(name, directory):
for file_path in curr_file_paths:
artifact_paths.append(os.path.join(root, file_path))
logging.debug('Artifact paths: %s.', artifact_paths)
- return artifact_client.upload_artifact(name, artifact_paths, directory)
+ return _upload_artifact_with_upload_js(name, artifact_paths, directory)
diff --git a/infra/cifuzz/filestore/github_actions/github_actions_test.py b/infra/cifuzz/filestore/github_actions/github_actions_test.py
index 01d977715..7745065a9 100644
--- a/infra/cifuzz/filestore/github_actions/github_actions_test.py
+++ b/infra/cifuzz/filestore/github_actions/github_actions_test.py
@@ -93,8 +93,7 @@ class GithubActionsFilestoreTest(fake_filesystem_unittest.TestCase):
'cifuzz-corpus-' + name)
@mock.patch('filestore.github_actions.tar_directory')
- @mock.patch('third_party.github_actions_toolkit.artifact.artifact_client'
- '.upload_artifact')
+ @mock.patch('filestore.github_actions._upload_artifact_with_upload_js')
def test_upload_corpus(self, mock_upload_artifact, mock_tar_directory):
"""Test uploading corpus."""
self._create_local_dir()
@@ -109,8 +108,7 @@ class GithubActionsFilestoreTest(fake_filesystem_unittest.TestCase):
self.assert_upload(mock_upload_artifact, mock_tar_directory,
'corpus-target')
- @mock.patch('third_party.github_actions_toolkit.artifact.artifact_client'
- '.upload_artifact')
+ @mock.patch('filestore.github_actions._upload_artifact_with_upload_js')
def test_upload_crashes(self, mock_upload_artifact):
"""Test uploading crashes."""
self._create_local_dir()
@@ -121,8 +119,7 @@ class GithubActionsFilestoreTest(fake_filesystem_unittest.TestCase):
[mock.call('crashes-current', ['/local-dir/testcase'], '/local-dir')])
@mock.patch('filestore.github_actions.tar_directory')
- @mock.patch('third_party.github_actions_toolkit.artifact.artifact_client'
- '.upload_artifact')
+ @mock.patch('filestore.github_actions._upload_artifact_with_upload_js')
def test_upload_build(self, mock_upload_artifact, mock_tar_directory):
"""Test uploading build."""
self._create_local_dir()
@@ -138,8 +135,7 @@ class GithubActionsFilestoreTest(fake_filesystem_unittest.TestCase):
'build-sanitizer')
@mock.patch('filestore.github_actions.tar_directory')
- @mock.patch('third_party.github_actions_toolkit.artifact.artifact_client'
- '.upload_artifact')
+ @mock.patch('filestore.github_actions._upload_artifact_with_upload_js')
def test_upload_coverage(self, mock_upload_artifact, mock_tar_directory):
"""Test uploading coverage."""
self._create_local_dir()
diff --git a/infra/cifuzz/filestore/github_actions/upload.js b/infra/cifuzz/filestore/github_actions/upload.js
new file mode 100755
index 000000000..cd025e560
--- /dev/null
+++ b/infra/cifuzz/filestore/github_actions/upload.js
@@ -0,0 +1,33 @@
+#!/usr/bin/env node
+// Copyright 2021 Google LLC
+//
+// 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.
+// Script for uploading an artifact. Returns 0 on success.
+// Usage: upload.js <aritfactName> <rootDirectory> <file 1>...<file N>
+
+const fs = require('fs');
+const artifact = require('@actions/artifact');
+const artifactClient = artifact.create()
+const artifactName = process.argv[2];
+const rootDirectory = process.argv[3]
+const files = process.argv.slice(4);
+const options = {
+ continueOnError: true
+}
+
+const uploadResult = artifactClient.uploadArtifact(artifactName, files, rootDirectory, options)
+console.log(uploadResult);
+if (uploadResult['failedItems']) {
+ return 1;
+}
+return 0;
diff --git a/infra/cifuzz/package-lock.json b/infra/cifuzz/package-lock.json
new file mode 100644
index 000000000..9ee58404c
--- /dev/null
+++ b/infra/cifuzz/package-lock.json
@@ -0,0 +1,316 @@
+{
+ "name": "cifuzz",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "version": "1.0.0",
+ "license": "Apache2",
+ "dependencies": {
+ "@actions/artifact": "^0.5.2"
+ }
+ },
+ "node_modules/@actions/artifact": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-0.5.2.tgz",
+ "integrity": "sha512-q/r8WSqyxBJ0ffLCRrtjCBTGnAYqP+ID4yG7f7YSlhrQ4thNg/d+Tq9f1YkLPKX46ZR97OWtGDY+oU/nxcqvLw==",
+ "dependencies": {
+ "@actions/core": "^1.2.6",
+ "@actions/http-client": "^1.0.11",
+ "@types/tmp": "^0.1.0",
+ "tmp": "^0.1.0",
+ "tmp-promise": "^2.0.2"
+ }
+ },
+ "node_modules/@actions/core": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz",
+ "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==",
+ "dependencies": {
+ "@actions/http-client": "^1.0.11"
+ }
+ },
+ "node_modules/@actions/http-client": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
+ "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
+ "dependencies": {
+ "tunnel": "0.0.6"
+ }
+ },
+ "node_modules/@types/tmp": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.1.0.tgz",
+ "integrity": "sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA=="
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "node_modules/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/tmp": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
+ "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
+ "dependencies": {
+ "rimraf": "^2.6.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tmp-promise": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-2.1.1.tgz",
+ "integrity": "sha512-Z048AOz/w9b6lCbJUpevIJpRpUztENl8zdv1bmAKVHimfqRFl92ROkmT9rp7TVBnrEw2gtMTol/2Cp2S2kJa4Q==",
+ "dependencies": {
+ "tmp": "0.1.0"
+ }
+ },
+ "node_modules/tunnel": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+ "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
+ "engines": {
+ "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ },
+ "dependencies": {
+ "@actions/artifact": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-0.5.2.tgz",
+ "integrity": "sha512-q/r8WSqyxBJ0ffLCRrtjCBTGnAYqP+ID4yG7f7YSlhrQ4thNg/d+Tq9f1YkLPKX46ZR97OWtGDY+oU/nxcqvLw==",
+ "requires": {
+ "@actions/core": "^1.2.6",
+ "@actions/http-client": "^1.0.11",
+ "@types/tmp": "^0.1.0",
+ "tmp": "^0.1.0",
+ "tmp-promise": "^2.0.2"
+ }
+ },
+ "@actions/core": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz",
+ "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==",
+ "requires": {
+ "@actions/http-client": "^1.0.11"
+ }
+ },
+ "@actions/http-client": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
+ "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
+ "requires": {
+ "tunnel": "0.0.6"
+ }
+ },
+ "@types/tmp": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.1.0.tgz",
+ "integrity": "sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA=="
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "tmp": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
+ "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
+ "requires": {
+ "rimraf": "^2.6.3"
+ }
+ },
+ "tmp-promise": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-2.1.1.tgz",
+ "integrity": "sha512-Z048AOz/w9b6lCbJUpevIJpRpUztENl8zdv1bmAKVHimfqRFl92ROkmT9rp7TVBnrEw2gtMTol/2Cp2S2kJa4Q==",
+ "requires": {
+ "tmp": "0.1.0"
+ }
+ },
+ "tunnel": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+ "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ }
+}
diff --git a/infra/cifuzz/package.json b/infra/cifuzz/package.json
new file mode 100644
index 000000000..5823747dd
--- /dev/null
+++ b/infra/cifuzz/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "cifuzz",
+ "version": "1.0.0",
+ "description": "",
+ "author": "Google",
+ "license": "Apache2",
+ "dependencies": {
+ "@actions/artifact": "^0.5.2"
+ }
+}
diff --git a/infra/cifuzz/run_fuzzers_test.py b/infra/cifuzz/run_fuzzers_test.py
index 9f5f9e025..db442b188 100644
--- a/infra/cifuzz/run_fuzzers_test.py
+++ b/infra/cifuzz/run_fuzzers_test.py
@@ -342,9 +342,7 @@ class CoverageReportIntegrationTest(unittest.TestCase):
def setUp(self):
test_helpers.patch_environ(self, runner=True)
- @mock.patch('third_party.github_actions_toolkit.artifact.artifact_client'
- '.upload_artifact',
- return_value=True)
+ @mock.patch('filestore.github_actions._upload_artifact_with_upload_js')
def test_coverage_report(self, _):
"""Tests generation of coverage reports end-to-end, from building to
generation."""
diff --git a/infra/cifuzz/third_party/github_actions_toolkit/LICENSE.md b/infra/cifuzz/third_party/github_actions_toolkit/LICENSE.md
deleted file mode 100644
index dbae2edb2..000000000
--- a/infra/cifuzz/third_party/github_actions_toolkit/LICENSE.md
+++ /dev/null
@@ -1,9 +0,0 @@
-The MIT License (MIT)
-
-Copyright 2019 GitHub
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/infra/cifuzz/third_party/github_actions_toolkit/README.md b/infra/cifuzz/third_party/github_actions_toolkit/README.md
deleted file mode 100644
index ada595397..000000000
--- a/infra/cifuzz/third_party/github_actions_toolkit/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This is a partial Python reimplementation of https://github.com/actions/toolkit.
-It only reimplements the parts that are needed for uploading artifacts.
diff --git a/infra/cifuzz/third_party/github_actions_toolkit/artifact/artifact_client.py b/infra/cifuzz/third_party/github_actions_toolkit/artifact/artifact_client.py
deleted file mode 100644
index ae5b3ab00..000000000
--- a/infra/cifuzz/third_party/github_actions_toolkit/artifact/artifact_client.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""Public interface for artifact. Based on artifact-client.ts"""
-import logging
-
-from third_party.github_actions_toolkit.artifact import utils
-from third_party.github_actions_toolkit.artifact import upload_http_client
-from third_party.github_actions_toolkit.artifact import upload_specification
-
-
-def upload_artifact(name, files, root_directory, options=None):
- """Uploads an artifact based on uploadArtifact."""
- utils.check_artifact_name(name)
- upload_spec = upload_specification.get_upload_specification(
- name, root_directory, files)
-
- upload_response = {
- 'artifactName': name,
- 'artifactItems': [],
- 'size': 0,
- 'failedItems': []
- }
- if len(upload_spec) == 0:
- raise Exception('No files to upload')
-
- response = upload_http_client.create_artifact_in_file_container(name, options)
- file_container_resource_url = response.get('fileContainerResourceUrl')
- if not file_container_resource_url:
- logging.debug('create_artifact_in_file_container response: %s.', response)
- raise Exception('GitHub artifacts API didn\'t provide upload URL')
-
- logging.debug('Upload resource URL: %s', file_container_resource_url)
- upload_result = upload_http_client.upload_artifact_to_file_container(
- file_container_resource_url, upload_spec, options)
- # Update artifact size when done.
- # Uncompressed size used in UI when downloading a zip of the artifact.
- upload_http_client.patch_artifact_size(upload_result['totalSize'], name)
- logging.info(
- 'Uploaded artifact: %s, size is: %s bytes, '
- '%d items failed to upload.', name, upload_result['uploadSize'],
- len(upload_result['failedItems']))
- upload_response['artifactItems'] = [
- spec.absolute_file_path for spec in upload_spec
- ]
- upload_response['size'] = upload_result['uploadSize']
- upload_response['failedItems'] = upload_result['failedItems']
- return upload_response
diff --git a/infra/cifuzz/third_party/github_actions_toolkit/artifact/config_variables.py b/infra/cifuzz/third_party/github_actions_toolkit/artifact/config_variables.py
deleted file mode 100644
index 06237e1a8..000000000
--- a/infra/cifuzz/third_party/github_actions_toolkit/artifact/config_variables.py
+++ /dev/null
@@ -1,40 +0,0 @@
-"""Module for getting configuration values (often from env). Based on
-config-variables.ts."""
-
-import os
-
-UPLOAD_CHUNK_SIZE = 8 * 1024**2 # 8 MB.
-UPLOAD_FILE_CONCURRENCY = 2
-
-
-def get_runtime_url():
- """Returns the value of the ACTIONS_RUNTIME_URL var in the environment. Raises
- an exception if not set."""
- url = os.environ.get('ACTIONS_RUNTIME_URL')
- if not url:
- raise Exception('Unable to get ACTIONS_RUNTIME_URL env variable')
- return url
-
-
-def get_runtime_token():
- """Returns the value of the ACTIONS_RUNTIME_TOKEN var in the environment.
- Raises an exception if not set."""
- token = os.environ.get('ACTIONS_RUNTIME_TOKEN')
- if not token:
- raise Exception('Unable to get ACTIONS_RUNTIME_TOKEN env variable')
- return token
-
-
-def get_work_flow_run_id():
- """Returns the value of the GITHUB_RUN_ID var in the environment. Raises an
- exception if not set."""
- work_flow_run_id = os.environ.get('GITHUB_RUN_ID')
- if not work_flow_run_id:
- raise Exception('Unable to get GITHUB_RUN_ID env variable.')
- return work_flow_run_id
-
-
-def get_retention_days():
- """Returns the value of the GITHUB_RETENTION_DAYS or None if it wasn't
- specified."""
- return os.environ.get('GITHUB_RETENTION_DAYS')
diff --git a/infra/cifuzz/third_party/github_actions_toolkit/artifact/upload_http_client.py b/infra/cifuzz/third_party/github_actions_toolkit/artifact/upload_http_client.py
deleted file mode 100644
index 6e9ad9322..000000000
--- a/infra/cifuzz/third_party/github_actions_toolkit/artifact/upload_http_client.py
+++ /dev/null
@@ -1,216 +0,0 @@
-"""Module for uploading artifacts using HTTP. Based on upload-http-client.ts."""
-import json
-import logging
-import os
-import time
-import urllib
-import urllib.error
-import urllib.parse
-import urllib.request
-
-import requests
-
-from third_party.github_actions_toolkit.artifact import config_variables
-from third_party.github_actions_toolkit.artifact import utils
-from third_party.github_actions_toolkit import http_client
-
-
-def upload_file(parameters):
- """Based on uploadFileAsync. Note that this doesn't take
- index because we don't need it to do HTTP requests like the typescript code
- does."""
- # Skip gzip as it is unneeded for now.
- total_file_size = os.path.getsize(parameters['file'].absolute_file_path)
- if not upload_chunk(parameters['resourceUrl'],
- parameters['file'].absolute_file_path, total_file_size):
- return {
- 'isSuccess': False,
- 'successfulUploadSize': 0,
- 'totalSize': total_file_size
- }
- return {
- 'isSuccess': True,
- 'successfulUploadSize': total_file_size,
- 'totalSize': total_file_size
- }
-
-
-def upload_chunk(resource_url, file_path, total_file_size):
- """Based on uploadChunk. Differences from upstream are because:
- 1. HTTP client index since we don't need it to do HTTP uploads like typescript
- code.
- 2. This implementation doesn't use GZIP for simplicity.
- """
- start = 0
- end = total_file_size - 1
- content_range = utils.get_content_range(start, end, total_file_size)
- upload_headers = utils.get_upload_headers('application/octet-stream',
- is_keep_alive=False,
- is_gzip=False,
- content_length=total_file_size,
- content_range=content_range)
- for _ in range(utils.MAX_API_ATTEMPTS):
- try:
- with open(file_path, 'rb') as file_handle:
- response = requests.put(resource_url,
- data=file_handle,
- headers=upload_headers)
- logging.debug('upload_chunk response: %s', response.text)
- return True
- except Exception as err: # pylint: disable=broad-except
- logging.error('Failed to upload chunk because of %s', err)
-
- time.sleep(utils.SLEEP_TIME)
-
- return False
-
-
-def patch_artifact_size(size, artifact_name):
- """Based on patchArtifactSize from upload-http-client.ts"""
- resource_url = utils.get_artifact_url()
- resource_url = _add_url_params(resource_url, {'artifactName': artifact_name})
- logging.debug('resource_url is %s.', resource_url)
- parameters = {'Size': size}
- data = json.dumps(parameters)
- headers = utils.get_upload_headers('application/json')
- for _ in range(utils.MAX_API_ATTEMPTS):
- # TODO(metzman): Create better method for handling.
- try:
- _do_patch_request(resource_url, data, headers)
- logging.debug('Artifact "%s" successfully uploaded. Size: %d bytes',
- artifact_name, size)
- break
- except urllib.error.HTTPError as http_error:
- code = http_error.getcode()
- if code == http_client.HTTPCode.NOT_FOUND:
- logging.error('Artifact "%s" not found.', artifact_name)
- raise
-
- logging.error('Other error: %s', http_error.file.read())
-
- except ConnectionResetError:
- pass
-
- time.sleep(utils.SLEEP_TIME)
-
-
-def create_artifact_in_file_container(artifact_name, options):
- """Creates a file container for the new artifact in the remote blob
- storage/file service. |artifact_name| is the name of the artifact being
- created. Returns the response form the artifact service if the file container
- was created successfully."""
- parameters = {
- 'Type': 'actions_storage',
- 'Name': artifact_name,
- }
-
- # Set retention period.
- if options and 'retentionDays' in options:
- max_retention_str = config_variables.get_retention_days()
- parameters['RetentionDays'] = utils.get_proper_retention(
- options['retentionDays'], max_retention_str)
-
- data = json.dumps(parameters)
- artifact_url = utils.get_artifact_url()
- headers = utils.get_upload_headers('application/json')
- for _ in range(utils.MAX_API_ATTEMPTS):
- try:
- response = _do_post_request(artifact_url, data, headers)
- response_data = response.read()
- return json.loads(response_data)
- except urllib.error.HTTPError as http_error:
- code = http_error.getcode()
- if code == http_client.HTTPCode.BAD_REQUEST:
- logging.error('Invalid artifact name: "%s". Request URL: %s.',
- artifact_name, artifact_url)
- raise
- if code == http_client.HTTPCode.FORBIDDEN:
- logging.error('Unable to upload artifacts. Storage quota reached.')
- raise
- # Otherwise we can retry.
-
- except ConnectionResetError:
- pass
-
- time.sleep(utils.SLEEP_TIME)
-
- raise Exception('Can\'t retry creating artifact in file container again')
-
-
-def _do_post_request(url, data, headers=None):
- """Do a POST request to |url|."""
- return _do_upload_http_request(url, data, headers, method='POST')
-
-
-def _do_patch_request(url, data, headers=None):
- """Do a PATCH request to |url|."""
- return _do_upload_http_request(url, data, headers, method='PATCH')
-
-
-def _do_upload_http_request(url, data, headers, method):
- """Does an HTTP request that uploads |data| to |url| with |headers| using
- |method|."""
- if headers is None:
- headers = {}
- post_request = urllib.request.Request(url,
- data=data.encode(),
- headers=headers,
- method=method)
- logging.debug('Did request %s', post_request)
- return urllib.request.urlopen(post_request)
-
-
-def upload_artifact_to_file_container(upload_url, files_to_upload, options):
- """Sequentially uploads |files_to_upload| in chunks to |upload_url|."""
- logging.debug('File concurrency: %d, and chunk size: %d.',
- config_variables.UPLOAD_FILE_CONCURRENCY,
- config_variables.UPLOAD_CHUNK_SIZE)
- # By default, file uploads will continue if there is an error unless specified
- # differently in the options.
- if options:
- continue_on_error = options.get('continue_on_error', True)
- else:
- continue_on_error = True
-
- # Prepare the necessary parameters to upload all the files.
- upload_file_size = 0
- total_file_size = 0
- failed_items_to_report = []
- for file_to_upload in files_to_upload:
- url_params = {'itemPath': file_to_upload.upload_file_path}
- resource_url = _add_url_params(upload_url, url_params)
- upload_parameters = {
- 'file': file_to_upload,
- 'resourceUrl': resource_url,
- 'maxChunkSize': config_variables.UPLOAD_CHUNK_SIZE,
- 'continueOnError': continue_on_error
- }
- upload_file_result = upload_file(upload_parameters)
- upload_file_size += upload_file_result['successfulUploadSize']
- total_file_size += upload_file_result['totalSize']
- if not upload_file_result['isSuccess']:
- failed_items_to_report.append(file_to_upload)
- if not continue_on_error:
- logging.error('Stopping artifact upload due to error.')
-
- logging.info('Total size of files uploaded is %s bytes.', upload_file_size)
- return {
- 'uploadSize': upload_file_size,
- 'totalSize': total_file_size,
- 'failedItems': failed_items_to_report
- }
-
-
-def _add_url_params(url, params):
- """Returns |url| with the specified query |params| added."""
- # Parse URL into mutable format.
- # It's OK we use _asdict(), this is actually a public method.
- url_parts = urllib.parse.urlparse(url)._asdict()
-
- # Update URL.
- query = dict(urllib.parse.parse_qsl(url_parts['query']))
- query.update(params)
- url_parts['query'] = urllib.parse.urlencode(query)
-
- # Return a URL string.
- return urllib.parse.urlunparse(urllib.parse.ParseResult(**url_parts))
diff --git a/infra/cifuzz/third_party/github_actions_toolkit/artifact/upload_specification.py b/infra/cifuzz/third_party/github_actions_toolkit/artifact/upload_specification.py
deleted file mode 100644
index 0243e02d4..000000000
--- a/infra/cifuzz/third_party/github_actions_toolkit/artifact/upload_specification.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""Module for upload specifications. Based on upload-specification.ts."""
-import logging
-import os
-
-from third_party.github_actions_toolkit.artifact import utils
-
-
-class UploadSpecification: # pylint: disable=too-few-public-methods
- """Spec for uploads."""
-
- def __init__(self, abs_file_path, upload_file_path):
- self.absolute_file_path = abs_file_path
- self.upload_file_path = upload_file_path
-
-
-def get_upload_specification(artifact_name, root_directory, artifact_files):
- """Returns specifications that describe how files that are part of the
- artifact should be uploaded."""
- specifications = []
-
- if not root_directory.endswith('/'):
- root_directory += '/'
-
- for artifact_file in artifact_files:
- if not os.path.exists(artifact_file):
- raise Exception('File does not exist.', artifact_file)
-
- if os.path.isdir(artifact_file):
- logging.debug('Not uploading diectory: %s.', artifact_file)
- continue
-
- artifact_file = os.path.normpath(artifact_file)
- if not artifact_file.startswith(root_directory):
- raise Exception('Root directory is not a parent of artifact.',
- root_directory, artifact_file)
- upload_path = artifact_file.replace(root_directory, '')
- utils.check_artifact_file_path(upload_path)
- upload_file_path = os.path.join(artifact_name, upload_path)
- specification = UploadSpecification(artifact_file, upload_file_path)
- specifications.append(specification)
-
- return specifications
diff --git a/infra/cifuzz/third_party/github_actions_toolkit/artifact/utils.py b/infra/cifuzz/third_party/github_actions_toolkit/artifact/utils.py
deleted file mode 100644
index ec5738b91..000000000
--- a/infra/cifuzz/third_party/github_actions_toolkit/artifact/utils.py
+++ /dev/null
@@ -1,118 +0,0 @@
-"""Utility module. Based on utils.ts."""
-import logging
-
-from third_party.github_actions_toolkit.artifact import config_variables
-
-MAX_API_ATTEMPTS = 5
-SLEEP_TIME = 1
-INVALID_ARTIFACT_FILEPATH_CHARACTERS = [
- '"',
- ':',
- '<',
- '>',
- '|',
- '*',
- '?',
-]
-INVALID_ARTIFACT_NAME_CHARACTERS = ['\\', '/'
- ] + INVALID_ARTIFACT_FILEPATH_CHARACTERS
-
-# TODO(metzman): Convert exceptions to special kind.
-
-
-def get_proper_retention(retention, retention_setting):
- """Checks that |retention| is a normal value and replaces it with
- |retention_setting| if set."""
- if retention < 0:
- raise Exception('Invalid retention, minimum value is 1.')
-
- if retention_setting:
- retention_setting = int(retention_setting)
- if retention_setting < retention:
- logging.warning(
- 'Retention days is greater than max allowed by the repository.'
- ' Reducing retention to %d days', retention)
- retention = retention_setting
- return retention
-
-
-def check_artifact_name(name):
- """utils.ts checkArtifactName."""
- for invalid_char in INVALID_ARTIFACT_NAME_CHARACTERS:
- if invalid_char in name:
- raise Exception(
- f'Artifact name is invalid: {name}. Contains char: "{invalid_char}". '
- f'Invalid chars are: {INVALID_ARTIFACT_NAME_CHARACTERS}.')
-
-
-def check_artifact_file_path(artifact_file_path):
- """Raises an exception if |artifact_file| is invalid."""
- if not artifact_file_path:
- raise Exception('Artifact file path does not exist', artifact_file_path)
-
- for invalid_char in INVALID_ARTIFACT_NAME_CHARACTERS:
- if invalid_char in artifact_file_path:
- raise Exception(
- f'Artifact path: {artifact_file_path} is invalid, contains '
- f'invalid char: {invalid_char}.')
-
-
-def get_content_range(start, end, total):
- """Returns the content range for an HTTP request."""
- return f'bytes {start}-{end}/{total}'
-
-
-def get_http_request_headers():
- """Returns commonly needed headers for HTTP requests to Github actions
- APIs."""
- auth_token = config_variables.get_runtime_token()
- authorization = f'Bearer {auth_token}'
- return {'Authorization': authorization}
-
-
-def get_upload_headers( # pylint: disable=too-many-arguments
- content_type=None,
- is_keep_alive=False,
- is_gzip=None,
- uncompressed_length=None,
- content_length=None,
- content_range=None):
- """Based on getUploadHeaders implemented in utils.ts"""
- request_options = get_http_request_headers()
- api_version = get_api_version()
- request_options['Accept'] = f'application/json;api-version={api_version}'
-
- if content_type:
- request_options['Content-Type'] = content_type
-
- if is_keep_alive:
- request_options['Connection'] = 'Keep-Alive'
- request_options['Keep-Alive'] = '10'
-
- if is_gzip:
- assert uncompressed_length is not None
- request_options['Content-Encoding'] = 'gzip'
- request_options['x-tfs-filelength'] = str(uncompressed_length)
-
- if content_length:
- request_options['Content-Length'] = str(content_length)
-
- if content_range:
- request_options['Content-Range'] = content_range
-
- return request_options
-
-
-def get_api_version():
- """Based on getApiVersion from utils.ts"""
- return '6.0-preview'
-
-
-def get_artifact_url(work_flow_run_id=None):
- """Based on getArtifactUrl from utils.ts"""
- if work_flow_run_id is None:
- work_flow_run_id = config_variables.get_work_flow_run_id()
- runtime_url = config_variables.get_runtime_url()
- api_version = get_api_version()
- return (f'{runtime_url}_apis/pipelines/workflows/{work_flow_run_id}/artifacts'
- f'?api-version={api_version}')
diff --git a/infra/cifuzz/third_party/github_actions_toolkit/http_client/__init__.py b/infra/cifuzz/third_party/github_actions_toolkit/http_client/__init__.py
deleted file mode 100644
index 21e60e85e..000000000
--- a/infra/cifuzz/third_party/github_actions_toolkit/http_client/__init__.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""Module for HTTP code.
-Based on https://github.com/actions/http-client/blob/main/index.ts"""
-
-import enum
-
-
-class HTTPCode(enum.Enum):
- """Enum representing meaning of HTTP codes."""
- BAD_REQUEST = 400
- FORBIDDEN = 403
- NOT_FOUND = 404
diff --git a/infra/utils.py b/infra/utils.py
index b11edf19b..f0b58a4da 100644
--- a/infra/utils.py
+++ b/infra/utils.py
@@ -17,6 +17,7 @@ import logging
import os
import posixpath
import re
+import shlex
import stat
import subprocess
import sys
@@ -39,17 +40,25 @@ def chdir_to_root():
os.chdir(helper.OSS_FUZZ_DIR)
+def command_to_string(command):
+ """Returns the stringfied version of |command| a list representing a binary to
+ run and arguments to pass to it or a string representing a binary to run."""
+ if isinstance(command, str):
+ return command
+ return shlex.join(command)
+
+
def execute(command, env=None, location=None, check_result=False):
"""Runs a shell command in the specified directory location.
Args:
command: The command as a list to be run.
env: (optional) an environment to pass to Popen to run the command in.
- location: The directory the command is run in.
- check_result: Should an exception be thrown on failed command.
+ location (optional): The directory to run command in.
+ check_result (optional): Should an exception be thrown on failure.
Returns:
- stdout, stderr, return code.
+ stdout, stderr, returncode.
Raises:
RuntimeError: running a command resulted in an error.
@@ -65,17 +74,18 @@ def execute(command, env=None, location=None, check_result=False):
out, err = process.communicate()
out = out.decode('utf-8', errors='ignore')
err = err.decode('utf-8', errors='ignore')
+
+ command_str = command_to_string(command)
if err:
- logging.debug('Stderr of command \'%s\' is %s.', ' '.join(command), err)
+ logging.debug('Stderr of command "%s" is: %s.', command_str, err)
if check_result and process.returncode:
- raise RuntimeError(
- 'Executing command \'{0}\' failed with error: {1}.'.format(
- ' '.join(command), err))
+ raise RuntimeError('Executing command "{0}" failed with error: {1}.'.format(
+ command_str, err))
return out, err, process.returncode
def get_fuzz_targets(path, top_level_only=False):
- """Get list of fuzz targets in a directory.
+ """Gets fuzz targets in a directory.
Args:
path: A path to search for fuzz targets in.
@@ -148,7 +158,7 @@ def is_fuzz_target_local(file_path):
def binary_print(string):
- """Print that can print a binary string."""
+ """Prints string. Can print a binary string."""
if isinstance(string, bytes):
string += b'\n'
else:
diff --git a/infra/utils_test.py b/infra/utils_test.py
index a57e68e1f..9b7fbc903 100644
--- a/infra/utils_test.py
+++ b/infra/utils_test.py
@@ -131,5 +131,21 @@ class BinaryPrintTest(unittest.TestCase):
mock_write.assert_called_with(b'hello\n')
+class CommandToStringTest(unittest.TestCase):
+ """Tests for command_to_string."""
+
+ def test_string(self):
+ """Tests that command_to_string returns the argument passed to it when it is
+ passed a string."""
+ command = 'command'
+ self.assertEqual(utils.command_to_string(command), command)
+
+ def test_list(self):
+ """Tests that command_to_string returns the correct stringwhen it is passed
+ a list."""
+ command = ['command', 'arg1', 'arg2']
+ self.assertEqual(utils.command_to_string(command), 'command arg1 arg2')
+
+
if __name__ == '__main__':
unittest.main()