╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───────╮ │src/test/net.zig│ ╰────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │107 } │ │108 } │ │109 │ │110 + if (builtin.os.tag == .windows) { │ │111 + // openssh needs an acl granting access only to the current user. │ │112 + // use cwd so the temp path doesn't need powershell quoting. │ │113 + var permissions_process = try std.process.spawn(io, .{ │ │114 + .argv = &.{ │ │115 + "powershell.exe", "-NoProfile", "-NonInteractive", "-Command", │ │116 + \\$ErrorActionPreference = 'Stop' │ │117 + \\$user = [System.Security.Principal.WindowsIdentity]::GetCurrent().User │ │118 + \\$acl = [System.Security.AccessControl.FileSecurity]::new() │ │119 + \\$acl.SetOwner($user) │ │120 + \\$acl.SetAccessRuleProtection($true, $false) │ │121 + \\$rule = [System.Security.AccessControl.FileSystemAccessRule]::new($user, 'FullControl', 'Allow')│ │122 + \\$acl.AddAccessRule($rule) │ │123 + \\Set-Acl -LiteralPath .\key -AclObject $acl │ │124 + , │ │125 + }, │ │126 + .cwd = .{ .path = temp_path }, │ │127 + .stdin = .ignore, │ │128 + .stdout = .ignore, │ │129 + .stderr = .inherit, │ │130 + }); │ │131 + defer permissions_process.kill(io); │ │132 + const term = try permissions_process.wait(io); │ │133 + if (term != .exited or term.exited != 0) return error.SshKeyPermissionsFailed; │ │134 + } │ │135 + │ │136 // seed the admin event store so the server can resolve <owner>/<repo> paths │ │137 // and authenticate pushes from the dev key above │ │138 try setupAdmin(io, allocator, temp_path); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │769 const other_url = try std.fmt.allocPrint(allocator, "git@localhost:fork/admin/other/patch:{s}", .{&fork_id_hex}); │ │770 defer allocator.free(other_url); │ │771 try client.addRemote(io, allocator, .{ .name = "other", .value = other_url }); │ │764 - const wrong_repo_rejected = if (client.push(io, allocator, "other", "master:patch", false, .{ .wire = .{ .ssh = .{ .command = ssh_cmd } } })) |_| false else |_| true; │ │765 - try std.testing.expect(wrong_repo_rejected); │ │772 + // early rejections need no input. give ssh eof so windows openssh │ │773 + // doesn't hang closing stdin before forwarding the error. │ │774 + const reject_ssh_cmd = try std.fmt.allocPrint(allocator, "{s} -n", .{ssh_cmd}); │ │775 + defer allocator.free(reject_ssh_cmd); │ │776 + try std.testing.expectError(error.ServerReportedError, client.push(io, allocator, "other", "master:patch", false, .{ .wire = .{ .ssh = .{ .command = reject_ssh_cmd } } }));│ │777 │ │778 // │ │779 // the fork remains cloneable after the target repo is removed │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │829 const denied_url = try std.fmt.allocPrint(allocator, "git@localhost:fork/admin/target/patch:{s}", .{&fork_id_hex}); │ │830 defer allocator.free(denied_url); │ │831 try client.addRemote(io, allocator, .{ .name = "denied", .value = denied_url }); │ │821 - const unauthorized_rejected = if (client.push(io, allocator, "denied", "master:patch", false, .{ .wire = .{ .ssh = .{ .command = ssh_cmd } } })) |_| false else |_| true; │ │822 - try std.testing.expect(unauthorized_rejected); │ │832 + try std.testing.expectError(error.ServerReportedError, client.push(io, allocator, "denied", "master:patch", false, .{ .wire = .{ .ssh = .{ .command = reject_ssh_cmd } } }));│ │833 } │ │834 │ │835 fn testPushCreatesMissingRepo( │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1110 }; │ │1111 │ │1112 if (shell_out_to_git) { │ │1103 - const priv_key_path = try std.fs.path.join(allocator, &.{ temp_path, "key" }); │ │1104 - defer allocator.free(priv_key_path); │ │1105 - const ssh_config_arg = try std.fmt.allocPrint(allocator, "core.sshCommand=ssh -p {} -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityFile={s}", .{ port + 1, priv_key_path });│ │1113 + const ssh_cmd = (try sshCommand(true, allocator, temp_path, port)).?; │ │1114 + defer allocator.free(ssh_cmd); │ │1115 + const ssh_config_arg = try std.fmt.allocPrint(allocator, "core.sshCommand={s}", .{ssh_cmd}); │ │1116 defer allocator.free(ssh_config_arg); │ │1117 │ │1118 { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1119 var process = try std.process.spawn(io, .{ │ │1120 .argv = if (is_ssh) │ │1111 - &.{ "git", "-c", ssh_config_arg, "clone", "--depth", "1", remote_url, "client" } │ │1121 + &.{ "git", "-c", ssh_config_arg, "clone", "--quiet", "--depth", "1", remote_url, "client" }│ │1122 else │ │1113 - &.{ "git", "clone", "--depth", "1", remote_url, "client" }, │ │1123 + &.{ "git", "clone", "--quiet", "--depth", "1", remote_url, "client" }, │ │1124 .cwd = .{ .path = temp_path }, │ │1125 .stdin = .ignore, │ │1126 .stdout = .ignore, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │1117 - .stderr = .ignore, │ │1127 + .stderr = .inherit, │ │1128 }); │ │1129 + defer process.kill(io); │ │1130 const term = try process.wait(io); │ │1120 - if (term != .exited or term.exited != 0) { │ │1121 - return error.GitCommandFailed; │ │1122 - } │ │1131 + if (term != .exited or term.exited != 0) return error.GitCommandFailed;│ │1132 } │ │1133 │ │1134 // make sure shallow clone was successful │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │1144 { │ │1145 var process = try std.process.spawn(io, .{ │ │1146 .argv = if (is_ssh) │ │1138 - &.{ "git", "-c", ssh_config_arg, "pull", "--unshallow" } │ │1147 + &.{ "git", "-c", ssh_config_arg, "pull", "--quiet", "--unshallow" }│ │1148 else │ │1140 - &.{ "git", "pull", "--unshallow" }, │ │1149 + &.{ "git", "pull", "--quiet", "--unshallow" }, │ │1150 .cwd = .{ .path = client_path }, │ │1151 .stdin = .ignore, │ │1152 .stdout = .ignore, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │1144 - .stderr = .ignore, │ │1153 + .stderr = .inherit, │ │1154 }); │ │1155 + defer process.kill(io); │ │1156 const term = try process.wait(io); │ │1147 - if (term != .exited or term.exited != 0) { │ │1148 - return error.GitCommandFailed; │ │1149 - } │ │1157 + if (term != .exited or term.exited != 0) return error.GitCommandFailed;│ │1158 } │ │1159 │ │1160 // make sure unshallow pull was successful │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1169 { │ │1170 var process = try std.process.spawn(io, .{ │ │1171 .argv = if (is_ssh) │ │1164 - &.{ "git", "-c", ssh_config_arg, "clone", "--shallow-since=2000-01-01", remote_url, "client" } │ │1172 + &.{ "git", "-c", ssh_config_arg, "clone", "--quiet", "--shallow-since=2000-01-01", remote_url, "client" }│ │1173 else │ │1166 - &.{ "git", "clone", "--shallow-since=2000-01-01", remote_url, "client" }, │ │1174 + &.{ "git", "clone", "--quiet", "--shallow-since=2000-01-01", remote_url, "client" }, │ │1175 .cwd = .{ .path = temp_path }, │ │1176 .stdin = .ignore, │ │1177 .stdout = .ignore, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │1170 - .stderr = .ignore, │ │1178 + .stderr = .inherit, │ │1179 }); │ │1180 + defer process.kill(io); │ │1181 const term = try process.wait(io); │ │1173 - if (term != .exited or term.exited != 0) { │ │1174 - return error.GitCommandFailed; │ │1175 - } │ │1182 + if (term != .exited or term.exited != 0) return error.GitCommandFailed;│ │1183 } │ │1184 │ │1185 // make sure shallow clone was successful │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯