Skip to content

Commit a7a9371

Browse files
committed
fix examples and github workflows
1 parent 0849119 commit a7a9371

File tree

8 files changed

+271
-65
lines changed

8 files changed

+271
-65
lines changed

.github/dependabot.yml

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,57 @@
11
version: 2
22
updates:
3+
# Cargo dependencies
34
- package-ecosystem: "cargo"
45
directory: "/"
56
schedule:
67
interval: "weekly"
78
day: "monday"
8-
time: "09:00"
9+
time: "10:00"
10+
timezone: "America/New_York"
911
open-pull-requests-limit: 10
1012
reviewers:
11-
- "your-username" # Replace with actual username
13+
- "blacksky-algorithms"
1214
assignees:
13-
- "your-username" # Replace with actual username
15+
- "blacksky-algorithms"
1416
commit-message:
1517
prefix: "deps"
16-
include: "scope"
18+
prefix-development: "deps(dev)"
19+
include: "scope"
20+
labels:
21+
- "dependencies"
22+
- "rust"
23+
allow:
24+
# Allow all dependency updates
25+
- dependency-type: "all"
26+
# Group minor and patch updates together to reduce PR volume
27+
groups:
28+
rust-dependencies:
29+
patterns:
30+
- "*"
31+
update-types:
32+
- "minor"
33+
- "patch"
34+
35+
# GitHub Actions dependencies
36+
- package-ecosystem: "github-actions"
37+
directory: "/"
38+
schedule:
39+
interval: "weekly"
40+
day: "monday"
41+
time: "10:30"
42+
timezone: "America/New_York"
43+
open-pull-requests-limit: 5
44+
reviewers:
45+
- "blacksky-algorithms"
46+
assignees:
47+
- "blacksky-algorithms"
48+
commit-message:
49+
prefix: "ci"
50+
include: "scope"
51+
labels:
52+
- "dependencies"
53+
- "github-actions"
54+
groups:
55+
github-actions:
56+
patterns:
57+
- "*"

.github/workflows/ci.yml

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,27 @@ env:
1111
RUST_BACKTRACE: 1
1212

1313
jobs:
14+
msrv:
15+
name: Minimum Supported Rust Version (MSRV)
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Install Rust MSRV
22+
uses: dtolnay/rust-toolchain@stable
23+
with:
24+
toolchain: 1.70.0 # Must match rust-version in Cargo.toml
25+
26+
- name: Cache dependencies
27+
uses: Swatinem/rust-cache@v2
28+
29+
- name: Check MSRV
30+
run: cargo check --all-targets --all-features
31+
32+
- name: Test MSRV
33+
run: cargo test --all-features
34+
1435
check-style:
1536
name: Check Style
1637
runs-on: ubuntu-latest
@@ -152,8 +173,71 @@ jobs:
152173
- name: Install Rust stable
153174
uses: dtolnay/rust-toolchain@stable
154175

155-
- name: Install cargo-audit
156-
run: cargo install cargo-audit
176+
- name: Security audit
177+
uses: rustsec/audit-check@v1.4.1
178+
with:
179+
token: ${{ secrets.GITHUB_TOKEN }}
180+
181+
sbom:
182+
name: Software Bill of Materials (SBOM)
183+
runs-on: ubuntu-latest
184+
steps:
185+
- name: Checkout
186+
uses: actions/checkout@v4
187+
188+
- name: Install Rust stable
189+
uses: dtolnay/rust-toolchain@stable
190+
191+
- name: Cache dependencies
192+
uses: Swatinem/rust-cache@v2
193+
194+
- name: Install cargo-cyclonedx
195+
run: cargo install cargo-cyclonedx
196+
197+
- name: Generate SBOM (JSON)
198+
run: cargo cyclonedx --format json --output-file rsdo-sbom.json
199+
200+
- name: Upload SBOM JSON artifact
201+
uses: actions/upload-artifact@v4
202+
with:
203+
name: rsdo-sbom-json
204+
path: rsdo-sbom.json
205+
retention-days: 30
206+
207+
- name: Generate SBOM (XML)
208+
run: cargo cyclonedx --format xml --output-file rsdo-sbom.xml
209+
210+
- name: Upload SBOM XML artifact
211+
uses: actions/upload-artifact@v4
212+
with:
213+
name: rsdo-sbom-xml
214+
path: rsdo-sbom.xml
215+
retention-days: 30
157216

158-
- name: Run security audit
159-
run: cargo audit
217+
coverage:
218+
name: Code Coverage
219+
runs-on: ubuntu-latest
220+
steps:
221+
- name: Checkout
222+
uses: actions/checkout@v4
223+
224+
- name: Install Rust stable
225+
uses: dtolnay/rust-toolchain@stable
226+
with:
227+
components: llvm-tools-preview
228+
229+
- name: Cache dependencies
230+
uses: Swatinem/rust-cache@v2
231+
232+
- name: Install cargo-llvm-cov
233+
uses: taiki-e/install-action@cargo-llvm-cov
234+
235+
- name: Generate code coverage
236+
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
237+
238+
- name: Upload coverage to Codecov
239+
uses: codecov/codecov-action@v4
240+
with:
241+
files: lcov.info
242+
fail_ci_if_error: false
243+
token: ${{ secrets.CODECOV_TOKEN }}

.github/workflows/release.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,5 +341,51 @@ jobs:
341341
tag_name: ${{ needs.check-and-release.outputs.tag_name }}
342342
files: target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
343343
fail_on_unmatched_files: true
344+
env:
345+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
346+
347+
generate-sbom:
348+
name: Generate and Attach SBOM
349+
runs-on: ubuntu-latest
350+
needs: check-and-release
351+
if: needs.check-and-release.outputs.should_release == 'true'
352+
353+
steps:
354+
- name: Checkout
355+
uses: actions/checkout@v4
356+
with:
357+
ref: ${{ needs.check-and-release.outputs.tag_name }}
358+
359+
- name: Install Rust stable
360+
uses: dtolnay/rust-toolchain@stable
361+
362+
- name: Cache cargo registry
363+
uses: actions/cache@v4
364+
with:
365+
path: |
366+
~/.cargo/registry
367+
~/.cargo/git
368+
target
369+
key: ${{ runner.os }}-sbom-cargo-${{ hashFiles('Cargo.lock') }}
370+
371+
- name: Install cargo-cyclonedx
372+
run: cargo install cargo-cyclonedx
373+
374+
- name: Generate SBOM (JSON)
375+
run: |
376+
cargo cyclonedx --format json --output-file rsdo-sbom-${{ needs.check-and-release.outputs.version }}.json
377+
378+
- name: Generate SBOM (XML)
379+
run: |
380+
cargo cyclonedx --format xml --output-file rsdo-sbom-${{ needs.check-and-release.outputs.version }}.xml
381+
382+
- name: Upload SBOM files to release
383+
uses: softprops/action-gh-release@v2
384+
with:
385+
tag_name: ${{ needs.check-and-release.outputs.tag_name }}
386+
files: |
387+
rsdo-sbom-${{ needs.check-and-release.outputs.version }}.json
388+
rsdo-sbom-${{ needs.check-and-release.outputs.version }}.xml
389+
fail_on_unmatched_files: true
344390
env:
345391
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ edition = "2021"
55
authors = ["Clinton Bowen <clinton.bowen@gmail.com>"]
66
description = "A Rust client library for the DigitalOcean API"
77
license = "Apache-2.0"
8-
repository = "https://https://github.com/blacksky-algorithms/rsdo"
8+
repository = "https://github.com/blacksky-algorithms/rsdo"
99
documentation = "https://docs.rs/rsdo"
1010
keywords = ["digitalocean", "api", "client", "async"]
1111
categories = ["api-bindings", "web-programming::http-client"]
12+
rust-version = "1.85.1"
1213

1314
[dependencies]
1415
progenitor-client = "0.11.0"
@@ -33,4 +34,4 @@ reqwest = { version = "0.12", features = ["blocking", "json"] }
3334
zip = "2.1"
3435

3536
[dev-dependencies]
36-
tokio = { version = "1.0", features = ["full"] }
37+
tokio = { version = "1.0", features = ["full"] }

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
[![Crates.io](https://img.shields.io/crates/v/rsdo.svg)](https://crates.io/crates/rsdo)
99
[![Documentation](https://docs.rs/rsdo/badge.svg)](https://docs.rs/rsdo)
1010
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
11+
[![CI](https://github.com/blacksky-algorithms/rsdo/workflows/CI/badge.svg)](https://github.com/blacksky-algorithms/rsdo/actions/workflows/ci.yml)
12+
[![MSRV](https://img.shields.io/badge/MSRV-1.70.0-blue.svg)](https://github.com/blacksky-algorithms/rsdo#minimum-supported-rust-version-msrv)
13+
[![Codecov](https://codecov.io/gh/blacksky-algorithms/rsdo/branch/main/graph/badge.svg)](https://codecov.io/gh/blacksky-algorithms/rsdo)
1114

1215
A comprehensive, type-safe Rust client for the DigitalOcean API, automatically generated from the official OpenAPI specification using [progenitor](https://github.com/oxidecomputer/progenitor).
1316

@@ -361,6 +364,31 @@ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for gui
361364
- **Documentation**: Help improve our examples and documentation
362365
- **Features**: Suggest new features or improvements
363366

367+
## Minimum Supported Rust Version (MSRV)
368+
369+
**Current MSRV**: `1.70.0`
370+
371+
### MSRV Policy
372+
373+
This crate follows an aggressive MSRV policy to take advantage of the latest Rust language features, performance improvements, and safety enhancements:
374+
375+
-**MSRV can be raised at any time** for new features, safety improvements, or maintainability
376+
-**MSRV increases will result in a semver minor release** (not patch)
377+
-**We will always document MSRV changes** in release notes and changelog
378+
-**No advance warning period** - we adopt new language features as soon as they're beneficial
379+
380+
### For Users
381+
382+
- If you need to support older Rust versions, **pin to a specific version range** in your `Cargo.toml`:
383+
```toml
384+
[dependencies]
385+
rsdo = ">=1.0.0, <1.2.0" # Example: avoid MSRV bumps in 1.2.0+
386+
```
387+
- Check the [`rust-version`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field) field in our `Cargo.toml` for the current MSRV
388+
- Our CI automatically tests against the declared MSRV to ensure compatibility
389+
390+
This policy allows us to provide the safest, most performant, and maintainable DigitalOcean client possible by leveraging the latest Rust ecosystem improvements.
391+
364392
## API Reference
365393

366394
Full API documentation is available at [docs.rs/rsdo](https://docs.rs/rsdo).

examples/create_droplet.rs

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
1919

2020
// First, let's check available regions and sizes
2121
println!("📍 Checking available regions...");
22-
let regions = client.regions_list().await?;
22+
let regions = client.regions_list(None, None).await?;
2323
let available_regions: Vec<_> = regions.into_inner().regions
2424
.into_iter()
2525
.filter(|r| r.available)
@@ -30,7 +30,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
3030
println!("Available regions: {}", available_regions.join(", "));
3131

3232
println!("\n💾 Checking available sizes...");
33-
let sizes = client.sizes_list().await?;
33+
let sizes = client.sizes_list(None, None).await?;
3434
let small_sizes: Vec<_> = sizes.into_inner().sizes
3535
.into_iter()
3636
.filter(|s| s.memory <= 2048) // Only show smaller sizes
@@ -41,17 +41,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4141
println!("Available sizes: {}", small_sizes.join(", "));
4242

4343
// Create droplet configuration
44-
let droplet_name = format!("rsdo-example-{}", chrono::Utc::now().timestamp());
44+
let droplet_name = format!("rsdo-example-{}", std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs());
4545

46-
let create_request = DropletCreate {
47-
name: droplet_name.clone(),
48-
region: "nyc1".to_string(),
46+
let create_request = DropletsCreateBody::SingleDropletRequest {
47+
name: droplet_name.clone().parse()?,
48+
region: Some("nyc1".to_string()),
4949
size: "s-1vcpu-1gb".to_string(),
50-
image: DropletCreateImage::Slug("ubuntu-22-04-x64".to_string()),
51-
ssh_keys: None,
52-
backups: Some(false),
53-
ipv6: Some(true),
54-
monitoring: Some(true),
50+
image: SingleDropletRequestImage::Variant0("ubuntu-22-04-x64".to_string()),
51+
ssh_keys: vec![],
52+
backups: false,
53+
ipv6: true,
54+
monitoring: true,
55+
private_networking: false,
56+
backup_policy: None,
5557
tags: Some(vec![
5658
"rsdo".to_string(),
5759
"example".to_string(),
@@ -64,7 +66,7 @@ apt-get update
6466
apt-get install -y curl
6567
"#.to_string(),
6668
),
67-
volumes: None,
69+
volumes: vec![],
6870
vpc_uuid: None,
6971
with_droplet_agent: Some(true),
7072
};
@@ -77,29 +79,27 @@ apt-get install -y curl
7779
let response = client.droplets_create(&create_request).await?;
7880
let new_droplet = response.into_inner();
7981

80-
println!("\n✅ Droplet created successfully!");
81-
println!(" ID: {}", new_droplet.droplet.id);
82-
println!(" Name: {}", new_droplet.droplet.name);
83-
println!(" Status: {}", new_droplet.droplet.status);
84-
println!(" Created: {}", new_droplet.droplet.created_at);
82+
match new_droplet {
83+
DropletsCreateResponse::SingleDropletResponse { droplet, .. } => {
84+
println!("\n✅ Droplet created successfully!");
85+
println!(" ID: {}", droplet.id);
86+
println!(" Name: {}", droplet.name);
87+
println!(" Status: {}", droplet.status);
88+
println!(" Created: {}", droplet.created_at);
8589

86-
// If there are associated actions, show them
87-
if !new_droplet.actions.is_empty() {
88-
println!("\n⚡ Associated actions:");
89-
for action in &new_droplet.actions {
90-
println!(" - {} ({}): {}",
91-
action.type_,
92-
action.id,
93-
action.status
94-
);
90+
println!("\n💡 The droplet is being created. You can check its status with:");
91+
println!(" cargo run --example get_droplet {}", droplet.id);
92+
93+
println!("\n⚠️ Remember to delete this droplet when you're done to avoid charges:");
94+
println!(" cargo run --example delete_droplet {}", droplet.id);
95+
}
96+
DropletsCreateResponse::MultipleDropletResponse { droplets, .. } => {
97+
println!("\n✅ Multiple droplets created successfully!");
98+
for droplet in droplets {
99+
println!(" ID: {}, Name: {}", droplet.id, droplet.name);
100+
}
95101
}
96102
}
97103

98-
println!("\n💡 The droplet is being created. You can check its status with:");
99-
println!(" cargo run --example get_droplet {}", new_droplet.droplet.id);
100-
101-
println!("\n⚠️ Remember to delete this droplet when you're done to avoid charges:");
102-
println!(" cargo run --example delete_droplet {}", new_droplet.droplet.id);
103-
104104
Ok(())
105105
}

0 commit comments

Comments
 (0)