feat: zupper tracks what version is in use

This commit is contained in:
LordMZTE 2022-12-07 12:22:29 +01:00
parent 22c39a6712
commit aef6ee81aa
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
9 changed files with 135 additions and 56 deletions

View file

@ -1,6 +1,6 @@
pkgs: pkgs:
zupper: zupper:
version: 0.1.0 version: 0.2.0
description: Zig version manager description: Zig version manager
license: GPL-3.0 license: GPL-3.0
source_url: "https://mzte.de/git/LordMZTE/zupper" source_url: "https://mzte.de/git/LordMZTE/zupper"

View file

@ -4,6 +4,7 @@ const json = @import("json");
const getty = @import("getty"); const getty = @import("getty");
versions: std.StringHashMap(std.SemanticVersion), versions: std.StringHashMap(std.SemanticVersion),
in_use: ?[]u8 = null,
const Self = @This(); const Self = @This();
@ -91,6 +92,17 @@ pub fn removeVersion(self: *Self, version: []const u8) void {
} }
} }
pub fn setInUse(self: *Self, in_use: ?[]const u8) !void {
if (self.in_use) |old| {
self.versions.allocator.free(old);
}
if (in_use) |v| {
const dup = try self.versions.allocator.dupe(u8, v);
self.in_use = dup;
}
}
pub fn save(self: *Self) !void { pub fn save(self: *Self) !void {
const mpath = try path(self.versions.allocator); const mpath = try path(self.versions.allocator);
defer self.versions.allocator.free(mpath); defer self.versions.allocator.free(mpath);

View file

@ -1,6 +1,6 @@
const std = @import("std"); const std = @import("std");
pub const version = "0.1.0"; pub const version = "0.2.0";
pub const Info = struct {}; pub const Info = struct {};
@ -13,6 +13,7 @@ pub const Install = struct {
}; };
pub const Uninstall = struct {}; pub const Uninstall = struct {};
pub const Unuse = struct {};
pub const Update = struct {}; pub const Update = struct {};
pub const Use = struct {}; pub const Use = struct {};
@ -28,6 +29,7 @@ pub const Args = union(enum) {
info: Info, info: Info,
install: Install, install: Install,
uninstall: Uninstall, uninstall: Uninstall,
unuse: Unuse,
update: Update, update: Update,
use: Use, use: Use,
}; };
@ -42,9 +44,10 @@ const help_s =
\\Subcommands: \\Subcommands:
\\ {[a0]s} info prints information about the setup \\ {[a0]s} info prints information about the setup
\\ {[a0]s} install <VERSIONS...> [--use, -u] downloads the given zig versions and optionally uses them \\ {[a0]s} install <VERSIONS...> [--use, -u] downloads the given zig versions and optionally uses them
\\ {[a0]s} uninstall <VERSIONS...> uninstall the given versions
\\ {[a0]s} unuse unuse the version currently in use (essentially a soft-uninstall)
\\ {[a0]s} update [VERSIONS...] update the given version, or all versions if none are given \\ {[a0]s} update [VERSIONS...] update the given version, or all versions if none are given
\\ {[a0]s} use <VERSION> uses the given version by symlinking the compiler into $PATH \\ {[a0]s} use <VERSION> uses the given version by symlinking the compiler into $PATH
\\ {[a0]s} uninstall <VERSIONS...> uninstall the given versions
\\ \\
; ;

View file

@ -69,6 +69,6 @@ pub fn run(
} }
if (args.use) { if (args.use) {
try dirs.useVersion(alloc, home, positionals[0]); try dirs.useVersion(alloc, manifest, home, positionals[0]);
} }
} }

View file

@ -32,5 +32,9 @@ pub fn run(
std.log.info("deleting {s}", .{ver_path}); std.log.info("deleting {s}", .{ver_path});
manifest.removeVersion(version); manifest.removeVersion(version);
try std.fs.cwd().deleteTree(ver_path); try std.fs.cwd().deleteTree(ver_path);
if (manifest.in_use != null and std.mem.eql(u8, manifest.in_use.?, version)) {
try dirs.unuseCurrentVersion(alloc, manifest);
}
} }
} }

25
src/commands/unuse.zig Normal file
View file

@ -0,0 +1,25 @@
const std = @import("std");
const dirs = @import("../dirs.zig");
const Manifest = @import("../Manifest.zig");
const Args = @import("../args.zig").Unuse;
pub fn run(
alloc: std.mem.Allocator,
manifest: *Manifest,
args: Args,
positionals: []const [:0]const u8,
) !void {
_ = args;
if (positionals.len != 0) {
std.log.err("expected exactly 0 positional arguments", .{});
return error.Explained;
}
if (manifest.in_use == null) {
std.log.err("no version currently in use!", .{});
return error.Explained;
}
try dirs.unuseCurrentVersion(alloc, manifest);
}

View file

@ -1,9 +1,11 @@
const std = @import("std"); const std = @import("std");
const dirs = @import("../dirs.zig"); const dirs = @import("../dirs.zig");
const Manifest = @import("../Manifest.zig");
const Args = @import("../args.zig").Use; const Args = @import("../args.zig").Use;
pub fn run( pub fn run(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
manifest: *Manifest,
args: Args, args: Args,
positionals: []const [:0]const u8, positionals: []const [:0]const u8,
) !void { ) !void {
@ -22,5 +24,5 @@ pub fn run(
return error.Explained; return error.Explained;
} }
try dirs.useVersion(alloc, home, positionals[0]); try dirs.useVersion(alloc, manifest, home, positionals[0]);
} }

View file

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const kf = @import("known-folders"); const kf = @import("known-folders");
const Manifest = @import("Manifest.zig");
pub fn zupperHome(alloc: std.mem.Allocator) ![]const u8 { pub fn zupperHome(alloc: std.mem.Allocator) ![]const u8 {
const datapath = (try kf.getPath(alloc, .data)) orelse return error.GetDataFolder; const datapath = (try kf.getPath(alloc, .data)) orelse return error.GetDataFolder;
@ -21,9 +22,44 @@ pub fn isVersionInstalled(alloc: std.mem.Allocator, home: []const u8, version: [
return true; return true;
} }
pub fn useVersion(alloc: std.mem.Allocator, home: []const u8, version: []const u8) !void { pub fn binInstallationPath(alloc: std.mem.Allocator) !?[]u8 {
switch (@import("builtin").os.tag) {
.linux => {
const user_home = (try kf.getPath(alloc, .home)) orelse return error.GetHomeDir;
defer alloc.free(user_home);
return try std.fs.path.join(alloc, &.{ user_home, ".local", "bin" });
},
.windows => {
return try std.fs.selfExeDirPathAlloc(alloc);
},
.macos => {
const user_home = (try kf.getPath(alloc, .home)) orelse return error.GetHomeDir;
defer alloc.free(user_home);
// not sure if this is the correct path. mac got kind of a windowsy situation too
return try std.fs.path.join(alloc, &.{ user_home, "Applications" });
},
else => {
return null;
},
}
}
pub fn useVersion(
alloc: std.mem.Allocator,
manifest: *Manifest,
home: []const u8,
version: []const u8,
) !void {
if (manifest.in_use != null and std.mem.eql(u8, manifest.in_use.?, version)) {
std.log.err("version '{s}' is already in use!", .{version});
return error.Explained;
}
if (!try isVersionInstalled(alloc, home, version)) { if (!try isVersionInstalled(alloc, home, version)) {
std.log.err("this version isn't installed! have you tried installing it first?", .{}); std.log.err(
"version '{s}' isn't installed! have you tried installing it first?",
.{version},
);
return error.Explained; return error.Explained;
} }
@ -45,36 +81,22 @@ pub fn useVersion(alloc: std.mem.Allocator, home: []const u8, version: []const u
return error.Explained; return error.Explained;
}; };
const target_binary_directory = switch (@import("builtin").os.tag) { if (@import("builtin").os.tag == .windows) {
.linux => blk: {
const user_home = (try kf.getPath(alloc, .home)) orelse return error.GetHomeDir;
defer alloc.free(user_home);
break :blk try std.fs.path.join(alloc, &.{ user_home, ".local", "bin" });
},
.windows => blk: {
std.log.warn( std.log.warn(
\\installing on windows! windows is so bad, it can't get it's conventions \\installing on windows! windows is so bad, it can't get it's conventions
\\for locally installed programs right. i'll use my own install directory... \\for locally installed programs right. i'll use my own install directory...
, ,
.{}, .{},
); );
}
break :blk try std.fs.selfExeDirPathAlloc(alloc); const target_binary_directory = try binInstallationPath(alloc) orelse {
},
.macos => blk: {
const user_home = (try kf.getPath(alloc, .home)) orelse return error.GetHomeDir;
defer alloc.free(user_home);
// not sure if this is the correct path. mac got kind of a windowsy situation too
break :blk try std.fs.path.join(alloc, &.{ user_home, "Applications" });
},
else => {
std.log.err( std.log.err(
"using a version isn't supported on {s}. please install the zig binary @ '{s}' manually", "using a version isn't supported on {s}. please install the zig binary @ '{s}' manually",
.{ @tagName(@import("builtin").os.tag), zig_exe_path }, .{ @tagName(@import("builtin").os.tag), zig_exe_path },
); );
return; return;
},
}; };
defer alloc.free(target_binary_directory); defer alloc.free(target_binary_directory);
@ -92,4 +114,28 @@ pub fn useVersion(alloc: std.mem.Allocator, home: []const u8, version: []const u
} }
}; };
try std.fs.cwd().symLink(zig_exe_path, target_binary, .{}); try std.fs.cwd().symLink(zig_exe_path, target_binary, .{});
try manifest.setInUse(version);
}
pub fn unuseCurrentVersion(
alloc: std.mem.Allocator,
manifest: *Manifest,
) !void {
const install_dir = try binInstallationPath(alloc) orelse return;
defer alloc.free(install_dir);
const target_binary = try std.fs.path.join(alloc, &.{
install_dir,
comptime "zig" ++ @import("builtin").target.exeFileExt(),
});
defer alloc.free(target_binary);
std.log.info("unusing zig installation @ {s}", .{target_binary});
try std.fs.cwd().deleteFile(target_binary);
if (manifest.in_use) |old| {
manifest.versions.allocator.free(old);
}
manifest.in_use = null;
} }

View file

@ -38,42 +38,29 @@ fn runCmd(alloc: std.mem.Allocator, opts: anytype) !void {
try args.printHelp(alloc); try args.printHelp(alloc);
return error.Explained; return error.Explained;
}; };
var manifest = try Manifest.load(alloc);
defer manifest.deinit();
switch (verb) { switch (verb) {
.info => |a| { .info => |a| {
var manifest = try Manifest.load(alloc);
defer manifest.deinit();
try @import("commands/info.zig").run(alloc, &manifest, a, opts.positionals); try @import("commands/info.zig").run(alloc, &manifest, a, opts.positionals);
try manifest.save();
}, },
.install => |a| { .install => |a| {
var manifest = try Manifest.load(alloc);
defer manifest.deinit();
try @import("commands/install.zig").run(alloc, &manifest, a, opts.positionals); try @import("commands/install.zig").run(alloc, &manifest, a, opts.positionals);
try manifest.save();
}, },
.update => |a| { .update => |a| {
var manifest = try Manifest.load(alloc);
defer manifest.deinit();
try @import("commands/update.zig").run(alloc, &manifest, a, opts.positionals); try @import("commands/update.zig").run(alloc, &manifest, a, opts.positionals);
try manifest.save();
}, },
.use => |a| { .use => |a| {
try @import("commands/use.zig").run(alloc, a, opts.positionals); try @import("commands/use.zig").run(alloc, &manifest, a, opts.positionals);
}, },
.uninstall => |a| { .uninstall => |a| {
var manifest = try Manifest.load(alloc);
defer manifest.deinit();
try @import("commands/uninstall.zig").run(alloc, &manifest, a, opts.positionals); try @import("commands/uninstall.zig").run(alloc, &manifest, a, opts.positionals);
},
try manifest.save(); .unuse => |a| {
try @import("commands/unuse.zig").run(alloc, &manifest, a, opts.positionals);
}, },
} }
try manifest.save();
} }