Skip to content
Open
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
52 changes: 52 additions & 0 deletions skills/build-single-package/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
name: build-single-package
description: Build a single package in a kit to test changes without building the entire kit
context: fork
---

# Build Single Package

Build and verify a single package within a Bottlerocket kit for rapid iteration during development.

## When to Use

- Testing changes to a package spec file
- Debugging package build failures
- Iterating on package patches
- Verifying package dependencies resolve

## Prerequisites

- Working in a kit directory (bottlerocket-core-kit or bottlerocket-kernel-kit)
- Package spec exists in kit's packages directory

## Workflow

1. **Identify Package and Kit**
- Determine which kit contains the package
- Navigate to kit directory

2. **Build Package**
- Run `<grove-root>/skills/build-single-package/scripts/build-single-package -p <package-name> [-a <arch>]`
- Use absolute path from grove root
- `-p` flag specifies the package name (required)
- `-a` flag specifies target architecture (optional)
- Defaults to host architecture (detected via `uname -m`) if not specified
- This builds only the specified package and its direct dependencies

3. **Verify Build Success**
- Check for RPM artifacts in `build/rpms/<package-name>/`
- Confirm expected package files exist
- Report build output and artifact locations

## Notes

- Package must be defined in the kit's packages/ directory
- Build artifacts are placed in kit's build/rpms/<package-name>/ directory
- Script detects host architecture automatically using `uname -m`
- Override with `-a` flag to cross-compile
- Supported architectures: x86_64, aarch64

## Related Skills

- `patch-third-party-package` - Create patches for packages
36 changes: 36 additions & 0 deletions skills/build-single-package/scripts/build-single-package
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail

PACKAGE=""
ARCH=""

usage() {
echo "Usage: $0 -p <package-name> [-a <arch>]" >&2
echo " -p Package name (required)" >&2
echo " -a Target architecture (optional, defaults to host arch)" >&2
exit 1
}

while getopts "p:a:h" opt; do
case ${opt} in
p) PACKAGE="${OPTARG}" ;;
a) ARCH="${OPTARG}" ;;
h) usage ;;
*) usage ;;
esac
done

if [[ -z "${PACKAGE}" ]]; then
echo "Error: Package name required" >&2
usage
fi

# Detect host architecture if not provided
if [[ -z "${ARCH}" ]]; then
ARCH=$(uname -m)
fi

echo "Building package: ${PACKAGE} for architecture: ${ARCH}"
PACKAGE="${PACKAGE}" BUILDSYS_ARCH="${ARCH}" make twoliter build-package

echo "Build completed successfully"
192 changes: 192 additions & 0 deletions skills/patch-third-party-package/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
name: patch-third-party-package
description: Create patches for Bottlerocket third-party packages by extracting sources, making changes, generating patch files, and updating RPM specs
---

# Patch Third-Party Package

## Purpose

Create patches that modify third-party packages in Bottlerocket to adjust paths, fix compatibility issues, or backport fixes. This skill guides you through fetching sources, making changes, generating git-formatted patches, and integrating them into the RPM build system for Bottlerocket

## When to Use

- Need to adjust filesystem paths for Bottlerocket's layout
- Fix build system compatibility issues (e.g., musl vs glibc)
- Modify package behavior for Bottlerocket-specific requirements
- Add Bottlerocket-specific configuration or features

## Prerequisites

- Running from within a grove (not forest root)
- Target package exists in `kits/*/packages/`

## Procedure

### 1. Locate Package

Find the package directory:

```bash
find kits/*/packages -name "PACKAGE_NAME" -type d
```

### 2. Read Source Metadata

Extract source URLs and checksums from the package's `Cargo.toml`:

```bash
.<grove-root>/skills/patch-third-party-package/scripts/extract-source-metadata PACKAGE_NAME
```

This displays the source tarball URL and its sha512 checksums.
Each external file has its own entry with URL and sha512 checksum.

### 3. Fetch and Verify Sources

Create temporary workspace and fetch sources:

```bash
WORK_DIR=$(mktemp -d -t patch-PACKAGE-XXXX)
cd $WORK_DIR

# Fetch sources
curl -LO SOURCE_URL

# Verify sha512 checksum
echo "SHA512_FROM_CARGO filename" | sha512sum -c -
```

### 4. Extract Sources

Read the package's RPM spec file to understand how sources should be extracted:

```bash
cat kits/*/packages/PACKAGE_NAME/PACKAGE_NAME.spec
```

Look at the `%prep` section and follow its pattern exactly. Common patterns:

**Simple autosetup** (most common):
```spec
%autosetup -n package-%{version} -p1
```

**Git-based autosetup**:
```spec
%autosetup -Sgit -n package-%{version} -p1
```

**SRPM extraction** (grub, kernel):
```spec
rpmkeys --import %{S:1} --dbpath "${PWD}/rpmdb"
rpmkeys --checksig %{S:0} --dbpath "${PWD}/rpmdb"
rm -rf "${PWD}/rpmdb"
rpm2cpio %{S:0} | cpio -iu '*.tar.xz' '*.patch'
tar -xof package.tar.xz
rm package.tar.xz
%setup -TDn package-%{version}
```

Extract the sources following the pattern from the spec file.

### 5. Initialize Git Repository

After extraction, initialize git in the source directory:

```bash
cd EXTRACTED_SOURCE_DIR
git init
git add .
git commit -S -s -m "Initial import of PACKAGE_NAME sources"
```

### 6. Make Changes

Create the necessary changes. Commit each logical change separately:

```bash
# Make your changes
git add -A
git commit -S -s -m "fix: adjust paths for Bottlerocket filesystem layout"

# Make additional changes if needed
git add -A
git commit -S -s -m "build: disable feature incompatible with musl"
```

Each commit message should explain **why** the change is needed, not what code changed.

### 7. Generate Patches

Generate git-formatted patches from your commits:

```bash
# Generate patches (excluding the initial import commit)
git format-patch --no-numbered --no-signature -N HEAD~N
```

Where N is the number of commits to convert to patches. This creates files like:
- `0001-fix-adjust-paths-for-bottlerocket.patch`
- `0002-build-disable-feature-incompatible.patch`

### 8. Copy Patches to Package

Copy the generated patches to the package directory:

```bash
cp *.patch GROVE_ROOT/kits/*/packages/PACKAGE_NAME/
```

### 9. Update RPM Spec

Add patch references to the spec file header:

```spec
Patch0001: 0001-fix-adjust-paths-for-bottlerocket.patch
Patch0002: 0002-build-disable-feature-incompatible.patch
```

Verify the `%prep` section will apply them. Most packages use `%autosetup` which automatically applies all numbered patches:

```spec
%prep
%autosetup -n PACKAGE_NAME-%{version} -p1
```

If the package uses a different method, ensure your patches will be applied:
- `%autopatch -p1`: Applies patches after manual setup
- `git am` with `-Sgit`: For git-formatted patches
- Manual `patch -p1` loop: When order matters

### 10. Commit Changes

Commit the patches and spec file changes to the repository:

```bash
cd GROVE_ROOT/REPO_NAME
git add .
git commit -S -s -m "Git commit message"
```

## Validation

Build and test the package:
1. Build the package using existing build skills
2. Verify the package builds successfully

## Common Issues

**Patch fails to apply during build:**
- Verify patch was generated with correct `-p` level (usually `-p1`)
- Check that `%prep` section uses matching `-p` level
- Ensure patches are numbered sequentially starting from 0001

**Source extraction fails:**
- Verify sha512 checksums match Cargo.toml
- Check that extraction commands match the spec file's `%prep` section exactly

## Next Steps

After creating patches:
- Use **build-kit-locally** or similar skills to build and test the package
59 changes: 59 additions & 0 deletions skills/patch-third-party-package/scripts/extract-source-metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail

check_required_tool() {
if ! command -v "$1" &> /dev/null; then
echo "Error: $1 is not installed" >&2
exit 1
fi
}

usage() {
cat <<EOF
Usage: $0 PACKAGE_NAME

Extract source URLs and checksums from a Bottlerocket package's Cargo.toml.

Arguments:
PACKAGE_NAME Name of the package (e.g., glibc, coreutils)

Output:
Prints source URLs and sha512 checksums in the format:
URL: <url>
SHA512: <checksum>

Example:
$0 glibc
EOF
exit 1
}

if [[ $# -ne 1 ]]; then
usage
fi

check_required_tool jq
check_required_tool toml2json

PACKAGE_NAME="$1"

# Find package directory
PACKAGE_DIR=$(find kits/*/packages -type d -name "${PACKAGE_NAME}" 2>/dev/null | head -n1)

if [[ -z "${PACKAGE_DIR}" ]]; then
echo "Error: Package '${PACKAGE_NAME}' not found in kits/*/packages/" >&2
exit 1
fi

CARGO_TOML="${PACKAGE_DIR}/Cargo.toml"

if [[ ! -f "${CARGO_TOML}" ]]; then
echo "Error: Cargo.toml not found at ${CARGO_TOML}" >&2
exit 1
fi

# Parse external-files entries using toml2json and jq
toml2json < "${CARGO_TOML}" | jq -r '
.package.metadata."build-package"."external-files"[]? |
"URL: \(.url)\nSHA512: \(.sha512)\n"
'