Skip to content

Commit bbc1863

Browse files
authored
Merge pull request #38 from OpenMined/madhava/otel-instrumentation
chore: standardize lint.sh scripts across repos
2 parents 2c364a3 + 2c99cfc commit bbc1863

File tree

11 files changed

+249
-67
lines changed

11 files changed

+249
-67
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,12 @@ jobs:
5252
with:
5353
workspaces: ". -> target"
5454

55-
- name: Check formatting
55+
- name: Run lint checks and tests
5656
if: matrix.os != 'windows-latest'
57-
run: cargo fmt --all -- --check
57+
run: ./lint.sh --check --test
5858

59-
- name: Run clippy (all targets)
60-
if: matrix.os != 'windows-latest'
61-
run: cargo clippy --all-targets --all-features --no-deps -- -D warnings
62-
63-
- name: Run tests
59+
- name: Run tests (Windows)
60+
if: matrix.os == 'windows-latest'
6461
run: cargo test --all-features --verbose
6562

6663
- name: Build

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ process = []
1313
auth = ["reqwest", "tokio"]
1414
embedded = ["dep:syftbox-rs"]
1515
test-plaintext = []
16+
telemetry = ["opentelemetry", "tracing-opentelemetry"]
1617

1718
[dependencies]
1819
anyhow = "1.0"
@@ -22,6 +23,8 @@ dirs = "5.0"
2223
walkdir = "2.4"
2324
thiserror = "1.0"
2425
tracing = "0.1"
26+
opentelemetry = { version = "0.27", optional = true }
27+
tracing-opentelemetry = { version = "0.28", optional = true }
2528
tokio = { version = "1.40", features = ["macros", "rt"], optional = true }
2629
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"], optional = true }
2730
base64 = "0.22"

clippy.sh

Lines changed: 0 additions & 8 deletions
This file was deleted.

lint.sh

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,82 @@
11
#!/usr/bin/env bash
2+
# lint.sh - Auto-fix + run quick tests (parallel, quiet on success)
3+
# Usage: ./lint.sh [--check] [--test]
4+
# --check Read-only mode for CI (no auto-fix)
5+
# --test Also run tests (slower)
26
set -euo pipefail
37

4-
echo "==> cargo fmt (check)"
5-
cargo fmt --all -- --check
8+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9+
cd "$ROOT_DIR"
610

7-
echo "==> cargo clippy (warnings as errors)"
8-
cargo clippy --all-targets --all-features --no-deps -- -D warnings
11+
RED='\033[0;31m'
12+
GREEN='\033[0;32m'
13+
CYAN='\033[0;36m'
14+
NC='\033[0m'
915

10-
echo "✓ Lint checks passed"
16+
CHECK_MODE=0
17+
RUN_TESTS=0
18+
for arg in "$@"; do
19+
case "$arg" in
20+
--check) CHECK_MODE=1 ;;
21+
--test) RUN_TESTS=1 ;;
22+
esac
23+
done
24+
25+
TMPDIR_LINT=$(mktemp -d)
26+
trap "rm -rf $TMPDIR_LINT" EXIT
27+
28+
FAILED=0
29+
PIDS=()
30+
TASKS=()
31+
32+
run_task() {
33+
local name="$1"
34+
local outfile="$TMPDIR_LINT/$name.out"
35+
shift
36+
TASKS+=("$name")
37+
echo -e "${CYAN}$name${NC}"
38+
(
39+
if "$@" > "$outfile" 2>&1; then
40+
echo "0" > "$outfile.status"
41+
else
42+
echo "1" > "$outfile.status"
43+
fi
44+
) &
45+
PIDS+=($!)
46+
}
47+
48+
wait_all() {
49+
local i=0
50+
for pid in "${PIDS[@]}"; do
51+
wait "$pid" || true
52+
local name="${TASKS[$i]}"
53+
local outfile="$TMPDIR_LINT/$name.out"
54+
if [[ -f "$outfile.status" && "$(cat "$outfile.status")" != "0" ]]; then
55+
echo -e "${RED}$name${NC}"
56+
cat "$outfile"
57+
echo ""
58+
FAILED=1
59+
fi
60+
i=$((i + 1))
61+
done
62+
}
63+
64+
if [[ "$CHECK_MODE" -eq 1 ]]; then
65+
run_task "rust-fmt" cargo fmt --all -- --check
66+
run_task "rust-clippy" cargo clippy --all-targets --all-features --no-deps -- -D warnings
67+
else
68+
run_task "rust-fmt" cargo fmt --all
69+
run_task "rust-clippy" cargo clippy --fix --allow-dirty --allow-staged --all-targets --all-features --no-deps -- -D warnings
70+
fi
71+
72+
if [[ "$RUN_TESTS" -eq 1 ]]; then
73+
run_task "rust-test" cargo test
74+
fi
75+
76+
wait_all
77+
78+
if [[ "$FAILED" -eq 0 ]]; then
79+
echo -e "${GREEN}✓ All checks passed${NC}"
80+
else
81+
exit 1
82+
fi

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ pub use syftbox::syc::{
2525
provision_local_identity_with_options, resolve_identity, restore_identity_from_mnemonic,
2626
IdentityProvisioningOutcome,
2727
};
28-
pub use syftbox::types::{RpcHeaders, RpcRequest, RpcResponse};
28+
pub use syftbox::types::{RpcHeaders, RpcRequest, RpcResponse, COMPONENT_ATTR, TRACEPARENT_HEADER};
29+
30+
#[cfg(feature = "telemetry")]
31+
pub use syftbox::types::trace_context;
2932

3033
#[cfg(feature = "auth")]
3134
pub use syftbox::auth::{request_otp, verify_otp, OtpRequestPayload, OtpTokens, OtpVerifyOutcome};

src/syftbox/control.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::thread;
1212
use std::time::{Duration, Instant};
1313

1414
const SYFTBOX_PIDFILE_NAME: &str = "syftbox.pid";
15+
#[cfg(feature = "embedded")]
1516
const SYFTBOX_EMBEDDED_PIDFILE_NAME: &str = "syftbox.embedded.pid";
1617

1718
#[cfg(target_os = "windows")]

src/syftbox/endpoint.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::syftbox::storage::WritePolicy;
33
use crate::syftbox::types::{RpcRequest, RpcResponse};
44
use anyhow::{anyhow, bail, Context, Result};
55
use std::path::{Path, PathBuf};
6+
use tracing::instrument;
67

78
/// Request with its file path
89
pub type RequestWithPath = (PathBuf, RpcRequest);
@@ -19,6 +20,7 @@ pub struct Endpoint {
1920

2021
impl Endpoint {
2122
/// Create a new endpoint for a SyftBox app
23+
#[instrument(skip(app), fields(component = "rpc", endpoint = %name), err)]
2224
pub fn new(app: &SyftBoxApp, name: &str) -> Result<Self> {
2325
let path = app.register_endpoint(name)?;
2426

@@ -31,13 +33,15 @@ impl Endpoint {
3133

3234
/// Check for request files in this endpoint (your own folder only)
3335
/// Requests are written by others TO you in datasites/your-email/app_data/<app>/rpc/<endpoint>/
36+
#[instrument(skip(self), fields(component = "rpc", endpoint = %self.name), err)]
3437
pub fn check_requests(&self) -> Result<Vec<(PathBuf, RpcRequest)>> {
3538
// Only check your own endpoint folder - this is where others write requests TO you
3639
self.check_requests_in_folder(&self.path)
3740
}
3841

3942
/// Check for request files and return both successes and failures
4043
/// Returns (successful_requests, failed_requests)
44+
#[instrument(skip(self), fields(component = "rpc", endpoint = %self.name), err)]
4145
pub fn check_requests_with_failures(
4246
&self,
4347
) -> Result<(Vec<RequestWithPath>, Vec<FailedRequest>)> {
@@ -109,6 +113,12 @@ impl Endpoint {
109113
}
110114

111115
/// Send a response for a request
116+
#[instrument(skip(self, request, response), fields(
117+
component = "rpc",
118+
endpoint = %self.name,
119+
request_id = %request.id,
120+
status = %response.status_code
121+
), err)]
112122
pub fn send_response(
113123
&self,
114124
request_path: &Path,
@@ -160,6 +170,13 @@ impl Endpoint {
160170
}
161171

162172
/// Create a request file (for sending requests to others)
173+
#[instrument(skip(self, request), fields(
174+
component = "rpc",
175+
endpoint = %self.name,
176+
request_id = %request.id,
177+
method = %request.method,
178+
recipient = %request.url
179+
), err)]
163180
pub fn create_request(&self, request: &RpcRequest) -> Result<PathBuf> {
164181
let request_filename = format!("{}.request", request.id);
165182
let request_path = self.path.join(request_filename);
@@ -183,6 +200,7 @@ impl Endpoint {
183200
/// Check for response files (when we sent a request and are waiting for response)
184201
/// Responses are in OTHER identity folders (where you wrote requests), NOT your own folder
185202
/// You write requests to datasites/their-email/, they write responses back there
203+
#[instrument(skip(self), fields(component = "rpc", endpoint = %self.name), err)]
186204
pub fn check_responses(&self) -> Result<Vec<(PathBuf, RpcResponse)>> {
187205
let mut responses = Vec::new();
188206

@@ -294,6 +312,7 @@ impl Endpoint {
294312
}
295313

296314
/// Clean up a response file after processing
315+
#[instrument(skip(self), fields(component = "rpc", endpoint = %self.name), err)]
297316
pub fn cleanup_response(&self, response_path: &Path) -> Result<()> {
298317
self.app.storage.remove_path(response_path)?;
299318
Ok(())

src/syftbox/rpc.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ use crate::syftbox::endpoint::Endpoint;
33
use crate::syftbox::types::{RpcRequest, RpcResponse};
44
use anyhow::Result;
55
use std::path::{Path, PathBuf};
6+
use tracing::instrument;
67

78
/// Check for requests across all endpoints of an app
9+
#[instrument(skip(app), fields(component = "rpc", app_email = %app.email), err)]
810
pub fn check_all_requests(app: &SyftBoxApp) -> Result<Vec<(String, PathBuf, RpcRequest)>> {
911
let mut all_requests = Vec::new();
1012

@@ -22,12 +24,19 @@ pub fn check_all_requests(app: &SyftBoxApp) -> Result<Vec<(String, PathBuf, RpcR
2224
}
2325

2426
/// Check for requests on a specific endpoint
27+
#[instrument(skip(app), fields(component = "rpc", endpoint = %endpoint_name), err)]
2528
pub fn check_requests(app: &SyftBoxApp, endpoint_name: &str) -> Result<Vec<(PathBuf, RpcRequest)>> {
2629
let endpoint = Endpoint::new(app, endpoint_name)?;
2730
endpoint.check_requests()
2831
}
2932

3033
/// Send a response to a request
34+
#[instrument(skip(app, request, response), fields(
35+
component = "rpc",
36+
endpoint = %endpoint_name,
37+
request_id = %request.id,
38+
status = %response.status_code
39+
), err)]
3140
pub fn send_response(
3241
app: &SyftBoxApp,
3342
endpoint_name: &str,
@@ -41,6 +50,13 @@ pub fn send_response(
4150
}
4251

4352
/// Create and send a request to another datasite
53+
#[instrument(skip(app, request), fields(
54+
component = "rpc",
55+
endpoint = %endpoint_name,
56+
request_id = %request.id,
57+
method = %request.method,
58+
recipient = %request.url
59+
), err)]
4460
pub fn send_request(
4561
app: &SyftBoxApp,
4662
endpoint_name: &str,
@@ -51,6 +67,7 @@ pub fn send_request(
5167
}
5268

5369
/// Check for responses to requests we've sent
70+
#[instrument(skip(app), fields(component = "rpc", endpoint = %endpoint_name), err)]
5471
pub fn check_responses(
5572
app: &SyftBoxApp,
5673
endpoint_name: &str,
@@ -60,6 +77,12 @@ pub fn check_responses(
6077
}
6178

6279
/// Helper to process a request and automatically send a response
80+
#[instrument(skip(app, request, handler), fields(
81+
component = "rpc",
82+
endpoint = %endpoint_name,
83+
request_id = %request.id,
84+
method = %request.method
85+
), err)]
6386
pub fn process_request<F>(
6487
app: &SyftBoxApp,
6588
endpoint_name: &str,

src/syftbox/storage.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use syft_crypto_protocol::datasite::{
1010
bytes::{read_bytes, write_bytes, BytesReadOpts, BytesWriteOpts, BytesWriteOutcome},
1111
context::{ensure_vault_layout, resolve_vault, AppContext},
1212
};
13-
use tracing::warn;
13+
use tracing::{instrument, warn};
1414
use walkdir::WalkDir;
1515

1616
/// Result from read_with_shadow when metadata is requested
@@ -143,6 +143,7 @@ impl SyftBoxStorage {
143143
/// Write encrypted file using shadow folder pattern:
144144
/// 1. Write plaintext to shadow folder
145145
/// 2. Encrypt from shadow → datasites
146+
#[instrument(skip(self, data), fields(component = "storage", size = data.len(), encrypted = true), err)]
146147
pub fn write_encrypted_with_shadow(
147148
&self,
148149
datasite_path: &Path,
@@ -218,6 +219,7 @@ impl SyftBoxStorage {
218219
/// Read encrypted file using shadow folder pattern:
219220
/// 1. Decrypt from datasites → shadow
220221
/// 2. Read plaintext from shadow
222+
#[instrument(skip(self), fields(component = "storage"), err)]
221223
pub fn read_with_shadow(&self, datasite_path: &Path) -> Result<Vec<u8>> {
222224
match &self.backend {
223225
StorageBackend::SyctCrypto(backend) => {
@@ -322,6 +324,7 @@ impl SyftBoxStorage {
322324

323325
/// Read encrypted file using shadow folder pattern, returning metadata about the sender.
324326
/// This is the same as read_with_shadow but also returns verified sender identity.
327+
#[instrument(skip(self), fields(component = "storage"), err)]
325328
pub fn read_with_shadow_metadata(&self, datasite_path: &Path) -> Result<ReadWithShadowResult> {
326329
match &self.backend {
327330
StorageBackend::SyctCrypto(backend) => {
@@ -416,6 +419,7 @@ impl SyftBoxStorage {
416419

417420
/// Write with shadow folder pattern - creates both encrypted file and plaintext shadow
418421
/// This makes unencrypted/ a true mirror of datasites/
422+
#[instrument(skip(self, plaintext), fields(component = "storage", size = plaintext.len()), err)]
419423
pub fn write_with_shadow(
420424
&self,
421425
absolute_path: &Path,

0 commit comments

Comments
 (0)