-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathruby.go
More file actions
138 lines (129 loc) · 4.42 KB
/
ruby.go
File metadata and controls
138 lines (129 loc) · 4.42 KB
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package buildpack
import (
"context"
"fmt"
"strings"
"github.com/yourbase/yb"
"github.com/yourbase/yb/internal/biome"
"github.com/yourbase/yb/internal/ybdata"
"zombiezen.com/go/log"
)
// TODO: Install libssl-dev (or equivalent / warn) and zlib-dev based on platform
func installRuby(ctx context.Context, sys Sys, spec yb.BuildpackSpec) (biome.Environment, error) {
rbenvDir := sys.Biome.JoinPath(sys.Biome.Dirs().Tools, "rbenv")
rubyDir := sys.Biome.JoinPath(rbenvDir, "versions", spec.Version())
gemsDir := sys.Biome.JoinPath(sys.Biome.Dirs().Home, "rubygems")
env := biome.Environment{
Vars: map[string]string{
"GEM_HOME": gemsDir,
},
PrependPath: []string{
sys.Biome.JoinPath(gemsDir, "bin"),
sys.Biome.JoinPath(rubyDir, "bin"),
},
}
// If directory already exists, then use it.
if _, err := biome.EvalSymlinks(ctx, sys.Biome, rubyDir); err == nil {
log.Infof(ctx, "Ruby v%s located in %s", spec.Version(), rubyDir)
return env, nil
}
// Try to download a YourBase pre-built binary.
desc := sys.Biome.Describe()
const template = "https://yourbase-build-tools.s3-us-west-2.amazonaws.com/ruby/ruby-{{ .Version }}-{{ .OS }}-{{ .Arch }}-{{ .DistroCodename }}.tar.bz2"
data := struct {
Version string
OS string
DistroCodename string
Arch string
}{
Version: spec.Version(),
OS: map[string]string{
biome.Linux: "Linux",
}[desc.OS],
Arch: map[string]string{
biome.Intel64: "x86_64",
}[desc.Arch],
}
if data.OS == "" || data.Arch == "" {
log.Debugf(ctx, "Pre-built binaries unsupported for %s/%s", desc.OS, desc.Arch)
} else {
log.Infof(ctx, "Searching for YourBase-built Ruby binary...")
var err error
data.DistroCodename, err = readLSBCodename(ctx, sys)
if err != nil {
log.Warnf(ctx, "Skipping search for pre-built binary: %v", err)
} else {
downloadURL, err := templateToString(template, data)
if err != nil {
return biome.Environment{}, fmt.Errorf("download pre-built binary: %w", err)
}
err = extract(ctx, sys, rubyDir, downloadURL, stripTopDirectory)
if err == nil {
return env, nil
}
if !ybdata.IsNotFound(err) {
return biome.Environment{}, fmt.Errorf("download pre-built binary: %w", err)
}
}
}
// Use rbenv to install Ruby.
if _, err := biome.EvalSymlinks(ctx, sys.Biome, rbenvDir); err != nil {
log.Debugf(ctx, "rbenv not found: %v", err)
log.Infof(ctx, "Installing rbenv in %s", rbenvDir)
err := extract(ctx, sys, rbenvDir, "https://github.com/rbenv/rbenv/archive/633436706fa9227b014b89390892db9e74f5d435.zip", stripTopDirectory)
if err != nil {
return biome.Environment{}, fmt.Errorf("download rbenv: %w", err)
}
}
rubyBuildDir := sys.Biome.JoinPath(rbenvDir, "plugins", "ruby-build")
if _, err := biome.EvalSymlinks(ctx, sys.Biome, rubyBuildDir); err != nil {
log.Debugf(ctx, "ruby-build not found: %v", err)
log.Infof(ctx, "Installing ruby-build plugin in %s", rubyBuildDir)
err := extract(ctx, sys, rubyBuildDir, "https://github.com/rbenv/ruby-build/archive/v20210423.zip", stripTopDirectory)
if err != nil {
return biome.Environment{}, fmt.Errorf("download ruby-build plugin: %w", err)
}
}
buildEnv := map[string]string{"RBENV_ROOT": rbenvDir}
// Special bits for older Ruby on M1
if desc.OS == biome.MacOS && desc.Arch == biome.ARM64 {
log.Debugf(ctx, "Setting RUBY_CFLAGS=-DUSE_FFI_CLOSURE_ALLOC to support Apple M1 with older Ruby versions")
buildEnv["RUBY_CFLAGS"] = "-DUSE_FFI_CLOSURE_ALLOC"
}
err := sys.Biome.Run(ctx, &biome.Invocation{
Argv: []string{"rbenv", "install", spec.Version()},
Env: biome.Environment{
Vars: buildEnv,
PrependPath: []string{
sys.Biome.JoinPath(rbenvDir, "bin"),
},
},
Stdout: sys.Stdout,
Stderr: sys.Stderr,
})
if err != nil {
return biome.Environment{}, fmt.Errorf("rbenv: %w", err)
}
return env, nil
}
func readLSBCodename(ctx context.Context, sys Sys) (string, error) {
const filename = "/etc/os-release"
const varname = "VERSION_CODENAME"
info := new(strings.Builder)
err := sys.Biome.Run(ctx, &biome.Invocation{
Argv: []string{"cat", filename},
Stdout: info,
Stderr: sys.Stderr,
})
if err != nil {
return "", fmt.Errorf("read lsb release: %w", err)
}
lines := strings.Split(info.String(), "\n")
const varPrefix = varname + "="
for _, line := range lines {
if strings.HasPrefix(line, varPrefix) {
return line[len(varPrefix):], nil
}
}
return "", fmt.Errorf("read lsb release: could not find %s in %s", varname, filename)
}