diff --git a/.github/workflows/release-main.yml b/.github/workflows/release-main.yml index 67dc36324a9..2987e2e5c90 100644 --- a/.github/workflows/release-main.yml +++ b/.github/workflows/release-main.yml @@ -17,6 +17,8 @@ jobs: NAME: dev-release steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 2 - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: '3.11' @@ -34,7 +36,25 @@ jobs: - name: Build and publish run: | CIRQ_PRE_RELEASE_VERSION=$(dev_tools/packaging/generate-dev-version-id.sh) - [[ "$CIRQ_PRE_RELEASE_VERSION" =~ .*dev.* ]] && echo "Deploying dev version '$CIRQ_PRE_RELEASE_VERSION'" || (echo "not dev version"; exit 1) - out_dir="${PWD}/dist" - dev_tools/packaging/produce-package.sh ${out_dir} $CIRQ_PRE_RELEASE_VERSION + if [[ "${CIRQ_PRE_RELEASE_VERSION}" != *.dev* ]]; then + echo "Not a dev version" + exit 1 + fi + echo "Building wheels for the dev version '${CIRQ_PRE_RELEASE_VERSION}'" + THIS_DATE_EPOCH=$(git log -1 --pretty="%ct") + out_dir="${HOME}/cirq-dist" + out_dir_last="${HOME}/cirq-dist-last" + dev_tools/packaging/produce-package.sh "${out_dir}" "${CIRQ_PRE_RELEASE_VERSION}" + # disregard errors from building wheels at the previous commit + SOURCE_DATE_EPOCH=${THIS_DATE_EPOCH} dev_tools/packaging/produce-package.sh \ + --commit="HEAD~1" "${out_dir_last}" "${CIRQ_PRE_RELEASE_VERSION}" || true + echo "Comparing wheels with the build at previous commit" + if diff -q -r "${out_dir_last}" "${out_dir}"; then + echo "Wheels are identical - skipping the release" + echo "### Skipped identical release" >> ${GITHUB_STEP_SUMMARY} + echo "Cirq wheels for ${CIRQ_PRE_RELEASE_VERSION} (${GITHUB_SHA})" \ + "are identical to their build at the previous commit." >> ${GITHUB_STEP_SUMMARY} + exit 0 + fi + echo "Deploying dev version '$CIRQ_PRE_RELEASE_VERSION'" twine upload "${out_dir}/*" diff --git a/dev_tools/packaging/produce-package.sh b/dev_tools/packaging/produce-package.sh index 42854523968..6b2fde3fca8 100755 --- a/dev_tools/packaging/produce-package.sh +++ b/dev_tools/packaging/produce-package.sh @@ -14,62 +14,116 @@ # See the License for the specific language governing permissions and # limitations under the License. -################################################################################ -# Produces wheels that can be uploaded to the pypi package repository. -# -# First argument must be the output directory. Second argument is an optional -# version specifier. If not set, the version from `_version.py` is used. If set, -# it overwrites `_version.py`. -# -# Usage: -# dev_tools/packaging/produce-package.sh output_dir [version] -################################################################################ +set -o errexit +set -o nounset -set -e trap "{ echo -e '\033[31mFAILED\033[0m'; }" ERR -if [ -z "${1}" ]; then - echo -e "\033[31mNo output directory given.\033[0m" - exit 1 -fi -out_dir=$(realpath "${1}") +DOC="\ +usage: $0 [options] output_dir [version] + +Produces wheels that can be uploaded to the pypi package repository. + +First argument must be the output directory. Second argument is an optional +version specifier, which overwrites the version in _version.py files. +If not set, the version from _version.py is used as is. -SPECIFIED_VERSION="${2}" +Options: + + -c, --commit=COMMIT create wheels from sources at COMMIT instead of HEAD + -h, --help display this message and exit. +" + +out_dir="" +specified_version="" +commitish=HEAD + + +die() { + ( shift; echo -e "\033[31m${*}\033[0m" ) + exit "$1" +} # Helper to isolate dev_tools/modules.py from Python environment variables my_dev_tools_modules() { python3 -E dev_tools/modules.py "$@" } -# Get the working directory to the repo root. +# Process command-line arguments +while (( $# )); do + case "$1" in + -h|--help) + echo "$DOC" + exit 0 + ;; + -c?*) + commitish="${1#-c}" + ;; + --commit=?*) + commitish="${1#*=}" + ;; + -c|--commit) + shift + (( $# )) || die 2 "Option '-c,--commit' requires an argument." + commit="$1" + ;; + *) + if [[ -z "${out_dir}" ]]; then + out_dir=$(realpath "${1}") + elif [[ -z "${specified_version}" ]]; then + specified_version="${1}" + else + die 2 "Unrecognized argument '$1'." + fi + ;; + esac + shift +done + +if [[ -z "${out_dir}" ]]; then + die 2 "No output directory given." +fi + +# Change to the root of the Cirq git repository thisdir=$(dirname "${BASH_SOURCE[0]:?}") repo_dir=$(git -C "${thisdir}" rev-parse --show-toplevel) cd "${repo_dir}" -# Make a clean copy of HEAD, without files ignored by git (but potentially kept by setup.py). -if [ -n "$(git status --short)" ]; then +# Validate and resolve the commit value +commit=$(git rev-parse --verify --quiet "${commitish}^{commit}") || + die "$?" "Invalid commit identifier '${commitish}'" + +# Make a pristine temporary clone of the Cirq repository +if [[ -n "$(git status --short)" ]]; then echo -e "\033[31mWARNING: You have uncommitted changes. They won't be included in the package.\033[0m" fi + tmp_git_dir=$(mktemp -d "/tmp/produce-package-git.XXXXXXXXXXXXXXXX") -trap '{ rm -rf "${tmp_git_dir}"; }' EXIT echo "Creating pristine repository clone at ${tmp_git_dir}" -git clone --shared --quiet "${repo_dir}" "${tmp_git_dir}" +git clone --shared --quiet --no-checkout "${repo_dir}" "${tmp_git_dir}" cd "${tmp_git_dir}" -if [ -n "${SPECIFIED_VERSION}" ]; then - CURRENT_VERSION=$(my_dev_tools_modules print_version) - my_dev_tools_modules replace_version --old="${CURRENT_VERSION}" --new="${SPECIFIED_VERSION}" +git checkout --quiet "${commit}" + +if [[ -n "${specified_version}" ]]; then + current_version=$(my_dev_tools_modules print_version) + my_dev_tools_modules replace_version --old="${current_version}" --new="${specified_version}" fi # Python 3 wheel. -echo "Producing python 3 package files." +echo "Producing Python 3 package files." -CIRQ_MODULES=$(my_dev_tools_modules list --mode folder --include-parent) -date_epoch=$(git log -1 --pretty="%ct") +# Reuse SOURCE_DATE_EPOCH if specified in the caller environment +date_epoch=${SOURCE_DATE_EPOCH:-$(git log -1 --pretty="%ct")} +cirq_modules=$(my_dev_tools_modules list --mode folder --include-parent) -for m in $CIRQ_MODULES; do - echo "creating wheel for ${m}" - SOURCE_DATE_EPOCH="${date_epoch}" \ - python3 -m pip wheel --no-deps --wheel-dir="${out_dir}" "./${m}" +for m in ${cirq_modules}; do + echo "creating wheel for ${m}" + SOURCE_DATE_EPOCH="${date_epoch}" \ + python3 -m pip wheel --no-deps --wheel-dir="${out_dir}" "./${m}" done ls "${out_dir}" + +# Final clean up (all is well if we got here) +cd "${repo_dir}" +rm -rf "${tmp_git_dir}"