Package Assembly#
This tutorial teaches you how to create conda packages programmatically using the
assemble_package function. This is useful when you already have files staged and
want to package them directly, without going through the full recipe build process.
When to Use assemble_package#
Use assemble_package when:
- You have files already compiled/staged and just need to package them
- You're building packages in a custom CI/CD pipeline
For building from recipes, use Stage0Recipe.from_yaml() with render() and run_build() instead.
import tempfile
from datetime import datetime, timezone
from pathlib import Path
from rattler_build import (
ArchiveType,
FileEntry,
assemble_package,
collect_files,
)
Example 1: Minimal Package#
Let's create the simplest possible package - just a name, version, and some files:
# Create temporary directories for our example
work_dir = Path(tempfile.mkdtemp())
files_dir = work_dir / "files"
output_dir = work_dir / "output"
files_dir.mkdir()
output_dir.mkdir()
# Create some example files to package
(files_dir / "bin").mkdir()
(files_dir / "bin" / "hello").write_text("#!/bin/bash\necho 'Hello, World!'")
# Create the package
output = assemble_package(
name="hello",
version="1.0.0",
target_platform="linux-64",
build_string="0",
output_dir=output_dir,
files_dir=files_dir,
)
print(f"Package created: {output.path.name}")
print(f"Identifier: {output.identifier}")
Package created: hello-1.0.0-0.conda
Identifier: hello-1.0.0-0
Example 2: Package with Metadata#
Add package metadata like license, homepage, and dependencies:
# Create new directories
files_dir2 = work_dir / "files2"
output_dir2 = work_dir / "output2"
files_dir2.mkdir()
output_dir2.mkdir()
# Create a Python package structure
(files_dir2 / "lib" / "python3.12" / "site-packages" / "mylib").mkdir(parents=True)
(files_dir2 / "lib" / "python3.12" / "site-packages" / "mylib" / "__init__.py").write_text(
'"""My library."""\n__version__ = "2.0.0"\n'
)
# Create package with full metadata
output = assemble_package(
name="mylib",
version="2.0.0",
target_platform="linux-64",
build_string="py312_0",
output_dir=output_dir2,
files_dir=files_dir2,
# Metadata
homepage="https://github.com/example/mylib",
license="MIT",
license_family="MIT",
summary="A demonstration library",
description="This is a longer description of the library.",
# Dependencies
depends=["python >=3.12,<3.13", "numpy >=1.20"],
constrains=["scipy >=1.0"],
build_number=0,
)
print(f"Package: {output.path.name}")
print(f"Identifier: {output.identifier}")
Package: mylib-2.0.0-py312_0.conda
Identifier: mylib-2.0.0-py312_0
Example 3: Collecting Files with Glob Patterns#
For more control over which files to include, use collect_files:
# Create a directory with mixed content
mixed_dir = work_dir / "mixed"
mixed_dir.mkdir()
# Create various files
(mixed_dir / "src").mkdir()
(mixed_dir / "src" / "main.py").write_text("print('main')")
(mixed_dir / "src" / "utils.py").write_text("print('utils')")
(mixed_dir / "src" / "__pycache__").mkdir()
(mixed_dir / "src" / "__pycache__" / "main.cpython-312.pyc").write_bytes(b"bytecode")
(mixed_dir / "tests").mkdir()
(mixed_dir / "tests" / "test_main.py").write_text("def test(): pass")
(mixed_dir / "README.md").write_text("# My Project")
# Use collect_files to select only Python source files, excluding pycache
files = collect_files(
mixed_dir,
include_globs=["**/*.py"],
exclude_globs=["**/__pycache__/**", "**/tests/**"],
)
print("Files selected:")
for f in files:
print(f" {f.destination}")
# Now create a package with these files
output_dir3 = work_dir / "output3"
output_dir3.mkdir()
output = assemble_package(
name="filtered-pkg",
version="1.0.0",
target_platform="noarch",
build_string="py_0",
output_dir=output_dir3,
files=files,
noarch="python",
)
print(f"\nPackage: {output.path.name}")
Files selected:
src/main.py
src/utils.py
Package: filtered-pkg-1.0.0-py_0.conda
Example 4: Reproducible Builds with Timestamps#
For reproducible builds, set a fixed timestamp:
import hashlib
# Create simple files
repro_dir = work_dir / "repro"
repro_dir.mkdir()
(repro_dir / "data.txt").write_text("Hello")
output_dir4 = work_dir / "output4"
output_dir4.mkdir()
# Fixed timestamp: 2024-01-01 00:00:00 UTC
FIXED_TIMESTAMP = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
# Build twice with the same timestamp
output1 = assemble_package(
name="repro-test",
version="1.0.0",
target_platform="noarch",
build_string="0",
output_dir=output_dir4,
files_dir=repro_dir,
timestamp=FIXED_TIMESTAMP,
noarch="generic",
)
# Rename first package to avoid overwrite
output1.path.rename(output_dir4 / "first.conda")
output2 = assemble_package(
name="repro-test",
version="1.0.0",
target_platform="noarch",
build_string="0",
output_dir=output_dir4,
files_dir=repro_dir,
timestamp=FIXED_TIMESTAMP,
noarch="generic",
)
# Compare hashes
hash1 = hashlib.sha256((output_dir4 / "first.conda").read_bytes()).hexdigest()[:16]
hash2 = hashlib.sha256(output2.path.read_bytes()).hexdigest()[:16]
print(f"First build hash: {hash1}")
print(f"Second build hash: {hash2}")
print(f"Reproducible: {hash1 == hash2}")
First build hash: 34e60cd2d5ccde9a
Second build hash: 34e60cd2d5ccde9a
Reproducible: True
Archive Formats#
You can choose between .conda (modern, recommended) and .tar.bz2 (legacy) formats:
print("Available archive types:")
print(f" ArchiveType.Conda -> {ArchiveType.Conda.extension()}")
print(f" ArchiveType.TarBz2 -> {ArchiveType.TarBz2.extension()}")
# Create a .tar.bz2 package
output_dir5 = work_dir / "output5"
output_dir5.mkdir()
output = assemble_package(
name="legacy-format",
version="1.0.0",
target_platform="linux-64",
build_string="0",
output_dir=output_dir5,
files_dir=repro_dir,
archive_type=ArchiveType.TarBz2,
)
print(f"\nCreated: {output.path.name}")
Available archive types:
ArchiveType.Conda -> .conda
ArchiveType.TarBz2 -> .tar.bz2
Created: legacy-format-1.0.0-0.tar.bz2
Summary#
| Function/Class | Purpose |
|---|---|
assemble_package() |
Main function to create packages from files |
collect_files() |
Collect files with glob patterns |
FileEntry |
Represent a single file with source/destination paths |
ArchiveType |
Choose .conda or .tar.bz2 format |
PackageOutput |
Result with path and identifier |
Key Parameters for assemble_package()#
Required:
name,version,target_platform,build_string,output_dir- At least one of
files_dirorfiles
Metadata (optional):
homepage,license,license_family,summary,description
Dependencies (optional):
depends,constrains,build_number,noarch
Build options (optional):
compression_level(0-9),archive_type,timestamp(datetime object),detect_prefix