@@ -369,304 +369,3 @@ jobs:
369369 exit 1
370370 fi
371371 done
372-
373- publish-wheel : # to internal Artifactory
374- runs-on : [self-hosted, linux]
375- needs :
376- - build-ubuntu
377- - test-cloudxr
378- - test-teleop-ros2
379- outputs :
380- wheel_paths : ${{ steps.upload-artifactory.outputs.wheel_paths }}
381- environment : dev
382-
383- # Publish only for PRs wihtin the canonical repository after build and CloudXR tests succeed
384- if : >-
385- ${{
386- github.repository == 'NVIDIA/IsaacTeleop'
387- && needs.build-ubuntu.result == 'success'
388- && needs.test-cloudxr.result == 'success'
389- && needs.test-teleop-ros2.result == 'success'
390- }}
391-
392- steps :
393- - name : Set up Python
394- uses : actions/setup-python@v6
395- with :
396- python-version : ' 3.11'
397-
398- - name : Ensure clean wheels directory
399- run : |
400- rm -rf wheels
401-
402- - name : Download wheel artifacts
403- uses : actions/download-artifact@v7
404- with :
405- pattern : isaacteleop-wheels-*
406- merge-multiple : true
407- path : wheels
408-
409- - name : Upload wheel(s) to Artifactory
410- id : upload-artifactory
411- env :
412- ARTIFACTORY_URL : ${{ secrets.ARTIFACTORY_URL }}
413- ARTIFACTORY_REPO : ${{ secrets.ARTIFACTORY_REPO }}
414- ARTIFACTORY_USERNAME : ${{ secrets.ARTIFACTORY_USERNAME }}
415- ARTIFACTORY_API_KEY : ${{ secrets.ARTIFACTORY_API_KEY }}
416- run : |
417- set -euo pipefail
418-
419- if [[ -z "${ARTIFACTORY_URL}" || -z "${ARTIFACTORY_REPO}" || -z "${ARTIFACTORY_USERNAME}" || -z "${ARTIFACTORY_API_KEY}" ]]; then
420- echo "Missing one or more required secrets: ARTIFACTORY_URL, ARTIFACTORY_REPO, ARTIFACTORY_USERNAME, ARTIFACTORY_API_KEY"
421- exit 1
422- fi
423-
424- if [[ "${ARTIFACTORY_URL}" != https://* ]]; then
425- echo "ARTIFACTORY_URL must use https://"
426- exit 1
427- fi
428-
429- shopt -s nullglob
430- wheels=(wheels/*.whl)
431- if (( ${#wheels[@]} == 0 )); then
432- echo "No wheels found under wheels/*.whl"
433- ls -la wheels || true
434- exit 1
435- fi
436-
437- python -m pip install --upgrade pip twine
438-
439- # Artifactory PyPI repositories use the PyPI API endpoint.
440- repository_url="${ARTIFACTORY_URL%/}/api/pypi/${ARTIFACTORY_REPO}"
441- echo "Publishing ${#wheels[@]} wheel(s)"
442-
443- python -m twine upload \
444- --non-interactive \
445- --repository-url "${repository_url}" \
446- -u "${ARTIFACTORY_USERNAME}" \
447- -p "${ARTIFACTORY_API_KEY}" \
448- "${wheels[@]}"
449-
450- wheel_base="${ARTIFACTORY_URL%/}/${ARTIFACTORY_REPO}"
451- wheel_prefix="${wheel_base}/"
452-
453- wheel_paths=()
454- for wheel in "${wheels[@]}"; do
455- wheel_name="$(basename "${wheel}")"
456- echo "Resolving Artifactory URL for ${wheel_name}"
457-
458- search_json="$(curl --fail-with-body --show-error --silent --location --get --connect-timeout 10 --max-time 60 \
459- -u "${ARTIFACTORY_USERNAME}:${ARTIFACTORY_API_KEY}" \
460- --data-urlencode "name=${wheel_name}" \
461- --data-urlencode "repos=${ARTIFACTORY_REPO}" \
462- "${ARTIFACTORY_URL%/}/api/search/artifact")"
463-
464- result_count="$(jq '.results | length' <<< "${search_json}")"
465- if [[ "${result_count}" -ne 1 ]]; then
466- echo "Expected exactly 1 Artifactory search result for ${wheel_name}, but got ${result_count}"
467- echo "${search_json}"
468- exit 1
469- fi
470-
471- storage_uri="$(jq -r '.results[0].uri // empty' <<< "${search_json}")"
472- if [[ -z "${storage_uri}" ]]; then
473- echo "Unable to resolve storage URI for ${wheel_name}"
474- echo "${search_json}"
475- exit 1
476- fi
477-
478- metadata_json="$(curl --fail-with-body --show-error --silent --location --connect-timeout 10 --max-time 60 \
479- -u "${ARTIFACTORY_USERNAME}:${ARTIFACTORY_API_KEY}" \
480- "${storage_uri}")"
481-
482- download_uri="$(jq -r '.downloadUri // empty' <<< "${metadata_json}")"
483- if [[ -z "${download_uri}" ]]; then
484- echo "Unable to resolve downloadUri for ${wheel_name}"
485- echo "${metadata_json}"
486- exit 1
487- fi
488-
489- wheel_path="${download_uri#${wheel_prefix}}"
490- if [[ "${wheel_path}" == "${download_uri}" || -z "${wheel_path}" ]]; then
491- echo "Unable to clip Artifactory prefix from downloadUri for ${wheel_name}"
492- echo "Expected prefix: ${wheel_prefix}"
493- echo "${metadata_json}"
494- exit 1
495- fi
496-
497- wheel_paths+=("${wheel_path}")
498- done
499-
500- {
501- echo "wheel_paths<<EOF"
502- printf '%s\n' "${wheel_paths[@]}"
503- echo "EOF"
504- } >> "$GITHUB_OUTPUT"
505-
506- kitmaker :
507- runs-on : [self-hosted, linux]
508- needs :
509- - publish-wheel
510- outputs :
511- release_uuid : ${{ steps.submit-kitmaker.outputs.release_uuid }}
512- environment : release
513- permissions :
514- contents : read
515- if : ${{ github.event_name != 'pull_request' && needs.publish-wheel.result == 'success' }}
516-
517- steps :
518- - name : Submit wheel release to Kitmaker
519- id : submit-kitmaker
520- env :
521- KITMAKER_API_ENDPOINT : ${{ secrets.KITMAKER_API_ENDPOINT }}
522- KITMAKER_PROJECT_ID : ${{ secrets.KITMAKER_PROJECT_ID }}
523- KITMAKER_TOKEN : ${{ secrets.KITMAKER_TOKEN }}
524- KITMAKER_PIC_EMAIL : ${{ secrets.KITMAKER_PIC_EMAIL }}
525- ARTIFACTORY_URL : ${{ secrets.ARTIFACTORY_URL }}
526- ARTIFACTORY_REPO : ${{ secrets.ARTIFACTORY_REPO }}
527- WHEEL_PATHS : ${{ needs.publish-wheel.outputs.wheel_paths }}
528- KITMAKER_UPLOAD : ${{ startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/tags/v') }}
529- run : |
530- set -euo pipefail
531-
532- if [[ -z "${KITMAKER_API_ENDPOINT}" || -z "${KITMAKER_PROJECT_ID}" || -z "${KITMAKER_TOKEN}" || -z "${KITMAKER_PIC_EMAIL}" || -z "${ARTIFACTORY_URL}" || -z "${ARTIFACTORY_REPO}" ]]; then
533- echo "Missing one or more required secrets: KITMAKER_API_ENDPOINT, KITMAKER_PROJECT_ID, KITMAKER_TOKEN, KITMAKER_PIC_EMAIL, ARTIFACTORY_URL, ARTIFACTORY_REPO"
534- exit 1
535- fi
536-
537- if [[ "${KITMAKER_API_ENDPOINT}" != https://* ]]; then
538- echo "KITMAKER_API_ENDPOINT must use https://"
539- exit 1
540- fi
541-
542- if [[ "${ARTIFACTORY_URL}" != https://* ]]; then
543- echo "ARTIFACTORY_URL must use https://"
544- exit 1
545- fi
546-
547- if [[ -z "${WHEEL_PATHS}" ]]; then
548- echo "No wheel paths were produced by publish-wheel job"
549- exit 1
550- fi
551-
552- wheel_base="${ARTIFACTORY_URL%/}/${ARTIFACTORY_REPO}"
553-
554- api_url="${KITMAKER_API_ENDPOINT%/}/v0/projects/${KITMAKER_PROJECT_ID}/releases"
555- payload_items=()
556- while IFS= read -r wheel_path; do
557- [[ -z "${wheel_path}" ]] && continue
558- wheel_url="${wheel_base}/${wheel_path}"
559- wheel_name="$(basename "${wheel_path}")"
560- echo "Queueing wheel for Kitmaker payload: ${wheel_name}"
561-
562- payload_items+=("$(jq -cn \
563- --arg pic "${KITMAKER_PIC_EMAIL}" \
564- --arg url "${wheel_url}" \
565- --argjson upload "${KITMAKER_UPLOAD}" \
566- '{pic: $pic, job_type: "wheel-release-job", publish_to: "both_devzone_pypi", url: $url, size: "small", upload: $upload}')")
567- done <<< "${WHEEL_PATHS}"
568-
569- if (( ${#payload_items[@]} == 0 )); then
570- echo "No valid wheel URLs found to submit to Kitmaker"
571- exit 1
572- fi
573-
574- payload_array="$(printf '%s\n' "${payload_items[@]}" | jq -s '.')"
575- payload="$(jq -cn --arg project_name "isaacteleop" --argjson payload "${payload_array}" '{project_name: $project_name, payload: $payload}')"
576-
577- echo "Posting ${#payload_items[@]} wheel(s) to Kitmaker in a single request"
578- response_json="$(curl --fail-with-body --show-error --silent --location --connect-timeout 10 --max-time 120 \
579- -X POST "${api_url}" \
580- -H "Authorization: Bearer ${KITMAKER_TOKEN}" \
581- -H "Content-Type: application/json" \
582- -d "${payload}")"
583- echo "${response_json}"
584-
585- release_uuid="$(jq -r '.release_uuid // empty' <<< "${response_json}")"
586- if [[ -z "${release_uuid}" ]]; then
587- echo "Kitmaker response missing release_uuid"
588- exit 1
589- fi
590-
591- echo "release_uuid=${release_uuid}" >> "$GITHUB_OUTPUT"
592-
593- kitmaker-status :
594- runs-on : [self-hosted, linux]
595- needs :
596- - kitmaker
597- environment : release
598- permissions :
599- contents : read
600- if : ${{ github.event_name != 'pull_request' && needs.kitmaker.result == 'success' }}
601-
602- steps :
603- - name : Monitor Kitmaker release status
604- env :
605- KITMAKER_API_ENDPOINT : ${{ secrets.KITMAKER_API_ENDPOINT }}
606- KITMAKER_PROJECT_ID : ${{ secrets.KITMAKER_PROJECT_ID }}
607- KITMAKER_TOKEN : ${{ secrets.KITMAKER_TOKEN }}
608- KITMAKER_RELEASE_UUID : ${{ needs.kitmaker.outputs.release_uuid }}
609- ARTIFACTORY_URL : ${{ secrets.ARTIFACTORY_URL }}
610- ARTIFACTORY_REPO : ${{ secrets.ARTIFACTORY_REPO }}
611- run : |
612- set -euo pipefail
613-
614- # Redact sensitive values that may appear in API responses.
615- echo "::add-mask::${KITMAKER_API_ENDPOINT}"
616- echo "::add-mask::${KITMAKER_API_ENDPOINT%/}"
617- echo "::add-mask::${KITMAKER_RELEASE_UUID}"
618- echo "::add-mask::${KITMAKER_PROJECT_ID}"
619- echo "::add-mask::${ARTIFACTORY_URL}"
620- echo "::add-mask::${ARTIFACTORY_URL%/}"
621- echo "::add-mask::${ARTIFACTORY_REPO}"
622-
623- if [[ -z "${KITMAKER_API_ENDPOINT}" || -z "${KITMAKER_TOKEN}" ]]; then
624- echo "Missing required secrets: KITMAKER_API_ENDPOINT, KITMAKER_TOKEN"
625- exit 1
626- fi
627-
628- if [[ "${KITMAKER_API_ENDPOINT}" != https://* ]]; then
629- echo "KITMAKER_API_ENDPOINT must use https://"
630- exit 1
631- fi
632-
633- if [[ -z "${KITMAKER_RELEASE_UUID}" ]]; then
634- echo "No release_uuid was produced by the kitmaker job"
635- exit 1
636- fi
637-
638- status_url="${KITMAKER_API_ENDPOINT%/}/v0/status/${KITMAKER_RELEASE_UUID}"
639- # Limit total polling time to under an hour with exponential backoff starting at 30s
640- max_attempts=15
641- sleep_seconds=30
642-
643- for ((attempt=1; attempt<=max_attempts; attempt++)); do
644- echo "Polling Kitmaker status (attempt ${attempt}/${max_attempts})"
645-
646- response_json="$(curl --fail-with-body --show-error --silent --location --connect-timeout 10 --max-time 60 \
647- -H "Authorization: Bearer ${KITMAKER_TOKEN}" \
648- "${status_url}")"
649-
650- echo "${response_json}"
651- status="$(jq -r '.status // empty' <<< "${response_json}")"
652-
653- if [[ "${status}" == "completed" ]]; then
654- echo "Kitmaker release ${KITMAKER_RELEASE_UUID} completed"
655- exit 0
656- fi
657-
658- if [[ "${status}" == "failed" ]]; then
659- echo "Kitmaker release ${KITMAKER_RELEASE_UUID} failed"
660- exit 1
661- fi
662-
663- if (( attempt == max_attempts )); then
664- break
665- fi
666-
667- sleep "${sleep_seconds}"
668- sleep_seconds=$(( sleep_seconds * 125 / 100 ))
669- done
670-
671- echo "Timed out waiting for Kitmaker release ${KITMAKER_RELEASE_UUID} to complete"
672- exit 1
0 commit comments