Add CI quality gate

This commit is contained in:
Mikhail Kilin
2026-05-17 18:55:36 +03:00
parent 2d4c1906d5
commit 51e9cf5c10
13 changed files with 50 additions and 60 deletions

View File

@@ -8,6 +8,14 @@ steps:
- rustup component add rustfmt - rustup component add rustfmt
- cargo fmt -- --check - cargo fmt -- --check
- name: check
image: rust:latest
environment:
CARGO_HOME: /tmp/cargo
commands:
- apt-get update -qq && apt-get install -y -qq pkg-config libssl-dev libdbus-1-dev zlib1g-dev > /dev/null 2>&1
- cargo check --all-targets --all-features
- name: clippy - name: clippy
image: rust:latest image: rust:latest
environment: environment:
@@ -15,7 +23,7 @@ steps:
commands: commands:
- apt-get update -qq && apt-get install -y -qq pkg-config libssl-dev libdbus-1-dev zlib1g-dev > /dev/null 2>&1 - apt-get update -qq && apt-get install -y -qq pkg-config libssl-dev libdbus-1-dev zlib1g-dev > /dev/null 2>&1
- rustup component add clippy - rustup component add clippy
- cargo clippy -- -D warnings - cargo clippy --all-targets --all-features -- -D warnings
- name: test - name: test
image: rust:latest image: rust:latest
@@ -23,4 +31,4 @@ steps:
CARGO_HOME: /tmp/cargo CARGO_HOME: /tmp/cargo
commands: commands:
- apt-get update -qq && apt-get install -y -qq pkg-config libssl-dev libdbus-1-dev zlib1g-dev > /dev/null 2>&1 - apt-get update -qq && apt-get install -y -qq pkg-config libssl-dev libdbus-1-dev zlib1g-dev > /dev/null 2>&1
- cargo test - cargo test --all-features

View File

@@ -13,5 +13,6 @@
- Не запускай `cargo run`, `cargo build`, `cargo test`, `cargo check` без прямой команды пользователя. - Не запускай `cargo run`, `cargo build`, `cargo test`, `cargo check` без прямой команды пользователя.
- Не коммить изменения, пока пользователь не попросит. - Не коммить изменения, пока пользователь не попросит.
- Если пользователь попросил тесты/коммит/план до конца, используй quality gate из [DEVELOPMENT.md](DEVELOPMENT.md).
- После функциональной правки дай короткий ручной сценарий проверки. - После функциональной правки дай короткий ручной сценарий проверки.
- Обновляй [CONTEXT.md](CONTEXT.md), только если изменились статус, риск, архитектурное решение или следующий шаг. - Обновляй [CONTEXT.md](CONTEXT.md), только если изменились статус, риск, архитектурное решение или следующий шаг.

View File

@@ -21,6 +21,20 @@ cargo check
В финальном ответе после изменения укажи, какие cargo-команды не запускались, и дай минимальную ручную проверку. В финальном ответе после изменения укажи, какие cargo-команды не запускались, и дай минимальную ручную проверку.
## Quality Gate
Если пользователь прямо попросил проверить, закоммитить или выполнить план с тестами, используй тот же набор проверок, что и CI:
```bash
cargo fmt -- --check
cargo check --all-targets --all-features
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
git diff --check
```
Перед коммитом не оставляй `*.snap.new` файлы.
## Scope ## Scope
- Делай одну логическую правку за раз. - Делай одну логическую правку за раз.

View File

@@ -43,9 +43,7 @@ fn benchmark_format_simple_text(c: &mut Criterion) {
let entities = vec![]; let entities = vec![];
c.bench_function("format_simple_text", |b| { c.bench_function("format_simple_text", |b| {
b.iter(|| { b.iter(|| format_text_with_entities(black_box(&text), black_box(&entities), Color::White));
format_text_with_entities(black_box(&text), black_box(&entities), Color::White)
});
}); });
} }
@@ -53,9 +51,7 @@ fn benchmark_format_markdown_text(c: &mut Criterion) {
let (text, entities) = create_text_with_entities(); let (text, entities) = create_text_with_entities();
c.bench_function("format_markdown_text", |b| { c.bench_function("format_markdown_text", |b| {
b.iter(|| { b.iter(|| format_text_with_entities(black_box(&text), black_box(&entities), Color::White));
format_text_with_entities(black_box(&text), black_box(&entities), Color::White)
});
}); });
} }
@@ -79,9 +75,7 @@ fn benchmark_format_long_text(c: &mut Criterion) {
} }
c.bench_function("format_long_text_with_100_entities", |b| { c.bench_function("format_long_text_with_100_entities", |b| {
b.iter(|| { b.iter(|| format_text_with_entities(black_box(&text), black_box(&entities), Color::White));
format_text_with_entities(black_box(&text), black_box(&entities), Color::White)
});
}); });
} }

View File

@@ -216,11 +216,11 @@ Target files:
Steps: Steps:
- [ ] Add CI steps for `cargo check --all-targets --all-features`. - [x] Add CI steps for `cargo check --all-targets --all-features`.
- [ ] Add CI steps for `cargo clippy --all-targets --all-features -- -D warnings`. - [x] Add CI steps for `cargo clippy --all-targets --all-features -- -D warnings`.
- [ ] Add CI steps for `cargo test --all-features`. - [x] Add CI steps for `cargo test --all-features`.
- [ ] Document the same commands in `DEVELOPMENT.md` or `AGENT.md`. - [x] Document the same commands in `DEVELOPMENT.md` or `AGENT.md`.
- [ ] Keep CI commands aligned with the commands used by agents and humans locally. - [x] Keep CI commands aligned with the commands used by agents and humans locally.
Acceptance criteria: Acceptance criteria:
@@ -231,13 +231,13 @@ Acceptance criteria:
Before considering the refactor layer complete: Before considering the refactor layer complete:
- [ ] `cargo check --all-targets --all-features` passes. - [x] `cargo check --all-targets --all-features` passes.
- [ ] `cargo clippy --all-targets --all-features -- -D warnings` passes. - [x] `cargo clippy --all-targets --all-features -- -D warnings` passes.
- [ ] `cargo test --all-features` passes. - [x] `cargo test --all-features` passes.
- [ ] `git diff --check` passes. - [x] `git diff --check` passes.
- [ ] No unexpected `*.snap.new` files remain. - [x] No unexpected `*.snap.new` files remain.
- [ ] `rg -n "current_chat_messages_mut|chats_mut|folders_mut|pending_user_ids_mut|user_cache_mut" src/tdlib` shows only intentionally contained internal access. - [x] `rg -n "current_chat_messages_mut|chats_mut|folders_mut|pending_user_ids_mut|user_cache_mut" src/tdlib` shows only intentionally contained internal access.
- [ ] `rg -n "unwrap\\(|expect\\(|panic!\\(" src` has no risky production UI or TDLib data-path panics left. - [x] `rg -n "unwrap\\(|expect\\(|panic!\\(" src` has no risky production UI or TDLib data-path panics left.
## Recommended Commit Order ## Recommended Commit Order

View File

@@ -33,17 +33,12 @@ pub fn acquire_lock(account_name: &str) -> Result<File, String> {
// Ensure parent directory exists // Ensure parent directory exists
if let Some(parent) = lock_path.parent() { if let Some(parent) = lock_path.parent() {
fs::create_dir_all(parent).map_err(|e| { fs::create_dir_all(parent)
format!( .map_err(|e| format!("Не удалось создать директорию для lock-файла: {}", e))?;
"Не удалось создать директорию для lock-файла: {}",
e
)
})?;
} }
let file = File::create(&lock_path).map_err(|e| { let file = File::create(&lock_path)
format!("Не удалось создать lock-файл {}: {}", lock_path.display(), e) .map_err(|e| format!("Не удалось создать lock-файл {}: {}", lock_path.display(), e))?;
})?;
file.try_lock_exclusive().map_err(|_| { file.try_lock_exclusive().map_err(|_| {
format!( format!(

View File

@@ -74,4 +74,3 @@ pub async fn select_folder<T: TdClientTrait>(app: &mut App<T>, folder_idx: usize
app.chat_list_state.select(Some(0)); app.chat_list_state.select(Some(0));
} }
} }

View File

@@ -21,10 +21,7 @@ pub mod modal;
pub mod profile; pub mod profile;
pub mod search; pub mod search;
pub use chat_loader::{ pub use chat_loader::{process_chat_init_events, process_pending_chat_init};
process_chat_init_events,
process_pending_chat_init,
};
pub use clipboard::*; pub use clipboard::*;
pub use global::*; pub use global::*;
pub use profile::get_available_actions_count; pub use profile::get_available_actions_count;

View File

@@ -56,12 +56,7 @@ pub fn render<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &App<T>) {
if idx > 0 { if idx > 0 {
lines.push(Line::from("")); lines.push(Line::from(""));
} }
lines.extend(render_message_item( lines.extend(render_message_item(msg, idx == selected_index, content_width, 3));
msg,
idx == selected_index,
content_width,
3,
));
} }
if lines.is_empty() { if lines.is_empty() {

View File

@@ -80,12 +80,7 @@ pub fn render<T: TdClientTrait>(f: &mut Frame, area: Rect, app: &App<T>) {
if idx > 0 { if idx > 0 {
lines.push(Line::from("")); lines.push(Line::from(""));
} }
lines.extend(render_message_item( lines.extend(render_message_item(msg, idx == selected_index, content_width, 2));
msg,
idx == selected_index,
content_width,
2,
));
} }
} }

View File

@@ -1,6 +1,6 @@
use chrono::{DateTime, Local, NaiveDate, Utc};
#[cfg(test)] #[cfg(test)]
use chrono::FixedOffset; use chrono::FixedOffset;
use chrono::{DateTime, Local, NaiveDate, Utc};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
pub trait LocalTimeSource { pub trait LocalTimeSource {

View File

@@ -56,7 +56,6 @@ fn snapshot_chat_with_unread_count() {
#[test] #[test]
fn test_incoming_message_shows_unread_badge() { fn test_incoming_message_shows_unread_badge() {
// Создаём чат БЕЗ непрочитанных сообщений // Создаём чат БЕЗ непрочитанных сообщений
let chat = TestChatBuilder::new("Friend", 999) let chat = TestChatBuilder::new("Friend", 999)
.unread_count(0) .unread_count(0)
@@ -485,7 +484,6 @@ fn snapshot_chat_search_mode() {
#[test] #[test]
fn snapshot_chat_with_online_status() { fn snapshot_chat_with_online_status() {
let chat = TestChatBuilder::new("Alice", 123) let chat = TestChatBuilder::new("Alice", 123)
.last_message("Hey there!") .last_message("Hey there!")
.build(); .build();

View File

@@ -144,19 +144,13 @@ impl TestAppBuilder {
/// Добавить сообщение для чата /// Добавить сообщение для чата
pub fn with_message(mut self, chat_id: i64, message: MessageInfo) -> Self { pub fn with_message(mut self, chat_id: i64, message: MessageInfo) -> Self {
self.messages self.messages.entry(chat_id).or_default().push(message);
.entry(chat_id)
.or_default()
.push(message);
self self
} }
/// Добавить несколько сообщений для чата /// Добавить несколько сообщений для чата
pub fn with_messages(mut self, chat_id: i64, messages: Vec<MessageInfo>) -> Self { pub fn with_messages(mut self, chat_id: i64, messages: Vec<MessageInfo>) -> Self {
self.messages self.messages.entry(chat_id).or_default().extend(messages);
.entry(chat_id)
.or_default()
.extend(messages);
self self
} }