237 lines
No EOL
8.8 KiB
Markdown
237 lines
No EOL
8.8 KiB
Markdown
# Chunk 5: Build Status
|
|
|
|
**Parent:** [Build Graph Dashboard](../build-graph-dashboard.md)
|
|
**Previous:** [Chunk 4: Recent Activity](./chunk-4-recent-activity.md)
|
|
**Next:** [Chunk 6: Partition Pages](./chunk-6-partition-pages.md)
|
|
|
|
## Overview
|
|
|
|
Implement the core operational build request status page with real-time updates, job/partition status visualization, and execution logs.
|
|
|
|
## Scope
|
|
|
|
### In Scope
|
|
- Build request status display with real-time updates
|
|
- Job and partition status visualization
|
|
- Execution timeline and logs from build events
|
|
- Delegation indicators for shared builds
|
|
- Auto-refresh with Page Visibility API
|
|
- Expandable job details
|
|
|
|
### Out of Scope
|
|
- Complex graph visualizations (simple status indicators)
|
|
- Historical build comparisons
|
|
- Advanced filtering of events
|
|
- User interactions beyond viewing
|
|
|
|
## Technical Approach
|
|
|
|
### Data Sources
|
|
From Build Graph Service API:
|
|
- `/api/v1/builds/:id` - Individual build request details and events
|
|
- `/api/v1/partitions/:ref/status` - Individual partition status
|
|
- `/api/v1/partitions/:ref/events` - Partition-specific events
|
|
|
|
Available TypeScript types from generated client:
|
|
- `BuildStatusResponse` - Build request metadata, status, events
|
|
- `PartitionStatusResponse` - Individual partition status
|
|
- `PartitionEventsResponse` - Partition build events
|
|
- `BuildRequestStatus` enum - Status values (Received, Planning, Executing, Completed, Failed, Cancelled)
|
|
- `PartitionStatus` enum - Status values (Requested, Scheduled, Building, Available, Failed, Delegated)
|
|
|
|
**Note**: Timestamps are in nanoseconds and require conversion via `/1000000` for JavaScript Date objects.
|
|
|
|
### Component Structure
|
|
```typescript
|
|
import { DefaultApi, Configuration, BuildStatusResponse, PartitionStatusResponse } from '../client/typescript_generated/src/index';
|
|
import { pollingManager, formatTime } from './services';
|
|
|
|
const BuildStatus = {
|
|
data: null as BuildStatusResponse | null,
|
|
loading: true,
|
|
error: null as string | null,
|
|
partitionStatuses: new Map<string, PartitionStatusResponse>(),
|
|
logsExpanded: {} as Record<string, boolean>,
|
|
|
|
oninit: (vnode) => {
|
|
this.buildId = vnode.attrs.id;
|
|
this.loadBuild();
|
|
this.startPolling();
|
|
},
|
|
|
|
onremove: () => {
|
|
pollingManager.stopPolling(`build-status-${this.buildId}`);
|
|
},
|
|
|
|
async loadBuild() {
|
|
try {
|
|
this.loading = true;
|
|
this.error = null;
|
|
m.redraw();
|
|
|
|
const apiClient = new DefaultApi(new Configuration({ basePath: '' }));
|
|
|
|
// Get build status
|
|
const buildResponse = await apiClient.apiV1BuildsBuildRequestIdGet({ buildRequestId: this.buildId });
|
|
this.data = buildResponse;
|
|
|
|
// Load partition statuses for all requested partitions
|
|
if (buildResponse.requestedPartitions) {
|
|
for (const partition of buildResponse.requestedPartitions) {
|
|
try {
|
|
const partitionStatus = await apiClient.apiV1PartitionsRefStatusGet({
|
|
ref: partition.str
|
|
});
|
|
this.partitionStatuses.set(partition.str, partitionStatus);
|
|
} catch (e) {
|
|
console.warn(`Failed to load status for partition ${partition.str}:`, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.loading = false;
|
|
m.redraw();
|
|
} catch (error) {
|
|
console.error('Failed to load build:', error);
|
|
this.error = error instanceof Error ? error.message : 'Failed to load build';
|
|
this.loading = false;
|
|
m.redraw();
|
|
}
|
|
},
|
|
|
|
startPolling() {
|
|
// Use different poll intervals based on build status
|
|
const isActive = this.data?.status === 'BuildRequestExecuting' ||
|
|
this.data?.status === 'BuildRequestPlanning';
|
|
const interval = isActive ? 2000 : 10000; // 2s for active, 10s for completed
|
|
|
|
pollingManager.startPolling(`build-status-${this.buildId}`, () => {
|
|
this.loadBuild();
|
|
}, interval);
|
|
},
|
|
|
|
view: () => [
|
|
// Loading/error states similar to RecentActivity component
|
|
this.loading && !this.data ? m('.loading-state', '...') : null,
|
|
this.error ? m('.error-state', this.error) : null,
|
|
|
|
this.data ? [
|
|
m('.build-header', [
|
|
m('h1', `Build ${this.buildId}`),
|
|
m('.build-meta', [
|
|
m(`span.badge.${this.getStatusClass(this.data.status)}`, this.data.status),
|
|
m('.timestamp', formatTime(new Date(this.data.createdAt / 1000000).toISOString())),
|
|
m('.partitions', `${this.data.requestedPartitions?.length || 0} partitions`),
|
|
])
|
|
]),
|
|
|
|
m('.build-content', [
|
|
m('.partition-status', [
|
|
m('h2', 'Partition Status'),
|
|
m('.partition-grid',
|
|
this.data.requestedPartitions?.map(partition => {
|
|
const status = this.partitionStatuses.get(partition.str);
|
|
return m('.partition-card', [
|
|
m('.partition-ref', partition.str),
|
|
m(`span.badge.${this.getPartitionStatusClass(status?.status)}`,
|
|
status?.status || 'Unknown'),
|
|
status?.updatedAt ?
|
|
m('.updated-time', formatTime(new Date(status.updatedAt / 1000000).toISOString())) : null
|
|
]);
|
|
}) || []
|
|
)
|
|
]),
|
|
|
|
m('.execution-timeline', [
|
|
m('h2', 'Execution Timeline'),
|
|
m('.timeline', this.data.events?.map(event =>
|
|
m('.timeline-item', [
|
|
m('.timestamp', formatTime(new Date(event.timestamp / 1000000).toISOString())),
|
|
m('.event-type', event.eventType),
|
|
m('.message', event.message || ''),
|
|
// Add expandable logs for job events
|
|
this.isJobEvent(event) ? m('.expandable-logs', [
|
|
m('button.btn.btn-sm', {
|
|
onclick: () => this.toggleLogs(event.eventId)
|
|
}, this.logsExpanded[event.eventId] ? 'Hide Logs' : 'Show Logs'),
|
|
this.logsExpanded[event.eventId] ?
|
|
m('.logs', this.formatJobLogs(event)) : null
|
|
]) : null
|
|
])
|
|
) || [])
|
|
])
|
|
])
|
|
] : null
|
|
]
|
|
};
|
|
```
|
|
|
|
### Real-time Updates
|
|
- Use existing `pollingManager` from `services.ts` with Page Visibility API
|
|
- Poll every 2 seconds when build status is `BuildRequestExecuting` or `BuildRequestPlanning`
|
|
- Poll every 10 seconds when build is `BuildRequestCompleted`, `BuildRequestFailed`, or `BuildRequestCancelled`
|
|
- Automatic polling pause when tab is not visible
|
|
- Explicit `m.redraw()` calls after async data loading
|
|
|
|
### Status Visualization
|
|
- Color-coded status badges using DaisyUI classes:
|
|
- `badge-success` for `BuildRequestCompleted` / `PartitionAvailable`
|
|
- `badge-warning` for `BuildRequestExecuting` / `PartitionBuilding`
|
|
- `badge-error` for `BuildRequestFailed` / `PartitionFailed`
|
|
- `badge-neutral` for other states
|
|
- Timeline visualization for build events with timestamp formatting
|
|
- Delegation indicators for `PartitionDelegated` status with build request links
|
|
|
|
## Implementation Strategy
|
|
|
|
1. **Extend Existing Infrastructure**
|
|
- Use established `DefaultApi` client pattern from `services.ts`
|
|
- Leverage existing `pollingManager` with Page Visibility API
|
|
- Follow `RecentActivity` component patterns for loading/error states
|
|
- Import generated TypeScript types from client
|
|
|
|
2. **Build Status Components**
|
|
- Build header with metadata using `BuildStatusResponse`
|
|
- Partition status grid using `PartitionStatusResponse` for each partition
|
|
- Execution timeline parsing `events` array from build response
|
|
- Expandable log sections for job events with state management
|
|
|
|
3. **Real-time Updates**
|
|
- Intelligent polling based on `BuildRequestStatus` enum values
|
|
- Reuse existing `pollingManager` infrastructure
|
|
- Loading states and error handling following established patterns
|
|
- Proper `m.redraw()` calls after async operations
|
|
|
|
4. **Status Visualization**
|
|
- Status badge classes using established DaisyUI patterns
|
|
- Timeline layout similar to existing dashboard components
|
|
- Partition delegation links using build request IDs
|
|
- Timestamp formatting using existing `formatTime` utility
|
|
|
|
## Deliverables
|
|
|
|
- [ ] Build request status page with real-time updates
|
|
- [ ] Partition status grid with visual indicators
|
|
- [ ] Execution timeline with build events
|
|
- [ ] Expandable job logs and details
|
|
- [ ] Auto-refresh with visibility detection
|
|
- [ ] Delegation indicators and links
|
|
|
|
## Success Criteria
|
|
|
|
- Real-time updates show build progress accurately
|
|
- All partition statuses are clearly displayed
|
|
- Job logs are accessible and readable
|
|
- Polling behaves correctly based on build state
|
|
- Delegation to other builds is clearly indicated
|
|
- Page is responsive and performs well
|
|
|
|
## Testing
|
|
|
|
- Test with running builds to verify real-time updates
|
|
- Verify partition status changes are reflected
|
|
- Test job log expansion and readability
|
|
- Validate polling behavior with tab visibility
|
|
- Test with delegated builds
|
|
- Verify error handling with invalid build IDs
|
|
- Check performance with large build requests |