winprefs v0.3.2
A registry exporter for programmers.
Loading...
Searching...
No Matches
git.c
1#include "git.h"
2#include "constants.h"
3#include "git_branch.h"
4
5// This function does NOT escape double quotes. Inner double quotes must be escaped by the caller.
6static inline bool run_process_no_window(int n_args, wchar_t *arg0, ...) {
7 PROCESS_INFORMATION pi = {0};
8 STARTUPINFO si = {.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES,
9 .wShowWindow = SW_HIDE};
10 wchar_t cmd[32767];
11 wmemset(cmd, L'\0', 32767);
12 va_list args;
13 va_start(args, arg0);
14 wchar_t *cur;
15 size_t i = 0;
16 size_t req_size = (size_t)_snwprintf(nullptr, 0, L"\"%ls\" ", arg0);
17 _snwprintf(cmd, req_size + 1, L"\"%ls\" ", arg0);
18 i += req_size;
19 int index = 0;
20 for (index = 0; index < (n_args - 1); index++) {
21 cur = va_arg(args, wchar_t *);
22 if (!cur) { // LCOV_EXCL_START
23 break;
24 } // LCOV_EXCL_STOP
25 req_size = (size_t)_snwprintf(nullptr, 0, L"\"%ls\" ", cur);
26 _snwprintf(cmd + i, req_size + 1, L"\"%ls\" ", cur);
27 i += req_size;
28 }
29 va_end(args);
30 if (i >= 32767) { // LCOV_EXCL_START
31 return false;
32 } // LCOV_EXCL_STOP
33 cmd[i - 1] = L'\0';
34 debug_print(L"Executing: '%ls'\n", cmd);
35 bool ret = CreateProcess(
36 nullptr, cmd, nullptr, nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi);
37 if (ret) {
38 WaitForSingleObject(pi.hProcess, INFINITE);
39 DWORD exit_code;
40 bool result = GetExitCodeProcess(pi.hProcess, &exit_code);
41 CloseHandle(pi.hProcess);
42 CloseHandle(pi.hThread);
43 if (!result) {
44 debug_print(L"Failed to get exit code.\n");
45 return false;
46 }
47 debug_print(L"Exit code: %lu\n", exit_code);
48 return exit_code == 0;
49 }
50 return ret;
51}
52
53static inline bool has_git() {
54 return run_process_no_window(2, L"git.exe", L"--version");
55}
56
57static inline bool dir_exists(wchar_t *path) {
58 DWORD attrib = GetFileAttributes(path);
59 return attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY);
60}
61
62bool git_commit(const wchar_t *output_dir, const wchar_t *deploy_key) {
63 bool ret = true;
64 wchar_t *cwd, *date_buf, *git_dir, *git_dir_arg, *message_buf, *ssh_command, *time_buf,
65 *work_tree_arg;
66 cwd = date_buf = git_dir = git_dir_arg = message_buf = ssh_command = time_buf = work_tree_arg =
67 nullptr;
68 if (!has_git()) {
69 debug_print(L"Wanted to commit but git.exe is not in PATH or failed to run.\n");
70 goto cleanup;
71 }
72 debug_print(L"Committing changes.\n");
73 size_t work_tree_arg_len = wcslen(output_dir) + wcslen(L"--work-tree=") + 1;
74 work_tree_arg = calloc(work_tree_arg_len, WL);
75 if (!work_tree_arg) { // LCOV_EXCL_START
76 goto fail;
77 } // LCOV_EXCL_STOP
78 wmemset(work_tree_arg, L'\0', work_tree_arg_len);
79 _snwprintf(work_tree_arg, work_tree_arg_len, L"--work-tree=%ls", output_dir);
80 size_t git_dir_len = wcslen(output_dir) + wcslen(L"\\.git") + 1;
81 git_dir = calloc(git_dir_len, WL);
82 if (!git_dir) { // LCOV_EXCL_START
83 goto fail;
84 } // LCOV_EXCL_STOP
85 _snwprintf(git_dir, git_dir_len, L"%ls\\.git", output_dir);
86 git_dir[git_dir_len - 1] = L'\0';
87 if (!dir_exists(git_dir)) {
88 cwd = calloc(MAX_PATH, WL);
89 if (!cwd) { // LCOV_EXCL_START
90 goto fail;
91 } // LCOV_EXCL_STOP
92 wmemset(cwd, L'\0', MAX_PATH);
93 if (!_wgetcwd(cwd, MAX_PATH) || _wchdir(output_dir) != 0 ||
94 !run_process_no_window(3, L"git.exe", L"init", L"--quiet") || _wchdir(cwd) != 0) {
95 goto fail;
96 }
97 }
98 size_t git_dir_arg_len = git_dir_len + wcslen(L"--git-dir=") + 1;
99 git_dir_arg = calloc(git_dir_arg_len, WL);
100 if (!git_dir_arg) { // LCOV_EXCL_START
101 goto fail;
102 } // LCOV_EXCL_STOP
103 wmemset(git_dir_arg, L'\0', git_dir_arg_len);
104 _snwprintf(git_dir_arg, git_dir_arg_len, L"--git-dir=%ls", git_dir);
105 git_dir_arg[git_dir_arg_len - 1] = L'\0';
106 if (!run_process_no_window(5, L"git.exe", git_dir_arg, work_tree_arg, L"add", L".")) {
107 goto fail;
108 }
109 size_t time_needed_size =
110 (size_t)GetTimeFormat(LOCALE_USER_DEFAULT, 0, nullptr, nullptr, nullptr, 0);
111 if (!time_needed_size) {
112 goto fail;
113 }
114 time_buf = calloc(time_needed_size, WL);
115 if (!time_buf) { // LCOV_EXCL_START
116 goto fail;
117 } // LCOV_EXCL_STOP
118 if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, nullptr, nullptr, time_buf, (int)time_needed_size)) {
119 goto fail;
120 }
121 size_t date_needed_size =
122 (size_t)GetDateFormat(LOCALE_USER_DEFAULT, 0, nullptr, nullptr, nullptr, 0);
123 if (!date_needed_size) {
124 goto fail;
125 }
126 date_buf = calloc(date_needed_size, WL);
127 if (!date_buf) { // LCOV_EXCL_START
128 goto fail;
129 } // LCOV_EXCL_STOP
130 if (!GetDateFormat(LOCALE_USER_DEFAULT, 0, nullptr, nullptr, date_buf, (int)date_needed_size)) {
131 goto fail;
132 }
133 size_t needed_size =
134 wcslen(AUTOMATIC_COMMIT_MESSAGE_PREFIX) + 3 + time_needed_size + date_needed_size;
135 message_buf = calloc(needed_size, WL);
136 if (!message_buf) { // LCOV_EXCL_START
137 goto fail;
138 } // LCOV_EXCL_STOP
139 wmemset(message_buf, L'\0', needed_size);
140 _snwprintf(message_buf,
141 needed_size,
142 L"%ls%ls %ls",
143 AUTOMATIC_COMMIT_MESSAGE_PREFIX,
144 date_buf,
145 time_buf);
146 if (!run_process_no_window(10,
147 L"git.exe",
148 git_dir_arg,
149 work_tree_arg,
150 L"commit",
151 L"--no-gpg-sign",
152 L"--quiet",
153 L"--no-verify",
154 L"--author=winprefs <winprefs@tat.sh>",
155 L"-m",
156 message_buf)) {
157 goto fail;
158 }
159 if (deploy_key) {
160 wchar_t full_deploy_key_path[MAX_PATH];
161 if (!_wfullpath(full_deploy_key_path, deploy_key, MAX_PATH)) {
162 goto fail;
163 }
164 debug_print(L"Deploy key: %ls\n", full_deploy_key_path);
165 size_t ssh_command_len = 68 + wcslen(full_deploy_key_path) + 3;
166 ssh_command = calloc(ssh_command_len, WL);
167 if (!ssh_command) { // LCOV_EXCL_START
168 goto fail;
169 } // LCOV_EXCL_STOP
170 wmemset(ssh_command, L'\0', ssh_command_len);
171 _snwprintf(ssh_command,
172 ssh_command_len,
173 L"ssh -i %ls -F nul -o UserKnownHostsFile=nul -o StrictHostKeyChecking=no",
174 full_deploy_key_path);
175 if (!run_process_no_window(6,
176 L"git.exe",
177 git_dir_arg,
178 work_tree_arg,
179 L"config",
180 L"core.sshCommand",
181 ssh_command)) {
182 goto fail;
183 }
184 wchar_t *branch_arg =
185 get_git_branch(git_dir_arg, git_dir_arg_len, work_tree_arg, work_tree_arg_len);
186 if (!run_process_no_window(10,
187 L"git.exe",
188 git_dir_arg,
189 work_tree_arg,
190 L"push",
191 L"-u",
192 L"--porcelain",
193 L"--no-signed",
194 L"origin",
195 L"origin",
196 branch_arg)) {
197 goto fail;
198 }
199 }
200 goto cleanup;
201fail:
202 ret = false;
203cleanup:
204 free(cwd);
205 free(date_buf);
206 free(git_dir);
207 free(git_dir_arg);
208 free(message_buf);
209 free(ssh_command);
210 free(time_buf);
211 free(work_tree_arg);
212 return ret;
213}