Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPA Provider #35

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
{
"caches": {
"node-modules": {
"directory": "/app/node_modules/.cache",
"type": "shared"
},
"npm-install": {
"directory": "/root/.npm",
"type": "shared"
}
},
"deploy": {
"inputs": [
{
"image": "ghcr.io/railwayapp/railpack-runtime-base:latest"
},
{
"include": [
"/bin/caddy"
],
"step": "packages:caddy"
},
{
"include": [
"Caddyfile"
],
"step": "caddy"
},
{
"exclude": [
"node_modules"
],
"include": [
"."
],
"step": "build"
},
{
"include": [
"."
],
"local": true
}
],
"startCommand": "caddy run --config Caddyfile --adapter caddyfile 2\u003e\u00261",
"variables": {
"CI": "true",
"NODE_ENV": "production",
"NPM_CONFIG_FUND": "false",
"NPM_CONFIG_PRODUCTION": "false",
"NPM_CONFIG_UPDATE_NOTIFIER": "false",
"YARN_PRODUCTION": "false"
}
},
"steps": [
{
"assets": {
"mise.toml": "[mise.toml]"
},
"commands": [
{
"path": "/mise/shims"
},
{
"customName": "create mise config",
"name": "mise.toml",
"path": "/etc/mise/config.toml"
},
{
"cmd": "sh -c 'mise trust -a \u0026\u0026 mise install'",
"customName": "install mise packages: node"
}
],
"inputs": [
{
"image": "ghcr.io/railwayapp/railpack-builder-base:latest"
}
],
"name": "packages:mise",
"variables": {
"MISE_CACHE_DIR": "/mise/cache",
"MISE_CONFIG_DIR": "/mise",
"MISE_DATA_DIR": "/mise",
"MISE_INSTALLS_DIR": "/mise/installs",
"MISE_SHIMS_DIR": "/mise/shims"
}
},
{
"caches": [
"npm-install"
],
"commands": [
{
"path": "/app/node_modules/.bin"
},
{
"dest": "package.json",
"src": "package.json"
},
{
"dest": "package-lock.json",
"src": "package-lock.json"
},
{
"cmd": "npm ci"
}
],
"inputs": [
{
"step": "packages:mise"
}
],
"name": "install",
"variables": {
"CI": "true",
"NODE_ENV": "production",
"NPM_CONFIG_FUND": "false",
"NPM_CONFIG_PRODUCTION": "false",
"NPM_CONFIG_UPDATE_NOTIFIER": "false",
"YARN_PRODUCTION": "false"
}
},
{
"inputs": [
{
"step": "install"
}
],
"name": "prune",
"secrets": [
"*"
]
},
{
"caches": [
"node-modules"
],
"commands": [
{
"dest": ".",
"src": "."
},
{
"cmd": "npm run build"
}
],
"inputs": [
{
"step": "install"
}
],
"name": "build",
"secrets": [
"*"
]
},
{
"commands": [
{
"cmd": "mise install-into [email protected] /bin"
},
{
"path": "/bin/caddy"
}
],
"inputs": [
{
"image": "ghcr.io/railwayapp/railpack-builder-base:latest"
}
],
"name": "packages:caddy"
},
{
"assets": {
"Caddyfile": "# global options\n{\n\tadmin off # theres no need for the admin api in railway's environment\n\tpersist_config off # storage isn't persistent anyway\n\tauto_https off # railway handles https for us, this would cause issues if left enabled\n\t# runtime logs\n\tlog {\n\t\tformat json # set runtime log format to json mode \n\t}\n\t# server options\n\tservers {\n\t\ttrusted_proxies static private_ranges 100.0.0.0/8 # trust railway's proxy\n\t}\n}\n\n# site block, listens on the $PORT environment variable, automatically assigned by railway\n:{$PORT:3000} {\n\t# access logs\n\tlog {\n\t\tformat json # set access log format to json mode\n\t}\n\n\t# health check for railway\n\trewrite /health /*\n\n\t# serve from the 'dist' folder (Vite builds into the 'dist' folder)\n\troot * dist\n\n\t# enable gzipping responses\n\tencode gzip\n\n\t# serve files from 'dist'\n\tfile_server \n\n\t# if path doesn't exist, redirect it to 'index.html' for client side routing\n\ttry_files {path} /index.html\n}\n"
},
"commands": [
{
"name": "Caddyfile",
"path": "Caddyfile"
},
{
"cmd": "caddy fmt --overwrite Caddyfile"
}
],
"inputs": [
{
"step": "packages:caddy"
}
],
"name": "caddy",
"secrets": [
"*"
]
}
]
}
74 changes: 74 additions & 0 deletions core/generate/install_bin_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package generate

import (
"fmt"

"github.com/railwayapp/railpack/core/plan"
"github.com/railwayapp/railpack/core/resolver"
)

const (
BinDir = "/bin"
)

type InstallBinStepBuilder struct {
DisplayName string
Resolver *resolver.Resolver
SupportingAptPackages []string
Package resolver.PackageRef
}

func (c *GenerateContext) NewInstallBinStepBuilder(name string) *InstallBinStepBuilder {
step := &InstallBinStepBuilder{
DisplayName: c.GetStepName(name),
Resolver: c.Resolver,
Package: resolver.PackageRef{},
}

c.Steps = append(c.Steps, step)

return step
}

func (b *InstallBinStepBuilder) Name() string {
return b.DisplayName
}

func (b *InstallBinStepBuilder) Default(name string, defaultVersion string) resolver.PackageRef {
b.Package = b.Resolver.Default(name, defaultVersion)
return b.Package
}

func (b *InstallBinStepBuilder) GetOutputPaths() []string {
return []string{b.getBinPath()}
}

func (b *InstallBinStepBuilder) Version(name resolver.PackageRef, version string, source string) {
b.Resolver.Version(name, version, source)
}

func (b *InstallBinStepBuilder) Build(options *BuildStepOptions) (*plan.Step, error) {
packageVersion := options.ResolvedPackages[b.Package.Name].ResolvedVersion
if packageVersion == nil {
return nil, fmt.Errorf("package %s not found", b.Package.Name)
}

step := plan.NewStep(b.DisplayName)

step.Inputs = []plan.Input{
plan.NewImageInput(plan.RAILPACK_BUILDER_IMAGE),
}

step.AddCommands([]plan.Command{
plan.NewExecCommand(fmt.Sprintf("mise install-into %s@%s %s", b.Package.Name, *packageVersion, BinDir)),
plan.NewPathCommand(b.getBinPath()),
})

step.Secrets = []string{}

return step, nil
}

func (b *InstallBinStepBuilder) getBinPath() string {
return fmt.Sprintf("%s/%s", BinDir, b.Package.Name)
}
37 changes: 37 additions & 0 deletions core/providers/node/Caddyfile.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# global options
{
admin off # theres no need for the admin api in railway's environment
persist_config off # storage isn't persistent anyway
auto_https off # railway handles https for us, this would cause issues if left enabled
# runtime logs
log {
format json # set runtime log format to json mode
}
# server options
servers {
trusted_proxies static private_ranges 100.0.0.0/8 # trust railway's proxy
}
}

# site block, listens on the $PORT environment variable, automatically assigned by railway
:{$PORT:3000} {
# access logs
log {
format json # set access log format to json mode
}

# health check for railway
rewrite /health /*

# serve from the 'dist' folder (Vite builds into the 'dist' folder)
root * {{.DIST_DIR}}

# enable gzipping responses
encode gzip

# serve files from 'dist'
file_server

# if path doesn't exist, redirect it to 'index.html' for client side routing
try_files {path} /index.html
}
53 changes: 39 additions & 14 deletions core/providers/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
return fmt.Errorf("package.json not loaded, did you call Initialize?")
}

isSPA := p.isVite(ctx)

miseStep := ctx.GetMiseStepBuilder()
p.InstallMisePackages(ctx, miseStep)

Expand All @@ -66,7 +68,9 @@
// Prune
prune := ctx.NewCommandStep("prune")
prune.AddInput(plan.NewStepInput(install.Name()))
p.PruneNodeDeps(ctx, prune)
if !isSPA {
p.PruneNodeDeps(ctx, prune)
}

// Build
build := ctx.NewCommandStep("build")
Expand All @@ -82,19 +86,24 @@
buildIncludeDirs = append(buildIncludeDirs, "/root/.cache")
}

ctx.Deploy.Inputs = []plan.Input{
ctx.DefaultRuntimeInput(),
plan.NewStepInput(miseStep.Name(), plan.InputOptions{
Include: miseStep.GetOutputPaths(),
}),
plan.NewStepInput(prune.Name(), plan.InputOptions{
Include: []string{"/app/node_modules"}, // we only wanted the pruned node_modules
}),
plan.NewStepInput(build.Name(), plan.InputOptions{
Include: buildIncludeDirs,
Exclude: []string{"node_modules"},
}),
plan.NewLocalInput("."),
if isSPA {
p.DeploySPA(ctx, build)

Check failure on line 90 in core/providers/node/node.go

View workflow job for this annotation

GitHub Actions / Check and Test

Error return value of `p.DeploySPA` is not checked (errcheck)
} else {
ctx.Deploy.Inputs = []plan.Input{
ctx.DefaultRuntimeInput(),
plan.NewStepInput(miseStep.Name(), plan.InputOptions{
Include: miseStep.GetOutputPaths(),
}),
plan.NewStepInput(prune.Name(), plan.InputOptions{
Include: []string{"/app/node_modules"}, // we only wanted the pruned node_modules
}),
plan.NewStepInput(build.Name(), plan.InputOptions{
Include: buildIncludeDirs,
Exclude: []string{"node_modules"},
}),
plan.NewLocalInput("."),
}

}

return nil
Expand Down Expand Up @@ -287,3 +296,19 @@

return filteredPaths, nil
}

func (p *NodeProvider) hasDependency(dependency string) bool {
if p.packageJson.Dependencies != nil {
if _, ok := p.packageJson.Dependencies[dependency]; ok {
return true
}
}

if p.packageJson.DevDependencies != nil {
if _, ok := p.packageJson.DevDependencies[dependency]; ok {
return true
}
}

return false
}
Loading
Loading