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:
parent
b5efe40f04
commit
0732b05c5d
72
cmd/init.go
72
cmd/init.go
|
@ -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
|
||||
}
|
|
@ -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())
|
||||
|
|
94
cmd/util.go
94
cmd/util.go
|
@ -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 {
|
||||
|
|
|
@ -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,58 +1044,21 @@ 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,
|
||||
Owner: owner,
|
||||
Stack: string(stackRef.StackName()),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -67,31 +67,12 @@ 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...)
|
||||
}
|
||||
|
||||
// getUpdatePath returns the API path to for the given stack with the given components joined with path separators
|
||||
// and appended to the update root.
|
||||
func getUpdatePath(update UpdateIdentifier, components ...string) string {
|
||||
|
@ -154,28 +135,20 @@ 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
|
||||
|
||||
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}
|
||||
}{ProjectFilter: string(*projectFilter)}
|
||||
}
|
||||
|
||||
if err := pc.restCall(ctx, "GET", "/api/user/stacks", queryFilter, nil, &stacks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return stacks, nil
|
||||
}
|
||||
|
@ -223,39 +196,30 @@ 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,
|
||||
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 {
|
||||
ctx, "POST", fmt.Sprintf("/api/stacks/%s", stackID.Owner), 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
|
||||
}
|
||||
}
|
||||
|
||||
stack.CloudName = createStackResp.CloudName
|
||||
return stack, nil
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
86
pkg/util/gitutil/git.go
Normal 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)
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -22,7 +22,6 @@ 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.
|
||||
}
|
||||
|
||||
|
@ -30,7 +29,6 @@ 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 {
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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())
|
||||
|
|
Loading…
Reference in a new issue