@@ -11,6 +11,9 @@ PUSH_FAILED=""
1111TERMINATED=false
1212COMMIT_ID=" "
1313
14+ # Path where x2a-convertor writes error details on failure
15+ export X2A_ERROR_FILE=" /tmp/x2a-error.txt"
16+
1417# Report job result back to the backend.
1518report_result () {
1619 local status=" $1 "
@@ -74,6 +77,38 @@ sanitize_secrets() {
7477 fi
7578}
7679
80+ # Run an x2a tool command with error reporting.
81+ # On failure, reads the error details file written by x2a-convertor and sets ERROR_MESSAGE.
82+ # On success, clears ERROR_MESSAGE.
83+ # Captured output is stored in X2A_OUTPUT for callers that need to parse it.
84+ # Usage: run_x2a uv run app.py <phase> [args...]
85+ run_x2a () {
86+ rm -f " ${X2A_ERROR_FILE} "
87+
88+ echo " Command: $* "
89+
90+ local tmpfile
91+ tmpfile=$( mktemp)
92+
93+ set +e
94+ " $@ " 2>&1 | tee " ${tmpfile} "
95+ local rc=${PIPESTATUS[0]}
96+ set -e
97+
98+ X2A_OUTPUT=$( cat " ${tmpfile} " )
99+ rm -f " ${tmpfile} "
100+
101+ if [ ${rc} -ne 0 ]; then
102+ ERROR_MESSAGE=" Unexpected error during ${PHASE} phase. See the job log for details."
103+ if [ -f " ${X2A_ERROR_FILE} " ]; then
104+ ERROR_MESSAGE+=" Message: $( cat " ${X2A_ERROR_FILE} " ) "
105+ fi
106+ exit ${rc}
107+ fi
108+
109+ ERROR_MESSAGE=" "
110+ }
111+
77112# Cleanup trap: fires on every exit (success or failure).
78113# Guarantees exactly one report_result call regardless of how the script ends.
79114cleanup () {
@@ -232,10 +267,7 @@ case "${PHASE}" in
232267 # Usage: app.py init [OPTIONS] USER_REQUIREMENTS
233268 # --source-dir DIRECTORY Source directory to analyze
234269 USER_REQ=" ${USER_PROMPT:- Analyze the Chef cookbooks and create a migration plan} "
235- echo " Command: uv run app.py init --source-dir ${SOURCE_BASE} \" ${USER_REQ} \" "
236- ERROR_MESSAGE=" Unexpected error during init phase. See the job log for details."
237- uv run app.py init --source-dir " ${SOURCE_BASE} " " ${USER_REQ} "
238- ERROR_MESSAGE=" "
270+ run_x2a uv run app.py init --source-dir " ${SOURCE_BASE} " " ${USER_REQ} "
239271
240272 # Copy output to target location
241273 # Note: x2a tool writes files to the source directory (--source-dir)
@@ -296,10 +328,7 @@ case "${PHASE}" in
296328 echo " Working directory: $( pwd) "
297329
298330 USER_REQ=" ${USER_PROMPT:- Analyze the module ' ${MODULE_NAME}' for migration to Ansible} "
299- echo " Command: uv run app.py analyze --source-dir ${SOURCE_BASE} \" ${USER_REQ} \" "
300- ERROR_MESSAGE=" Unexpected error during analyze phase. See the job log for details."
301- uv run app.py analyze --source-dir " ${SOURCE_BASE} " " ${USER_REQ} "
302- ERROR_MESSAGE=" "
331+ run_x2a uv run app.py analyze --source-dir " ${SOURCE_BASE} " " ${USER_REQ} "
303332
304333 # Copy output to target location
305334 # Note: x2a tool produces migration-plan-{module_name}.md (spaces replaced with underscores)
@@ -365,15 +394,12 @@ case "${PHASE}" in
365394 echo " Working directory: $( pwd) "
366395
367396 USER_REQ=" ${USER_PROMPT:- Migrate this module to Ansible} "
368- echo " Command: uv run app.py migrate --source-dir ${SOURCE_BASE} --source-technology Chef --high-level-migration-plan ${PROJECT_PATH} /migration-plan.md --module-migration-plan ${OUTPUT_DIR} /migration-plan-${MODULE_NAME_SANITIZED} .md \" ${USER_REQ} \" "
369- ERROR_MESSAGE=" Unexpected error during migrate phase. See the job log for details."
370- uv run app.py migrate \
397+ run_x2a uv run app.py migrate \
371398 --source-dir " ${SOURCE_BASE} " \
372399 --source-technology Chef \
373400 --high-level-migration-plan " ${PROJECT_PATH} /migration-plan.md" \
374401 --module-migration-plan " ${OUTPUT_DIR} /migration-plan-${MODULE_NAME_SANITIZED} .md" \
375402 " ${USER_REQ} "
376- ERROR_MESSAGE=" "
377403
378404 # Copy output to target location
379405 # Note: x2a tool writes to ansible/roles/{module}/ in the source directory
@@ -414,15 +440,11 @@ case "${PHASE}" in
414440
415441 # Step 1: publish-project — assemble Ansible project from migrated role
416442 echo " === Step 1: Assembling Ansible project ==="
417- echo " Command: uv run app.py publish-project ${PROJECT_DIR} ${MODULE_NAME} "
418-
419443 # publish-project reads from {project_id}/modules/{module_name}/ansible/roles/{module_name}/
420444 # and writes to {project_id}/ansible-project/
421445 # It operates relative to CWD, so we run from TARGET_BASE
422446 pushd " ${TARGET_BASE} "
423- ERROR_MESSAGE=" Unexpected error during publish phase (publish-project). See the job log for details."
424- uv run --project /app /app/app.py publish-project " ${PROJECT_DIR} " " ${MODULE_NAME} "
425- ERROR_MESSAGE=" "
447+ run_x2a uv run --project /app /app/app.py publish-project " ${PROJECT_DIR} " " ${MODULE_NAME} "
426448 popd
427449
428450 # Verify ansible-project was created
@@ -439,17 +461,14 @@ case "${PHASE}" in
439461 # Step 2: publish-aap — register with AAP and sync
440462 echo " "
441463 echo " === Step 2: Publishing to AAP ==="
442- echo " Command: uv run app.py publish-aap --target-repo ${TARGET_REPO_URL} --target-branch ${TARGET_REPO_BRANCH} --project-id ${PROJECT_DIR} "
443464 cd /app
444- ERROR_MESSAGE=" Unexpected error during publish phase (publish-aap). See the job log for details."
445- PUBLISH_OUTPUT=$( uv run app.py publish-aap \
465+ run_x2a uv run app.py publish-aap \
446466 --target-repo " ${TARGET_REPO_URL} " \
447467 --target-branch " ${TARGET_REPO_BRANCH} " \
448- --project-id " ${PROJECT_DIR} " 2>&1 | tee /dev/stderr)
449- ERROR_MESSAGE=" "
468+ --project-id " ${PROJECT_DIR} "
450469
451- # Parse AAP project ID from output and construct URL
452- AAP_PROJECT_ID=$( echo " ${PUBLISH_OUTPUT } " | grep -oP ' ID: \K[0-9]+' | tail -1)
470+ # Parse AAP project ID from captured output
471+ AAP_PROJECT_ID=$( echo " ${X2A_OUTPUT } " | grep -oP ' ID: \K[0-9]+' | tail -1)
453472 if [ -n " ${AAP_PROJECT_ID} " ]; then
454473 ARTIFACTS+=(" ansible_project:${AAP_CONTROLLER_URL} /execution/projects/${AAP_PROJECT_ID} /details" )
455474 else
0 commit comments