Skip to content

Commit 31345d5

Browse files
derrickstoleesunshineco
authored andcommitted
maintenance: extract platform-specific scheduling
The existing schedule mechanism using 'cron' is supported by POSIX platforms, but not Windows. It also works slightly differently on macOS to significant detriment of the user experience. To allow for new implementations on these platforms, extract a method that performs the platform-specific scheduling mechanism. This will be swapped at compile time with new implementations on specialized platforms. As we add this generality, rename GIT_TEST_CRONTAB to GIT_TEST_MAINT_SCHEDULER. Further, this variable is now parsed as "<scheduler>:<command>" so we can test platform-specific scheduling logic even when not on the correct platform. By specifying the <scheduler> in this string, we will be able to test all three sets of Git logic from a Linux machine. Co-authored-by: Eric Sunshine <[email protected]> Signed-off-by: Eric Sunshine <[email protected]> Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0016b61 commit 31345d5

File tree

3 files changed

+51
-34
lines changed

3 files changed

+51
-34
lines changed

builtin/gc.c

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,35 +1494,23 @@ static int maintenance_unregister(void)
14941494
#define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE"
14951495
#define END_LINE "# END GIT MAINTENANCE SCHEDULE"
14961496

1497-
static int update_background_schedule(int run_maintenance)
1497+
static int crontab_update_schedule(int run_maintenance, int fd, const char *cmd)
14981498
{
14991499
int result = 0;
15001500
int in_old_region = 0;
15011501
struct child_process crontab_list = CHILD_PROCESS_INIT;
15021502
struct child_process crontab_edit = CHILD_PROCESS_INIT;
15031503
FILE *cron_list, *cron_in;
1504-
const char *crontab_name;
15051504
struct strbuf line = STRBUF_INIT;
1506-
struct lock_file lk;
1507-
char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path);
15081505

1509-
if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0)
1510-
return error(_("another process is scheduling background maintenance"));
1511-
1512-
crontab_name = getenv("GIT_TEST_CRONTAB");
1513-
if (!crontab_name)
1514-
crontab_name = "crontab";
1515-
1516-
strvec_split(&crontab_list.args, crontab_name);
1506+
strvec_split(&crontab_list.args, cmd);
15171507
strvec_push(&crontab_list.args, "-l");
15181508
crontab_list.in = -1;
1519-
crontab_list.out = dup(lk.tempfile->fd);
1509+
crontab_list.out = dup(fd);
15201510
crontab_list.git_cmd = 0;
15211511

1522-
if (start_command(&crontab_list)) {
1523-
result = error(_("failed to run 'crontab -l'; your system might not support 'cron'"));
1524-
goto cleanup;
1525-
}
1512+
if (start_command(&crontab_list))
1513+
return error(_("failed to run 'crontab -l'; your system might not support 'cron'"));
15261514

15271515
/* Ignore exit code, as an empty crontab will return error. */
15281516
finish_command(&crontab_list);
@@ -1531,17 +1519,15 @@ static int update_background_schedule(int run_maintenance)
15311519
* Read from the .lock file, filtering out the old
15321520
* schedule while appending the new schedule.
15331521
*/
1534-
cron_list = fdopen(lk.tempfile->fd, "r");
1522+
cron_list = fdopen(fd, "r");
15351523
rewind(cron_list);
15361524

1537-
strvec_split(&crontab_edit.args, crontab_name);
1525+
strvec_split(&crontab_edit.args, cmd);
15381526
crontab_edit.in = -1;
15391527
crontab_edit.git_cmd = 0;
15401528

1541-
if (start_command(&crontab_edit)) {
1542-
result = error(_("failed to run 'crontab'; your system might not support 'cron'"));
1543-
goto cleanup;
1544-
}
1529+
if (start_command(&crontab_edit))
1530+
return error(_("failed to run 'crontab'; your system might not support 'cron'"));
15451531

15461532
cron_in = fdopen(crontab_edit.in, "w");
15471533
if (!cron_in) {
@@ -1586,14 +1572,44 @@ static int update_background_schedule(int run_maintenance)
15861572
close(crontab_edit.in);
15871573

15881574
done_editing:
1589-
if (finish_command(&crontab_edit)) {
1575+
if (finish_command(&crontab_edit))
15901576
result = error(_("'crontab' died"));
1591-
goto cleanup;
1577+
else
1578+
fclose(cron_list);
1579+
return result;
1580+
}
1581+
1582+
static const char platform_scheduler[] = "crontab";
1583+
1584+
static int update_background_schedule(int enable)
1585+
{
1586+
int result;
1587+
const char *scheduler = platform_scheduler;
1588+
const char *cmd = scheduler;
1589+
char *testing;
1590+
struct lock_file lk;
1591+
char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path);
1592+
1593+
testing = xstrdup_or_null(getenv("GIT_TEST_MAINT_SCHEDULER"));
1594+
if (testing) {
1595+
char *sep = strchr(testing, ':');
1596+
if (!sep)
1597+
die("GIT_TEST_MAINT_SCHEDULER unparseable: %s", testing);
1598+
*sep = '\0';
1599+
scheduler = testing;
1600+
cmd = sep + 1;
15921601
}
1593-
fclose(cron_list);
15941602

1595-
cleanup:
1603+
if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0)
1604+
return error(_("another process is scheduling background maintenance"));
1605+
1606+
if (!strcmp(scheduler, "crontab"))
1607+
result = crontab_update_schedule(enable, lk.tempfile->fd, cmd);
1608+
else
1609+
die("unknown background scheduler: %s", scheduler);
1610+
15961611
rollback_lock_file(&lk);
1612+
free(testing);
15971613
return result;
15981614
}
15991615

t/t7900-maintenance.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ test_expect_success 'register and unregister' '
368368
'
369369

370370
test_expect_success 'start from empty cron table' '
371-
GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start &&
371+
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
372372
373373
# start registers the repo
374374
git config --get --global maintenance.repo "$(pwd)" &&
@@ -379,19 +379,19 @@ test_expect_success 'start from empty cron table' '
379379
'
380380

381381
test_expect_success 'stop from existing schedule' '
382-
GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
382+
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
383383
384384
# stop does not unregister the repo
385385
git config --get --global maintenance.repo "$(pwd)" &&
386386
387387
# Operation is idempotent
388-
GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
388+
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
389389
test_must_be_empty cron.txt
390390
'
391391

392392
test_expect_success 'start preserves existing schedule' '
393393
echo "Important information!" >cron.txt &&
394-
GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start &&
394+
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
395395
grep "Important information!" cron.txt
396396
'
397397

t/test-lib.sh

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1704,7 +1704,8 @@ test_lazy_prereq REBASE_P '
17041704
'
17051705

17061706
# Ensure that no test accidentally triggers a Git command
1707-
# that runs 'crontab', affecting a user's cron schedule.
1708-
# Tests that verify the cron integration must set this locally
1707+
# that runs the actual maintenance scheduler, affecting a user's
1708+
# system permanently.
1709+
# Tests that verify the scheduler integration must set this locally
17091710
# to avoid errors.
1710-
GIT_TEST_CRONTAB="exit 1"
1711+
GIT_TEST_MAINT_SCHEDULER="none:exit 1"

0 commit comments

Comments
 (0)