Remove pulumi init

`pulumi init` was part of our old identity model with the service and
is no longer used. We can now delete this code.

Fixes #1241
This commit is contained in:
Matt Ellis 2018-05-21 16:17:12 -07:00
parent b5efe40f04
commit 0732b05c5d
14 changed files with 134 additions and 569 deletions

View file

@ -1,72 +0,0 @@
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
package cmd
import (
"fmt"
"os"
"github.com/pulumi/pulumi/pkg/util/cmdutil"
"github.com/pulumi/pulumi/pkg/workspace"
"github.com/spf13/cobra"
)
func newInitCmd() *cobra.Command {
var owner string
var name string
cmd := &cobra.Command{
Use: "init",
Short: "Initialize a new Pulumi repository",
Args: cmdutil.NoArgs,
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
cwd, err := os.Getwd()
if err != nil {
return err
}
repo, err := workspace.GetRepository(cwd)
if err != nil && err != workspace.ErrNoRepository {
return err
}
if err == workspace.ErrNoRepository {
// No existing repository, so we'll need to create one
repo = workspace.NewRepository(cwd)
detectedOwner, detectedName, detectErr := detectOwnerAndName(cwd)
if detectErr != nil {
return detectErr
}
repo.Owner = detectedOwner
repo.Name = detectedName
}
// explicit command line arguments should overwrite any existing values
if owner != "" {
repo.Owner = owner
}
if name != "" {
repo.Name = name
}
err = repo.Save()
if err != nil {
return err
}
fmt.Printf("Initialized Pulumi repository\n")
return nil
}),
}
cmd.PersistentFlags().StringVar(
&owner, "owner", "",
"Override the repository owner; default is taken from current Git repository or username")
cmd.PersistentFlags().StringVar(
&name, "name", "",
"Override the repository name; default is taken from current Git repository or current working directory")
return cmd
}

View file

@ -92,7 +92,6 @@ func NewPulumiCmd() *cobra.Command {
cmd.AddCommand(newCancelCmd())
cmd.AddCommand(newConfigCmd())
cmd.AddCommand(newDestroyCmd())
cmd.AddCommand(newInitCmd())
cmd.AddCommand(newLoginCmd())
cmd.AddCommand(newLogoutCmd())
cmd.AddCommand(newLogsCmd())

View file

@ -9,18 +9,14 @@ import (
"os"
"os/exec"
"os/signal"
"os/user"
"path"
"path/filepath"
"sort"
"strings"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh/terminal"
survey "gopkg.in/AlecAivazis/survey.v1"
surveycore "gopkg.in/AlecAivazis/survey.v1/core"
git "gopkg.in/src-d/go-git.v4"
"github.com/pulumi/pulumi/pkg/backend"
"github.com/pulumi/pulumi/pkg/backend/cloud"
@ -32,7 +28,7 @@ import (
"github.com/pulumi/pulumi/pkg/util/cancel"
"github.com/pulumi/pulumi/pkg/util/cmdutil"
"github.com/pulumi/pulumi/pkg/util/contract"
"github.com/pulumi/pulumi/pkg/util/fsutil"
"github.com/pulumi/pulumi/pkg/util/gitutil"
"github.com/pulumi/pulumi/pkg/util/logging"
"github.com/pulumi/pulumi/pkg/workspace"
)
@ -232,90 +228,6 @@ func chooseStack(b backend.Backend, offerNew bool) (backend.Stack, error) {
return stacks[option], nil
}
func detectOwnerAndName(dir string) (string, string, error) {
owner, repo, err := getGitHubProjectForOrigin(dir)
if err == nil {
return owner, repo, nil
}
user, err := user.Current()
if err != nil {
return "", "", err
}
return user.Username, filepath.Base(dir), nil
}
// getGitRepository returns the git repository by walking up from the provided directory.
// If no repository is found, will return (nil, nil).
func getGitRepository(dir string) (*git.Repository, error) {
gitRoot, err := fsutil.WalkUp(dir, func(s string) bool { return filepath.Base(s) == ".git" }, nil)
if err != nil {
return nil, errors.Wrapf(err, "searching for git repository from %v", dir)
}
if gitRoot == "" {
return nil, nil
}
// Open the git repo in the .git folder's parent, not the .git folder itself.
repo, err := git.PlainOpen(path.Join(gitRoot, ".."))
if err == git.ErrRepositoryNotExists {
return nil, nil
}
if err != nil {
return nil, errors.Wrap(err, "reading git repository")
}
return repo, nil
}
func getGitHubProjectForOrigin(dir string) (string, string, error) {
repo, err := getGitRepository(dir)
if repo == nil {
return "", "", fmt.Errorf("no git repository found from %v", dir)
}
if err != nil {
return "", "", err
}
return getGitHubProjectForOriginByRepo(repo)
}
// Returns the GitHub login, and GitHub repo name if the "origin" remote is
// a GitHub URL.
func getGitHubProjectForOriginByRepo(repo *git.Repository) (string, string, error) {
remote, err := repo.Remote("origin")
if err != nil {
return "", "", errors.Wrap(err, "could not read origin information")
}
remoteURL := ""
if len(remote.Config().URLs) > 0 {
remoteURL = remote.Config().URLs[0]
}
project := ""
const GitHubSSHPrefix = "git@github.com:"
const GitHubHTTPSPrefix = "https://github.com/"
const GitHubRepositorySuffix = ".git"
if strings.HasPrefix(remoteURL, GitHubSSHPrefix) {
project = trimGitRemoteURL(remoteURL, GitHubSSHPrefix, GitHubRepositorySuffix)
} else if strings.HasPrefix(remoteURL, GitHubHTTPSPrefix) {
project = trimGitRemoteURL(remoteURL, GitHubHTTPSPrefix, GitHubRepositorySuffix)
}
split := strings.Split(project, "/")
if len(split) != 2 {
return "", "", errors.Errorf("could not detect GitHub project from url: %v", remote)
}
return split[0], split[1], nil
}
func trimGitRemoteURL(url string, prefix string, suffix string) string {
return strings.TrimSuffix(strings.TrimPrefix(url, prefix), suffix)
}
// readProject attempts to detect and read the project for the current workspace. If an error occurs, it will be
// printed to Stderr, and the returned value will be nil. If the project is successfully detected and read, it
// is returned along with the path to its containing directory, which will be used as the root of the project's
@ -424,7 +336,7 @@ func getUpdateMetadata(msg, root string) (backend.UpdateMetadata, error) {
}
// Gather git-related data as appropriate. (Returns nil, nil if no repo found.)
repo, err := getGitRepository(root)
repo, err := gitutil.GetGitRepository(root)
if err != nil {
cmdutil.Diag().Warningf(diag.Message("", "could not detect Git repository: %v"), err)
}
@ -434,7 +346,7 @@ func getUpdateMetadata(msg, root string) (backend.UpdateMetadata, error) {
}
// GitHub repo slug if applicable. We don't require GitHub, so swallow errors.
ghLogin, ghRepo, err := getGitHubProjectForOriginByRepo(repo)
ghLogin, ghRepo, err := gitutil.GetGitHubProjectForOriginByRepo(repo)
if err != nil {
cmdutil.Diag().Warningf(diag.Message("", "could not detect GitHub project information: %v"), err)
} else {

View file

@ -255,24 +255,10 @@ func cloudConsoleURL(cloudURL string, paths ...string) string {
return cloudURL[:ix] + path.Join(append([]string{cloudURL[ix+len(defaultAPIURLPrefix):]}, paths...)...)
}
// cloudConsoleProjectPath returns the project path components for getting to a stack in the cloud console. This path
// must, of course, be combined with the actual console base URL by way of the CloudConsoleURL function above.
func (b *cloudBackend) cloudConsoleProjectPath(projID client.ProjectIdentifier) string {
// When projID.Repository is the empty string, we are using the new identity model. In this case, the service
// does not include project or repository information in URLS, so the "project path" is simply the owner.
//
// TODO(ellismg)[pulumi/pulumi#1241] Clean this up once we remove pulumi init
if projID.Repository == "" {
return projID.Owner
}
return path.Join(projID.Owner, projID.Repository, projID.Project)
}
// CloudConsoleStackPath returns the stack path components for getting to a stack in the cloud console. This path
// must, of coursee, be combined with the actual console base URL by way of the CloudConsoleURL function above.
func (b *cloudBackend) cloudConsoleStackPath(stackID client.StackIdentifier) string {
return path.Join(b.cloudConsoleProjectPath(stackID.ProjectIdentifier), stackID.Stack)
return path.Join(stackID.Owner, stackID.Stack)
}
// Logout logs out of the target cloud URL.
@ -370,15 +356,6 @@ type CreateStackOptions struct {
CloudName string
}
func ownerFromRef(stackRef backend.StackReference) string {
if r, ok := stackRef.(cloudBackendReference); ok {
return r.owner
}
contract.Failf("bad StackReference type")
return ""
}
func (b *cloudBackend) CreateStack(ctx context.Context, stackRef backend.StackReference,
opts interface{}) (backend.Stack, error) {
@ -391,7 +368,7 @@ func (b *cloudBackend) CreateStack(ctx context.Context, stackRef backend.StackRe
return nil, errors.New("expected a CloudStackOptions value for opts parameter")
}
project, err := b.getCloudProjectIdentifier(ownerFromRef(stackRef))
stackID, err := b.getCloudStackIdentifier(stackRef)
if err != nil {
return nil, err
}
@ -401,11 +378,11 @@ func (b *cloudBackend) CreateStack(ctx context.Context, stackRef backend.StackRe
return nil, errors.Wrap(err, "error determining initial tags")
}
apistack, err := b.client.CreateStack(ctx, project, cloudOpts.CloudName, string(stackRef.StackName()), tags)
apistack, err := b.client.CreateStack(ctx, stackID, cloudOpts.CloudName, tags)
if err != nil {
// If the status is 409 Conflict (stack already exists), return StackAlreadyExistsError.
if errResp, ok := err.(*apitype.ErrorResponse); ok && errResp.Code == http.StatusConflict {
return nil, &backend.StackAlreadyExistsError{StackName: string(stackRef.StackName())}
return nil, &backend.StackAlreadyExistsError{StackName: stackID.Stack}
}
return nil, err
}
@ -421,12 +398,7 @@ func (b *cloudBackend) CreateStack(ctx context.Context, stackRef backend.StackRe
}
func (b *cloudBackend) ListStacks(ctx context.Context, projectFilter *tokens.PackageName) ([]backend.Stack, error) {
project, err := b.getCloudProjectIdentifier("")
if err != nil {
return nil, err
}
stacks, err := b.client.ListStacks(ctx, project, projectFilter)
stacks, err := b.client.ListStacks(ctx, projectFilter)
if err != nil {
return nil, err
}
@ -1072,59 +1044,22 @@ func (b *cloudBackend) ImportDeployment(ctx context.Context, stackRef backend.St
return nil
}
// getCloudProjectIdentifier returns information about the current repository and project, based on the current
// working directory.
func (b *cloudBackend) getCloudProjectIdentifier(owner string) (client.ProjectIdentifier, error) {
w, err := workspace.New()
if err != nil {
return client.ProjectIdentifier{}, err
}
proj, err := workspace.DetectProject()
if err != nil {
return client.ProjectIdentifier{}, err
}
repo := w.Repository()
// If we have repository information (this is the case when `pulumi init` has been run, use that.) To support the
// old and new model, we either set the Repository field in ProjectIdentifer or keep it as the empty string. The
// client type uses this to decide what REST endpoints to hit.
//
// TODO(ellismg)[pulumi/pulumi#1241] Clean this up once we remove pulumi init
if repo != nil {
return client.ProjectIdentifier{
Owner: repo.Owner,
Repository: repo.Name,
Project: string(proj.Name),
}, nil
}
// getCloudStackIdentifier returns information about the given stack in the current repository and project, based on
// the current working directory.
func (b *cloudBackend) getCloudStackIdentifier(stackRef backend.StackReference) (client.StackIdentifier, error) {
owner := stackRef.(cloudBackendReference).owner
var err error
if owner == "" {
owner, err = b.client.GetPulumiAccountName(context.Background())
if err != nil {
return client.ProjectIdentifier{}, err
return client.StackIdentifier{}, err
}
}
// Otherwise, we are on the new plan.
return client.ProjectIdentifier{
Owner: owner,
Project: string(proj.Name),
}, nil
}
// getCloudStackIdentifier returns information about the given stack in the current repository and project, based on
// the current working directory.
func (b *cloudBackend) getCloudStackIdentifier(stackRef backend.StackReference) (client.StackIdentifier, error) {
project, err := b.getCloudProjectIdentifier(ownerFromRef(stackRef))
if err != nil {
return client.StackIdentifier{}, errors.Wrap(err, "failed to detect project")
}
return client.StackIdentifier{
ProjectIdentifier: project,
Stack: string(stackRef.StackName()),
Owner: owner,
Stack: string(stackRef.StackName()),
}, nil
}

View file

@ -35,18 +35,9 @@ const (
UpdateKindImport UpdateKind = "import"
)
// ProjectIdentifier is the set of data needed to identify a Pulumi Cloud project. This the
// logical "home" of a stack on the Pulumi Cloud.
type ProjectIdentifier struct {
Owner string
Repository string
Project string
}
// StackIdentifier is the set of data needed to identify a Pulumi Cloud stack.
type StackIdentifier struct {
ProjectIdentifier
Owner string
Stack string
}

View file

@ -67,29 +67,10 @@ func (pc *Client) updateRESTCall(ctx context.Context, method, path string, query
return pulumiRESTCall(ctx, pc.apiURL, method, path, queryObj, reqObj, respObj, token, httpOptions)
}
// getProjectPath returns the API path to for the given project with the given components joined with path separators
// and appended to the project root.
func getProjectPath(project ProjectIdentifier, components ...string) string {
contract.Assertf(project.Repository != "", "need repository in ProjectIdentifier")
projectRoot := fmt.Sprintf("/api/orgs/%s/programs/%s/%s", project.Owner, project.Repository, project.Project)
return path.Join(append([]string{projectRoot}, components...)...)
}
// getStackPath returns the API path to for the given stack with the given components joined with path separators
// and appended to the stack root.
func getStackPath(stack StackIdentifier, components ...string) string {
// When stack.Repository is not empty, we are on the old pulumi init based identity plan, and we hit different REST
// endpoints.
//
// TODO(ellismg)[pulumi/pulumi#1241] Clean this up once we remove pulumi init
if stack.Repository == "" {
return path.Join(append([]string{fmt.Sprintf("/api/stacks/%s/%s", stack.Owner, stack.Stack)}, components...)...)
}
components = append([]string{"stacks", stack.Stack}, components...)
return getProjectPath(stack.ProjectIdentifier, components...)
return path.Join(append([]string{fmt.Sprintf("/api/stacks/%s/%s", stack.Owner, stack.Stack)}, components...)...)
}
// getUpdatePath returns the API path to for the given stack with the given components joined with path separators
@ -154,27 +135,19 @@ func (pc *Client) DownloadTemplate(ctx context.Context, name string) (io.ReadClo
}
// ListStacks lists all stacks for the indicated project.
func (pc *Client) ListStacks(ctx context.Context, project ProjectIdentifier,
projectFilter *tokens.PackageName) ([]apitype.Stack, error) {
func (pc *Client) ListStacks(ctx context.Context, projectFilter *tokens.PackageName) ([]apitype.Stack, error) {
// Query all stacks for the project on Pulumi.
var stacks []apitype.Stack
var queryFilter interface{}
if projectFilter != nil {
queryFilter = struct {
ProjectFilter string `url:"project"`
}{ProjectFilter: string(*projectFilter)}
}
if project.Repository != "" {
if err := pc.restCall(ctx, "GET", getProjectPath(project, "stacks"), nil, nil, &stacks); err != nil {
return nil, err
}
} else {
var queryFilter interface{}
if projectFilter != nil {
queryFilter = struct {
ProjectFilter string `url:"project"`
}{ProjectFilter: project.Project}
}
if err := pc.restCall(ctx, "GET", "/api/user/stacks", queryFilter, nil, &stacks); err != nil {
return nil, err
}
if err := pc.restCall(ctx, "GET", "/api/user/stacks", queryFilter, nil, &stacks); err != nil {
return nil, err
}
return stacks, nil
@ -223,38 +196,29 @@ func (pc *Client) GetStack(ctx context.Context, stackID StackIdentifier) (apityp
// CreateStack creates a stack with the given cloud and stack name in the scope of the indicated project.
func (pc *Client) CreateStack(
ctx context.Context, project ProjectIdentifier, cloudName string, stackName string,
ctx context.Context, stackID StackIdentifier, cloudName string,
tags map[apitype.StackTagName]string) (apitype.Stack, error) {
// Validate names and tags.
if err := backend.ValidateStackProperties(stackName, tags); err != nil {
if err := backend.ValidateStackProperties(stackID.Stack, tags); err != nil {
return apitype.Stack{}, errors.Wrap(err, "validating stack properties")
}
stack := apitype.Stack{
CloudName: cloudName,
StackName: tokens.QName(stackName),
OrgName: project.Owner,
RepoName: project.Repository,
ProjectName: project.Project,
Tags: tags,
CloudName: cloudName,
StackName: tokens.QName(stackID.Stack),
OrgName: stackID.Owner,
Tags: tags,
}
createStackReq := apitype.CreateStackRequest{
CloudName: cloudName,
StackName: stackName,
StackName: stackID.Stack,
Tags: tags,
}
var createStackResp apitype.CreateStackResponseByName
if project.Repository != "" {
if err := pc.restCall(
ctx, "POST", getProjectPath(project, "stacks"), nil, &createStackReq, &createStackResp); err != nil {
return apitype.Stack{}, err
}
} else {
if err := pc.restCall(
ctx, "POST", fmt.Sprintf("/api/stacks/%s", project.Owner), nil, &createStackReq, &createStackResp); err != nil {
return apitype.Stack{}, err
}
if err := pc.restCall(
ctx, "POST", fmt.Sprintf("/api/stacks/%s", stackID.Owner), nil, &createStackReq, &createStackResp); err != nil {
return apitype.Stack{}, err
}
stack.CloudName = createStackResp.CloudName

View file

@ -4,6 +4,7 @@ package backend
import (
"context"
"path/filepath"
"regexp"
"github.com/pkg/errors"
@ -13,6 +14,7 @@ import (
"github.com/pulumi/pulumi/pkg/operations"
"github.com/pulumi/pulumi/pkg/resource/config"
"github.com/pulumi/pulumi/pkg/resource/deploy"
"github.com/pulumi/pulumi/pkg/util/gitutil"
"github.com/pulumi/pulumi/pkg/workspace"
)
@ -105,17 +107,6 @@ func ImportStackDeployment(ctx context.Context, s Stack, deployment *apitype.Unt
func GetStackTags() (map[apitype.StackTagName]string, error) {
tags := make(map[apitype.StackTagName]string)
// Tags based on the workspace's repository.
w, err := workspace.New()
if err != nil {
return nil, err
}
repo := w.Repository()
if repo != nil {
tags[apitype.GitHubOwnerNameTag] = repo.Owner
tags[apitype.GitHubRepositoryNameTag] = repo.Name
}
// Tags based on Pulumi.yaml.
projPath, err := workspace.DetectProjectPath()
if err != nil {
@ -131,6 +122,12 @@ func GetStackTags() (map[apitype.StackTagName]string, error) {
if proj.Description != nil {
tags[apitype.ProjectDescriptionTag] = *proj.Description
}
if owner, repo, err := gitutil.GetGitHubProjectForOrigin(filepath.Dir(projPath)); err == nil {
tags[apitype.GitHubOwnerNameTag] = owner
tags[apitype.GitHubRepositoryNameTag] = repo
}
}
return tags, nil

View file

@ -1,7 +1,6 @@
package integration
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
@ -25,29 +24,6 @@ func CreateBasicPulumiRepo(e *testing.Environment) {
assert.NoError(e, err, "writing %s file", filePath)
}
// GetRepository returns the contents of the workspace's repository settings file. Assumes the
// bookkeeping dir (.pulumi) is in the CWD. Any IO errors fails the test.
func GetRepository(e *testing.Environment) workspace.Repository {
relativePathToRepoFile := fmt.Sprintf("%s/%s", workspace.BookkeepingDir, workspace.RepoFile)
if !e.PathExists(relativePathToRepoFile) {
e.Fatalf("did not find .pulumi/settings.json")
}
path := path.Join(e.CWD, relativePathToRepoFile)
contents, err := ioutil.ReadFile(path)
if err != nil {
e.Fatalf("error reading %s: %v", workspace.RepoFile, err)
}
var repo workspace.Repository
err = json.Unmarshal(contents, &repo)
if err != nil {
e.Fatalf("error unmarshalling JSON: %v", err)
}
return repo
}
// GetStacks returns the list of stacks and current stack by scraping `pulumi stack ls`.
// Assumes .pulumi is in the current working directory. Fails the test on IO errors.
func GetStacks(e *testing.Environment) ([]string, *string) {

86
pkg/util/gitutil/git.go Normal file
View file

@ -0,0 +1,86 @@
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
package gitutil
import (
"fmt"
"path"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/util/fsutil"
git "gopkg.in/src-d/go-git.v4"
)
// GetGitRepository returns the git repository by walking up from the provided directory.
// If no repository is found, will return (nil, nil).
func GetGitRepository(dir string) (*git.Repository, error) {
gitRoot, err := fsutil.WalkUp(dir, func(s string) bool { return filepath.Base(s) == ".git" }, nil)
if err != nil {
return nil, errors.Wrapf(err, "searching for git repository from %v", dir)
}
if gitRoot == "" {
return nil, nil
}
// Open the git repo in the .git folder's parent, not the .git folder itself.
repo, err := git.PlainOpen(path.Join(gitRoot, ".."))
if err == git.ErrRepositoryNotExists {
return nil, nil
}
if err != nil {
return nil, errors.Wrap(err, "reading git repository")
}
return repo, nil
}
// GetGitHubProjectForOrigin returns the GitHub login, and GitHub repo name if the "origin" remote is
// a GitHub URL.
func GetGitHubProjectForOrigin(dir string) (string, string, error) {
repo, err := GetGitRepository(dir)
if repo == nil {
return "", "", fmt.Errorf("no git repository found from %v", dir)
}
if err != nil {
return "", "", err
}
return GetGitHubProjectForOriginByRepo(repo)
}
// GetGitHubProjectForOriginByRepo returns the GitHub login, and GitHub repo name if the "origin" remote is
// a GitHub URL.
func GetGitHubProjectForOriginByRepo(repo *git.Repository) (string, string, error) {
remote, err := repo.Remote("origin")
if err != nil {
return "", "", errors.Wrap(err, "could not read origin information")
}
remoteURL := ""
if len(remote.Config().URLs) > 0 {
remoteURL = remote.Config().URLs[0]
}
project := ""
const GitHubSSHPrefix = "git@github.com:"
const GitHubHTTPSPrefix = "https://github.com/"
const GitHubRepositorySuffix = ".git"
if strings.HasPrefix(remoteURL, GitHubSSHPrefix) {
project = trimGitRemoteURL(remoteURL, GitHubSSHPrefix, GitHubRepositorySuffix)
} else if strings.HasPrefix(remoteURL, GitHubHTTPSPrefix) {
project = trimGitRemoteURL(remoteURL, GitHubHTTPSPrefix, GitHubRepositorySuffix)
}
split := strings.Split(project, "/")
if len(split) != 2 {
return "", "", errors.Errorf("could not detect GitHub project from url: %v", remote)
}
return split[0], split[1], nil
}
func trimGitRemoteURL(url string, prefix string, suffix string) string {
return strings.TrimSuffix(strings.TrimPrefix(url, prefix), suffix)
}

View file

@ -63,7 +63,7 @@ func DetectProjectStackPath(stackName tokens.QName) (string, error) {
// hierarchy. If no project is found, an empty path is returned.
func DetectProjectPathFrom(path string) (string, error) {
return fsutil.WalkUp(path, isProject, func(s string) bool {
return !isRepositoryFolder(filepath.Join(s, BookkeepingDir))
return true
})
}
@ -115,22 +115,6 @@ func SaveProjectStack(stackName tokens.QName, stack *ProjectStack) error {
return stack.Save(path)
}
func isGitFolder(path string) bool {
info, err := os.Stat(path)
return err == nil && info.IsDir() && info.Name() == GitDir
}
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 {

View file

@ -1,90 +0,0 @@
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
package workspace
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/util/contract"
"github.com/pulumi/pulumi/pkg/util/fsutil"
)
type Repository struct {
Owner string `json:"owner" yaml:"owner"` // the owner of this repository
Name string `json:"name" yaml:"name"` // the name of the repository
root string // storage location
}
func (r *Repository) Save() error {
contract.Requiref(r.root != "", "r", "needs non empty root")
b, err := json.MarshalIndent(r, "", " ")
if err != nil {
return err
}
// nolint: gas
err = os.MkdirAll(r.root, 0755)
if err != nil {
return err
}
return ioutil.WriteFile(filepath.Join(r.root, RepoFile), b, 0644)
}
func NewRepository(root string) *Repository {
return &Repository{root: getDotPulumiDirectoryPath(root)}
}
var ErrNoRepository = errors.New("no repository detected; did you forget to run 'pulumi init'?")
func GetRepository(root string) (*Repository, error) {
dotPulumiPath := getDotPulumiDirectoryPath(root)
repofilePath := filepath.Join(dotPulumiPath, RepoFile)
b, err := ioutil.ReadFile(repofilePath)
if os.IsNotExist(err) {
return nil, ErrNoRepository
} else if err != nil {
return nil, err
}
var repo Repository
err = json.Unmarshal(b, &repo)
if err != nil {
return nil, err
}
if repo.Owner == "" {
return nil, errors.New("invalid repo.json file, missing name property")
}
if repo.Name == "" {
return nil, errors.New("invalid repo.json file, missing name property")
}
repo.root = dotPulumiPath
return &repo, nil
}
func getDotPulumiDirectoryPath(dir string) string {
// First, let's look to see if there's an existing .pulumi folder
dotpulumipath, _ := fsutil.WalkUp(dir, isRepositoryFolder, nil)
if dotpulumipath != "" {
return dotpulumipath
}
// If there's a .git folder, put .pulumi there
dotgitpath, _ := fsutil.WalkUp(dir, isGitFolder, nil)
if dotgitpath != "" {
return filepath.Join(filepath.Dir(dotgitpath), ".pulumi")
}
return filepath.Join(dir, ".pulumi")
}

View file

@ -21,16 +21,14 @@ import (
// W offers functionality for interacting with Pulumi workspaces.
type W interface {
Settings() *Settings // returns a mutable pointer to the optional workspace settings info.
Repository() *Repository // (optional) returns the repository this project belongs to.
Save() error // saves any modifications to the workspace.
Settings() *Settings // returns a mutable pointer to the optional workspace settings info.
Save() error // saves any modifications to the workspace.
}
type projectWorkspace struct {
name tokens.PackageName // the package this workspace is associated with.
project string // the path to the Pulumi.[yaml|json] file for this project.
settings *Settings // settings for this workspace.
repo *Repository // the repo this workspace is associated with.
}
var cache = make(map[string]W)
@ -75,13 +73,6 @@ func NewFrom(dir string) (W, error) {
return w, nil
}
repo, err := GetRepository(dir)
if err == ErrNoRepository {
repo = nil
} else if err != nil {
return nil, err
}
path, err := DetectProjectPathFrom(dir)
if err != nil {
return nil, err
@ -97,7 +88,6 @@ func NewFrom(dir string) (W, error) {
w := &projectWorkspace{
name: proj.Name,
project: path,
repo: repo,
}
err = w.readSettings()
@ -117,10 +107,6 @@ func (pw *projectWorkspace) Settings() *Settings {
return pw.settings
}
func (pw *projectWorkspace) Repository() *Repository {
return pw.repo
}
func (pw *projectWorkspace) Save() error {
// let's remove all the empty entries from the config array
for k, v := range pw.settings.ConfigDeprecated {

View file

@ -1,100 +0,0 @@
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
package tests
import (
"os"
"path"
"testing"
ptesting "github.com/pulumi/pulumi/pkg/testing"
"github.com/pulumi/pulumi/pkg/testing/integration"
"github.com/pulumi/pulumi/pkg/workspace"
"github.com/stretchr/testify/assert"
)
func TestPulumiInit(t *testing.T) {
t.Run("SanityTest", func(t *testing.T) {
e := ptesting.NewEnvironment(t)
defer func() {
if !t.Failed() {
e.DeleteEnvironment()
}
}()
// With a .git folder in the test root, `pulumi init` sets up shop there.
const dirName = workspace.BookkeepingDir
e.RunCommand("git", "init")
assert.False(t, e.PathExists(dirName), "expecting no %s folder yet", dirName)
e.RunCommand("pulumi", "init")
assert.True(t, e.PathExists(dirName), "expecting %s folder to be created", dirName)
})
t.Run("WalkUpToGitFolder", func(t *testing.T) {
e := ptesting.NewEnvironment(t)
defer func() {
if !t.Failed() {
e.DeleteEnvironment()
}
}()
// Create a git repo in the root.
e.RunCommand("git", "init")
assert.True(t, e.PathExists(".git"), "expecting .git folder")
// Create a subdirectory and CD into it.,
subdir := path.Join(e.RootPath, "/foo/bar/baz/")
err := os.MkdirAll(subdir, os.ModePerm)
assert.NoError(t, err, "error creating subdirectory")
e.CWD = subdir
// Confirm we are in the new location (no .git folder found.)
assert.False(t, e.PathExists(".git"), "expecting no .git folder (in new dir)")
// pulumi init won't create the folder here, but rather along side .git.
const dirName = workspace.BookkeepingDir
assert.False(t, e.PathExists(dirName), "expecting no %s folder", dirName)
e.RunCommand("pulumi", "init")
assert.False(t, e.PathExists(dirName), "expecting no %s folder. still.", dirName)
e.CWD = e.RootPath
assert.True(t, e.PathExists(dirName), "expecting %s folder to exist (next to .git)", dirName)
})
t.Run("DefaultRepositoryInfo", func(t *testing.T) {
e := ptesting.NewEnvironment(t)
defer func() {
if !t.Failed() {
e.DeleteEnvironment()
}
}()
e.RunCommand("git", "init")
e.RunCommand("pulumi", "init")
// Defaults
repo := integration.GetRepository(e)
testRootName := path.Base(e.RootPath)
assert.Equal(t, os.Getenv("USER"), repo.Owner)
assert.Equal(t, testRootName, repo.Name)
})
t.Run("ReadRemoteInfo", func(t *testing.T) {
e := ptesting.NewEnvironment(t)
defer func() {
if !t.Failed() {
e.DeleteEnvironment()
}
}()
e.RunCommand("git", "init")
e.RunCommand("git", "remote", "add", "not-origin", "git@github.com:moolumi/pasture.git")
e.RunCommand("git", "remote", "add", "origin", "git@github.com:pulumi/pulumi-cloud.git")
e.RunCommand("pulumi", "init")
// We pick up the settings from "origin", not any other remote name.
repo := integration.GetRepository(e)
assert.Equal(t, "pulumi", repo.Owner)
assert.Equal(t, "pulumi-cloud", repo.Name)
})
}

View file

@ -100,7 +100,6 @@ func TestStackTagValidation(t *testing.T) {
}
}()
e.RunCommand("git", "init")
e.RunCommand("pulumi", "init")
e.ImportDirectory("stack_project_name")
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())
@ -120,7 +119,6 @@ func TestStackTagValidation(t *testing.T) {
}
}()
e.RunCommand("git", "init")
e.RunCommand("pulumi", "init")
e.ImportDirectory("stack_project_name")
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())
@ -145,7 +143,6 @@ func TestStackTagValidation(t *testing.T) {
}
}()
e.RunCommand("git", "init")
e.RunCommand("pulumi", "init")
e.ImportDirectory("stack_project_name")
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())