diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml new file mode 100644 index 000000000..ea5bd266a --- /dev/null +++ b/.github/workflows/rust-release.yml @@ -0,0 +1,120 @@ +# This workflow publishes the aws-db-esdk crate to crates.io and runs +# the post-publish smoke test against the published version +# (`cargo publish` and `./test_published.sh N.N.N`). +# +# Regenerating `releases/rust/db_esdk/` and opening a release PR is +# automated by the `Rust Start Release` workflow (rust-start-release.yml) +# and MUST be run before this workflow. Dispatching this workflow without +# first running `Rust Start Release` (and reviewing/approving the +# resulting release PR) will publish whatever happens to be committed at +# `releases/rust/db_esdk/` and is unsupported. +# +# This workflow MUST be dispatched on the release PR's branch (head ref) +# BEFORE the PR is merged, so a failed publish or failed smoke test +# leaves the unmerged PR for cleanup. +# +# Authenticates to crates.io with a long-lived API token issued under the +# Crypto Tools CI bot account, stored in the CARGO_REGISTRY_TOKEN repo +# secret and gated by the `crates-io-publish` GitHub environment. +name: Rust Release + +on: + workflow_dispatch: + inputs: + version: + description: "Optional. If provided, must match releases/rust/db_esdk/Cargo.toml version exactly (N.N.N format, e.g. '1.2.5'). Used as a typo safeguard; if omitted, the version in Cargo.toml is published as-is." + required: false + type: string + +permissions: {} + +jobs: + publish: + name: Publish aws-db-esdk to crates.io + runs-on: ubuntu-22.04 + environment: crates-io-publish + permissions: + id-token: write + contents: read + steps: + - name: Support longpaths on Git checkout + run: | + git config --global core.longpaths true + + - uses: actions/checkout@v6 + + - name: Setup Rust Toolchain for GitHub CI + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: rustfmt, clippy + + - name: Read crate version from Cargo.toml + id: cargo + shell: bash + working-directory: releases/rust/db_esdk + run: | + set -euo pipefail + CRATE_VERSION="$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].version')" + CRATE_NAME="$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].name')" + echo "version=${CRATE_VERSION}" >> "$GITHUB_OUTPUT" + echo "name=${CRATE_NAME}" >> "$GITHUB_OUTPUT" + echo "Will publish ${CRATE_NAME} v${CRATE_VERSION}" + + - name: Verify input version matches Cargo.toml (if provided) + if: ${{ github.event.inputs.version != '' }} + shell: bash + env: + INPUT_VERSION: ${{ github.event.inputs.version }} + CARGO_VERSION: ${{ steps.cargo.outputs.version }} + run: | + set -euo pipefail + if [ "${INPUT_VERSION}" != "${CARGO_VERSION}" ]; then + echo "::error::Input version '${INPUT_VERSION}' does not match Cargo.toml version '${CARGO_VERSION}'." + exit 1 + fi + echo "Input version matches Cargo.toml: ${CARGO_VERSION}" + + - name: Cargo publish (dry run) + shell: bash + working-directory: releases/rust/db_esdk + run: cargo publish --dry-run + + - name: Cargo publish + shell: bash + working-directory: releases/rust/db_esdk + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish + + - name: Configure AWS Credentials for test_published.sh + uses: aws-actions/configure-aws-credentials@v6 + with: + aws-region: us-west-2 + role-to-assume: arn:aws:iam::370957321024:role/GitHub-CI-DDBEC-Dafny-Role-us-west-2 + role-session-name: DDBEC-Rust-Release-TestPublished + special-characters-workaround: "true" + + - name: Wait for new version to be available on crates.io + shell: bash + env: + CRATE_NAME: ${{ steps.cargo.outputs.name }} + CRATE_VERSION: ${{ steps.cargo.outputs.version }} + run: | + set -euo pipefail + # crates.io can take a few seconds to surface a freshly-published + # version via the sparse index. Poll for up to 5 minutes. + for i in $(seq 1 60); do + if curl -fsSL "https://crates.io/api/v1/crates/${CRATE_NAME}/${CRATE_VERSION}" >/dev/null 2>&1; then + echo "${CRATE_NAME} v${CRATE_VERSION} is available on crates.io" + exit 0 + fi + echo "Attempt ${i}: ${CRATE_NAME} v${CRATE_VERSION} not yet available; sleeping 5s" + sleep 5 + done + echo "::error::${CRATE_NAME} v${CRATE_VERSION} did not appear on crates.io within 5 minutes" + exit 1 + + - name: Test the published crate + shell: bash + working-directory: DynamoDbEncryption/runtimes/rust + run: ./test_published.sh "${{ steps.cargo.outputs.version }}" diff --git a/.github/workflows/rust-start-release.yml b/.github/workflows/rust-start-release.yml new file mode 100644 index 000000000..514448b74 --- /dev/null +++ b/.github/workflows/rust-start-release.yml @@ -0,0 +1,179 @@ +# This workflow regenerates `releases/rust/db_esdk/` for a new version of the +# aws-db-esdk crate by running DynamoDbEncryption/runtimes/rust/start_release.sh, +# committing the result on a release branch, and opening a PR back to main. +# +# `cargo publish` and `test_published.sh` live in rust-release.yml and +# should be dispatched on the resulting PR's branch before merging. +# +# This workflow only ever runs against the default branch (`main`); the +# version input must be in N.N.N form. The release branch is pushed and +# the release PR is opened as the Crypto Tools CI bot (via a PAT pulled +# from AWS Secrets Manager) so that the resulting PR's `pull_request` +# event triggers required-checks workflows on the release PR. +name: Rust Start Release + +on: + workflow_dispatch: + inputs: + version: + description: "New aws-db-esdk version in N.N.N format (e.g. '1.2.5')." + required: true + type: string + +permissions: {} + +jobs: + start-release: + name: Run start_release.sh and open a release PR + # Disallow dispatching from anywhere but the default branch; the workflow + # checks out main below regardless of github.ref, but failing fast here + # avoids confusion. + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-22.04 + permissions: + id-token: write + contents: write + env: + RUST_MIN_STACK: 838860800 + steps: + - name: Validate version input + shell: bash + env: + VERSION: ${{ github.event.inputs.version }} + run: | + set -euo pipefail + if ! [[ "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::Version '${VERSION}' must be in N.N.N format (e.g. '1.2.5')." + exit 1 + fi + + - name: Configure AWS Credentials for tests + uses: aws-actions/configure-aws-credentials@v6 + with: + aws-region: us-west-2 + role-to-assume: arn:aws:iam::370957321024:role/GitHub-CI-DDBEC-Dafny-Role-us-west-2 + role-session-name: DDBEC-Rust-Start-Release + special-characters-workaround: "true" + + - name: Support longpaths on Git checkout + run: | + git config --global core.longpaths true + + - uses: actions/checkout@v6 + with: + ref: main + fetch-depth: 0 + + - name: Init Submodules + shell: bash + run: | + git submodule update --init --recursive submodules/smithy-dafny + git submodule update --init --recursive submodules/MaterialProviders + + - name: Setup Rust Toolchain for GitHub CI + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: rustfmt, clippy + + - name: Setup Dafny + uses: ./submodules/MaterialProviders/.github/actions/setup_dafny/ + with: + dafny-version: 4.10.0 + + - name: Setup Java 17 for codegen + uses: actions/setup-java@v5 + with: + distribution: "corretto" + java-version: "17" + + - name: Install Smithy-Dafny codegen dependencies + uses: ./submodules/MaterialProviders/.github/actions/install_smithy_dafny_codegen_dependencies + with: + mpl-submodule-path: ./submodules/MaterialProviders/ + + - name: Configure Git + shell: bash + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + + - name: Create release branch + id: branch + shell: bash + env: + VERSION: ${{ github.event.inputs.version }} + run: | + set -euo pipefail + BRANCH="release/db_esdk/v${VERSION}" + git checkout -b "${BRANCH}" + echo "name=${BRANCH}" >> "$GITHUB_OUTPUT" + + - name: Run start_release.sh + shell: bash + working-directory: DynamoDbEncryption/runtimes/rust + env: + VERSION: ${{ github.event.inputs.version }} + run: ./start_release.sh "${VERSION}" + + - name: Commit regenerated releases/rust/db_esdk + shell: bash + env: + VERSION: ${{ github.event.inputs.version }} + run: | + set -euo pipefail + git add releases/rust/db_esdk DynamoDbEncryption/runtimes/rust/Cargo.toml + if git diff --cached --quiet; then + echo "::error::start_release.sh produced no changes; nothing to release." + exit 1 + fi + git commit -m "chore(release): aws-db-esdk v${VERSION}" + + # Switch from the testing role to the CI-bot-credential-access role so + # we can pull the CI bot's PAT and push/open the PR as the bot. This + # makes the resulting PR fire the repo's normal pull_request CI. + - name: Configure AWS Credentials for CI bot creds + uses: aws-actions/configure-aws-credentials@v6 + with: + aws-region: us-west-2 + role-to-assume: arn:aws:iam::587316601012:role/GitHub-CI-CI-Bot-Credential-Access-Role-us-west-2 + role-session-name: CI_Bot_RustStartRelease + special-characters-workaround: "true" + + - name: Get CI Bot Creds Secret + uses: aws-actions/aws-secretsmanager-get-secrets@v2 + with: + secret-ids: Github/aws-crypto-tools-ci-bot + parse-json-secrets: true + + - name: Push release branch and open release PR as CI bot + shell: bash + env: + BRANCH: ${{ steps.branch.outputs.name }} + VERSION: ${{ github.event.inputs.version }} + run: | + set -euo pipefail + # Authenticate gh as the CI bot. Following the pattern used by + # semantic_release.yml, write the token to a temp file and feed it + # to `gh auth login --with-token`. + echo '${{ env.GITHUB_AWS_CRYPTO_TOOLS_CI_BOT_ESDK_RELEASE_TOKEN }}' > token.txt + gh auth login --with-token < token.txt + rm token.txt + gh auth status + + # Push using the bot PAT so the push event is attributed to the bot + # and required pull_request CI workflows fire on the resulting PR. + REMOTE="https://x-access-token:$(gh auth token)@github.com/${GITHUB_REPOSITORY}.git" + git push "${REMOTE}" "${BRANCH}" + + gh pr create \ + --base main \ + --head "${BRANCH}" \ + --title "chore(release): aws-db-esdk v${VERSION}" \ + --body "Automated release PR generated by \`rust-start-release.yml\` for aws-db-esdk v${VERSION}. + + Reviewer checklist: + - Update \`CHANGELOG.md\` in the root directory with the changes for this version. + - If this is a major version bump, update \`SUPPORT_POLICY.rst\` for Rust. + - After approval and BEFORE merging, dispatch the \`Rust Release\` workflow on this branch to publish to crates.io and run \`test_published.sh\`. + + Do NOT merge this PR before publishing."