[engine] Move most of Run logic into runInner

The Run/runInner distinction exists only so that `Run` can handle
creating a temporary directory and cleaning it up after, whether or not
`runInner` fails. The split of business logic between the two functions
was pointless and arbitrary, so move it all into `runInner`.

Change-Id: Id1696ee66d3985e123b62a2621e00cf9085682c1
Reviewed-on: https://fuchsia-review.googlesource.com/c/shac-project/shac/+/916073
Reviewed-by: Anthony Fandrianto <atyfto@google.com>
Fuchsia-Auto-Submit: Oliver Newman <olivernewman@google.com>
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
diff --git a/internal/engine/run.go b/internal/engine/run.go
index cd4ec1f..e2eaf35 100644
--- a/internal/engine/run.go
+++ b/internal/engine/run.go
@@ -187,6 +187,18 @@
 
 // Run loads a main shac.star file from a root directory and runs it.
 func Run(ctx context.Context, o *Options) error {
+	tmpdir, err := os.MkdirTemp("", "shac")
+	if err != nil {
+		return err
+	}
+	err = runInner(ctx, o, tmpdir)
+	if err2 := os.RemoveAll(tmpdir); err == nil {
+		err = err2
+	}
+	return err
+}
+
+func runInner(ctx context.Context, o *Options, tmpdir string) error {
 	root, err := resolveRoot(ctx, o.Dir)
 	if err != nil {
 		return err
@@ -256,78 +268,12 @@
 		vars[name] = value
 	}
 
-	tmpdir, err := os.MkdirTemp("", "shac")
-	if err != nil {
-		return err
-	}
 	pkgMgr := NewPackageManager(tmpdir)
 	packages, err := pkgMgr.RetrievePackages(ctx, root, &doc)
 	if err != nil {
 		return err
 	}
-	err = runInner(ctx, root, tmpdir, main, doc.AllowNetwork, doc.WritableRoot, o, scm, packages, vars)
-	if err2 := os.RemoveAll(tmpdir); err == nil {
-		err = err2
-	}
-	return err
-}
 
-// normalizeFiles makes all the file paths relative to the project root, sorts,
-// and removes duplicates.
-//
-// Input paths may be absolute or relative. If relative, they are assumed to be
-// relative to the current working directory.
-func normalizeFiles(files []string, root string) ([]file, error) {
-	var cwd string
-	cwd, err := os.Getwd()
-	if err != nil {
-		return nil, err
-	}
-	var relativized []string
-	for _, orig := range files {
-		f := orig
-		if !filepath.IsAbs(f) {
-			f = filepath.Join(cwd, f)
-		}
-		var rel string
-		rel, err = filepath.Rel(root, f)
-		if err != nil {
-			return nil, err
-		}
-		// Validates that the path is within the root directory (i.e.
-		// doesn't start with "..").
-		if !filepath.IsLocal(rel) {
-			return nil, fmt.Errorf("cannot analyze file outside root: %s", orig)
-		}
-		fi, err := os.Stat(f)
-		if err != nil {
-			if errors.Is(err, os.ErrNotExist) {
-				// Make the error message more concise and use the original
-				// user-specified path rather than the normalized absolute path.
-				return nil, fmt.Errorf("no such file: %s", orig)
-			}
-			return nil, err
-		}
-		// TODO(olivernewman): Support analyzing directories. This will require
-		// doing a filesystem traversal that respects the scm, so `shac check .`
-		// still ignores git-ignored files.
-		if fi.IsDir() {
-			return nil, fmt.Errorf("is a directory: %s", orig)
-		}
-		relativized = append(relativized, rel)
-	}
-
-	slices.Sort(relativized)
-	slices.Compact(relativized)
-
-	var res []file
-	for _, f := range relativized {
-		res = append(res, &fileImpl{path: filepath.ToSlash(f)})
-	}
-	return res, nil
-}
-
-func runInner(ctx context.Context, root, tmpdir, main string, allowNetwork, writableRoot bool, o *Options, scm scmCheckout, packages map[string]fs.FS, vars map[string]string) error {
 	sb, err := sandbox.New(tmpdir)
 	if err != nil {
 		return err
@@ -348,7 +294,7 @@
 			scm = &subdirSCM{s: scm, subdir: normalized}
 		}
 		return &shacState{
-			allowNetwork: allowNetwork,
+			allowNetwork: doc.AllowNetwork,
 			env:          &env,
 			filter:       o.Filter,
 			main:         main,
@@ -358,7 +304,7 @@
 			scm:          scm,
 			subdir:       subdir,
 			tmpdir:       filepath.Join(tmpdir, strconv.Itoa(idx)),
-			writableRoot: writableRoot,
+			writableRoot: doc.WritableRoot,
 			vars:         vars,
 		}
 	}
@@ -495,6 +441,61 @@
 	return root, nil
 }
 
+// normalizeFiles makes all the file paths relative to the project root, sorts,
+// and removes duplicates.
+//
+// Input paths may be absolute or relative. If relative, they are assumed to be
+// relative to the current working directory.
+func normalizeFiles(files []string, root string) ([]file, error) {
+	var cwd string
+	cwd, err := os.Getwd()
+	if err != nil {
+		return nil, err
+	}
+	var relativized []string
+	for _, orig := range files {
+		f := orig
+		if !filepath.IsAbs(f) {
+			f = filepath.Join(cwd, f)
+		}
+		var rel string
+		rel, err = filepath.Rel(root, f)
+		if err != nil {
+			return nil, err
+		}
+		// Validates that the path is within the root directory (i.e.
+		// doesn't start with "..").
+		if !filepath.IsLocal(rel) {
+			return nil, fmt.Errorf("cannot analyze file outside root: %s", orig)
+		}
+		fi, err := os.Stat(f)
+		if err != nil {
+			if errors.Is(err, os.ErrNotExist) {
+				// Make the error message more concise and use the original
+				// user-specified path rather than the normalized absolute path.
+				return nil, fmt.Errorf("no such file: %s", orig)
+			}
+			return nil, err
+		}
+		// TODO(olivernewman): Support analyzing directories. This will require
+		// doing a filesystem traversal that respects the scm, so `shac check .`
+		// still ignores git-ignored files.
+		if fi.IsDir() {
+			return nil, fmt.Errorf("is a directory: %s", orig)
+		}
+		relativized = append(relativized, rel)
+	}
+
+	slices.Sort(relativized)
+	slices.Compact(relativized)
+
+	var res []file
+	for _, f := range relativized {
+		res = append(res, &fileImpl{path: filepath.ToSlash(f)})
+	}
+	return res, nil
+}
+
 // shacState represents a parsing state of one shac.star.
 type shacState struct {
 	env          *starlarkEnv