diff --git a/internal/cachedirectory/cachedirectory.go b/internal/cachedirectory/cachedirectory.go index 1e5ad84..69158ce 100644 --- a/internal/cachedirectory/cachedirectory.go +++ b/internal/cachedirectory/cachedirectory.go @@ -103,10 +103,43 @@ func (cacheDirectory *CacheDirectory) CheckOrCreateVersionFile(pull bool, versio return errors.New(errorPushNonCache) } +func (cacheDirectory *CacheDirectory) Lock() error { + file, err := os.Create(cacheDirectory.lockFilePath()) + if err != nil { + return errors.Wrap(err, "Error locking cache directory.") + } + defer file.Close() + // If the cache directory is already locked, it's not really a huge issue since the purpose of the lock is mostly to check whether a `pull` operation was interrupted before pushing. + return nil +} + +func (cacheDirectory *CacheDirectory) Unlock() error { + err := os.Remove(cacheDirectory.lockFilePath()) + if err != nil { + return errors.Wrap(err, "Error unlocking cache directory.") + } + return nil +} + +func (cacheDirectory *CacheDirectory) CheckLock() error { + _, err := os.Stat(cacheDirectory.lockFilePath()) + if err == nil { + return errors.New("The cache directory is locked, likely due to a `pull` command being interrupted. Please run `pull` again to ensure all required data is downloaded.") + } + if os.IsNotExist(err) { + return nil + } + return errors.Wrap(err, "Error checking if cache directory is locked.") +} + func (cacheDirectory *CacheDirectory) versionFilePath() string { return path.Join(cacheDirectory.path, ".codeql-actions-sync-version") } +func (cacheDirectory *CacheDirectory) lockFilePath() string { + return path.Join(cacheDirectory.path, ".codeql-actions-sync-lock") +} + func (cacheDirectory *CacheDirectory) GitPath() string { return path.Join(cacheDirectory.path, "git") } diff --git a/internal/cachedirectory/cachedirectory_test.go b/internal/cachedirectory/cachedirectory_test.go index 0f75024..5a28739 100644 --- a/internal/cachedirectory/cachedirectory_test.go +++ b/internal/cachedirectory/cachedirectory_test.go @@ -88,3 +88,14 @@ func TestCreateCacheDirectoryWithTrailingSlash(t *testing.T) { err := cacheDirectory.CheckOrCreateVersionFile(true, aVersion) require.NoError(t, err) } + +func TestLocking(t *testing.T) { + temporaryDirectory := test.CreateTemporaryDirectory(t) + cacheDirectory := NewCacheDirectory(path.Join(temporaryDirectory, "cache")) + require.NoError(t, cacheDirectory.CheckOrCreateVersionFile(true, aVersion)) + require.NoError(t, cacheDirectory.Lock()) + require.NoError(t, cacheDirectory.Lock()) + require.Error(t, cacheDirectory.CheckLock()) + require.NoError(t, cacheDirectory.Unlock()) + require.NoError(t, cacheDirectory.CheckLock()) +} diff --git a/internal/pull/pull.go b/internal/pull/pull.go index 607ea96..ebba12a 100644 --- a/internal/pull/pull.go +++ b/internal/pull/pull.go @@ -237,6 +237,10 @@ func Pull(ctx context.Context, cacheDirectory cachedirectory.CacheDirectory, sou if err != nil { return err } + err = cacheDirectory.Lock() + if err != nil { + return err + } var tokenClient *http.Client if sourceToken != "" { @@ -266,6 +270,11 @@ func Pull(ctx context.Context, cacheDirectory cachedirectory.CacheDirectory, sou if err != nil { return err } + + err = cacheDirectory.Unlock() + if err != nil { + return err + } log.Print("Finished pulling the CodeQL Action repository and bundles!") return nil } diff --git a/internal/push/push.go b/internal/push/push.go index 588e601..555f4cb 100644 --- a/internal/push/push.go +++ b/internal/push/push.go @@ -291,6 +291,10 @@ func Push(ctx context.Context, cacheDirectory cachedirectory.CacheDirectory, des if err != nil { return err } + err = cacheDirectory.CheckLock() + if err != nil { + return err + } destinationURL = strings.TrimRight(destinationURL, "/") tokenSource := oauth2.StaticTokenSource(