Skip to content

Commit

Permalink
feat: support --env flag (#1046)
Browse files Browse the repository at this point in the history
* feat: support --env flag

* refactor: make common logic reusable

* doc(compute/build): fix annotation

* tests(compute): add flag divergence tests
  • Loading branch information
Integralist authored Oct 18, 2023
1 parent 0f33513 commit ca0f97c
Show file tree
Hide file tree
Showing 18 changed files with 659 additions and 308 deletions.
1 change: 1 addition & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Release Process

1. Merge all PRs intended for the release.
1. Ensure any relevant `FIXME` notes in the code are addressed (e.g. `FIXME: remove this feature before next major release`).
1. Rebase latest remote main branch locally (`git pull --rebase origin main`).
1. Ensure all analysis checks and tests are passing (`time TEST_COMPUTE_INIT=1 TEST_COMPUTE_BUILD=1 TEST_COMPUTE_DEPLOY=1 make all`).
1. Ensure goreleaser builds locally (`make release GORELEASER_ARGS="--skip-validate --skip-post-hooks --clean"`).
Expand Down
14 changes: 7 additions & 7 deletions pkg/app/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,16 @@ func defineCommands(
backendList := backend.NewListCommand(backendCmdRoot.CmdClause, g, m)
backendUpdate := backend.NewUpdateCommand(backendCmdRoot.CmdClause, g, m)
computeCmdRoot := compute.NewRootCommand(app, g)
computeBuild := compute.NewBuildCommand(computeCmdRoot.CmdClause, g, m)
computeDeploy := compute.NewDeployCommand(computeCmdRoot.CmdClause, g, m)
computeHashFiles := compute.NewHashFilesCommand(computeCmdRoot.CmdClause, g, computeBuild, m)
computeHashsum := compute.NewHashsumCommand(computeCmdRoot.CmdClause, g, computeBuild, m)
computeBuild := compute.NewBuildCommand(computeCmdRoot.CmdClause, g)
computeDeploy := compute.NewDeployCommand(computeCmdRoot.CmdClause, g)
computeHashFiles := compute.NewHashFilesCommand(computeCmdRoot.CmdClause, g, computeBuild)
computeHashsum := compute.NewHashsumCommand(computeCmdRoot.CmdClause, g, computeBuild)
computeInit := compute.NewInitCommand(computeCmdRoot.CmdClause, g, m)
computePack := compute.NewPackCommand(computeCmdRoot.CmdClause, g, m)
computePublish := compute.NewPublishCommand(computeCmdRoot.CmdClause, g, computeBuild, computeDeploy, m)
computeServe := compute.NewServeCommand(computeCmdRoot.CmdClause, g, computeBuild, opts.Versioners.Viceroy, m)
computePublish := compute.NewPublishCommand(computeCmdRoot.CmdClause, g, computeBuild, computeDeploy)
computeServe := compute.NewServeCommand(computeCmdRoot.CmdClause, g, computeBuild, opts.Versioners.Viceroy)
computeUpdate := compute.NewUpdateCommand(computeCmdRoot.CmdClause, g, m)
computeValidate := compute.NewValidateCommand(computeCmdRoot.CmdClause, g, m)
computeValidate := compute.NewValidateCommand(computeCmdRoot.CmdClause, g)
configCmdRoot := config.NewRootCommand(app, g)
configstoreCmdRoot := configstore.NewRootCommand(app, g)
configstoreCreate := configstore.NewCreateCommand(configstoreCmdRoot.CmdClause, g, m)
Expand Down
122 changes: 91 additions & 31 deletions pkg/commands/compute/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/kennygrant/sanitize"
"github.com/mholt/archiver/v3"
Expand All @@ -26,11 +27,12 @@ const IgnoreFilePath = ".fastlyignore"

// CustomPostScriptMessage is the message displayed to a user when there is
// either a post_init or post_build script defined.
const CustomPostScriptMessage = "This project has a custom post_%s script defined in the fastly.toml manifest"
const CustomPostScriptMessage = "This project has a custom post_%s script defined in the %s manifest"

// Flags represents the flags defined for the command.
type Flags struct {
Dir string
Env string
IncludeSrc bool
Lang string
PackageName string
Expand All @@ -43,20 +45,19 @@ type BuildCommand struct {

// NOTE: these are public so that the "serve" and "publish" composite
// commands can set the values appropriately before calling Exec().
Flags Flags
Manifest manifest.Data
Flags Flags
}

// NewBuildCommand returns a usable command registered under the parent.
func NewBuildCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *BuildCommand {
func NewBuildCommand(parent cmd.Registerer, g *global.Data) *BuildCommand {
var c BuildCommand
c.Globals = g
c.Manifest = m // TODO: Stop passing a non-mutable 'copy' in any commands.
c.CmdClause = parent.Command("build", "Build a Compute package locally")

// NOTE: when updating these flags, be sure to update the composite commands:
// `compute publish` and `compute serve`.
c.CmdClause.Flag("dir", "Project directory to build (default: current directory)").Short('C').StringVar(&c.Flags.Dir)
c.CmdClause.Flag("env", "The manifest environment config to use (e.g. 'stage' will attempt to read 'fastly.stage.toml')").StringVar(&c.Flags.Env)
c.CmdClause.Flag("include-source", "Include source code in built package").BoolVar(&c.Flags.IncludeSrc)
c.CmdClause.Flag("language", "Language type").StringVar(&c.Flags.Lang)
c.CmdClause.Flag("package-name", "Package name").StringVar(&c.Flags.PackageName)
Expand All @@ -69,28 +70,32 @@ func NewBuildCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Bu
func (c *BuildCommand) Exec(in io.Reader, out io.Writer) (err error) {
// We'll restore this at the end to print a final successful build output.
originalOut := out

if c.Globals.Flags.Quiet {
out = io.Discard
}

var projectDir string
if c.Flags.Dir != "" {
projectDir, err = filepath.Abs(c.Flags.Dir)
if err != nil {
return fmt.Errorf("failed to construct absolute path to directory '%s': %w", c.Flags.Dir, err)
}
wd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current working directory: %w", err)
}
if err := os.Chdir(projectDir); err != nil {
return fmt.Errorf("failed to change working directory to '%s': %w", projectDir, err)
manifestFilename := EnvironmentManifest(c.Flags.Env)
if c.Flags.Env != "" {
if c.Globals.Verbose() {
text.Info(out, EnvManifestMsg, manifestFilename, manifest.Filename)
}
defer os.Chdir(wd)
}
wd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current working directory: %w", err)
}
defer os.Chdir(wd)
manifestPath := filepath.Join(wd, manifestFilename)

projectDir, err := ChangeProjectDirectory(c.Flags.Dir)
if err != nil {
return err
}
if projectDir != "" {
if c.Globals.Verbose() {
text.Info(out, "Changed project directory to '%s'\n\n", projectDir)
text.Info(out, ProjectDirMsg, projectDir)
}
manifestPath = filepath.Join(projectDir, manifestFilename)
}

spinner, err := text.NewSpinner(out)
Expand All @@ -104,11 +109,11 @@ func (c *BuildCommand) Exec(in io.Reader, out io.Writer) (err error) {
}
}(c.Globals.ErrLog)

err = spinner.Process("Verifying fastly.toml", func(_ *text.SpinnerWrapper) error {
if projectDir == "" {
err = c.Globals.Manifest.File.ReadError()
err = spinner.Process(fmt.Sprintf("Verifying %s", manifestFilename), func(_ *text.SpinnerWrapper) error {
if projectDir != "" || c.Flags.Env != "" {
err = c.Globals.Manifest.File.Read(manifestPath)
} else {
err = c.Globals.Manifest.File.Read(filepath.Join(projectDir, manifest.Filename))
err = c.Globals.Manifest.File.ReadError()
}
if err != nil {
if errors.Is(err, os.ErrNotExist) {
Expand All @@ -125,7 +130,7 @@ func (c *BuildCommand) Exec(in io.Reader, out io.Writer) (err error) {

var pkgName string
err = spinner.Process("Identifying package name", func(_ *text.SpinnerWrapper) error {
pkgName, err = packageName(c)
pkgName, err = c.PackageName(manifestFilename)
if err != nil {
return err
}
Expand All @@ -147,7 +152,7 @@ func (c *BuildCommand) Exec(in io.Reader, out io.Writer) (err error) {
return err
}

language, err := language(toolchain, c, in, out, spinner)
language, err := language(toolchain, manifestFilename, c, in, out, spinner)
if err != nil {
return err
}
Expand All @@ -167,7 +172,57 @@ func (c *BuildCommand) Exec(in io.Reader, out io.Writer) (err error) {
dest := filepath.Join("pkg", fmt.Sprintf("%s.tar.gz", pkgName))

err = spinner.Process("Creating package archive", func(_ *text.SpinnerWrapper) error {
// NOTE: The minimum package requirement is `fastly.toml` and `main.wasm`.
// IMPORTANT: The minimum package requirement is `fastly.toml` and `main.wasm`.
//
// The Fastly platform will reject a package that doesn't have a manifest
// named exactly fastly.toml which means if the user is building and
// deploying a package with an environment manifest (e.g. fastly.stage.toml)
// then we need to:
//
// 1. Rename any existing fastly.toml to fastly.toml.backup.<TIMESTAMP>
// 2. Make a temp copy of the environment manifest and name it fastly.toml
// 3. Remove the newly created fastly.toml once the packaging is done
// 4. Rename the fastly.toml.backup back to fastly.toml
if c.Flags.Env != "" {
// 1. Rename any existing fastly.toml to fastly.toml.backup.<TIMESTAMP>
//
// For example, the user is trying to deploy a fastly.stage.toml rather
// than the standard fastly.toml manifest.
if _, err := os.Stat(manifest.Filename); err == nil {
backup := fmt.Sprintf("%s.backup.%d", manifest.Filename, time.Now().Unix())
if err := os.Rename(manifest.Filename, backup); err != nil {
return fmt.Errorf("failed to backup primary manifest file: %w", err)
}
defer func() {
// 4. Rename the fastly.toml.backup back to fastly.toml
if err = os.Rename(backup, manifest.Filename); err != nil {
text.Error(out, err.Error())
}
}()
} else {
// 3. Remove the newly created fastly.toml once the packaging is done
//
// If there wasn't an existing fastly.toml because the user only wants
// to work with environment manifests (e.g. fastly.stage.toml and
// fastly.production.toml) then we should remove the fastly.toml that we
// created just for the packaging process (see step 2. below).
defer func() {
if err = os.Remove(manifest.Filename); err != nil {
text.Error(out, err.Error())
}
}()
}
// 2. Make a temp copy of the environment manifest and name it fastly.toml
//
// If there was no existing fastly.toml then this step will create one, so
// we need to make sure we remove it after packaging has finished so as to
// not confuse the user with a fastly.toml that has suddenly appeared (see
// step 3. above).
if err := filesystem.CopyFile(manifestFilename, manifest.Filename); err != nil {
return fmt.Errorf("failed to copy environment manifest file: %w", err)
}
}

files := []string{
manifest.Filename,
"bin/main.wasm",
Expand Down Expand Up @@ -236,9 +291,9 @@ func (c *BuildCommand) includeSourceCode(files []string, srcDir string) ([]strin
return files, nil
}

// packageName acquires the package name from either a flag or manifest.
// PackageName acquires the package name from either a flag or manifest.
// Additionally it will sanitize the name.
func packageName(c *BuildCommand) (string, error) {
func (c *BuildCommand) PackageName(manifestFilename string) (string, error) {
var name string

switch {
Expand All @@ -249,7 +304,7 @@ func packageName(c *BuildCommand) (string, error) {
default:
return "", fsterr.RemediationError{
Inner: fmt.Errorf("package name is missing"),
Remediation: "Add a name to the fastly.toml 'name' field. Reference: https://developer.fastly.com/reference/compute/fastly-toml/",
Remediation: fmt.Sprintf("Add a name to the %s 'name' field. Reference: https://developer.fastly.com/reference/compute/fastly-toml/", manifestFilename),
}
}

Expand Down Expand Up @@ -277,7 +332,7 @@ func identifyToolchain(c *BuildCommand) (string, error) {
}

// language returns a pointer to a supported language.
func language(toolchain string, c *BuildCommand, in io.Reader, out io.Writer, spinner text.Spinner) (*Language, error) {
func language(toolchain, manifestFilename string, c *BuildCommand, in io.Reader, out io.Writer, spinner text.Spinner) (*Language, error) {
var language *Language
switch toolchain {
case "assemblyscript":
Expand All @@ -289,6 +344,7 @@ func language(toolchain string, c *BuildCommand, in io.Reader, out io.Writer, sp
c.Globals,
c.Flags,
in,
manifestFilename,
out,
spinner,
),
Expand All @@ -302,6 +358,7 @@ func language(toolchain string, c *BuildCommand, in io.Reader, out io.Writer, sp
c.Globals,
c.Flags,
in,
manifestFilename,
out,
spinner,
),
Expand All @@ -315,6 +372,7 @@ func language(toolchain string, c *BuildCommand, in io.Reader, out io.Writer, sp
c.Globals,
c.Flags,
in,
manifestFilename,
out,
spinner,
),
Expand All @@ -328,6 +386,7 @@ func language(toolchain string, c *BuildCommand, in io.Reader, out io.Writer, sp
c.Globals,
c.Flags,
in,
manifestFilename,
out,
spinner,
),
Expand All @@ -340,6 +399,7 @@ func language(toolchain string, c *BuildCommand, in io.Reader, out io.Writer, sp
c.Globals,
c.Flags,
in,
manifestFilename,
out,
spinner,
),
Expand Down
Loading

0 comments on commit ca0f97c

Please sign in to comment.