diff --git a/.github/scripts/publish_r2_release.py b/.github/scripts/publish_r2_release.py index ab83c18684..1129ff776f 100755 --- a/.github/scripts/publish_r2_release.py +++ b/.github/scripts/publish_r2_release.py @@ -6,7 +6,9 @@ standard AWS credentials and the R2 endpoint from ``AWS_ENDPOINT_URL``. Objects are created under ``codex/releases//`` with a validated upload checksum and checked using object metadata before the run succeeds. The versioned prefix includes every release asset plus installer-facing -``release.json`` metadata derived from the verified downloads. +``release.json`` metadata derived from the verified downloads. Once those +objects are verified, the same metadata advances ``codex/channels/latest`` when +the release is marked latest and ``codex/channels/prerelease`` for prereleases. """ import argparse @@ -148,7 +150,14 @@ def raise_s3( ) from error -def put_immutable(endpoint: str, key: str, path: Path, sha256: str) -> None: +def put_object( + endpoint: str, + key: str, + path: Path, + sha256: str, + *, + extra_args: list[str], +) -> None: try: run_command( [ @@ -157,7 +166,7 @@ def put_immutable(endpoint: str, key: str, path: Path, sha256: str) -> None: "cp", str(path), f"s3://{BUCKET}/{key}", - "--no-overwrite", + *extra_args, "--checksum-algorithm", "CRC64NVME", "--metadata", @@ -223,6 +232,8 @@ def verify_remote( def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--tag", required=True) + parser.add_argument("--make-latest", choices=("true", "false"), required=True) + parser.add_argument("--prerelease", choices=("true", "false"), required=True) return parser.parse_args() @@ -258,7 +269,7 @@ def main() -> int: size = asset.size sha256 = asset.sha256 key = f"{PREFIX}/releases/{version}/{path.name}" - put_immutable(endpoint, key, path, sha256) + put_object(endpoint, key, path, sha256, extra_args=["--no-overwrite"]) verify_remote(endpoint, key, size, sha256) print( f"published and verified s3://{BUCKET}/{key} " @@ -298,7 +309,13 @@ def main() -> int: with metadata_path.open("rb") as source: metadata_size, metadata_sha256 = stream_digest(source) metadata_key = f"{PREFIX}/releases/{version}/{RELEASE_METADATA_NAME}" - put_immutable(endpoint, metadata_key, metadata_path, metadata_sha256) + put_object( + endpoint, + metadata_key, + metadata_path, + metadata_sha256, + extra_args=["--no-overwrite"], + ) verify_remote( endpoint, metadata_key, @@ -310,6 +327,31 @@ def main() -> int: f"size={metadata_size} sha256={metadata_sha256}", file=sys.stderr, ) + channels = [] + if args.make_latest == "true": + channels.append("latest") + if args.prerelease == "true": + channels.append("prerelease") + for channel in channels: + channel_key = f"{PREFIX}/channels/{channel}" + put_object( + endpoint, + channel_key, + metadata_path, + metadata_sha256, + extra_args=["--content-type", "application/json"], + ) + verify_remote( + endpoint, + channel_key, + metadata_size, + metadata_sha256, + ) + print( + f"published and verified s3://{BUCKET}/{channel_key} " + f"size={metadata_size} sha256={metadata_sha256}", + file=sys.stderr, + ) print( json.dumps( diff --git a/.github/workflows/r2-release.yml b/.github/workflows/r2-release.yml index ea6a0ea2a5..3abe94dde0 100644 --- a/.github/workflows/r2-release.yml +++ b/.github/workflows/r2-release.yml @@ -6,6 +6,12 @@ on: tag: required: true type: string + make_latest: + required: true + type: boolean + prerelease: + required: true + type: boolean permissions: {} @@ -32,7 +38,11 @@ jobs: AWS_REGION: ${{ vars.CODEX_R2_REGION }} GH_TOKEN: ${{ github.token }} RELEASE_TAG: ${{ inputs.tag }} + RELEASE_MAKE_LATEST: ${{ inputs.make_latest }} + RELEASE_PRERELEASE: ${{ inputs.prerelease }} run: | set -euo pipefail python3 .github/scripts/publish_r2_release.py \ - --tag "${RELEASE_TAG}" + --tag "${RELEASE_TAG}" \ + --make-latest "${RELEASE_MAKE_LATEST}" \ + --prerelease "${RELEASE_PRERELEASE}" diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 805d231e98..a6e056030e 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -1130,6 +1130,8 @@ jobs: outputs: version: ${{ steps.release_name.outputs.name }} tag: ${{ github.ref_name }} + make_latest: ${{ steps.release_name.outputs.make_latest }} + prerelease: ${{ steps.release_name.outputs.prerelease }} should_publish_npm: ${{ steps.npm_publish_settings.outputs.should_publish }} npm_tag: ${{ steps.npm_publish_settings.outputs.npm_tag }} @@ -1208,6 +1210,13 @@ jobs: # "rust-v0.1.0". version="${GITHUB_REF_NAME#rust-v}" echo "name=${version}" >> $GITHUB_OUTPUT + if [[ "${version}" == *-* ]]; then + echo "make_latest=false" >> $GITHUB_OUTPUT + echo "prerelease=true" >> $GITHUB_OUTPUT + else + echo "make_latest=true" >> $GITHUB_OUTPUT + echo "prerelease=false" >> $GITHUB_OUTPUT + fi - name: Determine npm publish settings id: npm_publish_settings @@ -1268,10 +1277,10 @@ jobs: body_path: ${{ steps.release_notes.outputs.path }} files: dist/** overwrite_files: true - make_latest: ${{ !contains(steps.release_name.outputs.name, '-') }} + make_latest: ${{ steps.release_name.outputs.make_latest }} # Mark as prerelease only when the version has a suffix after x.y.z # (e.g. -alpha, -beta). Otherwise publish a normal release. - prerelease: ${{ contains(steps.release_name.outputs.name, '-') }} + prerelease: ${{ steps.release_name.outputs.prerelease }} publish-r2: name: publish-r2 @@ -1282,6 +1291,8 @@ jobs: secrets: inherit with: tag: ${{ needs.release.outputs.tag }} + make_latest: ${{ fromJSON(needs.release.outputs.make_latest) }} + prerelease: ${{ fromJSON(needs.release.outputs.prerelease) }} publish-dotslash: name: publish-dotslash