Skip to content

Commit b7a652f

Browse files
authored
Merge branch 'main' into dependabot/github_actions/docker/login-action-4
2 parents d7c79e1 + cb0001a commit b7a652f

10 files changed

Lines changed: 244 additions & 1 deletion

File tree

builders/cuda-selector/builder.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ value = "vscodium"
6666
uri = "docker://docker.io/heroku/buildpack-deb-packages:0.2.0"
6767
version = "0.2.0"
6868

69+
[[buildpacks]]
70+
uri = "../../buildpacks/homebrew"
71+
version = "0.3.1"
72+
6973
[[extensions]]
7074
uri="../../extensions/r-deps"
7175
version= "0.2.3"
@@ -101,6 +105,10 @@ value = "vscodium"
101105
id = "heroku/deb-packages"
102106
version = "0.2.0"
103107
optional = true
108+
[[order.group]]
109+
id = "renku/homebrew"
110+
version = "0.3.1."
111+
optional = true
104112
[[order.group]]
105113
id = "renku/init-scripts"
106114
version = "0.3.1"
@@ -127,6 +135,10 @@ value = "vscodium"
127135
id = "heroku/deb-packages"
128136
version = "0.2.0"
129137
optional = true
138+
[[order.group]]
139+
id = "renku/homebrew"
140+
version = "0.3.1."
141+
optional = true
130142
[[order.group]]
131143
id = "renku/init-scripts"
132144
version = "0.3.1"
@@ -154,6 +166,10 @@ value = "vscodium"
154166
id = "heroku/deb-packages"
155167
version = "0.2.0"
156168
optional = true
169+
[[order.group]]
170+
id = "renku/homebrew"
171+
version = "0.3.1."
172+
optional = true
157173
[[order.group]]
158174
id = "renku/init-scripts"
159175
version = "0.3.1"
@@ -180,6 +196,10 @@ value = "vscodium"
180196
id = "heroku/deb-packages"
181197
version = "0.2.0"
182198
optional = true
199+
[[order.group]]
200+
id = "renku/homebrew"
201+
version = "0.3.1."
202+
optional = true
183203
[[order.group]]
184204
id = "renku/init-scripts"
185205
version = "0.3.1"

builders/selector/builder.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ value = "vscodium"
6666
uri = "docker://docker.io/heroku/buildpack-deb-packages:0.2.0"
6767
version = "0.2.0"
6868

69+
[[buildpacks]]
70+
uri = "../../buildpacks/homebrew"
71+
version = "0.3.1"
72+
6973
[[extensions]]
7074
uri="../../extensions/r-deps"
7175
version= "0.2.3"
@@ -101,6 +105,10 @@ value = "vscodium"
101105
id = "heroku/deb-packages"
102106
version = "0.2.0"
103107
optional = true
108+
[[order.group]]
109+
id = "renku/homebrew"
110+
version = "0.3.1"
111+
optional = true
104112
[[order.group]]
105113
id = "renku/init-scripts"
106114
version = "0.3.1"
@@ -127,6 +135,10 @@ value = "vscodium"
127135
id = "heroku/deb-packages"
128136
version = "0.2.0"
129137
optional = true
138+
[[order.group]]
139+
id = "renku/homebrew"
140+
version = "0.3.1"
141+
optional = true
130142
[[order.group]]
131143
id = "renku/init-scripts"
132144
version = "0.3.1"
@@ -154,6 +166,10 @@ value = "vscodium"
154166
id = "heroku/deb-packages"
155167
version = "0.2.0"
156168
optional = true
169+
[[order.group]]
170+
id = "renku/homebrew"
171+
version = "0.3.1"
172+
optional = true
157173
[[order.group]]
158174
id = "renku/init-scripts"
159175
version = "0.3.1"
@@ -180,6 +196,10 @@ value = "vscodium"
180196
id = "heroku/deb-packages"
181197
version = "0.2.0"
182198
optional = true
199+
[[order.group]]
200+
id = "renku/homebrew"
201+
version = "0.3.1"
202+
optional = true
183203
[[order.group]]
184204
id = "renku/init-scripts"
185205
version = "0.3.1"

buildpacks/homebrew/bin/build

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env bash
2+
set -eo pipefail
3+
4+
echo "---> Homebrew Buildpack"
5+
6+
LAYERS_DIR="$1"
7+
8+
# Separate layer for the CLI vs the installed packages
9+
BREW_CLI_LAYER="${LAYERS_DIR}/brew-cli"
10+
BREW_PKG_LAYER="${LAYERS_DIR}/brew-packages"
11+
12+
# Homebrew CLI setup
13+
export HOMEBREW_REPOSITORY="${BREW_CLI_LAYER}"
14+
CLI_METADATA_FILE="${BREW_CLI_LAYER}.toml"
15+
16+
# Determine the version to install
17+
if [[ -n "${BP_HOMEBREW_VERSION}" ]]; then
18+
BREW_VERSION="${BP_HOMEBREW_VERSION}"
19+
else
20+
echo "---> Determining latest stable Homebrew release..."
21+
# Follow GitHub's latest release redirect and grab the tag from the end of the URL
22+
LATEST_URL=$(curl -Ls -o /dev/null -w "%{url_effective}" https://github.com/Homebrew/brew/releases/latest)
23+
BREW_VERSION=$(echo "${LATEST_URL}" | grep -o '[^/]*$')
24+
25+
# Safety fallback just in case GitHub rate-limits the request
26+
if [[ -z "${BREW_VERSION}" || "${BREW_VERSION}" == "latest" ]]; then
27+
echo "---> WARNING: Could not determine latest release. Falling back to master."
28+
BREW_VERSION="master"
29+
fi
30+
fi
31+
32+
echo "---> Target Homebrew version: ${BREW_VERSION}"
33+
34+
# Check if we need to clear the CLI cache (if the version changed)
35+
if [[ -f "${CLI_METADATA_FILE}" ]]; then
36+
CACHED_VERSION=$(grep "brew_version" "${CLI_METADATA_FILE}" | cut -d '"' -f 2 || true)
37+
if [[ "${CACHED_VERSION}" != "${BREW_VERSION}" ]]; then
38+
echo "---> Homebrew version changed from ${CACHED_VERSION:-unknown} to ${BREW_VERSION}. Invalidating CLI cache..."
39+
rm -rf "${BREW_CLI_LAYER}"
40+
fi
41+
fi
42+
43+
# Download or use cached CLI
44+
if [[ ! -x "${HOMEBREW_REPOSITORY}/bin/brew" ]]; then
45+
echo "---> Downloading Homebrew CLI (version: ${BREW_VERSION}) to ${BREW_CLI_LAYER}..."
46+
mkdir -p "${HOMEBREW_REPOSITORY}"
47+
48+
# The Homebrew source tarball is architecture-agnostic (Ruby/Bash scripts)
49+
curl -fsSL "https://github.com/Homebrew/brew/tarball/${BREW_VERSION}" | tar xz --strip 1 -C "${HOMEBREW_REPOSITORY}"
50+
else
51+
echo "---> Using cached Homebrew CLI (version: ${BREW_VERSION})"
52+
fi
53+
54+
# Keep the CLI layer for builds, leave it out of the final image, and track the version
55+
cat >"${CLI_METADATA_FILE}" <<EOF
56+
[types]
57+
cache = true
58+
build = true
59+
launch = false
60+
61+
[metadata]
62+
brew_version = "${BREW_VERSION}"
63+
EOF
64+
65+
# Packages installation env vars
66+
export HOMEBREW_PREFIX="${BREW_PKG_LAYER}"
67+
export HOMEBREW_CELLAR="${BREW_PKG_LAYER}/Cellar"
68+
69+
# Calculate a checksum of the current Brewfile
70+
BREWFILE_SHA=$(sha256sum "Brewfile" | cut -d ' ' -f 1)
71+
PKG_METADATA_FILE="${BREW_PKG_LAYER}.toml"
72+
73+
# Check if we need to clear the package cache (if the Brewfile changed)
74+
if [[ -f "${PKG_METADATA_FILE}" ]]; then
75+
CACHED_SHA=$(grep "brewfile_sha" "${PKG_METADATA_FILE}" | cut -d '"' -f 2 || true)
76+
if [[ "${CACHED_SHA}" != "${BREWFILE_SHA}" ]]; then
77+
echo "---> Brewfile changed. Invalidating package cache..."
78+
rm -rf "${BREW_PKG_LAYER}"
79+
fi
80+
fi
81+
82+
# Ensure package layer directories exist
83+
mkdir -p "${HOMEBREW_PREFIX}/bin" "${HOMEBREW_CELLAR}"
84+
85+
# Symlink the brew executable into our prefix so the bundle command works
86+
ln -sfn "${HOMEBREW_REPOSITORY}/bin/brew" "${HOMEBREW_PREFIX}/bin/brew"
87+
88+
# Keep the package layer for both build and launch, and save the SHA
89+
cat >"${PKG_METADATA_FILE}" <<EOF
90+
[types]
91+
cache = true
92+
build = true
93+
launch = true
94+
95+
[metadata]
96+
brewfile_sha = "${BREWFILE_SHA}"
97+
EOF
98+
99+
# Install packages
100+
export PATH="${HOMEBREW_PREFIX}/bin:${HOMEBREW_REPOSITORY}/bin:${PATH}"
101+
export HOMEBREW_NO_AUTO_UPDATE=1
102+
export HOMEBREW_NO_INSTALL_CLEANUP=1
103+
104+
# Only run bundle if there's work to do (meaning the layer was just created or cleared)
105+
if [[ ! -d "${HOMEBREW_CELLAR}" ]] || [[ -z "$(ls -A "${HOMEBREW_CELLAR}" 2>/dev/null)" ]]; then
106+
echo "---> Running brew bundle into ${BREW_PKG_LAYER}..."
107+
brew bundle --file=Brewfile
108+
else
109+
echo "---> Using cached Homebrew packages"
110+
fi
111+
112+
echo "---> Homebrew buildpack finished."

buildpacks/homebrew/bin/detect

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
set -eo pipefail
3+
4+
# The application directory is passed as the first argument
5+
plan_path="${CNB_BUILD_PLAN_PATH}"
6+
7+
# Check if Brewfile exists
8+
if [[ -f "Brewfile" ]]; then
9+
# Output a simple build plan
10+
cat <<EOF >"${plan_path}"
11+
[[provides]]
12+
name = "homebrew"
13+
14+
[[requires]]
15+
name = "homebrew"
16+
EOF
17+
# Exit with 0 to indicate the buildpack applies
18+
exit 0
19+
fi
20+
21+
# Exit with 100 to indicate the buildpack does not apply
22+
exit 100

buildpacks/homebrew/buildpack.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Buildpack API version
2+
api = "0.11"
3+
4+
# Buildpack ID and metadata
5+
[buildpack]
6+
id = "renku/homebrew"
7+
version = "0.3.1"
8+
name = "homebrew buildpack"
9+
description = "A buildpack that installs homebrew packages from a Brewfile."
10+
11+
# Targets the buildpack will work with
12+
[[targets]]
13+
os = "linux"
14+
arch = "amd64"
15+
16+
[[targets]]
17+
os = "linux"
18+
arch = "arm64"

buildpacks/homebrew/package.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[buildpack]
2+
uri = "."

samples/homebrew/Brewfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
brew "lazygit"

samples/homebrew/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
flask

scripts/install_shellcheck.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ if [ -f "$LOCATION/shellcheck" ]; then
3131
fi
3232

3333
ALREADY_EXISTS=$(which shellcheck || true)
34-
if [ -n "$ALREADY_EXISTS" ]; then
34+
if [ -n "$ALREADY_EXISTS" ] && [ ! -f "$LOCATION/shellcheck" ]; then
3535
echo "Already found shellcheck installed elsewhere, linking in $LOCATION"
3636
ln -s "$ALREADY_EXISTS" "$LOCATION/shellcheck"
3737
exit 0

tests/e2e/buildpacks_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,51 @@ var _ = Describe("Testing samples", Label("samples"), Ordered, func() {
268268
},
269269
Entry("using deb sample", "../../samples/deb"),
270270
)
271+
272+
DescribeTableSubtree(
273+
"homebrew",
274+
func(source string) {
275+
var image string
276+
var container string
277+
var port int
278+
BeforeAll(func(ctx SpecContext) {
279+
image = strings.ToLower(fmt.Sprintf("test-image-%s", getULID()))
280+
Expect(buildImage(ctx, builderImg, source, image, map[string]string{})).To(Succeed())
281+
port = getFreePortOrDie()
282+
envVars := []string{fmt.Sprintf("RENKU_SESSION_PORT=%d", port)}
283+
ports := map[int]int{port: port}
284+
container, err = runImage(ctx, client, image, envVars, ports)
285+
Expect(err).ToNot(HaveOccurred())
286+
})
287+
288+
AfterAll(func(ctx SpecContext) {
289+
if container != "" && client != nil {
290+
log.Println("Cleaning up container")
291+
err = removeContainer(ctx, client, container)
292+
if err != nil {
293+
log.Println(err)
294+
}
295+
}
296+
if image != "" && client != nil {
297+
log.Println("Cleaning up image")
298+
err = removeImage(ctx, client, image)
299+
if err != nil {
300+
log.Println(err)
301+
}
302+
}
303+
})
304+
305+
Context("when the container is running", func() {
306+
It("lazygit should exist as a command in the container", func(ctx SpecContext) {
307+
_, err := execInContainer(ctx, client, container, []string{"launcher", "lazygit", "--version"})
308+
Expect(err).ToNot(HaveOccurred())
309+
})
310+
It("brew should not exist as a command in the container", func(ctx SpecContext) {
311+
_, err := execInContainer(ctx, client, container, []string{"launcher", "brew"})
312+
Expect(err).To(HaveOccurred())
313+
})
314+
})
315+
},
316+
Entry("using homebrew sample", "../../samples/homebrew"),
317+
)
271318
})

0 commit comments

Comments
 (0)