From dd08ea5c328fb2c10c2acaaba178b0b8fb7f75a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kruli=C5=A1?= Date: Thu, 18 Jul 2024 16:32:19 +0200 Subject: [PATCH] Db migration (adding success-exit-codes into exercise and pipeline configs). --- .../Pipeline/Box/BoxService.php | 2 +- .../Pipeline/Box/Boxes/ElfExecutionBox.php | 4 +- migrations/Version20240716161149.php | 267 ++++++++++++++++++ 3 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 migrations/Version20240716161149.php diff --git a/app/helpers/ExerciseConfig/Pipeline/Box/BoxService.php b/app/helpers/ExerciseConfig/Pipeline/Box/BoxService.php index 74072633..5c94472a 100644 --- a/app/helpers/ExerciseConfig/Pipeline/Box/BoxService.php +++ b/app/helpers/ExerciseConfig/Pipeline/Box/BoxService.php @@ -34,7 +34,7 @@ public function __construct() GccCompilationBox::$GCC_TYPE => GccCompilationBox::class, GppCompilationBox::$GPP_TYPE => GppCompilationBox::class, BisonCompilationBox::$BOX_TYPE => BisonCompilationBox::class, - ElfExecutionBox::$ELF_EXEC_TYPE => ElfExecutionBox::class, + ElfExecutionBox::$BOX_TYPE => ElfExecutionBox::class, WrappedExecutionBox::$BOX_TYPE => WrappedExecutionBox::class, FpcCompilationBox::$FPC_TYPE => FpcCompilationBox::class, FetchFilesBox::$FETCH_TYPE => FetchFilesBox::class, diff --git a/app/helpers/ExerciseConfig/Pipeline/Box/Boxes/ElfExecutionBox.php b/app/helpers/ExerciseConfig/Pipeline/Box/Boxes/ElfExecutionBox.php index e7f7814c..be05ef39 100644 --- a/app/helpers/ExerciseConfig/Pipeline/Box/Boxes/ElfExecutionBox.php +++ b/app/helpers/ExerciseConfig/Pipeline/Box/Boxes/ElfExecutionBox.php @@ -15,7 +15,7 @@ class ElfExecutionBox extends ExecutionBox { /** Type key */ - public static $ELF_EXEC_TYPE = "elf-exec"; + public static $BOX_TYPE = "elf-exec"; public static $DEFAULT_NAME = "ELF Execution"; private static $initialized = false; @@ -59,7 +59,7 @@ public function __construct(BoxMeta $meta) */ public function getType(): string { - return self::$ELF_EXEC_TYPE; + return self::$BOX_TYPE; } /** diff --git a/migrations/Version20240716161149.php b/migrations/Version20240716161149.php new file mode 100644 index 00000000..a9133e33 --- /dev/null +++ b/migrations/Version20240716161149.php @@ -0,0 +1,267 @@ +boxes = [ + ElfExecutionBox::$BOX_TYPE => true, + HaskellExecutionBox::$BOX_TYPE => true, + JvmRunBox::$BOX_TYPE => true, + WrappedExecutionBox::$BOX_TYPE => true, + ScriptExecutionBox::$BOX_TYPE => true, + ]; + } + + public function getDescription(): string + { + return ''; + } + + public function up(Schema $schema): void + { + // this up() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE assignment ADD can_view_measured_values TINYINT(1) NOT NULL'); + + $this->addSql('ALTER TABLE assignment_localized_exercise DROP FOREIGN KEY FK_9C8F78CD19302F8'); + $this->addSql('ALTER TABLE assignment_localized_exercise ADD CONSTRAINT FK_9DF069D6D19302F8 FOREIGN KEY (assignment_id) REFERENCES assignment (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE assignment_solution DROP FOREIGN KEY FK_DB055AF3D19302F8'); + $this->addSql('ALTER TABLE assignment_solution DROP FOREIGN KEY FK_DB055AF31C0BE183'); + $this->addSql('ALTER TABLE assignment_solution ADD CONSTRAINT FK_5B315D2ED19302F8 FOREIGN KEY (assignment_id) REFERENCES assignment (id)'); + $this->addSql('ALTER TABLE assignment_solution ADD CONSTRAINT FK_5B315D2E1C0BE183 FOREIGN KEY (solution_id) REFERENCES solution (id)'); + $this->addSql('ALTER TABLE exercise_localized_exercise DROP FOREIGN KEY FK_8327FD4EE934951A'); + $this->addSql('ALTER TABLE exercise_localized_exercise ADD CONSTRAINT FK_98A84F90E934951A FOREIGN KEY (exercise_id) REFERENCES exercise (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE exercise_attachment_file DROP FOREIGN KEY FK_C0EAC68CED6C0B59'); + $this->addSql('ALTER TABLE exercise_attachment_file DROP FOREIGN KEY FK_C0EAC68CE934951A'); + $this->addSql('ALTER TABLE exercise_attachment_file ADD CONSTRAINT FK_24161E21E934951A FOREIGN KEY (exercise_id) REFERENCES exercise (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE exercise_attachment_file ADD CONSTRAINT FK_24161E215B5E2CEA FOREIGN KEY (attachment_file_id) REFERENCES `uploaded_file` (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE localized_exercise DROP FOREIGN KEY FK_7C6ACF3E3EA4CB4D'); + $this->addSql('ALTER TABLE localized_exercise ADD CONSTRAINT FK_BCAD00373EA4CB4D FOREIGN KEY (created_from_id) REFERENCES localized_exercise (id) ON DELETE SET NULL'); + $this->addSql('ALTER TABLE reference_solution_submission DROP FOREIGN KEY FK_62BA741F79F7D87D'); + $this->addSql('ALTER TABLE reference_solution_submission DROP FOREIGN KEY FK_62BA741FA398D0F9'); + $this->addSql('ALTER TABLE reference_solution_submission DROP FOREIGN KEY FK_62BA741F456C5646'); + $this->addSql('ALTER TABLE reference_solution_submission DROP FOREIGN KEY FK_62BA741FFA3CA3B7'); + $this->addSql('ALTER TABLE reference_solution_submission ADD CONSTRAINT FK_AA9C8B99FA3CA3B7 FOREIGN KEY (reference_solution_id) REFERENCES reference_exercise_solution (id)'); + $this->addSql('ALTER TABLE reference_solution_submission ADD CONSTRAINT FK_AA9C8B99A398D0F9 FOREIGN KEY (hw_group_id) REFERENCES hardware_group (id)'); + $this->addSql('ALTER TABLE reference_solution_submission ADD CONSTRAINT FK_AA9C8B9979F7D87D FOREIGN KEY (submitted_by_id) REFERENCES user (id)'); + $this->addSql('ALTER TABLE reference_solution_submission ADD CONSTRAINT FK_AA9C8B99456C5646 FOREIGN KEY (evaluation_id) REFERENCES solution_evaluation (id)'); + } + + public function postUp(Schema $schema): void + { + /* + * scan all pipelines + */ + $pipelines = $this->connection->fetchAllAssociative('SELECT p.id AS id, pc.id AS cid, pc.pipeline_config AS `config` + FROM pipeline AS p JOIN pipeline_config AS pc ON p.pipeline_config_id = pc.id'); + $modifiedPipelines = []; + foreach ($pipelines as $pipeline) { + $id = $pipeline['id']; + $config = Yaml::parse($pipeline['config']); + if (empty($config['boxes'])) { + continue; + } + + // scan all pipeline boxes + foreach ($config['boxes'] as &$box) { + // if this is one of the boxes that needs updating and it still does not have the 'success-exit-codes' + if (array_key_exists($box['type'], $this->boxes) && empty($box['portsIn']['success-exit-codes'])) { + $modifiedPipelines[$id] = true; // mark the pipeline as modified + $box['portsIn']['success-exit-codes'] = [ // add the port + 'type' => 'string[]', + 'value' => 'success-exit-codes', + ]; + } + } + + if (!empty($modifiedPipelines[$id])) { // if the configuration was modified... + // let's make sure we have a variable connected to newly created port... + $secVariable = array_filter($config['variables'], function ($var) { + return $var['name'] === 'success-exit-codes'; + }); + if (!$secVariable) { + // add the variable if missing + $config['variables'][] = [ + 'name' => 'success-exit-codes', + 'type' => 'string[]', + 'value' => '$success-exit-codes', // refers to external var in exercise config + ]; + } + + // ... and save the modified pipeline config + $this->connection->executeQuery( + "UPDATE pipeline_config SET pipeline_config = :config WHERE id = :id", + ['id' => $pipeline['cid'], 'config' => Yaml::dump($config)] + ); + } + } + + /* + * scan all exercise configs + */ + $econfigs = $this->connection->fetchAllKeyValue('SELECT id, `config` FROM exercise_config'); + foreach ($econfigs as $id => $configYaml) { + $config = Yaml::parse($configYaml); + if (empty($config['tests'])) { + continue; + } + + $modified = false; + foreach ($config['tests'] as &$test) { + foreach ($test['environments'] as &$environment) { + foreach ($environment['pipelines'] as &$pipeline) { + // check if the pipeline was modified + if (!$modifiedPipelines[$pipeline['name']]) { + continue; + } + + // ... if so, we must ensure that success-exit-codes variable is in the configuration + $secVariable = array_filter($pipeline['variables'], function ($var) { + return $var['name'] === 'success-exit-codes'; + }); + + if (!$secVariable) { + // add the variable if it is not present + $pipeline['variables'][] = [ + 'name' => 'success-exit-codes', + 'type' => 'string[]', + 'value' => ['0'], // zero exit code is the default success + ]; + $modified = true; + } + } + } + } + + if ($modified) { + // save the changes + $this->connection->executeQuery( + "UPDATE exercise_config SET `config` = :config WHERE id = :id", + ['id' => $id, 'config' => Yaml::dump($config)] + ); + } + } + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE assignment DROP can_view_measured_values'); + $this->addSql('ALTER TABLE assignment_localized_exercise DROP FOREIGN KEY FK_9DF069D6D19302F8'); + $this->addSql('ALTER TABLE assignment_localized_exercise ADD CONSTRAINT FK_9C8F78CD19302F8 FOREIGN KEY (assignment_id) REFERENCES assignment (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE assignment_solution DROP FOREIGN KEY FK_5B315D2ED19302F8'); + $this->addSql('ALTER TABLE assignment_solution DROP FOREIGN KEY FK_5B315D2E1C0BE183'); + $this->addSql('ALTER TABLE assignment_solution ADD CONSTRAINT FK_DB055AF3D19302F8 FOREIGN KEY (assignment_id) REFERENCES assignment (id)'); + $this->addSql('ALTER TABLE assignment_solution ADD CONSTRAINT FK_DB055AF31C0BE183 FOREIGN KEY (solution_id) REFERENCES solution (id)'); + $this->addSql('ALTER TABLE exercise_attachment_file DROP FOREIGN KEY FK_24161E21E934951A'); + $this->addSql('ALTER TABLE exercise_attachment_file DROP FOREIGN KEY FK_24161E215B5E2CEA'); + $this->addSql('ALTER TABLE exercise_attachment_file ADD CONSTRAINT FK_C0EAC68CED6C0B59 FOREIGN KEY (attachment_file_id) REFERENCES uploaded_file (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE exercise_attachment_file ADD CONSTRAINT FK_C0EAC68CE934951A FOREIGN KEY (exercise_id) REFERENCES exercise (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE exercise_localized_exercise DROP FOREIGN KEY FK_98A84F90E934951A'); + $this->addSql('ALTER TABLE exercise_localized_exercise ADD CONSTRAINT FK_8327FD4EE934951A FOREIGN KEY (exercise_id) REFERENCES exercise (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE localized_exercise DROP FOREIGN KEY FK_BCAD00373EA4CB4D'); + $this->addSql('ALTER TABLE localized_exercise ADD CONSTRAINT FK_7C6ACF3E3EA4CB4D FOREIGN KEY (created_from_id) REFERENCES localized_exercise (id) ON DELETE SET NULL'); + $this->addSql('ALTER TABLE reference_solution_submission DROP FOREIGN KEY FK_AA9C8B99FA3CA3B7'); + $this->addSql('ALTER TABLE reference_solution_submission DROP FOREIGN KEY FK_AA9C8B99A398D0F9'); + $this->addSql('ALTER TABLE reference_solution_submission DROP FOREIGN KEY FK_AA9C8B9979F7D87D'); + $this->addSql('ALTER TABLE reference_solution_submission DROP FOREIGN KEY FK_AA9C8B99456C5646'); + $this->addSql('ALTER TABLE reference_solution_submission ADD CONSTRAINT FK_62BA741F79F7D87D FOREIGN KEY (submitted_by_id) REFERENCES user (id)'); + $this->addSql('ALTER TABLE reference_solution_submission ADD CONSTRAINT FK_62BA741FA398D0F9 FOREIGN KEY (hw_group_id) REFERENCES hardware_group (id)'); + $this->addSql('ALTER TABLE reference_solution_submission ADD CONSTRAINT FK_62BA741F456C5646 FOREIGN KEY (evaluation_id) REFERENCES solution_evaluation (id)'); + $this->addSql('ALTER TABLE reference_solution_submission ADD CONSTRAINT FK_62BA741FFA3CA3B7 FOREIGN KEY (reference_solution_id) REFERENCES reference_exercise_solution (id)'); + } + + public function postDown(Schema $schema): void + { + // scan all pipelines + $pipelines = $this->connection->fetchAllAssociative('SELECT p.id AS id, pc.id AS cid, pc.pipeline_config AS `config` + FROM pipeline AS p JOIN pipeline_config AS pc ON p.pipeline_config_id = pc.id'); + $modifiedPipelines = []; + foreach ($pipelines as $pipeline) { + $id = $pipeline['id']; + $config = Yaml::parse($pipeline['config']); + if (empty($config['boxes'])) { + continue; + } + + // scan all pipeline boxes + foreach ($config['boxes'] as &$box) { + // remove success-exit-codes port + if (array_key_exists($box['type'], $this->boxes) && !empty($box['portsIn']['success-exit-codes'])) { + $modifiedPipelines[$id] = true; // mark the pipeline as modified + unset($box['portsIn']['success-exit-codes']); + } + } + + if (!empty($modifiedPipelines[$id])) { // if the configuration was modified... + // let's make sure the corresponding variable is removed as well + $config['variables'] = array_filter($config['variables'], function ($var) { + return $var['name'] !== 'success-exit-codes'; + }); + + // ... and save the modified pipeline config + $this->connection->executeQuery( + "UPDATE pipeline_config SET pipeline_config = :config WHERE id = :id", + ['id' => $pipeline['cid'], 'config' => Yaml::dump($config)] + ); + } + } + + // scan all exercise configs + $econfigs = $this->connection->fetchAllKeyValue('SELECT id, `config` FROM exercise_config'); + foreach ($econfigs as $id => $configYaml) { + $config = Yaml::parse($configYaml); + if (empty($config['tests'])) { + continue; + } + + $modified = false; + foreach ($config['tests'] as &$test) { + foreach ($test['environments'] as &$environment) { + foreach ($environment['pipelines'] as &$pipeline) { + // check if the pipeline was modified + if (!$modifiedPipelines[$pipeline['name']]) { + continue; + } + + // ... if so, we must remove success-exit-codes variable + $variables = array_filter($pipeline['variables'], function ($var) { + return $var['name'] !== 'success-exit-codes'; + }); + if (count($variables) !== count($pipeline['variables'])) { + $pipeline['variables'] = $variables; + $modified = true; + } + } + } + } + + if ($modified) { + // save the changes + $this->connection->executeQuery( + "UPDATE exercise_config SET config = :config WHERE id = :id", + ['id' => $id, 'config' => Yaml::dump($config)] + ); + } + } + } +}