Merge fix/shared-ecosystem-ci: shared Arch CI image + clippy fixes
All checks were successful
dev release / build (push) Successful in 36s
All checks were successful
dev release / build (push) Successful in 36s
This commit is contained in:
commit
598f85d930
12 changed files with 97 additions and 53 deletions
24
.forgejo/workflows/check.yml
Normal file
24
.forgejo/workflows/check.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
name: check
|
||||
|
||||
# Fast-fail lint/test on short-lived work branches, before it ever reaches
|
||||
# main and triggers a dev-track release build.
|
||||
on:
|
||||
push:
|
||||
branches: ['feature/**', 'fix/**']
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: [self-hosted, hestia]
|
||||
steps:
|
||||
- name: checkout
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf src && mkdir src
|
||||
git clone --branch "${GITHUB_REF_NAME}" --depth 1 \
|
||||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||
|
||||
- name: clippy
|
||||
run: cd src && bash ci/build.sh cargo clippy --workspace --all-targets --locked -- -D warnings
|
||||
|
||||
- name: test
|
||||
run: cd src && bash ci/build.sh cargo test --workspace --locked
|
||||
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||
|
||||
- name: build
|
||||
run: cd src && cargo build --release --locked
|
||||
run: cd src && bash ci/build.sh cargo build --release --locked
|
||||
|
||||
- name: compute dev version
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||
|
||||
- name: build
|
||||
run: cd src && cargo build --release --locked
|
||||
run: cd src && bash ci/build.sh cargo build --release --locked
|
||||
|
||||
- name: prepare artifacts
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" src
|
||||
|
||||
- name: build
|
||||
run: cd src && cargo build --release --locked
|
||||
run: cd src && bash ci/build.sh cargo build --release --locked
|
||||
|
||||
- name: prepare artifacts
|
||||
run: |
|
||||
|
|
|
|||
1
ci/bread-ecosystem.rev
Normal file
1
ci/bread-ecosystem.rev
Normal file
|
|
@ -0,0 +1 @@
|
|||
620c5a1317a6b57276eabca961facdb78bf510db
|
||||
20
ci/build.sh
Executable file
20
ci/build.sh
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
# Delegates to bread-ecosystem's shared CI build image/script, pinned to
|
||||
# the commit in ci/bread-ecosystem.rev — not `main`. bread-ecosystem's CI
|
||||
# files now affect every product's release pipeline, so bumping the pin
|
||||
# is a deliberate act instead of silent drift.
|
||||
#
|
||||
# Usage: ci/build.sh cargo build --release --locked
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
REV="$(cat "${ROOT}/ci/bread-ecosystem.rev")"
|
||||
|
||||
CACHE_DIR="/tmp/bread-ecosystem-ci-${REV}"
|
||||
if [ ! -d "$CACHE_DIR" ]; then
|
||||
rm -rf /tmp/bread-ecosystem-ci-*
|
||||
git clone https://git.breadway.dev/Breadway/bread-ecosystem.git "$CACHE_DIR"
|
||||
git -C "$CACHE_DIR" checkout --quiet "$REV"
|
||||
fi
|
||||
|
||||
bash "${CACHE_DIR}/ci/build.sh" breadmon "$ROOT" "$@"
|
||||
|
|
@ -103,7 +103,7 @@ pub fn snap_position(
|
|||
}
|
||||
|
||||
/// Move the selected monitor by (dx, dy) pixels, then snap.
|
||||
pub fn move_selected(state: &LayoutState, monitors: &mut Vec<Monitor>, dx: i32, dy: i32) {
|
||||
pub fn move_selected(state: &LayoutState, monitors: &mut [Monitor], dx: i32, dy: i32) {
|
||||
let idx = state.selected;
|
||||
if idx >= monitors.len() {
|
||||
return;
|
||||
|
|
@ -116,7 +116,7 @@ pub fn move_selected(state: &LayoutState, monitors: &mut Vec<Monitor>, dx: i32,
|
|||
}
|
||||
|
||||
/// Place monitors in a left-to-right row with no gaps.
|
||||
pub fn auto_arrange(monitors: &mut Vec<Monitor>) {
|
||||
pub fn auto_arrange(monitors: &mut [Monitor]) {
|
||||
let mut cursor = 0i32;
|
||||
for m in monitors.iter_mut() {
|
||||
m.x = cursor;
|
||||
|
|
|
|||
|
|
@ -62,11 +62,10 @@ pub fn find_mirror_modes(source: &Monitor, target: &Monitor) -> Option<MirrorRes
|
|||
// Approximate: check all source ARs within 5%
|
||||
for &s_ar in src_by_ar.keys() {
|
||||
let s_ratio = ratio_f64(s_ar);
|
||||
if (s_ratio - tgt_ratio).abs() / s_ratio < 0.05 {
|
||||
if !candidates.iter().any(|c| c.src_ar == s_ar && c.tgt_ar == tgt_ar) {
|
||||
if (s_ratio - tgt_ratio).abs() / s_ratio < 0.05
|
||||
&& !candidates.iter().any(|c| c.src_ar == s_ar && c.tgt_ar == tgt_ar) {
|
||||
candidates.push(Candidate { src_ar: s_ar, tgt_ar, is_exact: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ pub fn from_monitors(name: &str, monitors: &[Monitor]) -> Profile {
|
|||
|
||||
/// Apply a profile's settings onto a list of live monitors (matched by name).
|
||||
/// Monitors not in the profile are left unchanged.
|
||||
pub fn apply_to_monitors(profile: &Profile, monitors: &mut Vec<Monitor>) {
|
||||
pub fn apply_to_monitors(profile: &Profile, monitors: &mut [Monitor]) {
|
||||
for pm in &profile.monitors {
|
||||
if let Some(m) = monitors.iter_mut().find(|m| m.name == pm.name) {
|
||||
if let Some(mode) = Mode::parse(&format!("{}Hz", pm.mode)) {
|
||||
|
|
|
|||
|
|
@ -242,14 +242,14 @@ fn handle_field_key(event: KeyEvent, state: &mut AppState) {
|
|||
state.dirty = true;
|
||||
}
|
||||
}
|
||||
KeyCode::Char('l') | KeyCode::Right => {
|
||||
if state.config.res_idx + 1 < state.config.resolutions.len() {
|
||||
state.config.res_idx += 1;
|
||||
let m = &state.monitors[idx];
|
||||
state.config.update_refreshes(m);
|
||||
sync_mode_to_monitor(state, idx);
|
||||
state.dirty = true;
|
||||
}
|
||||
KeyCode::Char('l') | KeyCode::Right
|
||||
if state.config.res_idx + 1 < state.config.resolutions.len() =>
|
||||
{
|
||||
state.config.res_idx += 1;
|
||||
let m = &state.monitors[idx];
|
||||
state.config.update_refreshes(m);
|
||||
sync_mode_to_monitor(state, idx);
|
||||
state.dirty = true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
|
@ -261,12 +261,12 @@ fn handle_field_key(event: KeyEvent, state: &mut AppState) {
|
|||
state.dirty = true;
|
||||
}
|
||||
}
|
||||
KeyCode::Char('l') | KeyCode::Right => {
|
||||
if state.config.refresh_idx + 1 < state.config.refreshes.len() {
|
||||
state.config.refresh_idx += 1;
|
||||
sync_mode_to_monitor(state, idx);
|
||||
state.dirty = true;
|
||||
}
|
||||
KeyCode::Char('l') | KeyCode::Right
|
||||
if state.config.refresh_idx + 1 < state.config.refreshes.len() =>
|
||||
{
|
||||
state.config.refresh_idx += 1;
|
||||
sync_mode_to_monitor(state, idx);
|
||||
state.dirty = true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
|
@ -335,12 +335,12 @@ fn handle_field_key(event: KeyEvent, state: &mut AppState) {
|
|||
state.dirty = true;
|
||||
}
|
||||
}
|
||||
KeyCode::Char('l') | KeyCode::Right => {
|
||||
if state.config.mirror_idx + 1 < state.config.mirror_options.len() {
|
||||
state.config.mirror_idx += 1;
|
||||
sync_mirror_to_monitor(state, idx);
|
||||
state.dirty = true;
|
||||
}
|
||||
KeyCode::Char('l') | KeyCode::Right
|
||||
if state.config.mirror_idx + 1 < state.config.mirror_options.len() =>
|
||||
{
|
||||
state.config.mirror_idx += 1;
|
||||
sync_mirror_to_monitor(state, idx);
|
||||
state.dirty = true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -259,9 +259,9 @@ pub fn canvas_area(terminal_size: (u16, u16)) -> Rect {
|
|||
}
|
||||
|
||||
fn in_canvas(col: u16, row: u16, canvas: Rect) -> bool {
|
||||
col >= canvas.x + 1
|
||||
col > canvas.x
|
||||
&& col < canvas.x + canvas.width.saturating_sub(1)
|
||||
&& row >= canvas.y + 1
|
||||
&& row > canvas.y
|
||||
&& row < canvas.y + canvas.height.saturating_sub(1)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,33 +210,33 @@ pub fn handle_mouse(event: MouseEvent, state: &mut AppState) {
|
|||
}
|
||||
}
|
||||
}
|
||||
r if r >= 7 => {
|
||||
r if r >= 7
|
||||
// Result panel: Apply is on the line with buttons.
|
||||
// Rough column check: col < 20 = Apply, col >= 20 = Cancel
|
||||
if state.mirror.result.is_some() {
|
||||
let col = event.column;
|
||||
if col < 20 {
|
||||
// Activate Apply
|
||||
state.mirror.focused = FIELDS.iter().position(|&f| f == MirrorField::Apply).unwrap_or(3);
|
||||
if let Some(result) = state.mirror.result.clone() {
|
||||
state.push_undo();
|
||||
let src_name = state.monitors[state.mirror.source_idx].name.clone();
|
||||
let tgt_idx = state.mirror.target_idx;
|
||||
state.monitors[tgt_idx].active_mode = result.mirror_mode.clone();
|
||||
state.monitors[tgt_idx].mirror_of = Some(src_name.clone());
|
||||
state.dirty = true;
|
||||
state.mirror.result = None;
|
||||
state.mirror.focused = 0;
|
||||
state.set_status(
|
||||
format!("Mirror set: {} → {} at {}", src_name, state.monitors[tgt_idx].name, result.mirror_mode),
|
||||
crate::ui::StatusLevel::Success,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Cancel
|
||||
&& state.mirror.result.is_some() =>
|
||||
{
|
||||
let col = event.column;
|
||||
if col < 20 {
|
||||
// Activate Apply
|
||||
state.mirror.focused = FIELDS.iter().position(|&f| f == MirrorField::Apply).unwrap_or(3);
|
||||
if let Some(result) = state.mirror.result.clone() {
|
||||
state.push_undo();
|
||||
let src_name = state.monitors[state.mirror.source_idx].name.clone();
|
||||
let tgt_idx = state.mirror.target_idx;
|
||||
state.monitors[tgt_idx].active_mode = result.mirror_mode.clone();
|
||||
state.monitors[tgt_idx].mirror_of = Some(src_name.clone());
|
||||
state.dirty = true;
|
||||
state.mirror.result = None;
|
||||
state.mirror.focused = 0;
|
||||
state.set_status(
|
||||
format!("Mirror set: {} → {} at {}", src_name, state.monitors[tgt_idx].name, result.mirror_mode),
|
||||
crate::ui::StatusLevel::Success,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Cancel
|
||||
state.mirror.result = None;
|
||||
state.mirror.focused = 0;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue