Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions tools/cli/src/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,14 +468,22 @@ async fn main() -> Result<()> {
Some(Box::new(move || {
pb_clone.finish_and_clear();
})),
args.skip_changes,
)
.await?;
} else {
serve_features_with_watching(&features, args.port, path.clone(), None).await?;
serve_features_with_watching(
&features,
args.port,
path.clone(),
None,
args.skip_changes,
)
.await?;
}
} else if args.build {
let build_config = BuildConfig::new(args.build_dir);
create_build(&features, build_config).await?;
create_build(&features, build_config, args.skip_changes).await?;
} else if args.check {
run_checks(&features)?;
} else if args.generate_codeowners {
Expand Down
33 changes: 31 additions & 2 deletions tools/cli/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ impl BuildConfig {
///
/// * `features` - Slice of Feature objects to include in the build
/// * `config` - Build configuration
/// * `skip_changes` - Whether changes were skipped during feature computation
///
/// # Returns
///
Expand All @@ -64,6 +65,7 @@ impl BuildConfig {
/// The build directory will contain:
/// * All static files from the embedded public directory
/// * `features.json` - Generated features data
/// * `metadata.json` - Generated metadata with version and skipChanges flag
///
/// # Example
///
Expand All @@ -75,10 +77,14 @@ impl BuildConfig {
/// async fn main() -> anyhow::Result<()> {
/// let features = vec![]; // Your features data
/// let config = BuildConfig::new("dist");
/// create_build(&features, config).await
/// create_build(&features, config, false).await
/// }
/// ```
pub async fn create_build(features: &[Feature], config: BuildConfig) -> Result<()> {
pub async fn create_build(
features: &[Feature],
config: BuildConfig,
skip_changes: bool,
) -> Result<()> {
println!(
"Creating build in directory: {}",
config.output_dir.display()
Expand All @@ -99,6 +105,9 @@ pub async fn create_build(features: &[Feature], config: BuildConfig) -> Result<(
// Generate features.json
generate_features_json(features, &config.output_dir).await?;

// Generate metadata.json
generate_metadata_json(&config.output_dir, skip_changes).await?;

println!("Build completed successfully!");
println!("Output directory: {}", config.output_dir.display());

Expand Down Expand Up @@ -165,3 +174,23 @@ async fn generate_features_json(features: &[Feature], output_dir: &Path) -> Resu

Ok(())
}

/// Generates the metadata.json file
async fn generate_metadata_json(output_dir: &Path, skip_changes: bool) -> Result<()> {
println!("Generating metadata.json...");

let metadata = serde_json::json!({
"version": env!("CARGO_PKG_VERSION"),
"skipChanges": skip_changes
});

let metadata_json = serde_json::to_string_pretty(&metadata)
.map_err(|e| anyhow::anyhow!("Failed to serialize metadata to JSON: {}", e))?;

let metadata_path = output_dir.join("metadata.json");
fs::write(&metadata_path, metadata_json).await?;

println!(" Created: {}", metadata_path.display());

Ok(())
}
17 changes: 14 additions & 3 deletions tools/cli/src/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ impl ServerConfig {
/// * `port` - Port number to run the server on
/// * `watch_path` - Path to watch for file changes
/// * `on_ready` - Optional callback to be called when server is ready
/// * `skip_changes` - Whether changes were skipped during feature computation
///
/// # Returns
///
Expand All @@ -77,10 +78,17 @@ pub async fn serve_features_with_watching(
port: u16,
watch_path: PathBuf,
on_ready: Option<Box<dyn FnOnce() + Send>>,
skip_changes: bool,
) -> Result<()> {
let config = ServerConfig::new(port);
serve_features_with_config_and_watching(features, config, Some(watch_path.clone()), on_ready)
.await
serve_features_with_config_and_watching(
features,
config,
Some(watch_path.clone()),
on_ready,
skip_changes,
)
.await
}

/// Starts an HTTP server with custom configuration and optional file watching.
Expand All @@ -91,6 +99,7 @@ pub async fn serve_features_with_watching(
/// * `config` - Server configuration
/// * `watch_path` - Optional path to watch for file changes
/// * `on_ready` - Optional callback to be called when server is ready
/// * `skip_changes` - Whether changes were skipped during feature computation
///
/// # Returns
///
Expand All @@ -100,6 +109,7 @@ pub async fn serve_features_with_config_and_watching(
config: ServerConfig,
watch_path: Option<PathBuf>,
on_ready: Option<Box<dyn FnOnce() + Send>>,
skip_changes: bool,
) -> Result<()> {
// Create shared state for features
let features_data = Arc::new(RwLock::new(features.to_vec()));
Expand Down Expand Up @@ -152,7 +162,8 @@ pub async fn serve_features_with_config_and_watching(
let repo_url = repository_url.clone();
async move {
let mut metadata = serde_json::json!({
"version": env!("CARGO_PKG_VERSION")
"version": env!("CARGO_PKG_VERSION"),
"skipChanges": skip_changes
});

// Add repository URL if available
Expand Down
180 changes: 100 additions & 80 deletions tools/web/src/features/insights/feature-insights-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip'
import { useMetadata } from '@/hooks/use-metadata'
import { useTableFilter } from '@/hooks/use-table-filter'
import { useTableSort } from '@/hooks/use-table-sort'
import { formatDate } from '@/lib/format-date'
Expand Down Expand Up @@ -68,6 +69,8 @@ function getAllMetadataKeys(features: Feature[]): string[] {

export function FeatureInsightsTable({ features }: FeatureInsightsTableProps) {
const [showSearch] = useState(true)
const { metadata } = useMetadata()
const skipChanges = metadata?.skipChanges ?? false

// Get all metadata keys present in features
const metadataKeys = getAllMetadataKeys(features)
Expand Down Expand Up @@ -170,18 +173,22 @@ export function FeatureInsightsTable({ features }: FeatureInsightsTableProps) {
sortConfig={sortConfig}
onSort={requestSort}
/>
<SortableTableHeader
field="stats.commits.first_commit_date"
label="Initial Date"
sortConfig={sortConfig}
onSort={requestSort}
/>
<SortableTableHeader
field="stats.commits.last_commit_date"
label="Last changed"
sortConfig={sortConfig}
onSort={requestSort}
/>
{!skipChanges && (
<>
<SortableTableHeader
field="stats.commits.first_commit_date"
label="Initial Date"
sortConfig={sortConfig}
onSort={requestSort}
/>
<SortableTableHeader
field="stats.commits.last_commit_date"
label="Last changed"
sortConfig={sortConfig}
onSort={requestSort}
/>
</>
)}
<SortableTableHeader
field="stats.files_count"
label="Files"
Expand All @@ -203,34 +210,38 @@ export function FeatureInsightsTable({ features }: FeatureInsightsTableProps) {
onSort={requestSort}
align="right"
/>
<SortableTableHeader
field="stats.commits.total_commits"
label="Total Changes"
sortConfig={sortConfig}
onSort={requestSort}
align="right"
/>
<SortableTableHeader
field="stats.commits.count_by_type.feat"
label="Feat"
sortConfig={sortConfig}
onSort={requestSort}
align="right"
/>
<SortableTableHeader
field="stats.commits.count_by_type.fix"
label="Fix"
sortConfig={sortConfig}
onSort={requestSort}
align="right"
/>
<SortableTableHeader
field="stats.commits.count_by_type.refactor"
label="Refactor"
sortConfig={sortConfig}
onSort={requestSort}
align="right"
/>
{!skipChanges && (
<>
<SortableTableHeader
field="stats.commits.total_commits"
label="Total Changes"
sortConfig={sortConfig}
onSort={requestSort}
align="right"
/>
<SortableTableHeader
field="stats.commits.count_by_type.feat"
label="Feat"
sortConfig={sortConfig}
onSort={requestSort}
align="right"
/>
<SortableTableHeader
field="stats.commits.count_by_type.fix"
label="Fix"
sortConfig={sortConfig}
onSort={requestSort}
align="right"
/>
<SortableTableHeader
field="stats.commits.count_by_type.refactor"
label="Refactor"
sortConfig={sortConfig}
onSort={requestSort}
align="right"
/>
</>
)}
{metadataKeys.map((key) => (
<TableHead key={key} className="text-right capitalize">
{key}
Expand Down Expand Up @@ -270,12 +281,16 @@ export function FeatureInsightsTable({ features }: FeatureInsightsTableProps) {
<TableCell className="text-muted-foreground">
<FeatureOwner feature={feature} />
</TableCell>
<TableCell className="text-muted-foreground">
{formatDate(feature.stats?.commits.first_commit_date)}
</TableCell>
<TableCell className="text-muted-foreground">
{formatDate(feature.stats?.commits.last_commit_date)}
</TableCell>
{!skipChanges && (
<>
<TableCell className="text-muted-foreground">
{formatDate(feature.stats?.commits.first_commit_date)}
</TableCell>
<TableCell className="text-muted-foreground">
{formatDate(feature.stats?.commits.last_commit_date)}
</TableCell>
</>
)}
<TableCell className="text-right tabular-nums">
{feature.stats?.files_count ?? 0}
</TableCell>
Expand All @@ -292,40 +307,45 @@ export function FeatureInsightsTable({ features }: FeatureInsightsTableProps) {
>
{feature.stats?.todos_count ?? 0}
</TableCell>
<TableCell className="text-right tabular-nums">
{feature.stats?.commits.total_commits}
</TableCell>
<TableCell
className={cn(
'text-right tabular-nums',
(feature.stats?.commits.count_by_type?.feat ?? 0) === 0
? 'text-muted-foreground/50'
: '',
)}
>
{feature.stats?.commits.count_by_type?.feat ?? 0}
</TableCell>
<TableCell
className={cn(
'text-right tabular-nums',
(feature.stats?.commits.count_by_type?.fix ?? 0) === 0
? 'text-muted-foreground/50'
: '',
)}
>
{feature.stats?.commits.count_by_type?.fix ?? 0}
</TableCell>
<TableCell
className={cn(
'text-right tabular-nums',
(feature.stats?.commits.count_by_type?.refactor ?? 0) ===
0
? 'text-muted-foreground/50'
: '',
)}
>
{feature.stats?.commits.count_by_type?.refactor ?? 0}
</TableCell>
{!skipChanges && (
<>
<TableCell className="text-right tabular-nums">
{feature.stats?.commits.total_commits}
</TableCell>
<TableCell
className={cn(
'text-right tabular-nums',
(feature.stats?.commits.count_by_type?.feat ?? 0) ===
0
? 'text-muted-foreground/50'
: '',
)}
>
{feature.stats?.commits.count_by_type?.feat ?? 0}
</TableCell>
<TableCell
className={cn(
'text-right tabular-nums',
(feature.stats?.commits.count_by_type?.fix ?? 0) === 0
? 'text-muted-foreground/50'
: '',
)}
>
{feature.stats?.commits.count_by_type?.fix ?? 0}
</TableCell>
<TableCell
className={cn(
'text-right tabular-nums',
(feature.stats?.commits.count_by_type?.refactor ??
0) === 0
? 'text-muted-foreground/50'
: '',
)}
>
{feature.stats?.commits.count_by_type?.refactor ?? 0}
</TableCell>
</>
)}
{metadataKeys.map((key) => {
const metadataArrays = getMetadataArrays(feature)
const count = metadataArrays[key] ?? 0
Expand Down
1 change: 1 addition & 0 deletions tools/web/src/hooks/use-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import useSWR from 'swr'
export interface Metadata {
version: string
repository?: string
skipChanges?: boolean
}

const fetcher = async (url: string): Promise<Metadata> => {
Expand Down