feat: support build backend secrets#6101
Conversation
Add a `secrets` list to `[package.build]` so the manifest can declare
environment variables that should be exposed to the build script as
secrets. Names flow from the manifest into the `ProjectModel` sent to
the build backend; the intermediate backend then merges them into the
generated recipe's `build.script.secrets`, where rattler-build performs
the host-environment passthrough at build time.
Only names cross the wire — values are looked up by rattler-build from
the host environment during the build, so no secret material is
serialized in protocol messages or build records.
Example:
[package.build]
backend = { name = "pixi-build-rust", version = "*" }
secrets = ["CARGO_REGISTRY_TOKEN", "SCCACHE_BUCKET"]
|
@ruben-arts thoughts? This came from Wintermute having issues using |
|
If it works the same as rattler-build I'm all for it! |
|
Yeah teh biggest question is where to put the config in the |
| fn merge_project_secrets(script_secrets: &mut Vec<String>, project_secrets: &[String]) { | ||
| for name in project_secrets { | ||
| if !script_secrets.iter().any(|existing| existing == name) { | ||
| script_secrets.push(name.clone()); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
I dont think this is correct. The secrets are part of the project model, so the backend should have added the secrets when generating the initial recipe.
|
|
||
| let build_string_prefix = th.optional("build-string-prefix"); | ||
| let build_number = th.optional("build-number"); | ||
| let secrets: Vec<String> = th.optional("secrets").unwrap_or_default(); |
There was a problem hiding this comment.
I dont think this type annotation is needed, is it?
baszalmstra
left a comment
There was a problem hiding this comment.
Can you also update the schema (model.py) and the documentation?
|
Please also use the PR template from the repository. |
|
I am fine with the comments but for me the main question is whether that is the right place to put the |
- switch ProjectModel.secrets and PackageBuild.secrets from Vec to BTreeSet so order changes don't invalidate caches (baszalmstra) - move secrets injection into individual backends; each generate_recipe impl now calls .with_secrets(model.secrets...) directly instead of having the intermediate backend post-process the recipe (baszalmstra) - drop redundant test_secrets_default_empty - add IsDefault impl for BTreeSet to support the field in stable hashing - document secrets under [package.build] in pixi_manifest.md and schema/model.py https://claude.ai/code/session_015JhTEMr2bEMaK4iSEiWbjj
It would be nice to have it at the workspace level also so we don't have to repeat for every package in a monorepo. |
|
That goes a bit against the isolation of the package though, and "depending on what you need" ... |
|
That's a fair point. I see it similar to workspace dependencies proposal from bas (on my phone sorry will link in an edit). Maybe a {workspace = true} syntax similar to the proposal? |
|
Longer version not from my phone. I think the isolation is a great idea. The main point being security. My monorepo includes packaging for third_party packages we had to patch. We could reasonably get supply chain hacked and have our secret environment variables exposed since most build systems can execute arbitrary code. So we definitely should not do the default "passthru to all" approach. However, we could consider an explicit one. There is this proposal from @baszalmstra which will add the [workspace.dependencies] table. #5945 which will add to the list of fields that packages can inherit from the workspace. I think in the interest of consistency. I think it would be a good idea to allow a [package.build]
secret = ["MYBLAH", {workspace = true}]so that the general concept of "packages can inherit from the workspace" isn't too arbitrary decided per field. That being said I also see the justification for only allowing package specific secrets and making this an exception. Since broadly sending build secrets to all packages build systems sounds like a code smell at face value. And the maintenance burden isn't very high. |
| .collect::<std::collections::BTreeSet<_>>() | ||
| .into_iter() | ||
| .collect(); |
There was a problem hiding this comment.
Collecting twice seems unnecesary
| let build_string_prefix = th.optional("build-string-prefix"); | ||
| let build_number = th.optional("build-number"); | ||
| let secrets = th | ||
| .optional::<Vec<String>>("secrets") |
There was a problem hiding this comment.
Cant we just use BTreeSet here? Instead of collecting twice?
There was a problem hiding this comment.
Tried optional::<BTreeSet<String>>(...) but BTreeSet doesn't implement toml_span::Deserialize (only Vec, IndexSet via the local TomlIndexSet, etc.), so the workspace fails to compile. Reverted to parsing as Vec<String> and collecting once into the field's BTreeSet. Happy to add a TomlBTreeSet wrapper in pixi_toml if you'd prefer that route.
Generated by Claude Code
There was a problem hiding this comment.
Yes I want that. Its nonsense to collect twice.
Adds a TomlBTreeSet deserializer in pixi_toml so the secrets field can be parsed into the BTreeSet without an intermediate Vec collect. https://claude.ai/code/session_015JhTEMr2bEMaK4iSEiWbjj
|
I could see Alternatively, we could also add a flag to disable the isolation globally (we have the settings in rattler-build). Passing through secrets like this is probably the cleanest option though. |
Description
Adds a
secretslist to[package.build]so manifests can declare environment variables that should be exposed to the build script as secrets, mirroring rattler-build'sbuild.script.secrets.Motivation: in the rattler-build recipe execution we restrict env access by default. This gives package authors a way to opt specific env vars through, without putting secret values in the manifest. Useful for credentials such as
GH_TOKEN, registry tokens, sccache config, etc.How Has This Been Tested?
Snapshots updated and TOML parsing tests added.
AI Disclosure
Tools: Claude (Claude Code)
Checklist: