Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,46 @@ on:
- main

jobs:
changes:
name: Detect Rust changes
runs-on: ubuntu-latest
outputs:
rust: ${{ steps.filter.outputs.rust }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
# Avoids unnecessary Rust jobs running on `push` events; paths-filter
# needs the prior commit so it can do a proper diff, else it sees all
# files as new
fetch-depth: 2

# Doing a check here instead of using paths in our trigger conditions
# so that branch protection rules which require our Rust jobs aren't
# unsatisfied
- name: Filter
id: filter
uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d #v4.0.3
with:
filters: |
rust:
- "cli-engine/**"
- "cli-engine-macros/**"
- "Cargo.toml"
- "Cargo.lock"
- "rust-toolchain.toml"
- ".github/workflows/ci.yml"

rust:
name: Rust
needs: changes
# `always()` overrides the implicit skip-on-upstream-failure default, so
# a `changes` failure (e.g. paths-filter erroring) can't silently skip
# the one check branch protection requires — it fails safe into running
# Rust CI rather than trusting an output that was never produced.
if: |
always() &&
(needs.changes.result != 'success' || needs.changes.outputs.rust == 'true')
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/rust-toolchain-bump.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Bump Rust Toolchain

on:
schedule:
- cron: "13 7 * * 1"
workflow_dispatch: {}

permissions:
contents: write
pull-requests: write

env:
CARGO_TERM_COLOR: always

jobs:
bump:
name: Check for a newer stable Rust
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2

# Explicit `toolchain: stable` bypasses rust-toolchain.toml so this
# always installs the current real latest, not our pinned version.
- name: Install latest stable Rust
uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 #v1.16.1
with:
toolchain: stable

- name: Record latest stable version
id: latest
run: echo "version=$(rustc --version | cut -d' ' -f2)" >> "$GITHUB_OUTPUT"

- name: Update pinned toolchain
run: |
sed \
-i \
-E "s/^channel = \".*\"/channel = \"${{ steps.latest.outputs.version }}\"/" \
rust-toolchain.toml

# No-op (and no PR) when rust-toolchain.toml already has this version,
# since there's nothing left for create-pull-request to commit.
#
# GitHub does not trigger `pull_request`/`push` workflow runs for
# branches/PRs created with the default GITHUB_TOKEN (anti-recursion
# protection), so the resulting PR would never run ci.yml unless a PAT
# or GitHub App token is supplied here instead. Falls back to
# GITHUB_TOKEN (today's no-auto-CI behavior) until that secret exists.
- name: Open pull request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 #v8.1.1
Comment thread
jpage-godaddy marked this conversation as resolved.
with:
token: ${{ secrets.AUTOMATED_PRS_TOKEN || secrets.GITHUB_TOKEN }}
commit-message: "chore(rust): bump pinned toolchain to ${{ steps.latest.outputs.version }}"
title: "chore(rust): bump pinned toolchain to ${{ steps.latest.outputs.version }}"
body: |
Weekly check found a newer stable Rust release.

`rust-toolchain.toml` is now pinned to `${{ steps.latest.outputs.version }}`. The
normal CI checks run on this PR like any other — if the new toolchain introduces
clippy/rustdoc/test failures, push fixes to this branch before merging so the bump
and its fixes land together.
branch: chore/bump-rust-toolchain
delete-branch: true
labels: |
rust
dependencies
5 changes: 4 additions & 1 deletion cli-engine/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,14 @@ impl<'req> CredentialRequest<'req> {
}
}

#[async_trait]
/// Named auth provider used by middleware and transport injectors.
///
/// Implementations own their credential cache strategy. The framework only
/// routes calls and passes command context (`env`, colon command path, and tier).
// async_trait's expansion attaches a bare `#[must_use]` to each generated
// method, duplicating the one already implied by `Result`.
#[allow(clippy::double_must_use)]
#[async_trait]
pub trait AuthProvider: Send + Sync + std::fmt::Debug {
/// Stable provider registration name, for example `primary` or `oauth`.
fn name(&self) -> &str;
Expand Down
3 changes: 3 additions & 0 deletions cli-engine/src/auth/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ impl<'key> CredentialKey<'key> {
/// Values are opaque strings (typically JSON); the backend never interprets
/// them, so it stays independent of any provider's token shape. Callers own
/// (de)serialization and any validity/expiry checks.
// async_trait's expansion attaches a bare `#[must_use]` to each generated
// method, duplicating the one already implied by `Option`/`Result`.
#[allow(clippy::double_must_use)]
#[async_trait]
pub trait CredentialStorage: Send + Sync + std::fmt::Debug {
/// Loads the stored blob for `key`, or `None` when absent or unreadable.
Expand Down
2 changes: 1 addition & 1 deletion cli-engine/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
//! [`crate::flags::resolve_default_output_format`].
//!
//! where `${PREFIX}` is the app id sanitized by
//! [`app_id_env_prefix`](crate::flags::app_id_env_prefix).
//! [`crate::flags::app_id_env_prefix`].

use std::cell::Cell;
use std::path::{Path, PathBuf};
Expand Down
15 changes: 12 additions & 3 deletions cli-engine/src/middleware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,14 +380,17 @@ fn identity_key(credential: &Credential) -> &str {
}
}

#[async_trait]
/// Authorization hook called before business logic.
///
/// The authorizer receives a [`CredentialResolver`] rather than an
/// already-resolved credential so authorization remains lazy: an authorizer that
/// does not need identity never triggers a credential/auth flow. Call
/// [`CredentialResolver::try_resolve`] only when a decision actually depends on
/// the credential.
// async_trait's expansion attaches a bare `#[must_use]` to each generated
// method, duplicating the one already implied by `Result`.
#[allow(clippy::double_must_use)]
#[async_trait]
pub trait Authorizer: Send + Sync + std::fmt::Debug {
/// Verifies whether `command_path` may run with the provided args, reason, and tier.
async fn authorize(
Expand All @@ -400,8 +403,11 @@ pub trait Authorizer: Send + Sync + std::fmt::Debug {
) -> Result<()>;
}

#[async_trait]
/// Audit hook called for success, error, denied, auth-error, and dry-run outcomes.
// async_trait's expansion attaches a bare `#[must_use]` to each generated
// method, duplicating the one already implied by `Result`.
#[allow(clippy::double_must_use)]
#[async_trait]
pub trait Auditor: Send + Sync + std::fmt::Debug {
/// Appends an audit record.
async fn append(
Expand All @@ -414,8 +420,11 @@ pub trait Auditor: Send + Sync + std::fmt::Debug {
) -> Result<()>;
}

#[async_trait]
/// Activity hook for structured command lifecycle events.
// async_trait's expansion attaches a bare `#[must_use]` to each generated
// method, duplicating the one already implied by `Result`.
#[allow(clippy::double_must_use)]
#[async_trait]
pub trait ActivityEmitter: Send + Sync + std::fmt::Debug {
/// Emits one completed command event.
async fn emit(&self, event: ActivityEvent) -> Result<()>;
Expand Down
5 changes: 4 additions & 1 deletion cli-engine/src/transport/injector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ use crate::{AuthProvider, CliCoreError, Result};
pub type TokenFunc =
Arc<dyn Fn() -> Pin<Box<dyn Future<Output = Result<String>> + Send>> + Send + Sync>;

#[async_trait::async_trait]
/// Mutates an outbound request with authentication material.
// async_trait's expansion attaches a bare `#[must_use]` to each generated
// method, duplicating the one already implied by `Result`.
#[allow(clippy::double_must_use)]
#[async_trait::async_trait]
pub trait AuthInjector: Send + Sync + std::fmt::Debug {
/// Adds auth headers or cookies to `request`.
async fn inject(&self, request: &mut reqwest::Request) -> Result<()>;
Expand Down
3 changes: 3 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[toolchain]
channel = "1.99.0"
components = ["clippy", "rustfmt"]
Loading