in shells/abstract.go [887:989]
func (b *AbstractShell) writeSubmoduleUpdateCmd(w ShellWriter, build *common.Build, recursive bool) error {
depth := build.GetSubmoduleDepth()
b.writeSubmoduleUpdateNoticeMsg(w, recursive, depth)
var pathArgs []string
submodulePaths, err := build.GetSubmodulePaths()
if err != nil {
return err
}
if len(submodulePaths) != 0 {
pathArgs = append(pathArgs, "--")
pathArgs = append(pathArgs, submodulePaths...)
}
// Init submodules must occur prior to sync to ensure completeness of .git/config
w.Command("git", "submodule", "init")
// Sync .git/config to .gitmodules in case URL changes (e.g. new build token)
syncArgs := []string{"submodule", "sync"}
if recursive {
syncArgs = append(syncArgs, "--recursive")
}
syncArgs = append(syncArgs, pathArgs...)
w.Command("git", syncArgs...)
// Update / initialize submodules
gitURLArgs, err := build.GetURLInsteadOfArgs()
if err != nil {
return fmt.Errorf("writing submodule update commands: %w", err)
}
if credConfigFile := b.credConfigFile(build, w); credConfigFile != "" {
gitURLArgs = append(gitURLArgs, "-c", "include.path="+credConfigFile)
}
updateArgs := append(gitURLArgs, "submodule", "update", "--init") //nolint:gocritic
foreachArgs := []string{"submodule", "foreach"}
if recursive {
updateArgs = append(updateArgs, "--recursive")
foreachArgs = append(foreachArgs, "--recursive")
}
if depth > 0 {
updateArgs = append(updateArgs, "--depth", strconv.Itoa(depth))
}
submoduleUpdateFlags := build.GetGitSubmoduleUpdateFlags()
updateArgs = append(updateArgs, submoduleUpdateFlags...)
updateArgs = append(updateArgs, pathArgs...)
// Clean changed files in submodules
cleanFlags := []string{"-ffdx"}
if len(build.GetGitCleanFlags()) > 0 {
cleanFlags = build.GetGitCleanFlags()
}
cleanCommand := []string{"git clean " + strings.Join(cleanFlags, " ")}
w.Command("git", append(foreachArgs, cleanCommand...)...)
w.Command("git", append(foreachArgs, "git reset --hard")...)
w.IfCmdWithOutput("git", updateArgs...)
w.Noticef("Updated submodules")
w.Command("git", syncArgs...)
w.Else()
// call sync and update again if the initial update fails
w.Warningf("Updating submodules failed. Retrying...")
hasSubmoduleRemoteFlag := slices.ContainsFunc(submoduleUpdateFlags, func(s string) bool {
return strings.EqualFold(s, "--remote")
})
if hasSubmoduleRemoteFlag {
// We've observed issues like
// fatal: Unable to find refs/remotes/origin/dev revision in submodule path 'subs-1'
// when updating submodule with `--remote` and `branch` was set in `.gitmodules` (which is not the default branch). To
// work around that, we explicitly pull in the remote heads.
// We only do this as a fallback / on retry *and* when the `--remote` update flag is used, so that we don't
// unnecessarily pull in a ton of remote heads.
// This renders a command similar to:
// git \
// -c url.https://test.local.insteadOf=ssh://git@test.local \
// -c include.path=blipp/blarp/blarz.conf \
// submodule foreach 'git fetch origin +refs/heads/*:refs/remotes/origin/*'
w.Command("git", slices.Concat(
gitURLArgs, foreachArgs, []string{"git fetch origin +refs/heads/*:refs/remotes/origin/*"},
)...)
}
w.Command("git", syncArgs...)
w.Command("git", updateArgs...)
w.Command("git", append(foreachArgs, "git reset --hard")...)
w.EndIf()
w.Command("git", append(foreachArgs, cleanCommand...)...)
if !build.IsLFSSmudgeDisabled() {
w.IfCmd("git", "lfs", "version")
w.Command("git", append(append(gitURLArgs, foreachArgs...), "git lfs pull")...)
w.EndIf()
}
return nil
}