Skip to content

Add a package for working with a cache. #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

125 changes: 125 additions & 0 deletions internal/cachedirectory/cachedirectory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package cachedirectory

import (
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"

"github.com/pkg/errors"
)

const errorCacheWrongVersion = "The cache you are trying to push was created with an old version of the CodeQL Action Sync tool. Please re-pull it with this version of the tool."
const errorNotACacheOrEmpty = "The cache directory you have selected is not empty, but was not created by the CodeQL Action Sync tool. If you are sure you want to use this directory, please delete it and run the sync tool again."
const errorCacheParentDoesNotExist = "Cannot create cache directory because its parent, does not exist."
const errorPushNonCache = "The directory you have provided does not appear to be valid. Please check it exists and that you have run the `pull` command to populate it."

type CacheDirectory struct {
path string
}

func NewCacheDirectory(path string) CacheDirectory {
return CacheDirectory{
path: path,
}
}

func isEmptyOrNonExistentDirectory(path string) (bool, error) {
f, err := os.Open(path)
if err != nil {
if os.IsNotExist(err) {
return true, nil
}
return false, errors.Wrap(err, fmt.Sprintf("Could not access directory %s.", path))
}
defer f.Close()

_, err = f.Readdirnames(1)
if err != nil {
if err == io.EOF {
return true, nil
}
return false, errors.Wrap(err, fmt.Sprintf("Could not read contents of directory %s.", path))
}
return false, nil
}

func (cacheDirectory *CacheDirectory) CheckOrCreateVersionFile(pull bool, version string) error {
cacheVersionFilePath := cacheDirectory.versionFilePath()
cacheVersionBytes, err := ioutil.ReadFile(cacheVersionFilePath)
cacheVersionFileExists := !os.IsNotExist(err)
if err != nil && cacheVersionFileExists {
return errors.Wrap(err, "Could not read version file from cache directory.")
}
cacheVersion := string(cacheVersionBytes)

if cacheVersion == version {
return nil
}

if pull {
cacheParentPath := filepath.Dir(cacheDirectory.path)
_, err := os.Stat(cacheParentPath)
if err != nil {
if os.IsNotExist(err) {
return errors.New(errorCacheParentDoesNotExist)
}
return errors.Wrap(err, "Could not access parent path of cache directory.")
}

if cacheVersionFileExists {
err := os.RemoveAll(cacheDirectory.path)
if err != nil {
return errors.Wrap(err, "Error removing outdated cache directory.")
}
}

isEmptyOrNonExistent, err := isEmptyOrNonExistentDirectory(cacheDirectory.path)
if err != nil {
return err
}
if isEmptyOrNonExistent {
err := os.Mkdir(cacheDirectory.path, 0755)
if err != nil {
return errors.Wrap(err, "Could not create cache directory.")
}
err = ioutil.WriteFile(cacheVersionFilePath, []byte(version), 0644)
if err != nil {
return errors.Wrap(err, "Could not create cache version file.")
}
return nil
}
return errors.New(errorNotACacheOrEmpty)
}

if cacheVersionFileExists {
return errors.New(errorCacheWrongVersion)
}
return errors.New(errorPushNonCache)
}

func (cacheDirectory *CacheDirectory) versionFilePath() string {
return path.Join(cacheDirectory.path, ".codeql-actions-sync-version")
}

func (cacheDirectory *CacheDirectory) GitPath() string {
return path.Join(cacheDirectory.path, "git")
}

func (cacheDirectory *CacheDirectory) ReleasesPath() string {
return path.Join(cacheDirectory.path, "releases")
}

func (cacheDirectory *CacheDirectory) ReleasePath(release string) string {
return path.Join(cacheDirectory.ReleasesPath(), release)
}

func (cacheDirectory *CacheDirectory) AssetsPath(release string) string {
return path.Join(cacheDirectory.ReleasePath(release), "assets")
}

func (cacheDirectory *CacheDirectory) MetadataPath(release string) string {
return path.Join(cacheDirectory.ReleasePath(release), "metadata.json")
}
83 changes: 83 additions & 0 deletions internal/cachedirectory/cachedirectory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package cachedirectory

import (
"io/ioutil"
"os"
"path"
"testing"

"github.com/github/codeql-action-sync/test"
"github.com/stretchr/testify/require"
)

const aVersion = "1.0.0"
const aDifferentVersion = "1.0.1"

func TestCreateCacheDirectoryDuringPullThenReuse(t *testing.T) {
temporaryDirectory := test.CreateTemporaryDirectory(t)
cacheDirectory := NewCacheDirectory(path.Join(temporaryDirectory, "cache"))
err := cacheDirectory.CheckOrCreateVersionFile(true, aVersion)
require.NoError(t, err)
flagFile := path.Join(cacheDirectory.path, "flag")
ioutil.WriteFile(flagFile, []byte("test"), 0644)
err = cacheDirectory.CheckOrCreateVersionFile(true, aVersion)
require.NoError(t, err)
err = cacheDirectory.CheckOrCreateVersionFile(false, aVersion)
require.NoError(t, err)
require.FileExists(t, flagFile)
}

func TestOverwriteCacheDirectoryIfVersionMismatchDuringPull(t *testing.T) {
temporaryDirectory := test.CreateTemporaryDirectory(t)
cacheDirectory := NewCacheDirectory(path.Join(temporaryDirectory, "cache"))
err := cacheDirectory.CheckOrCreateVersionFile(true, aVersion)
require.NoError(t, err)
flagFile := path.Join(cacheDirectory.path, "flag")
ioutil.WriteFile(flagFile, []byte("test"), 0644)
err = cacheDirectory.CheckOrCreateVersionFile(true, aDifferentVersion)
require.NoError(t, err)
require.NoFileExists(t, flagFile)
}

func TestErrorIfVersionMismatchDuringPush(t *testing.T) {
temporaryDirectory := test.CreateTemporaryDirectory(t)
cacheDirectory := NewCacheDirectory(path.Join(temporaryDirectory, "cache"))
err := cacheDirectory.CheckOrCreateVersionFile(true, aVersion)
require.NoError(t, err)
err = cacheDirectory.CheckOrCreateVersionFile(false, aDifferentVersion)
require.EqualError(t, err, errorCacheWrongVersion)
}

func TestErrorIfCacheIsNonEmptyAndNotCache(t *testing.T) {
temporaryDirectory := test.CreateTemporaryDirectory(t)
cacheDirectoryPath := path.Join(temporaryDirectory, "cache")
err := os.MkdirAll(cacheDirectoryPath, 0755)
require.NoError(t, err)
flagFile := path.Join(cacheDirectoryPath, "flag")
ioutil.WriteFile(flagFile, []byte("test"), 0644)
cacheDirectory := NewCacheDirectory(cacheDirectoryPath)
err = cacheDirectory.CheckOrCreateVersionFile(true, aVersion)
require.EqualError(t, err, errorNotACacheOrEmpty)
require.FileExists(t, flagFile)
}

func TestErrorIfCacheParentDoesNotExist(t *testing.T) {
temporaryDirectory := test.CreateTemporaryDirectory(t)
cacheDirectory := NewCacheDirectory(path.Join(temporaryDirectory, "non-existent-parent", "cache"))
err := cacheDirectory.CheckOrCreateVersionFile(true, aVersion)
require.EqualError(t, err, errorCacheParentDoesNotExist)
}

func TestErrorIfPushNonCache(t *testing.T) {
temporaryDirectory := test.CreateTemporaryDirectory(t)
cacheDirectory := NewCacheDirectory(temporaryDirectory)
err := cacheDirectory.CheckOrCreateVersionFile(false, aVersion)
require.EqualError(t, err, errorPushNonCache)
}

func TestErrorIfPushNonExistent(t *testing.T) {
temporaryDirectory := test.CreateTemporaryDirectory(t)
cacheDirectory := NewCacheDirectory(path.Join(temporaryDirectory, "cache"))
err := cacheDirectory.CheckOrCreateVersionFile(false, aVersion)
require.EqualError(t, err, errorPushNonCache)
}
18 changes: 18 additions & 0 deletions test/test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package test

import (
"io/ioutil"
"os"
"testing"

"github.com/stretchr/testify/require"
)

func CreateTemporaryDirectory(t *testing.T) string {
directory, err := ioutil.TempDir("", "codeql-action-sync-tests")
require.NoError(t, err)
t.Cleanup(func() {
os.RemoveAll(directory)
})
return directory
}