Hello All, Writing my first plugin that creates sy...
# plugins
m
Hello All, Writing my first plugin that creates synthetic targets and I am wanting to provide a token to allow for http requests. Are there any patterns that could be used here?
f
You can read the token from an environment variable look for usages of
EnvironmentVarsRequest
https://github.com/pantsbuild/pants/blob/62828b7f56abeeea79b9249b8d97ae7d78847785/src/python/pants/backend/go/util_rules/cgo.py#L390
m
Have been trying to read from
EnvironmentVarsRequest
With the example seen here: https://www.pantsbuild.org/2.19/docs/writing-plugins/the-rules-api/processes#environment-variables
Copy code
TOKEN=asdad pants package ::
The token is used to dynamically create synthetic targets.
Copy code
token = await Get(EnvironmentVars, EnvironmentVarsRequest(["TOKEN"]))
Copy code
20:01:50.78 [ERROR] Encountered 14 rule graph errors:
  No installed rules return the type EnvironmentNameRequest, and it was not provided by potential callers of @rule(pants.core.util_rules.environments:702:resolve_environment_name(EnvironmentNameRequest, EnvironmentsSubsystem, GlobalOptions) -> EnvironmentName, gets=[ChosenLocalEnvironmentName, Get(EnvironmentTarget, [EnvironmentName]), Get(EnvironmentName, [EnvironmentNameRequest])]).
    If that type should be computed by a rule, ensure that that rule is installed.
    If it should be provided by a caller, ensure that it is included in any relevant Query or Get.
Doing something wrong here?
c
just a hunch, but make sure to annotate any pants union types you have with
Copy code
@union(in_scope_types=[EnvironmentName])
like so: https://github.com/pantsbuild/pants/blob/42ff1dd86a7c3b370a2802df272d27ff0bb42ce3/src/python/pants/engine/target.py#L1122
if this doesn't work, if you have a repo you can share, it'll make it easier to look at 😉
m
Copy code
import requests
from typing import Iterable
from dataclasses import dataclass
from pants.engine.internals.synthetic_targets import (
    SyntheticTargetsRequest,
    SyntheticAddressMaps
)

from pants.engine.internals.target_adaptor import TargetAdaptor
from pants.engine.unions import UnionRule, union
from pants.engine.environment import EnvironmentName
from pants.engine.env_vars import EnvironmentVars, EnvironmentVarsRequest
from pants.engine.rules import collect_rules, Get, rule
from pants.util.logging import LogLevel

@union(in_scope_types=[EnvironmentName])
@dataclass(frozen=True)
class SynthenticFooRequest(SyntheticTargetsRequest):
    path: str = SyntheticTargetsRequest.SINGLE_REQUEST_FOR_ALL_TARGETS
    pass

def generate_some_targets(versions: Iterable[str]) -> Iterable[TargetAdaptor]:
    """_summary_

    Args:
        versions (Iterable[str]): versions eg, a.b.c

    Returns:
        Iterable[TargetAdaptor]: Iterable pants targets
    """
    target_adapters = []
    for index, version in enumerate(versions):
        adapter = TargetAdaptor(
            "docker_image", name=f"test_{index}",
            __description_of_origin__= "some origin",
            source = "Dockerfile",
            dependencies = [
                ":some-other-docker",
            ],
            tags = [
                "some-test"
            ],
            extra_build_args=[f"BASE_IMAGE=some/image:{version}"]
        )
        target_adapters.append(adapter)
    return target_adapters


def get_ids_from_url(token: str) -> list[str]:
    url = "https:/some-url/"
    headers = {"TOKEN": token}
    ids = requests.get(url=url, headers=headers, timeout=30).json()
    return ids

async def get_ids() -> list[str]:
    envs = await Get(EnvironmentVars, EnvironmentVarsRequest(["TOKEN"]))
    token = envs.get("TOKEN")
    ids = get_ids_from_url(token)
    return ids


@rule(level=LogLevel.DEBUG)
async def synthetic_targets(request: SynthenticFooRequest) -> SyntheticAddressMaps:
    iids = get_ids()

    return SyntheticAddressMaps.for_targets_request(
        request,
        [
            (
                # Address
                "somePath//BUILD.synthetic-targets",
                (
                    generate_some_targets(iids)
                ),
            ),
        ]
    )

def rules():
    return (
        *collect_rules(),
        UnionRule(SyntheticTargetsRequest, SynthenticFooRequest),
    )
@curved-television-6568 Thanks for helping out.
👀 1
It still does not work, needing some guidance.
c
Aaah! this should've been called out in the docs for synthetic targets. The synthetic targets are "low level", and must work during the bootstrap phase of parsing the BUILD files, so environments are not available to rules providing synthetic targets. (in other words, my hunch previously does not apply in this case, as you can't use that) If you really need env vars, you can look for the host specific env vars using the
SessionValues
directly, indexing that using
CompleteEnvironmentVars
as here: https://github.com/pantsbuild/pants/blob/42ff1dd86a7c3b370a2802df272d27ff0bb42ce3/src/python/pants/engine/internals/platform_rules.py#L46-L72 This is on the edge of being hacky though.. as I don't think we'd consider this part of the PluginAPI, so it might break unexpectedly between pants releases if it is changed for some reason.. just beware 😉
👍 1
Also note, that you shouldn't provide the union decorator on your synthetic request subclass, and the correct way to register it is by using :
Copy code
def rules():
    return (
        *collect_rules(),
        *SynthenticFooRequest.rules(),
    )
m
Thanks, able to use the SessionValue directly for the time being.