Skip to content

Commit 90a15b4

Browse files
anbei.yuanCopilot
andcommitted
ci: merge web+rust into single job and apply rustfmt
The previous workflow had Rust and Web as parallel jobs, but the Rust build needs web/dist/ at compile time (rust-embed embeds the SPA into the binary). Without the web build running first, both clippy and test fail in CI on a fresh checkout. Fixed by: - Single 'build' job: npm ci + npm run build → cargo fmt --check → cargo clippy → cargo test - Added Swatinem/rust-cache for faster incremental CI - Added Node + npm cache via setup-node - Dropped the macOS matrix entry to halve runtime (ubuntu is representative enough for a personal project) - Applied cargo fmt to existing source (one block in skills.rs was out of style) Verified locally: fmt clean, clippy clean (-D warnings), 22/22 tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4f3522b commit 90a15b4

9 files changed

Lines changed: 72 additions & 30 deletions

File tree

.github/workflows/ci.yml

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,38 @@
11
name: CI
22
on: [push, pull_request]
3+
4+
env:
5+
CARGO_TERM_COLOR: always
6+
37
jobs:
4-
rust:
5-
runs-on: ${{ matrix.os }}
6-
strategy: { matrix: { os: [ubuntu-latest, macos-latest] } }
7-
steps:
8-
- uses: actions/checkout@v4
9-
- uses: dtolnay/rust-toolchain@stable
10-
- run: cargo fmt --all -- --check
11-
- run: cargo clippy --all-targets -- -D warnings
12-
- run: cargo test --locked
13-
web:
8+
build:
149
runs-on: ubuntu-latest
1510
steps:
1611
- uses: actions/checkout@v4
12+
1713
- uses: actions/setup-node@v4
18-
with: { node-version: 20 }
19-
- run: cd web && npm ci && npm run build
14+
with:
15+
node-version: 20
16+
cache: npm
17+
cache-dependency-path: web/package-lock.json
18+
19+
- name: Build web bundle
20+
working-directory: web
21+
run: |
22+
npm ci
23+
npm run build
24+
25+
- uses: dtolnay/rust-toolchain@stable
26+
with:
27+
components: rustfmt, clippy
28+
29+
- uses: Swatinem/rust-cache@v2
30+
31+
- name: cargo fmt --check
32+
run: cargo fmt --all -- --check
33+
34+
- name: cargo clippy
35+
run: cargo clippy --all-targets --workspace -- -D warnings
36+
37+
- name: cargo test
38+
run: cargo test --workspace --locked

crates/pawscope-claude/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
//! Active detection: Claude Code writes no PID lock; we mark a session "active" when
99
//! its file mtime is within the last 5 minutes.
1010
11+
use async_trait::async_trait;
12+
use chrono::{DateTime, Utc};
1113
use pawscope_core::{
1214
AgentAdapter, AgentKind, CoreError, Result, SessionDetail, SessionEvent, SessionMeta,
1315
SessionStatus,
1416
};
15-
use async_trait::async_trait;
16-
use chrono::{DateTime, Utc};
1717
use std::collections::HashMap;
1818
use std::path::{Path, PathBuf};
1919
use std::sync::{Arc, RwLock};

crates/pawscope-codex/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
use async_trait::async_trait;
2+
use chrono::{DateTime, TimeZone, Utc};
13
use pawscope_core::{
24
AgentAdapter, AgentKind, CoreError, Result, SessionDetail, SessionEvent, SessionMeta,
35
SessionStatus,
46
};
5-
use async_trait::async_trait;
6-
use chrono::{DateTime, TimeZone, Utc};
77
use rusqlite::Connection;
88
use std::path::{Path, PathBuf};
99
use std::sync::{Arc, Mutex};

crates/pawscope-copilot/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ pub mod lock;
33
pub mod watcher;
44
pub mod workspace;
55

6+
use async_trait::async_trait;
67
use pawscope_core::{
78
AgentAdapter, AgentKind, CoreError, Result, SessionDetail, SessionEvent, SessionMeta,
89
SessionStatus,
910
};
10-
use async_trait::async_trait;
1111
use std::collections::HashMap;
1212
use std::path::{Path, PathBuf};
1313
use std::sync::{Arc, RwLock};

crates/pawscope-copilot/src/watcher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::events;
2-
use pawscope_core::{CoreError, Result, SessionEvent};
32
use notify::{Config, PollWatcher, RecursiveMode, Watcher};
3+
use pawscope_core::{CoreError, Result, SessionEvent};
44
use std::collections::{HashMap, HashSet};
55
use std::path::PathBuf;
66
use std::sync::{Arc, RwLock};

crates/pawscope-server/src/api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::AppState;
2-
use pawscope_core::SessionStatus;
32
use axum::{
43
Json,
54
extract::{Path, State},
65
http::StatusCode,
76
response::IntoResponse,
87
};
8+
use pawscope_core::SessionStatus;
99
use std::collections::HashMap;
1010

1111
pub async fn list_sessions(State(s): State<AppState>) -> impl IntoResponse {

crates/pawscope-server/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
use axum::{
2+
Router,
3+
routing::{get, post},
4+
};
15
use pawscope_core::AgentAdapter;
2-
use axum::{Router, routing::{get, post}};
36
use std::sync::Arc;
47
use tokio::sync::broadcast;
58

@@ -59,8 +62,8 @@ pub fn spawn_watcher(state: AppState) {
5962
#[cfg(test)]
6063
mod tests {
6164
use super::*;
62-
use pawscope_core::*;
6365
use async_trait::async_trait;
66+
use pawscope_core::*;
6467
use std::sync::Arc;
6568
use tokio::sync::mpsc;
6669

crates/pawscope-server/src/multi.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Fan-out adapter that combines multiple `AgentAdapter`s into one.
22
3-
use pawscope_core::{AgentAdapter, Result, SessionDetail, SessionEvent, SessionMeta};
43
use async_trait::async_trait;
4+
use pawscope_core::{AgentAdapter, Result, SessionDetail, SessionEvent, SessionMeta};
55
use std::collections::HashMap;
66
use std::sync::Arc;
77
use tokio::sync::mpsc;
@@ -37,8 +37,7 @@ impl AgentAdapter for MultiAdapter {
3737
Err(e) => last_err = Some(e),
3838
}
3939
}
40-
Err(last_err
41-
.unwrap_or_else(|| pawscope_core::CoreError::NotFound(session_id.to_string())))
40+
Err(last_err.unwrap_or_else(|| pawscope_core::CoreError::NotFound(session_id.to_string())))
4241
}
4342

4443
async fn session_activity_hourly(&self, session_id: &str, hours: u32) -> Result<Vec<u64>> {

crates/pawscope-server/src/skills.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,18 @@ pub async fn list_skills(State(state): State<AppState>) -> Json<SkillsResponse>
3939

4040
let home = std::env::var("HOME").unwrap_or_default();
4141
let sources = vec![
42-
("copilot-superpowers", PathBuf::from(format!("{home}/.copilot/installed-plugins"))),
43-
("claude-skills", PathBuf::from(format!("{home}/.claude/skills"))),
44-
("agents-skills", PathBuf::from(format!("{home}/.agents/skills"))),
42+
(
43+
"copilot-superpowers",
44+
PathBuf::from(format!("{home}/.copilot/installed-plugins")),
45+
),
46+
(
47+
"claude-skills",
48+
PathBuf::from(format!("{home}/.claude/skills")),
49+
),
50+
(
51+
"agents-skills",
52+
PathBuf::from(format!("{home}/.agents/skills")),
53+
),
4554
];
4655

4756
let mut skills = Vec::new();
@@ -56,10 +65,18 @@ pub async fn list_skills(State(state): State<AppState>) -> Json<SkillsResponse>
5665
for s in &mut skills {
5766
s.invocations = invocations.get(&s.name).copied().unwrap_or(0);
5867
}
59-
skills.sort_by(|a, b| b.invocations.cmp(&a.invocations).then_with(|| a.name.cmp(&b.name)));
68+
skills.sort_by(|a, b| {
69+
b.invocations
70+
.cmp(&a.invocations)
71+
.then_with(|| a.name.cmp(&b.name))
72+
});
6073

6174
let total = skills.len();
62-
Json(SkillsResponse { skills, total, by_source })
75+
Json(SkillsResponse {
76+
skills,
77+
total,
78+
by_source,
79+
})
6380
}
6481

6582
fn scan_skills_recursive(root: &Path, source: &str, max_depth: usize) -> Vec<SkillEntry> {
@@ -288,7 +305,11 @@ pub async fn skill_usage(
288305
.into_iter()
289306
.map(|(name, sessions)| SkillCoOccurrence { name, sessions })
290307
.collect();
291-
cooccurring.sort_by(|a, b| b.sessions.cmp(&a.sessions).then_with(|| a.name.cmp(&b.name)));
308+
cooccurring.sort_by(|a, b| {
309+
b.sessions
310+
.cmp(&a.sessions)
311+
.then_with(|| a.name.cmp(&b.name))
312+
});
292313
cooccurring.truncate(12);
293314

294315
Ok(Json(SkillUsage {

0 commit comments

Comments
 (0)