fix(droid): align hooks with Factory 0.175
This commit is contained in:
1 parent
827374e7f8
commit
4521cbe32f
15 files changed
+2749
-881
No files matched your search
@@ -0,0 +1,267 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type droidPolicyLocation struct {
|
||||
filePath string
|
||||
label string
|
||||
}
|
||||
|
||||
type droidPolicyLayer struct {
|
||||
filePath string
|
||||
label string
|
||||
snapshot *managedFileSnapshot
|
||||
hooksDisabled *bool
|
||||
allowManagedHooksOnly *bool
|
||||
showHookOutput *bool
|
||||
}
|
||||
|
||||
type droidPolicyOrigin struct {
|
||||
filePath string
|
||||
label string
|
||||
}
|
||||
|
||||
type droidPolicyState struct {
|
||||
allowManagedHooksOnlyBy *droidPolicyOrigin
|
||||
hooksDisabled *bool
|
||||
hooksDisabledBy *droidPolicyOrigin
|
||||
preconditions []filePrecondition
|
||||
}
|
||||
|
||||
func defaultDroidManagedSettingsPath() string {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return "/Library/Application Support/Factory/settings.json"
|
||||
case "windows":
|
||||
programFiles := os.Getenv("ProgramFiles")
|
||||
if programFiles == "" {
|
||||
programFiles = `C:\Program Files`
|
||||
}
|
||||
return filepath.Join(programFiles, "Factory", "settings.json")
|
||||
default:
|
||||
return "/etc/factory/settings.json"
|
||||
}
|
||||
}
|
||||
|
||||
var droidManagedSettingsPath = defaultDroidManagedSettingsPath
|
||||
|
||||
func readDroidPolicyLayer(location droidPolicyLocation) (*droidPolicyLayer, error) {
|
||||
if _, err := managedPhysicalDirectoryExists(
|
||||
filepath.Dir(location.filePath),
|
||||
location.label+" directory",
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshot, err := readManagedFile(
|
||||
location.filePath,
|
||||
location.label,
|
||||
maxManagedSourceBytes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
layer := &droidPolicyLayer{
|
||||
filePath: location.filePath,
|
||||
label: location.label,
|
||||
snapshot: snapshot,
|
||||
}
|
||||
if snapshot == nil {
|
||||
return layer, nil
|
||||
}
|
||||
root, err := decodeJSONCObject(
|
||||
snapshot.contents,
|
||||
fmt.Sprintf("%s at %s", location.label, location.filePath),
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
layer.hooksDisabled, err = droidOptionalBoolean(root, "hooksDisabled", location.label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
layer.allowManagedHooksOnly, err = droidOptionalBoolean(
|
||||
root,
|
||||
"allowManagedHooksOnly",
|
||||
location.label,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
layer.showHookOutput, err = droidOptionalBoolean(root, "showHookOutput", location.label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return layer, nil
|
||||
}
|
||||
|
||||
func droidGitRoot(start string) (string, bool, error) {
|
||||
current, err := filepath.Abs(start)
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("resolve Factory Droid project directory: %w", err)
|
||||
}
|
||||
for {
|
||||
marker := filepath.Join(current, ".git")
|
||||
info, inspectErr := os.Lstat(marker)
|
||||
switch {
|
||||
case inspectErr == nil:
|
||||
if info.Mode()&os.ModeSymlink != 0 || (!info.IsDir() && !info.Mode().IsRegular()) {
|
||||
return "", false, fmt.Errorf(
|
||||
"Factory Droid project marker is not physical: %s",
|
||||
marker,
|
||||
)
|
||||
}
|
||||
return current, true, nil
|
||||
case !os.IsNotExist(inspectErr):
|
||||
return "", false, fmt.Errorf(
|
||||
"inspect Factory Droid project marker at %s: %w",
|
||||
marker,
|
||||
inspectErr,
|
||||
)
|
||||
}
|
||||
parent := filepath.Dir(current)
|
||||
if parent == current {
|
||||
return "", false, nil
|
||||
}
|
||||
current = parent
|
||||
}
|
||||
}
|
||||
|
||||
func droidProjectDirectories(root, current string) []string {
|
||||
directories := []string{root}
|
||||
relative, err := filepath.Rel(root, current)
|
||||
if err != nil || relative == "." || relative == "" || filepath.IsAbs(relative) {
|
||||
return directories
|
||||
}
|
||||
if relative == ".." || len(relative) > 3 && relative[:3] == ".."+string(os.PathSeparator) {
|
||||
return directories
|
||||
}
|
||||
directory := root
|
||||
for _, segment := range splitDroidPath(relative) {
|
||||
directory = filepath.Join(directory, segment)
|
||||
directories = append(directories, directory)
|
||||
}
|
||||
return directories
|
||||
}
|
||||
|
||||
func splitDroidPath(value string) []string {
|
||||
parts := make([]string, 0)
|
||||
for value != "." && value != "" {
|
||||
directory, file := filepath.Split(value)
|
||||
if file != "" {
|
||||
parts = append([]string{file}, parts...)
|
||||
}
|
||||
value = filepath.Clean(directory)
|
||||
if value == string(os.PathSeparator) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
func droidProjectPolicyLocations() ([][2]droidPolicyLocation, error) {
|
||||
current, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve Factory Droid working directory: %w", err)
|
||||
}
|
||||
current, err = filepath.Abs(current)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve Factory Droid working directory: %w", err)
|
||||
}
|
||||
root, found, err := droidGitRoot(current)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
root = current
|
||||
}
|
||||
directories := droidProjectDirectories(root, current)
|
||||
locations := make([][2]droidPolicyLocation, 0, len(directories))
|
||||
for index, directory := range directories {
|
||||
scope := "project"
|
||||
if index > 0 {
|
||||
scope = "folder " + directory
|
||||
}
|
||||
factory := filepath.Join(directory, ".factory")
|
||||
locations = append(locations, [2]droidPolicyLocation{
|
||||
{filepath.Join(factory, "settings.json"), "Factory Droid " + scope + " settings"},
|
||||
{filepath.Join(factory, "settings.local.json"), "Factory Droid " + scope + " local settings"},
|
||||
})
|
||||
}
|
||||
return locations, nil
|
||||
}
|
||||
|
||||
func droidScopeHooksDisabled(
|
||||
settings, local *droidPolicyLayer,
|
||||
) *droidPolicyLayer {
|
||||
if local.hooksDisabled != nil {
|
||||
return local
|
||||
}
|
||||
if settings.hooksDisabled != nil {
|
||||
return settings
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readDroidPolicy() (*droidPolicyState, error) {
|
||||
managed, err := readDroidPolicyLayer(droidPolicyLocation{
|
||||
droidManagedSettingsPath(),
|
||||
"Factory Droid system-managed settings",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
locations, err := droidProjectPolicyLocations()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
projectLayers := make([][2]*droidPolicyLayer, 0, len(locations))
|
||||
for _, scope := range locations {
|
||||
settings, readErr := readDroidPolicyLayer(scope[0])
|
||||
if readErr != nil {
|
||||
return nil, readErr
|
||||
}
|
||||
local, readErr := readDroidPolicyLayer(scope[1])
|
||||
if readErr != nil {
|
||||
return nil, readErr
|
||||
}
|
||||
projectLayers = append(projectLayers, [2]*droidPolicyLayer{settings, local})
|
||||
}
|
||||
state := &droidPolicyState{}
|
||||
if managed.allowManagedHooksOnly != nil && *managed.allowManagedHooksOnly {
|
||||
state.allowManagedHooksOnlyBy = &droidPolicyOrigin{managed.filePath, managed.label}
|
||||
}
|
||||
selected := managed
|
||||
if selected.hooksDisabled == nil {
|
||||
selected = nil
|
||||
for _, scope := range projectLayers {
|
||||
if layer := droidScopeHooksDisabled(scope[0], scope[1]); layer != nil {
|
||||
selected = layer
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if selected != nil {
|
||||
state.hooksDisabled = selected.hooksDisabled
|
||||
state.hooksDisabledBy = &droidPolicyOrigin{selected.filePath, selected.label}
|
||||
}
|
||||
layers := []*droidPolicyLayer{managed}
|
||||
for _, scope := range projectLayers {
|
||||
layers = append(layers, scope[0], scope[1])
|
||||
}
|
||||
state.preconditions = make([]filePrecondition, 0, len(layers))
|
||||
for _, layer := range layers {
|
||||
state.preconditions = append(state.preconditions, filePrecondition{
|
||||
filePath: layer.filePath,
|
||||
label: layer.label,
|
||||
snapshot: layer.snapshot,
|
||||
maximumSize: maxManagedSourceBytes,
|
||||
})
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
Reference in new issue
Block a user