runner icon indicating copy to clipboard operation
runner copied to clipboard

checkout@v1: Race condition/Unable to re-run job with `fetch-depth 1`

Open bram-perl opened this issue 2 years ago • 0 comments

Preliminary notes

  1. I'm aware that there is an /actions/checkout repository but the code for checkout@v1 is part of this repository.
  2. I'm aware that checkout@v2 and checkout@v3 exist but these can not be used in i386-containers; for those checkout@v1 must still be used^1^3.

Describe the bug

When fetch-depth: 1 is used in combination with checkout@v1 the git checkout command may fail.

This bug manifests itself in two ways:

  1. When 2 (or more) pushes are done in a short time interval and the first action run does the git clone after the second push.
  2. When re-running an older run

In both of these cases git checkout fails and the action log contains:

Warning: Git checkout failed on shallow repository, this might because of git fetch with depth '1' doesn't include the checkout commit '...'

To Reproduce

To reproduce:

  1. Add a workflow that uses checkout@v1 with fetch-depth: 1
  2. Make a change, commit it and push it
  3. Make another change, commit it and push it
  4. Re-run the action job of the first change

A sample repository with a sample workflow: https://github.com/bram-perl/action-checkout-race-condition

Expected behavior

Checkout succeeds

Additional information

The problem happens because in checkout@v1 the git fetch command does not include the commit-sha.

Example commands:

  • checkout@v1: git ... fetch --tags --prune --progress --no-recurse-submodules --depth=1 origin +refs/heads/*:refs/remotes/origin/*
  • checkout@v3: git ... fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +a3aa7af2e2582d7a73cb5f74283fbfde076162ab:refs/remotes/origin/...

checkout@v3 (and checkout@v2) explicitly include the commit-sha while checkout@v1 does not.

Possible fix

An untested fix (which is why I'm including it here and not as PR):

Show patch
diff --git a/src/Runner.Plugins/Repository/v1.0/GitSourceProvider.cs b/src/Runner.Plugins/Repository/v1.0/GitSourceProvider.cs
index a63eaaf..f9a0418 100644
--- a/src/Runner.Plugins/Repository/v1.0/GitSourceProvider.cs
+++ b/src/Runner.Plugins/Repository/v1.0/GitSourceProvider.cs
@@ -328,12 +328,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
                 additionalFetchSpecs.Add($"+{sourceBranch}:{GetRemoteRefName(sourceBranch)}");
             }

-            int exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, additionalFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
-            if (exitCode_fetch != 0)
-            {
-                throw new InvalidOperationException($"Git fetch failed with exit code: {exitCode_fetch}");
-            }
-
             // Checkout
             // sourceToBuild is used for checkout
             // if sourceBranch is a PR branch or sourceVersion is null, make sure branch name is a remote branch. we need checkout to detached head.
@@ -350,6 +344,14 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
                 sourcesToBuild = sourceVersion;
             }

+            additionalFetchSpecs.Add(sourcesToBuild);
+
+            int exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, additionalFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
+            if (exitCode_fetch != 0)
+            {
+                throw new InvalidOperationException($"Git fetch failed with exit code: {exitCode_fetch}");
+            }
+
             // fetch lfs object upfront, this will avoid fetch lfs object during checkout which cause checkout taking forever
             // since checkout will fetch lfs object 1 at a time, while git lfs fetch will fetch lfs object in parallel.
             if (gitLfsSupport)
diff --git a/src/Runner.Plugins/Repository/v1.1/GitSourceProvider.cs b/src/Runner.Plugins/Repository/v1.1/GitSourceProvider.cs
index e138cb1..018c266 100644
--- a/src/Runner.Plugins/Repository/v1.1/GitSourceProvider.cs
+++ b/src/Runner.Plugins/Repository/v1.1/GitSourceProvider.cs
@@ -311,12 +311,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
                 additionalFetchSpecs.Add($"+{sourceBranch}:{GetRemoteRefName(sourceBranch)}");
             }

-            int exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, additionalFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
-            if (exitCode_fetch != 0)
-            {
-                throw new InvalidOperationException($"Git fetch failed with exit code: {exitCode_fetch}");
-            }
-
             // Checkout
             // sourceToBuild is used for checkout
             // if sourceBranch is a PR branch or sourceVersion is null, make sure branch name is a remote branch. we need checkout to detached head.
@@ -333,6 +327,14 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
                 sourcesToBuild = sourceVersion;
             }

+            additionalFetchSpecs.Add(sourcesToBuild);
+
+            int exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, additionalFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
+            if (exitCode_fetch != 0)
+            {
+                throw new InvalidOperationException($"Git fetch failed with exit code: {exitCode_fetch}");
+            }
+
             // fetch lfs object upfront, this will avoid fetch lfs object during checkout which cause checkout taking forever
             // since checkout will fetch lfs object 1 at a time, while git lfs fetch will fetch lfs object in parallel.
             if (gitLfsSupport)

bram-perl avatar Jan 07 '23 19:01 bram-perl