Plugin API

The plugin registration and its options model are defined in cwl2codemeta.plugin.

Registration

from cwl2codemeta.plugin import cwl2codemeta
Attribute Value
Entry-point group transpiler_mate.plugins
Entry-point name cwl2codemeta
Entry-point object cwl2codemeta.plugin:cwl2codemeta
Registration name cwl2codemeta
Options model CWL2CodeMetaOptions
Execution return value None

cwl2codemeta is a PluginRegistration, not a plain conversion function. Invoke it directly with cwl2codemeta.execute(context, options) or let a Transpiler-Mate host invoke it.

Options

from cwl2codemeta.plugin import CWL2CodeMetaOptions
Field Type Model default Meaning
code_repository str \| None Required Repository URL used for SoftwareSourceCode metadata; None disables the wrapper for direct API calls.
output pathlib.Path codemeta.json JSON-LD file written by the plugin.

Unknown fields are forbidden. Because code_repository has no default, the generated CLI exposes --code-repository as required even though direct API callers can explicitly provide None.

Input contract

The plugin reads context.metadata, which must be a normalized SoftwareApplication supplied by the host. The current model requires these Schema.org properties:

  • name
  • description
  • dateCreated
  • license
  • softwareVersion
  • softwareHelp
  • publisher
  • author

The plugin serializes the model with Schema.org aliases, excludes properties whose value is None, and converts the result through JSON-LD compaction.

Output contract

The output is indented JSON with @context set to https://w3id.org/codemeta/3.0.

When code_repository is a string, the top-level @type is SoftwareSourceCode and targetProduct contains the input SoftwareApplication. With an explicit None, the top-level object remains a SoftwareApplication.

GitHub and GitLab URLs receive derived continuousIntegration, issueTracker, and relatedLink properties. Other recognized Git URL forms are normalized to HTTPS when supported by giturlparse, but do not receive derived links.

Errors

Any exception raised while parsing the repository, converting JSON-LD, opening the destination, or serializing the document is wrapped in PluginExecutionError, with the original exception chained as __cause__.

Python API details

Convert normalized CWL Schema.org metadata to CodeMeta JSON-LD.

CWL2CodeMetaOptions

Bases: BaseModel

Options accepted by the CWL-to-CodeMeta plugin.

Source code in src/cwl2codemeta/plugin.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class CWL2CodeMetaOptions(BaseModel):
    """Options accepted by the CWL-to-CodeMeta plugin."""

    model_config = ConfigDict(extra="forbid")

    code_repository: Annotated[
        str | None,
        Field(
            default=None,
            description="The (SVN, GitHub, CodePlex, ...) code repository URL",
        ),
    ]

    output: Annotated[
        Path,
        Field(default=Path("codemeta.json"), description="The output file path"),
    ]

cwl2codemeta(context, options)

Write normalized CWL software metadata as CodeMeta JSON-LD.

Source code in src/cwl2codemeta/plugin.py
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
@transpiler_plugin(
    name="cwl2codemeta",
    description="Convert CWL Schema.org metadata to CodeMeta 3.0 JSON-LD.",
    options_model=CWL2CodeMetaOptions,
)
def cwl2codemeta(context: TranspilerContext, options: CWL2CodeMetaOptions) -> None:
    """Write normalized CWL software metadata as CodeMeta JSON-LD."""

    metadata: SoftwareApplication | SoftwareSourceCode = context.metadata

    try:
        if options.code_repository:
            logger.debug(
                f"code_repository detected, analyzing: {options.code_repository}"
            )

            parsed_url: GitUrlParsed = gitparse(options.code_repository)

            continuous_integration: str | None = None
            issue_tracker: str | None = None
            related_links: list[str] = []

            logger.debug(f"Creating URLs for Git URL platform: {parsed_url.platform}")

            match parsed_url.platform:
                case "github":
                    continuous_integration = parsed_url.url2https.replace(
                        ".git", "/actions"
                    )
                    issue_tracker = parsed_url.url2https.replace(".git", "/issues")
                    related_links = [
                        parsed_url.url2https.replace(".git", page)
                        for page in ["/wiki", "/releases", "/deployments"]
                    ]

                case "gitlab":
                    continuous_integration = parsed_url.url2https.replace(
                        ".git", "/-/pipelines"
                    )
                    issue_tracker = parsed_url.url2https.replace(".git", "/-/issues")
                    related_links = [
                        parsed_url.url2https.replace(".git", page)
                        for page in ["/-/wikis/home", "/-/packages", "/-/pipelines"]
                    ]

                case _:
                    logger.warning(f"Platform {parsed_url.platform} unsupported yet")

            logger.debug("Rebuilding SoftwareSourceCode...")

            metadata = SoftwareSourceCode(
                code_repository=parsed_url.url2https,
                target_product=context.metadata,
                continuous_integration=AnyUrl(continuous_integration)
                if continuous_integration
                else None,
                issue_tracker=AnyUrl(issue_tracker) if issue_tracker else None,
                related_link=[AnyUrl(related_link) for related_link in related_links],
            )

            logger.debug("SoftwareSourceCode successfully rebuilt.")

        logger.info("Converting CWL Metadata to CodeMeta via JSON-LD conversion...")

        doc: dict[str, Any] = metadata.model_dump(exclude_none=True, by_alias=True)

        compacted: MutableMapping[str, Any] = jsonld.compact(
            doc,
            {"@vocab": "https://schema.org/"},
            options={"processingMode": "json-ld-1.1", "ordered": None},
        )

        compacted["@context"] = "https://w3id.org/codemeta/3.0"

        logger.success("CWL Metadata successfully converted to CodeMeta via JSON-LD!")

        if context.metadata.keywords and isinstance(context.metadata.keywords, list):
            compacted["keywords"] = list(
                filter(
                    lambda keyword: isinstance(keyword, str), context.metadata.keywords
                )
            )

        options.output.parent.mkdir(parents=True, exist_ok=True)
        logger.info(f"Serializing CodeMeta metadata to {options.output.absolute()}")

        with options.output.open("w") as output_stream:
            json.dump(compacted, output_stream, indent=2)

        logger.success(
            f"CodeMeta metadata successfully serialized to {options.output.absolute()}"
        )
    except Exception as e:
        raise PluginExecutionError(
            f"An error occurred when serializing to {options.output.absolute()}, see nested exception"
        ) from e