Skip to content

Commit 146aadc

Browse files
committed
macos 15 runner (#47)
Signed-off-by: Daniel Guns <danbguns@gmail.com>
1 parent cfa0ea1 commit 146aadc

7 files changed

Lines changed: 332 additions & 68 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
asset_name: flux9s-macos-aarch64.tar.gz
6060
use_tar: true
6161

62-
- os: macos-13
62+
- os: macos-15-intel
6363
target: x86_64-apple-darwin
6464
artifact_name: flux9s
6565
asset_name: flux9s-macos-x86_64.tar.gz

src/tui/app.rs

Lines changed: 212 additions & 59 deletions
Large diffs are not rendered by default.

src/tui/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,13 +277,18 @@ pub async fn run_tui(
277277
// Process watch events (non-blocking)
278278
// Update state from watch events
279279
let mut events_processed = 0;
280+
// Track resource type count to detect when header layout needs recalculation
281+
let resource_type_count_before = app.state().count_by_type().len();
282+
280283
while let Ok(event) = event_rx.try_recv() {
281284
events_processed += 1;
282285
match event {
283286
crate::watcher::WatchEvent::Applied(resource_type, ns, name, obj_json) => {
284287
let key = crate::watcher::resource_key(&ns, &name, &resource_type);
285288
let (suspended, ready, message, revision) =
286289
crate::watcher::extract_status_fields(&obj_json);
290+
let labels = crate::watcher::extract_labels(&obj_json);
291+
let annotations = crate::watcher::extract_annotations(&obj_json);
287292
app.state().upsert(
288293
key.clone(),
289294
crate::watcher::ResourceInfo {
@@ -295,6 +300,8 @@ pub async fn run_tui(
295300
ready,
296301
message,
297302
revision,
303+
labels,
304+
annotations,
298305
},
299306
);
300307
// Store full object for detail view
@@ -317,6 +324,11 @@ pub async fn run_tui(
317324

318325
// Force a redraw if we processed events
319326
if events_processed > 0 {
327+
// Check if number of resource types changed (affects header layout)
328+
let resource_type_count_after = app.state().count_by_type().len();
329+
if resource_type_count_after != resource_type_count_before {
330+
app.notify_resource_types_changed();
331+
}
320332
terminal.draw(|f| app.render(f))?;
321333
}
322334
}

src/tui/operations.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use crate::watcher::ResourceInfo;
77
use anyhow::Result;
88
use kube::Api;
99
use serde_json::json;
10+
#[cfg(test)]
11+
use std::collections::HashMap;
1012

1113
use crate::tui::api::get_api_resource_with_fallback;
1214

@@ -663,6 +665,8 @@ mod tests {
663665
ready: None,
664666
message: None,
665667
revision: None,
668+
labels: HashMap::new(),
669+
annotations: HashMap::new(),
666670
};
667671

668672
let msg = op.confirmation_message(&resource);
@@ -698,6 +702,8 @@ mod tests {
698702
ready: None,
699703
message: None,
700704
revision: None,
705+
labels: HashMap::new(),
706+
annotations: HashMap::new(),
701707
};
702708

703709
let msg = op.confirmation_message(&resource);
@@ -778,6 +784,8 @@ mod tests {
778784
ready: None,
779785
message: None,
780786
revision: None,
787+
labels: HashMap::new(),
788+
annotations: HashMap::new(),
781789
};
782790

783791
let msg = op.confirmation_message(&resource);

src/tui/views/help.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,22 @@ pub fn render_help(f: &mut Frame, area: Rect, _theme: &Theme) {
3030
Line::from(" W - Reconcile with source (Kustomization/HelmRelease)"),
3131
Line::from(" d - Delete resource"),
3232
Line::from(""),
33+
Line::from("Filter Syntax (/):"),
34+
Line::from(" /text - Filter by name"),
35+
Line::from(" /label:key - Filter by label key (any value)"),
36+
Line::from(" /label:key=value - Filter by label key=value"),
37+
Line::from(" /ann:key - Filter by annotation key"),
38+
Line::from(" /ann:key=value - Filter by annotation key=value"),
39+
Line::from(" /annotations:... - Alias for /ann:..."),
40+
Line::from(""),
3341
Line::from("Commands:"),
3442
Line::from(" :help / :h / :? - Show/hide this help"),
3543
Line::from(" :readonly - Toggle readonly mode"),
36-
Line::from(" :skin <name> - Change theme/skin"),
44+
Line::from(" :skin <name> - Change theme/skin"),
3745
Line::from(" :ns <name> / :namespace <name> - Switch namespace"),
38-
Line::from(" :ns all / :ns -A - Show all namespaces"),
39-
Line::from(" :all / :clear - Show all resources"),
40-
Line::from(" :q / :quit / :exit - Quit application"),
46+
Line::from(" :ns all / :ns -A - Show all namespaces"),
47+
Line::from(" :all / :clear - Show all resources"),
48+
Line::from(" :q / :quit / :exit - Quit application"),
4149
Line::from(""),
4250
];
4351

src/tui/views/resource_fields.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,23 @@ pub fn extract_resource_specific_fields(
1212

1313
if let Some(spec) = obj.get("spec").and_then(|s| s.as_object()) {
1414
match FluxResourceKind::parse_optional(resource_type) {
15-
Some(FluxResourceKind::GitRepository)
16-
| Some(FluxResourceKind::OCIRepository)
17-
| Some(FluxResourceKind::HelmRepository) => {
15+
Some(FluxResourceKind::GitRepository) | Some(FluxResourceKind::HelmRepository) => {
1816
if let Some(url) = spec.get("url").and_then(|u| u.as_str()) {
1917
fields.insert("URL".to_string(), url.to_string());
2018
}
2119
if let Some(branch) = spec.get("branch").and_then(|b| b.as_str()) {
2220
fields.insert("BRANCH".to_string(), branch.to_string());
2321
}
2422
}
23+
Some(FluxResourceKind::OCIRepository) => {
24+
if let Some(semver) = spec
25+
.get("ref")
26+
.and_then(|s| s.get("semver"))
27+
.and_then(|se| se.as_str())
28+
{
29+
fields.insert("SEMVER".to_string(), semver.to_string());
30+
}
31+
}
2532
Some(FluxResourceKind::Kustomization) => {
2633
if let Some(path) = spec.get("path").and_then(|p| p.as_str()) {
2734
fields.insert("PATH".to_string(), path.to_string());
@@ -119,6 +126,23 @@ pub fn extract_resource_specific_fields(
119126
fields.insert("STATUS".to_string(), release_status.to_string());
120127
}
121128
}
129+
if FluxResourceKind::parse_optional(resource_type) == Some(FluxResourceKind::OCIRepository)
130+
{
131+
if let Some(digest) = status
132+
.get("artifact")
133+
.and_then(|a| a.get("digest"))
134+
.and_then(|d| d.as_str())
135+
{
136+
fields.insert("DIGEST".to_string(), digest.to_string());
137+
}
138+
if let Some(revision) = status
139+
.get("artifact")
140+
.and_then(|a| a.get("revision"))
141+
.and_then(|r| r.as_str())
142+
{
143+
fields.insert("REVISION".to_string(), revision.to_string());
144+
}
145+
}
122146
}
123147

124148
fields
@@ -127,7 +151,7 @@ pub fn extract_resource_specific_fields(
127151
/// Get column headers for a resource type
128152
pub fn get_resource_type_columns(resource_type: &str) -> Vec<&'static str> {
129153
match FluxResourceKind::parse_optional(resource_type) {
130-
Some(FluxResourceKind::GitRepository) | Some(FluxResourceKind::OCIRepository) => vec![
154+
Some(FluxResourceKind::GitRepository) => vec![
131155
"STATUS",
132156
"NAMESPACE",
133157
"NAME",
@@ -137,6 +161,16 @@ pub fn get_resource_type_columns(resource_type: &str) -> Vec<&'static str> {
137161
"SUSPENDED",
138162
"READY",
139163
],
164+
Some(FluxResourceKind::OCIRepository) => {
165+
vec![
166+
"STATUS",
167+
"NAMESPACE",
168+
"NAME",
169+
"SEMVER",
170+
"DIGEST",
171+
"REVISION",
172+
]
173+
}
140174
Some(FluxResourceKind::HelmRepository) => vec![
141175
"STATUS",
142176
"NAMESPACE",

src/watcher/state.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,37 @@ pub struct ResourceInfo {
1818
pub ready: Option<bool>,
1919
pub message: Option<String>,
2020
pub revision: Option<String>,
21+
// Cached metadata for filtering
22+
pub labels: HashMap<String, String>,
23+
pub annotations: HashMap<String, String>,
24+
}
25+
26+
/// Extract labels from a Kubernetes resource JSON object
27+
pub fn extract_labels(obj: &serde_json::Value) -> HashMap<String, String> {
28+
obj.get("metadata")
29+
.and_then(|m| m.get("labels"))
30+
.and_then(|l| l.as_object())
31+
.map(|labels| {
32+
labels
33+
.iter()
34+
.filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
35+
.collect()
36+
})
37+
.unwrap_or_default()
38+
}
39+
40+
/// Extract annotations from a Kubernetes resource JSON object
41+
pub fn extract_annotations(obj: &serde_json::Value) -> HashMap<String, String> {
42+
obj.get("metadata")
43+
.and_then(|m| m.get("annotations"))
44+
.and_then(|a| a.as_object())
45+
.map(|annotations| {
46+
annotations
47+
.iter()
48+
.filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
49+
.collect()
50+
})
51+
.unwrap_or_default()
2152
}
2253

2354
/// Thread-safe resource state store
@@ -135,6 +166,8 @@ mod tests {
135166
ready: Some(true),
136167
message: Some("Ready".to_string()),
137168
revision: None,
169+
labels: HashMap::new(),
170+
annotations: HashMap::new(),
138171
};
139172

140173
use crate::models::FluxResourceKind;
@@ -168,6 +201,8 @@ mod tests {
168201
ready: None,
169202
message: None,
170203
revision: None,
204+
labels: HashMap::new(),
205+
annotations: HashMap::new(),
171206
};
172207
let key = resource_key(
173208
"default",
@@ -197,6 +232,8 @@ mod tests {
197232
ready: None,
198233
message: None,
199234
revision: None,
235+
labels: HashMap::new(),
236+
annotations: HashMap::new(),
200237
};
201238

202239
let gitrepo = ResourceInfo {
@@ -208,6 +245,8 @@ mod tests {
208245
ready: None,
209246
message: None,
210247
revision: None,
248+
labels: HashMap::new(),
249+
annotations: HashMap::new(),
211250
};
212251

213252
let kustomization2 = ResourceInfo {
@@ -219,6 +258,8 @@ mod tests {
219258
ready: None,
220259
message: None,
221260
revision: None,
261+
labels: HashMap::new(),
262+
annotations: HashMap::new(),
222263
};
223264

224265
state.upsert(
@@ -258,6 +299,8 @@ mod tests {
258299
ready: None,
259300
message: None,
260301
revision: None,
302+
labels: HashMap::new(),
303+
annotations: HashMap::new(),
261304
},
262305
);
263306

@@ -272,6 +315,8 @@ mod tests {
272315
ready: None,
273316
message: None,
274317
revision: None,
318+
labels: HashMap::new(),
319+
annotations: HashMap::new(),
275320
},
276321
);
277322

@@ -286,6 +331,8 @@ mod tests {
286331
ready: None,
287332
message: None,
288333
revision: None,
334+
labels: HashMap::new(),
335+
annotations: HashMap::new(),
289336
},
290337
);
291338

@@ -317,6 +364,8 @@ mod tests {
317364
ready: None,
318365
message: None,
319366
revision: None,
367+
labels: HashMap::new(),
368+
annotations: HashMap::new(),
320369
},
321370
);
322371

0 commit comments

Comments
 (0)