railpack

Configure and troubleshoot Railpack builds, with emphasis on RAILPACK_* environment variables, railpack.json overlays, build-plan inspection, local CLI installation and usage, and local BuildKit containers. Use for Railpack provider configuration, custom install/build/start commands, Mise or Apt packages, build or runtime variables and secrets, generated-plan debugging, BUILDKIT_HOST errors, or running Railpack from a release or source checkout.

Install
npx skills add 'https://github.com/railwayapp/railpack/tree/main/.'
Incomplete bundle · no download
main · 21a254fScanned 2026-09-17

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗

core/pretty_print.go

core/pretty_print.goBrowse 1970 files
View on GitHub
← Back to SKILL.md
package core import (	"fmt"	"io"	"slices"	"strings" 	"github.com/charmbracelet/lipgloss"	"github.com/muesli/termenv"	"github.com/railwayapp/railpack/core/logger"	"github.com/railwayapp/railpack/core/plan"	"github.com/railwayapp/railpack/core/resolver"	"github.com/railwayapp/railpack/internal/utils"	"github.com/tidwall/pretty") const (	AnsiRed           = "1"	AnsiYellow        = "3"	AnsiBlue          = "4"	AnsiMagenta       = "5"	AnsiCyan          = "6"	AnsiWhite         = "7"	AnsiGray          = "8"	AnsiBrightBlue    = "12"	AnsiBrightCyan    = "14"	AnsiBrightWhite   = "15"	AnsiBrightMagenta = "13"	AnsiDarkGray      = "238"	AnsiMediumGray    = "245") var (	highlight = lipgloss.AdaptiveColor{Light: "#874BFD", Dark: "#7D56F4"} 	headerStyle = lipgloss.NewStyle().			BorderStyle(lipgloss.RoundedBorder()).			BorderForeground(highlight).			MarginTop(1).			Padding(0, 1) 	sectionHeaderStyle = lipgloss.NewStyle().				Bold(true).				Width(10).				MarginLeft(2).				MarginTop(2).				BorderStyle(lipgloss.RoundedBorder()).				BorderForeground(lipgloss.Color(AnsiDarkGray)).				BorderBottom(true) 	packageNameStyle = lipgloss.NewStyle().				MarginLeft(2).				Foreground(lipgloss.Color(AnsiBrightMagenta)) 	versionStyle = lipgloss.NewStyle().			Foreground(lipgloss.Color(AnsiBrightCyan)) 	sourceStyle = lipgloss.NewStyle() 	separatorStyle = lipgloss.NewStyle().			Foreground(lipgloss.Color(AnsiDarkGray)).			Margin(0, 2) 	indentedStepHeaderStyle = lipgloss.NewStyle().				Foreground(lipgloss.Color(AnsiBrightMagenta)).				MarginLeft(2) 	commandPrefixStyle = lipgloss.NewStyle().				Foreground(lipgloss.Color(AnsiMediumGray)).				MarginLeft(4) 	commandStyle = lipgloss.NewStyle().			Bold(true) 	logInfoStyle = lipgloss.NewStyle().			Foreground(lipgloss.Color(AnsiBrightWhite)).			MarginLeft(2) 	logWarnStyle = lipgloss.NewStyle().			Foreground(lipgloss.Color(AnsiYellow)).			MarginLeft(2) 	logDeprecationStyle = lipgloss.NewStyle().				Foreground(lipgloss.Color(AnsiBrightMagenta)).				MarginLeft(2) 	logSuggestionStyle = lipgloss.NewStyle().				Foreground(lipgloss.Color(AnsiCyan)).				MarginLeft(2) 	logDocsLinkStyle = lipgloss.NewStyle().				Foreground(lipgloss.Color(AnsiBrightBlue)).				Underline(true) 	logErrorStyle = lipgloss.NewStyle().			Foreground(lipgloss.Color(AnsiRed)).			Bold(true).			MarginLeft(2) 	metadataStyle = lipgloss.NewStyle().			MarginLeft(2) 	metadataSeparatorStyle = lipgloss.NewStyle().				Foreground(lipgloss.Color(AnsiMediumGray)).				MarginRight(1) 	metadataValueStyle = lipgloss.NewStyle().				Bold(true) 	boxStyle = lipgloss.NewStyle().			BorderStyle(lipgloss.RoundedBorder()).			BorderForeground(highlight).			MarginTop(1).			Padding(0, 1) 	highlightedTextStyle = lipgloss.NewStyle().				Bold(true).				Foreground(lipgloss.Color(AnsiBrightCyan))) type PrintOptions struct {	Metadata bool	Version  string} func PrettyPrintBuildResult(buildResult *BuildResult, options ...PrintOptions) {	output := FormatBuildResult(buildResult, options...)	fmt.Print(output)} func PrettyPrintSectionHeader(output io.Writer, title string) {	style := sectionHeaderStyle.		MarginTop(0).		Width(max(10, lipgloss.Width(title)))	_, _ = fmt.Fprintf(output, "%s\n\n", style.Render(title))} func PrettyPrintBox(content string) {	fmt.Println(boxStyle.Render(content))} func FormatHighlight(content string) string {	return highlightedTextStyle.Render(content)} func PrettyPrintJSON(output io.Writer, content []byte) {	if lipgloss.ColorProfile() != termenv.Ascii {		content = pretty.Color(content, nil)	} 	_, _ = fmt.Fprintln(output, string(content))} func FormatBuildResult(br *BuildResult, options ...PrintOptions) string {	var opts PrintOptions	if len(options) > 0 {		opts = options[0]	}	var output strings.Builder 	formatHeader(&output, opts.Version)	formatLogs(&output, br.Logs)	formatPackages(&output, br.ResolvedPackages)	formatSteps(&output, br)	formatDeploy(&output, br)	formatMetadata(&output, br.Metadata, opts.Metadata) 	output.WriteString("\n\n")	return output.String()} func formatHeader(output *strings.Builder, version string) {	header := fmt.Sprintf("Railpack %s", version)	output.WriteString(headerStyle.Render(header))	output.WriteString("\n")} func formatLogs(output *strings.Builder, logs []logger.Msg) {	if len(logs) == 0 {		return	} 	output.WriteString("\n") 	for _, log := range logs {		msg := utils.CapitalizeFirst(log.Msg) 		switch log.Level {		case logger.Info:			output.WriteString(logInfoStyle.Render(fmt.Sprintf("↳ %s", msg)))		case logger.Warn:			output.WriteString(logWarnStyle.Render(fmt.Sprintf("⚠ %s", msg)))		case logger.Deprecation:			output.WriteString(logDeprecationStyle.Render(fmt.Sprintf("⚑ Deprecated: %s", msg)))		case logger.Suggestion:			rendered := logSuggestionStyle.Render(fmt.Sprintf("→ %s", msg))			if log.DocsPath != "" {				rendered += " " + logDocsLinkStyle.Render(logger.DocsURL(log.DocsPath))			}			output.WriteString(rendered)		case logger.Error:			lines := strings.Split(msg, "\n")			for i, line := range lines {				if i == 0 {					output.WriteString(logErrorStyle.Render(fmt.Sprintf("✖ %s", line)))				} else {					fmt.Fprintf(output, "  %s", line)				}				if i < len(lines)-1 {					output.WriteString("\n")				}			}		default:			output.WriteString(logInfoStyle.Render(fmt.Sprintf("• %s", msg)))		}		output.WriteString("\n")	}} func formatPackages(output *strings.Builder, packages map[string]*resolver.ResolvedPackage) {	if len(packages) == 0 {		return	} 	output.WriteString(sectionHeaderStyle.MarginTop(1).Render("Packages"))	output.WriteString("\n") 	nameWidth, versionWidth := 1, 1	for _, pkg := range packages {		nameWidth = max(nameWidth, len(pkg.Name))		if pkg.ResolvedVersion != nil {			versionWidth = max(versionWidth, len(*pkg.ResolvedVersion))		}	} 	localPackageNameStyle := packageNameStyle.Width(nameWidth).MaxWidth(20)	localVersionStyle := versionStyle.Width(versionWidth).MaxWidth(20)	separator := separatorStyle.Render("│") 	for _, pkg := range packages {		name := localPackageNameStyle.Render(pkg.Name) 		version := "-"		if pkg.ResolvedVersion != nil {			version = *pkg.ResolvedVersion		}		version = localVersionStyle.Render(version)		source := sourceStyle.Render(formatSource(pkg))		fmt.Fprintf(output, "%s%s%s%s%s", name, separator, version, separator, source)		output.WriteString("\n")	}} func formatSteps(output *strings.Builder, br *BuildResult) {	stepsToPrint := getStepsToPrint(br)	if len(stepsToPrint) == 0 {		return	} 	output.WriteString(sectionHeaderStyle.MarginTop(1).Render("Steps"))	output.WriteString("\n") 	for i, step := range stepsToPrint {		commands := getCommandsToPrint(step.Commands)		if len(commands) == 0 {			continue		} 		currentStepStyle := indentedStepHeaderStyle		if i > 0 {			currentStepStyle = currentStepStyle.MarginTop(1)		} 		output.WriteString(currentStepStyle.Render(fmt.Sprintf("▸ %s", step.Name)))		output.WriteString("\n") 		for _, cmd := range commands {			cmdText := cmd.Cmd			if cmd.CustomName != "" {				cmdText = cmd.CustomName			}			fmt.Fprintf(output, "%s %s", commandPrefixStyle.Render("$"), commandStyle.Render(cmdText))			output.WriteString("\n")		}	}} func formatDeploy(output *strings.Builder, br *BuildResult) {	if br.Plan != nil && br.Plan.Deploy.StartCmd != "" {		output.WriteString(sectionHeaderStyle.MarginTop(1).Render("Deploy"))		output.WriteString("\n")		fmt.Fprintf(output, "%s %s", commandPrefixStyle.Render("$"), commandStyle.Render(br.Plan.Deploy.StartCmd))	}} func formatMetadata(output *strings.Builder, metadata map[string]string, showMetadata bool) {	if !showMetadata || metadata == nil || len(metadata) == 0 {		return	} 	output.WriteString(sectionHeaderStyle.MarginTop(2).Render("Metadata"))	output.WriteString("\n") 	separator := metadataSeparatorStyle.Render(":") 	for key, value := range metadata {		output.WriteString(metadataStyle.Render(fmt.Sprintf("%s%s%s", key, separator, metadataValueStyle.Render(value))))		output.WriteString("\n")	}} func getStepsToPrint(br *BuildResult) []*plan.Step {	execSteps := []*plan.Step{}	if br.Plan == nil {		return execSteps	} 	for _, step := range br.Plan.Steps {		if !strings.HasPrefix(step.Name, "packages") && step.Commands != nil {			commands := getCommandsToPrint(step.Commands)			if len(commands) > 0 {				execSteps = append(execSteps, &step)			}		}	}	return execSteps} // TODO we should really just mark a command as skippable on the ExecCommand insteadvar skippableCommands = []string{	"mkdir -p /app/node_modules/.cache",	"mkdir -p config deps _build",} func getCommandsToPrint(commands []plan.Command) []plan.ExecCommand {	if commands == nil {		return []plan.ExecCommand{}	} 	execCommands := []plan.ExecCommand{}	for _, cmd := range commands {		if execCmd, ok := cmd.(plan.ExecCommand); ok {			if !isSkippableCommand(execCmd.Cmd) {				execCommands = append(execCommands, execCmd)			}		}	}	return execCommands} func isSkippableCommand(cmd string) bool {	return slices.Contains(skippableCommands, cmd)} func formatSource(pkg *resolver.ResolvedPackage) string {	if pkg.RequestedVersion != nil {		return fmt.Sprintf("%s (%s)", pkg.Source, *pkg.RequestedVersion)	}	return pkg.Source}