Skip to content

Commit 5be397d

Browse files
committed
chore: wip
1 parent b8684f2 commit 5be397d

File tree

6 files changed

+749
-12
lines changed

6 files changed

+749
-12
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dependencies:
2+
bun.sh: 1.0.0

packages/zig/src/cli/commands.zig

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -835,11 +835,108 @@ pub fn shellLookupCommand(allocator: std.mem.Allocator, dir: []const u8) !Comman
835835
}
836836

837837
/// Shell activate command (for shell integration)
838-
pub fn shellActivateCommand(_: std.mem.Allocator, dir: []const u8) !CommandResult {
839-
std.debug.print("Activating environment for {s}...\n", .{dir});
838+
pub fn shellActivateCommand(allocator: std.mem.Allocator, dir: []const u8) !CommandResult {
839+
const detector = @import("../deps/detector.zig");
840+
const parser = @import("../deps/parser.zig");
841+
842+
// Find dependency file
843+
const deps_file = (try detector.findDepsFile(allocator, dir)) orelse {
844+
// No dependency file found
845+
return .{ .exit_code = 1 };
846+
};
847+
defer allocator.free(deps_file.path);
848+
849+
// Calculate environment hash from dependency file path
850+
const hash = string.hashDependencyFile(deps_file.path);
851+
const hash_hex = try string.hashToHex(hash, allocator);
852+
defer allocator.free(hash_hex);
853+
854+
// Check environment cache first
855+
var env_cache = cache.EnvCache.init(allocator);
856+
defer env_cache.deinit();
857+
858+
if (try env_cache.get(hash)) |entry| {
859+
// Cache hit - output cached shell code
860+
std.debug.print("export PATH=\"{s}:$PATH\"\n", .{entry.path});
861+
return .{ .exit_code = 0 };
862+
}
863+
864+
std.debug.print("Found dependency file: {s} (hash: {s})\n", .{ deps_file.path, hash_hex });
865+
866+
// Parse dependency file (auto-detects format)
867+
const deps = try parser.inferDependencies(allocator, deps_file);
868+
defer {
869+
for (deps) |*dep| {
870+
var d = dep.*;
871+
d.deinit(allocator);
872+
}
873+
allocator.free(deps);
874+
}
875+
876+
if (deps.len == 0) {
877+
std.debug.print("No dependencies found\n", .{});
878+
return .{ .exit_code = 0 };
879+
}
880+
881+
// Initialize package cache and installer
882+
var pkg_cache = try cache.PackageCache.init(allocator);
883+
defer pkg_cache.deinit();
884+
885+
var installer = try install.Installer.init(allocator, &pkg_cache);
886+
defer installer.deinit();
887+
888+
std.debug.print("Installing {d} package(s)...\n", .{deps.len});
889+
890+
// Install each dependency
891+
for (deps) |dep| {
892+
std.debug.print(" → {s}@{s}...", .{ dep.name, dep.version });
893+
894+
const spec = lib.packages.PackageSpec{
895+
.name = dep.name,
896+
.version = dep.version,
897+
};
898+
899+
var result = installer.install(spec, .{}) catch |err| {
900+
std.debug.print(" failed: {}\n", .{err});
901+
continue;
902+
};
903+
defer result.deinit(allocator);
904+
905+
if (result.from_cache) {
906+
std.debug.print(" done (cached, {d}ms)\n", .{result.install_time_ms});
907+
} else {
908+
std.debug.print(" done ({d}ms)\n", .{result.install_time_ms});
909+
}
910+
}
911+
912+
// Output shell code to add bin directory to PATH
913+
const bin_dir = try std.fmt.allocPrint(
914+
allocator,
915+
"{s}/bin",
916+
.{installer.data_dir},
917+
);
918+
defer allocator.free(bin_dir);
919+
920+
// Cache this environment for fast lookup next time
921+
const mtime = blk: {
922+
const file_stat = std.fs.cwd().statFile(deps_file.path) catch break :blk 0;
923+
break :blk @as(i64, @intCast(file_stat.mtime));
924+
};
925+
926+
const entry = try allocator.create(cache.env_cache.Entry);
927+
const env_vars = std.StringHashMap([]const u8).init(allocator);
928+
entry.* = .{
929+
.hash = hash,
930+
.dep_file = try allocator.dupe(u8, deps_file.path),
931+
.dep_mtime = @as(i128, @intCast(mtime)),
932+
.path = try allocator.dupe(u8, bin_dir),
933+
.env_vars = env_vars,
934+
.created_at = std.time.timestamp(),
935+
};
936+
try env_cache.put(entry);
937+
938+
std.debug.print("\nexport PATH=\"{s}:$PATH\"\n", .{bin_dir});
840939

841-
// TODO: Detect dependency files, install packages, generate shell code
842-
// For now, return success
843940
return .{ .exit_code = 0 };
844941
}
845942

packages/zig/src/deps/detector.zig

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const std = @import("std");
2+
3+
pub const DepsFile = struct {
4+
path: []const u8,
5+
format: FileFormat,
6+
7+
pub const FileFormat = enum {
8+
deps_yaml,
9+
deps_yml,
10+
dependencies_yaml,
11+
pkgx_yaml,
12+
package_json,
13+
cargo_toml,
14+
pyproject_toml,
15+
requirements_txt,
16+
gemfile,
17+
go_mod,
18+
composer_json,
19+
};
20+
};
21+
22+
/// Find dependency file in directory or parent directories
23+
pub fn findDepsFile(allocator: std.mem.Allocator, start_dir: []const u8) !?DepsFile {
24+
const file_names = [_][]const u8{
25+
"deps.yaml",
26+
"deps.yml",
27+
"dependencies.yaml",
28+
"pkgx.yaml",
29+
"package.json",
30+
"Cargo.toml",
31+
"pyproject.toml",
32+
"requirements.txt",
33+
"Gemfile",
34+
"go.mod",
35+
"composer.json",
36+
};
37+
38+
var current_dir_buf: [std.fs.max_path_bytes]u8 = undefined;
39+
const current_dir = try std.fs.realpath(start_dir, &current_dir_buf);
40+
41+
var dir_path = try allocator.dupe(u8, current_dir);
42+
defer allocator.free(dir_path);
43+
44+
// Walk up the directory tree
45+
while (true) {
46+
// Try each dependency file name
47+
for (file_names, 0..) |file_name, i| {
48+
const full_path = try std.fs.path.join(allocator, &[_][]const u8{ dir_path, file_name });
49+
defer allocator.free(full_path);
50+
51+
// Check if file exists
52+
std.fs.accessAbsolute(full_path, .{}) catch {
53+
continue;
54+
};
55+
56+
// Found a dependency file!
57+
const format: DepsFile.FileFormat = @enumFromInt(i);
58+
return DepsFile{
59+
.path = try allocator.dupe(u8, full_path),
60+
.format = format,
61+
};
62+
}
63+
64+
// Move to parent directory
65+
const parent = std.fs.path.dirname(dir_path) orelse break;
66+
if (std.mem.eql(u8, parent, dir_path)) break; // Reached root
67+
68+
const new_dir = try allocator.dupe(u8, parent);
69+
allocator.free(dir_path);
70+
dir_path = new_dir;
71+
}
72+
73+
return null;
74+
}
75+
76+
test "findDepsFile" {
77+
const allocator = std.testing.allocator;
78+
79+
// Test with current directory
80+
const result = try findDepsFile(allocator, ".");
81+
if (result) |deps_file| {
82+
defer allocator.free(deps_file.path);
83+
std.debug.print("Found: {s}\n", .{deps_file.path});
84+
}
85+
}

0 commit comments

Comments
 (0)