Skip to content

Commit 934870e

Browse files
authored
fix(bundle): remap installed workflow step metadata (#1709)
1 parent 9e5b93e commit 934870e

2 files changed

Lines changed: 136 additions & 33 deletions

File tree

inc/Core/Agents/AgentBundler.php

Lines changed: 97 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ public function import( array $bundle, ?string $new_slug = null, int $owner_id =
591591
$pipeline_id_map = array(); // old_id => new_id.
592592
foreach ( $bundle['pipelines'] ?? array() as $pipeline_data ) {
593593
$old_id = (int) ( $pipeline_data['original_id'] ?? 0 );
594+
$pipeline_config = is_array( $pipeline_data['pipeline_config'] ?? null ) ? $pipeline_data['pipeline_config'] : array();
594595
$portable_slug = PortableSlug::normalize(
595596
(string) ( $pipeline_data['portable_slug'] ?? ( $pipeline_data['pipeline_name'] ?? 'pipeline' ) ),
596597
'pipeline'
@@ -617,25 +618,27 @@ public function import( array $bundle, ?string $new_slug = null, int $owner_id =
617618

618619
if ( $existing_pipeline ) {
619620
$new_pipeline_id = (int) $existing_pipeline['pipeline_id'];
620-
$this->pipelines_repo->update_pipeline(
621-
$new_pipeline_id,
622-
array(
623-
'pipeline_name' => $pipeline_data['pipeline_name'],
624-
'pipeline_config' => $pipeline_data['pipeline_config'] ?? array(),
625-
'portable_slug' => $portable_slug,
626-
)
627-
);
628621
} else {
629622
$new_pipeline_id = $this->pipelines_repo->create_pipeline( array(
630623
'pipeline_name' => $pipeline_data['pipeline_name'],
631-
'pipeline_config' => $pipeline_data['pipeline_config'] ?? array(),
624+
'pipeline_config' => $pipeline_config,
632625
'portable_slug' => $portable_slug,
633626
'agent_id' => $agent_id,
634627
'user_id' => $owner_id,
635628
) );
636629
}
637630

638631
if ( $new_pipeline_id ) {
632+
$pipeline_config = $this->remap_pipeline_step_ids( $pipeline_config, $old_id, (int) $new_pipeline_id );
633+
$this->pipelines_repo->update_pipeline(
634+
(int) $new_pipeline_id,
635+
array(
636+
'pipeline_name' => $pipeline_data['pipeline_name'],
637+
'pipeline_config' => $pipeline_config,
638+
'portable_slug' => $portable_slug,
639+
)
640+
);
641+
639642
$pipeline_id_map[ $old_id ] = (int) $new_pipeline_id;
640643
$artifact_records[ $artifact_key ] = $this->bundle_artifact_record(
641644
$bundle_metadata,
@@ -677,12 +680,16 @@ public function import( array $bundle, ?string $new_slug = null, int $owner_id =
677680
$scheduling['interval'] = 'manual';
678681
}
679682

680-
$flow_config = $flow_data['flow_config'] ?? array();
681-
682-
// Remap pipeline step IDs inside flow_config.
683-
$flow_config = $this->remap_flow_step_ids( $flow_config, $old_pipeline_id, $new_pipeline_id );
683+
$flow_config = is_array( $flow_data['flow_config'] ?? null ) ? $flow_data['flow_config'] : array();
684684
$existing_flow = $this->flows_repo->get_by_portable_slug( (int) $new_pipeline_id, $portable_slug );
685-
$flow_payload_source = array_merge( $flow_data, array( 'flow_config' => $flow_config ) );
685+
$flow_payload_source = array_merge(
686+
$flow_data,
687+
array(
688+
'flow_config' => $existing_flow
689+
? $this->remap_flow_step_ids( $flow_config, $old_pipeline_id, (int) $new_pipeline_id, (int) $existing_flow['flow_id'] )
690+
: $flow_config,
691+
)
692+
);
686693
$payload = $this->flow_artifact_payload( $flow_payload_source, $portable_slug );
687694

688695
if (
@@ -702,6 +709,7 @@ public function import( array $bundle, ?string $new_slug = null, int $owner_id =
702709

703710
if ( $existing_flow ) {
704711
$new_flow_id = (int) $existing_flow['flow_id'];
712+
$flow_config = $this->remap_flow_step_ids( $flow_config, $old_pipeline_id, (int) $new_pipeline_id, $new_flow_id );
705713
$flow_config = $this->preserve_runtime_queue_fields( $flow_config, $existing_flow['flow_config'] ?? array() );
706714
$this->flows_repo->update_flow(
707715
$new_flow_id,
@@ -715,12 +723,24 @@ public function import( array $bundle, ?string $new_slug = null, int $owner_id =
715723
$new_flow_id = $this->flows_repo->create_flow( array(
716724
'pipeline_id' => $new_pipeline_id,
717725
'flow_name' => $flow_data['flow_name'],
718-
'flow_config' => $flow_config,
726+
'flow_config' => array(),
719727
'scheduling_config' => $scheduling,
720728
'portable_slug' => $portable_slug,
721729
'agent_id' => $agent_id,
722730
'user_id' => $owner_id,
723731
) );
732+
733+
if ( $new_flow_id ) {
734+
$flow_config = $this->remap_flow_step_ids( $flow_config, $old_pipeline_id, (int) $new_pipeline_id, (int) $new_flow_id );
735+
$this->flows_repo->update_flow(
736+
(int) $new_flow_id,
737+
array(
738+
'flow_name' => $flow_data['flow_name'],
739+
'flow_config' => $flow_config,
740+
'portable_slug' => $portable_slug,
741+
)
742+
);
743+
}
724744
}
725745

726746
if ( $new_flow_id ) {
@@ -1197,38 +1217,82 @@ private function write_flow_memory_files( int $pipeline_id, int $flow_id, array
11971217
}
11981218
}
11991219

1220+
/**
1221+
* Remap pipeline step IDs inside a pipeline config.
1222+
*
1223+
* @param array $pipeline_config Pipeline config.
1224+
* @param int $old_pipeline_id Original pipeline ID.
1225+
* @param int $new_pipeline_id New pipeline ID.
1226+
* @return array Updated pipeline config.
1227+
*/
1228+
private function remap_pipeline_step_ids( array $pipeline_config, int $old_pipeline_id, int $new_pipeline_id ): array {
1229+
$remapped = array();
1230+
1231+
foreach ( $pipeline_config as $pipeline_step_id => $step_config ) {
1232+
$new_pipeline_step_id = $this->remap_step_id_prefix( (string) $pipeline_step_id, $old_pipeline_id, $new_pipeline_id );
1233+
if ( is_array( $step_config ) ) {
1234+
$step_config['pipeline_step_id'] = $new_pipeline_step_id;
1235+
}
1236+
1237+
$remapped[ $new_pipeline_step_id ] = $step_config;
1238+
}
1239+
1240+
return $remapped;
1241+
}
1242+
12001243
/**
12011244
* Remap pipeline step IDs inside a flow config.
12021245
*
1203-
* Pipeline step IDs have the format {pipeline_id}_{uuid}. When importing,
1204-
* the pipeline ID changes, so we need to rewrite these keys.
1246+
* Pipeline step IDs have the format {pipeline_id}_{uuid}. Flow step IDs add
1247+
* the installed flow ID as the final suffix. Bundle-local IDs must be
1248+
* rewritten after install or runtime lookups resolve the wrong pipeline.
12051249
*
1206-
* @param array $flow_config Flow config.
1207-
* @param int $old_pipeline_id Original pipeline ID.
1208-
* @param int $new_pipeline_id New pipeline ID.
1250+
* @param array $flow_config Flow config.
1251+
* @param int $old_pipeline_id Original pipeline ID.
1252+
* @param int $new_pipeline_id New pipeline ID.
1253+
* @param int $new_flow_id New flow ID.
12091254
* @return array Updated flow config.
12101255
*/
1211-
private function remap_flow_step_ids( array $flow_config, int $old_pipeline_id, int $new_pipeline_id ): array {
1212-
if ( $old_pipeline_id === $new_pipeline_id ) {
1213-
return $flow_config;
1214-
}
1215-
1256+
private function remap_flow_step_ids( array $flow_config, int $old_pipeline_id, int $new_pipeline_id, int $new_flow_id ): array {
12161257
$remapped = array();
1217-
$prefix = $old_pipeline_id . '_';
12181258

1219-
foreach ( $flow_config as $key => $value ) {
1220-
// Remap step ID keys that start with old pipeline ID.
1221-
if ( is_string( $key ) && str_starts_with( $key, $prefix ) ) {
1222-
$new_key = $new_pipeline_id . '_' . substr( $key, strlen( $prefix ) );
1223-
$remapped[ $new_key ] = $value;
1224-
} else {
1225-
$remapped[ $key ] = $value;
1259+
foreach ( $flow_config as $flow_step_id => $step_config ) {
1260+
$pipeline_step_id = is_array( $step_config ) && is_string( $step_config['pipeline_step_id'] ?? null )
1261+
? $step_config['pipeline_step_id']
1262+
: preg_replace( '/_\d+$/', '', (string) $flow_step_id );
1263+
$pipeline_step_id = $this->remap_step_id_prefix( (string) $pipeline_step_id, $old_pipeline_id, $new_pipeline_id );
1264+
$new_flow_step_id = $pipeline_step_id . '_' . $new_flow_id;
1265+
1266+
if ( is_array( $step_config ) ) {
1267+
$step_config['pipeline_step_id'] = $pipeline_step_id;
1268+
$step_config['pipeline_id'] = $new_pipeline_id;
1269+
$step_config['flow_id'] = $new_flow_id;
1270+
$step_config['flow_step_id'] = $new_flow_step_id;
12261271
}
1272+
1273+
$remapped[ $new_flow_step_id ] = $step_config;
12271274
}
12281275

12291276
return $remapped;
12301277
}
12311278

1279+
/**
1280+
* Remap the pipeline ID prefix of a step ID.
1281+
*
1282+
* @param string $step_id Step ID.
1283+
* @param int $old_pipeline_id Original pipeline ID.
1284+
* @param int $new_pipeline_id New pipeline ID.
1285+
* @return string Remapped step ID.
1286+
*/
1287+
private function remap_step_id_prefix( string $step_id, int $old_pipeline_id, int $new_pipeline_id ): string {
1288+
$prefix = $old_pipeline_id . '_';
1289+
if ( $old_pipeline_id === $new_pipeline_id || ! str_starts_with( $step_id, $prefix ) ) {
1290+
return $step_id;
1291+
}
1292+
1293+
return $new_pipeline_id . '_' . substr( $step_id, strlen( $prefix ) );
1294+
}
1295+
12321296
/**
12331297
* Serialize a bundle to JSON string.
12341298
*

tests/agent-bundle-portable-update-smoke.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,45 @@ function call_bundle_private( AgentBundler $bundler, string $method, array $args
189189
assert_bundle_update_equals( 'upgrade preserves existing config_patch_queue', 'Local queue head', $preserved['flow-step-1']['config_patch_queue'][0]['patch']['query'] ?? null );
190190
assert_bundle_update_equals( 'upgrade preserves existing queue_mode', 'static', $preserved['flow-step-1']['queue_mode'] ?? null );
191191

192+
$remapped_pipeline = call_bundle_private(
193+
$bundler,
194+
'remap_pipeline_step_ids',
195+
array(
196+
array(
197+
'2_bundle_step_0' => array(
198+
'pipeline_step_id' => '2_bundle_step_0',
199+
'step_type' => 'fetch',
200+
),
201+
),
202+
2,
203+
3,
204+
)
205+
);
206+
assert_bundle_update( 'pipeline step key remaps to installed pipeline ID', isset( $remapped_pipeline['3_bundle_step_0'] ) );
207+
assert_bundle_update_equals( 'pipeline step metadata remaps to installed pipeline ID', '3_bundle_step_0', $remapped_pipeline['3_bundle_step_0']['pipeline_step_id'] ?? null );
208+
209+
$remapped_flow = call_bundle_private(
210+
$bundler,
211+
'remap_flow_step_ids',
212+
array(
213+
array(
214+
'2_bundle_step_0_4' => array(
215+
'flow_step_id' => '2_bundle_step_0_4',
216+
'pipeline_step_id' => '2_bundle_step_0',
217+
'pipeline_id' => 2,
218+
'flow_id' => 4,
219+
),
220+
),
221+
2,
222+
3,
223+
9,
224+
)
225+
);
226+
assert_bundle_update( 'flow step key remaps to installed pipeline and flow IDs', isset( $remapped_flow['3_bundle_step_0_9'] ) );
227+
assert_bundle_update_equals( 'flow step metadata pipeline_step_id remaps', '3_bundle_step_0', $remapped_flow['3_bundle_step_0_9']['pipeline_step_id'] ?? null );
228+
assert_bundle_update_equals( 'flow step metadata pipeline_id remaps', 3, $remapped_flow['3_bundle_step_0_9']['pipeline_id'] ?? null );
229+
assert_bundle_update_equals( 'flow step metadata flow_id remaps', 9, $remapped_flow['3_bundle_step_0_9']['flow_id'] ?? null );
230+
192231
$agent_bundler_source = file_get_contents( dirname( __DIR__ ) . '/inc/Core/Agents/AgentBundler.php' ) ?: '';
193232
$pipelines_source = file_get_contents( dirname( __DIR__ ) . '/inc/Core/Database/Pipelines/Pipelines.php' ) ?: '';
194233
$flows_source = file_get_contents( dirname( __DIR__ ) . '/inc/Core/Database/Flows/Flows.php' ) ?: '';

0 commit comments

Comments
 (0)