summaryrefslogtreecommitdiff
path: root/config/node.go
blob: 5834443643fba4fa232cbf715617427eb8eae161 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package config

import (
	"path"
	"phabricator.wikimedia.org/source/blubber/build"
)

// NodeConfig holds configuration fields related to the Node environment and
// whether/how to install NPM packages.
//
type NodeConfig struct {
	Requirements []string `yaml:"requirements"`                     // install requirements from given files
	Env          string   `yaml:"env" validate:"omitempty,nodeenv"` // environment name ("production" install)
}

// Merge takes another NodeConfig and merges its fields into this one's,
// overwriting both the environment and requirements files.
//
func (nc *NodeConfig) Merge(nc2 NodeConfig) {
	if nc2.Requirements != nil {
		nc.Requirements = nc2.Requirements
	}

	if nc2.Env != "" {
		nc.Env = nc2.Env
	}
}

// InstructionsForPhase injects instructions into the build related to Node
// dependency installation and setting of the NODE_ENV, NODE_PATH, and PATH
// environment variables.
//
// PhasePreInstall
//
// Installs Node package dependencies declared in requirements files into the
// shared library directory (/opt/lib). Only production related packages are
// install if NodeConfig.Env is set to "production" in which case `npm dedupe`
// is also run. Installing dependencies during the build.PhasePreInstall phase
// allows a compiler implementation (e.g. Docker) to produce cache-efficient
// output so only changes to package.json will invalidate these steps of the
// image build.
//
// PhasePostInstall
//
// Injects build.Env instructions for NODE_ENV, NODE_PATH, and PATH, setting
// the environment according to the configuration, ensuring that packages
// installed during build.PhasePreInstall are found by Node, and that any
// installed binaries are found by shells.
//
func (nc NodeConfig) InstructionsForPhase(phase build.Phase) []build.Instruction {
	switch phase {
	case build.PhasePreInstall:
		if len(nc.Requirements) > 0 {
			npmInstall := build.RunAll{[]build.Run{
				{"cd", []string{LocalLibPrefix}},
				{"npm install", []string{}},
			}}

			if nc.Env == "production" {
				npmInstall.Runs[1].Arguments = []string{"--production"}
				npmInstall.Runs = append(npmInstall.Runs,
					build.Run{"npm dedupe", []string{}},
				)
			}

			return []build.Instruction{
				build.Copy{nc.Requirements, LocalLibPrefix},
				npmInstall,
			}
		}
	case build.PhasePostInstall:
		if nc.Env != "" || len(nc.Requirements) > 0 {
			return []build.Instruction{build.Env{map[string]string{
				"NODE_ENV":  nc.Env,
				"NODE_PATH": path.Join(LocalLibPrefix, "node_modules"),
				"PATH":      path.Join(LocalLibPrefix, "node_modules", ".bin") + ":${PATH}",
			}}}
		}
	}

	return []build.Instruction{}
}