From 20886e8ddadfabc5b31fafb5edbb9bd9192e5672 Mon Sep 17 00:00:00 2001 From: liu-shaojun Date: Mon, 20 Apr 2026 02:04:20 +0000 Subject: [PATCH 1/2] Fix unsafe pickle deserialization in distributed training workers Replace raw cloudpickle.load() with HMAC-verified SafePickle across all four distributed training backends (MPI, Horovod, PyTorch DDP, TensorFlow). The vulnerability allowed arbitrary code execution via crafted pickle files passed through CLI-controlled paths. Worker scripts deserialized these files without integrity verification, enabling cluster-wide RCE in distributed deployments. Changes: - Upgrade SafePickle to use per-session random HMAC-SHA256 keys instead of the hardcoded b'shared-key' with SHA1 - SafePickle.dump() now auto-generates .sig sidecar files for integrity - SafePickle.load() mandates signature verification, rejecting unsigned or tampered pickle files - Use timing-safe hmac.compare_digest() to prevent timing attacks - Propagate HMAC key to MPI workers via mpiexec -genv and to subprocesses via inherited environment - SCP .sig files alongside .pkl files for remote MPI nodes Affected files: - mpi_train.py, mpi_estimator.py, mpi_runner.py (MPI training) - horovod_worker.py, multiprocs_backend.py (Horovod) - worker.py, ddp_subprocess.py (PyTorch DDP) - subprocess_worker.py, backend.py (TensorFlow) - safepickle.py in nano, ppml, and friesian packages Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/bigdl/friesian/utils/safepickle.py | 75 ++++++++++++----- .../bigdl/nano/deps/horovod/horovod_worker.py | 8 +- .../nano/pytorch/strategies/ddp_subprocess.py | 6 +- .../bigdl/nano/pytorch/strategies/worker.py | 4 +- .../src/bigdl/nano/utils/common/safepickle.py | 75 ++++++++++++----- .../nano/src/bigdl/nano/utils/tf/backend.py | 8 +- .../bigdl/nano/utils/tf/subprocess_worker.py | 8 +- .../src/bigdl/orca/learn/mpi/mpi_estimator.py | 15 ++-- .../src/bigdl/orca/learn/mpi/mpi_runner.py | 4 + .../src/bigdl/orca/learn/mpi/mpi_train.py | 6 +- .../ppml/src/bigdl/ppml/utils/safepickle.py | 83 +++++++++++++------ 11 files changed, 196 insertions(+), 96 deletions(-) diff --git a/python/friesian/src/bigdl/friesian/utils/safepickle.py b/python/friesian/src/bigdl/friesian/utils/safepickle.py index cb9c17d46c6..98aa7176a47 100644 --- a/python/friesian/src/bigdl/friesian/utils/safepickle.py +++ b/python/friesian/src/bigdl/friesian/utils/safepickle.py @@ -14,6 +14,7 @@ # limitations under the License. # +import os import pickle import hmac import hashlib @@ -24,31 +25,61 @@ class SafePickle: - key = b'shared-key' - """ - Example: - >>> from bigdl.friesian.utils import SafePickle - >>> with open(file_path, 'wb') as file: - >>> signature = SafePickle.dump(data, file, return_digest=True) - >>> with open(file_path, 'rb') as file: - >>> data = SafePickle.load(file, signature) - """ + _key = None + + @classmethod + def _get_key(cls): + if cls._key is None: + env_key = os.environ.get('BIGDL_SAFE_PICKLE_KEY') + if env_key: + cls._key = bytes.fromhex(env_key) + else: + cls._key = os.urandom(32) + os.environ['BIGDL_SAFE_PICKLE_KEY'] = cls._key.hex() + return cls._key + @classmethod - def dump(self, obj, file, return_digest=False, *args, **kwargs): + def dump(cls, obj, file, return_digest=False, *args, **kwargs): + """ + Example: + >>> from bigdl.friesian.utils import SafePickle + >>> with open(file_path, 'wb') as file: + >>> SafePickle.dump(data, file) + """ + pickled_data = pickle.dumps(obj) + file.write(pickled_data) + digest = hmac.new(cls._get_key(), pickled_data, hashlib.sha256).hexdigest() if return_digest: - pickled_data = pickle.dumps(obj) - file.write(pickled_data) - digest = hmac.new(self.key, pickled_data, hashlib.sha1).hexdigest() return digest - else: - pickle.dump(obj, file, *args, **kwargs) + sig_path = file.name + '.sig' + with open(sig_path, 'w') as sig_file: + sig_file.write(digest) @classmethod - def load(self, file, digest=None, *args, **kwargs): + def load(cls, file, digest=None, *args, **kwargs): + """ + Example: + >>> from bigdl.friesian.utils import SafePickle + >>> with open(file_path, 'rb') as file: + >>> data = SafePickle.load(file) + """ + content = file.read() + + if digest is None: + sig_path = file.name + '.sig' + if os.path.exists(sig_path): + with open(sig_path, 'r') as sig_file: + digest = sig_file.read().strip() + if digest: - content = file.read() - new_digest = hmac.new(self.key, content, hashlib.sha1).hexdigest() - if digest != new_digest: - invalidInputError(False, 'Pickle safe check failed') - file.seek(0) - return pickle.load(file, *args, **kwargs) + new_digest = hmac.new(cls._get_key(), content, hashlib.sha256).hexdigest() + if not hmac.compare_digest(digest, new_digest): + invalidInputError(False, + 'Pickle integrity check failed: ' + 'file may have been tampered with') + else: + invalidInputError(False, + 'No HMAC signature found for pickle file: ' + 'cannot verify integrity') + + return pickle.loads(content, *args, **kwargs) diff --git a/python/nano/src/bigdl/nano/deps/horovod/horovod_worker.py b/python/nano/src/bigdl/nano/deps/horovod/horovod_worker.py index 07e7e6251d9..aeb25d5ba2b 100644 --- a/python/nano/src/bigdl/nano/deps/horovod/horovod_worker.py +++ b/python/nano/src/bigdl/nano/deps/horovod/horovod_worker.py @@ -15,18 +15,18 @@ # import os -import cloudpickle import sys +from bigdl.nano.utils.common import SafePickle if __name__ == '__main__': temp_dir = sys.argv[1] with open(os.path.join(temp_dir, "args.pkl"), 'rb') as f: - args = cloudpickle.load(f) + args = SafePickle.load(f) with open(os.path.join(temp_dir, "target.pkl"), 'rb') as f: - target = cloudpickle.load(f) + target = SafePickle.load(f) import horovod.tensorflow.keras as hvd hvd.init() @@ -36,4 +36,4 @@ with open(os.path.join(temp_dir, f"history_{idx}"), "wb") as f: - cloudpickle.dump(history, f) + SafePickle.dump(history, f) diff --git a/python/nano/src/bigdl/nano/pytorch/strategies/ddp_subprocess.py b/python/nano/src/bigdl/nano/pytorch/strategies/ddp_subprocess.py index 5bbb4e63a0b..7688890b1a5 100644 --- a/python/nano/src/bigdl/nano/pytorch/strategies/ddp_subprocess.py +++ b/python/nano/src/bigdl/nano/pytorch/strategies/ddp_subprocess.py @@ -31,7 +31,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import cloudpickle import os import multiprocessing import subprocess @@ -46,6 +45,7 @@ from bigdl.nano.pytorch.strategies.ddp_spawn import DDPSpawnStrategy, _DDPSpawnLauncher from bigdl.nano.utils.common import schedule_processors from bigdl.nano.utils.common import invalidInputError +from bigdl.nano.utils.common import SafePickle from bigdl.nano.pytorch.dispatcher import _get_patch_status import logging @@ -100,9 +100,9 @@ def launch(self, function: Callable, *args: Any, # `self._wrapping_function`. with open(os.path.join(temp_dir, "args.pkl"), "wb") as f: if trainer is not None: - cloudpickle.dump((None, args, error_queue), f) + SafePickle.dump((None, args, error_queue), f) else: - cloudpickle.dump((self._wrapping_function, args, error_queue), f) + SafePickle.dump((self._wrapping_function, args, error_queue), f) # we also need to pass sys.path to subprocess with open(os.path.join(temp_dir, "sys_path.json"), "w") as f: diff --git a/python/nano/src/bigdl/nano/pytorch/strategies/worker.py b/python/nano/src/bigdl/nano/pytorch/strategies/worker.py index 03ba06c52b9..d7506cf541a 100644 --- a/python/nano/src/bigdl/nano/pytorch/strategies/worker.py +++ b/python/nano/src/bigdl/nano/pytorch/strategies/worker.py @@ -18,10 +18,10 @@ import sys import json -import cloudpickle import multiprocessing from torch.multiprocessing.spawn import _wrap from bigdl.nano.pytorch.dispatcher import patch_torch +from bigdl.nano.utils.common import SafePickle if __name__ == '__main__': @@ -44,7 +44,7 @@ patch_torch(cuda_to_cpu=patch_status['patch_cuda']) with open(os.path.join(temp_dir, "args.pkl"), "rb") as f: - (fn, args, error_queue) = cloudpickle.load(f) + (fn, args, error_queue) = SafePickle.load(f) # args[0] is `trainer`, when it is None, it means when are using LightningLite, # otherwise we are using trainer, for the details here, see `ddp_subprocess.py` diff --git a/python/nano/src/bigdl/nano/utils/common/safepickle.py b/python/nano/src/bigdl/nano/utils/common/safepickle.py index 5843fa655b9..15186cd30ec 100644 --- a/python/nano/src/bigdl/nano/utils/common/safepickle.py +++ b/python/nano/src/bigdl/nano/utils/common/safepickle.py @@ -14,6 +14,7 @@ # limitations under the License. # +import os import pickle import hmac import hashlib @@ -24,31 +25,61 @@ class SafePickle: - key = b'shared-key' - """ - Example: - >>> from bigdl.nano.utils.common import SafePickle - >>> with open(file_path, 'wb') as file: - >>> signature = SafePickle.dump(data, file, return_digest=True) - >>> with open(file_path, 'rb') as file: - >>> data = SafePickle.load(file, signature) - """ + _key = None + + @classmethod + def _get_key(cls): + if cls._key is None: + env_key = os.environ.get('BIGDL_SAFE_PICKLE_KEY') + if env_key: + cls._key = bytes.fromhex(env_key) + else: + cls._key = os.urandom(32) + os.environ['BIGDL_SAFE_PICKLE_KEY'] = cls._key.hex() + return cls._key + @classmethod - def dump(self, obj, file, return_digest=False, *args, **kwargs): + def dump(cls, obj, file, return_digest=False, *args, **kwargs): + """ + Example: + >>> from bigdl.nano.utils.common import SafePickle + >>> with open(file_path, 'wb') as file: + >>> SafePickle.dump(data, file) + """ + pickled_data = pickle.dumps(obj) + file.write(pickled_data) + digest = hmac.new(cls._get_key(), pickled_data, hashlib.sha256).hexdigest() if return_digest: - pickled_data = pickle.dumps(obj) - file.write(pickled_data) - digest = hmac.new(self.key, pickled_data, hashlib.sha1).hexdigest() return digest - else: - pickle.dump(obj, file, *args, **kwargs) + sig_path = file.name + '.sig' + with open(sig_path, 'w') as sig_file: + sig_file.write(digest) @classmethod - def load(self, file, digest=None, *args, **kwargs): + def load(cls, file, digest=None, *args, **kwargs): + """ + Example: + >>> from bigdl.nano.utils.common import SafePickle + >>> with open(file_path, 'rb') as file: + >>> data = SafePickle.load(file) + """ + content = file.read() + + if digest is None: + sig_path = file.name + '.sig' + if os.path.exists(sig_path): + with open(sig_path, 'r') as sig_file: + digest = sig_file.read().strip() + if digest: - content = file.read() - new_digest = hmac.new(self.key, content, hashlib.sha1).hexdigest() - if digest != new_digest: - invalidInputError(False, 'Pickle safe check failed') - file.seek(0) - return pickle.load(file, *args, **kwargs) + new_digest = hmac.new(cls._get_key(), content, hashlib.sha256).hexdigest() + if not hmac.compare_digest(digest, new_digest): + invalidInputError(False, + 'Pickle integrity check failed: ' + 'file may have been tampered with') + else: + invalidInputError(False, + 'No HMAC signature found for pickle file: ' + 'cannot verify integrity') + + return pickle.loads(content, *args, **kwargs) diff --git a/python/nano/src/bigdl/nano/utils/tf/backend.py b/python/nano/src/bigdl/nano/utils/tf/backend.py index f79633fb872..21878cf57aa 100644 --- a/python/nano/src/bigdl/nano/utils/tf/backend.py +++ b/python/nano/src/bigdl/nano/utils/tf/backend.py @@ -43,15 +43,15 @@ def run(self, target, args=..., nprocs=1, envs=None) -> Any: return self.run_subprocess(target, args=args, nprocs=nprocs, envs=envs) def run_subprocess(self, target, args=..., nprocs=1, envs=None) -> Any: - import cloudpickle + from bigdl.nano.utils.common import SafePickle import subprocess import sys with TemporaryDirectory() as temp_dir: with open(os.path.join(temp_dir, "args.pkl"), 'wb') as f: - cloudpickle.dump(args, f) + SafePickle.dump(args, f) with open(os.path.join(temp_dir, "target.pkl"), 'wb') as f: - cloudpickle.dump(target, f) + SafePickle.dump(target, f) ex_list = [] cwd_path = os.path.dirname(__file__) @@ -72,5 +72,5 @@ def run_subprocess(self, target, args=..., nprocs=1, envs=None) -> Any: results = [] for i in range(nprocs): with open(os.path.join(temp_dir, f"history_{i}"), "rb") as f: - results.append(cloudpickle.load(f)) + results.append(SafePickle.load(f)) return results diff --git a/python/nano/src/bigdl/nano/utils/tf/subprocess_worker.py b/python/nano/src/bigdl/nano/utils/tf/subprocess_worker.py index fa20eb54b8c..7286ea7d98f 100644 --- a/python/nano/src/bigdl/nano/utils/tf/subprocess_worker.py +++ b/python/nano/src/bigdl/nano/utils/tf/subprocess_worker.py @@ -16,9 +16,9 @@ import json import os -import cloudpickle import sys import tensorflow as tf +from bigdl.nano.utils.common import SafePickle if __name__ == '__main__': # Set number of threads in subprocess @@ -27,14 +27,14 @@ temp_dir = sys.argv[1] with open(os.path.join(temp_dir, "args.pkl"), 'rb') as f: - args = cloudpickle.load(f) + args = SafePickle.load(f) with open(os.path.join(temp_dir, "target.pkl"), 'rb') as f: - target = cloudpickle.load(f) + target = SafePickle.load(f) history = target(*args) tf_config = json.loads(os.environ["TF_CONFIG"]) with open(os.path.join(temp_dir, f"history_{tf_config['task']['index']}"), "wb") as f: - cloudpickle.dump(history, f) + SafePickle.dump(history, f) diff --git a/python/orca/src/bigdl/orca/learn/mpi/mpi_estimator.py b/python/orca/src/bigdl/orca/learn/mpi/mpi_estimator.py index 4a6127eb2f0..2870f38f0c5 100644 --- a/python/orca/src/bigdl/orca/learn/mpi/mpi_estimator.py +++ b/python/orca/src/bigdl/orca/learn/mpi/mpi_estimator.py @@ -18,13 +18,13 @@ import subprocess import types -import cloudpickle from pyspark.rdd import RDD from pyspark.sql import DataFrame from torch.utils.data import Dataset, DataLoader from bigdl.dllib.utils.log4Error import * from bigdl.dllib.utils.utils import get_node_ip +from bigdl.nano.utils.common import SafePickle from bigdl.orca.learn.mpi.mpi_runner import MPIRunner from bigdl.orca.learn.mpi.utils import * @@ -69,11 +69,13 @@ def __init__(self, :param env: Special environment should be passed to MPI environment. """ self.dir = os.getcwd() + SafePickle._get_key() self.mpi_runner = MPIRunner(hosts=hosts, processes_per_node=workers_per_node, env=env) with open("saved_mpi_estimator.pkl", "wb") as f: - cloudpickle.dump((model_creator, optimizer_creator, loss_creator, metrics, - scheduler_creator, config, init_func), f) + SafePickle.dump((model_creator, optimizer_creator, loss_creator, metrics, + scheduler_creator, config, init_func), f) self.mpi_runner.scp_file("saved_mpi_estimator.pkl", self.dir) + self.mpi_runner.scp_file("saved_mpi_estimator.pkl.sig", self.dir) # Need to put mpi_train.py in the current directory so that the PYTHONPATH is the same. train_file = os.path.abspath(__file__ + "/../mpi_train.py") p = subprocess.Popen(["cp", train_file, self.dir]) @@ -196,10 +198,11 @@ def fit(self, data, epochs=1, batch_size=32, validation_data=None, validate_batc validate_func = validate with open("mpi_train_data.pkl", "wb") as f: - cloudpickle.dump((data_creator, epochs, batch_size, validation_data_creator, - validate_batch_size, train_func, validate_func, train_batches, - validate_batches, validate_steps), f) + SafePickle.dump((data_creator, epochs, batch_size, validation_data_creator, + validate_batch_size, train_func, validate_func, train_batches, + validate_batches, validate_steps), f) self.mpi_runner.scp_file("mpi_train_data.pkl", self.dir) + self.mpi_runner.scp_file("mpi_train_data.pkl.sig", self.dir) self.mpi_runner.run("{}/mpi_train.py".format(self.dir), mpi_options=mpi_options, pkl_path=self.dir) diff --git a/python/orca/src/bigdl/orca/learn/mpi/mpi_runner.py b/python/orca/src/bigdl/orca/learn/mpi/mpi_runner.py index fda343da1a6..6fb86e6a39a 100644 --- a/python/orca/src/bigdl/orca/learn/mpi/mpi_runner.py +++ b/python/orca/src/bigdl/orca/learn/mpi/mpi_runner.py @@ -85,6 +85,10 @@ def run(self, file, mpi_options=None, **kwargs): invalidInputError(str.isdigit(mpi_env["OMP_NUM_THREADS"]), "OMP_NUM_THREADS must be a positive integer") mpi_config.extend(["-genv", "OMP_NUM_THREADS={}".format(mpi_env["OMP_NUM_THREADS"])]) + if "BIGDL_SAFE_PICKLE_KEY" in mpi_env: + mpi_config.extend(["-genv", + "BIGDL_SAFE_PICKLE_KEY={}".format( + mpi_env["BIGDL_SAFE_PICKLE_KEY"])]) if len(self.remote_hosts) > 0: mpi_config.extend(["-hosts", ",".join(self.hosts)]) if mpi_options: diff --git a/python/orca/src/bigdl/orca/learn/mpi/mpi_train.py b/python/orca/src/bigdl/orca/learn/mpi/mpi_train.py index b1824339e00..6e31212bc4a 100644 --- a/python/orca/src/bigdl/orca/learn/mpi/mpi_train.py +++ b/python/orca/src/bigdl/orca/learn/mpi/mpi_train.py @@ -16,7 +16,7 @@ import os import argparse -import cloudpickle +from bigdl.nano.utils.common import SafePickle from bigdl.dllib.utils.utils import get_node_ip print(f"Worker on {get_node_ip()} with global rank {os.environ.get('PMI_RANK', 0)}") @@ -29,12 +29,12 @@ with open("{}/saved_mpi_estimator.pkl".format(pkl_path), "rb") as f: model_creator, optimizer_creator, loss_creator, metrics, \ - scheduler_creator, config, init_func = cloudpickle.load(f) + scheduler_creator, config, init_func = SafePickle.load(f) with open("{}/mpi_train_data.pkl".format(pkl_path), "rb") as f: train_data_creator, epochs, batch_size, validation_data_creator,\ validate_batch_size, train_func, validate_func, train_batches, \ - validate_batches, validate_steps = cloudpickle.load(f) + validate_batches, validate_steps = SafePickle.load(f) config["batch_size"] = batch_size config["validate_batch_size"] = validate_batch_size diff --git a/python/ppml/src/bigdl/ppml/utils/safepickle.py b/python/ppml/src/bigdl/ppml/utils/safepickle.py index b40fe58006d..c6edc0cba78 100644 --- a/python/ppml/src/bigdl/ppml/utils/safepickle.py +++ b/python/ppml/src/bigdl/ppml/utils/safepickle.py @@ -14,6 +14,7 @@ # limitations under the License. # +import os import pickle import hmac import hashlib @@ -24,39 +25,69 @@ class SafePickle: - key = b'shared-key' - """ - Example: - >>> from bigdl.ppml.utils.safepickle import SafePickle - >>> with open(file_path, 'wb') as file: - >>> signature = SafePickle.dump(data, file, return_digest=True) - >>> with open(file_path, 'rb') as file: - >>> data = SafePickle.load(file, signature) - """ + _key = None + + @classmethod + def _get_key(cls): + if cls._key is None: + env_key = os.environ.get('BIGDL_SAFE_PICKLE_KEY') + if env_key: + cls._key = bytes.fromhex(env_key) + else: + cls._key = os.urandom(32) + os.environ['BIGDL_SAFE_PICKLE_KEY'] = cls._key.hex() + return cls._key + @classmethod - def dump(self, obj, file, return_digest=False, *args, **kwargs): + def dump(cls, obj, file, return_digest=False, *args, **kwargs): + """ + Example: + >>> from bigdl.ppml.utils.safepickle import SafePickle + >>> with open(file_path, 'wb') as file: + >>> SafePickle.dump(data, file) + """ + pickled_data = pickle.dumps(obj) + file.write(pickled_data) + digest = hmac.new(cls._get_key(), pickled_data, hashlib.sha256).hexdigest() if return_digest: - pickled_data = pickle.dumps(obj) - file.write(pickled_data) - digest = hmac.new(self.key, pickled_data, hashlib.sha1).hexdigest() return digest - else: - pickle.dump(obj, file, *args, **kwargs) + sig_path = file.name + '.sig' + with open(sig_path, 'w') as sig_file: + sig_file.write(digest) @classmethod - def load(self, file, digest=None, *args, **kwargs): + def load(cls, file, digest=None, *args, **kwargs): + """ + Example: + >>> from bigdl.ppml.utils.safepickle import SafePickle + >>> with open(file_path, 'rb') as file: + >>> data = SafePickle.load(file) + """ + content = file.read() + + if digest is None: + sig_path = file.name + '.sig' + if os.path.exists(sig_path): + with open(sig_path, 'r') as sig_file: + digest = sig_file.read().strip() + if digest: - content = file.read() - new_digest = hmac.new(self.key, content, hashlib.sha1).hexdigest() - if digest != new_digest: - invalidInputError(False, 'Pickle safe check failed') - file.seek(0) - return pickle.load(file, *args, **kwargs) - + new_digest = hmac.new(cls._get_key(), content, hashlib.sha256).hexdigest() + if not hmac.compare_digest(digest, new_digest): + invalidInputError(False, + 'Pickle integrity check failed: ' + 'file may have been tampered with') + else: + invalidInputError(False, + 'No HMAC signature found for pickle file: ' + 'cannot verify integrity') + + return pickle.loads(content, *args, **kwargs) + @classmethod - def dumps(self, obj, *args, **kwargs): + def dumps(cls, obj, *args, **kwargs): return pickle.dumps(obj, *args, **kwargs) - + @classmethod - def loads(self, data, *args, **kwargs): + def loads(cls, data, *args, **kwargs): return pickle.loads(data, *args, **kwargs) From e021332fab76aa5a62f76e12541a7422bb120aaf Mon Sep 17 00:00:00 2001 From: liu-shaojun Date: Mon, 20 Apr 2026 02:07:59 +0000 Subject: [PATCH 2/2] Fix CI workflow allowing unauthorized code execution on self-hosted runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The performance-regression-test workflow had two vulnerabilities that allowed any GitHub user to execute arbitrary code on Intel's self-hosted CI runner (Rohan): 1. The issue_comment trigger had no author_association check, so anyone could comment 'APRT' on any PR to trigger benchmark execution of fork PR code on the self-hosted runner. 2. The pull_request trigger caused the workflow to run the PR author's version of the workflow file on the self-hosted runner, giving full control over the execution environment. Fix: - Remove the pull_request trigger entirely — it allowed fork PRs to override the workflow definition itself - Add author_association guard requiring MEMBER, OWNER, or COLLABORATOR status to trigger via issue_comment - Simplify the job-level condition to only handle the comment-based flow Co-Authored-By: Claude Opus 4.6 (1M context) --- .../workflows/performance-regression-test.yml | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/performance-regression-test.yml b/.github/workflows/performance-regression-test.yml index bfa38df3134..c570371a846 100644 --- a/.github/workflows/performance-regression-test.yml +++ b/.github/workflows/performance-regression-test.yml @@ -8,24 +8,25 @@ on: # - cron: '0 16 * * *' issue_comment: types: [created] - pull_request: - branches: [ main ] - paths: - - '.github/workflows/performance-regression-test.yml' jobs: performace-regression-test: runs-on: [Rohan, ubuntu-20.04-lts] - if: github.event.pull_request || github.event.schedule || github.event.issue.pull_request && github.event.comment.body=='APRT' + if: >- + github.event.issue.pull_request + && github.event.comment.body == 'APRT' + && ( + github.event.comment.author_association == 'MEMBER' + || github.event.comment.author_association == 'OWNER' + || github.event.comment.author_association == 'COLLABORATOR' + ) steps: - # trigged by opening a PR which modifies this file or scheduling or commenting 'APRT' in a closed PR - - name: checkout (open pr or schedule or closed pr comments) - if: github.event.pull_request || github.event.schedule || github.event.issue.pull_request.merged_at + - name: checkout (merged pr comments) + if: github.event.issue.pull_request.merged_at uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3 - # trigged by commenting 'APRT' in an opening PR - - name: checkout (opening pr comments) - if: github.event.issue.pull_request && !github.event.issue.pull_request.merged_at + - name: checkout (open pr comments) + if: "!github.event.issue.pull_request.merged_at" uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # actions/checkout@v3 with: ref: ${{ format('refs/pull/{0}/merge', github.event.issue.number) }} @@ -62,5 +63,5 @@ jobs: COMMENT_URL: ${{ github.event.comment.html_url }} JOB_URL: https://github.com/intel-analytics/BigDL/actions/runs/${{ github.run_id }} ANALYTICS_ZOO_ROOT: ${{ github.workspace }} - IS_PR: ${{ github.event.pull_request != null || github.event.issue.pull_request != null }} + IS_PR: ${{ github.event.issue.pull_request != null }} IS_COMMENTS: ${{ github.event.issue.pull_request != null }}