From e65718178eee380d18fe8f69d8be5de760192e12 Mon Sep 17 00:00:00 2001 From: Squibid Date: Fri, 9 Jan 2026 21:31:59 -0500 Subject: [PATCH] ! drop current implementation --- README.md | 28 -------- build.zig | 42 ------------ build.zig.zon | 20 ------ build.zig.zon.nix | 34 ---------- flake.nix | 62 +---------------- src/NixStore.zig | 53 --------------- src/extractor.zig | 3 - src/main.zig | 153 ------------------------------------------ src/remotes/Curl.zig | 33 --------- src/remotes/Fs.zig | 11 --- src/remotes/Git.zig | 117 -------------------------------- src/util/gpg_helper.c | 7 -- src/util/gpg_helper.h | 3 - 13 files changed, 1 insertion(+), 565 deletions(-) delete mode 100644 build.zig delete mode 100644 build.zig.zon delete mode 100644 build.zig.zon.nix delete mode 100644 src/NixStore.zig delete mode 100644 src/extractor.zig delete mode 100644 src/main.zig delete mode 100644 src/remotes/Curl.zig delete mode 100644 src/remotes/Fs.zig delete mode 100644 src/remotes/Git.zig delete mode 100644 src/util/gpg_helper.c delete mode 100644 src/util/gpg_helper.h diff --git a/README.md b/README.md index bde3573..f24556c 100644 --- a/README.md +++ b/README.md @@ -6,32 +6,4 @@ Declaratively keep your stuff up to date in your nixos config. ## Usage ```nix -{ - description = "My flake config"; - inputs.fooud.url = "git+https://git.squi.bid/squibid/fooud"; - outputs = { self, nixpkgs, fooud }: { - nixosConfigurations.my-system = nixpkgs.lib.nixosSystem { - modules = [{ - services.nginx.virtualHosts."squi.bid" = { - root = fooud.lib.gitUpdater config { - git = "https://git.squi.bid/squibid/squi.bid"; # the source of the data - dest = "/var/www/squi.bid"; # where should the files live on disk - keys = [ "BECE5684D3C4005D" ]; # requires the commit to be signed by me - check = "5m"; # we may be no more than 5 minutes out of date from the source - }; - - locations."/" = { - tryFiles = "$uri $uri.html $uri/"; - index = "index.html index.htm"; - }; - }; - }]; - }; - }; -} ``` - -# TODO -- extract archives -- support copying files from other parts of the filesystem (although that's rathar impure isn't it) -- add some checks diff --git a/build.zig b/build.zig deleted file mode 100644 index a5104a3..0000000 --- a/build.zig +++ /dev/null @@ -1,42 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); - - const mod = b.addModule("fooud", .{ - .root_source_file = b.path("src/main.zig"), - .target = target, - }); - - const exe = b.addExecutable(.{ - .name = "fooud", - .root_module = b.createModule(.{ - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, - .imports = &.{ - .{ .name = "fooud", .module = mod }, - }, - }), - }); - - const dep_curl = b.dependency("curl", .{}); - - exe.root_module.addImport("curl", dep_curl.module("curl")); - exe.root_module.linkSystemLibrary("git2", .{}); - exe.root_module.linkSystemLibrary("gpgme", .{}); - exe.root_module.addIncludePath(b.path("src/util")); - exe.root_module.addCSourceFile(.{ .file = b.path("src/util/gpg_helper.c") }); - exe.linkLibC(); - - b.installArtifact(exe); - - const run_step = b.step("run", "Run the app"); - const run_cmd = b.addRunArtifact(exe); - run_step.dependOn(&run_cmd.step); - run_cmd.step.dependOn(b.getInstallStep()); - if (b.args) |args| { - run_cmd.addArgs(args); - } -} diff --git a/build.zig.zon b/build.zig.zon deleted file mode 100644 index babc251..0000000 --- a/build.zig.zon +++ /dev/null @@ -1,20 +0,0 @@ -.{ - .name = .fooud, - .version = "1.0.0", - .fingerprint = 0xe809364a41c00a2b, // Changing this has security and trust implications. - .minimum_zig_version = "0.15.2", - .dependencies = .{ - .curl = .{ - .url = "https://github.com/jiacai2050/zig-curl/archive/refs/tags/v0.3.2.zip", - .hash = "curl-0.3.2-P4tT4SXPAACuV6f5eyh4jG_1SspjWwMm_vRJfoKrQep5", - }, - }, - .paths = .{ - "build.zig", - "build.zig.zon", - "src", - // For example... - //"LICENSE", - //"README.md", - }, -} diff --git a/build.zig.zon.nix b/build.zig.zon.nix deleted file mode 100644 index 5ce3c9a..0000000 --- a/build.zig.zon.nix +++ /dev/null @@ -1,34 +0,0 @@ -# generated by zon2nix (https://github.com/nix-community/zon2nix) - -{ linkFarm, fetchzip, fetchgit }: - -linkFarm "zig-packages" [ - { - name = "N-V-__8AAFrtpQI1j9eOv7aN3lt3eH1TJfH4npAdRIrg2gGH"; - path = fetchzip { - url = "https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v3.6.0.tar.gz"; - hash = "sha256-yzGBkrqh+T/5GS66xL5zJstCmvcfG09TfxqA3F8UPJg="; - }; - } - { - name = "N-V-__8AAHipPQF9UuLPiaV1CtJzZIxvTN61tMGdFx8LGjIV"; - path = fetchzip { - url = "https://github.com/curl/curl/releases/download/curl-8_8_0/curl-8.8.0.tar.gz"; - hash = "sha256-Gqfe8iiC8aCBiuUVO6VYqW5DmqgSv5oS4XvMfQgbwFw="; - }; - } - { - name = "N-V-__8AAJj_QgDBhU17TCtcvdjOZZPDfkvxrEAyZkc14VN8"; - path = fetchzip { - url = "https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz"; - hash = "sha256-acY8yFzIRYbrZ2CGODoxLnZuppsP6KZy19I9Yy77pfc="; - }; - } - { - name = "curl-0.3.2-P4tT4SXPAACuV6f5eyh4jG_1SspjWwMm_vRJfoKrQep5"; - path = fetchzip { - url = "https://github.com/jiacai2050/zig-curl/archive/refs/tags/v0.3.2.zip"; - hash = "sha256-1FjYirex2Q/zs5GLNtCGgClyV5/SW0GhxFHA1Hm+e4o="; - }; - } -] diff --git a/flake.nix b/flake.nix index d5bf44f..3d8d708 100644 --- a/flake.nix +++ b/flake.nix @@ -1,66 +1,6 @@ { description = "Declaratively update your data."; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - outputs = { self, nixpkgs, ... }: let - system = "x86_64-linux"; - pkgs = import nixpkgs { inherit system; }; - package = pkgs.stdenv.mkDerivation rec { - pname = "fooud"; - version = "1.0"; - - src = ./.; - deps = pkgs.callPackage ./build.zig.zon.nix {}; - nativeBuildInputs = [ - pkgs.zig.hook - pkgs.glibc - pkgs.libgit2 - pkgs.gpgme - pkgs.libgpg-error - pkgs.pkg-config - ]; - zigBuildFlags = [ - "--system" "${deps}" - # "-Doptimize=Debug" - # "-Dtarget=${system}" - ]; - }; - in { - lib = builtins.import ./lib.nix { pkgs = pkgs; }; - nixosModules = rec { - fooud = { pkgs, lib, config, inputs, ... }: { - options.programs.fooud.enable = lib.mkEnableOption ("fooud") - // { default = true; }; - config = lib.mkIf config.programs.fooud.enable { - environment.systemPackages = [ - pkgs.glibc - pkgs.libgit2 - pkgs.gpgme - pkgs.libgpg-error - pkgs.nix - package - ]; - }; - }; - default = fooud; - }; - packages.${system} = rec { default = package; fooud = default; }; - - checks.${system}.build = let - package = self.packages.${system}.default; - in pkgs.runCommand "fooud-build" { buildInputs = [ - pkgs.git - pkgs.nix - ]; } '' - mkdir repo - git -C repo init > /dev/null 2>&1 - echo "hi" > repo/README.md - git -C repo config user.email "you@example.com" > /dev/null 2>&1 - git -C repo config user.name "Your Name" > /dev/null 2>&1 - git -C repo add . > /dev/null 2>&1 - git -C repo commit -m "initial commit" > /dev/null 2>&1 - - # this check won't succeed until I find a way to run a chroot store - ${package}/bin/fooud --git repo --dest test - ''; + outputs = { ... }: { }; } diff --git a/src/NixStore.zig b/src/NixStore.zig deleted file mode 100644 index b7db990..0000000 --- a/src/NixStore.zig +++ /dev/null @@ -1,53 +0,0 @@ -const NixStore = @This(); - -const std = @import("std"); - -const gpa = std.heap.page_allocator; - -/// Add a path to the store, this will copy the contents of path recursively -/// into the store and return a (hopefully) valid store path. To try and keep -/// this store path valid you should follow this with a call to realize() and -/// then root the store path. -pub fn add(path: []const u8) error{Failure}![]const u8 { - const res = std.process.Child.run(.{ - .allocator = gpa, - .argv = &[_][]const u8{ "nix", "store", "add", path }, - }) catch return error.Failure; - - if (res.term != .Exited or res.term.Exited != 0) return error.Failure; - if (res.stdout.len == 0) return error.Failure; - return res.stdout[0 .. res.stdout.len - 1]; // to chop off the \n -} - -/// This tries to tell the store that a store_path should stick around a while -/// longer, there's a chance that it doesn't listen and removes that store_path -/// but in my testing it seemed to stick around. -pub fn realize(store_path: []const u8) error{Failure}!void { - _ = std.process.Child.run(.{ - .allocator = gpa, - .argv = &[_][]const u8{ "nix-store", "--realize", store_path }, - }) catch return error.Failure; - return; -} - -/// This tells the nix store to not gc our new store_path because it has a -/// dependency at root_path and that only if root_path doesn't exist anymore -/// it can delete store_path. -pub fn root(store_path: []const u8, root_path: []const u8) error{Failure}!void { - _ = std.process.Child.run(.{ - .allocator = gpa, - .argv = &[_][]const u8{ "nix-store", "--add-root", root_path, "--indirect", store_path }, - }) catch return error.Failure; - return; -} - -/// Delete a path from the store. -pub fn delete(store_path: []const u8) error{Failure}!void { - _ = std.process.Child.run(.{ - .allocator = gpa, - .argv = &[_][]const u8{ "nix", "store", "delete", store_path }, - }) catch { - return error.Failure; - }; - return; -} diff --git a/src/extractor.zig b/src/extractor.zig deleted file mode 100644 index 9995341..0000000 --- a/src/extractor.zig +++ /dev/null @@ -1,3 +0,0 @@ -const Extractor = @This(); - -// TODO: impl diff --git a/src/main.zig b/src/main.zig deleted file mode 100644 index aacd242..0000000 --- a/src/main.zig +++ /dev/null @@ -1,153 +0,0 @@ -const std = @import("std"); - -const NixStore = @import("NixStore.zig"); -const Git = @import("remotes/Git.zig"); -const Curl = @import("remotes/Curl.zig"); -const Fs = @import("remotes/Fs.zig"); - -const gpa = std.heap.page_allocator; -const Options = enum { - git, - url, - path, - dest, - key, - extract, -}; - -const Config = struct { - const Remote = union(enum) { - git: []const u8, - url: []const u8, - path: []const u8, - none, - }; - remote: Remote, - dest: ?[]const u8, - keys: std.ArrayList([]const u8), - extract: bool, -}; - -pub fn main() !void { - var config: Config = .{ - .remote = .none, - .dest = null, - .keys = .empty, - .extract = false, - }; - - try check_args(&config); - - const tmp_dest = try std.fmt.allocPrint(gpa, "/tmp/{s}", .{config.dest.?}); - defer gpa.free(tmp_dest); - - // we're gonna delete the temp file(s) if they exist because we don't want - // to deal with them - var exists = true; - std.fs.cwd().access(tmp_dest, .{}) catch { - exists = false; - }; - if (exists) try std.fs.cwd().deleteTree(tmp_dest); - - const res = try switch (config.remote) { - .git => Git.get(config.remote.git, tmp_dest, try config.keys.toOwnedSlice(gpa)), - .url => Curl.get(config.remote.url, tmp_dest), - .path => Fs.get(config.remote.path, tmp_dest), - else => unreachable, - }; - defer std.fs.cwd().deleteTree(tmp_dest) catch {}; - - if (!res) { - std.log.err("failed to obtain the remote file(s)", .{}); - std.process.exit(1); - } - - if (config.extract) { - // TODO: impl - } - - // Now that we've gotten all our files it's time to add it to the nix store - const store_path = try NixStore.add(tmp_dest); - try NixStore.realize(store_path); - std.log.info("new store path: {s}", .{store_path}); - - // link the dest to the new nix store path - var dest_old_store_path: ?[]u8 = null; - while (true) { - std.fs.cwd().symLink(store_path, config.dest.?, .{}) catch |err| switch (err) { - error.PathAlreadyExists => { - // get the old nix store path - const buf: [1024]u8 = undefined; - dest_old_store_path = try std.fs.cwd().readLink(config.dest.?, @constCast(buf[0..])); - std.log.info("old store path: {s}", .{dest_old_store_path.?}); - - // if the old store path and the current store path are the - // same then don't do anything, nothing will change - if (std.mem.eql(u8, dest_old_store_path.?, store_path)) return; - - try std.fs.cwd().deleteFile(config.dest.?); - continue; - }, - else => { - if (dest_old_store_path) |path| { - try std.fs.cwd().symLink(path, config.dest.?, .{}); - } - return err; - }, - }; - try NixStore.root(store_path, config.dest.?); - break; - } - - // delete the old nix store path - if (dest_old_store_path) |path| { - std.log.info("deleting old store path: {s}", .{path}); - try NixStore.delete(path); - } -} - -fn check_args(config: *Config) !void { - const iter = @constCast(&std.process.args()); - while (iter.next()) |arg| if (std.mem.eql(u8, arg[0..2], "--")) { - const t = std.meta.stringToEnum(Options, arg[2..]) orelse { - std.log.err("{s} is not a valid option", .{arg}); - std.process.exit(1); - }; - if (t == .extract) { // this option doesn't require arguments - config.extract = true; - continue; - } - const val = iter.next() orelse return error.Invalid; - switch (t) { - .git => config.remote = Config.Remote{ .git = val }, - .url => config.remote = Config.Remote{ .url = val }, - .path => config.remote = Config.Remote{ .path = val }, - .key => try config.keys.append(gpa, val), - .dest => config.dest = val, - else => unreachable, - } - }; - - var err = false; - if (config.remote == .none) { - std.log.err("you must set one of --git, --url, or --path", .{}); - err = true; - } - - if (config.dest == null) { - std.log.err("you must set a --dest", .{}); - err = true; - } - - if (config.keys.items.len > 0 and config.remote != .git) { - std.log.err("you can only check keys on a git repository", .{}); - err = true; - } - - if (config.extract and config.remote == .git) { - std.log.err("you cannot extract a git repository", .{}); - err = true; - } - - if (err) std.process.exit(1); -} diff --git a/src/remotes/Curl.zig b/src/remotes/Curl.zig deleted file mode 100644 index fd9f986..0000000 --- a/src/remotes/Curl.zig +++ /dev/null @@ -1,33 +0,0 @@ -const Curl = @This(); - -const std = @import("std"); -const curl = @import("curl"); - -const gpa = std.heap.page_allocator; - -pub fn get(url: []const u8, dest: []const u8) !bool { - const ca_bundle = try curl.allocCABundle(gpa); - defer ca_bundle.deinit(); - const easy = try curl.Easy.init(.{ .ca_bundle = ca_bundle }); - defer easy.deinit(); - - // let's get that dest file opened up - const fp = try std.fs.cwd().createFile(dest, .{}); - defer fp.close(); - const buffer: [1024]u8 = undefined; - const writer = fp.writer(@constCast(&buffer)); - - var tmp_url = try gpa.alloc(u8, url.len + 1); - defer gpa.free(tmp_url); - @memcpy(tmp_url[0..url.len], url); - tmp_url[url.len] = 0; - - // download it - try easy.setUrl(@ptrCast(tmp_url)); - try easy.setWriter(@constCast(&writer.interface)); - const response = try easy.perform(); - if (response.status_code != 200) return false; - - try @constCast(&writer.interface).flush(); - return true; -} diff --git a/src/remotes/Fs.zig b/src/remotes/Fs.zig deleted file mode 100644 index 301414e..0000000 --- a/src/remotes/Fs.zig +++ /dev/null @@ -1,11 +0,0 @@ -const Fs = @This(); - -const std = @import("std"); - -pub fn get(src: []const u8, dest: []const u8) !bool { - _ = src; - _ = dest; - - // TODO: impl - return false; -} diff --git a/src/remotes/Git.zig b/src/remotes/Git.zig deleted file mode 100644 index f82f0b4..0000000 --- a/src/remotes/Git.zig +++ /dev/null @@ -1,117 +0,0 @@ -const Git = @This(); - -const std = @import("std"); -pub const c = @cImport({ - @cInclude("git2.h"); - @cInclude("gpgme.h"); - @cInclude("gpg_helper.h"); -}); - -pub const GitError = error{ - InitFailed, - OpenFailed, - NotFound, - InvalidObject, - InvalidReference, - OutOfMemory, - Unknown, -}; - -pub fn get(url: []const u8, dest: []const u8, keys: []const []const u8) !bool { - if (c.git_libgit2_init() < 0) return GitError.InitFailed; - defer _ = c.git_libgit2_shutdown(); - - var exists = true; - std.fs.cwd().access(dest, .{}) catch { - exists = false; - }; - - var repo: ?*c.git_repository = null; - if (!exists) repo = clone(url, dest) catch return false; - defer if (repo) |r| c.git_repository_free(r); - - if (keys.len <= 0) return repo != null; - return try check_signing_key(repo, keys); -} - -fn clone(url: []const u8, dest: []const u8) !?*c.git_repository { - var clone_opts: c.git_clone_options = undefined; - _ = c.git_clone_options_init(&clone_opts, c.GIT_CLONE_OPTIONS_VERSION); - var repo: ?*c.git_repository = null; - - const err = c.git_clone(&repo, url.ptr, dest.ptr, &clone_opts); - if (err != 0) { - const git_err = c.git_error_last(); - if (git_err != null and git_err.*.message != null) { - std.log.err("Clone error {d}: {s}", .{ err, git_err.*.message }); - } else { - std.log.err("Clone error {d}: ", .{err}); - } - return null; - } - - return repo; -} - -fn check_signing_key(repo: ?*c.git_repository, keys: []const []const u8) !bool { - var head_ref: ?*c.git_reference = null; - if (c.git_repository_head(&head_ref, repo) != 0) { - std.log.err("Failed to get HEAD", .{}); - return false; - } - defer c.git_reference_free(head_ref); - - const oid_ptr = c.git_reference_target(head_ref); - if (oid_ptr == null) { - std.log.err("HEAD is not pointing to a commit", .{}); - return false; - } - - var signature = c.git_buf{}; - var signed_data = c.git_buf{}; - if (c.git_commit_extract_signature(&signature, &signed_data, repo, @constCast(oid_ptr), null) != 0) { - std.log.err("HEAD commit is not signed", .{}); - return false; - } - defer c.git_buf_dispose(&signature); - defer c.git_buf_dispose(&signed_data); - - // gpg me - - _ = c.gpgme_check_version(null); - var ctx: c.gpgme_ctx_t = null; - _ = c.gpgme_new(&ctx); - defer _ = c.gpgme_release(ctx); - - var sig_data: c.gpgme_data_t = null; - var signed_text_data: c.gpgme_data_t = null; - _ = c.gpgme_data_new_from_mem(&sig_data, signature.ptr, signature.size, 0); - defer _ = c.gpgme_data_release(sig_data); - _ = c.gpgme_data_new_from_mem(&signed_text_data, signed_data.ptr, signed_data.size, 0); - defer _ = c.gpgme_data_release(signed_text_data); - - if (c.gpgme_op_verify(ctx, sig_data, signed_text_data, null) != 0) { - std.log.err("Failed to verify signature", .{}); - return false; - } - - const result = c.gpgme_op_verify_result(ctx); - if (result == null) { - std.log.err("No signature found in verification result", .{}); - return false; - } - - const fpr = c.first_signature_fpr(ctx); - if (fpr == null) { - std.log.err("No signature found", .{}); - return false; - } - - for (keys) |key| { - if (std.mem.eql(u8, std.mem.span(fpr + 24), key)) { - return true; - } - } - - return false; -} diff --git a/src/util/gpg_helper.c b/src/util/gpg_helper.c deleted file mode 100644 index 9cc5b88..0000000 --- a/src/util/gpg_helper.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "gpg_helper.h" - -const char* first_signature_fpr(gpgme_ctx_t ctx) { - gpgme_verify_result_t result = gpgme_op_verify_result(ctx); - if (!result || !result->signatures) return NULL; - return result->signatures->fpr; -} diff --git a/src/util/gpg_helper.h b/src/util/gpg_helper.h deleted file mode 100644 index 3cfcda4..0000000 --- a/src/util/gpg_helper.h +++ /dev/null @@ -1,3 +0,0 @@ -#include - -const char* first_signature_fpr(gpgme_ctx_t ctx);