Skip to content

Commit f36a43e

Browse files
authored
feat(x2a): Publish Phase implemented on backend and UI (#2420)
* publish in k8s job - draft * Publish Job works; second module appeneded * Working Publish flow, SSL disable option * default ssl verify true * Tidying up skipSSLVerify variable defaults
1 parent 0c598fb commit f36a43e

19 files changed

Lines changed: 133 additions & 7 deletions

File tree

workspaces/x2a/app-config.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ x2a:
172172
url: ${AAP_URL:-https://aap.example.com}
173173
orgName: ${AAP_ORG_NAME:-MyOrganization}
174174
# Option 1: OAuth token
175-
oauthToken: ${AAP_OAUTH_TOKEN:-your-oauth-token}
175+
# oauthToken: ${AAP_OAUTH_TOKEN:-your-oauth-token}
176176
# Option 2: Username and password
177-
# username: ${AAP_USERNAME}
178-
# password: ${AAP_PASSWORD}
177+
username: ${AAP_USERNAME}
178+
password: ${AAP_PASSWORD}
179+
# skipSSLVerification: false # Set to true for dev/test environments with self-signed certs

workspaces/x2a/plugins/x2a-backend/config.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export interface X2AConfig {
5050
oauthToken?: string;
5151
username?: string;
5252
password?: string;
53+
skipSSLVerification?: boolean;
5354
};
5455
};
5556
}
@@ -217,6 +218,13 @@ export interface Config {
217218
* Password for AAP authentication (alternative to oauthToken)
218219
*/
219220
password?: string;
221+
/**
222+
* Whether to skip SSL certificate verification when connecting to AAP.
223+
* Set to true for dev/test environments with self-signed certs.
224+
* Defaults to false (SSL is verified).
225+
* @visibility backend
226+
*/
227+
skipSSLVerification?: boolean;
220228
};
221229
};
222230
};

workspaces/x2a/plugins/x2a-backend/src/router/collectArtifacts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const artifactSchema = z.object({
5151
'module_migration_plan',
5252
'migrated_sources',
5353
'project_metadata',
54+
'ansible_project',
5455
]),
5556
value: z.string(),
5657
});

workspaces/x2a/plugins/x2a-backend/src/schema/openapi.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,7 @@ components:
682682
- module_migration_plan
683683
- migrated_sources
684684
- project_metadata
685+
- ansible_project
685686

686687
Artifact:
687688
type: object

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/models/ArtifactType.model.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ export type ArtifactType =
2525
| 'migration_plan'
2626
| 'module_migration_plan'
2727
| 'migrated_sources'
28-
| 'project_metadata';
28+
| 'project_metadata'
29+
| 'ansible_project';

workspaces/x2a/plugins/x2a-backend/src/schema/openapi/generated/router.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,8 @@ export const spec = {
960960
"migration_plan",
961961
"module_migration_plan",
962962
"migrated_sources",
963-
"project_metadata"
963+
"project_metadata",
964+
"ansible_project"
964965
]
965966
},
966967
"Artifact": {

workspaces/x2a/plugins/x2a-backend/src/services/JobResourceBuilder.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ describe('JobResourceBuilder', () => {
125125
AAP_CONTROLLER_URL: 'https://aap.example.com',
126126
AAP_ORG_NAME: 'TestOrg',
127127
AAP_OAUTH_TOKEN: 'test-oauth-token',
128+
AAP_VERIFY_SSL: 'true',
128129
});
129130
expect(secret.stringData!.AAP_USERNAME).toBeUndefined();
130131
expect(secret.stringData!.AAP_PASSWORD).toBeUndefined();
@@ -188,6 +189,29 @@ describe('JobResourceBuilder', () => {
188189
});
189190
});
190191

192+
it('should set AAP_VERIFY_SSL to false when skipSSLVerification is true', () => {
193+
mockConfig.credentials.aap!.skipSSLVerification = true;
194+
195+
const secret = JobResourceBuilder.buildProjectSecret(
196+
projectId,
197+
undefined,
198+
mockConfig,
199+
);
200+
201+
expect(secret.stringData!.AAP_VERIFY_SSL).toBe('false');
202+
});
203+
204+
it('should default AAP_VERIFY_SSL to true when skipSSLVerification is not set', () => {
205+
// skipSSLVerification is not set in default mockConfig
206+
const secret = JobResourceBuilder.buildProjectSecret(
207+
projectId,
208+
undefined,
209+
mockConfig,
210+
);
211+
212+
expect(secret.stringData!.AAP_VERIFY_SSL).toBe('true');
213+
});
214+
191215
it('should throw error when no AAP credentials provided', () => {
192216
mockConfig.credentials.aap = undefined;
193217

workspaces/x2a/plugins/x2a-backend/src/services/JobResourceBuilder.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ export class JobResourceBuilder {
9393
aapEnvVars.AAP_PASSWORD = password!;
9494
}
9595

96+
// Resolve SSL verification: skip=false by default (SSL is verified)
97+
const skipSSL = config.credentials.aap?.skipSSLVerification ?? false;
98+
9699
return {
97100
apiVersion: 'v1',
98101
kind: 'Secret',
@@ -124,6 +127,9 @@ export class JobResourceBuilder {
124127

125128
// AAP credentials (from config or user override)
126129
...aapEnvVars,
130+
131+
// AAP SSL verification setting (derived from skipSSLVerification config)
132+
AAP_VERIFY_SSL: String(!skipSSL),
127133
},
128134
};
129135
}

workspaces/x2a/plugins/x2a-backend/templates/x2a-job-script.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,59 @@ case "${PHASE}" in
365365
ARTIFACTS+=("migrated_sources:${PROJECT_DIR}/modules/${MODULE_NAME}/ansible")
366366
;;
367367

368+
publish)
369+
echo "=== Running x2a publish phase ==="
370+
MODULE_NAME_SANITIZED=$(echo "${MODULE_NAME}" | tr ' ' '_')
371+
ROLE_NAME=$(echo "${MODULE_NAME}" | tr ' -' '__')
372+
OUTPUT_DIR="${PROJECT_PATH}/modules/${MODULE_NAME}"
373+
374+
# Verify migrate phase output exists
375+
if [ ! -d "${OUTPUT_DIR}/ansible/roles/${ROLE_NAME}" ]; then
376+
ERROR_MESSAGE="Migrated role not found at ${OUTPUT_DIR}/ansible/roles/${ROLE_NAME} - migrate phase must complete first"
377+
exit 1
378+
fi
379+
380+
# Check if x2a tool is available (required)
381+
if [ ! -d /app ] || [ ! -f /app/app.py ]; then
382+
ERROR_MESSAGE="/app/app.py not found - x2a tool is required"
383+
exit 1
384+
fi
385+
386+
# Step 1: publish-project — assemble Ansible project from migrated role
387+
echo "=== Step 1: Assembling Ansible project ==="
388+
echo "Command: uv run app.py publish-project ${PROJECT_DIR} ${MODULE_NAME}"
389+
390+
# publish-project reads from {project_id}/modules/{module_name}/ansible/roles/{module_name}/
391+
# and writes to {project_id}/ansible-project/
392+
# It operates relative to CWD, so we run from TARGET_BASE
393+
pushd "${TARGET_BASE}"
394+
uv run --project /app /app/app.py publish-project "${PROJECT_DIR}" "${MODULE_NAME}"
395+
popd
396+
397+
# Verify ansible-project was created
398+
ANSIBLE_PROJECT_DIR="${PROJECT_PATH}/ansible-project"
399+
if [ ! -d "${ANSIBLE_PROJECT_DIR}" ]; then
400+
ERROR_MESSAGE="ansible-project directory not created by publish-project"
401+
exit 1
402+
fi
403+
404+
echo ""
405+
echo "=== Ansible project contents ==="
406+
find "${ANSIBLE_PROJECT_DIR}" -type f | head -50
407+
408+
# Step 2: publish-aap — register with AAP and sync
409+
echo ""
410+
echo "=== Step 2: Publishing to AAP ==="
411+
echo "Command: uv run app.py publish-aap --target-repo ${TARGET_REPO_URL} --target-branch ${TARGET_REPO_BRANCH} --project-id ${PROJECT_DIR}"
412+
cd /app
413+
uv run app.py publish-aap \
414+
--target-repo "${TARGET_REPO_URL}" \
415+
--target-branch "${TARGET_REPO_BRANCH}" \
416+
--project-id "${PROJECT_DIR}"
417+
418+
ARTIFACTS+=("ansible_project:${PROJECT_DIR}/ansible-project")
419+
;;
420+
368421
*)
369422
ERROR_MESSAGE="Unknown phase: ${PHASE}"
370423
exit 1

workspaces/x2a/plugins/x2a-common/client/src/schema/openapi/generated/models/ArtifactType.model.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ export type ArtifactType =
2525
| 'migration_plan'
2626
| 'module_migration_plan'
2727
| 'migrated_sources'
28-
| 'project_metadata';
28+
| 'project_metadata'
29+
| 'ansible_project';

0 commit comments

Comments
 (0)