srctree

Gregory Mullen parent b1d50d05 094ec9f3
add support hidden repos

note the difference between secret (not yet implemented) and privatewhere I also haven't validated all private repos are hidden from publicview yet. Additionally, private repos are currently private even whenlogged in.

inlinesplit
src/endpoints/network.zig added: 192, removed: 105, total 87
@@ -5,7 +5,7 @@ const NetworkPage = template.PageData("network.html");
pub fn index(ctx: *Frame) Error!void {
var dom = DOM.new(ctx.alloc);
 
const list = try Repos.allNames(ctx.alloc);
const list = Repos.allNames(ctx.alloc) catch return error.Unknown;
const cwd = std.fs.cwd();
for (list) |reponame| {
var b: [0x800]u8 = undefined;
 
src/endpoints/repos.zig added: 192, removed: 105, total 87
@@ -46,15 +46,7 @@ pub const RouteData = struct {
}
 
pub fn exists(self: RouteData) bool {
var cwd = std.fs.cwd();
var dir = cwd.openDir("./repos", .{ .iterate = true }) catch return false;
defer dir.close();
var itr = dir.iterate();
while (itr.next() catch return false) |file| {
if (file.kind != .directory and file.kind != .sym_link) continue;
if (std.mem.eql(u8, file.name, self.name)) return true;
}
return false;
return repos.exists(self.name, .public);
}
};
 
@@ -211,67 +203,57 @@ const RepoSortReq = struct {
};
 
fn list(ctx: *Frame) Error!void {
var cwd = std.fs.cwd();
 
const udata = ctx.request.data.query.validate(RepoSortReq) catch return error.BadData;
const tag_sort: bool = if (udata.sort) |srt| if (eql(u8, srt, "tag")) true else false else false;
 
if (cwd.openDir("./repos", .{ .iterate = true })) |idir| {
var repos = std.ArrayList(Git.Repo).init(ctx.alloc);
var itr = idir.iterate();
while (itr.next() catch return Error.Unknown) |file| {
if (file.kind != .directory and file.kind != .sym_link) continue;
if (file.name[0] == '.') continue;
const rdir = idir.openDir(file.name, .{}) catch continue;
var rpo = Git.Repo.init(rdir) catch continue;
rpo.loadData(ctx.alloc) catch return error.Unknown;
rpo.repo_name = ctx.alloc.dupe(u8, file.name) catch null;
var repo_iter = repos.allRepoIterator(.public) catch return error.Unknown;
var current_repos = std.ArrayList(Git.Repo).init(ctx.alloc);
while (repo_iter.next() catch return Error.Unknown) |rpo_| {
var rpo = rpo_;
rpo.loadData(ctx.alloc) catch return error.Unknown;
rpo.repo_name = ctx.alloc.dupe(u8, repo_iter.current_name.?) catch null;
 
if (rpo.tags != null) {
std.sort.heap(Git.Tag, rpo.tags.?, {}, tagSorter);
}
try repos.append(rpo);
if (rpo.tags != null) {
std.sort.heap(Git.Tag, rpo.tags.?, {}, tagSorter);
}
 
std.sort.heap(Git.Repo, repos.items, repoctx{
.alloc = ctx.alloc,
.by = if (tag_sort) .tag else .commit,
}, repoSorterNew);
 
var repo_buttons: []const u8 = "";
if (ctx.user != null and ctx.user.?.valid()) {
repo_buttons =
\\<div class="act-btns"><a class="btn" href="/admin/clone-upstream">New Upstream</a></div>
;
}
 
const repos_compiled = try ctx.alloc.alloc(template.Structs.RepoList, repos.items.len);
for (repos.items, repos_compiled) |*repo, *compiled| {
defer repo.raze();
compiled.* = repoBlock(ctx.alloc, repo.repo_name orelse "unknown", repo.*) catch {
return error.Unknown;
};
}
 
//var btns = [1]template.Structs.NavButtons{.{
// .name = "inbox",
// .extra = 0,
// .url = "/inbox",
//}};
 
var page = ReposPage.init(.{
.meta_head = .{ .open_graph = .{} },
.body_header = ctx.response_data.get(S.BodyHeaderHtml) catch return error.Unknown,
 
.buttons = .{ .buttons = repo_buttons },
.repo_list = repos_compiled,
});
 
try ctx.sendPage(&page);
} else |err| {
std.debug.print("unable to open given dir {}\n", .{err});
return;
try current_repos.append(rpo);
}
 
std.sort.heap(Git.Repo, current_repos.items, repoctx{
.alloc = ctx.alloc,
.by = if (tag_sort) .tag else .commit,
}, repoSorterNew);
 
var repo_buttons: []const u8 = "";
if (ctx.user != null and ctx.user.?.valid()) {
repo_buttons =
\\<div class="act-btns"><a class="btn" href="/admin/clone-upstream">New Upstream</a></div>
;
}
 
const repos_compiled = try ctx.alloc.alloc(template.Structs.RepoList, current_repos.items.len);
for (current_repos.items, repos_compiled) |*repo, *compiled| {
defer repo.raze();
compiled.* = repoBlock(ctx.alloc, repo.repo_name orelse "unknown", repo.*) catch {
return error.Unknown;
};
}
 
//var btns = [1]template.Structs.NavButtons{.{
// .name = "inbox",
// .extra = 0,
// .url = "/inbox",
//}};
 
var page = ReposPage.init(.{
.meta_head = .{ .open_graph = .{} },
.body_header = ctx.response_data.get(S.BodyHeaderHtml) catch return error.Unknown,
 
.buttons = .{ .buttons = repo_buttons },
.repo_list = repos_compiled,
});
 
try ctx.sendPage(&page);
}
 
fn dupeDir(a: Allocator, name: []const u8) ![]u8 {
@@ -292,10 +274,7 @@ fn treeBlob(ctx: *Frame) Error!void {
const rd = RouteData.make(&ctx.uri) orelse return error.Unrouteable;
_ = ctx.uri.next();
 
var cwd = std.fs.cwd();
const filename = try allocPrint(ctx.alloc, "./repos/{s}", .{rd.name});
const dir = cwd.openDir(filename, .{}) catch return error.Unknown;
var repo = Git.Repo.init(dir) catch return error.Unknown;
var repo = (repos.open(rd.name, .public) catch return error.Unknown) orelse return error.Unrouteable;
repo.loadData(ctx.alloc) catch return error.Unknown;
defer repo.raze();
 
@@ -419,14 +398,12 @@ fn blame(ctx: *Frame) Error!void {
_ = ctx.uri.next();
const blame_file = ctx.uri.rest();
 
var cwd = std.fs.cwd();
const fname = try allocPrint(ctx.alloc, "./repos/{s}", .{rd.name});
const dir = cwd.openDir(fname, .{}) catch return error.Unknown;
var repo = Git.Repo.init(dir) catch return error.Unknown;
var repo = (repos.open(rd.name, .public) catch return error.Unknown) orelse return error.Unrouteable;
defer repo.raze();
 
var actions = repo.getAgent(ctx.alloc);
actions.cwd = cwd.openDir(fname, .{}) catch unreachable;
actions.cwd = if (!repo.bare) repo.dir.openDir("..", .{}) catch unreachable else repo.dir;
defer if (!repo.bare) actions.cwd.?.close();
const git_blame = actions.blame(blame_file) catch unreachable;
 
const parsed = parseBlame(ctx.alloc, git_blame) catch unreachable;
@@ -761,10 +738,7 @@ const TagPage = template.PageData("repo-tags.html");
fn tagsList(ctx: *Frame) Error!void {
const rd = RouteData.make(&ctx.uri) orelse return error.Unrouteable;
 
var cwd = std.fs.cwd();
const filename = try allocPrint(ctx.alloc, "./repos/{s}", .{rd.name});
const dir = cwd.openDir(filename, .{}) catch return error.Unknown;
var repo = Git.Repo.init(dir) catch return error.Unknown;
var repo = (repos.open(rd.name, .public) catch return error.Unknown) orelse return error.Unrouteable;
repo.loadData(ctx.alloc) catch return error.Unknown;
defer repo.raze();
 
@@ -811,7 +785,7 @@ const RequestData = verse.RequestData.RequestData;
 
const Humanize = @import("../humanize.zig");
const Ini = @import("../ini.zig");
const Repos = @import("../repos.zig");
const repos = @import("../repos.zig");
const Git = @import("../git.zig");
const Highlight = @import("../syntax-highlight.zig");
 
 
src/git.zig added: 192, removed: 105, total 87
@@ -1,5 +1,3 @@
const Ini = @import("ini.zig");
 
pub const Actor = @import("git/actor.zig");
pub const Agent = @import("git/agent.zig");
pub const Blob = @import("git/blob.zig");
@@ -1332,6 +1330,8 @@ test "list remotes" {
try std.testing.expectEqualStrings("gr.ht", remotes[1].name);
}
 
const Ini = @import("ini.zig");
 
const std = @import("std");
const Allocator = std.mem.Allocator;
const startsWith = std.mem.startsWith;
 
src/git/agent.zig added: 192, removed: 105, total 87
@@ -1,18 +1,11 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
 
const Git = @import("../git.zig");
const Repo = Git.Repo;
const Tree = Git.Tree;
 
pub const Agent = @This();
 
const DEBUG_GIT_ACTIONS = false;
 
alloc: Allocator,
repo: ?*const Repo = null,
cwd: ?std.fs.Dir = null,
 
const Agent = @This();
 
const DEBUG_GIT_ACTIONS = false;
 
pub fn updateUpstream(self: Agent, branch: []const u8) !bool {
const fetch = try self.exec(&[_][]const u8{
"git",
@@ -201,8 +194,10 @@ fn execCustom(self: Agent, argv: []const []const u8) !std.process.Child.RunResul
 
fn exec(self: Agent, argv: []const []const u8) ![]u8 {
const child = try self.execCustom(argv);
if (child.stderr.len > 0) std.debug.print("git Agent error\nstderr {s}\n", .{child.stderr});
self.alloc.free(child.stderr);
if (child.stderr.len > 0) {
std.debug.print("git Agent error\nstderr: {s}\n", .{child.stderr});
}
defer self.alloc.free(child.stderr);
 
if (DEBUG_GIT_ACTIONS) std.debug.print(
\\git action
@@ -210,7 +205,19 @@ fn exec(self: Agent, argv: []const []const u8) ![]u8 {
\\'''
\\{s}
\\'''
\\{s}
\\'''
\\
, .{ argv[1], child.stdout });
\\git agent
\\{any}
\\
, .{ argv[1], child.stdout, child.stderr, self.cwd });
return child.stdout;
}
 
const std = @import("std");
const Allocator = std.mem.Allocator;
 
const Git = @import("../git.zig");
const Repo = Git.Repo;
const Tree = Git.Tree;
 
src/repos.zig added: 192, removed: 105, total 87
@@ -1,22 +1,127 @@
const Repos = @This();
const repos = @This();
 
const DEBUG = false;
pub var dirs: RepoDirs = .{};
 
pub const RepoDirs = struct {
public: ?[]const u8 = "./repos",
private: ?[]const u8 = null,
secret: ?[]const u8 = null,
 
pub fn directory(rds: RepoDirs, vis: Visability) !std.fs.Dir {
var cwd = std.fs.cwd();
return cwd.openDir(switch (vis) {
.public => rds.public orelse return error.NoDirectory,
.private => rds.private orelse return error.NoDirectory,
.secret => rds.secret orelse return error.NoDirectory,
}, .{ .iterate = true });
}
};
 
pub const Visability = enum {
public,
private,
secret,
 
pub const len = @typeInfo(Visability).@"enum".fields.len;
};
 
/// public, but use with caution, might cause side channel leakage
pub fn isHiddenVis(name: []const u8, vis: Visability) bool {
if (global_config.repos) |crepos| {
if (crepos.@"hidden-repos") |hr| {
// if you actually use null, I hate you!
var repo_itr = std.mem.tokenizeAny(u8, hr, "\x00|;, \t");
while (repo_itr.next()) |r|
if (eql(u8, name, r)) return switch (vis) {
.public => true,
.private => false,
.secret => @panic("not implemented"),
};
}
}
return false;
}
 
/// public, but use with caution, might cause side channel leakage
pub fn isHidden(name: []const u8) bool {
return isHiddenVis(name, .public);
}
 
pub fn exists(name: []const u8, vis: Visability) bool {
var dir = dirs.directory(vis) catch return false;
defer dir.close();
var itr = dir.iterate();
while (itr.next() catch return false) |file| {
if (file.kind != .directory and file.kind != .sym_link) continue;
if (eql(u8, file.name, name)) {
// lol, crap, there's a side channel leak no matter where I put
// this... given near zero thought I've decided this is the better
// option
if (isHiddenVis(name, vis)) return false;
return true;
}
}
return false;
}
 
pub fn open(name: []const u8, vis: Visability) !?Git.Repo {
if (isHiddenVis(name, vis)) return null;
var root = try dirs.directory(vis);
defer root.close();
const dir = root.openDir(name, .{}) catch |err| switch (err) {
error.FileNotFound => return null,
error.NotDir => return null,
else => return err,
};
return try Git.Repo.init(dir);
}
 
pub fn allNames(a: Allocator) ![][]u8 {
var list = std.ArrayList([]u8).init(a);
 
const cwd = std.fs.cwd();
var repo_dirs = cwd.openDir("repos", .{ .iterate = true }) catch unreachable;
defer repo_dirs.close();
var itr_repo = repo_dirs.iterate();
var dir_set = try dirs.directory(.public);
defer dir_set.close();
var itr_repo = dir_set.iterate();
 
while (itr_repo.next() catch null) |dir| {
if (dir.kind != .directory and dir.kind != .sym_link) continue;
if (isHidden(dir.name)) continue;
try list.append(try a.dupe(u8, dir.name));
}
return try list.toOwnedSlice();
}
 
pub const RepoIterator = struct {
dir: std.fs.Dir,
itr: std.fs.Dir.Iterator,
vis: Visability,
/// only valid until the following call to next()
current_name: ?[]const u8 = null,
 
pub fn next(ri: *RepoIterator) !?Git.Repo {
while (try ri.itr.next()) |file| {
if (file.kind != .directory and file.kind != .sym_link) continue;
if (file.name[0] == '.') continue;
if (isHidden(file.name)) continue;
const rdir = ri.dir.openDir(file.name, .{}) catch continue;
ri.current_name = file.name;
return try Git.Repo.init(rdir);
}
ri.current_name = null;
return null;
}
};
 
pub fn allRepoIterator(vis: Visability) !RepoIterator {
const dir = try dirs.directory(vis);
return .{
.dir = dir,
.itr = dir.iterate(),
.vis = vis,
};
}
 
pub fn containsName(name: []const u8) bool {
return if (name.len > 0) true else false;
}
@@ -146,3 +251,4 @@ const eql = std.mem.eql;
 
const Git = @import("git.zig");
const SrcConfig = @import("main.zig").SrcConfig;
const global_config = &@import("main.zig").global_config.config;