This addresses pulumi/pulumi#446: what we used to call "package" is now called "project". This has gotten more confusing over time, now that we're doing real package management. Also fixes pulumi/pulumi#426, while in here.
141 lines
4.7 KiB
Go
141 lines
4.7 KiB
Go
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
|
|
|
package workspace
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
|
|
"github.com/pulumi/pulumi/pkg/resource/config"
|
|
"github.com/pulumi/pulumi/pkg/util/contract"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/pulumi/pulumi/pkg/encoding"
|
|
"github.com/pulumi/pulumi/pkg/tokens"
|
|
)
|
|
|
|
// Analyzers is a list of analyzers to run on this project.
|
|
type Analyzers []tokens.QName
|
|
|
|
// Project is a Pulumi project manifest..
|
|
//
|
|
// We explicitly add yaml tags (instead of using the default behavior from https://github.com/ghodss/yaml which works
|
|
// in terms of the JSON tags) so we can directly marshall and unmarshall this struct using go-yaml an have the fields
|
|
// in the serialized object match the order they are defined in this struct.
|
|
//
|
|
// TODO[pulumi/pulumi#423]: use DOM based marshalling so we can roundtrip the seralized structure perfectly.
|
|
// nolint: lll
|
|
type Project struct {
|
|
Name tokens.PackageName `json:"name" yaml:"name"` // a required fully qualified name.
|
|
Runtime string `json:"runtime" yaml:"runtime"` // a required runtime that executes code.
|
|
Main string `json:"main,omitempty" yaml:"main,omitempty"` // an optional override for the main program location.
|
|
|
|
Description *string `json:"description,omitempty" yaml:"description,omitempty"` // an optional informational description.
|
|
Author *string `json:"author,omitempty" yaml:"author,omitempty"` // an optional author.
|
|
Website *string `json:"website,omitempty" yaml:"website,omitempty"` // an optional website for additional info.
|
|
License *string `json:"license,omitempty" yaml:"license,omitempty"` // an optional license governing this project's usage.
|
|
|
|
Analyzers *Analyzers `json:"analyzers,omitempty" yaml:"analyzers,omitempty"` // any analyzers enabled for this project.
|
|
|
|
EncryptionSalt string `json:"encryptionsalt,omitempty" yaml:"encryptionsalt,omitempty"` // base64 encoded encryption salt.
|
|
Context string `json:"context,omitempty" yaml:"context,omitempty"` // an optional path (combined with the on disk location of Pulumi.yaml) to control the data uploaded to the service.
|
|
NoDefaultIgnores *bool `json:"nodefaultignores,omitempty" yaml:"nodefaultignores,omitempty"` // true if we should only respect .pulumiignore when archiving
|
|
|
|
Config map[tokens.ModuleMember]config.Value `json:"config,omitempty" yaml:"config,omitempty"` // optional config (applies to all stacks).
|
|
|
|
Stacks map[tokens.QName]ProjectStack `json:"stacks,omitempty" yaml:"stacks,omitempty"` // optional stack specific information.
|
|
}
|
|
|
|
func (proj *Project) Validate() error {
|
|
if proj.Name == "" {
|
|
return errors.New("project is missing a 'name' attribute")
|
|
}
|
|
if proj.Runtime == "" {
|
|
return errors.New("project is missing a 'runtime' attribute")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (proj *Project) UseDefaultIgnores() bool {
|
|
if proj.NoDefaultIgnores == nil {
|
|
return true
|
|
}
|
|
|
|
return !(*proj.NoDefaultIgnores)
|
|
}
|
|
|
|
// Save writes a project defitiniton to a file.
|
|
func (proj *Project) Save(path string) error {
|
|
contract.Require(path != "", "path")
|
|
contract.Require(proj != nil, "proj")
|
|
contract.Requiref(proj.Validate() == nil, "proj", "Validate()")
|
|
|
|
for name, info := range proj.Stacks {
|
|
if info.IsEmpty() {
|
|
delete(proj.Stacks, name)
|
|
}
|
|
}
|
|
|
|
m, err := marshallerForPath(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
b, err := m.Marshal(proj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return ioutil.WriteFile(path, b, 0644)
|
|
}
|
|
|
|
// ProjectStack holds stack specific information about a project.
|
|
// nolint: lll
|
|
type ProjectStack struct {
|
|
EncryptionSalt string `json:"encryptionsalt,omitempty" yaml:"encryptionsalt,omitempty"` // base64 encoded encryption salt.
|
|
Config config.Map `json:"config,omitempty" yaml:"config,omitempty"` // optional config.
|
|
}
|
|
|
|
// IsEmpty returns True if this object contains no information (i.e. all members have their zero values)
|
|
func (s *ProjectStack) IsEmpty() bool {
|
|
return len(s.Config) == 0 && s.EncryptionSalt == ""
|
|
}
|
|
|
|
// LoadProject reads a project definition from a file.
|
|
func LoadProject(path string) (*Project, error) {
|
|
contract.Require(path != "", "proj")
|
|
|
|
m, err := marshallerForPath(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var proj Project
|
|
err = m.Unmarshal(b, &proj)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = proj.Validate()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &proj, err
|
|
}
|
|
|
|
func marshallerForPath(path string) (encoding.Marshaler, error) {
|
|
ext := filepath.Ext(path)
|
|
m, has := encoding.Marshalers[ext]
|
|
if !has {
|
|
return nil, errors.Errorf("no marshaler found for file format '%v'", ext)
|
|
}
|
|
|
|
return m, nil
|
|
}
|