-
Notifications
You must be signed in to change notification settings - Fork 7
feat: add cargo publish action #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| name: "Cargo Publish" | ||
| description: "Publishes one Rust crate to crates.io using Trusted Publishing" | ||
| inputs: | ||
| package: | ||
| description: "Package name to publish. Leave empty to infer from the current package." | ||
| required: false | ||
| default: "" | ||
| working-directory: | ||
| description: "Directory containing the Cargo manifest or workspace." | ||
| required: false | ||
| default: "." | ||
| toolchain: | ||
| description: "Rust toolchain to install and use." | ||
| required: false | ||
| default: "stable" | ||
| build-command: | ||
| description: "Build command to run before publishing. Leave empty to skip." | ||
| required: false | ||
| default: "cargo build" | ||
| locked: | ||
| description: "Pass --locked to cargo publish." | ||
| required: false | ||
| default: "true" | ||
| allow-dirty: | ||
| description: "Pass --allow-dirty to cargo publish." | ||
| required: false | ||
| default: "false" | ||
| dry-run: | ||
| description: "Run publish validation without publishing to crates.io." | ||
| required: false | ||
| default: "false" | ||
| check-version-available: | ||
| description: "Fail early when this crate version already exists on crates.io." | ||
| required: false | ||
| default: "true" | ||
| skip-existing: | ||
| description: "Skip publishing successfully when this crate version already exists on crates.io." | ||
| required: false | ||
| default: "false" | ||
| outputs: | ||
| package: | ||
| description: "Published package name." | ||
| value: ${{ steps.package.outputs.package }} | ||
| version: | ||
| description: "Published package version." | ||
| value: ${{ steps.package.outputs.version }} | ||
| published: | ||
| description: "Whether the action published to crates.io." | ||
| value: ${{ steps.status.outputs.published }} | ||
| already-published: | ||
| description: "Whether this crate version already existed on crates.io." | ||
| value: ${{ steps.version_exists.outputs.exists }} | ||
|
|
||
| runs: | ||
| using: "composite" | ||
| steps: | ||
| - name: Install Rust toolchain | ||
| uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you trust this action? Or is just rustup enough? |
||
| with: | ||
| toolchain: ${{ inputs.toolchain }} | ||
|
|
||
| - name: Show Cargo version | ||
| shell: bash | ||
| run: cargo --version | ||
|
|
||
| - name: Run build command | ||
| if: inputs.build-command != '' | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| run: | | ||
| set -euo pipefail | ||
| ${{ inputs.build-command }} | ||
|
dev-jodee marked this conversation as resolved.
|
||
|
|
||
| - name: Resolve package version | ||
| id: package | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| PACKAGE_INPUT="${{ inputs.package }}" | ||
| if [ -n "$PACKAGE_INPUT" ]; then | ||
| if ! PKG_ID="$(cargo pkgid -p "$PACKAGE_INPUT" 2>&1)"; then | ||
| echo "$PKG_ID" | ||
| echo "::error::Could not resolve package '$PACKAGE_INPUT'." | ||
| exit 1 | ||
| fi | ||
| else | ||
| if ! PKG_ID="$(cargo pkgid 2>&1)"; then | ||
| echo "$PKG_ID" | ||
| echo "::error::Could not infer package. Set the package input when publishing from a workspace root." | ||
| exit 1 | ||
| fi | ||
| fi | ||
|
|
||
| NAME_VERSION="${PKG_ID##*#}" | ||
| PACKAGE="${NAME_VERSION%@*}" | ||
| VERSION="${NAME_VERSION##*@}" | ||
|
|
||
| if [ -z "$PACKAGE" ] || [ -z "$VERSION" ] || [ "$PACKAGE" = "$NAME_VERSION" ] || [ "$VERSION" = "$NAME_VERSION" ]; then | ||
| echo "::error::Could not parse package and version from cargo pkgid output: $PKG_ID" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "package=$PACKAGE" >> "$GITHUB_OUTPUT" | ||
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | ||
| echo "Publishing target: $PACKAGE@$VERSION" | ||
|
|
||
| - name: Verify crate package | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| PUBLISH_ARGS=(-p "${{ steps.package.outputs.package }}") | ||
| if [ "${{ inputs.locked }}" = "true" ]; then | ||
| PUBLISH_ARGS+=(--locked) | ||
| fi | ||
| if [ "${{ inputs.allow-dirty }}" = "true" ]; then | ||
| PUBLISH_ARGS+=(--allow-dirty) | ||
| fi | ||
|
|
||
| cargo publish "${PUBLISH_ARGS[@]}" --dry-run | ||
|
|
||
| - name: Check crates.io version availability | ||
| id: version_exists | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| if [ "${{ inputs.check-version-available }}" != "true" ] && [ "${{ inputs.skip-existing }}" != "true" ]; then | ||
| echo "exists=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| REGISTRY_CHECK_DIR="${RUNNER_TEMP:-/tmp}" | ||
| PACKAGE="${{ steps.package.outputs.package }}" | ||
| VERSION="${{ steps.package.outputs.version }}" | ||
|
|
||
| if (cd "$REGISTRY_CHECK_DIR" && cargo info "$PACKAGE@$VERSION" >/dev/null 2>&1); then | ||
| echo "exists=true" >> "$GITHUB_OUTPUT" | ||
| if [ "${{ inputs.skip-existing }}" = "true" ]; then | ||
| echo "$PACKAGE@$VERSION already exists on crates.io; skipping publish." | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "::error::$PACKAGE@$VERSION already exists on crates.io. Bump the crate version before publishing." | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "exists=false" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Authenticate crates.io trusted publisher | ||
| if: inputs.dry-run != 'true' && steps.version_exists.outputs.exists != 'true' | ||
| id: crates_io_auth | ||
| uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe # v1.0.4 | ||
|
|
||
| - name: Publish to crates.io | ||
| if: inputs.dry-run != 'true' && steps.version_exists.outputs.exists != 'true' | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| env: | ||
| CARGO_REGISTRY_TOKEN: ${{ steps.crates_io_auth.outputs.token }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| PUBLISH_ARGS=(-p "${{ steps.package.outputs.package }}") | ||
| if [ "${{ inputs.locked }}" = "true" ]; then | ||
| PUBLISH_ARGS+=(--locked) | ||
| fi | ||
| if [ "${{ inputs.allow-dirty }}" = "true" ]; then | ||
| PUBLISH_ARGS+=(--allow-dirty) | ||
| fi | ||
|
|
||
| cargo publish "${PUBLISH_ARGS[@]}" | ||
|
|
||
| - name: Set publish status | ||
| id: status | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| if [ "${{ inputs.dry-run }}" = "true" ]; then | ||
| echo "published=false" >> "$GITHUB_OUTPUT" | ||
| elif [ "${{ steps.version_exists.outputs.exists }}" = "true" ]; then | ||
| echo "published=false" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "published=true" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,6 +82,74 @@ Customize the workflow to your needs! | |
| - `program`: Program name to build | ||
| - `features`: Optional Cargo features to enable | ||
|
|
||
| ### Cargo Publishing | ||
|
|
||
| - `cargo-publish`: Publishes one Rust crate to crates.io using Trusted Publishing | ||
| - Uses GitHub OIDC and `rust-lang/crates-io-auth-action` | ||
| - Runs a build command and `cargo publish --dry-run` before publishing | ||
| - Optionally checks whether the crate version already exists on crates.io | ||
| - Leaves checkout, tests, generated clients, tags, and GitHub releases to the caller workflow | ||
| - Inputs: | ||
| - `package`: Package name to publish, or empty to infer from the current package | ||
| - `working-directory`: Directory containing the Cargo manifest or workspace | ||
| - `toolchain`: Rust toolchain to install and use | ||
| - `build-command`: Build command to run before publishing, or empty to skip | ||
| - `locked`: Pass `--locked` to `cargo publish` | ||
| - `allow-dirty`: Pass `--allow-dirty` to `cargo publish` | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably good to add an explanation here what this does and that ppl should not use it |
||
| - `dry-run`: Validate without publishing to crates.io | ||
| - `check-version-available`: Fail early when this crate version already exists on crates.io | ||
| - `skip-existing`: Skip publishing successfully when this crate version already exists on crates.io | ||
| - Outputs: | ||
| - `package`: Published package name | ||
| - `version`: Published package version | ||
| - `published`: Whether the action published to crates.io | ||
| - `already-published`: Whether this crate version already existed on crates.io | ||
|
|
||
| Caller workflows must grant OIDC token access and configure Trusted Publishing for the crate on crates.io. The Trusted Publisher configuration should match the caller repository and workflow file, not this shared action repository: | ||
|
|
||
| ```yaml | ||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
| ``` | ||
|
|
||
| Single crate: | ||
|
|
||
| ```yaml | ||
| - uses: actions/checkout@v6 | ||
|
|
||
| - uses: solana-developers/github-actions/cargo-publish@main | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should not recommend @main |
||
| with: | ||
| package: solana-keychain | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rather write: Path to package root or path to where the cargo toml lives or smth like that |
||
| working-directory: rust | ||
| ``` | ||
|
|
||
| Workspace package: | ||
|
|
||
| ```yaml | ||
| - uses: actions/checkout@v6 | ||
|
|
||
| - uses: solana-developers/github-actions/cargo-publish@main | ||
| with: | ||
| package: kora-lib | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of kora-lib could write "path to package root or smth" not everyone knows kora |
||
| build-command: cargo build --workspace | ||
| ``` | ||
|
|
||
| Generated client: | ||
|
|
||
| ```yaml | ||
| - uses: actions/checkout@v6 | ||
|
|
||
| - run: pnpm run generate-clients | ||
|
|
||
| - uses: solana-developers/github-actions/cargo-publish@main | ||
| with: | ||
| package: subscriptions | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here. Readers of the readme dont know what subscriptions means |
||
| working-directory: clients/rust | ||
| allow-dirty: "true" | ||
| skip-existing: "true" | ||
| ``` | ||
|
|
||
| ### Deployment | ||
|
|
||
| - `write-program-buffer`: Writes a buffer that will then later be set either from the provided keypair or from the squads multisig | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.