package builder import ( "context" "errors" "io" "io/fs" "os" pathspkg "path" "runtime" "slices" "github.com/rs/zerolog" "encore.dev/appruntime/exported/experiments" "encr.dev/cli/daemon/apps" "encr.dev/internal/optracker" "encr.dev/internal/version" "encr.dev/pkg/cueutil" "encr.dev/pkg/fns" "encr.dev/pkg/option" "encr.dev/pkg/paths" meta "encr.dev/proto/encore/parser/meta/v1" ) var LocalBuildTags = []string{ "encore_local", "encore_no_gcp", "encore_no_aws", "encore_no_azure", "encore_no_datadog", "encore_no_prometheus", } // DebugMode specifies how to compile the application for debugging. type DebugMode string const ( DebugModeDisabled DebugMode = "disabled" DebugModeEnabled DebugMode = "enabled" DebugModeBreak DebugMode = "break" ) // InstallMode selects which dependencies to install for TypeScript apps. type InstallMode string const ( // InstallAll installs every dependency. It is the default. InstallAll InstallMode = "all" // InstallProduction omits development dependencies, using each // package manager's equivalent of npm's --omit=dev. InstallProduction InstallMode = "production" ) type BuildInfo struct { BuildTags []string CgoEnabled bool StaticLink bool DebugMode DebugMode Environ []string GOOS, GOARCH string KeepOutput bool Revision string UncommittedChanges bool // MainPkg is the path to the existing main package to use, if any. MainPkg option.Option[paths.Pkg] // Overrides to explicitly set the GoRoot and EncoreRuntime paths. // if not set, they will be inferred from the current executable. GoRoot option.Option[paths.FS] EncoreRuntimes option.Option[paths.FS] // UseLocalJSRuntime specifies whether to override the installed Encore version // with the local JS runtime. UseLocalJSRuntime bool // Logger allows a custom logger to be used by the various phases of the builder. Logger option.Option[zerolog.Logger] // DisableSensitiveScrubbing, if true, disables scrubbing of sensitive fields. // Used for local development. DisableSensitiveScrubbing bool } func (b *BuildInfo) IsCrossBuild() bool { return b.GOOS != runtime.GOOS || b.GOARCH != runtime.GOARCH } // DefaultBuildInfo returns a BuildInfo with default values. // It can be modified afterwards. func DefaultBuildInfo() BuildInfo { return BuildInfo{ BuildTags: slices.Clone(LocalBuildTags), CgoEnabled: true, StaticLink: false, DebugMode: DebugModeDisabled, GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, KeepOutput: false, Revision: "", UncommittedChanges: false, // Use the local JS runtime if this is a development build. UseLocalJSRuntime: version.Channel == version.DevBuild, } } type PrepareParams struct { Build BuildInfo App *apps.Instance WorkingDir string Stderr option.Option[io.Writer] // InstallMode selects which dependencies to install for TypeScript apps. // The zero value means InstallAll. Ignored by the Go builder. InstallMode InstallMode } type PrepareResult struct { Data any } type ParseParams struct { Build BuildInfo App *apps.Instance Experiments *experiments.Set WorkingDir string ParseTests bool // Prepare is the result from calling Prepare(). // Required for TypeScript apps, ignored for Go apps. Prepare *PrepareResult // Optional writer to redirect stderr to. Stderr option.Option[io.Writer] } type ParseResult struct { Meta *meta.Data Data any } type CompileParams struct { Build BuildInfo App *apps.Instance Parse *ParseResult OpTracker *optracker.OpTracker Experiments *experiments.Set WorkingDir string // Override to explicitly allow the Encore version to be set. EncoreVersion option.Option[string] Environ []string } type ArtifactString string func (a ArtifactString) Join(strs ...string) ArtifactString { str := pathspkg.Join(strs...) return ArtifactString(pathspkg.Join(string(a), str)) } func (a ArtifactString) Expand(artifactDir paths.FS) string { return os.Expand(string(a), func(key string) string { if key == "ARTIFACT_DIR" { return artifactDir.ToIO() } return "" }) } type ArtifactStrings []ArtifactString func (a ArtifactStrings) Expand(artifactDir paths.FS) []string { return fns.Map(a, func(a ArtifactString) string { return a.Expand(artifactDir) }) } // CmdSpec is a specification for a command to run. // // The fields can refer to file paths within the artifact directory // using the "${ARTIFACT_DIR}" placeholder (substituted with os.ExpandEnv). // This is necessary when building docker images, as otherwise the file paths // will refer to the wrong filesystem location in the built docker image. type CmdSpec struct { // The command to execute. Can either be a filesystem path // or a path to a binary (using "${ARTIFACT_DIR}" as a placeholder). Command ArtifactStrings `json:"command"` // Additional env variables to pass in. Env ArtifactStrings `json:"env"` // PrioritizedFiles are file paths that should be prioritized when // building a streamable docker image. PrioritizedFiles ArtifactStrings `json:"prioritized_files"` } func (s *CmdSpec) Expand(artifactDir paths.FS) Cmd { return Cmd{ Command: s.Command.Expand(artifactDir), Env: s.Env.Expand(artifactDir), } } // Cmd defines a command to run. It's like CmdSpec, but uses expanded paths // instead of ArtifactStrings. A CmdSpec can be turned into a Cmd using Expand. type Cmd struct { // The command to execute, with arguments. Command []string // Additional env variables to pass in. Env []string } type CompileResult struct { OS string Arch string Outputs []BuildOutput } type BuildOutput interface { GetArtifactDir() paths.FS GetEntrypoints() []Entrypoint } type Entrypoint struct { // How to run this entrypoint. Cmd CmdSpec `json:"cmd"` // Services hosted by this entrypoint. Services []string `json:"services"` // Gateways hosted by this entrypoint. Gateways []string `json:"gateways"` // Whether this entrypoint uses the new runtime config. UseRuntimeConfigV2 bool `json:"use_runtime_config_v2"` } type GoBuildOutput struct { // The folder containing the build artifacts. // These artifacts are assumed to be relocatable. ArtifactDir paths.FS `json:"artifact_dir"` // The entrypoints that are part of this build output. Entrypoints []Entrypoint `json:"entrypoints"` } func (o *GoBuildOutput) GetArtifactDir() paths.FS { return o.ArtifactDir } func (o *GoBuildOutput) GetEntrypoints() []Entrypoint { return o.Entrypoints } type JSBuildOutput struct { // The folder containing the build artifacts. // These artifacts are assumed to be relocatable. ArtifactDir paths.FS `json:"artifact_dir"` // The entrypoints that are part of this build output. Entrypoints []Entrypoint `json:"entrypoints"` // Whether the build output uses the local runtime on the builder, // as opposed to installing a published release via e.g. 'npm install'. UsesLocalRuntime bool `json:"uses_local_runtime"` } func (o *JSBuildOutput) GetArtifactDir() paths.FS { return o.ArtifactDir } func (o *JSBuildOutput) GetEntrypoints() []Entrypoint { return o.Entrypoints } type RunTestsParams struct { Spec *TestSpecResult // WorkingDir is the directory to invoke the test command from. WorkingDir paths.FS // Stdout and Stderr are where to redirect the command output. Stdout, Stderr io.Writer } type TestSpecParams struct { Compile CompileParams // Env sets environment variables for "go test". Env []string // Args sets extra arguments for "go test". Args []string } // ErrNoTests is reported by TestSpec when there aren't any tests to run. var ErrNoTests = errors.New("no tests found") type TestSpecResult struct { Command string Args []string Environ []string // For use by the builder when invoking RunTests. BuilderData any } type GenUserFacingParams struct { Build BuildInfo App *apps.Instance Parse *ParseResult } type ServiceConfigsParams struct { Parse *ParseResult CueMeta *cueutil.Meta } type ServiceConfigsResult struct { Configs map[string]string ConfigFiles fs.FS } type Impl interface { Prepare(context.Context, PrepareParams) (*PrepareResult, error) Parse(context.Context, ParseParams) (*ParseResult, error) Compile(context.Context, CompileParams) (*CompileResult, error) TestSpec(context.Context, TestSpecParams) (*TestSpecResult, error) RunTests(context.Context, RunTestsParams) error ServiceConfigs(context.Context, ServiceConfigsParams) (*ServiceConfigsResult, error) GenUserFacing(context.Context, GenUserFacingParams) error UseNewRuntimeConfig() bool NeedsMeta() bool Close() error }