func buildFn()

in cmd/dotnet/publish/main.go [59:161]


func buildFn(ctx *gcp.Context) error {
	proj, err := dotnet.FindProjectFile(ctx)
	if err != nil {
		return fmt.Errorf("finding project: %w", err)
	}
	ctx.Logf("Installing application dependencies.")
	pkgLayer, err := ctx.Layer("packages", gcp.BuildLayer, gcp.CacheLayer)
	if err != nil {
		return fmt.Errorf("creating layer: %w", err)
	}

	cached, err := checkCache(ctx, pkgLayer)
	if err != nil {
		return fmt.Errorf("checking cache: %w", err)
	}
	// Print cache status for testing/debugging only, `dotnet restore` reuses any existing artifacts.
	if cached {
		ctx.CacheHit(cacheTag)
	} else {
		ctx.CacheMiss(cacheTag)
	}

	// Run restore regardless of cache status because it generates files expected by publish.
	cmd := []string{"dotnet", "restore", "--packages", pkgLayer.Path, proj}
	if _, err := ctx.Exec(cmd, gcp.WithEnv("DOTNET_CLI_TELEMETRY_OPTOUT=true"), gcp.WithUserAttribution); err != nil {
		return err
	}

	binLayer, err := ctx.Layer(dotnet.PublishLayerName, gcp.BuildLayer, gcp.LaunchLayer)
	if err != nil {
		return fmt.Errorf("creating layer: %w", err)
	}

	outputDirectory := path.Join(binLayer.Path, dotnet.PublishOutputDirName)

	// The existence of a project file indicates this is not prebuilt.  Any uploaded bin folder interferes with publish.
	deleted, err := deleteFolder(ctx, path.Join(ctx.ApplicationRoot(), dotnet.PublishOutputDirName))
	if err != nil {
		return fmt.Errorf("deleting upload bin: %w", err)
	}
	if deleted {
		ctx.Warnf("A project file was uploaded, causing `dotnet publish` to be called, but the output bin folder already existed in application source.  Deleting %v.", outputDirectory)
	}

	cmd = []string{
		"dotnet",
		"publish",
		"-nologo",
		"--verbosity", "minimal",
		"--configuration", "Release",
		"--output", outputDirectory,
		"--no-restore",
		"--packages", pkgLayer.Path,
		proj,
	}

	if args := os.Getenv(env.BuildArgs); args != "" {
		// Use bash to excute the command to avoid havnig to parse the build arguments.
		// strings.Fields may be unsafe here in case some arguments have a space.
		cmd = []string{"/bin/bash", "-c", strings.Join(append(cmd, args), " ")}
	}

	if _, err := ctx.Exec(cmd, gcp.WithEnv("DOTNET_CLI_TELEMETRY_OPTOUT=true"), gcp.WithUserAttribution); err != nil {
		return err
	}

	// Set GOOGLE_ASP_NET_CORE_VERSION, so subsequent buildpacks know which runtime version to install
	runtimeVersion, err := dotnet.GetRuntimeVersion(ctx, outputDirectory)
	if err != nil {
		return gcp.InternalErrorf("getting runtime version: %v", err)
	}
	binLayer.BuildEnvironment.Default(dotnet.EnvRuntimeVersion, runtimeVersion)

	// `dotnet publish` output originally went to ctx.ApplicationRoot()/bin/.  This was moved into a
	// layer, but we create a symlink in the original location for backwards compatability.
	if err := configureBinSymlink(ctx, outputDirectory); err != nil {
		return fmt.Errorf("creating symlink: %w", err)
	}

	// Infer the entrypoint in case an explicit override was not provided.
	entrypoint := os.Getenv(env.Entrypoint)
	if entrypoint != "" {
		entrypoint = "exec " + entrypoint
	} else {
		ep, err := getEntrypoint(ctx, outputDirectory, proj)
		if err != nil {
			return fmt.Errorf("getting entrypoint: %w", err)
		}
		entrypoint = ep
		binLayer.BuildEnvironment.Default(env.Entrypoint, entrypoint)
	}
	binLayer.LaunchEnvironment.Default("DOTNET_RUNNING_IN_CONTAINER", "true")

	// Configure the entrypoint for production.
	if !devmode.Enabled(ctx) {
		ctx.AddWebProcess([]string{"/bin/bash", "-c", entrypoint})
		return nil
	}

	// Configure the entrypoint and metadata for dev mode.
	ctx.AddWebProcess([]string{"dotnet", "watch", "--project", proj, "run"})
	return nil
}