From 5e22c340720eb1b351dddbb16bdea542e2f3b841 Mon Sep 17 00:00:00 2001 From: Vaibhav Desai Date: Fri, 30 Jan 2026 12:18:00 -0800 Subject: [PATCH 1/9] add-password-file-reload-flag --- main.go | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 95dbec7ad..95219a9d3 100644 --- a/main.go +++ b/main.go @@ -245,6 +245,9 @@ func main() { flPasswordFile := pflag.String("password-file", envString("", "GITSYNC_PASSWORD_FILE", "GIT_SYNC_PASSWORD_FILE"), "the file from which the password or personal access token for git auth will be sourced") + flPasswordFileReload := pflag.Bool("password-file-reload", + envBool(false, "GITSYNC_PASSWORD_FILE_RELOAD"), + "reload the password from --password-file on each sync cycle (useful when password is rotated by an external system like Vault Dynamic Engine)") flCredentials := pflagCredentialSlice("credential", envString("", "GITSYNC_CREDENTIAL"), "one or more credentials (see --man for details) available for authentication") flSSHKeyFiles := pflag.StringArray("ssh-key-file", @@ -883,7 +886,20 @@ func main() { refreshCreds := func(ctx context.Context) error { // These should all be mutually-exclusive configs. for _, cred := range *flCredentials { - if err := git.StoreCredentials(ctx, cred.URL, cred.Username, cred.Password); err != nil { + password := cred.Password + + // If reload flag is set and this credential has a password file, + // re-read it from disk to pick up token rotation (e.g. from Vault) + if *flPasswordFileReload && cred.PasswordFile != "" { + passwordFileBytes, err := os.ReadFile(cred.PasswordFile) + if err != nil { + return fmt.Errorf("can't reload password file %q: %w", cred.PasswordFile, err) + } + password = string(passwordFileBytes) + git.log.V(3).Info("reloaded password from file", "file", cred.PasswordFile) + } + + if err := git.StoreCredentials(ctx, cred.URL, cred.Username, password); err != nil { return err } } @@ -2586,6 +2602,15 @@ OPTIONS github docs) to use for git authentication (see --username) will be read. See also $GITSYNC_PASSWORD. + --password-file-reload, $GITSYNC_PASSWORD_FILE_RELOAD + Reload the password from --password-file on each sync cycle. This + is useful when using dynamic credentials that are rotated by an + external system (e.g. Vault's dynamic GitHub secrets engine). + Without this flag, the password file is read once at startup and + cached in memory. With this flag enabled, the file will be re-read + before each sync attempt, allowing git-sync to pick up token + rotations. If not specified, this defaults to false. + --period , $GITSYNC_PERIOD How long to wait between sync attempts. This must be at least 10ms. This flag obsoletes --wait, but if --wait is specified, it From 7ef87f174b05e629b110bf72bfa5ad7f50a1f915 Mon Sep 17 00:00:00 2001 From: Vaibhav Desai Date: Wed, 4 Feb 2026 13:11:28 -0800 Subject: [PATCH 2/9] Update main.go Co-authored-by: Tim Hockin --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 95219a9d3..f8500f31c 100644 --- a/main.go +++ b/main.go @@ -247,7 +247,7 @@ func main() { "the file from which the password or personal access token for git auth will be sourced") flPasswordFileReload := pflag.Bool("password-file-reload", envBool(false, "GITSYNC_PASSWORD_FILE_RELOAD"), - "reload the password from --password-file on each sync cycle (useful when password is rotated by an external system like Vault Dynamic Engine)") + "reload the password from --password-file on each sync cycle, useful when password is rotated by an external system") flCredentials := pflagCredentialSlice("credential", envString("", "GITSYNC_CREDENTIAL"), "one or more credentials (see --man for details) available for authentication") flSSHKeyFiles := pflag.StringArray("ssh-key-file", From a50b04ede282cb7b771610cd0cd7a5663a831f1b Mon Sep 17 00:00:00 2001 From: Vaibhav Desai Date: Wed, 4 Feb 2026 14:30:25 -0800 Subject: [PATCH 3/9] Remove password file reload functionality Removed the password file reload flag and related logic to simplify credential management. --- main.go => vaibhavdesai137@gmail.com | 40 +++++++--------------------- 1 file changed, 9 insertions(+), 31 deletions(-) rename main.go => vaibhavdesai137@gmail.com (98%) diff --git a/main.go b/vaibhavdesai137@gmail.com similarity index 98% rename from main.go rename to vaibhavdesai137@gmail.com index f8500f31c..43ffaec81 100644 --- a/main.go +++ b/vaibhavdesai137@gmail.com @@ -245,9 +245,6 @@ func main() { flPasswordFile := pflag.String("password-file", envString("", "GITSYNC_PASSWORD_FILE", "GIT_SYNC_PASSWORD_FILE"), "the file from which the password or personal access token for git auth will be sourced") - flPasswordFileReload := pflag.Bool("password-file-reload", - envBool(false, "GITSYNC_PASSWORD_FILE_RELOAD"), - "reload the password from --password-file on each sync cycle, useful when password is rotated by an external system") flCredentials := pflagCredentialSlice("credential", envString("", "GITSYNC_CREDENTIAL"), "one or more credentials (see --man for details) available for authentication") flSSHKeyFiles := pflag.StringArray("ssh-key-file", @@ -752,19 +749,6 @@ func main() { os.Exit(1) } - // Finish populating credentials. - for i := range *flCredentials { - cred := &(*flCredentials)[i] - if cred.PasswordFile != "" { - passwordFileBytes, err := os.ReadFile(cred.PasswordFile) - if err != nil { - log.Error(err, "can't read password file", "file", cred.PasswordFile) - os.Exit(1) - } - cred.Password = string(passwordFileBytes) - } - } - // If the --repo or any submodule uses SSH, we need to know which keys. if err := git.SetupGitSSH(*flSSHKnownHosts, *flSSHKeyFiles, *flSSHKnownHostsFile); err != nil { log.Error(err, "can't set up git SSH", "keyFiles", *flSSHKeyFiles, "useKnownHosts", *flSSHKnownHosts, "knownHostsFile", *flSSHKnownHostsFile) @@ -888,15 +872,15 @@ func main() { for _, cred := range *flCredentials { password := cred.Password - // If reload flag is set and this credential has a password file, - // re-read it from disk to pick up token rotation (e.g. from Vault) - if *flPasswordFileReload && cred.PasswordFile != "" { + // If this credential has a password file, re-read it from disk + // to pick up token rotation (e.g. from Vault) + if cred.PasswordFile != "" { passwordFileBytes, err := os.ReadFile(cred.PasswordFile) if err != nil { - return fmt.Errorf("can't reload password file %q: %w", cred.PasswordFile, err) + return fmt.Errorf("can't read password file %q: %w", cred.PasswordFile, err) } password = string(passwordFileBytes) - git.log.V(3).Info("reloaded password from file", "file", cred.PasswordFile) + git.log.V(3).Info("read password from file", "file", cred.PasswordFile) } if err := git.StoreCredentials(ctx, cred.URL, cred.Username, password); err != nil { @@ -2600,16 +2584,10 @@ OPTIONS --password-file , $GITSYNC_PASSWORD_FILE The file from which the password or personal access token (see github docs) to use for git authentication (see --username) will be - read. See also $GITSYNC_PASSWORD. - - --password-file-reload, $GITSYNC_PASSWORD_FILE_RELOAD - Reload the password from --password-file on each sync cycle. This - is useful when using dynamic credentials that are rotated by an - external system (e.g. Vault's dynamic GitHub secrets engine). - Without this flag, the password file is read once at startup and - cached in memory. With this flag enabled, the file will be re-read - before each sync attempt, allowing git-sync to pick up token - rotations. If not specified, this defaults to false. + read. The file is re-read before each sync attempt, allowing + git-sync to pick up token rotations automatically (e.g. when + credentials are dynamically rotated by external systems). + See also $GITSYNC_PASSWORD. --period , $GITSYNC_PERIOD How long to wait between sync attempts. This must be at least From 11eb021d85af82b24a06e8a08399ff06d3916eca Mon Sep 17 00:00:00 2001 From: Vaibhav Desai Date: Wed, 4 Feb 2026 14:36:20 -0800 Subject: [PATCH 4/9] Simplify comment about token rotation Removed redundant mention of 'from Vault' in comment. --- vaibhavdesai137@gmail.com => main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename vaibhavdesai137@gmail.com => main.go (99%) diff --git a/vaibhavdesai137@gmail.com b/main.go similarity index 99% rename from vaibhavdesai137@gmail.com rename to main.go index 43ffaec81..f85d313c6 100644 --- a/vaibhavdesai137@gmail.com +++ b/main.go @@ -873,7 +873,7 @@ func main() { password := cred.Password // If this credential has a password file, re-read it from disk - // to pick up token rotation (e.g. from Vault) + // to pick up token rotation if cred.PasswordFile != "" { passwordFileBytes, err := os.ReadFile(cred.PasswordFile) if err != nil { From c6bd2d38347d2ef295d25b185e27cfbe456e52f0 Mon Sep 17 00:00:00 2001 From: Vaibhav Desai Date: Wed, 4 Feb 2026 14:37:25 -0800 Subject: [PATCH 5/9] Implement password-file reload test in e2e script Add end-to-end test for password-file reload during sync. --- test_e2e.sh | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/test_e2e.sh b/test_e2e.sh index ab9629bd3..b4cce6e60 100755 --- a/test_e2e.sh +++ b/test_e2e.sh @@ -2022,6 +2022,63 @@ function e2e::auth_http_password_file() { assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}" } +############################################## +# Test password-file reload on each sync +############################################## +function e2e::auth_http_password_file_reload() { + # Run a git-over-HTTP server. + local ctr + ctr=$(docker_run \ + -v "$REPO":/git/repo:ro \ + e2e/test/httpd) + local ip + ip=$(docker_ip "$ctr") + + # Create a password file with the correct password. + echo -n "testpass" > "$WORK/password-file" + + # First sync + echo "${FUNCNAME[0]} 1" > "$REPO/file" + git -C "$REPO" commit -qam "${FUNCNAME[0]} 1" + + GIT_SYNC \ + --period=1s \ + --repo="http://$ip/repo" \ + --root="$ROOT" \ + --link="link" \ + --username="testuser" \ + --password-file="$WORK/password-file" \ + & + wait_for_sync "${MAXWAIT}" + assert_link_exists "$ROOT/link" + assert_file_exists "$ROOT/link/file" + assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1" + + # Change password file to wrong password + echo -n "wrong" > "$WORK/password-file.tmp" + mv "$WORK/password-file.tmp" "$WORK/password-file" + + # Commit a change to the repo + echo "${FUNCNAME[0]} 2" > "$REPO/file" + git -C "$REPO" commit -qam "${FUNCNAME[0]} 2" + + # Wait a bit and verify sync did NOT happen (still has old content) + sleep 3 + assert_link_exists "$ROOT/link" + assert_file_exists "$ROOT/link/file" + assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1" + + # Change password file back to correct password + echo -n "testpass" > "$WORK/password-file.tmp" + mv "$WORK/password-file.tmp" "$WORK/password-file" + + # Verify sync now picks up the new change + wait_for_sync "${MAXWAIT}" + assert_link_exists "$ROOT/link" + assert_file_exists "$ROOT/link/file" + assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2" +} + ############################################## # Test SSH (user@host:path syntax) ############################################## From 0781d2f92238fc5aede23cbcc01a794097d53e90 Mon Sep 17 00:00:00 2001 From: Vaibhav Desai Date: Wed, 4 Feb 2026 14:38:54 -0800 Subject: [PATCH 6/9] Fix indentation in comments for clarity --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index f85d313c6..bcf75a7e2 100644 --- a/main.go +++ b/main.go @@ -2586,7 +2586,7 @@ OPTIONS github docs) to use for git authentication (see --username) will be read. The file is re-read before each sync attempt, allowing git-sync to pick up token rotations automatically (e.g. when - credentials are dynamically rotated by external systems). + credentials are dynamically rotated by external systems). See also $GITSYNC_PASSWORD. --period , $GITSYNC_PERIOD From 18a1a0fc0f4ec4225a3b45eb8fb0a9062797ec46 Mon Sep 17 00:00:00 2001 From: Vaibhav Desai Date: Wed, 4 Feb 2026 14:44:14 -0800 Subject: [PATCH 7/9] Format --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index bcf75a7e2..9326aeac4 100644 --- a/main.go +++ b/main.go @@ -2585,8 +2585,8 @@ OPTIONS The file from which the password or personal access token (see github docs) to use for git authentication (see --username) will be read. The file is re-read before each sync attempt, allowing - git-sync to pick up token rotations automatically (e.g. when - credentials are dynamically rotated by external systems). + git-sync to pick up token rotations automatically (e.g. when using + dynamic credentials from Vault's dynamic GitHub secrets engine). See also $GITSYNC_PASSWORD. --period , $GITSYNC_PERIOD From 4e6f9d24910fae45a0fdcbbcef39522c7d168e93 Mon Sep 17 00:00:00 2001 From: Vaibhav Desai Date: Wed, 4 Feb 2026 20:50:28 -0800 Subject: [PATCH 8/9] Update main.go Co-authored-by: Tim Hockin --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 9326aeac4..5c4c3c13a 100644 --- a/main.go +++ b/main.go @@ -2586,7 +2586,7 @@ OPTIONS github docs) to use for git authentication (see --username) will be read. The file is re-read before each sync attempt, allowing git-sync to pick up token rotations automatically (e.g. when using - dynamic credentials from Vault's dynamic GitHub secrets engine). + dynamic credentials from an external secrets system). See also $GITSYNC_PASSWORD. --period , $GITSYNC_PERIOD From 73fd4a5e456cc899d20389bcb24fe5e95ee7e5e6 Mon Sep 17 00:00:00 2001 From: Vaibhav Desai Date: Fri, 6 Feb 2026 12:40:47 -0800 Subject: [PATCH 9/9] rename e2e test function & add max-attempts flag --- test_e2e.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_e2e.sh b/test_e2e.sh index b4cce6e60..8966c79c5 100755 --- a/test_e2e.sh +++ b/test_e2e.sh @@ -2025,7 +2025,7 @@ function e2e::auth_http_password_file() { ############################################## # Test password-file reload on each sync ############################################## -function e2e::auth_http_password_file_reload() { +function e2e::auth_password_file_reload() { # Run a git-over-HTTP server. local ctr ctr=$(docker_run \ @@ -2048,6 +2048,7 @@ function e2e::auth_http_password_file_reload() { --link="link" \ --username="testuser" \ --password-file="$WORK/password-file" \ + --max-failures=100 \ & wait_for_sync "${MAXWAIT}" assert_link_exists "$ROOT/link"