pulumi/pkg/workspace/paths.go
Matt Ellis 44d432a559 Suport workspace local configuration and use it by default
Previously, we stored configuration information in the Pulumi.yaml
file. This was a change from the old model where configuration was
stored in a special section of the checkpoint file.

While doing things this way has some upsides with being able to flow
configuration changes with your source code (e.g. fixed values for a
production stack that version with the code) it caused some friction
for the local development scinerio. In this case, setting
configuration values would pend changes to Pulumi.yaml and if you
didn't want to publish these changes, you'd have to remember to remove
them before commiting. It also was problematic for our examples, where
it was not clear if we wanted to actually include values like
`aws:config:region` in our samples.  Finally, we found that for our
own pulumi service, we'd have values that would differ across each
individual dev stack, and publishing these values to a global
Pulumi.yaml file would just be adding noise to things.

We now adopt a hybrid model, where by default configuration is stored
locally, in the workspace's settings per project. A new flag `--save`
tests commands to actual operate on the configuration information
stored in Pulumi.yaml.

With the following change, we have have four "slots" configuration
values can end up in:

1. In the Pulumi.yaml file, applies to all stacks
2. In the Pulumi.yaml file, applied to a specific stack
3. In the local workspace.json file, applied to all stacks
4. In the local workspace.json file, applied to a specific stack

When computing the configuration information for a stack, we apply
configuration in the above order, overriding values as we go
along.

We also invert the default behavior of the `pulumi config` commands so
they operate on a specific stack (i.e. how they did before
e3610989). If you want to apply configuration to all stacks, `--all`
can be passed to any configuration command.
2017-11-02 13:05:01 -07:00

76 lines
2.6 KiB
Go

// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
package workspace
import (
"os"
"path/filepath"
"strings"
"github.com/pulumi/pulumi/pkg/util/fsutil"
"github.com/pulumi/pulumi/pkg/encoding"
)
const ProjectFile = "Pulumi" // the base name of a project file.
const GitDir = ".git" // the name of the folder git uses to store information
const BookkeepingDir = ".pulumi" // the name of our bookeeping folder, we store all state information here (like .git for git)
const StackDir = "stacks" // the name of the directory that holds stack information for projects.
const WorkspaceDir = "workspaces" // the name of the directory that holds workspace information for projects.
const RepoFile = "settings.json" // the name of the file that holds information specific to the entire repository.
const ConfigDir = "config" // the name of the folder that holds local configuration information.
const WorkspaceFile = "workspace.json" // the name of the file that holds workspace information.
// DetectPackage locates the closest package from the given path, searching "upwards" in the directory hierarchy. If no
// Project is found, an empty path is returned. If problems are detected, they are logged to the diag.Sink.
func DetectPackage(path string) (string, error) {
return fsutil.WalkUp(path, isProject, func(s string) bool { return !isRepositoryFolder(filepath.Join(s, BookkeepingDir)) })
}
func isGitFolder(path string) bool {
info, err := os.Stat(path)
return err == nil && info.IsDir() && info.Name() == ".git"
}
func isRepositoryFolder(path string) bool {
info, err := os.Stat(path)
if err == nil && info.IsDir() && info.Name() == BookkeepingDir {
// make sure it has a settings.json file in it
info, err := os.Stat(filepath.Join(path, RepoFile))
return err == nil && !info.IsDir()
}
return false
}
// isProject returns true if the path references what appears to be a valid project. If problems are detected -- like
// an incorrect extension -- they are logged to the provided diag.Sink (if non-nil).
func isProject(path string) bool {
return isMarkupFile(path, ProjectFile)
}
func isMarkupFile(path string, expect string) bool {
info, err := os.Stat(path)
if err != nil || info.IsDir() {
// Missing files and directories can't be markup files.
return false
}
// Ensure the base name is expected.
name := info.Name()
ext := filepath.Ext(name)
base := strings.TrimSuffix(name, ext)
if base != expect {
return false
}
// Check all supported extensions.
for _, mext := range encoding.Exts {
if name == expect+mext {
return true
}
}
return false
}