ci: consolidate CI into single job; fix sidebar footer (#14)
All checks were successful
All checks were successful
This commit was merged in pull request #14.
This commit is contained in:
@@ -2,11 +2,9 @@ name: CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
|
||||||
- "**"
|
|
||||||
pull_request:
|
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
@@ -23,10 +21,10 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Stage 1: Code quality checks (run in parallel)
|
# Stage 1: Lint, audit, and test (single job to share cargo cache)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
fmt:
|
check:
|
||||||
name: Format
|
name: Check
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container:
|
container:
|
||||||
image: rust:1.94-bookworm
|
image: rust:1.94-bookworm
|
||||||
@@ -37,105 +35,59 @@ jobs:
|
|||||||
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||||
git fetch --depth=1 origin "${GITHUB_SHA}"
|
git fetch --depth=1 origin "${GITHUB_SHA}"
|
||||||
git checkout FETCH_HEAD
|
git checkout FETCH_HEAD
|
||||||
- run: rustup component add rustfmt
|
- name: Install tools
|
||||||
# Format check does not compile, so sccache is not needed here.
|
|
||||||
- run: cargo fmt --all --check
|
|
||||||
env:
|
|
||||||
RUSTC_WRAPPER: ""
|
|
||||||
|
|
||||||
clippy:
|
|
||||||
name: Clippy
|
|
||||||
runs-on: docker
|
|
||||||
container:
|
|
||||||
image: rust:1.94-bookworm
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
run: |
|
|
||||||
git init
|
|
||||||
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
|
||||||
git fetch --depth=1 origin "${GITHUB_SHA}"
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
- name: Install sccache
|
|
||||||
run: |
|
run: |
|
||||||
|
rustup component add rustfmt clippy
|
||||||
curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
|
curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
|
||||||
| tar xz --strip-components=1 -C /usr/local/bin/ sccache-v0.9.1-x86_64-unknown-linux-musl/sccache
|
| tar xz --strip-components=1 -C /usr/local/bin/ sccache-v0.9.1-x86_64-unknown-linux-musl/sccache
|
||||||
chmod +x /usr/local/bin/sccache
|
chmod +x /usr/local/bin/sccache
|
||||||
- run: rustup component add clippy
|
cargo install cargo-audit --locked
|
||||||
# Lint the agent (native only).
|
env:
|
||||||
|
RUSTC_WRAPPER: ""
|
||||||
|
|
||||||
|
# Format (no compilation needed)
|
||||||
|
- name: Format
|
||||||
|
run: cargo fmt --all --check
|
||||||
|
env:
|
||||||
|
RUSTC_WRAPPER: ""
|
||||||
|
|
||||||
|
# Clippy (compiles once, sccache reuses across feature sets)
|
||||||
- name: Clippy (agent)
|
- name: Clippy (agent)
|
||||||
run: cargo clippy -p compliance-agent -- -D warnings
|
run: cargo clippy -p compliance-agent -- -D warnings
|
||||||
# Lint the dashboard for both feature sets independently.
|
|
||||||
# sccache deduplicates shared crates between the two compilations.
|
|
||||||
- name: Clippy (dashboard server)
|
- name: Clippy (dashboard server)
|
||||||
run: cargo clippy -p compliance-dashboard --features server --no-default-features -- -D warnings
|
run: cargo clippy -p compliance-dashboard --features server --no-default-features -- -D warnings
|
||||||
- name: Clippy (dashboard web)
|
- name: Clippy (dashboard web)
|
||||||
run: cargo clippy -p compliance-dashboard --features web --no-default-features -- -D warnings
|
run: cargo clippy -p compliance-dashboard --features web --no-default-features -- -D warnings
|
||||||
- name: Clippy (mcp)
|
- name: Clippy (mcp)
|
||||||
run: cargo clippy -p compliance-mcp -- -D warnings
|
run: cargo clippy -p compliance-mcp -- -D warnings
|
||||||
- name: Show sccache stats
|
|
||||||
run: sccache --show-stats
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
audit:
|
# Security audit
|
||||||
name: Security Audit
|
- name: Security Audit
|
||||||
runs-on: docker
|
run: cargo audit
|
||||||
if: github.ref == 'refs/heads/main'
|
|
||||||
container:
|
|
||||||
image: rust:1.94-bookworm
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
run: |
|
|
||||||
git init
|
|
||||||
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
|
||||||
git fetch --depth=1 origin "${GITHUB_SHA}"
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
- run: cargo install cargo-audit
|
|
||||||
env:
|
|
||||||
RUSTC_WRAPPER: ""
|
|
||||||
- run: cargo audit
|
|
||||||
env:
|
env:
|
||||||
RUSTC_WRAPPER: ""
|
RUSTC_WRAPPER: ""
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# Tests (reuses compilation artifacts from clippy)
|
||||||
# Stage 2: Tests (only after all quality checks pass)
|
- name: Tests (core + agent)
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
test:
|
|
||||||
name: Tests
|
|
||||||
runs-on: docker
|
|
||||||
needs: [fmt, clippy, audit]
|
|
||||||
container:
|
|
||||||
image: rust:1.94-bookworm
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
run: |
|
|
||||||
git init
|
|
||||||
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
|
||||||
git fetch --depth=1 origin "${GITHUB_SHA}"
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
- name: Install sccache
|
|
||||||
run: |
|
|
||||||
curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
|
|
||||||
| tar xz --strip-components=1 -C /usr/local/bin/ sccache-v0.9.1-x86_64-unknown-linux-musl/sccache
|
|
||||||
chmod +x /usr/local/bin/sccache
|
|
||||||
- name: Run tests (core + agent)
|
|
||||||
run: cargo test -p compliance-core -p compliance-agent
|
run: cargo test -p compliance-core -p compliance-agent
|
||||||
- name: Run tests (dashboard server)
|
- name: Tests (dashboard server)
|
||||||
run: cargo test -p compliance-dashboard --features server --no-default-features
|
run: cargo test -p compliance-dashboard --features server --no-default-features
|
||||||
- name: Run tests (dashboard web)
|
- name: Tests (dashboard web)
|
||||||
run: cargo test -p compliance-dashboard --features web --no-default-features
|
run: cargo test -p compliance-dashboard --features web --no-default-features
|
||||||
|
|
||||||
- name: Show sccache stats
|
- name: Show sccache stats
|
||||||
run: sccache --show-stats
|
run: sccache --show-stats
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Stage 3: Deploy (only on main, after tests pass)
|
# Stage 2: Deploy (only on main, after checks pass)
|
||||||
# Each service only deploys when its relevant files changed.
|
# Each service only deploys when its relevant files changed.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
detect-changes:
|
detect-changes:
|
||||||
name: Detect Changes
|
name: Detect Changes
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main'
|
||||||
needs: [test]
|
needs: [check]
|
||||||
container:
|
container:
|
||||||
image: alpine:latest
|
image: alpine:latest
|
||||||
outputs:
|
outputs:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://www.rust-lang.org/"><img src="https://img.shields.io/badge/Rust-1.89-orange?logo=rust&logoColor=white" alt="Rust" /></a>
|
<a href="https://www.rust-lang.org/"><img src="https://img.shields.io/badge/Rust-1.94-orange?logo=rust&logoColor=white" alt="Rust" /></a>
|
||||||
<a href="https://dioxuslabs.com/"><img src="https://img.shields.io/badge/Dioxus-0.7-blue?logo=webassembly&logoColor=white" alt="Dioxus" /></a>
|
<a href="https://dioxuslabs.com/"><img src="https://img.shields.io/badge/Dioxus-0.7-blue?logo=webassembly&logoColor=white" alt="Dioxus" /></a>
|
||||||
<a href="https://www.mongodb.com/"><img src="https://img.shields.io/badge/MongoDB-8.0-47A248?logo=mongodb&logoColor=white" alt="MongoDB" /></a>
|
<a href="https://www.mongodb.com/"><img src="https://img.shields.io/badge/MongoDB-8.0-47A248?logo=mongodb&logoColor=white" alt="MongoDB" /></a>
|
||||||
<a href="https://axum.rs/"><img src="https://img.shields.io/badge/Axum-0.8-4A4A55?logo=rust&logoColor=white" alt="Axum" /></a>
|
<a href="https://axum.rs/"><img src="https://img.shields.io/badge/Axum-0.8-4A4A55?logo=rust&logoColor=white" alt="Axum" /></a>
|
||||||
@@ -94,7 +94,7 @@ Compliance Scanner is an autonomous agent that continuously monitors git reposit
|
|||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Rust 1.89+
|
- Rust 1.94+
|
||||||
- [Dioxus CLI](https://dioxuslabs.com/learn/0.7/getting_started) (`dx`)
|
- [Dioxus CLI](https://dioxuslabs.com/learn/0.7/getting_started) (`dx`)
|
||||||
- MongoDB
|
- MongoDB
|
||||||
- Docker & Docker Compose (optional)
|
- Docker & Docker Compose (optional)
|
||||||
|
|||||||
@@ -262,13 +262,120 @@ code {
|
|||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-footer {
|
.sidebar-spacer {
|
||||||
padding: 14px 20px;
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-user {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 16px;
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
font-family: var(--font-mono);
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-user {
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 12px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, rgba(56, 189, 248, 0.2), rgba(139, 92, 246, 0.15));
|
||||||
|
border: 2px solid rgba(56, 189, 248, 0.2);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar:hover {
|
||||||
|
border-color: rgba(56, 189, 248, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-initials {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent);
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-link {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--text-tertiary);
|
color: var(--text-tertiary);
|
||||||
letter-spacing: 0.02em;
|
text-decoration: none;
|
||||||
|
transition: color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-link:hover {
|
||||||
|
color: #fca5a5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-btn-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-btn-icon:hover {
|
||||||
|
color: #fca5a5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-legal {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 16px 14px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-legal a {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-legal a:hover {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.legal-dot {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-toggle {
|
.sidebar-toggle {
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ pub fn Sidebar() -> Element {
|
|||||||
span { "Docs" }
|
span { "Docs" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Spacer pushes footer to the bottom
|
||||||
|
div { class: "sidebar-spacer" }
|
||||||
button {
|
button {
|
||||||
class: "sidebar-toggle",
|
class: "sidebar-toggle",
|
||||||
onclick: move |_| collapsed.set(!collapsed()),
|
onclick: move |_| collapsed.set(!collapsed()),
|
||||||
@@ -122,9 +124,8 @@ pub fn Sidebar() -> Element {
|
|||||||
let auth_info = use_context::<Signal<AuthInfo>>();
|
let auth_info = use_context::<Signal<AuthInfo>>();
|
||||||
let info = auth_info();
|
let info = auth_info();
|
||||||
let initials = info.name.chars().next().unwrap_or('U').to_uppercase().to_string();
|
let initials = info.name.chars().next().unwrap_or('U').to_uppercase().to_string();
|
||||||
let user_class = if collapsed() { "sidebar-user sidebar-user-collapsed" } else { "sidebar-user" };
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div { class: "{user_class}",
|
div { class: "sidebar-user",
|
||||||
div { class: "user-avatar",
|
div { class: "user-avatar",
|
||||||
if info.avatar_url.is_empty() {
|
if info.avatar_url.is_empty() {
|
||||||
span { class: "avatar-initials", "{initials}" }
|
span { class: "avatar-initials", "{initials}" }
|
||||||
@@ -133,13 +134,29 @@ pub fn Sidebar() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !collapsed() {
|
if !collapsed() {
|
||||||
span { class: "user-name", "{info.name}" }
|
div { class: "user-info",
|
||||||
|
span { class: "user-name", "{info.name}" }
|
||||||
|
a {
|
||||||
|
href: "/logout",
|
||||||
|
class: "logout-link",
|
||||||
|
"Sign out"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
a {
|
if collapsed() {
|
||||||
href: "/logout",
|
a {
|
||||||
class: if collapsed() { "logout-btn logout-btn-collapsed" } else { "logout-btn" },
|
href: "/logout",
|
||||||
title: "Sign out",
|
class: "logout-btn-icon",
|
||||||
Icon { icon: BsBoxArrowRight, width: 16, height: 16 }
|
title: "Sign out",
|
||||||
|
Icon { icon: BsBoxArrowRight, width: 14, height: 14 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !collapsed() {
|
||||||
|
div { class: "sidebar-legal",
|
||||||
|
a { href: "/privacy", "Privacy" }
|
||||||
|
span { class: "legal-dot", "·" }
|
||||||
|
a { href: "/impressum", "Impressum" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user