gpg-interface: drop pointless config_error_nonbool() checks
[alt-git.git] / builtin / bugreport.c
blob3106e56a130c58994fced2afd8dbd337134861ad
1 #include "builtin.h"
2 #include "abspath.h"
3 #include "editor.h"
4 #include "gettext.h"
5 #include "parse-options.h"
6 #include "strbuf.h"
7 #include "help.h"
8 #include "compat/compiler.h"
9 #include "hook.h"
10 #include "hook-list.h"
11 #include "diagnose.h"
12 #include "object-file.h"
13 #include "setup.h"
15 static void get_system_info(struct strbuf *sys_info)
17 struct utsname uname_info;
18 char *shell = NULL;
20 /* get git version from native cmd */
21 strbuf_addstr(sys_info, _("git version:\n"));
22 get_version_info(sys_info, 1);
24 /* system call for other version info */
25 strbuf_addstr(sys_info, "uname: ");
26 if (uname(&uname_info))
27 strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"),
28 strerror(errno),
29 errno);
30 else
31 strbuf_addf(sys_info, "%s %s %s %s\n",
32 uname_info.sysname,
33 uname_info.release,
34 uname_info.version,
35 uname_info.machine);
37 strbuf_addstr(sys_info, _("compiler info: "));
38 get_compiler_info(sys_info);
40 strbuf_addstr(sys_info, _("libc info: "));
41 get_libc_info(sys_info);
43 shell = getenv("SHELL");
44 strbuf_addf(sys_info, "$SHELL (typically, interactive shell): %s\n",
45 shell ? shell : "<unset>");
48 static void get_populated_hooks(struct strbuf *hook_info, int nongit)
50 const char **p;
52 if (nongit) {
53 strbuf_addstr(hook_info,
54 _("not run from a git repository - no hooks to show\n"));
55 return;
58 for (p = hook_name_list; *p; p++) {
59 const char *hook = *p;
61 if (hook_exists(hook))
62 strbuf_addf(hook_info, "%s\n", hook);
66 static const char * const bugreport_usage[] = {
67 N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
68 " [--diagnose[=<mode>]]"),
69 NULL
72 static int get_bug_template(struct strbuf *template)
74 const char template_text[] = N_(
75 "Thank you for filling out a Git bug report!\n"
76 "Please answer the following questions to help us understand your issue.\n"
77 "\n"
78 "What did you do before the bug happened? (Steps to reproduce your issue)\n"
79 "\n"
80 "What did you expect to happen? (Expected behavior)\n"
81 "\n"
82 "What happened instead? (Actual behavior)\n"
83 "\n"
84 "What's different between what you expected and what actually happened?\n"
85 "\n"
86 "Anything else you want to add:\n"
87 "\n"
88 "Please review the rest of the bug report below.\n"
89 "You can delete any lines you don't wish to share.\n");
91 strbuf_addstr(template, _(template_text));
92 return 0;
95 static void get_header(struct strbuf *buf, const char *title)
97 strbuf_addf(buf, "\n\n[%s]\n", title);
100 int cmd_bugreport(int argc, const char **argv, const char *prefix)
102 struct strbuf buffer = STRBUF_INIT;
103 struct strbuf report_path = STRBUF_INIT;
104 int report = -1;
105 time_t now = time(NULL);
106 struct tm tm;
107 enum diagnose_mode diagnose = DIAGNOSE_NONE;
108 char *option_output = NULL;
109 char *option_suffix = "%Y-%m-%d-%H%M";
110 const char *user_relative_path = NULL;
111 char *prefixed_filename;
112 size_t output_path_len;
113 int ret;
115 const struct option bugreport_options[] = {
116 OPT_CALLBACK_F(0, "diagnose", &diagnose, N_("mode"),
117 N_("create an additional zip archive of detailed diagnostics (default 'stats')"),
118 PARSE_OPT_OPTARG, option_parse_diagnose),
119 OPT_STRING('o', "output-directory", &option_output, N_("path"),
120 N_("specify a destination for the bugreport file(s)")),
121 OPT_STRING('s', "suffix", &option_suffix, N_("format"),
122 N_("specify a strftime format suffix for the filename(s)")),
123 OPT_END()
126 argc = parse_options(argc, argv, prefix, bugreport_options,
127 bugreport_usage, 0);
129 if (argc) {
130 error(_("unknown argument `%s'"), argv[0]);
131 usage(bugreport_usage[0]);
134 /* Prepare the path to put the result */
135 prefixed_filename = prefix_filename(prefix,
136 option_output ? option_output : "");
137 strbuf_addstr(&report_path, prefixed_filename);
138 strbuf_complete(&report_path, '/');
139 output_path_len = report_path.len;
141 strbuf_addstr(&report_path, "git-bugreport-");
142 strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
143 strbuf_addstr(&report_path, ".txt");
145 switch (safe_create_leading_directories(report_path.buf)) {
146 case SCLD_OK:
147 case SCLD_EXISTS:
148 break;
149 default:
150 die(_("could not create leading directories for '%s'"),
151 report_path.buf);
154 /* Prepare diagnostics, if requested */
155 if (diagnose != DIAGNOSE_NONE) {
156 struct strbuf zip_path = STRBUF_INIT;
157 strbuf_add(&zip_path, report_path.buf, output_path_len);
158 strbuf_addstr(&zip_path, "git-diagnostics-");
159 strbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0);
160 strbuf_addstr(&zip_path, ".zip");
162 if (create_diagnostics_archive(&zip_path, diagnose))
163 die_errno(_("unable to create diagnostics archive %s"), zip_path.buf);
165 strbuf_release(&zip_path);
168 /* Prepare the report contents */
169 get_bug_template(&buffer);
171 get_header(&buffer, _("System Info"));
172 get_system_info(&buffer);
174 get_header(&buffer, _("Enabled Hooks"));
175 get_populated_hooks(&buffer, !startup_info->have_repository);
177 /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
178 report = xopen(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
180 if (write_in_full(report, buffer.buf, buffer.len) < 0)
181 die_errno(_("unable to write to %s"), report_path.buf);
183 close(report);
186 * We want to print the path relative to the user, but we still need the
187 * path relative to us to give to the editor.
189 if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
190 user_relative_path = report_path.buf;
191 fprintf(stderr, _("Created new report at '%s'.\n"),
192 user_relative_path);
194 free(prefixed_filename);
195 strbuf_release(&buffer);
197 ret = !!launch_editor(report_path.buf, NULL, NULL);
198 strbuf_release(&report_path);
199 return ret;