Skip to content

Commit 3098063

Browse files
pavelzwmgorny
andauthored
feat: read channel_sources from variant file (#1597)
Co-authored-by: Michał Górny <[email protected]>
1 parent 80cfafd commit 3098063

File tree

8 files changed

+110
-18
lines changed

8 files changed

+110
-18
lines changed

docs/variants.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,14 @@ requirements:
186186
- libcurl >=8,<9
187187
```
188188

189+
### Channel sources
190+
191+
You can specify the channels when building by adjusting `channel_sources` in your variant file:
192+
193+
```yaml
194+
channel_sources: conda-forge/label/rust_dev,conda-forge
195+
```
196+
189197
## Prioritizing variants
190198

191199
You might produce multiple variants for a package, but want to define a _priority_ for a given variant.

py-rattler-build/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,13 @@ fn build_recipes_py(
5555
.map(|c| ChannelPriorityWrapper::from_str(&c).map(|c| c.value))
5656
.transpose()
5757
.map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
58+
// todo: allow custom config here
59+
let config = pixi_config::Config::default();
5860
let common = CommonData::new(
5961
output_dir,
6062
false,
6163
auth_file.map(|a| a.into()),
64+
config,
6265
channel_priority,
6366
allow_insecure_host,
6467
);
@@ -146,13 +149,28 @@ fn test_package_py(
146149
.map(|c| ChannelPriorityWrapper::from_str(&c).map(|c| c.value))
147150
.transpose()
148151
.map_err(|e| PyRuntimeError::new_err(e.to_string()))?;
152+
// todo: allow custom config here
153+
let config = pixi_config::Config::default();
149154
let common = CommonData::new(
150155
None,
151156
false,
152157
auth_file,
158+
config,
153159
channel_priority,
154160
allow_insecure_host,
155161
);
162+
let channel = match channel {
163+
None => None,
164+
Some(channel) => Some(
165+
channel
166+
.iter()
167+
.map(|c| {
168+
NamedChannelOrUrl::from_str(c)
169+
.map_err(|e| PyRuntimeError::new_err(e.to_string()))
170+
})
171+
.collect::<PyResult<_>>()?,
172+
),
173+
};
156174
let test_data = TestData::new(package_file, channel, compression_threads, common);
157175

158176
let rt = tokio::runtime::Runtime::new().unwrap();

src/lib.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub mod source_code;
4646
use std::{
4747
collections::{BTreeMap, HashMap},
4848
path::{Path, PathBuf},
49+
str::FromStr,
4950
sync::{Arc, Mutex},
5051
};
5152

@@ -65,7 +66,8 @@ use package_test::TestConfiguration;
6566
use petgraph::{algo::toposort, graph::DiGraph, visit::DfsPostOrder};
6667
use pixi_config::PackageFormatAndCompression;
6768
use rattler_conda_types::{
68-
Channel, GenericVirtualPackage, MatchSpec, PackageName, Platform, package::ArchiveType,
69+
GenericVirtualPackage, MatchSpec, NamedChannelOrUrl, PackageName, Platform,
70+
package::ArchiveType,
6971
};
7072
use rattler_package_streaming::write::CompressionLevel;
7173
use rattler_solve::SolveStrategy;
@@ -310,10 +312,40 @@ pub async fn get_build_output(
310312
recipe.package().name().as_normalized().to_string()
311313
};
312314

313-
// Add the channels from the args and by default always conda-forge
314-
let channels = build_data
315-
.channels
316-
.clone()
315+
let variant_channels = if let Some(channel_sources) = discovered_output
316+
.used_vars
317+
.get(&NormalizedKey("channel_sources".to_string()))
318+
{
319+
Some(
320+
channel_sources
321+
.to_string()
322+
.split(',')
323+
.map(str::trim)
324+
.map(|s| NamedChannelOrUrl::from_str(s).into_diagnostic())
325+
.collect::<miette::Result<Vec<_>>>()?,
326+
)
327+
} else {
328+
None
329+
};
330+
331+
// priorities
332+
// 1. channel_sources from variant file
333+
// 2. channels from args
334+
// 3. channels from pixi_config
335+
// 4. conda-forge as fallback
336+
if variant_channels.is_some() && build_data.channels.is_some() {
337+
return Err(miette::miette!(
338+
"channel_sources and channels cannot both be set at the same time"
339+
));
340+
}
341+
let channels = variant_channels.unwrap_or_else(|| {
342+
build_data
343+
.channels
344+
.clone()
345+
.unwrap_or(vec![NamedChannelOrUrl::Name("conda-forge".to_string())])
346+
});
347+
348+
let channels = channels
317349
.into_iter()
318350
.map(|c| c.into_base_url(&tool_config.channel_config))
319351
.collect::<Result<Vec<_>, _>>()
@@ -632,8 +664,10 @@ pub async fn run_test(
632664

633665
let channels = test_data
634666
.channels
667+
.unwrap_or(vec![NamedChannelOrUrl::Name("conda-forge".to_string())]);
668+
let channels = channels
635669
.into_iter()
636-
.map(|name| Channel::from_str(name, &tool_config.channel_config).map(|c| c.base_url))
670+
.map(|c| c.into_base_url(&tool_config.channel_config))
637671
.collect::<Result<Vec<_>, _>>()
638672
.into_diagnostic()?;
639673

src/opt.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl CommonData {
237237
// mirror config
238238
// todo: this is a duplicate in pixi and pixi-pack: do it like in `compute_s3_config`
239239
let mut mirror_config = HashMap::new();
240-
tracing::info!("Using mirrors: {:?}", config.mirror_map());
240+
tracing::debug!("Using mirrors: {:?}", config.mirror_map());
241241

242242
fn ensure_trailing_slash(url: &url::Url) -> url::Url {
243243
if url.path().ends_with('/') {
@@ -449,7 +449,7 @@ pub struct BuildData {
449449
pub build_platform: Platform,
450450
pub target_platform: Platform,
451451
pub host_platform: Platform,
452-
pub channels: Vec<NamedChannelOrUrl>,
452+
pub channels: Option<Vec<NamedChannelOrUrl>>,
453453
pub variant_config: Vec<PathBuf>,
454454
pub ignore_recipe_variants: bool,
455455
pub render_only: bool,
@@ -508,7 +508,7 @@ impl BuildData {
508508
host_platform: host_platform
509509
.or(target_platform)
510510
.unwrap_or(Platform::current()),
511-
channels: channels.unwrap_or(vec![NamedChannelOrUrl::Name("conda-forge".to_string())]),
511+
channels,
512512
variant_config: variant_config.unwrap_or_default(),
513513
ignore_recipe_variants,
514514
render_only,
@@ -603,7 +603,7 @@ fn parse_key_val(s: &str) -> Result<(String, Value), Box<dyn Error + Send + Sync
603603
pub struct TestOpts {
604604
/// Channels to use when testing
605605
#[arg(short = 'c', long = "channel")]
606-
pub channels: Option<Vec<String>>,
606+
pub channels: Option<Vec<NamedChannelOrUrl>>,
607607

608608
/// The package file to test
609609
#[arg(short, long)]
@@ -621,7 +621,7 @@ pub struct TestOpts {
621621
#[derive(Debug, Clone)]
622622
#[allow(missing_docs)]
623623
pub struct TestData {
624-
pub channels: Vec<String>,
624+
pub channels: Option<Vec<NamedChannelOrUrl>>,
625625
pub package_file: PathBuf,
626626
pub compression_threads: Option<u32>,
627627
pub common: CommonData,
@@ -642,13 +642,13 @@ impl TestData {
642642
/// Create a new instance of `TestData`
643643
pub fn new(
644644
package_file: PathBuf,
645-
channels: Option<Vec<String>>,
645+
channels: Option<Vec<NamedChannelOrUrl>>,
646646
compression_threads: Option<u32>,
647647
common: CommonData,
648648
) -> Self {
649649
Self {
650650
package_file,
651-
channels: channels.unwrap_or(vec!["conda-forge".to_string()]),
651+
channels,
652652
compression_threads,
653653
common,
654654
}
@@ -1202,7 +1202,7 @@ pub struct DebugData {
12021202
/// Host platform for runtime dependencies
12031203
pub host_platform: Platform,
12041204
/// List of channels to search for dependencies
1205-
pub channels: Vec<NamedChannelOrUrl>,
1205+
pub channels: Option<Vec<NamedChannelOrUrl>>,
12061206
/// Common configuration options
12071207
pub common: CommonData,
12081208
/// Name of the specific output to debug (if recipe has multiple outputs)
@@ -1221,9 +1221,7 @@ impl DebugData {
12211221
host_platform: opts
12221222
.host_platform
12231223
.unwrap_or_else(|| opts.target_platform.unwrap_or(Platform::current())),
1224-
channels: opts.channels.unwrap_or(vec![
1225-
NamedChannelOrUrl::from_str("conda-forge").expect("conda-forge is parseable"),
1226-
]),
1224+
channels: opts.channels,
12271225
common: CommonData::from_opts_and_config(opts.common, config.unwrap_or_default()),
12281226
output_name: opts.output_name,
12291227
}

src/variant_render.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,8 +372,9 @@ pub(crate) fn stage_1_render<S: SourceCode>(
372372
additional_variables.insert("CONDA_BUILD_SYSROOT".into());
373373
}
374374

375-
// also always add `target_platform` and `channel_targets`
375+
// also always add `target_platform`, `channel_sources` and `channel_targets`
376376
additional_variables.insert("target_platform".into());
377+
additional_variables.insert("channel_sources".into());
377378
additional_variables.insert("channel_targets".into());
378379

379380
// Environment variables can be overwritten by the variant configuration
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
channel_sources:
2+
- conda-forge/label/rust_dev,conda-forge
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package:
2+
name: channel-sources
3+
version: 0.1.0
4+
5+
requirements:
6+
build:
7+
- rust ==1.88.0.dev20250428

test/end-to-end/test_simple.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,3 +1496,27 @@ def test_line_breaks(rattler_build: RattlerBuild, recipes: Path, tmp_path: Path)
14961496
assert found_lines[i], f"Expected to find 'line {i}' in the output"
14971497

14981498
assert any("done" in line for line in output_lines)
1499+
1500+
1501+
def test_channel_sources(
1502+
rattler_build: RattlerBuild, recipes: Path, tmp_path: Path, monkeypatch
1503+
):
1504+
with pytest.raises(CalledProcessError):
1505+
# channel_sources and channels cannot both be set at the same time
1506+
rattler_build.build(
1507+
recipes / "channel_sources",
1508+
tmp_path,
1509+
custom_channels=["conda-forge"],
1510+
)
1511+
1512+
output = rattler_build.build(
1513+
recipes / "channel_sources",
1514+
tmp_path,
1515+
extra_args=["--render-only"],
1516+
)
1517+
1518+
output_json = json.loads(output)
1519+
assert output_json[0]["build_configuration"]["channels"] == [
1520+
"https://conda.anaconda.org/conda-forge/label/rust_dev",
1521+
"https://conda.anaconda.org/conda-forge",
1522+
]

0 commit comments

Comments
 (0)