This is based on ds/maintenance-part-3. After sitting with the background maintenance as it has been cooking, I wanted to come back around and implement the background maintenance for Windows. However, I noticed that there were some things bothering me with background maintenance on my macOS machine. These are detailed in PATCH 3, but the tl;dr is that 'cron' is not recommended by Apple and instead 'launchd' satisfies our needs. This series implements the background scheduling so git maintenance (start|stop) works on those platforms. I've been operating with these schedules for a while now without the problems described in the patches. There is a particularly annoying case about console windows popping up on Windows, but PATCH 4 describes a plan to get around that. Update in V7 ============ * I had included an "encoding" string in the XML file for schtasks based on an example using UTF-8. The cross-platform tests then complained (in xmllint) because they wrote in ASCII instead. However, actually testing the situation on Windows (see [1]) against the real schtasks finds that it doesn't like that encoding string. I removed it entirely, and everything seems happier. * I squashed Eric's two commits making the tests better. He remains a co-author and I kept his Helped-by. I had to rearrange the commit message a bit to point out the care he took for the cross-platform tests without referring to the test doing the wrong thing. [1] https://github.com/microsoft/git/pull/304 Thanks, -Stolee cc: jrnieder@xxxxxxxxx cc: jonathantanmy@xxxxxxxxxx cc: sluongng@xxxxxxxxx cc: Đoàn Trần Công Danh congdanhqx@xxxxxxxxx cc: Martin Ågren martin.agren@xxxxxxxxx cc: Eric Sunshine sunshine@xxxxxxxxxxxxxx cc: Derrick Stolee stolee@xxxxxxxxx Derrick Stolee (4): maintenance: extract platform-specific scheduling maintenance: include 'cron' details in docs maintenance: use launchctl on macOS maintenance: use Windows scheduled tasks Documentation/git-maintenance.txt | 116 ++++++++ builtin/gc.c | 422 ++++++++++++++++++++++++++++-- t/t7900-maintenance.sh | 104 +++++++- t/test-lib.sh | 7 +- 4 files changed, 615 insertions(+), 34 deletions(-) base-commit: 0016b618182f642771dc589cf0090289f9fe1b4f Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-776%2Fderrickstolee%2Fmaintenance%2FmacOS-v7 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-776/derrickstolee/maintenance/macOS-v7 Pull-Request: https://github.com/gitgitgadget/git/pull/776 Range-diff vs v6: 1: 4807342b001 = 1: 4807342b001 maintenance: extract platform-specific scheduling 2: 7cc70a8fe7b = 2: 7cc70a8fe7b maintenance: include 'cron' details in docs 3: cd015a5cbd7 ! 3: 3576c7aa54e maintenance: use launchctl on macOS @@ Commit message the XML format. This is useful for any system that might contain the tool, so use it whenever it is available. + We strive to make these tests work on all platforms, but Windows caused + some headaches. In particular, the value of getuid() called by the C + code is not guaranteed to be the same as `$(id -u)` invoked by a test. + This is because `git.exe` is a native Windows program, whereas the + utility programs run by the test script mostly utilize the MSYS2 runtime, + which emulates a POSIX-like environment. Since the purpose of the test + is to check that the input to the hook is well-formed, the actual user + ID is immaterial, thus we can work around the problem by making the the + test UID-agnostic. Another subtle issue is the $HOME environment + variable being a Windows-style path instead of a Unix-style path. We can + be more flexible here instead of expecting exact path matches. + + Helped-by: Ævar Arnfjörð Bjarmason <avarab@xxxxxxxxx> Co-authored-by: Eric Sunshine <sunshine@xxxxxxxxxxxxxx> Signed-off-by: Eric Sunshine <sunshine@xxxxxxxxxxxxxx> Signed-off-by: Derrick Stolee <dstolee@xxxxxxxxxxxxx> @@ t/t7900-maintenance.sh: test_expect_success 'start preserves existing schedule' grep "Important information!" cron.txt ' -+test_expect_success !MINGW 'start and stop macOS maintenance' ' -+ uid=$(id -u) && ++test_expect_success 'start and stop macOS maintenance' ' ++ # ensure $HOME can be compared against hook arguments on all platforms ++ pfx=$(cd "$HOME" && pwd) && + + write_script print-args <<-\EOF && -+ echo $* >>args ++ echo $* | sed "s:gui/[0-9][0-9]*:gui/[UID]:" >>args + EOF + + rm -f args && @@ t/t7900-maintenance.sh: test_expect_success 'start preserves existing schedule' + EOF + test_cmp expect actual && + -+ rm expect && ++ rm -f expect && + for frequency in hourly daily weekly + do -+ PLIST="$HOME/Library/LaunchAgents/org.git-scm.git.$frequency.plist" && ++ PLIST="$pfx/Library/LaunchAgents/org.git-scm.git.$frequency.plist" && + test_xmllint "$PLIST" && + grep schedule=$frequency "$PLIST" && -+ echo "bootout gui/$uid $PLIST" >>expect && -+ echo "bootstrap gui/$uid $PLIST" >>expect || return 1 ++ echo "bootout gui/[UID] $PLIST" >>expect && ++ echo "bootstrap gui/[UID] $PLIST" >>expect || return 1 + done && + test_cmp expect args && + @@ t/t7900-maintenance.sh: test_expect_success 'start preserves existing schedule' + # stop does not unregister the repo + git config --get --global maintenance.repo "$(pwd)" && + -+ printf "bootout gui/$uid $HOME/Library/LaunchAgents/org.git-scm.git.%s.plist\n" \ ++ printf "bootout gui/[UID] $pfx/Library/LaunchAgents/org.git-scm.git.%s.plist\n" \ + hourly daily weekly >expect && + test_cmp expect args && + ls "$HOME/Library/LaunchAgents" >actual && 4: 6ad4a6b98c6 ! 4: 68f5013dee3 maintenance: use Windows scheduled tasks @@ Documentation/git-maintenance.txt: To create more advanced customizations to you Part of the linkgit:git[1] suite ## builtin/gc.c ## +@@ builtin/gc.c: static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit + die(_("failed to create directories for '%s'"), filename); + plist = xfopen(filename, "w"); + +- preamble = "<?xml version=\"1.0\" encoding=\"US-ASCII\"?>\n" ++ preamble = "<?xml version=\"1.0\"?>\n" + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + "<plist version=\"1.0\">" + "<dict>\n" @@ builtin/gc.c: static int launchctl_update_schedule(int run_maintenance, int fd, const char *cm return launchctl_remove_plists(cmd); } @@ builtin/gc.c: static int launchctl_update_schedule(int run_maintenance, int fd, + char *name = schtasks_task_name(frequency); + struct strbuf tfilename = STRBUF_INIT; + -+ strbuf_addf(&tfilename, "schedule_%s_XXXXXX", frequency); ++ strbuf_addf(&tfilename, "%s/schedule_%s_XXXXXX", ++ get_git_common_dir(), frequency); + tfile = xmks_tempfile(tfilename.buf); + strbuf_release(&tfilename); + + if (!fdopen_tempfile(tfile, "w")) + die(_("failed to create temp xml file")); + -+ xml = "<?xml version=\"1.0\" encoding=\"US-ASCII\"?>\n" ++ xml = "<?xml version=\"1.0\" ?>\n" + "<Task version=\"1.4\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">\n" + "<Triggers>\n" + "<CalendarTrigger>\n"; @@ builtin/gc.c: static int update_background_schedule(int enable) else ## t/t7900-maintenance.sh ## -@@ t/t7900-maintenance.sh: test_expect_success !MINGW 'start and stop macOS maintenance' ' +@@ t/t7900-maintenance.sh: test_expect_success 'start and stop macOS maintenance' ' test_line_count = 0 actual ' @@ t/t7900-maintenance.sh: test_expect_success !MINGW 'start and stop macOS mainten + EOF + + rm -f args && -+ GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" GIT_TRACE2_PERF=1 git maintenance start && ++ GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" git maintenance start && + + # start registers the repo + git config --get --global maintenance.repo "$(pwd)" && @@ t/t7900-maintenance.sh: test_expect_success !MINGW 'start and stop macOS mainten + for frequency in hourly daily weekly + do + grep "/create /tn Git Maintenance ($frequency) /f /xml" args && -+ file=$(ls schedule_$frequency*.xml) && -+ test_xmllint "$file" && -+ grep "encoding=.US-ASCII." "$file" || return 1 ++ file=$(ls .git/schedule_${frequency}*.xml) && ++ test_xmllint "$file" || return 1 + done && + + rm -f args && @@ t/t7900-maintenance.sh: test_expect_success !MINGW 'start and stop macOS mainten + # stop does not unregister the repo + git config --get --global maintenance.repo "$(pwd)" && + -+ rm expect && + printf "/delete /tn Git Maintenance (%s) /f\n" \ + hourly daily weekly >expect && + test_cmp expect args -- gitgitgadget