From 356d0cd4bc78d7bd533b2050be5cda4adb8f0657 Mon Sep 17 00:00:00 2001 From: Andrei Aristarkhov Date: Wed, 18 Mar 2026 14:35:33 -0700 Subject: [PATCH 1/7] wheel publishing from artifactory instead of gh releases --- .github/workflows/build-ubuntu.yml | 128 ++++++++--------------------- 1 file changed, 36 insertions(+), 92 deletions(-) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index c3ced68a7..78479760c 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -226,6 +226,8 @@ jobs: needs: - build-ubuntu - test-cloudxr + outputs: + wheel_urls: ${{ steps.upload-artifactory.outputs.wheel_urls }} environment: dev # Publish only after build and CloudXR tests succeed. if: ${{ needs.build-ubuntu.result == 'success' && needs.test-cloudxr.result == 'success' }} @@ -244,6 +246,7 @@ jobs: path: wheels - name: Upload wheel(s) to Artifactory + id: upload-artifactory env: ARTIFACTORY_URL: ${{ secrets.ARTIFACTORY_URL }} ARTIFACTORY_REPO: ${{ secrets.ARTIFACTORY_REPO }} @@ -283,88 +286,54 @@ jobs: -p "${ARTIFACTORY_API_KEY}" \ "${wheels[@]}" - publish-github-release-assets: - runs-on: ubuntu-latest - needs: - - build-ubuntu - - test-cloudxr - outputs: - wheel_urls: ${{ steps.upload-release-assets.outputs.wheel_urls }} - release_tag: ${{ steps.upload-release-assets.outputs.release_tag }} - permissions: - contents: write - # Publish on every push after build and CloudXR tests succeed. - if: ${{ github.event_name == 'push' && needs.build-ubuntu.result == 'success' && needs.test-cloudxr.result == 'success' }} - - steps: - - name: Download wheel artifacts - uses: actions/download-artifact@v7 - with: - pattern: isaacteleop-wheels-* - merge-multiple: true - path: wheels - - - name: Upload wheel(s) as GitHub release assets - id: upload-release-assets - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GH_REPO: ${{ github.repository }} - run: | - set -euo pipefail - - shopt -s nullglob - wheels=(wheels/*.whl) - if (( ${#wheels[@]} == 0 )); then - echo "No wheels found under wheels/*.whl" - ls -la wheels || true - exit 1 - fi - - tag="ci-wheels-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" - title="CI Wheels ${GITHUB_RUN_ID}.${GITHUB_RUN_ATTEMPT}" - - gh release create "${tag}" \ - --repo "${GH_REPO}" \ - --target "${GITHUB_SHA}" \ - --title "${title}" \ - --notes "Automated wheel artifacts for ${GITHUB_SHA}" \ - --draft - - # Persist tag output immediately so cleanup can still run even if later steps fail. - echo "release_tag=${tag}" >> "$GITHUB_OUTPUT" - - echo "Uploading ${#wheels[@]} wheel(s) to release ${tag}" - gh release upload "${tag}" "${wheels[@]}" --repo "${GH_REPO}" + wheel_urls=() + for wheel in "${wheels[@]}"; do + wheel_name="$(basename "${wheel}")" + echo "Resolving Artifactory URL for ${wheel_name}" + + search_json="$(curl --fail-with-body --show-error --silent --location --get --connect-timeout 10 --max-time 60 \ + -u "${ARTIFACTORY_USERNAME}:${ARTIFACTORY_API_KEY}" \ + --data-urlencode "name=${wheel_name}" \ + --data-urlencode "repos=${ARTIFACTORY_REPO}" \ + "${ARTIFACTORY_URL%/}/api/search/artifact")" + + storage_uri="$(jq -r '.results[0].uri // empty' <<< "${search_json}")" + if [[ -z "${storage_uri}" ]]; then + echo "Unable to resolve storage URI for ${wheel_name}" + echo "${search_json}" + exit 1 + fi - echo "Finalizing release ${tag} as pre-release" - gh release edit "${tag}" --repo "${GH_REPO}" --draft=false --prerelease + metadata_json="$(curl --fail-with-body --show-error --silent --location --connect-timeout 10 --max-time 60 \ + -u "${ARTIFACTORY_USERNAME}:${ARTIFACTORY_API_KEY}" \ + "${storage_uri}")" - echo "Uploaded assets on ${tag}:" - release_assets_json="$(gh release view "${tag}" --repo "${GH_REPO}" --json assets)" - echo "${release_assets_json}" + download_uri="$(jq -r '.downloadUri // empty' <<< "${metadata_json}")" + if [[ -z "${download_uri}" ]]; then + echo "Unable to resolve downloadUri for ${wheel_name}" + echo "${metadata_json}" + exit 1 + fi - wheel_urls="$(jq -r '.assets[] | select(.name | endswith(".whl")) | .url' <<< "${release_assets_json}")" - if [[ -z "${wheel_urls}" ]]; then - echo "Unable to find wheel asset URLs in uploaded release assets" - exit 1 - fi + wheel_urls+=("${download_uri}") + done { echo "wheel_urls<> "$GITHUB_OUTPUT" kitmaker: runs-on: [self-hosted, linux] needs: - - publish-github-release-assets + - publish-wheel outputs: release_uuid: ${{ steps.submit-kitmaker.outputs.release_uuid }} environment: release permissions: contents: read - if: ${{ needs.publish-github-release-assets.result == 'success' }} + if: ${{ needs.publish-wheel.result == 'success' }} steps: - name: Submit wheel release to Kitmaker @@ -374,7 +343,7 @@ jobs: KITMAKER_PROJECT_ID: ${{ secrets.KITMAKER_PROJECT_ID }} KITMAKER_TOKEN: ${{ secrets.KITMAKER_TOKEN }} KITMAKER_PIC_EMAIL: ${{ secrets.KITMAKER_PIC_EMAIL }} - WHEEL_URLS: ${{ needs.publish-github-release-assets.outputs.wheel_urls }} + WHEEL_URLS: ${{ needs.publish-wheel.outputs.wheel_urls }} KITMAKER_UPLOAD: ${{ startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/tags/v') }} run: | set -euo pipefail @@ -390,7 +359,7 @@ jobs: fi if [[ -z "${WHEEL_URLS}" ]]; then - echo "No wheel URLs were produced by publish-github-release-assets job" + echo "No wheel URLs were produced by publish-wheel job" exit 1 fi @@ -501,28 +470,3 @@ jobs: echo "Timed out waiting for Kitmaker release ${KITMAKER_RELEASE_UUID} to complete" exit 1 - cleanup-github-release-assets: - runs-on: ubuntu-latest - needs: - - publish-github-release-assets - - kitmaker-status - permissions: - contents: write - if: ${{ always() && needs.publish-github-release-assets.result == 'success' }} - - steps: - - name: Delete GitHub release and tag - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GH_REPO: ${{ github.repository }} - RELEASE_TAG: ${{ needs.publish-github-release-assets.outputs.release_tag }} - run: | - set -euo pipefail - - if [[ -z "${RELEASE_TAG}" ]]; then - echo "No release tag was produced by publish-github-release-assets job" - exit 1 - fi - - echo "Deleting GitHub release and tag: ${RELEASE_TAG}" - gh release delete "${RELEASE_TAG}" --repo "${GH_REPO}" --cleanup-tag --yes From f6cae5e7e9d7af4254cde8086658c66d28bc3451 Mon Sep 17 00:00:00 2001 From: Andrei Aristarkhov Date: Wed, 18 Mar 2026 15:34:51 -0700 Subject: [PATCH 2/7] avoid secrets in job output --- .github/workflows/build-ubuntu.yml | 49 +++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 78479760c..17f04e125 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -227,7 +227,7 @@ jobs: - build-ubuntu - test-cloudxr outputs: - wheel_urls: ${{ steps.upload-artifactory.outputs.wheel_urls }} + wheel_paths: ${{ steps.upload-artifactory.outputs.wheel_paths }} environment: dev # Publish only after build and CloudXR tests succeed. if: ${{ needs.build-ubuntu.result == 'success' && needs.test-cloudxr.result == 'success' }} @@ -286,7 +286,10 @@ jobs: -p "${ARTIFACTORY_API_KEY}" \ "${wheels[@]}" - wheel_urls=() + wheel_base="${ARTIFACTORY_URL%/}/${ARTIFACTORY_REPO}" + wheel_prefix="${wheel_base}/" + + wheel_paths=() for wheel in "${wheels[@]}"; do wheel_name="$(basename "${wheel}")" echo "Resolving Artifactory URL for ${wheel_name}" @@ -315,12 +318,20 @@ jobs: exit 1 fi - wheel_urls+=("${download_uri}") + wheel_path="${download_uri#${wheel_prefix}}" + if [[ "${wheel_path}" == "${download_uri}" || -z "${wheel_path}" ]]; then + echo "Unable to clip Artifactory prefix from downloadUri for ${wheel_name}" + echo "Expected prefix: ${wheel_prefix}" + echo "${metadata_json}" + exit 1 + fi + + wheel_paths+=("${wheel_path}") done { - echo "wheel_urls<> "$GITHUB_OUTPUT" @@ -343,13 +354,15 @@ jobs: KITMAKER_PROJECT_ID: ${{ secrets.KITMAKER_PROJECT_ID }} KITMAKER_TOKEN: ${{ secrets.KITMAKER_TOKEN }} KITMAKER_PIC_EMAIL: ${{ secrets.KITMAKER_PIC_EMAIL }} - WHEEL_URLS: ${{ needs.publish-wheel.outputs.wheel_urls }} + ARTIFACTORY_URL: ${{ secrets.ARTIFACTORY_URL }} + ARTIFACTORY_REPO: ${{ secrets.ARTIFACTORY_REPO }} + WHEEL_PATHS: ${{ needs.publish-wheel.outputs.wheel_paths }} KITMAKER_UPLOAD: ${{ startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/tags/v') }} run: | set -euo pipefail - if [[ -z "${KITMAKER_API_ENDPOINT}" || -z "${KITMAKER_PROJECT_ID}" || -z "${KITMAKER_TOKEN}" || -z "${KITMAKER_PIC_EMAIL}" ]]; then - echo "Missing one or more required secrets: KITMAKER_API_ENDPOINT, KITMAKER_PROJECT_ID, KITMAKER_TOKEN, KITMAKER_PIC_EMAIL" + if [[ -z "${KITMAKER_API_ENDPOINT}" || -z "${KITMAKER_PROJECT_ID}" || -z "${KITMAKER_TOKEN}" || -z "${KITMAKER_PIC_EMAIL}" || -z "${ARTIFACTORY_URL}" || -z "${ARTIFACTORY_REPO}" ]]; then + echo "Missing one or more required secrets: KITMAKER_API_ENDPOINT, KITMAKER_PROJECT_ID, KITMAKER_TOKEN, KITMAKER_PIC_EMAIL, ARTIFACTORY_URL, ARTIFACTORY_REPO" exit 1 fi @@ -358,16 +371,24 @@ jobs: exit 1 fi - if [[ -z "${WHEEL_URLS}" ]]; then - echo "No wheel URLs were produced by publish-wheel job" + if [[ "${ARTIFACTORY_URL}" != https://* ]]; then + echo "ARTIFACTORY_URL must use https://" + exit 1 + fi + + if [[ -z "${WHEEL_PATHS}" ]]; then + echo "No wheel paths were produced by publish-wheel job" exit 1 fi + wheel_base="${ARTIFACTORY_URL%/}/${ARTIFACTORY_REPO}" + api_url="${KITMAKER_API_ENDPOINT%/}/v0/projects/${KITMAKER_PROJECT_ID}/releases" payload_items=() - while IFS= read -r wheel_url; do - [[ -z "${wheel_url}" ]] && continue - wheel_name="$(basename "${wheel_url}")" + while IFS= read -r wheel_path; do + [[ -z "${wheel_path}" ]] && continue + wheel_url="${wheel_base}/${wheel_path}" + wheel_name="$(basename "${wheel_path}")" echo "Queueing wheel for Kitmaker payload: ${wheel_name}" payload_items+=("$(jq -cn \ @@ -375,7 +396,7 @@ jobs: --arg url "${wheel_url}" \ --argjson upload "${KITMAKER_UPLOAD}" \ '{pic: $pic, job_type: "wheel-release-job", publish_to: "both_devzone_pypi", url: $url, size: "small", upload: $upload}')") - done <<< "${WHEEL_URLS}" + done <<< "${WHEEL_PATHS}" if (( ${#payload_items[@]} == 0 )); then echo "No valid wheel URLs found to submit to Kitmaker" From ac86e99600472a2414f1c97478bf5ac5872dd58b Mon Sep 17 00:00:00 2001 From: Andrei Aristarkhov Date: Wed, 18 Mar 2026 22:23:22 -0700 Subject: [PATCH 3/7] ensure clean wheels directory before downloading artifacts --- .github/workflows/build-ubuntu.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 17f04e125..c7255b068 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -238,6 +238,10 @@ jobs: with: python-version: '3.11' + - name: Ensure clean wheels directory + run: | + rm -rf wheels + - name: Download wheel artifacts uses: actions/download-artifact@v7 with: From 078403542705994862bd004352381a6040d57d4b Mon Sep 17 00:00:00 2001 From: Andrei Aristarkhov Date: Thu, 19 Mar 2026 10:48:53 -0700 Subject: [PATCH 4/7] fix eof --- .github/workflows/build-ubuntu.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index c7255b068..842f71857 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -494,4 +494,3 @@ jobs: echo "Timed out waiting for Kitmaker release ${KITMAKER_RELEASE_UUID} to complete" exit 1 - From f8de74a9461c485693a9f9cde082e4ec4fb682dc Mon Sep 17 00:00:00 2001 From: Andrei Aristarkhov Date: Thu, 19 Mar 2026 11:03:09 -0700 Subject: [PATCH 5/7] skip wheel submission on pull requests --- .github/workflows/build-ubuntu.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 842f71857..58f7814d7 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -348,7 +348,7 @@ jobs: environment: release permissions: contents: read - if: ${{ needs.publish-wheel.result == 'success' }} + if: ${{ github.event_name != 'pull_request' && needs.publish-wheel.result == 'success' }} steps: - name: Submit wheel release to Kitmaker @@ -433,7 +433,7 @@ jobs: environment: release permissions: contents: read - if: ${{ needs.kitmaker.result == 'success' }} + if: ${{ github.event_name != 'pull_request' && needs.kitmaker.result == 'success' }} steps: - name: Monitor Kitmaker release status From a9140ac18218d32ddf779d7fe738c106ae42c348 Mon Sep 17 00:00:00 2001 From: Andrei Aristarkhov Date: Thu, 19 Mar 2026 11:21:45 -0700 Subject: [PATCH 6/7] add masking for sensitive values in Kitmaker release status monitoring --- .github/workflows/build-ubuntu.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 58f7814d7..d9ea495ac 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -439,11 +439,23 @@ jobs: - name: Monitor Kitmaker release status env: KITMAKER_API_ENDPOINT: ${{ secrets.KITMAKER_API_ENDPOINT }} + KITMAKER_PROJECT_ID: ${{ secrets.KITMAKER_PROJECT_ID }} KITMAKER_TOKEN: ${{ secrets.KITMAKER_TOKEN }} KITMAKER_RELEASE_UUID: ${{ needs.kitmaker.outputs.release_uuid }} + ARTIFACTORY_URL: ${{ secrets.ARTIFACTORY_URL }} + ARTIFACTORY_REPO: ${{ secrets.ARTIFACTORY_REPO }} run: | set -euo pipefail + # Redact sensitive values that may appear in API responses. + echo "::add-mask::${KITMAKER_API_ENDPOINT}" + echo "::add-mask::${KITMAKER_API_ENDPOINT%/}" + echo "::add-mask::${KITMAKER_RELEASE_UUID}" + echo "::add-mask::${KITMAKER_PROJECT_ID}" + echo "::add-mask::${ARTIFACTORY_URL}" + echo "::add-mask::${ARTIFACTORY_URL%/}" + echo "::add-mask::${ARTIFACTORY_REPO}" + if [[ -z "${KITMAKER_API_ENDPOINT}" || -z "${KITMAKER_TOKEN}" ]]; then echo "Missing required secrets: KITMAKER_API_ENDPOINT, KITMAKER_TOKEN" exit 1 From 79b032f6795564ac5b6d76b09347c26d6d84045c Mon Sep 17 00:00:00 2001 From: Andrei Aristarkhov Date: Thu, 19 Mar 2026 11:58:41 -0700 Subject: [PATCH 7/7] Ensure unique url result Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/build-ubuntu.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index d9ea495ac..5c1f91411 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -304,6 +304,13 @@ jobs: --data-urlencode "repos=${ARTIFACTORY_REPO}" \ "${ARTIFACTORY_URL%/}/api/search/artifact")" + result_count="$(jq '.results | length' <<< "${search_json}")" + if [[ "${result_count}" -ne 1 ]]; then + echo "Expected exactly 1 Artifactory search result for ${wheel_name}, but got ${result_count}" + echo "${search_json}" + exit 1 + fi + storage_uri="$(jq -r '.results[0].uri // empty' <<< "${search_json}")" if [[ -z "${storage_uri}" ]]; then echo "Unable to resolve storage URI for ${wheel_name}"