diff --git a/Documentation/config/credential.txt b/Documentation/config/credential.txt index 512f31876e17ed..0ed570ff979629 100644 --- a/Documentation/config/credential.txt +++ b/Documentation/config/credential.txt @@ -9,6 +9,14 @@ credential.helper:: Note that multiple helpers may be defined. See linkgit:gitcredentials[7] for details and examples. +credential.interactive:: + By default, Git and any configured credential helpers will ask for + user input when new credentials are required. Many of these helpers + will succeed based on stored credentials if those credentials are + still valid. To avoid the possibility of user interactivity from + Git, set `credential.interactive=false`. Some credential helpers + respect this option as well. + credential.useHttpPath:: When acquiring credentials, consider the "path" component of an http or https URL to be important. Defaults to false. See diff --git a/builtin/gc.c b/builtin/gc.c index b35af4518400d3..ac30c9d01a3efa 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1309,8 +1309,6 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts) char *lock_path = xstrfmt("%s/maintenance", r->objects->odb->path); if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) { - struct stat st; - struct strbuf lock_dot_lock = STRBUF_INIT; /* * Another maintenance command is running. * @@ -1321,25 +1319,6 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts) if (!opts->auto_flag && !opts->quiet) warning(_("lock file '%s' exists, skipping maintenance"), lock_path); - - /* - * Check timestamp on .lock file to see if we should - * delete it to recover from a fail state. - */ - strbuf_addstr(&lock_dot_lock, lock_path); - strbuf_addstr(&lock_dot_lock, ".lock"); - if (lstat(lock_dot_lock.buf, &st)) - warning_errno(_("unable to stat '%s'"), lock_dot_lock.buf); - else { - if (st.st_mtime < time(NULL) - (6 * 60 * 60)) { - if (unlink(lock_dot_lock.buf)) - warning_errno(_("unable to delete stale lock file")); - else - warning(_("deleted stale lock file")); - } - } - - strbuf_release(&lock_dot_lock); free(lock_path); return 0; } @@ -1678,6 +1657,42 @@ static const char *get_frequency(enum schedule_priority schedule) } } +static const char *extraconfig[] = { + "credential.interactive=false", + "core.askPass=true", /* 'true' returns success, but no output. */ + NULL +}; + +static const char *get_extra_config_parameters(void) { + static const char *result = NULL; + struct strbuf builder = STRBUF_INIT; + + if (result) + return result; + + for (const char **s = extraconfig; s && *s; s++) + strbuf_addf(&builder, "-c %s ", *s); + + result = strbuf_detach(&builder, NULL); + return result; +} + +static const char *get_extra_launchctl_strings(void) { + static const char *result = NULL; + struct strbuf builder = STRBUF_INIT; + + if (result) + return result; + + for (const char **s = extraconfig; s && *s; s++) { + strbuf_addstr(&builder, "-c\n"); + strbuf_addf(&builder, "%s\n", *s); + } + + result = strbuf_detach(&builder, NULL); + return result; +} + /* * get_schedule_cmd` reads the GIT_TEST_MAINT_SCHEDULER environment variable * to mock the schedulers that `git maintenance start` rely on. @@ -1884,6 +1899,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit "\n" "%s/git\n" "--exec-path=%s\n" + "%s" /* For extra config parameters. */ "for-each-repo\n" "--config=maintenance.repo\n" "maintenance\n" @@ -1892,7 +1908,8 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit "\n" "StartCalendarInterval\n" "\n"; - strbuf_addf(&plist, preamble, name, exec_path, exec_path, frequency); + strbuf_addf(&plist, preamble, name, exec_path, exec_path, + get_extra_launchctl_strings(), frequency); switch (schedule) { case SCHEDULE_HOURLY: @@ -2127,11 +2144,12 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "\n" "\n" "\"%s\\headless-git.exe\"\n" - "--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s\n" + "--exec-path=\"%s\" %s for-each-repo --config=maintenance.repo maintenance run --schedule=%s\n" "\n" "\n" "\n"; - fprintf(tfile->fp, xml, exec_path, exec_path, frequency); + fprintf(tfile->fp, xml, exec_path, exec_path, + get_extra_config_parameters(), frequency); strvec_split(&child.args, cmd); strvec_pushl(&child.args, "/create", "/tn", name, "/f", "/xml", get_tempfile_path(tfile), NULL); @@ -2272,8 +2290,8 @@ static int crontab_update_schedule(int run_maintenance, int fd) "# replaced in the future by a Git command.\n\n"); strbuf_addf(&line_format, - "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n", - exec_path, exec_path); + "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" %s for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n", + exec_path, exec_path, get_extra_config_parameters()); fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly"); fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily"); fprintf(cron_in, line_format.buf, minute, "0", "0", "weekly"); @@ -2473,7 +2491,7 @@ static int systemd_timer_write_service_template(const char *exec_path) "\n" "[Service]\n" "Type=oneshot\n" - "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%i\n" + "ExecStart=\"%s/git\" --exec-path=\"%s\" %s for-each-repo --config=maintenance.repo maintenance run --schedule=%%i\n" "LockPersonality=yes\n" "MemoryDenyWriteExecute=yes\n" "NoNewPrivileges=yes\n" @@ -2483,7 +2501,7 @@ static int systemd_timer_write_service_template(const char *exec_path) "RestrictSUIDSGID=yes\n" "SystemCallArchitectures=native\n" "SystemCallFilter=@system-service\n"; - if (fprintf(file, unit, exec_path, exec_path) < 0) { + if (fprintf(file, unit, exec_path, exec_path, get_extra_config_parameters()) < 0) { error(_("failed to write to '%s'"), filename); fclose(file); goto error; diff --git a/credential.c b/credential.c index 369fad17ad67dc..d9c6277fdda612 100644 --- a/credential.c +++ b/credential.c @@ -11,6 +11,8 @@ #include "strbuf.h" #include "urlmatch.h" #include "git-compat-util.h" +#include "trace2.h" +#include "repository.h" void credential_init(struct credential *c) { @@ -196,14 +198,36 @@ static char *credential_ask_one(const char *what, struct credential *c, return xstrdup(r); } -static void credential_getpass(struct credential *c) +static int credential_getpass(struct credential *c) { + int interactive; + char *value; + if (!git_config_get_maybe_bool("credential.interactive", &interactive) && + !interactive) { + trace2_data_intmax("credential", the_repository, + "interactive/skipped", 1); + return -1; + } + if (!git_config_get_string("credential.interactive", &value)) { + int same = !strcmp(value, "never"); + free(value); + if (same) { + trace2_data_intmax("credential", the_repository, + "interactive/skipped", 1); + return -1; + } + } + + trace2_region_enter("credential", "interactive", the_repository); if (!c->username) c->username = credential_ask_one("Username", c, PROMPT_ASKPASS|PROMPT_ECHO); if (!c->password) c->password = credential_ask_one("Password", c, PROMPT_ASKPASS); + trace2_region_leave("credential", "interactive", the_repository); + + return 0; } int credential_read(struct credential *c, FILE *fp) @@ -381,8 +405,8 @@ void credential_fill(struct credential *c) c->helpers.items[i].string); } - credential_getpass(c); - if (!c->username && !c->password) + if (credential_getpass(c) || + (!c->username && !c->password)) die("unable to get password from user"); } diff --git a/scalar.c b/scalar.c index 142672976c6584..120daea1f6e116 100644 --- a/scalar.c +++ b/scalar.c @@ -1098,6 +1098,9 @@ static int cmd_reconfigure(int argc, const char **argv) if (set_recommended_config(1) < 0) failed = -1; + if (toggle_maintenance(1) < 0) + failed = -1; + loop_end: if (failed) { res = failed; @@ -1400,7 +1403,7 @@ int cmd_main(int argc, const char **argv) if (is_unattended()) { setenv("GIT_ASKPASS", "", 0); setenv("GIT_TERMINAL_PROMPT", "false", 0); - git_config_push_parameter("credential.interactive=never"); + git_config_push_parameter("credential.interactive=false"); } while (argc > 1 && *argv[1] == '-') { diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index 21b7767cbd313b..6e70bca95a4c64 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -186,6 +186,28 @@ test_expect_success 'clone from password-protected repository' ' test_cmp expect actual ' +test_expect_success 'credential.interactive=false skips askpass' ' + set_askpass bogus nonsense && + ( + GIT_TRACE2_EVENT="$(pwd)/interactive-true" && + export GIT_TRACE2_EVENT && + test_must_fail git clone --bare "$HTTPD_URL/auth/smart/repo.git" interactive-true-dir && + test_region credential interactive interactive-true && + + GIT_TRACE2_EVENT="$(pwd)/interactive-false" && + export GIT_TRACE2_EVENT && + test_must_fail git -c credential.interactive=false \ + clone --bare "$HTTPD_URL/auth/smart/repo.git" interactive-false-dir && + test_region ! credential interactive interactive-false && + + GIT_TRACE2_EVENT="$(pwd)/interactive-never" && + export GIT_TRACE2_EVENT && + test_must_fail git -c credential.interactive=never \ + clone --bare "$HTTPD_URL/auth/smart/repo.git" interactive-never-dir && + test_region ! credential interactive interactive-never + ) +' + test_expect_success 'clone from auth-only-for-push repository' ' echo two >expect && set_askpass wrong && diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 6c483d6573ab85..083a20a9f7ae84 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -54,23 +54,6 @@ test_expect_success 'run [--auto|--quiet]' ' test_subcommand git gc --no-quiet err && - grep "lock file .* exists, skipping maintenance" err && - - test-tool chmtime =-22000 .git/objects/maintenance.lock && - git maintenance run --schedule=hourly --no-quiet 2>err && - grep "deleted stale lock file" err && - test_path_is_missing .git/objects/maintenance.lock && - - git maintenance run --schedule=hourly 2>err && - test_must_be_empty err -' - test_expect_success 'maintenance.auto config option' ' GIT_TRACE2_EVENT="$(pwd)/default" git commit --quiet --allow-empty -m 1 && test_subcommand git maintenance run --auto --quiet expect && test_cmp expect args && diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh index 43c4208cdc31b1..fbbf641881ac3f 100755 --- a/t/t9210-scalar.sh +++ b/t/t9210-scalar.sh @@ -176,8 +176,11 @@ test_expect_success 'scalar reconfigure' ' scalar reconfigure one && test true = "$(git -C one/src config core.preloadIndex)" && git -C one/src config core.preloadIndex false && - scalar reconfigure -a && - test true = "$(git -C one/src config core.preloadIndex)" + rm one/src/cron.txt && + GIT_TRACE2_EVENT="$(pwd)/reconfigure" scalar reconfigure -a && + test_path_is_file one/src/cron.txt && + test true = "$(git -C one/src config core.preloadIndex)" && + test_subcommand git maintenance start