@@ -736,21 +736,16 @@ jobs:
736736 # #1633: this KTLO leg's outcome despite ``continue-on-error``, for
737737 # ``release_status``. ``steps.*.outcome`` covers every step with an ``id``:
738738 # all that can fail here but the TestPyPI upload, never a failure, and
739- # #1634's tag check, which fails every leg alike.
739+ # #1634's tag check, which fails every leg alike. #1967: on the image's
740+ # ``python3``, as the summary below, since the leg's may be what failed.
740741 - name : Record this KTLO leg's outcome
741742 id : ktlo
742743 if : ${{ !cancelled() && matrix.ktlo == true }}
743744 continue-on-error : ${{ matrix.ktlo == true }}
744745 env :
745746 LEG : ${{ matrix.python-version }}
746747 OUTCOMES : ${{ join(steps.*.outcome, ' ') }}
747- run : |
748- case " $OUTCOMES " in
749- *" failure "*) result=failure ;;
750- *) result=success ;;
751- esac
752- echo "ktlo_$(printf '%s' "$LEG" | tr -d '.')=$result" >> "$GITHUB_OUTPUT"
753- echo "Python $LEG (KTLO, non-blocking): $result (step outcomes: $OUTCOMES)"
748+ run : /usr/bin/python3 util/release_ktlo.py
754749
755750 # #1672: the warning and job-summary line for each failed step, the same
756751 # step as ends every KTLO leg in unit-tests.yml; see its ``test`` job.
@@ -761,16 +756,7 @@ jobs:
761756 env :
762757 LEG : Python ${{ matrix.python-version }}${{ matrix.ktlo && ' (KTLO, non-blocking)' || '' }}
763758 STEPS : ${{ toJSON(steps) }}
764- run : |
765- failed=$(jq -r 'to_entries[] | select(.value.outcome == "failure") | .key' <<< "$STEPS")
766- if [ -z "$failed" ]; then
767- echo "$LEG: no step failed."
768- exit 0
769- fi
770- while IFS= read -r id; do
771- echo "::warning title=${id} failed on ${LEG//,/%2C}::KTLO legs never block (#1633), so this job still succeeds (#1672); see the ${id} step's log."
772- echo "- ${LEG}: step \`${id}\` failed; non-blocking, so this job still succeeds." >> "$GITHUB_STEP_SUMMARY"
773- done <<< "$failed"
759+ run : /usr/bin/python3 util/leg_summary.py ktlo
774760
775761 conda :
776762 name : Conda deployment (release) on ${{ matrix.os }} with Python ${{ matrix.python-version }}
@@ -825,60 +811,17 @@ jobs:
825811 run : python3 util/release_tags.py check
826812
827813 # #888: the per-leg correctness fix. Uncached, per ``version_check``'s
828- # Anaconda check, so this reads the live state.
814+ # Anaconda check, so this reads the live state. #1967: util/release_conda.py
815+ # holds this step's shell, and the build's and the Intel upload's, which
816+ # cron-conda.yml's ``conda-dist`` runs too; before setup-python, so here
817+ # on the image's ``python3``.
829818 - name : Check whether this leg is already on Anaconda
830819 id : check_leg
831820 env :
832821 PCAPKIT_VERSION : ${{ needs.version_check.outputs.PCAPKIT_VERSION }}
833- run : |
834- set -euo pipefail
835-
836- case "${{ matrix.os }}" in
837- macos-latest)
838- target_platform="osx-arm64"
839- ;;
840- macos-26-intel)
841- target_platform="osx-64"
842- ;;
843- ubuntu-latest)
844- target_platform="linux-64"
845- ;;
846- *)
847- target_platform=""
848- ;;
849- esac
850- py_tag="py$(echo "${{ matrix.python-version }}" | tr -d '.')"
851-
852- # Same coupling risk, and the same fix, as version_check's checks.
853- status="$(curl -s --retry 3 --retry-connrefused \
854- -o /tmp/anaconda_release.json -w '%{http_code}' \
855- "https://api.anaconda.org/release/jarryshaw/pypcapkit/${PCAPKIT_VERSION}" || echo 000)"
856-
857- exists=false
858- if [ "$status" = "200" ]; then
859- # The `printf` form of version_check's checks, and needed here: this
860- # runs before setup-python, so `python3` is the image's (3.12 on
861- # ubuntu-latest, 3.13 on macos-latest), and indented `python3 -c`
862- # source would fail under `set -e` -- only on the retry this is for.
863- exists="$(printf '%s\n' \
864- "import json" \
865- "with open('/tmp/anaconda_release.json') as f:" \
866- " data = json.load(f)" \
867- "target_platform = '$target_platform'" \
868- "py_tag = '$py_tag'" \
869- "found = any(" \
870- " d.get('attrs', {}).get('subdir') == target_platform" \
871- " and d.get('attrs', {}).get('build', '').startswith(py_tag + '_')" \
872- " for d in data.get('distributions', [])" \
873- ")" \
874- "print('true' if found else 'false')" \
875- | python3 -)"
876- fi
877-
878- echo "PCAPKIT_LEG_EXISTS=$exists" >> "$GITHUB_OUTPUT"
879- if [ "$exists" = "true" ]; then
880- echo "::notice title=conda leg already published::pypcapkit ${PCAPKIT_VERSION} for ${target_platform} / ${py_tag} is already on anaconda.org/jarryshaw (http ${status}); skipping this leg's upload."
881- fi
822+ PCAPKIT_CONDA_OS : ${{ matrix.os }}
823+ PCAPKIT_PYTHON_VERSION : ${{ matrix.python-version }}
824+ run : python3 util/release_conda.py leg /tmp/anaconda_release.json
882825
883826 - uses : actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
884827 with :
@@ -935,6 +878,8 @@ jobs:
935878 conda install -n base --override-channels -c conda-forge -y conda-build
936879 conda build --version
937880
881+ # On conda's base ``python``, which the login shell activates, from this
882+ # directory, as conda-dist's step of the same name.
938883 - name : Build conda packages
939884 id : conda_build
940885 shell : bash -el {0}
@@ -944,58 +889,10 @@ jobs:
944889 # because ``conda/build`` says so: at a hand-tagged commit it may still
945890 # hold the previous version's counter until ``tag``'s reset (#1634).
946891 PCAPKIT_BUILD : " 0"
892+ PCAPKIT_CONDA_OS : ${{ matrix.os }}
893+ PCAPKIT_PYTHON_VERSION : ${{ matrix.python-version }}
947894 working-directory : conda/pypcapkit
948- run : |
949- set -euo pipefail
950-
951- out_dir="$(mktemp -d -t compilation-XXXXXXXXXX)"
952- echo "out_dir=$out_dir" >> "$GITHUB_OUTPUT"
953-
954- conda build . --package-format .tar.bz2 --no-anaconda-upload --output-folder "$out_dir" --python "${{ matrix.python-version }}"
955- host_package="$(find "$out_dir" -type f \( -name '*.tar.bz2' -o -name '*.conda' \) -print | sort | sed -n '1p')"
956- if [ -z "$host_package" ]; then
957- echo "::error::No conda package was built"
958- exit 1
959- fi
960-
961- case "${{ matrix.os }}" in
962- macos-latest)
963- target_platform="osx-arm64"
964- ;;
965- macos-26-intel)
966- target_platform="osx-64"
967- ;;
968- ubuntu-latest)
969- target_platform="linux-64"
970- ;;
971- *)
972- target_platform=""
973- ;;
974- esac
975-
976- echo "host_package=$host_package" >> "$GITHUB_OUTPUT"
977-
978- # #1822: never ``conda convert`` across architectures; see the matrix.
979- host_platform="$(basename "$(dirname "$host_package")")"
980- if [ "$host_platform" != "$target_platform" ]; then
981- echo "::error title=${host_platform} package on a ${target_platform:-unmapped} leg (#1822)::${{ matrix.os }} publishes ${target_platform:-nothing}, and conda built ${host_platform} here; a package is published only for the platform it was built on."
982- exit 1
983- fi
984-
985- package_paths="$(find "$out_dir" -type f \( -name '*.tar.bz2' -o -name '*.conda' \) -print | sort)"
986- if [ -z "$package_paths" ]; then
987- echo "::error::No conda packages were found"
988- exit 1
989- fi
990-
991- printf '%s\n' "$package_paths"
992- package_paths_csv="$(printf '%s\n' "$package_paths" | paste -sd ',' -)"
993- echo "packages_csv=$package_paths_csv" >> "$GITHUB_OUTPUT"
994- {
995- echo 'packages<<EOF'
996- printf '%s\n' "$package_paths"
997- echo EOF
998- } >> "$GITHUB_OUTPUT"
895+ run : python ../../util/release_conda.py build
999896
1000897 # #1855: cron-conda.yml's ``conda-dist`` step of the same name, which says
1001898 # what it checks and why it runs setup-python's interpreter, so a release
@@ -1025,7 +922,8 @@ jobs:
1025922 # parser -- from conda-forge's anaconda-client in base. One difference:
1026923 # `--skip-existing`, so a file anaconda.org already has is skipped where
1027924 # the action, with no `force:`, fails on it. "Check whether this leg is
1028- # already on Anaconda" still gates both.
925+ # already on Anaconda" still gates both. ``release_conda.py upload`` runs
926+ # on setup-python's ``python3``.
1029927 - name : Upload conda packages to Anaconda (Intel)
1030928 if : ${{ matrix.os == 'macos-26-intel' && steps.check_leg.outputs.PCAPKIT_LEG_EXISTS != 'true' }}
1031929 shell : bash
@@ -1035,35 +933,7 @@ jobs:
1035933 PCAPKIT_CONDA_CHANNEL : jarryshaw
1036934 PCAPKIT_CONDA_PACKAGES : ${{ steps.conda_build.outputs.packages }}
1037935 PCAPKIT_CONDA_LABELS : ${{ needs.version_check.outputs.PCAPKIT_CONDA_LABEL }}
1038- run : |
1039- set -euo pipefail
1040-
1041- conda install -n base --override-channels -c conda-forge -y anaconda-client
1042-
1043- # The action's handling of its `labels:` and `packages:` inputs: a
1044- # comma-separated list, each trimmed, and the build step's
1045- # newline-separated paths.
1046- labels=()
1047- IFS=',' read -ra names <<< "$PCAPKIT_CONDA_LABELS"
1048- for name in ${names[@]+"${names[@]}"}; do
1049- name="$(echo "$name" | xargs)"
1050- if [ -n "$name" ]; then
1051- labels+=(--label "$name")
1052- fi
1053- done
1054- packages=()
1055- while IFS= read -r path; do
1056- if [ -n "$path" ]; then
1057- packages+=("$path")
1058- fi
1059- done <<< "$PCAPKIT_CONDA_PACKAGES"
1060- if [ "${#packages[@]}" -eq 0 ]; then
1061- echo "::error::No conda packages to upload"
1062- exit 1
1063- fi
1064-
1065- "${CONDA}/bin/anaconda" --token "$ANACONDA_API_TOKEN" upload \
1066- --user "$PCAPKIT_CONDA_CHANNEL" --skip-existing ${labels[@]+"${labels[@]}"} "${packages[@]}"
936+ run : python3 util/release_conda.py upload
1067937
1068938 # ``pypi``'s upload script, for the same reasons (#1634, #1640). Not
1069939 # gated on ``check_leg`` as the Anaconda upload is: a leg whose Anaconda
0 commit comments