forked from Plemya-x/ALR
		
	refactor: generate plugin executors
This commit is contained in:
		| @@ -11,7 +11,7 @@ | |||||||
|     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> |     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> | ||||||
|         <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text> |         <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text> | ||||||
|         <text x="33.5" y="14">coverage</text> |         <text x="33.5" y="14">coverage</text> | ||||||
|         <text x="86" y="15" fill="#010101" fill-opacity=".3">19.4%</text> |         <text x="86" y="15" fill="#010101" fill-opacity=".3">18.8%</text> | ||||||
|         <text x="86" y="14">19.4%</text> |         <text x="86" y="14">18.8%</text> | ||||||
|     </g> |     </g> | ||||||
| </svg> | </svg> | ||||||
|   | |||||||
| Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B | 
							
								
								
									
										390
									
								
								generators/plugin-generator/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										390
									
								
								generators/plugin-generator/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,390 @@ | |||||||
|  | // ALR - Any Linux Repository | ||||||
|  | // Copyright (C) 2025 The ALR Authors | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // (at your option) any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"go/ast" | ||||||
|  | 	"go/format" | ||||||
|  | 	"go/parser" | ||||||
|  | 	"go/token" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | 	"text/template" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/text/cases" | ||||||
|  | 	"golang.org/x/text/language" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type MethodInfo struct { | ||||||
|  | 	Name       string | ||||||
|  | 	Params     []ParamInfo | ||||||
|  | 	Results    []ResultInfo | ||||||
|  | 	EntityName string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ParamInfo struct { | ||||||
|  | 	Name string | ||||||
|  | 	Type string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ResultInfo struct { | ||||||
|  | 	Name  string | ||||||
|  | 	Type  string | ||||||
|  | 	Index int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func extractImports(node *ast.File) []string { | ||||||
|  | 	var imports []string | ||||||
|  | 	for _, imp := range node.Imports { | ||||||
|  | 		if imp.Path.Value != "" { | ||||||
|  | 			imports = append(imports, imp.Path.Value) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return imports | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func output(path string, buf bytes.Buffer) { | ||||||
|  | 	formatted, err := format.Source(buf.Bytes()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("formatting: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	outPath := strings.TrimSuffix(path, ".go") + "_gen.go" | ||||||
|  | 	outFile, err := os.Create(outPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("create file: %v", err) | ||||||
|  | 	} | ||||||
|  | 	_, err = outFile.Write(formatted) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("writing output: %v", err) | ||||||
|  | 	} | ||||||
|  | 	outFile.Close() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	path := os.Getenv("GOFILE") | ||||||
|  | 	if path == "" { | ||||||
|  | 		log.Fatal("GOFILE must be set") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(os.Args) < 2 { | ||||||
|  | 		log.Fatal("At least one entity name must be provided") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	entityNames := os.Args[1:] | ||||||
|  |  | ||||||
|  | 	fset := token.NewFileSet() | ||||||
|  | 	node, err := parser.ParseFile(fset, path, nil, parser.AllErrors) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("parsing file: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	packageName := node.Name.Name | ||||||
|  |  | ||||||
|  | 	// Find all specified entities | ||||||
|  | 	entityData := make(map[string][]*ast.Field) | ||||||
|  |  | ||||||
|  | 	for _, decl := range node.Decls { | ||||||
|  | 		genDecl, ok := decl.(*ast.GenDecl) | ||||||
|  | 		if !ok || genDecl.Tok != token.TYPE { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		for _, spec := range genDecl.Specs { | ||||||
|  | 			typeSpec := spec.(*ast.TypeSpec) | ||||||
|  | 			for _, entityName := range entityNames { | ||||||
|  | 				if typeSpec.Name.Name == entityName { | ||||||
|  | 					interfaceType, ok := typeSpec.Type.(*ast.InterfaceType) | ||||||
|  | 					if !ok { | ||||||
|  | 						log.Fatalf("entity %s is not an interface", entityName) | ||||||
|  | 					} | ||||||
|  | 					entityData[entityName] = interfaceType.Methods.List | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Verify all entities were found | ||||||
|  | 	for _, entityName := range entityNames { | ||||||
|  | 		if _, found := entityData[entityName]; !found { | ||||||
|  | 			log.Fatalf("interface %s not found", entityName) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var buf bytes.Buffer | ||||||
|  |  | ||||||
|  | 	buf.WriteString(` | ||||||
|  | // DO NOT EDIT MANUALLY. This file is generated. | ||||||
|  |  | ||||||
|  | // ALR - Any Linux Repository | ||||||
|  | // Copyright (C) 2025 The ALR Authors | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // (at your option) any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | `) | ||||||
|  |  | ||||||
|  | 	buf.WriteString(fmt.Sprintf("package %s\n", packageName)) | ||||||
|  |  | ||||||
|  | 	// Generate base structures for all entities | ||||||
|  | 	baseStructs(&buf, entityNames, extractImports(node)) | ||||||
|  |  | ||||||
|  | 	// Generate method-specific code for each entity | ||||||
|  | 	for _, entityName := range entityNames { | ||||||
|  | 		methods := parseMethodsFromFields(entityName, entityData[entityName]) | ||||||
|  | 		argsGen(&buf, methods) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	output(path, buf) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func parseMethodsFromFields(entityName string, fields []*ast.Field) []MethodInfo { | ||||||
|  | 	var methods []MethodInfo | ||||||
|  |  | ||||||
|  | 	for _, field := range fields { | ||||||
|  | 		if len(field.Names) == 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		methodName := field.Names[0].Name | ||||||
|  | 		funcType, ok := field.Type.(*ast.FuncType) | ||||||
|  | 		if !ok { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		method := MethodInfo{ | ||||||
|  | 			Name:       methodName, | ||||||
|  | 			EntityName: entityName, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Parse parameters, excluding context.Context | ||||||
|  | 		if funcType.Params != nil { | ||||||
|  | 			for i, param := range funcType.Params.List { | ||||||
|  | 				paramType := typeToString(param.Type) | ||||||
|  | 				// Skip context.Context parameters | ||||||
|  | 				if paramType == "context.Context" { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				if len(param.Names) == 0 { | ||||||
|  | 					method.Params = append(method.Params, ParamInfo{ | ||||||
|  | 						Name: fmt.Sprintf("Arg%d", i), | ||||||
|  | 						Type: paramType, | ||||||
|  | 					}) | ||||||
|  | 				} else { | ||||||
|  | 					for _, name := range param.Names { | ||||||
|  | 						method.Params = append(method.Params, ParamInfo{ | ||||||
|  | 							Name: cases.Title(language.Und, cases.NoLower).String(name.Name), | ||||||
|  | 							Type: paramType, | ||||||
|  | 						}) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Parse results | ||||||
|  | 		if funcType.Results != nil { | ||||||
|  | 			resultIndex := 0 | ||||||
|  | 			for _, result := range funcType.Results.List { | ||||||
|  | 				resultType := typeToString(result.Type) | ||||||
|  | 				if resultType == "error" { | ||||||
|  | 					continue // Skip error in response struct | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if len(result.Names) == 0 { | ||||||
|  | 					method.Results = append(method.Results, ResultInfo{ | ||||||
|  | 						Name:  fmt.Sprintf("Result%d", resultIndex), | ||||||
|  | 						Type:  resultType, | ||||||
|  | 						Index: resultIndex, | ||||||
|  | 					}) | ||||||
|  | 				} else { | ||||||
|  | 					for _, name := range result.Names { | ||||||
|  | 						method.Results = append(method.Results, ResultInfo{ | ||||||
|  | 							Name:  cases.Title(language.Und, cases.NoLower).String(name.Name), | ||||||
|  | 							Type:  resultType, | ||||||
|  | 							Index: resultIndex, | ||||||
|  | 						}) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				resultIndex++ | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		methods = append(methods, method) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return methods | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func argsGen(buf *bytes.Buffer, methods []MethodInfo) { | ||||||
|  | 	// Add template functions first | ||||||
|  | 	funcMap := template.FuncMap{ | ||||||
|  | 		"lowerFirst": func(s string) string { | ||||||
|  | 			if len(s) == 0 { | ||||||
|  | 				return s | ||||||
|  | 			} | ||||||
|  | 			return strings.ToLower(s[:1]) + s[1:] | ||||||
|  | 		}, | ||||||
|  | 		"zeroValue": func(typeName string) string { | ||||||
|  | 			switch typeName { | ||||||
|  | 			case "string": | ||||||
|  | 				return "\"\"" | ||||||
|  | 			case "int", "int8", "int16", "int32", "int64": | ||||||
|  | 				return "0" | ||||||
|  | 			case "uint", "uint8", "uint16", "uint32", "uint64": | ||||||
|  | 				return "0" | ||||||
|  | 			case "float32", "float64": | ||||||
|  | 				return "0.0" | ||||||
|  | 			case "bool": | ||||||
|  | 				return "false" | ||||||
|  | 			default: | ||||||
|  | 				return "nil" | ||||||
|  | 			} | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	argsTemplate := template.Must(template.New("args").Funcs(funcMap).Parse(` | ||||||
|  | {{range .}} | ||||||
|  | type {{.EntityName}}{{.Name}}Args struct { | ||||||
|  | {{range .Params}}	{{.Name}} {{.Type}} | ||||||
|  | {{end}}} | ||||||
|  |  | ||||||
|  | type {{.EntityName}}{{.Name}}Resp struct { | ||||||
|  | {{range .Results}}	{{.Name}} {{.Type}} | ||||||
|  | {{end}}} | ||||||
|  |  | ||||||
|  | func (s *{{.EntityName}}RPC) {{.Name}}(ctx context.Context, {{range $i, $p := .Params}}{{if $i}}, {{end}}{{lowerFirst $p.Name}} {{$p.Type}}{{end}}) ({{range $i, $r := .Results}}{{if $i}}, {{end}}{{$r.Type}}{{end}}{{if .Results}}, {{end}}error) { | ||||||
|  | 	var resp *{{.EntityName}}{{.Name}}Resp | ||||||
|  | 	err := s.client.Call("Plugin.{{.Name}}", &{{.EntityName}}{{.Name}}Args{ | ||||||
|  | {{range .Params}}		{{.Name}}: {{lowerFirst .Name}}, | ||||||
|  | {{end}}	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return {{range $i, $r := .Results}}{{if $i}}, {{end}}{{zeroValue $r.Type}}{{end}}{{if .Results}}, {{end}}err | ||||||
|  | 	} | ||||||
|  | 	return {{range $i, $r := .Results}}{{if $i}}, {{end}}resp.{{$r.Name}}{{end}}{{if .Results}}, {{end}}nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *{{.EntityName}}RPCServer) {{.Name}}(args *{{.EntityName}}{{.Name}}Args, resp *{{.EntityName}}{{.Name}}Resp) error { | ||||||
|  | 	{{if .Results}}{{range $i, $r := .Results}}{{if $i}}, {{end}}{{lowerFirst $r.Name}}{{end}}, err := {{else}}err := {{end}}s.Impl.{{.Name}}(context.Background(),{{range $i, $p := .Params}}{{if $i}}, {{end}}args.{{$p.Name}}{{end}}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	{{if .Results}}*resp = {{.EntityName}}{{.Name}}Resp{ | ||||||
|  | {{range .Results}}		{{.Name}}: {{lowerFirst .Name}}, | ||||||
|  | {{end}}	} | ||||||
|  | 	{{else}}*resp = {{.EntityName}}{{.Name}}Resp{} | ||||||
|  | 	{{end}}return nil | ||||||
|  | } | ||||||
|  | {{end}} | ||||||
|  | `)) | ||||||
|  |  | ||||||
|  | 	err := argsTemplate.Execute(buf, methods) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("execute args template: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func typeToString(expr ast.Expr) string { | ||||||
|  | 	switch t := expr.(type) { | ||||||
|  | 	case *ast.Ident: | ||||||
|  | 		return t.Name | ||||||
|  | 	case *ast.StarExpr: | ||||||
|  | 		return "*" + typeToString(t.X) | ||||||
|  | 	case *ast.ArrayType: | ||||||
|  | 		return "[]" + typeToString(t.Elt) | ||||||
|  | 	case *ast.SelectorExpr: | ||||||
|  | 		xStr := typeToString(t.X) | ||||||
|  | 		if xStr == "context" && t.Sel.Name == "Context" { | ||||||
|  | 			return "context.Context" | ||||||
|  | 		} | ||||||
|  | 		return xStr + "." + t.Sel.Name | ||||||
|  | 	case *ast.InterfaceType: | ||||||
|  | 		return "interface{}" | ||||||
|  | 	default: | ||||||
|  | 		return "interface{}" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func baseStructs(buf *bytes.Buffer, entityNames, imports []string) { | ||||||
|  | 	// Ensure "context" is included in imports | ||||||
|  | 	updatedImports := imports | ||||||
|  | 	hasContext := false | ||||||
|  | 	for _, imp := range imports { | ||||||
|  | 		if strings.Contains(imp, `"context"`) { | ||||||
|  | 			hasContext = true | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !hasContext { | ||||||
|  | 		updatedImports = append(updatedImports, `"context"`) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	contentTemplate := template.Must(template.New("").Parse(` | ||||||
|  | import ( | ||||||
|  | 	"net/rpc" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/go-plugin" | ||||||
|  | {{range .Imports}}	{{.}} | ||||||
|  | {{end}} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | {{range .EntityNames}} | ||||||
|  | type {{ . }}Plugin struct { | ||||||
|  | 	Impl {{ . }} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type {{ . }}RPCServer struct { | ||||||
|  | 	Impl {{ . }} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type {{ . }}RPC struct { | ||||||
|  | 	client *rpc.Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *{{ . }}Plugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { | ||||||
|  | 	return &{{ . }}RPC{client: c}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *{{ . }}Plugin) Server(*plugin.MuxBroker) (interface{}, error) { | ||||||
|  | 	return &{{ . }}RPCServer{Impl: p.Impl}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | {{end}} | ||||||
|  | `)) | ||||||
|  | 	err := contentTemplate.Execute(buf, struct { | ||||||
|  | 		EntityNames []string | ||||||
|  | 		Imports     []string | ||||||
|  | 	}{ | ||||||
|  | 		EntityNames: entityNames, | ||||||
|  | 		Imports:     updatedImports, | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("execute template: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -125,7 +125,7 @@ func InternalInstallCmd() *cli.Command { | |||||||
| 			plugin.Serve(&plugin.ServeConfig{ | 			plugin.Serve(&plugin.ServeConfig{ | ||||||
| 				HandshakeConfig: build.HandshakeConfig, | 				HandshakeConfig: build.HandshakeConfig, | ||||||
| 				Plugins: map[string]plugin.Plugin{ | 				Plugins: map[string]plugin.Plugin{ | ||||||
| 					"installer": &build.InstallerPlugin{ | 					"installer": &build.InstallerExecutorPlugin{ | ||||||
| 						Impl: build.NewInstaller( | 						Impl: build.NewInstaller( | ||||||
| 							manager.Detect(), | 							manager.Detect(), | ||||||
| 						), | 						), | ||||||
|   | |||||||
| @@ -176,25 +176,6 @@ type ScriptResolverExecutor interface { | |||||||
| 	ResolveScript(ctx context.Context, pkg *alrsh.Package) *ScriptInfo | 	ResolveScript(ctx context.Context, pkg *alrsh.Package) *ScriptInfo | ||||||
| } | } | ||||||
|  |  | ||||||
| type ScriptExecutor interface { |  | ||||||
| 	ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error) |  | ||||||
| 	ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error) |  | ||||||
| 	PrepareDirs( |  | ||||||
| 		ctx context.Context, |  | ||||||
| 		input *BuildInput, |  | ||||||
| 		basePkg string, |  | ||||||
| 	) error |  | ||||||
| 	ExecuteSecondPass( |  | ||||||
| 		ctx context.Context, |  | ||||||
| 		input *BuildInput, |  | ||||||
| 		sf *alrsh.ScriptFile, |  | ||||||
| 		varsOfPackages []*alrsh.Package, |  | ||||||
| 		repoDeps []string, |  | ||||||
| 		builtDeps []*BuiltDep, |  | ||||||
| 		basePkg string, |  | ||||||
| 	) ([]*BuiltDep, error) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type CacheExecutor interface { | type CacheExecutor interface { | ||||||
| 	CheckForBuiltPackage(ctx context.Context, input *BuildInput, vars *alrsh.Package) (string, bool, error) | 	CheckForBuiltPackage(ctx context.Context, input *BuildInput, vars *alrsh.Package) (string, bool, error) | ||||||
| } | } | ||||||
| @@ -211,14 +192,6 @@ type CheckerExecutor interface { | |||||||
| 	) (bool, error) | 	) (bool, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| type InstallerExecutor interface { |  | ||||||
| 	InstallLocal(paths []string, opts *manager.Opts) error |  | ||||||
| 	Install(pkgs []string, opts *manager.Opts) error |  | ||||||
| 	Remove(pkgs []string, opts *manager.Opts) error |  | ||||||
|  |  | ||||||
| 	RemoveAlreadyInstalled(pkgs []string) ([]string, error) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type SourcesInput struct { | type SourcesInput struct { | ||||||
| 	Sources   []string | 	Sources   []string | ||||||
| 	Checksums []string | 	Checksums []string | ||||||
| @@ -499,6 +472,7 @@ func (b *Builder) removeBuildDeps(ctx context.Context, input interface { | |||||||
|  |  | ||||||
| 		if remove { | 		if remove { | ||||||
| 			err = b.installerExecutor.Remove( | 			err = b.installerExecutor.Remove( | ||||||
|  | 				ctx, | ||||||
| 				deps, | 				deps, | ||||||
| 				&manager.Opts{ | 				&manager.Opts{ | ||||||
| 					NoConfirm: !input.BuildOpts().Interactive, | 					NoConfirm: !input.BuildOpts().Interactive, | ||||||
| @@ -545,6 +519,7 @@ func (b *Builder) InstallALRPackages( | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		err = b.installerExecutor.InstallLocal( | 		err = b.installerExecutor.InstallLocal( | ||||||
|  | 			ctx, | ||||||
| 			GetBuiltPaths(res), | 			GetBuiltPaths(res), | ||||||
| 			&manager.Opts{ | 			&manager.Opts{ | ||||||
| 				NoConfirm: !input.BuildOpts().Interactive, | 				NoConfirm: !input.BuildOpts().Interactive, | ||||||
| @@ -645,7 +620,7 @@ func (i *Builder) installBuildDeps( | |||||||
| 	var deps []string | 	var deps []string | ||||||
| 	var err error | 	var err error | ||||||
| 	if len(pkgs) > 0 { | 	if len(pkgs) > 0 { | ||||||
| 		deps, err = i.installerExecutor.RemoveAlreadyInstalled(pkgs) | 		deps, err = i.installerExecutor.RemoveAlreadyInstalled(ctx, pkgs) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, nil, err | 			return nil, nil, err | ||||||
| 		} | 		} | ||||||
| @@ -668,7 +643,7 @@ func (i *Builder) installOptDeps( | |||||||
| 	pkgs []string, | 	pkgs []string, | ||||||
| ) ([]*BuiltDep, error) { | ) ([]*BuiltDep, error) { | ||||||
| 	var builtDeps []*BuiltDep | 	var builtDeps []*BuiltDep | ||||||
| 	optDeps, err := i.installerExecutor.RemoveAlreadyInstalled(pkgs) | 	optDeps, err := i.installerExecutor.RemoveAlreadyInstalled(ctx, pkgs) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -710,7 +685,7 @@ func (i *Builder) InstallPkgs( | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(builtDeps) > 0 { | 	if len(builtDeps) > 0 { | ||||||
| 		err = i.installerExecutor.InstallLocal(GetBuiltPaths(builtDeps), &manager.Opts{ | 		err = i.installerExecutor.InstallLocal(ctx, GetBuiltPaths(builtDeps), &manager.Opts{ | ||||||
| 			NoConfirm: !input.BuildOpts().Interactive, | 			NoConfirm: !input.BuildOpts().Interactive, | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -719,7 +694,7 @@ func (i *Builder) InstallPkgs( | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(repoDeps) > 0 { | 	if len(repoDeps) > 0 { | ||||||
| 		err = i.installerExecutor.Install(repoDeps, &manager.Opts{ | 		err = i.installerExecutor.Install(ctx, repoDeps, &manager.Opts{ | ||||||
| 			NoConfirm: !input.BuildOpts().Interactive, | 			NoConfirm: !input.BuildOpts().Interactive, | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|   | |||||||
| @@ -17,6 +17,8 @@ | |||||||
| package build | package build | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -28,19 +30,19 @@ func NewInstaller(mgr manager.Manager) *Installer { | |||||||
|  |  | ||||||
| type Installer struct{ mgr manager.Manager } | type Installer struct{ mgr manager.Manager } | ||||||
|  |  | ||||||
| func (i *Installer) InstallLocal(paths []string, opts *manager.Opts) error { | func (i *Installer) InstallLocal(ctx context.Context, paths []string, opts *manager.Opts) error { | ||||||
| 	return i.mgr.InstallLocal(opts, paths...) | 	return i.mgr.InstallLocal(opts, paths...) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (i *Installer) Install(pkgs []string, opts *manager.Opts) error { | func (i *Installer) Install(ctx context.Context, pkgs []string, opts *manager.Opts) error { | ||||||
| 	return i.mgr.Install(opts, pkgs...) | 	return i.mgr.Install(opts, pkgs...) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (i *Installer) Remove(pkgs []string, opts *manager.Opts) error { | func (i *Installer) Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error { | ||||||
| 	return i.mgr.Remove(opts, pkgs...) | 	return i.mgr.Remove(opts, pkgs...) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (i *Installer) RemoveAlreadyInstalled(pkgs []string) ([]string, error) { | func (i *Installer) RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error) { | ||||||
| 	filteredPackages := []string{} | 	filteredPackages := []string{} | ||||||
|  |  | ||||||
| 	for _, dep := range pkgs { | 	for _, dep := range pkgs { | ||||||
|   | |||||||
							
								
								
									
										125
									
								
								internal/build/plugins.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								internal/build/plugins.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | |||||||
|  | // ALR - Any Linux Repository | ||||||
|  | // Copyright (C) 2025 The ALR Authors | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // (at your option) any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
|  | package build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"log/slog" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/go-plugin" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var pluginMap = map[string]plugin.Plugin{ | ||||||
|  | 	"script-executor": &ScriptExecutorPlugin{}, | ||||||
|  | 	"installer":       &InstallerExecutorPlugin{}, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var HandshakeConfig = plugin.HandshakeConfig{ | ||||||
|  | 	ProtocolVersion:  1, | ||||||
|  | 	MagicCookieKey:   "ALR_PLUGIN", | ||||||
|  | 	MagicCookieValue: "-", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func setCommonCmdEnv(cmd *exec.Cmd) { | ||||||
|  | 	cmd.Env = []string{ | ||||||
|  | 		"HOME=/var/cache/alr", | ||||||
|  | 		"LOGNAME=alr", | ||||||
|  | 		"USER=alr", | ||||||
|  | 		"PATH=/usr/bin:/bin:/usr/local/bin", | ||||||
|  | 	} | ||||||
|  | 	for _, env := range os.Environ() { | ||||||
|  | 		if strings.HasPrefix(env, "LANG=") || | ||||||
|  | 			strings.HasPrefix(env, "LANGUAGE=") || | ||||||
|  | 			strings.HasPrefix(env, "LC_") || | ||||||
|  | 			strings.HasPrefix(env, "ALR_LOG_LEVEL=") { | ||||||
|  | 			cmd.Env = append(cmd.Env, env) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetSafeInstaller() (InstallerExecutor, func(), error) { | ||||||
|  | 	return getSafeExecutor[InstallerExecutor]("_internal-installer", "installer") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetSafeScriptExecutor() (ScriptExecutor, func(), error) { | ||||||
|  | 	return getSafeExecutor[ScriptExecutor]("_internal-safe-script-executor", "script-executor") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getSafeExecutor[T any](subCommand, pluginName string) (T, func(), error) { | ||||||
|  | 	var err error | ||||||
|  |  | ||||||
|  | 	executable, err := os.Executable() | ||||||
|  | 	if err != nil { | ||||||
|  | 		var zero T | ||||||
|  | 		return zero, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cmd := exec.Command(executable, subCommand) | ||||||
|  | 	setCommonCmdEnv(cmd) | ||||||
|  |  | ||||||
|  | 	client := plugin.NewClient(&plugin.ClientConfig{ | ||||||
|  | 		HandshakeConfig: HandshakeConfig, | ||||||
|  | 		Plugins:         pluginMap, | ||||||
|  | 		Cmd:             cmd, | ||||||
|  | 		Logger:          logger.GetHCLoggerAdapter(), | ||||||
|  | 		SkipHostEnv:     true, | ||||||
|  | 		UnixSocketConfig: &plugin.UnixSocketConfig{ | ||||||
|  | 			Group: "alr", | ||||||
|  | 		}, | ||||||
|  | 		SyncStderr: os.Stderr, | ||||||
|  | 	}) | ||||||
|  | 	rpcClient, err := client.Client() | ||||||
|  | 	if err != nil { | ||||||
|  | 		var zero T | ||||||
|  | 		return zero, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var cleanupOnce sync.Once | ||||||
|  | 	cleanup := func() { | ||||||
|  | 		cleanupOnce.Do(func() { | ||||||
|  | 			client.Kill() | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	defer func() { | ||||||
|  | 		if err != nil { | ||||||
|  | 			slog.Debug("close executor") | ||||||
|  | 			cleanup() | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	raw, err := rpcClient.Dispense(pluginName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		var zero T | ||||||
|  | 		return zero, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	executor, ok := raw.(T) | ||||||
|  | 	if !ok { | ||||||
|  | 		var zero T | ||||||
|  | 		err = fmt.Errorf("dispensed object is not a %T (got %T)", zero, raw) | ||||||
|  | 		return zero, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return executor, cleanup, nil | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								internal/build/plugins_executors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								internal/build/plugins_executors.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | // ALR - Any Linux Repository | ||||||
|  | // Copyright (C) 2025 The ALR Authors | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // (at your option) any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
|  | package build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | //go:generate go run ../../generators/plugin-generator InstallerExecutor ScriptExecutor | ||||||
|  |  | ||||||
|  | // The Executors interfaces must use context.Context as the first parameter, | ||||||
|  | // because the plugin-generator cannot generate code without it. | ||||||
|  |  | ||||||
|  | type InstallerExecutor interface { | ||||||
|  | 	InstallLocal(ctx context.Context, paths []string, opts *manager.Opts) error | ||||||
|  | 	Install(ctx context.Context, pkgs []string, opts *manager.Opts) error | ||||||
|  | 	Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error | ||||||
|  | 	RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutor interface { | ||||||
|  | 	ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error) | ||||||
|  | 	ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error) | ||||||
|  | 	PrepareDirs( | ||||||
|  | 		ctx context.Context, | ||||||
|  | 		input *BuildInput, | ||||||
|  | 		basePkg string, | ||||||
|  | 	) error | ||||||
|  | 	ExecuteSecondPass( | ||||||
|  | 		ctx context.Context, | ||||||
|  | 		input *BuildInput, | ||||||
|  | 		sf *alrsh.ScriptFile, | ||||||
|  | 		varsOfPackages []*alrsh.Package, | ||||||
|  | 		repoDeps []string, | ||||||
|  | 		builtDeps []*BuiltDep, | ||||||
|  | 		basePkg string, | ||||||
|  | 	) ([]*BuiltDep, error) | ||||||
|  | } | ||||||
							
								
								
									
										318
									
								
								internal/build/plugins_executors_gen.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								internal/build/plugins_executors_gen.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,318 @@ | |||||||
|  | // DO NOT EDIT MANUALLY. This file is generated. | ||||||
|  |  | ||||||
|  | // ALR - Any Linux Repository | ||||||
|  | // Copyright (C) 2025 The ALR Authors | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // (at your option) any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
|  | package build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/rpc" | ||||||
|  |  | ||||||
|  | 	"context" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh" | ||||||
|  | 	"github.com/hashicorp/go-plugin" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type InstallerExecutorPlugin struct { | ||||||
|  | 	Impl InstallerExecutor | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorRPCServer struct { | ||||||
|  | 	Impl InstallerExecutor | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorRPC struct { | ||||||
|  | 	client *rpc.Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *InstallerExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { | ||||||
|  | 	return &InstallerExecutorRPC{client: c}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *InstallerExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) { | ||||||
|  | 	return &InstallerExecutorRPCServer{Impl: p.Impl}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorPlugin struct { | ||||||
|  | 	Impl ScriptExecutor | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorRPCServer struct { | ||||||
|  | 	Impl ScriptExecutor | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorRPC struct { | ||||||
|  | 	client *rpc.Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *ScriptExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { | ||||||
|  | 	return &ScriptExecutorRPC{client: c}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *ScriptExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) { | ||||||
|  | 	return &ScriptExecutorRPCServer{Impl: p.Impl}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorInstallLocalArgs struct { | ||||||
|  | 	Paths []string | ||||||
|  | 	Opts  *manager.Opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorInstallLocalResp struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerExecutorRPC) InstallLocal(ctx context.Context, paths []string, opts *manager.Opts) error { | ||||||
|  | 	var resp *InstallerExecutorInstallLocalResp | ||||||
|  | 	err := s.client.Call("Plugin.InstallLocal", &InstallerExecutorInstallLocalArgs{ | ||||||
|  | 		Paths: paths, | ||||||
|  | 		Opts:  opts, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerExecutorRPCServer) InstallLocal(args *InstallerExecutorInstallLocalArgs, resp *InstallerExecutorInstallLocalResp) error { | ||||||
|  | 	err := s.Impl.InstallLocal(context.Background(), args.Paths, args.Opts) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = InstallerExecutorInstallLocalResp{} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorInstallArgs struct { | ||||||
|  | 	Pkgs []string | ||||||
|  | 	Opts *manager.Opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorInstallResp struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerExecutorRPC) Install(ctx context.Context, pkgs []string, opts *manager.Opts) error { | ||||||
|  | 	var resp *InstallerExecutorInstallResp | ||||||
|  | 	err := s.client.Call("Plugin.Install", &InstallerExecutorInstallArgs{ | ||||||
|  | 		Pkgs: pkgs, | ||||||
|  | 		Opts: opts, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerExecutorRPCServer) Install(args *InstallerExecutorInstallArgs, resp *InstallerExecutorInstallResp) error { | ||||||
|  | 	err := s.Impl.Install(context.Background(), args.Pkgs, args.Opts) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = InstallerExecutorInstallResp{} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorRemoveArgs struct { | ||||||
|  | 	Pkgs []string | ||||||
|  | 	Opts *manager.Opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorRemoveResp struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerExecutorRPC) Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error { | ||||||
|  | 	var resp *InstallerExecutorRemoveResp | ||||||
|  | 	err := s.client.Call("Plugin.Remove", &InstallerExecutorRemoveArgs{ | ||||||
|  | 		Pkgs: pkgs, | ||||||
|  | 		Opts: opts, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerExecutorRPCServer) Remove(args *InstallerExecutorRemoveArgs, resp *InstallerExecutorRemoveResp) error { | ||||||
|  | 	err := s.Impl.Remove(context.Background(), args.Pkgs, args.Opts) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = InstallerExecutorRemoveResp{} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorRemoveAlreadyInstalledArgs struct { | ||||||
|  | 	Pkgs []string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerExecutorRemoveAlreadyInstalledResp struct { | ||||||
|  | 	Result0 []string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerExecutorRPC) RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error) { | ||||||
|  | 	var resp *InstallerExecutorRemoveAlreadyInstalledResp | ||||||
|  | 	err := s.client.Call("Plugin.RemoveAlreadyInstalled", &InstallerExecutorRemoveAlreadyInstalledArgs{ | ||||||
|  | 		Pkgs: pkgs, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return resp.Result0, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerExecutorRPCServer) RemoveAlreadyInstalled(args *InstallerExecutorRemoveAlreadyInstalledArgs, resp *InstallerExecutorRemoveAlreadyInstalledResp) error { | ||||||
|  | 	result0, err := s.Impl.RemoveAlreadyInstalled(context.Background(), args.Pkgs) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = InstallerExecutorRemoveAlreadyInstalledResp{ | ||||||
|  | 		Result0: result0, | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorReadScriptArgs struct { | ||||||
|  | 	ScriptPath string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorReadScriptResp struct { | ||||||
|  | 	Result0 *alrsh.ScriptFile | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPC) ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error) { | ||||||
|  | 	var resp *ScriptExecutorReadScriptResp | ||||||
|  | 	err := s.client.Call("Plugin.ReadScript", &ScriptExecutorReadScriptArgs{ | ||||||
|  | 		ScriptPath: scriptPath, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return resp.Result0, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPCServer) ReadScript(args *ScriptExecutorReadScriptArgs, resp *ScriptExecutorReadScriptResp) error { | ||||||
|  | 	result0, err := s.Impl.ReadScript(context.Background(), args.ScriptPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = ScriptExecutorReadScriptResp{ | ||||||
|  | 		Result0: result0, | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorExecuteFirstPassArgs struct { | ||||||
|  | 	Input *BuildInput | ||||||
|  | 	Sf    *alrsh.ScriptFile | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorExecuteFirstPassResp struct { | ||||||
|  | 	Result0 string | ||||||
|  | 	Result1 []*alrsh.Package | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPC) ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error) { | ||||||
|  | 	var resp *ScriptExecutorExecuteFirstPassResp | ||||||
|  | 	err := s.client.Call("Plugin.ExecuteFirstPass", &ScriptExecutorExecuteFirstPassArgs{ | ||||||
|  | 		Input: input, | ||||||
|  | 		Sf:    sf, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, err | ||||||
|  | 	} | ||||||
|  | 	return resp.Result0, resp.Result1, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPCServer) ExecuteFirstPass(args *ScriptExecutorExecuteFirstPassArgs, resp *ScriptExecutorExecuteFirstPassResp) error { | ||||||
|  | 	result0, result1, err := s.Impl.ExecuteFirstPass(context.Background(), args.Input, args.Sf) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = ScriptExecutorExecuteFirstPassResp{ | ||||||
|  | 		Result0: result0, | ||||||
|  | 		Result1: result1, | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorPrepareDirsArgs struct { | ||||||
|  | 	Input   *BuildInput | ||||||
|  | 	BasePkg string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorPrepareDirsResp struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPC) PrepareDirs(ctx context.Context, input *BuildInput, basePkg string) error { | ||||||
|  | 	var resp *ScriptExecutorPrepareDirsResp | ||||||
|  | 	err := s.client.Call("Plugin.PrepareDirs", &ScriptExecutorPrepareDirsArgs{ | ||||||
|  | 		Input:   input, | ||||||
|  | 		BasePkg: basePkg, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPCServer) PrepareDirs(args *ScriptExecutorPrepareDirsArgs, resp *ScriptExecutorPrepareDirsResp) error { | ||||||
|  | 	err := s.Impl.PrepareDirs(context.Background(), args.Input, args.BasePkg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = ScriptExecutorPrepareDirsResp{} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorExecuteSecondPassArgs struct { | ||||||
|  | 	Input          *BuildInput | ||||||
|  | 	Sf             *alrsh.ScriptFile | ||||||
|  | 	VarsOfPackages []*alrsh.Package | ||||||
|  | 	RepoDeps       []string | ||||||
|  | 	BuiltDeps      []*BuiltDep | ||||||
|  | 	BasePkg        string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorExecuteSecondPassResp struct { | ||||||
|  | 	Result0 []*BuiltDep | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPC) ExecuteSecondPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile, varsOfPackages []*alrsh.Package, repoDeps []string, builtDeps []*BuiltDep, basePkg string) ([]*BuiltDep, error) { | ||||||
|  | 	var resp *ScriptExecutorExecuteSecondPassResp | ||||||
|  | 	err := s.client.Call("Plugin.ExecuteSecondPass", &ScriptExecutorExecuteSecondPassArgs{ | ||||||
|  | 		Input:          input, | ||||||
|  | 		Sf:             sf, | ||||||
|  | 		VarsOfPackages: varsOfPackages, | ||||||
|  | 		RepoDeps:       repoDeps, | ||||||
|  | 		BuiltDeps:      builtDeps, | ||||||
|  | 		BasePkg:        basePkg, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return resp.Result0, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPCServer) ExecuteSecondPass(args *ScriptExecutorExecuteSecondPassArgs, resp *ScriptExecutorExecuteSecondPassResp) error { | ||||||
|  | 	result0, err := s.Impl.ExecuteSecondPass(context.Background(), args.Input, args.Sf, args.VarsOfPackages, args.RepoDeps, args.BuiltDeps, args.BasePkg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = ScriptExecutorExecuteSecondPassResp{ | ||||||
|  | 		Result0: result0, | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @@ -1,40 +0,0 @@ | |||||||
| // ALR - Any Linux Repository |  | ||||||
| // Copyright (C) 2025 The ALR Authors |  | ||||||
| // |  | ||||||
| // This program is free software: you can redistribute it and/or modify |  | ||||||
| // it under the terms of the GNU General Public License as published by |  | ||||||
| // the Free Software Foundation, either version 3 of the License, or |  | ||||||
| // (at your option) any later version. |  | ||||||
| // |  | ||||||
| // This program is distributed in the hope that it will be useful, |  | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
| // GNU General Public License for more details. |  | ||||||
| // |  | ||||||
| // You should have received a copy of the GNU General Public License |  | ||||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  |  | ||||||
| package build |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"os" |  | ||||||
| 	"os/exec" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func setCommonCmdEnv(cmd *exec.Cmd) { |  | ||||||
| 	cmd.Env = []string{ |  | ||||||
| 		"HOME=/var/cache/alr", |  | ||||||
| 		"LOGNAME=alr", |  | ||||||
| 		"USER=alr", |  | ||||||
| 		"PATH=/usr/bin:/bin:/usr/local/bin", |  | ||||||
| 	} |  | ||||||
| 	for _, env := range os.Environ() { |  | ||||||
| 		if strings.HasPrefix(env, "LANG=") || |  | ||||||
| 			strings.HasPrefix(env, "LANGUAGE=") || |  | ||||||
| 			strings.HasPrefix(env, "LC_") || |  | ||||||
| 			strings.HasPrefix(env, "ALR_LOG_LEVEL=") { |  | ||||||
| 			cmd.Env = append(cmd.Env, env) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,161 +0,0 @@ | |||||||
| // ALR - Any Linux Repository |  | ||||||
| // Copyright (C) 2025 The ALR Authors |  | ||||||
| // |  | ||||||
| // This program is free software: you can redistribute it and/or modify |  | ||||||
| // it under the terms of the GNU General Public License as published by |  | ||||||
| // the Free Software Foundation, either version 3 of the License, or |  | ||||||
| // (at your option) any later version. |  | ||||||
| // |  | ||||||
| // This program is distributed in the hope that it will be useful, |  | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
| // GNU General Public License for more details. |  | ||||||
| // |  | ||||||
| // You should have received a copy of the GNU General Public License |  | ||||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  |  | ||||||
| package build |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"log/slog" |  | ||||||
| 	"net/rpc" |  | ||||||
| 	"os" |  | ||||||
| 	"os/exec" |  | ||||||
| 	"sync" |  | ||||||
| 	"syscall" |  | ||||||
|  |  | ||||||
| 	"github.com/hashicorp/go-plugin" |  | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type InstallerPlugin struct { |  | ||||||
| 	Impl InstallerExecutor |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type InstallerRPC struct { |  | ||||||
| 	client *rpc.Client |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type InstallerRPCServer struct { |  | ||||||
| 	Impl InstallerExecutor |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type InstallArgs struct { |  | ||||||
| 	PackagesOrPaths []string |  | ||||||
| 	Opts            *manager.Opts |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *InstallerRPC) InstallLocal(paths []string, opts *manager.Opts) error { |  | ||||||
| 	return r.client.Call("Plugin.InstallLocal", &InstallArgs{ |  | ||||||
| 		PackagesOrPaths: paths, |  | ||||||
| 		Opts:            opts, |  | ||||||
| 	}, nil) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *InstallerRPCServer) InstallLocal(args *InstallArgs, reply *struct{}) error { |  | ||||||
| 	return s.Impl.InstallLocal(args.PackagesOrPaths, args.Opts) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *InstallerRPC) Install(pkgs []string, opts *manager.Opts) error { |  | ||||||
| 	return r.client.Call("Plugin.Install", &InstallArgs{ |  | ||||||
| 		PackagesOrPaths: pkgs, |  | ||||||
| 		Opts:            opts, |  | ||||||
| 	}, nil) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *InstallerRPCServer) Install(args *InstallArgs, reply *struct{}) error { |  | ||||||
| 	return s.Impl.Install(args.PackagesOrPaths, args.Opts) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *InstallerRPC) Remove(pkgs []string, opts *manager.Opts) error { |  | ||||||
| 	return r.client.Call("Plugin.Remove", &InstallArgs{ |  | ||||||
| 		PackagesOrPaths: pkgs, |  | ||||||
| 		Opts:            opts, |  | ||||||
| 	}, nil) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *InstallerRPCServer) Remove(args *InstallArgs, reply *struct{}) error { |  | ||||||
| 	return s.Impl.Remove(args.PackagesOrPaths, args.Opts) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *InstallerRPC) RemoveAlreadyInstalled(paths []string) ([]string, error) { |  | ||||||
| 	var val []string |  | ||||||
| 	err := r.client.Call("Plugin.RemoveAlreadyInstalled", paths, &val) |  | ||||||
| 	return val, err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *InstallerRPCServer) RemoveAlreadyInstalled(pkgs []string, res *[]string) error { |  | ||||||
| 	vars, err := s.Impl.RemoveAlreadyInstalled(pkgs) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	*res = vars |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *InstallerPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { |  | ||||||
| 	return &InstallerRPC{client: c}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *InstallerPlugin) Server(*plugin.MuxBroker) (interface{}, error) { |  | ||||||
| 	return &InstallerRPCServer{Impl: p.Impl}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func GetSafeInstaller() (InstallerExecutor, func(), error) { |  | ||||||
| 	var err error |  | ||||||
|  |  | ||||||
| 	executable, err := os.Executable() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
| 	cmd := exec.Command(executable, "_internal-installer") |  | ||||||
| 	setCommonCmdEnv(cmd) |  | ||||||
|  |  | ||||||
| 	slog.Debug("safe installer setup", "uid", syscall.Getuid(), "gid", syscall.Getgid()) |  | ||||||
|  |  | ||||||
| 	client := plugin.NewClient(&plugin.ClientConfig{ |  | ||||||
| 		HandshakeConfig: HandshakeConfig, |  | ||||||
| 		Plugins:         pluginMap, |  | ||||||
| 		Cmd:             cmd, |  | ||||||
| 		Logger:          logger.GetHCLoggerAdapter(), |  | ||||||
| 		SkipHostEnv:     true, |  | ||||||
| 		UnixSocketConfig: &plugin.UnixSocketConfig{ |  | ||||||
| 			Group: "alr", |  | ||||||
| 		}, |  | ||||||
| 		SyncStderr: os.Stderr, |  | ||||||
| 	}) |  | ||||||
| 	rpcClient, err := client.Client() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var cleanupOnce sync.Once |  | ||||||
| 	cleanup := func() { |  | ||||||
| 		cleanupOnce.Do(func() { |  | ||||||
| 			client.Kill() |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	defer func() { |  | ||||||
| 		if err != nil { |  | ||||||
| 			slog.Debug("close installer") |  | ||||||
| 			cleanup() |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	raw, err := rpcClient.Dispense("installer") |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	executor, ok := raw.(InstallerExecutor) |  | ||||||
| 	if !ok { |  | ||||||
| 		err = fmt.Errorf("dispensed object is not a ScriptExecutor (got %T)", raw) |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return executor, cleanup, nil |  | ||||||
| } |  | ||||||
| @@ -1,273 +0,0 @@ | |||||||
| // ALR - Any Linux Repository |  | ||||||
| // Copyright (C) 2025 The ALR Authors |  | ||||||
| // |  | ||||||
| // This program is free software: you can redistribute it and/or modify |  | ||||||
| // it under the terms of the GNU General Public License as published by |  | ||||||
| // the Free Software Foundation, either version 3 of the License, or |  | ||||||
| // (at your option) any later version. |  | ||||||
| // |  | ||||||
| // This program is distributed in the hope that it will be useful, |  | ||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
| // GNU General Public License for more details. |  | ||||||
| // |  | ||||||
| // You should have received a copy of the GNU General Public License |  | ||||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  |  | ||||||
| package build |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"log/slog" |  | ||||||
| 	"net/rpc" |  | ||||||
| 	"os" |  | ||||||
| 	"os/exec" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"github.com/hashicorp/go-plugin" |  | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var HandshakeConfig = plugin.HandshakeConfig{ |  | ||||||
| 	ProtocolVersion:  1, |  | ||||||
| 	MagicCookieKey:   "ALR_PLUGIN", |  | ||||||
| 	MagicCookieValue: "-", |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ScriptExecutorPlugin struct { |  | ||||||
| 	Impl ScriptExecutor |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ScriptExecutorRPCServer struct { |  | ||||||
| 	Impl ScriptExecutor |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ============================= |  | ||||||
| // |  | ||||||
| // ReadScript |  | ||||||
| // |  | ||||||
|  |  | ||||||
| func (s *ScriptExecutorRPC) ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error) { |  | ||||||
| 	var resp *alrsh.ScriptFile |  | ||||||
| 	err := s.client.Call("Plugin.ReadScript", scriptPath, &resp) |  | ||||||
| 	return resp, err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *ScriptExecutorRPCServer) ReadScript(scriptPath string, resp *alrsh.ScriptFile) error { |  | ||||||
| 	file, err := s.Impl.ReadScript(context.Background(), scriptPath) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	*resp = *file |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ============================= |  | ||||||
| // |  | ||||||
| // ExecuteFirstPass |  | ||||||
| // |  | ||||||
|  |  | ||||||
| type ExecuteFirstPassArgs struct { |  | ||||||
| 	Input *BuildInput |  | ||||||
| 	Sf    *alrsh.ScriptFile |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ExecuteFirstPassResp struct { |  | ||||||
| 	BasePkg        string |  | ||||||
| 	VarsOfPackages []*alrsh.Package |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *ScriptExecutorRPC) ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error) { |  | ||||||
| 	var resp *ExecuteFirstPassResp |  | ||||||
| 	err := s.client.Call("Plugin.ExecuteFirstPass", &ExecuteFirstPassArgs{ |  | ||||||
| 		Input: input, |  | ||||||
| 		Sf:    sf, |  | ||||||
| 	}, &resp) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", nil, err |  | ||||||
| 	} |  | ||||||
| 	return resp.BasePkg, resp.VarsOfPackages, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *ScriptExecutorRPCServer) ExecuteFirstPass(args *ExecuteFirstPassArgs, resp *ExecuteFirstPassResp) error { |  | ||||||
| 	basePkg, varsOfPackages, err := s.Impl.ExecuteFirstPass(context.Background(), args.Input, args.Sf) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	*resp = ExecuteFirstPassResp{ |  | ||||||
| 		BasePkg:        basePkg, |  | ||||||
| 		VarsOfPackages: varsOfPackages, |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ============================= |  | ||||||
| // |  | ||||||
| // PrepareDirs |  | ||||||
| // |  | ||||||
|  |  | ||||||
| type PrepareDirsArgs struct { |  | ||||||
| 	Input   *BuildInput |  | ||||||
| 	BasePkg string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *ScriptExecutorRPC) PrepareDirs( |  | ||||||
| 	ctx context.Context, |  | ||||||
| 	input *BuildInput, |  | ||||||
| 	basePkg string, |  | ||||||
| ) error { |  | ||||||
| 	err := s.client.Call("Plugin.PrepareDirs", &PrepareDirsArgs{ |  | ||||||
| 		Input:   input, |  | ||||||
| 		BasePkg: basePkg, |  | ||||||
| 	}, nil) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *ScriptExecutorRPCServer) PrepareDirs(args *PrepareDirsArgs, reply *struct{}) error { |  | ||||||
| 	err := s.Impl.PrepareDirs( |  | ||||||
| 		context.Background(), |  | ||||||
| 		args.Input, |  | ||||||
| 		args.BasePkg, |  | ||||||
| 	) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ============================= |  | ||||||
| // |  | ||||||
| // ExecuteSecondPass |  | ||||||
| // |  | ||||||
|  |  | ||||||
| type ExecuteSecondPassArgs struct { |  | ||||||
| 	Input          *BuildInput |  | ||||||
| 	Sf             *alrsh.ScriptFile |  | ||||||
| 	VarsOfPackages []*alrsh.Package |  | ||||||
| 	RepoDeps       []string |  | ||||||
| 	BuiltDeps      []*BuiltDep |  | ||||||
| 	BasePkg        string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *ScriptExecutorRPC) ExecuteSecondPass( |  | ||||||
| 	ctx context.Context, |  | ||||||
| 	input *BuildInput, |  | ||||||
| 	sf *alrsh.ScriptFile, |  | ||||||
| 	varsOfPackages []*alrsh.Package, |  | ||||||
| 	repoDeps []string, |  | ||||||
| 	builtDeps []*BuiltDep, |  | ||||||
| 	basePkg string, |  | ||||||
| ) ([]*BuiltDep, error) { |  | ||||||
| 	var resp []*BuiltDep |  | ||||||
| 	err := s.client.Call("Plugin.ExecuteSecondPass", &ExecuteSecondPassArgs{ |  | ||||||
| 		Input:          input, |  | ||||||
| 		Sf:             sf, |  | ||||||
| 		VarsOfPackages: varsOfPackages, |  | ||||||
| 		RepoDeps:       repoDeps, |  | ||||||
| 		BuiltDeps:      builtDeps, |  | ||||||
| 		BasePkg:        basePkg, |  | ||||||
| 	}, &resp) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return resp, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *ScriptExecutorRPCServer) ExecuteSecondPass(args *ExecuteSecondPassArgs, resp *[]*BuiltDep) error { |  | ||||||
| 	res, err := s.Impl.ExecuteSecondPass( |  | ||||||
| 		context.Background(), |  | ||||||
| 		args.Input, |  | ||||||
| 		args.Sf, |  | ||||||
| 		args.VarsOfPackages, |  | ||||||
| 		args.RepoDeps, |  | ||||||
| 		args.BuiltDeps, |  | ||||||
| 		args.BasePkg, |  | ||||||
| 	) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	*resp = res |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // |  | ||||||
| // ============================ |  | ||||||
| // |  | ||||||
|  |  | ||||||
| func (p *ScriptExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) { |  | ||||||
| 	return &ScriptExecutorRPCServer{Impl: p.Impl}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *ScriptExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { |  | ||||||
| 	return &ScriptExecutorRPC{client: c}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ScriptExecutorRPC struct { |  | ||||||
| 	client *rpc.Client |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var pluginMap = map[string]plugin.Plugin{ |  | ||||||
| 	"script-executor": &ScriptExecutorPlugin{}, |  | ||||||
| 	"installer":       &InstallerPlugin{}, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func GetSafeScriptExecutor() (ScriptExecutor, func(), error) { |  | ||||||
| 	var err error |  | ||||||
|  |  | ||||||
| 	executable, err := os.Executable() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	cmd := exec.Command(executable, "_internal-safe-script-executor") |  | ||||||
| 	setCommonCmdEnv(cmd) |  | ||||||
|  |  | ||||||
| 	client := plugin.NewClient(&plugin.ClientConfig{ |  | ||||||
| 		HandshakeConfig: HandshakeConfig, |  | ||||||
| 		Plugins:         pluginMap, |  | ||||||
| 		Cmd:             cmd, |  | ||||||
| 		Logger:          logger.GetHCLoggerAdapter(), |  | ||||||
| 		SkipHostEnv:     true, |  | ||||||
| 		UnixSocketConfig: &plugin.UnixSocketConfig{ |  | ||||||
| 			Group: "alr", |  | ||||||
| 		}, |  | ||||||
| 		SyncStderr: os.Stderr, |  | ||||||
| 	}) |  | ||||||
| 	rpcClient, err := client.Client() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var cleanupOnce sync.Once |  | ||||||
| 	cleanup := func() { |  | ||||||
| 		cleanupOnce.Do(func() { |  | ||||||
| 			client.Kill() |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	defer func() { |  | ||||||
| 		if err != nil { |  | ||||||
| 			slog.Debug("close script-executor") |  | ||||||
| 			cleanup() |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	raw, err := rpcClient.Dispense("script-executor") |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	executor, ok := raw.(ScriptExecutor) |  | ||||||
| 	if !ok { |  | ||||||
| 		err = fmt.Errorf("dispensed object is not a ScriptExecutor (got %T)", raw) |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return executor, cleanup, nil |  | ||||||
| } |  | ||||||
| @@ -218,23 +218,23 @@ msgstr "" | |||||||
| msgid "Error removing packages" | msgid "Error removing packages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: internal/build/build.go:378 | #: internal/build/build.go:351 | ||||||
| msgid "Building package" | msgid "Building package" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: internal/build/build.go:407 | #: internal/build/build.go:380 | ||||||
| msgid "The checksums array must be the same length as sources" | msgid "The checksums array must be the same length as sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: internal/build/build.go:449 | #: internal/build/build.go:422 | ||||||
| msgid "Downloading sources" | msgid "Downloading sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: internal/build/build.go:495 | #: internal/build/build.go:468 | ||||||
| msgid "Would you like to remove the build dependencies?" | msgid "Would you like to remove the build dependencies?" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: internal/build/build.go:571 | #: internal/build/build.go:546 | ||||||
| msgid "Installing dependencies" | msgid "Installing dependencies" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -225,23 +225,23 @@ msgstr "Для команды remove ожидался хотя бы 1 аргум | |||||||
| msgid "Error removing packages" | msgid "Error removing packages" | ||||||
| msgstr "Ошибка при удалении пакетов" | msgstr "Ошибка при удалении пакетов" | ||||||
|  |  | ||||||
| #: internal/build/build.go:378 | #: internal/build/build.go:351 | ||||||
| msgid "Building package" | msgid "Building package" | ||||||
| msgstr "Сборка пакета" | msgstr "Сборка пакета" | ||||||
|  |  | ||||||
| #: internal/build/build.go:407 | #: internal/build/build.go:380 | ||||||
| msgid "The checksums array must be the same length as sources" | msgid "The checksums array must be the same length as sources" | ||||||
| msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | ||||||
|  |  | ||||||
| #: internal/build/build.go:449 | #: internal/build/build.go:422 | ||||||
| msgid "Downloading sources" | msgid "Downloading sources" | ||||||
| msgstr "Скачивание источников" | msgstr "Скачивание источников" | ||||||
|  |  | ||||||
| #: internal/build/build.go:495 | #: internal/build/build.go:468 | ||||||
| msgid "Would you like to remove the build dependencies?" | msgid "Would you like to remove the build dependencies?" | ||||||
| msgstr "Хотели бы вы удалить зависимости сборки?" | msgstr "Хотели бы вы удалить зависимости сборки?" | ||||||
|  |  | ||||||
| #: internal/build/build.go:571 | #: internal/build/build.go:546 | ||||||
| msgid "Installing dependencies" | msgid "Installing dependencies" | ||||||
| msgstr "Установка зависимостей" | msgstr "Установка зависимостей" | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user