@@ -261,6 +261,152 @@ impl CircleCIGenerator {
261261 Ok ( circleci_config)
262262 }
263263
264+ /// Synthesize a minimal setup workflow that generates continuation config and continues the pipeline
265+ pub fn generate_synthesized_setup (
266+ & self ,
267+ config : & Config ,
268+ output_path : & std:: path:: Path ,
269+ ) -> Result < ( ) > {
270+ use crate :: providers:: circleci:: config as cc;
271+ use serde_yaml:: { Mapping , Value } ;
272+
273+ let mut circleci_config = cc:: CircleCIConfig {
274+ setup : Some ( true ) ,
275+ ..Default :: default ( )
276+ } ;
277+
278+ // Include orbs if specified (continuation or others)
279+ if let Some ( orbs) = & config. orbs {
280+ circleci_config. orbs = Some ( orbs. clone ( ) ) ;
281+ }
282+
283+ // Build setup workflow with a single job
284+ let workflow_name = "setup" . to_string ( ) ;
285+ let mut workflow = cc:: CircleCIWorkflow {
286+ when : None ,
287+ unless : None ,
288+ jobs : Vec :: new ( ) ,
289+ } ;
290+
291+ let job_name = "generate_config" . to_string ( ) ;
292+ workflow
293+ . jobs
294+ . push ( cc:: CircleCIWorkflowJob :: Simple ( job_name. clone ( ) ) ) ;
295+ circleci_config. workflows . insert ( workflow_name, workflow) ;
296+
297+ // Construct the job
298+ let mut job = cc:: CircleCIJob {
299+ executor : None ,
300+ docker : Some ( vec ! [ cc:: CircleCIDockerImage {
301+ image: "cimg/ruby:3.3.5" . to_string( ) ,
302+ auth: None ,
303+ name: None ,
304+ entrypoint: None ,
305+ command: None ,
306+ user: None ,
307+ environment: None ,
308+ } ] ) ,
309+ machine : None ,
310+ resource_class : Some ( "small" . to_string ( ) ) ,
311+ working_directory : None ,
312+ parallelism : None ,
313+ environment : None ,
314+ parameters : None ,
315+ steps : Vec :: new ( ) ,
316+ } ;
317+
318+ // Step: checkout
319+ job. steps
320+ . push ( cc:: CircleCIStep :: new ( Value :: String ( "checkout" . to_string ( ) ) ) ) ;
321+
322+ // Optional hook: set_package_versions_hash if present in user commands
323+ if let Some ( cmds) = & config. parameters {
324+ let _ = cmds; // retain for future param-driven hooks
325+ }
326+ // We can't access user commands here; add a safe reference (CircleCI will error if missing)
327+ // To avoid validation errors, only add if config.vars contains a marker (future). Skipping for now.
328+
329+ // Step: run cigen to generate continuation config (.circleci/main.yml)
330+ let mut run_gen = Mapping :: new ( ) ;
331+ let mut run_gen_details = Mapping :: new ( ) ;
332+ run_gen_details. insert (
333+ Value :: String ( "name" . to_string ( ) ) ,
334+ Value :: String ( "Generate continuation config" . to_string ( ) ) ,
335+ ) ;
336+ run_gen_details. insert (
337+ Value :: String ( "command" . to_string ( ) ) ,
338+ Value :: String ( "cigen generate" . to_string ( ) ) ,
339+ ) ;
340+ run_gen. insert (
341+ Value :: String ( "run" . to_string ( ) ) ,
342+ Value :: Mapping ( run_gen_details) ,
343+ ) ;
344+ job. steps
345+ . push ( cc:: CircleCIStep :: new ( Value :: Mapping ( run_gen) ) ) ;
346+
347+ // Step: continue pipeline using jq --rawfile
348+ let mut run_cont = Mapping :: new ( ) ;
349+ let mut run_cont_details = Mapping :: new ( ) ;
350+ run_cont_details. insert (
351+ Value :: String ( "name" . to_string ( ) ) ,
352+ Value :: String ( "Continue Pipeline" . to_string ( ) ) ,
353+ ) ;
354+ let cont_cmd = r#"
355+ if [ -z "${CIRCLE_CONTINUATION_KEY}" ]; then
356+ echo "CIRCLE_CONTINUATION_KEY is required. Make sure setup workflows are enabled."
357+ exit 1
358+ fi
359+
360+ CONFIG_PATH=".circleci/main.yml"
361+ if [ ! -f "$CONFIG_PATH" ]; then
362+ echo "Continuation config not found at $CONFIG_PATH" >&2
363+ exit 1
364+ fi
365+
366+ jq -n \
367+ --arg continuation "$CIRCLE_CONTINUATION_KEY" \
368+ --rawfile config "$CONFIG_PATH" \
369+ '{"continuation-key": $continuation, "configuration": $config}' \
370+ > /tmp/continuation.json
371+
372+ cat /tmp/continuation.json
373+
374+ [[ $(curl \
375+ -o /dev/stderr \
376+ -w '%{http_code}' \
377+ -XPOST \
378+ -H "Content-Type: application/json" \
379+ -H "Accept: application/json" \
380+ --data "@/tmp/continuation.json" \
381+ "https://circleci.com/api/v2/pipeline/continue") -eq 200 ]]
382+ "#
383+ . trim ( )
384+ . to_string ( ) ;
385+ run_cont_details. insert (
386+ Value :: String ( "command" . to_string ( ) ) ,
387+ Value :: String ( cont_cmd) ,
388+ ) ;
389+ run_cont. insert (
390+ Value :: String ( "run" . to_string ( ) ) ,
391+ Value :: Mapping ( run_cont_details) ,
392+ ) ;
393+ job. steps
394+ . push ( cc:: CircleCIStep :: new ( Value :: Mapping ( run_cont) ) ) ;
395+
396+ circleci_config. jobs . insert ( job_name, job) ;
397+
398+ // Write YAML
399+ let yaml_content = serde_yaml:: to_string ( & circleci_config) . into_diagnostic ( ) ?;
400+ let output_file = output_path. join ( "config.yml" ) ;
401+ std:: fs:: create_dir_all ( output_path) . into_diagnostic ( ) ?;
402+ std:: fs:: write ( & output_file, yaml_content) . into_diagnostic ( ) ?;
403+
404+ // Validate setup config
405+ self . validate_config ( & output_file) ?;
406+
407+ Ok ( ( ) )
408+ }
409+
264410 fn build_workflow (
265411 & self ,
266412 _workflow_name : & str ,
0 commit comments