* Documentation around adding custom fetchers to layer
@ 2025-09-16 20:04 seth.raymond
2025-09-17 9:14 ` [yocto] " Ross Burton
0 siblings, 1 reply; 8+ messages in thread
From: seth.raymond @ 2025-09-16 20:04 UTC (permalink / raw)
To: yocto
[-- Attachment #1: Type: text/plain, Size: 2032 bytes --]
Hello,
I am attempting to write a couple of custom fetcher that pull secrets from our password manager (using its CLI). To start, I wrote Python classes that inherit from bb.fetch2.FetchMethod and registered them in poky/bitbake/lib/bb/fetch2/__init__.py by importing them and appending them to methods. I confirmed that the fetchers themselves are working as expected and the secrets are getting pulled during the recipe build process.
Of course, I don't (necessarily) want to contribute these fetchers upstream to poky. I would like to add them to one of our company's layers. Our directory structure is laid out like:
top-dir/
|--- build/
|--- layers/
| |--- poky/
| |--- meta-matrixspace/
| | |--- meta-mtrx-core/ (this is the layer I want to add my fetchers to)
| | |--- meta-mtrx-other-layer/
| | |--- meta-mtrx-yet-another-layer/
We're based off of scarthgap. I've put my fetchers into top-dir/layers/meta-matrixspace/meta-mtrx-core/lib/bb/fetch2/op.py and updated top-dir/layers/meta-matrixspace/meta-mtrx-core/conf/layer.conf to include
PYTHONPATH .= ":${LAYERDIR}/lib"
When building recipes that use the fetchers, I get parsing errors saying that no fetchers could be found to support the URL:
bb.data_smart.ExpansionError: Failure expanding variable fetcher_hashes_dummyfunc[vardepvalue], expression was ${@bb.fetch.get_hashvalue(d)} which triggered exception NoMethodError: Could not find a fetcher which supports the URL: 'optemplate://aes_key.tpl;output=aes.key'
The variable dependency chain for the failure is: fetcher_hashes_dummyfunc[vardepvalue]
I'm not seeing any documentation on how to add a custom fetcher to a layer, and the fetchers in poky don't provide enough information on how to register these fetchers. Is this functionality supported, or will we have to fork poky and keep it up-to-date to add a new fetcher?
Thanks,
Seth
[-- Attachment #2: Type: text/html, Size: 2638 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [yocto] Documentation around adding custom fetchers to layer
2025-09-16 20:04 Documentation around adding custom fetchers to layer seth.raymond
@ 2025-09-17 9:14 ` Ross Burton
2025-09-17 9:19 ` Alexander Kanavin
0 siblings, 1 reply; 8+ messages in thread
From: Ross Burton @ 2025-09-17 9:14 UTC (permalink / raw)
To: yocto@lists.yoctoproject.org, seth.raymond@matrixspace.com,
bitbake-devel
On 16 Sep 2025, at 21:04, seth.raymond via lists.yoctoproject.org <seth.raymond=matrixspace.com@lists.yoctoproject.org> wrote:
>
> Hello,
> I am attempting to write a couple of custom fetcher that pull secrets from our password manager (using its CLI). To start, I wrote Python classes that inherit from bb.fetch2.FetchMethod and registered them in poky/bitbake/lib/bb/fetch2/__init__.py by importing them and appending them to methods. I confirmed that the fetchers themselves are working as expected and the secrets are getting pulled during the recipe build process.
> Of course, I don't (necessarily) want to contribute these fetchers upstream to poky. I would like to add them to one of our company's layers. Our directory structure is laid out like:
...
> PYTHONPATH .= ":${LAYERDIR}/lib"
> When building recipes that use the fetchers, I get parsing errors saying that no fetchers could be found to support the URL:
>
> bb.data_smart.ExpansionError: Failure expanding variable fetcher_hashes_dummyfunc[vardepvalue], expression was ${@bb.fetch.get_hashvalue(d)} which triggered exception NoMethodError: Could not find a fetcher which supports the URL: 'optemplate://aes_key.tpl;output=aes.key'
> The variable dependency chain for the failure is: fetcher_hashes_dummyfunc[vardepvalue]
>
> I'm not seeing any documentation on how to add a custom fetcher to a layer, and the fetchers in poky don't provide enough information on how to register these fetchers. Is this functionality supported, or will we have to fork poky and keep it up-to-date to add a new fetcher?
This is a bitbake issue, so I’ve copied in the bitbake list.
There’s no standard way to add out-of-bitbake fetchers, as you say you need to add the fetcher instance to bb.fetch2.methods.
I guess you might be able to write a do_fetch prefunc that creates an instance of your fetcher and adds it to the list, but I would not be surprised if there were more places where that would be needed.
python use_my_fetcher() {
bb.fetch2.methods.append(MyFetcher())
}
do_fetch[prefuncs] += “use_my_fetcher”
Ross
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [yocto] Documentation around adding custom fetchers to layer
2025-09-17 9:14 ` [yocto] " Ross Burton
@ 2025-09-17 9:19 ` Alexander Kanavin
2025-09-17 14:45 ` seth.raymond
2025-09-17 22:33 ` [bitbake-devel] " Richard Purdie
0 siblings, 2 replies; 8+ messages in thread
From: Alexander Kanavin @ 2025-09-17 9:19 UTC (permalink / raw)
To: yocto, ross.burton; +Cc: seth.raymond@matrixspace.com, bitbake-devel
On Wed, 17 Sept 2025 at 11:15, Ross Burton via lists.yoctoproject.org
<ross.burton=arm.com@lists.yoctoproject.org> wrote:
> > I'm not seeing any documentation on how to add a custom fetcher to a layer, and the fetchers in poky don't provide enough information on how to register these fetchers. Is this functionality supported, or will we have to fork poky and keep it up-to-date to add a new fetcher?
>
> This is a bitbake issue, so I’ve copied in the bitbake list.
>
> There’s no standard way to add out-of-bitbake fetchers, as you say you need to add the fetcher instance to bb.fetch2.methods.
>
> I guess you might be able to write a do_fetch prefunc that creates an instance of your fetcher and adds it to the list, but I would not be surprised if there were more places where that would be needed.
>
> python use_my_fetcher() {
> bb.fetch2.methods.append(MyFetcher())
> }
> do_fetch[prefuncs] += “use_my_fetcher”
To be honest, I was going to say that custom fetchers are not
supported. Doing that would fragment the ecosystem and make people
invent all kinds of private magic in their builds, and subvert the
promises that standard fetchers make.
Perhaps it would help to step back and look at what this customer
fecher does. Can we see its source? Or an extended description of how
the password manager is used, what data it takes and produces, and how
that data is used in actual fetching? Then we'd think of how to do
this with regular fetchers.
Alex
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [yocto] Documentation around adding custom fetchers to layer
2025-09-17 9:19 ` Alexander Kanavin
@ 2025-09-17 14:45 ` seth.raymond
2025-09-18 7:58 ` Alexander Kanavin
2025-09-21 21:22 ` Chuck Wolber
2025-09-17 22:33 ` [bitbake-devel] " Richard Purdie
1 sibling, 2 replies; 8+ messages in thread
From: seth.raymond @ 2025-09-17 14:45 UTC (permalink / raw)
To: Alexander Kanavin, yocto
[-- Attachment #1.1: Type: text/plain, Size: 1765 bytes --]
Hi Alex,
Sure, I can share the source. It grabs secrets from our password manager (1Password) and injects them into the build system. We currently have two use cases for this:
1. We use AES keys for encrypting/decrypting our SWU payloads. Those key/iv pairs are held in our password manager, so we need to take one of them out at build-time and hand it to the swupdate bbclass to encrypt the payload. We do the same thing for a signing .pem key
2. We want to install an SSH key into our OS so that we can SSH into our devices. 1Password also acts as an SSH agent, so it makes life seamless when we store the keys in there.
My source is attached. The use case in a recipe would be:
SRC_URI = " \
op://MyVault/SSHKey/public%20key ;output=ssh_key.pub \
optemplate://aes_key.tpl;output=aes_key \
"
The output would be:
1. The content of the MyVault > SSHKey > public key dumped into a file called ssh_key.pub in the $WORKDIR
2. Depending on the template ( https://developer.1password.com/docs/cli/secrets-template-syntax/ ), ${WORKDIR}/aes_key would be filled in with referenced secrets
Someone at my last company wrote a custom fetcher to fetch conan packages, but I unfortunately wasn't super plugged in to what he was doing. I vaguely remember he did something with a bbclass and might have used something similar to the proposed do_fetch[prefuncs] method.
We currently have a hacky workaround right now where we have a *-init-build-env script that pulls the secrets from the 1Password vault(s) and put them in the correct place. But persisting secrets on-disk, unencrypted is non-ideal. My preference would be for us to only grab the secrets as we need them and then clean them up from build/ after they've been consumed.
Cheers,
Seth
[-- Attachment #1.2: Type: text/html, Size: 2260 bytes --]
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: op.py --]
[-- Type: text/x-python; name="op.py", Size: 4612 bytes --]
import os
from pathlib import Path
import bb
from bb.fetch2 import FetchError, FetchMethod, runfetchcmd
class OpItem(FetchMethod):
"""Fetch method that runs `op read` on a 1Password CLI item path. URI should be in the format of:
op://<Vault>/<Item>/[<Section>/]<Field>
Note that all spaces in the string should be replaced with %20 so that bitbake can parse it properly.
"""
def supports(self, ud, d):
# Recognize URLs like: op://Vault/Item/[Section/]Field
return ud.type in ["op"]
def recommends_checksum(self, urldata):
return False
def supports_checksum(self, urldata):
return False
def urldata_init(self, ud, d):
# The 1Password path is given as the 'path' part of the URL.
# For example: op://Vault/Item/[Section/]Field
ud.path = ud.url.split("://", 1)[1].split(";")[0]
if not ud.path:
raise FetchError("Invalid op:// URI")
ud.path = f'op://{ud.path.replace("%20", " ")}'
# Optional query parameter: output=<filename>
ud.output = ud.parm.get("output") or os.path.basename(ud.path) + ".rendered"
# Store local destination path
ud.localfile = ud.output
def download(self, ud, d):
"""
Run `op read` to dump the contents of a field into a file.
"""
dl_dir = d.getVar("DL_DIR")
dest = os.path.join(dl_dir, ud.output)
os.environ["OP_SERVICE_ACCOUNT_TOKEN"] = d.getVar("OP_SERVICE_ACCOUNT_TOKEN")
cmd = f"/usr/local/bin/op read --out-file {dest} '{ud.path}'"
runfetchcmd(cmd, d)
if not os.path.exists(dest):
raise FetchError("op read did not produce an output file")
# The localfile is the file BitBake will checksum and stage.
ud.localpath = dest
def unpack(self, ud, destdir, d):
"""
Standard fetcher API: copy the injected file into the unpacked source dir.
"""
bb.utils.copyfile(ud.localpath, os.path.join(destdir, ud.output))
def checkstatus(self, fetch, urldata, d):
return True
class OpTemplate(FetchMethod):
"""Fetch method that runs `op inject` on a 1Password CLI injectable template."""
def supports(self, ud, d):
# Recognize URLs like: optemplate://path/to/template
return ud.type in ["optemplate"]
def recommends_checksum(self, urldata):
return False
def supports_checksum(self, urldata):
return False
def urldata_init(self, ud, d):
# The template file is given as the 'path' part of the URL.
# For example: optemplate:///absolute/path/to/template
ud.template = ud.url.split("://", 1)[1].split(";")[0]
if not ud.template:
raise FetchError("Missing template path in optemplate:// URL")
# Optional query parameter: output=<filename>
ud.output = ud.parm.get("output") or os.path.basename(ud.template) + ".rendered"
# Store local destination path
ud.localfile = ud.output
def download(self, ud, d):
"""
Run `op inject` to resolve secrets in the template and
produce the formatted output file.
"""
template_path = ""
if not os.path.exists(ud.template):
filespath = d.getVar("FILESPATH")
for location in filter(None, [Path(p) for p in filespath.split(":")]):
for glob_hit in location.glob(ud.template):
if glob_hit.exists():
template_path = location / ud.template
bb.note(f"Found template file at {template_path}")
break
if not template_path:
raise FetchError(f"Template file not found: {ud.template}")
dl_dir = d.getVar("DL_DIR")
dest = os.path.join(dl_dir, ud.output)
os.environ["OP_SERVICE_ACCOUNT_TOKEN"] = d.getVar("OP_SERVICE_ACCOUNT_TOKEN")
bb.note(f"Injecting 1Password template: {ud.template} -> {dest}")
cmd = f"/usr/local/bin/op inject -f -i {template_path} -o {dest}"
runfetchcmd(cmd, d)
if not os.path.exists(dest):
raise FetchError("op inject did not produce output file")
# The localfile is the file BitBake will checksum and stage.
ud.localpath = dest
def unpack(self, ud, destdir, d):
"""
Standard fetcher API: copy the injected file into the unpacked source dir.
"""
bb.utils.copyfile(ud.localpath, os.path.join(destdir, ud.output))
def checkstatus(self, fetch, urldata, d):
return True
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [bitbake-devel] [yocto] Documentation around adding custom fetchers to layer
2025-09-17 9:19 ` Alexander Kanavin
2025-09-17 14:45 ` seth.raymond
@ 2025-09-17 22:33 ` Richard Purdie
1 sibling, 0 replies; 8+ messages in thread
From: Richard Purdie @ 2025-09-17 22:33 UTC (permalink / raw)
To: alex.kanavin, yocto, ross.burton
Cc: seth.raymond@matrixspace.com, bitbake-devel
On Wed, 2025-09-17 at 11:19 +0200, Alexander Kanavin via lists.openembedded.org wrote:
> On Wed, 17 Sept 2025 at 11:15, Ross Burton via lists.yoctoproject.org
> <ross.burton=arm.com@lists.yoctoproject.org> wrote:
> > > I'm not seeing any documentation on how to add a custom fetcher to a layer, and the fetchers in poky don't provide enough information on how to register these fetchers. Is this functionality supported, or will we have to fork poky and keep it up-to-date to add a new fetcher?
> >
> > This is a bitbake issue, so I’ve copied in the bitbake list.
> >
> > There’s no standard way to add out-of-bitbake fetchers, as you say you need to add the fetcher instance to bb.fetch2.methods.
> >
> > I guess you might be able to write a do_fetch prefunc that creates an instance of your fetcher and adds it to the list, but I would not be surprised if there were more places where that would be needed.
> >
> > python use_my_fetcher() {
> > bb.fetch2.methods.append(MyFetcher())
> > }
> > do_fetch[prefuncs] += “use_my_fetcher”
>
> To be honest, I was going to say that custom fetchers are not
> supported. Doing that would fragment the ecosystem and make people
> invent all kinds of private magic in their builds, and subvert the
> promises that standard fetchers make.
>
> Perhaps it would help to step back and look at what this customer
> fecher does. Can we see its source? Or an extended description of how
> the password manager is used, what data it takes and produces, and how
> that data is used in actual fetching? Then we'd think of how to do
> this with regular fetchers.
I'm struggling with time/bandwidth so I'll just mention what I can
remember without digging too much.
There have been cases of successfully injecting extra fetchers from the
metadata, particularly if you do it from the global class code,
probably in an event handler. I think some of the early npm/node/crate
code did this (I don't remember which) before we merged things into
bitbake itself.
There was an open bug for making it easier to define new fetchers from
the metadata and we did get most things working. Sadly I can't spot the
bug. The one area that was problematic was AUTOREV since that happened
before you could inject the fetcher in the class code in some cases. We
never did work out an API which would allow AUTOREV to work and allow a
general API.
Cheers,
Richard
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [yocto] Documentation around adding custom fetchers to layer
2025-09-17 14:45 ` seth.raymond
@ 2025-09-18 7:58 ` Alexander Kanavin
2025-09-18 13:25 ` seth.raymond
2025-09-21 21:22 ` Chuck Wolber
1 sibling, 1 reply; 8+ messages in thread
From: Alexander Kanavin @ 2025-09-18 7:58 UTC (permalink / raw)
To: seth.raymond; +Cc: yocto
On Wed, 17 Sept 2025 at 16:45, seth.raymond via Lists.Yoctoproject.Org
<seth.raymond=matrixspace.com@lists.yoctoproject.org> wrote:
> Sure, I can share the source. It grabs secrets from our password manager (1Password) and injects them into the build system. We currently have two use cases for this:
> 1. We use AES keys for encrypting/decrypting our SWU payloads. Those key/iv pairs are held in our password manager, so we need to take one of them out at build-time and hand it to the swupdate bbclass to encrypt the payload. We do the same thing for a signing .pem key
> 2. We want to install an SSH key into our OS so that we can SSH into our devices. 1Password also acts as an SSH agent, so it makes life seamless when we store the keys in there.
>
> My source is attached. The use case in a recipe would be:
>
> SRC_URI = " \
> op://MyVault/SSHKey/public%20key;output=ssh_key.pub \
> optemplate://aes_key.tpl;output=aes_key \
> "
>
> The output would be:
> 1. The content of the MyVault > SSHKey > public key dumped into a file called ssh_key.pub in the $WORKDIR
> 2. Depending on the template (https://developer.1password.com/docs/cli/secrets-template-syntax/), ${WORKDIR}/aes_key would be filled in with referenced secrets
But this doesn't require a fetcher implementation. You can do it in a
task, put the task into a class, and write recipes that inherit the
class. The meat of the fetcher is this:
cmd = f"/usr/local/bin/op read --out-file {dest} '{ud.path}'"
and this can simply write directly into ${S} from a task.
Alex
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [yocto] Documentation around adding custom fetchers to layer
2025-09-18 7:58 ` Alexander Kanavin
@ 2025-09-18 13:25 ` seth.raymond
0 siblings, 0 replies; 8+ messages in thread
From: seth.raymond @ 2025-09-18 13:25 UTC (permalink / raw)
To: Alexander Kanavin, yocto
[-- Attachment #1: Type: text/plain, Size: 130 bytes --]
Yep, I'm pivoting to just make this a bbclass instead. It feels less elegant, but it does the job. Thanks for the tips!
-Seth
[-- Attachment #2: Type: text/html, Size: 169 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [yocto] Documentation around adding custom fetchers to layer
2025-09-17 14:45 ` seth.raymond
2025-09-18 7:58 ` Alexander Kanavin
@ 2025-09-21 21:22 ` Chuck Wolber
1 sibling, 0 replies; 8+ messages in thread
From: Chuck Wolber @ 2025-09-21 21:22 UTC (permalink / raw)
To: yocto, seth.raymond, Alexander Kanavin
On Wed Sep 17, 2025 at 2:53 PM UTC, seth.raymond via lists.yoctoproject.org wrote:
>
> Sure, I can share the source. It grabs secrets from our password manager
> (1Password) and injects them into the build system. We currently have two use
> cases for this:
It is probably worth noting that the 1Password v8 CLI and SDK both require a
round trip to the cloud even if you run the desktop 1P client locally (which
maintains a local copy of your vaults). The 1P CLI and SDK do _some_ caching,
but they still require a round trip for every call. My testing has shown that
this round-trip adds about a second when taking full advantage of what they
cache locally.
For a tool meant to help with automation, this falls into the "egregious"
category of oversights as far as I am concerned. After 12 years of 1P use, I am
actively exploring other options.
..Ch:W..
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-09-21 21:22 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-16 20:04 Documentation around adding custom fetchers to layer seth.raymond
2025-09-17 9:14 ` [yocto] " Ross Burton
2025-09-17 9:19 ` Alexander Kanavin
2025-09-17 14:45 ` seth.raymond
2025-09-18 7:58 ` Alexander Kanavin
2025-09-18 13:25 ` seth.raymond
2025-09-21 21:22 ` Chuck Wolber
2025-09-17 22:33 ` [bitbake-devel] " Richard Purdie
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.