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/app/app.go

core/app/app.goBrowse 1970 files
View on GitHub
← Back to SKILL.md
package app import (	"encoding/json"	"errors"	"fmt"	"os"	"path/filepath"	"regexp"	"strings" 	"github.com/BurntSushi/toml"	"github.com/bmatcuk/doublestar/v4"	"github.com/railwayapp/railpack/internal/utils"	"gopkg.in/yaml.v2") var ErrNoFileFound = errors.New("unable to find a matching file") type App struct {	Source    string	globCache map[string][]string} func NewApp(path string) (*App, error) {	var source string 	if filepath.IsAbs(path) {		source = path	} else {		currentDir, err := os.Getwd()		if err != nil {			return nil, err		}		source, err = filepath.Abs(filepath.Join(currentDir, path))		if err != nil {			return nil, errors.New("failed to read app source directory")		}	} 	if _, err := os.Stat(source); err != nil {		if os.IsNotExist(err) {			return nil, fmt.Errorf("directory %s does not exist", source)		}		return nil, fmt.Errorf("failed to check directory %s: %w", source, err)	} 	return &App{		Source:    source,		globCache: make(map[string][]string),	}, nil} // findMatches returns a list of paths matching a glob pattern, filtered by isDirfunc (a *App) findMatches(pattern string, isDir bool) ([]string, error) {	matches, err := a.findGlob(pattern) 	if err != nil {		return nil, err	} 	var paths []string	for _, match := range matches {		fullPath := filepath.Join(a.Source, match) 		info, err := os.Stat(fullPath)		if err != nil {			continue		} 		if info.IsDir() == isDir {			paths = append(paths, match)		}	}	return paths, nil} // returns a list of file paths matching a glob patternfunc (a *App) FindFiles(pattern string) ([]string, error) {	return a.findMatches(pattern, false)} // FindDirectories returns a list of directory paths matching a glob patternfunc (a *App) FindDirectories(pattern string) ([]string, error) {	return a.findMatches(pattern, true)} // findGlob finds paths matching a glob pattern, with cachingfunc (a *App) findGlob(pattern string) ([]string, error) {	if cached, ok := a.globCache[pattern]; ok {		return cached, nil	} 	matches, err := doublestar.Glob(os.DirFS(a.Source), pattern)	if err != nil {		return nil, err	} 	a.globCache[pattern] = matches	return matches, nil} // Check if a relative file exists in the app's source directoryfunc (a *App) HasFile(path string) bool {	fullPath := filepath.Join(a.Source, path) 	_, err := os.Stat(fullPath)	return !os.IsNotExist(err)} // HasMatch checks if a path matching a glob exists (files or directories)func (a *App) HasMatch(pattern string) bool {	files, err := a.FindFiles(pattern)	if err != nil {		return false	} 	dirs, err := a.FindDirectories(pattern)	if err != nil {		return false	} 	return len(files) > 0 || len(dirs) > 0} func (a *App) FindFilesWithContent(pattern string, regex *regexp.Regexp) []string {	files, err := a.FindFiles(pattern)	if err != nil {		return nil	} 	var matches []string	for _, file := range files {		content, err := a.ReadFile(file)		if err != nil {			continue		} 		if regex.MatchString(content) {			matches = append(matches, file)		}	} 	return matches} // reads the contents of the first file that exists within the application source directory// helpful for reading config from multiple possible locations (something.js, something.ts, etc)func (a *App) ReadFirstFileOf(names ...string) (string, string, error) {	for _, name := range names {		if !a.HasFile(name) {			continue		} 		contents, err := a.ReadFile(name)		if err != nil {			return "", "", err		} 		return name, contents, nil	} 	return "", "", ErrNoFileFound} // ReadFile reads the contents of a file within the application source directoryfunc (a *App) ReadFile(name string) (string, error) {	path := filepath.Join(a.Source, name)	data, err := os.ReadFile(path)	if err != nil {		relativePath, _ := a.stripSourcePath(path)		return "", fmt.Errorf("error reading %s: %w", relativePath, err)	} 	return strings.ReplaceAll(string(data), "\r\n", "\n"), nil} // ReadJSON reads and parses a JSON filefunc (a *App) ReadJSON(name string, v any) error {	data, err := a.ReadFile(name)	if err != nil {		return err	} 	jsonBytes, err := utils.StandardizeJSON([]byte(data))	if err != nil {		return err	} 	data = string(jsonBytes) 	if err := json.Unmarshal([]byte(data), v); err != nil {		relativePath, _ := a.stripSourcePath(filepath.Join(a.Source, name))		return fmt.Errorf("error reading %s as JSON: %w", relativePath, err)	} 	return nil} // ReadYAML reads and parses a YAML filefunc (a *App) ReadYAML(name string, v any) error {	data, err := a.ReadFile(name)	if err != nil {		return err	} 	if err := yaml.Unmarshal([]byte(data), v); err != nil {		return fmt.Errorf("error reading %s as YAML: %w", name, err)	} 	return nil} func (a *App) ReadTOML(name string, v any) error {	data, err := a.ReadFile(name)	if err != nil {		return err	} 	return toml.Unmarshal([]byte(data), v)} // checks if a path is an executable filefunc (a *App) IsFileExecutable(name string) bool {	path := filepath.Join(a.Source, name)	info, err := os.Stat(path)	if err != nil {		return false	} 	if !info.Mode().IsRegular() {		return false	} 	// Check executable bit	return info.Mode()&0111 != 0} // converts an absolute path to a path relative to the app source directoryfunc (a *App) stripSourcePath(absPath string) (string, error) {	rel, err := filepath.Rel(a.Source, absPath)	if err != nil {		return "", errors.New("failed to parse source path")	}	return rel, nil}