fix(droid): align hooks with Factory 0.175

This commit is contained in:
ZacharyZhang-NY committed 2026-07-20 14:34:54 -04:00
1 parent 827374e7f8
commit 4521cbe32f
15 files changed
+2749 -881

No files matched your search

+267
View File
@@ -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
}