Skip to content

Commit 8159c83

Browse files
authored
docs: more docs for testing and repackaging existing software (#1645)
1 parent 2621fc1 commit 8159c83

File tree

5 files changed

+220
-52
lines changed

5 files changed

+220
-52
lines changed

docs/reference/recipe_file.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,6 @@ Using a runtime dependency name:
698698
!!! note
699699
`ignore_run_exports` only applies to runtime dependencies coming from an upstream package.
700700

701-
702701
## Tests section
703702

704703
`rattler-build` supports four different types of tests. The "script test" installs
@@ -744,6 +743,19 @@ tests:
744743
- bspatch4 -h
745744
```
746745

746+
#### External scripts
747+
748+
You can also easily run a script from your recipe directory.
749+
Note that your package should either depend on the interpreter (e.g. Python or R)
750+
or you need to add a `requirements` section to the test that installs the interpreter.
751+
752+
```yaml
753+
tests:
754+
- script: tests/run_test.py
755+
- script: tests/run_test.R
756+
- script: tests/run_test.sh
757+
```
758+
747759
#### Extra test files
748760

749761
Test files that are copied from the source work directory into the temporary
@@ -815,6 +827,41 @@ import bsdiff4
815827
import bspatch4
816828
```
817829

830+
### Perl tests
831+
832+
For this test type you can list a set of Perl modules that need to be
833+
importable. The test will fail if any of the modules cannot be imported.
834+
835+
```yaml
836+
tests:
837+
- perl:
838+
uses:
839+
- Call::Context
840+
```
841+
842+
Internally this will write a small Perl script that imports the modules:
843+
844+
```perl
845+
use Call::Context;
846+
```
847+
848+
### R tests
849+
850+
For this test type you can list a set of R modules that need to be
851+
importable. The test will fail if any of the modules cannot be imported.
852+
853+
```yaml
854+
- r:
855+
libraries:
856+
- knitr
857+
```
858+
859+
Internally this will write a small R script that imports the modules:
860+
861+
```r
862+
library(knitr)
863+
```
864+
818865
### Check for package contents
819866

820867
Checks if the built package contains the mentioned items. These checks are executed directly at

docs/testing.md

Lines changed: 89 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,122 @@
11
# Testing packages
22

33
When you are developing a package, you should write tests for it. The tests are
4-
automatically executed right after the package build has finished.
4+
automatically executed as soon as the package build and all it's run dependencies
5+
are ready.
56

6-
The tests from the test section are actually packaged _into_ your package and
7-
can also be executed straight from the existing package.
7+
## Writing tests
88

9-
The idea behind adding the tests into the package is that you can execute the
10-
tests independently from building the package. That is also why we are shipping
11-
a `test` subcommand that takes as input an existing package and executes the
12-
tests:
9+
You can add one or more tests to your package in the `tests` section of the recipe (or output).
10+
Each test is run independently, in a separate environment.
1311

14-
```bash
15-
rattler-build test --package-file ./xtensor-0.24.6-h60d57d3_0.tar.bz2
16-
```
17-
18-
Running the above command will extract the package and create a clean
19-
environment where the package and dependencies are installed. Then the tests are
20-
executed in this newly-created environment.
12+
One notable difference are the `package_contents` tests that are executed right after the package
13+
is prepared and do not create a new environment (as we only analyze the contents of the package).
2114

22-
If you inspect the package contents, you would find the test files under
23-
`info/test/*`.
15+
```yaml title="recipe.yaml"
16+
tests:
17+
# commands to run to test the package. If any of the commands
18+
# returns with an error code, the test is considered failed.
19+
- script:
20+
- echo "Hello world"
21+
- exit 1 # this will fail the test
2422

25-
## How tests are translated
23+
# run a script from the recipe folder
24+
- script: sometest.py
2625

27-
The `tests` section allows you to specify the following things:
26+
# run a Python script with the Python interpreter
27+
- script:
28+
interpreter: python
29+
content: |
30+
import mypkg
31+
assert mypkg.__version__ == "0.1.0"
2832
29-
```yaml
30-
tests:
33+
# execute `pytest` with the tests from the `tests` folder
3134
- script:
32-
# commands to run to test the package. If any of the commands
33-
# returns with an error code, the test is considered failed.
34-
- echo "Hello world"
3535
- pytest ./tests
36-
3736
# additional requirements at test time
3837
requirements:
3938
run:
4039
- pytest
41-
40+
- python 3.9.* # require an older python version
41+
# extra files to be copied to the test folder from the recipe or source directory
4242
files:
43-
# Extra files to be copied to the test directory from the "work directory"
44-
source:
45-
- tests/
46-
- test.py
47-
- *.sh
4843
recipe:
49-
- more_tests/*.py
44+
- tests/
5045

51-
# This test section tries to import the Python modules and errors if it can't
46+
# python specific tests
5247
- python:
48+
# this test section tries to import the python modules and errors if it can't
5349
imports:
5450
- mypkg
55-
- mypkg.subpkg
51+
pip_check: true
52+
python_version: [3.9.*, 3.10.*] # run against multiple older python versions
53+
54+
- r:
55+
libraries:
56+
- dplyr
57+
58+
- perl:
59+
modules:
60+
- JSON
61+
62+
# test the contents of the package. If any of the globs does not match _any_ file, the test fails.
63+
- package_contents:
64+
files:
65+
- share/package/*.txt
5666
```
5767
58-
When you are writing a test for your package, additional files are created and
59-
added to your package. These files are placed under the `info/tests/{index}/`
60-
folder for each test.
68+
### Testing package contents
69+
70+
The `package_contents` test is a special test that is executed right after the
71+
package is prepared. It does not create a new environment, but instead checks the paths that will be part of the final package.
72+
It can be very useful as a "sanity check" to ensure that the package contains the expected files.
73+
74+
It has multiple sub-keys that help when building cross-platform packages:
75+
76+
- **`files`**: a list of globs that should match at least one file in the package. If any of the globs do not match, the test fails.
77+
- **`lib`**: matches libraries in the package (`.so`, `.dll`, `.dylib` files). The test fails if any of the libraries are not found. It's enough to specify the library name without any extension (e.g. `foo` will match `libfoo.so`, `libfoo.dylib`, and `foo.dll`).
78+
- **`include`**: matches files under the `include` directory in the package. You can specify the file name like `foo.h`.
79+
- **`bin`**: matches files under the `bin` directory in the package. You can specify executable names like `foo` which will match `foo.exe` on Windows and `foo` on Linux and macOS.
80+
- **`site_packages`**: matches files under the `site-packages` directory in the package. You can specify the import path like `foobar.api` which will match `foobar/api.py` and `foobar/api/__init__.py`.
81+
82+
## Testing existing packages
6183

62-
For a script test:
84+
The tests from the test section are actually added _into_ your package and
85+
can also be executed straight from the existing package.
86+
87+
The idea behind adding the tests into the package is that you can execute the
88+
tests independently from building the package. That is also why we are shipping
89+
a `test` subcommand that takes as input an existing package and executes the
90+
tests:
6391

64-
- All the files are copied straight into the test folder (under
65-
`info/tests/{index}/`)
66-
- The script is turned into a `run_test.sh` or `run_test.bat` file
67-
- The extra requirements are stored as a JSON file called
68-
`test_time_dependencies.json`
92+
```bash
93+
rattler-build test --package-file ./xtensor-0.24.6-h60d57d3_0.tar.bz2
94+
```
95+
96+
Running the above command will extract the package and create a clean
97+
environment where the package and dependencies are installed. Then the tests are
98+
executed in this newly-created environment.
99+
100+
If you inspect the package contents, you would find the test files under
101+
`info/test/*`.
102+
103+
## How tests are translated
69104

70-
For a Python import test:
105+
The `tests` section allows you to define test configurations for your package.
106+
Tests are serialized to `info/tests/tests.yaml` in the created package and read from there during test execution.
71107

72-
- A JSON file is created that is called `python_test.json` and stores the
73-
imports to be tested and whether to execute `pip check` or not. This file is
74-
placed under `info/tests/{index}/`
108+
When adding extra files to your tests:
75109

76-
For a downstream test:
110+
1. **During package creation**
111+
- Files are copied to `$PREFIX/etc/conda/test-files/{pkg_name}/{idx}`
112+
- `{idx}` is a sequential number assigned to each test
113+
- Files can come from both `source` (work directory) and `recipe` locations
114+
2. **During test execution**
115+
- Files are copied from `$PREFIX/etc/conda/test-files/{pkg_name}/{idx}` to a temporary directory
116+
- Tests run within this temporary directory
117+
- Use relative paths to access these files in your test commands
77118

78-
- A JSON file is created that is called `downstream_test.json` and stores the
79-
downstream tests to be executed. This file is placed under
80-
`info/tests/{index}/`
119+
This approach ensures test files are properly packaged and available during test execution.
81120

82121
## Legacy tests
83122

docs/tutorials/python.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ rattler-build build --recipe ./ipywidgets
8787

8888
We will build a package for `numpy` – which contains compiled code.
8989
Since compiled code is `python` version-specific, we will need to specify the `python` version explicitly.
90-
The best way to do this is with a "variant_config.yaml" file:
90+
91+
The best way to do this is with a "variant_config.yaml" file.
92+
The variant config file allows us to easily compile the package against multiple Python versions.
9193

9294
```yaml title="variants.yaml"
9395
python:

docs/tutorials/repackaging.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Repackaging existing software
2+
3+
It's totally possible to repackage existing software using rattler-build, and make it easy to install
4+
with conda, mamba or pixi.
5+
6+
Repackaging existing binaries is not recommended on `conda-forge`, but totally acceptable for your own channels / repositories.
7+
8+
## Example for `linkerd`
9+
10+
This example shows how to repackage the `linkerd` binary. The `linkerd` binary is a command line tool that is used to manage and monitor Kubernetes clusters, and a pre-built binary is available for download from Github releases. Alternatively, you could also follow the Go packaging tutorial to build linkerd from source!
11+
12+
```yaml
13+
package:
14+
name: linkerd
15+
version: 25.5.2
16+
17+
source:
18+
- if: target_platform == "linux-64"
19+
then:
20+
url: https://github.com/linkerd/linkerd2/releases/download/edge-25.5.2/linkerd2-cli-edge-25.5.2-linux-amd64
21+
sha256: 55e7721ab0eb48217f239628b55517b7d663a962df18cdab180e5d42e45f83cb
22+
file_name: linkerd
23+
- if: target_platform == "osx-arm64"
24+
then:
25+
url: https://github.com/linkerd/linkerd2/releases/download/edge-25.5.2/linkerd2-cli-edge-25.5.2-darwin-arm64
26+
sha256: 405ddf3af0089bfece93d811c9bfb9f63e3a000e3f423163fc56690ef4d427cf
27+
file_name: linkerd
28+
# To support other platforms you can add more `if` statements here
29+
30+
build:
31+
script:
32+
# make linkerd binary executable
33+
- chmod +x linkerd
34+
# make sure that the `$PREFIX/bin` directory exists
35+
- mkdir -p $PREFIX/bin
36+
# move or copy the binary to the `$PREFIX/bin` directory
37+
- mv linkerd $PREFIX/bin/
38+
39+
tests:
40+
- script:
41+
- linkerd version
42+
# you can add more tests here
43+
44+
about:
45+
homepage: https://linkerd.io/
46+
license: Apache-2.0
47+
summary: Linkerd is an ultralight service mesh for Kubernetes.
48+
description: |
49+
Linkerd is an ultralight service mesh for Kubernetes.
50+
It adds observability, reliability, and security to your
51+
applications without requiring any code changes.
52+
Linkerd is open source and free to use.
53+
# Note: since we are downloading a binary, we don't have a license file.
54+
# You can put the license in the recipe directory, and it will be picked up from there.
55+
license_file: LICENSE
56+
# documentation: ...
57+
repository: https://github.com/linkerd/linkerd2
58+
```
59+
60+
!!! note
61+
62+
To repackage the `linkerd` package on `osx-arm64` for `linux-64`, you can pass the `--target-platform` argument to `rattler-build`:
63+
64+
```bash
65+
rattler-build build --target-platform linux-64 linkerd
66+
```
67+
68+
## Adding system requirements
69+
70+
Some packages have system requirements (e.g. on `glibc` on Linux, or the macOS SDK on macOS).
71+
72+
You can add system requirements like this to the `run` section by depending on virtual packages:
73+
74+
```yaml
75+
requirements:
76+
run:
77+
- ${{ "__glibc >=2.17" if linux }}
78+
- ${{ "__osx >=10.15" if osx }}
79+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ nav:
105105
- "Go": tutorials/go.md
106106
- "Perl": tutorials/perl.md
107107
- "R": tutorials/r.md
108+
- "Repackaging": tutorials/repackaging.md
108109
- "Converting from conda-build": converting_from_conda_build.md
109110

110111
- Build options:

0 commit comments

Comments
 (0)