github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/command/workdir/dir.go (about)

     1  package workdir
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  )
     8  
     9  // Dir represents a single Durgaform working directory.
    10  //
    11  // "Working directory" is unfortunately a slight misnomer, because non-default
    12  // options can potentially stretch the definition such that multiple working
    13  // directories end up appearing to share a data directory, or other similar
    14  // anomolies, but we continue to use this terminology both for historical
    15  // reasons and because it reflects the common case without any special
    16  // overrides.
    17  //
    18  // The naming convention for methods on this type is that methods whose names
    19  // begin with "Override" affect only characteristics of the particular object
    20  // they're called on, changing where it looks for data, while methods whose
    21  // names begin with "Set" will write settings to disk such that other instances
    22  // referring to the same directories will also see them. Given that, the
    23  // "Override" methods should be used only during the initialization steps
    24  // for a Dir object, typically only inside "package main", so that all
    25  // subsequent work elsewhere will access consistent locations on disk.
    26  //
    27  // We're gradually transitioning to using this type to manage working directory
    28  // settings, and so not everything in the working directory "data dir" is
    29  // encapsulated here yet, but hopefully we'll gradually migrate all of those
    30  // settings here over time. The working directory state not yet managed in here
    31  // is typically managed directly in the "command" package, either directly
    32  // inside commands or in methods of the giant command.Meta type.
    33  type Dir struct {
    34  	// mainDir is the path to the directory that we present as the
    35  	// "working directory" in the user model, which is typically the
    36  	// current working directory when running Durgaform CLI, or the
    37  	// directory explicitly chosen by the user using the -chdir=...
    38  	// global option.
    39  	mainDir string
    40  
    41  	// originalDir is the path to the working directory that was
    42  	// selected when creating the Durgaform CLI process, regardless of
    43  	// -chdir=... being set. This is only for very limited purposes
    44  	// related to backward compatibility; most functionality should
    45  	// use mainDir instead.
    46  	originalDir string
    47  
    48  	// dataDir is the path to the directory where we will store our
    49  	// working directory settings and artifacts. This is typically a
    50  	// directory named ".durgaform" within mainDir, but users may
    51  	// override it.
    52  	dataDir string
    53  }
    54  
    55  // NewDir constructs a new working directory, anchored at the given path.
    56  //
    57  // In normal use, mainPath should be "." to reflect the current working
    58  // directory, with "package main" having switched the process's current
    59  // working directory if necessary prior to calling this function. However,
    60  // unusual situations in tests may set mainPath to a temporary directory, or
    61  // similar.
    62  //
    63  // WARNING: Although the logic in this package is intended to work regardless
    64  // of whether mainPath is actually the current working directory, we're
    65  // currently in a transitional state where this package shares responsibility
    66  // for the working directory with various command.Meta methods, and those
    67  // often assume that the main path of the working directory will always be
    68  // ".". If you're writing test code that spans across both areas of
    69  // responsibility then you must ensure that the test temporarily changes the
    70  // test process's working directory to the directory returned by RootModuleDir
    71  // before using the result inside a command.Meta.
    72  func NewDir(mainPath string) *Dir {
    73  	mainPath = filepath.Clean(mainPath)
    74  	return &Dir{
    75  		mainDir:     mainPath,
    76  		originalDir: mainPath,
    77  		dataDir:     filepath.Join(mainPath, ".durgaform"),
    78  	}
    79  }
    80  
    81  // OverrideOriginalWorkingDir records a different path as the
    82  // "original working directory" for the reciever.
    83  //
    84  // Use this only to record the original working directory when Durgaform is run
    85  // with the -chdir=... global option. In that case, the directory given in
    86  // -chdir=... is the "main path" to pass in to NewDir, while the original
    87  // working directory should be sent to this method.
    88  func (d *Dir) OverrideOriginalWorkingDir(originalPath string) {
    89  	d.originalDir = filepath.Clean(originalPath)
    90  }
    91  
    92  // OverrideDataDir chooses a specific alternative directory to read and write
    93  // the persistent working directory settings.
    94  //
    95  // "package main" can call this if it detects that the user has overridden
    96  // the default location by setting the relevant environment variable. Don't
    97  // call this when that environment variable isn't set, in order to preserve
    98  // the default setting of a dot-prefixed directory directly inside the main
    99  // working directory.
   100  func (d *Dir) OverrideDataDir(dataDir string) {
   101  	d.dataDir = filepath.Clean(dataDir)
   102  }
   103  
   104  // RootModuleDir returns the directory where we expect to find the root module
   105  // configuration for this working directory.
   106  func (d *Dir) RootModuleDir() string {
   107  	// The root module configuration is just directly inside the main directory.
   108  	return d.mainDir
   109  }
   110  
   111  // OriginalWorkingDir returns the true, operating-system-originated working
   112  // directory that the current Durgaform process was launched from.
   113  //
   114  // This is usually the same as the main working directory, but differs in the
   115  // special case where the user ran Durgaform with the global -chdir=...
   116  // option. This is here only for a few backward compatibility affordances
   117  // from before we had the -chdir=... option, so should typically not be used
   118  // for anything new.
   119  func (d *Dir) OriginalWorkingDir() string {
   120  	return d.originalDir
   121  }
   122  
   123  // DataDir returns the base path where the reciever keeps all of the settings
   124  // and artifacts that must persist between consecutive commands in a single
   125  // session.
   126  //
   127  // This is exported only to allow the legacy behaviors in command.Meta to
   128  // continue accessing this directory directly. Over time we should replace
   129  // all of those direct accesses with methods on this type, and then remove
   130  // this method. Avoid using this method for new use-cases.
   131  func (d *Dir) DataDir() string {
   132  	return d.dataDir
   133  }
   134  
   135  // ensureDataDir creates the data directory and all of the necessary parent
   136  // directories that lead to it, if they don't already exist.
   137  //
   138  // For directories that already exist ensureDataDir will preserve their
   139  // permissions, while it'll create any new directories to be owned by the user
   140  // running Durgaform, readable and writable by that user, and readable by
   141  // all other users, or some approximation of that on non-Unix platforms which
   142  // have a different permissions model.
   143  func (d *Dir) ensureDataDir() error {
   144  	err := os.MkdirAll(d.dataDir, 0755)
   145  	if err != nil {
   146  		return fmt.Errorf("failed to prepare working directory: %w", err)
   147  	}
   148  	return nil
   149  }