-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
265 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package steplib | ||
|
||
import ( | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/bitrise-io/stepman/models" | ||
"github.com/hashicorp/go-retryablehttp" | ||
) | ||
|
||
func activateStepExecutable( | ||
stepLibURI string, | ||
stepID string, | ||
version string, | ||
executable models.Executable, | ||
destination string, | ||
destinationStepYML string, | ||
) error { | ||
resp, err := retryablehttp.Get(executable.Url) | ||
if err != nil { | ||
return fmt.Errorf("fetch from %s: %w", executable.Url, err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
path := filepath.Join(destination, stepID) | ||
file, err := os.Create(path) | ||
if err != nil { | ||
return fmt.Errorf("create file %s: %w", path, err) | ||
} | ||
|
||
_, err = io.Copy(file, resp.Body) | ||
if err != nil { | ||
return fmt.Errorf("download %s to %s: %w", executable.Url, path, err) | ||
} | ||
|
||
err = validateHash(path, executable.Hash) | ||
if err != nil { | ||
return fmt.Errorf("validate hash: %s", err) | ||
} | ||
|
||
err = os.Chmod(path, 0755) | ||
if err != nil { | ||
return fmt.Errorf("set executable permission on file: %s", err) | ||
} | ||
|
||
if err := copyStepYML(stepLibURI, stepID, version, destinationStepYML); err != nil { | ||
return fmt.Errorf("copy step.yml: %s", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func validateHash(filePath string, expectedHash string) error { | ||
if expectedHash == "" { | ||
return fmt.Errorf("hash is empty") | ||
} | ||
|
||
if !strings.HasPrefix(expectedHash, "sha256-") { | ||
return fmt.Errorf("only SHA256 hashes supported at this time, make sure to prefix the hash with `sha256-`. Found hash value: %s", expectedHash) | ||
} | ||
|
||
expectedHash = strings.TrimPrefix(expectedHash, "sha256-") | ||
|
||
reader, err := os.Open(filePath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
h := sha256.New() | ||
_, err = io.Copy(h, reader) | ||
if err != nil { | ||
return fmt.Errorf("calculate hash: %w", err) | ||
} | ||
actualHash := hex.EncodeToString(h.Sum(nil)) | ||
if actualHash != expectedHash { | ||
return fmt.Errorf("hash mismatch: expected sha256-%s, got sha256-%s", expectedHash, actualHash) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package steplib | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
|
||
func TestValidateHash(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
filePath string | ||
expectedHash string | ||
expectedErr error | ||
}{ | ||
{ | ||
name: "Valid hash", | ||
filePath: "testdata/file.txt", | ||
expectedHash: "sha256-f2040af3939f5033be8ca9b363055b3e53107c4688ba39b71d4529869a9cc9b2", | ||
expectedErr: nil, | ||
}, | ||
{ | ||
name: "Hash mismatch", | ||
filePath: "testdata/file.txt", | ||
expectedHash: "sha256-1234567890abcdef", | ||
expectedErr: fmt.Errorf("hash mismatch: expected sha256-1234567890abcdef, got sha256-f2040af3939f5033be8ca9b363055b3e53107c4688ba39b71d4529869a9cc9b2"), | ||
}, | ||
{ | ||
name: "Nonexistent file", | ||
filePath: "testdata/nonexistent.txt", | ||
expectedHash: "sha256-3b6b4f1e2e8b8a9e4f7a4b5e6c7d8e9f", | ||
expectedErr: fmt.Errorf("open testdata/nonexistent.txt: no such file or directory"), | ||
}, | ||
{ | ||
name: "Empty hash", | ||
filePath: "testdata/file.txt", | ||
expectedHash: "", | ||
expectedErr: fmt.Errorf("hash is empty"), | ||
}, | ||
{ | ||
name: "Invalid hash type", | ||
filePath: "testdata/file.txt", | ||
expectedHash: "md5-3b6b4f1e2e8b8a9e4f7a4b5e6c7d8e9f", | ||
expectedErr: fmt.Errorf("only SHA256 hashes supported at this time, make sure to prefix the hash with `sha256-`. Found hash value: md5-3b6b4f1e2e8b8a9e4f7a4b5e6c7d8e9f"), | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
err := validateHash(tt.filePath, tt.expectedHash) | ||
if err != nil && tt.expectedErr == nil { | ||
t.Errorf("unexpected error: %s", err) | ||
} else if err == nil && tt.expectedErr != nil { | ||
t.Errorf("expected error: %s, but got nil", tt.expectedErr) | ||
} else if err != nil && tt.expectedErr != nil && err.Error() != tt.expectedErr.Error() { | ||
t.Errorf("expected error: %s, but got: %s", tt.expectedErr, err) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package steplib | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/bitrise-io/go-utils/command" | ||
"github.com/bitrise-io/go-utils/pathutil" | ||
"github.com/bitrise-io/stepman/models" | ||
"github.com/bitrise-io/stepman/stepman" | ||
) | ||
|
||
func activateStepSource( | ||
stepLib models.StepCollectionModel, | ||
stepLibURI, id, version string, | ||
step models.StepModel, | ||
destination string, | ||
stepYMLDestination string, | ||
log stepman.Logger, | ||
isOfflineMode bool, | ||
) error { | ||
route, found := stepman.ReadRoute(stepLibURI) | ||
if !found { | ||
return fmt.Errorf("no route found for %s steplib", stepLibURI) | ||
} | ||
|
||
stepCacheDir := stepman.GetStepCacheDirPath(route, id, version) | ||
if exist, err := pathutil.IsPathExists(stepCacheDir); err != nil { | ||
return fmt.Errorf("failed to check if %s path exist: %s", stepCacheDir, err) | ||
} else if exist { | ||
if err := copyStep(stepCacheDir, destination); err != nil { | ||
return fmt.Errorf("copy step failed: %s", err) | ||
} | ||
return nil | ||
} | ||
|
||
// version specific source cache not exists | ||
if isOfflineMode { | ||
availableVersions := ListCachedStepVersions(log, stepLib, stepLibURI, id) | ||
versionList := "Other versions available in the local cache:" | ||
for _, version := range availableVersions { | ||
versionList = versionList + fmt.Sprintf("\n- %s", version) | ||
} | ||
|
||
errMsg := fmt.Sprintf("version is not available in the local cache and $BITRISE_OFFLINE_MODE is set. %s", versionList) | ||
return fmt.Errorf("download step: %s", errMsg) | ||
} | ||
|
||
if err := stepman.DownloadStep(stepLibURI, stepLib, id, version, step.Source.Commit, log); err != nil { | ||
return fmt.Errorf("download failed: %s", err) | ||
} | ||
|
||
if err := copyStep(stepCacheDir, destination); err != nil { | ||
return fmt.Errorf("copy step failed: %s", err) | ||
} | ||
|
||
if err := copyStepYML(stepLibURI, id, version, stepYMLDestination); err != nil { | ||
return fmt.Errorf("copy step.yml failed: %s", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func copyStep(src, dst string) error { | ||
if exist, err := pathutil.IsPathExists(dst); err != nil { | ||
return fmt.Errorf("failed to check if %s path exist: %s", dst, err) | ||
} else if !exist { | ||
if err := os.MkdirAll(dst, 0777); err != nil { | ||
return fmt.Errorf("failed to create dir for %s path: %s", dst, err) | ||
} | ||
} | ||
|
||
if err := command.CopyDir(src+"/", dst, true); err != nil { | ||
return fmt.Errorf("copy command failed: %s", err) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
hash verification test |
Oops, something went wrong.