mirror of
https://github.com/git/git.git
synced 2024-11-23 01:46:13 +08:00
Merge branch 'ds/background-maintenance-with-credential'
Background tasks "git maintenance" runs may need to use credential information when going over the network, but a credential helper may work only in an interactive environment, and end up blocking a scheduled task waiting for UI. Credential helpers can now behave differently when they are not running interactively. * ds/background-maintenance-with-credential: scalar: configure maintenance during 'reconfigure' maintenance: add custom config to background jobs credential: add new interactive config option
This commit is contained in:
commit
4251403327
@ -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
|
||||
|
53
builtin/gc.c
53
builtin/gc.c
@ -1768,6 +1768,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, "<string>-c</string>\n");
|
||||
strbuf_addf(&builder, "<string>%s</string>\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.
|
||||
@ -1974,6 +2010,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
|
||||
"<array>\n"
|
||||
"<string>%s/git</string>\n"
|
||||
"<string>--exec-path=%s</string>\n"
|
||||
"%s" /* For extra config parameters. */
|
||||
"<string>for-each-repo</string>\n"
|
||||
"<string>--keep-going</string>\n"
|
||||
"<string>--config=maintenance.repo</string>\n"
|
||||
@ -1983,7 +2020,8 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
|
||||
"</array>\n"
|
||||
"<key>StartCalendarInterval</key>\n"
|
||||
"<array>\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:
|
||||
@ -2218,11 +2256,12 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
|
||||
"<Actions Context=\"Author\">\n"
|
||||
"<Exec>\n"
|
||||
"<Command>\"%s\\headless-git.exe\"</Command>\n"
|
||||
"<Arguments>--exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
|
||||
"<Arguments>--exec-path=\"%s\" %s for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
|
||||
"</Exec>\n"
|
||||
"</Actions>\n"
|
||||
"</Task>\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);
|
||||
@ -2363,8 +2402,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 --keep-going --config=maintenance.repo maintenance run --schedule=%%s\n",
|
||||
exec_path, exec_path);
|
||||
"%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" %s for-each-repo --keep-going --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");
|
||||
@ -2564,7 +2603,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 --keep-going --config=maintenance.repo maintenance run --schedule=%%i\n"
|
||||
"ExecStart=\"%s/git\" --exec-path=\"%s\" %s for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%i\n"
|
||||
"LockPersonality=yes\n"
|
||||
"MemoryDenyWriteExecute=yes\n"
|
||||
"NoNewPrivileges=yes\n"
|
||||
@ -2574,7 +2613,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;
|
||||
|
30
credential.c
30
credential.c
@ -13,6 +13,8 @@
|
||||
#include "strbuf.h"
|
||||
#include "urlmatch.h"
|
||||
#include "git-compat-util.h"
|
||||
#include "trace2.h"
|
||||
#include "repository.h"
|
||||
|
||||
void credential_init(struct credential *c)
|
||||
{
|
||||
@ -251,14 +253,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_has_capability(const struct credential_capability *capa,
|
||||
@ -501,8 +525,8 @@ void credential_fill(struct credential *c, int all_capabilities)
|
||||
c->helpers.items[i].string);
|
||||
}
|
||||
|
||||
credential_getpass(c);
|
||||
if (!c->username && !c->password && !c->credential)
|
||||
if (credential_getpass(c) ||
|
||||
(!c->username && !c->password && !c->credential))
|
||||
die("unable to get password from user");
|
||||
}
|
||||
|
||||
|
3
scalar.c
3
scalar.c
@ -733,6 +733,9 @@ static int cmd_reconfigure(int argc, const char **argv)
|
||||
|
||||
the_repository = old_repo;
|
||||
|
||||
if (toggle_maintenance(1) >= 0)
|
||||
succeeded = 1;
|
||||
|
||||
loop_end:
|
||||
if (!succeeded) {
|
||||
res = -1;
|
||||
|
@ -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 &&
|
||||
|
@ -825,6 +825,9 @@ test_expect_success 'start and stop Linux/systemd maintenance' '
|
||||
test_systemd_analyze_verify "systemd/user/git-maintenance@daily.service" &&
|
||||
test_systemd_analyze_verify "systemd/user/git-maintenance@weekly.service" &&
|
||||
|
||||
grep "core.askPass=true" "systemd/user/git-maintenance@.service" &&
|
||||
grep "credential.interactive=false" "systemd/user/git-maintenance@.service" &&
|
||||
|
||||
printf -- "--user enable --now git-maintenance@%s.timer\n" hourly daily weekly >expect &&
|
||||
test_cmp expect args &&
|
||||
|
||||
|
@ -194,8 +194,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 <reconfigure
|
||||
'
|
||||
|
||||
test_expect_success 'scalar reconfigure --all with includeIf.onbranch' '
|
||||
|
Loading…
Reference in New Issue
Block a user