Skip to content

Commit 47d44b1

Browse files
committed
Add multi-arch support to yarn buildpack: update publish.sh, package.sh, build.sh to read targets from buildpack.toml, add yj support, and update README with packaging/publishing instructions
1 parent f023f73 commit 47d44b1

8 files changed

Lines changed: 228 additions & 18 deletions

File tree

README.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,16 @@ file that looks like the following:
4646
launch = true
4747
```
4848

49-
## Usage
49+
## Packaging
5050

5151
To package this buildpack for consumption:
5252

53-
```shell
54-
$ ./scripts/package.sh --version <version-number>
53+
```bash
54+
./scripts/package.sh --version 2.2.6
5555
```
5656

57+
This will build the buildpack for all target architectures specified in `buildpack.toml` (amd64 and arm64 by default) and create a single archive containing binaries for all architectures in the `build/` directory.
58+
5759
This will create a `buildpackage.cnb` file under the `build` directory which you
5860
can use to build your app as follows:
5961
```shell
@@ -66,6 +68,29 @@ pack build <app-name> \
6668

6769
Though the API of this buildpack does not require `node`, yarn is unusable without node.
6870

71+
## Publishing
72+
73+
To publish this buildpack to ECR:
74+
75+
```bash
76+
# First, authenticate with ECR (if not already authenticated)
77+
aws ecr get-login-password --region us-east-1 | \
78+
docker login --username AWS --password-stdin 348674388966.dkr.ecr.us-east-1.amazonaws.com
79+
80+
# Then publish the buildpack
81+
./scripts/publish.sh \
82+
--image-ref 348674388966.dkr.ecr.us-east-1.amazonaws.com/neeto-deploy/paketo/buildpack/yarn:<version> \
83+
--buildpack-type buildpack
84+
```
85+
86+
The script will automatically:
87+
- Read target architectures from `buildpack.toml`
88+
- Extract the buildpack archive
89+
- Publish each architecture separately with arch-suffixed tags (e.g., `yarn:<version>-amd64`, `yarn:<version>-arm64`)
90+
- Create and push a multi-arch manifest list
91+
92+
## Usage
93+
6994
## Run Tests
7095

7196
To run all unit tests, run:

buildpack.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ api = "0.7"
55
id = "neeto-deploy/yarn"
66
name = "Paketo Buildpack for Yarn"
77
sbom-formats = ["application/vnd.cyclonedx+json", "application/spdx+json", "application/vnd.syft+json"]
8+
version = "2.2.6"
89

910
[[buildpack.licenses]]
1011
type = "Apache-2.0"
@@ -21,7 +22,7 @@ api = "0.7"
2122
"linux/arm64/bin/run",
2223
]
2324

24-
pre-package = "./scripts/build.sh --target linux/amd64 --target linux/arm64"
25+
pre-package = "./scripts/build.sh"
2526

2627
[metadata.default_versions]
2728
yarn = "1.*"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit c8afb63c824c616b09bec074b678209c4dc4951f

scripts/.util/tools.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"createpackage": "v1.73.0",
33
"jam": "v2.15.1",
44
"libpaktools": "v0.3.0",
5-
"pack": "v0.38.2"
5+
"pack": "v0.38.2",
6+
"yj": "v5.1.0"
67
}

scripts/.util/tools.sh

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,60 @@ function util::tools::create-package::install () {
283283
fi
284284
}
285285

286+
function util::tools::yj::install() {
287+
local dir token
288+
token=""
289+
290+
while [[ "${#}" != 0 ]]; do
291+
case "${1}" in
292+
--directory)
293+
dir="${2}"
294+
shift 2
295+
;;
296+
297+
--token)
298+
token="${2}"
299+
shift 2
300+
;;
301+
302+
*)
303+
util::print::error "unknown argument \"${1}\""
304+
esac
305+
done
306+
307+
mkdir -p "${dir}"
308+
util::tools::path::export "${dir}"
309+
310+
if [[ ! -f "${dir}/yj" ]]; then
311+
local version curl_args os arch
312+
313+
version="$(jq -r .yj "$(dirname "${BASH_SOURCE[0]}")/tools.json")"
314+
315+
curl_args=(
316+
"--fail"
317+
"--silent"
318+
"--location"
319+
"--output" "${dir}/yj"
320+
)
321+
322+
if [[ "${token}" != "" ]]; then
323+
curl_args+=("--header" "Authorization: Token ${token}")
324+
fi
325+
326+
util::print::title "Installing yj ${version}"
327+
328+
os=$(util::tools::os macos)
329+
arch=$(util::tools::arch)
330+
331+
curl "https://github.com/sclevine/yj/releases/download/${version}/yj-${os}-${arch}" \
332+
"${curl_args[@]}"
333+
334+
chmod +x "${dir}/yj"
335+
else
336+
util::print::info "Using yj $("${dir}"/yj -v 2>&1 || echo "unknown version")"
337+
fi
338+
}
339+
286340
function util::tools::tests::checkfocus() {
287341
testout="${1}"
288342
if grep -q 'Focused: [1-9]' "${testout}"; then

scripts/build.sh

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,31 @@ function main() {
3737

3838
mkdir -p "${BUILDPACKDIR}/bin"
3939

40+
# Read targets from buildpack.toml if no targets provided via flags
41+
if [[ ${#targets[@]} -eq 0 ]]; then
42+
local buildpack_toml="${BUILDPACKDIR}/buildpack.toml"
43+
if [[ -f "${buildpack_toml}" ]]; then
44+
util::print::info "Reading targets from ${buildpack_toml}..."
45+
# Check if yj and jq are available
46+
if command -v yj >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
47+
local targets_json
48+
targets_json=$(cat "${buildpack_toml}" | yj -tj | jq -r '.targets[]? | "\(.os)/\(.arch)"' 2>/dev/null || echo "")
49+
while IFS= read -r target; do
50+
if [[ -n "${target}" ]]; then
51+
targets+=("${target}")
52+
fi
53+
done <<< "${targets_json}"
54+
if [[ ${#targets[@]} -gt 0 ]]; then
55+
util::print::info "Found ${#targets[@]} target(s) in buildpack.toml: ${targets[*]}"
56+
fi
57+
fi
58+
fi
59+
60+
# Fallback to default if still no targets
4061
if [[ ${#targets[@]} -eq 0 ]]; then
4162
targets=("linux/amd64")
42-
util::print::info "Setting default target platform architecture to: linux/amd64"
63+
util::print::info "No targets found in buildpack.toml. Setting default target platform architecture to: linux/amd64"
64+
fi
4365
fi
4466

4567
run::build
@@ -61,6 +83,7 @@ OPTIONS
6183
--target strings Target platforms to build for.
6284
Targets should be in the format '[os][/arch][/variant]'.
6385
- To specify two different architectures: '--target "linux/amd64" --target "linux/arm64"'
86+
If not provided, targets will be read from buildpack.toml
6487
--help -h prints the command usage
6588
USAGE
6689
}

scripts/package.sh

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ function main {
7676
buildpack_type=extension
7777
fi
7878

79+
# Read targets from buildpack.toml if no targets provided via flags
80+
if [[ ${#targets[@]} -eq 0 ]]; then
81+
local buildpack_toml="${ROOT_DIR}/buildpack.toml"
82+
if [[ -f "${buildpack_toml}" ]]; then
83+
util::print::info "Reading targets from ${buildpack_toml}..."
84+
if command -v yj >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
85+
local targets_json
86+
targets_json=$(cat "${buildpack_toml}" | yj -tj | jq -r '.targets[]? | "\(.os)/\(.arch)"' 2>/dev/null || echo "")
87+
while IFS= read -r target; do
88+
if [[ -n "${target}" ]]; then
89+
targets+=("${target}")
90+
fi
91+
done <<< "${targets_json}"
92+
if [[ ${#targets[@]} -gt 0 ]]; then
93+
util::print::info "Found ${#targets[@]} target(s) in buildpack.toml: ${targets[*]}"
94+
fi
95+
fi
96+
fi
97+
fi
98+
7999
buildpack::archive "${version}" "${buildpack_type}"
80100
if [[ ${#targets[@]} -gt 0 ]]; then
81101
buildpackage::create "${output}" "${buildpack_type}" "${targets[@]}"
@@ -95,7 +115,7 @@ OPTIONS
95115
--version <version> -v <version> specifies the version number to use when packaging a buildpack or an extension
96116
--output <output> -o <output> location to output the packaged buildpackage or extension artifact (default: ${ROOT_DIR}/build/buildpackage.cnb)
97117
--token <token> Token used to download assets from GitHub (e.g. jam, pack, etc) (optional)
98-
--target <target> Target platform (e.g. linux/amd64). Can be specified multiple times for multi-arch (optional)
118+
--target <target> Target platform (e.g. linux/amd64). Can be specified multiple times for multi-arch (optional). If not provided, targets will be read from buildpack.toml
99119
USAGE
100120
}
101121

@@ -118,6 +138,10 @@ function tools::install() {
118138
--directory "${BIN_DIR}" \
119139
--token "${token}"
120140

141+
util::tools::yj::install \
142+
--directory "${BIN_DIR}" \
143+
--token "${token}"
144+
121145
if [[ -f "${ROOT_DIR}/.libbuildpack" ]]; then
122146
util::tools::packager::install \
123147
--directory "${BIN_DIR}"

scripts/publish.sh

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,30 +113,111 @@ function tools::install() {
113113
util::tools::pack::install \
114114
--directory "${BIN_DIR}" \
115115
--token "${token}"
116+
117+
util::tools::yj::install \
118+
--directory "${BIN_DIR}" \
119+
--token "${token}"
116120
}
117121

118122
function buildpack::publish() {
119-
120123
local image_ref buildpack_type archive_path
121124
image_ref="${1}"
122125
buildpack_type="${2}"
123126
archive_path="${3}"
124127

125128
util::print::title "Publishing ${buildpack_type}..."
126129

127-
util::print::info "Extracting archive..."
128-
tmp_dir=$(mktemp -d -p $ROOT_DIR)
129-
tar -xvf $archive_path -C $tmp_dir
130+
# Read targets from buildpack.toml
131+
local buildpack_toml="${ROOT_DIR}/buildpack.toml"
132+
local -a targets=()
133+
134+
if [[ -f "${buildpack_toml}" ]]; then
135+
util::print::info "Reading targets from ${buildpack_toml}..."
136+
# Use yj and jq if available
137+
if command -v yj >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
138+
local targets_json
139+
targets_json=$(cat "${buildpack_toml}" | yj -tj | jq -r '.targets[]? | "\(.os)/\(.arch)"' 2>/dev/null || echo "")
140+
while IFS= read -r target; do
141+
if [[ -n "${target}" ]]; then
142+
targets+=("${target}")
143+
fi
144+
done <<< "${targets_json}"
145+
fi
146+
fi
130147

131-
util::print::info "Publishing ${buildpack_type} to ${image_ref}"
148+
if [[ ${#targets[@]} -gt 0 ]]; then
149+
util::print::info "Found ${#targets[@]} target(s) in buildpack.toml: ${targets[*]}"
150+
fi
132151

133-
pack \
134-
${buildpack_type} package $image_ref \
135-
--path $tmp_dir \
136-
--format image \
137-
--publish
152+
if [[ ! -f "${archive_path}" ]]; then
153+
util::print::error "buildpack artifact not found at ${archive_path}; run scripts/package.sh first"
154+
fi
138155

139-
rm -rf $tmp_dir
156+
# For multi-arch, pack needs to publish each architecture separately, then create a manifest
157+
if [[ ${#targets[@]} -gt 1 ]]; then
158+
# Multi-arch publishing
159+
util::print::info "Publishing multi-arch ${buildpack_type} (${#targets[@]} architectures)..."
160+
161+
# Check if manifest list already exists and remove it
162+
if docker manifest inspect "${image_ref}" >/dev/null 2>&1; then
163+
util::print::info "Existing manifest list found for ${image_ref}. Removing it..."
164+
docker manifest rm "${image_ref}"
165+
fi
166+
167+
# Extract archive once
168+
local tmp_dir
169+
tmp_dir=$(mktemp -d -p "${ROOT_DIR}")
170+
tar -xzf "${archive_path}" -C "${tmp_dir}"
171+
172+
local arch_images=()
173+
for target in "${targets[@]}"; do
174+
local arch
175+
arch=$(echo "${target}" | cut -d'/' -f2)
176+
local arch_image_ref="${image_ref}-${arch}"
177+
178+
util::print::info "Publishing ${target} as ${arch_image_ref}..."
179+
180+
pack \
181+
${buildpack_type} package "${arch_image_ref}" \
182+
--path "${tmp_dir}" \
183+
--target "${target}" \
184+
--format image \
185+
--publish
186+
187+
arch_images+=("${arch_image_ref}")
188+
done
189+
190+
# Create multi-arch manifest
191+
util::print::info "Creating multi-arch manifest for ${image_ref}..."
192+
docker manifest create "${image_ref}" "${arch_images[@]}"
193+
docker manifest push "${image_ref}"
194+
195+
rm -rf "${tmp_dir}"
196+
util::print::info "Successfully published multi-arch ${buildpack_type}: ${image_ref}"
197+
else
198+
# Single architecture or no targets detected
199+
util::print::info "Extracting archive..."
200+
local tmp_dir
201+
tmp_dir=$(mktemp -d -p "${ROOT_DIR}")
202+
tar -xzf "${archive_path}" -C "${tmp_dir}"
203+
204+
util::print::info "Publishing ${buildpack_type} to ${image_ref}"
205+
206+
local pack_args=(
207+
${buildpack_type} package "${image_ref}"
208+
--path "${tmp_dir}"
209+
--format image
210+
--publish
211+
)
212+
213+
# Add target if we have one
214+
if [[ ${#targets[@]} -eq 1 ]]; then
215+
pack_args+=(--target "${targets[0]}")
216+
fi
217+
218+
pack "${pack_args[@]}"
219+
rm -rf "${tmp_dir}"
220+
fi
140221
}
141222

142223
main "${@:-}"

0 commit comments

Comments
 (0)