Skip to content
Snippets Groups Projects
Commit 91146050 authored by Olivier Maury's avatar Olivier Maury
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
dist
__pycache__
This diff is collapsed.
# pom2metadata
`pom2metadata` is a command line tool to create or update metadata project files from information stored in the Maven `pom.xml` file.
The metadata handled are:
- `AUTHORS.md`,
- [`CITATION.cff`](https://citation-file-format.github.io/),
- [`codemeta.json`](https://codemeta.github.io/),
- [`publiccode.yml`](https://docs.italia.it/italia/developers-italia/publiccodeyml-en/).
It uses [CodeMetaPy](https://pypi.org/project/CodeMetaPy/) to create or update `codemeta.json` ([codemeta](https://codemeta.github.io/) software metadata standard in JSON-LD).
[![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch)
## Installation
```
pip install pom2metadata
```
## Usage
In the source directory where `pom.xml` is:
```
pom2metadata
```
The Maven file `pom.xml` will be parsed to create or update the various metadata files.
Some new tags where added to fill fields in metadata files as much as possible:
- in `developers > developer > properties`:
- `metadata.gitlab-id`: identifier in GitLab, such as `@firstname.familyname`,
- `metadata.gitlab`: user's home page at GitLab, such as `https://gitlab/firstname.familyname`,
- `metadata.orcid`: ORCID URL, such as `https://orcid.org/0000-0001-2345-6789`.
- in `properties`:
- `metadata.description`, with attribute `xml:lang` to set translations for `description`,
- `metadata.long-description`, with attribute `xml:lang` to define the long description in all languages.
Moreover, take care of some tags:
- as [recommended](https://maven.apache.org/pom.html#licenses), `licenses > license > name` should use a [SPDX identifier](https://spdx.org/licenses/).
## Development
### Preparing
- Install [hatch](https://hatch.pypa.io/):
```
sudo apt install pipx
pipx install hatch
```
- Make sure `$HOME/.local/bin` is in the `PATH`
```
pipx ensurepath
```
### Developing
- Create environment: `pipx run hatch env create`.
- Run: `pipx run hatch env run pom2metadata`.
To update version: `hatch version 0.1.0'.
### Install local project
To install from sources, with pip, there are two ways to do this for:
* a regular install: from the source directory, `pip install`, or from elsewhere `pip install path/to/pom2metadata`,
* an [editable install](https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs): `pip install -e path/to/pom2metadata`.
If your installed pip is too old (and handles only `setup.py` files), use the Hatch environment:
```
hatch shell
pip install -e path/to/pom2metadata
```
### Generating distribution archives
To generate [distribution packages](https://packaging.python.org/en/latest/glossary/#term-Distribution-Package) - archives that are uploaded to the Python Package Index and can be installed by pip - for the package using with [hatch](https://hatch.pypa.io/latest/build/): `hatch build`.
## Authors
See [`AUTHORS.md`](AUTHORS.md) file.
## License
See [`License.md`](License.md) file.
{
"@context": [
"https://doi.org/10.5063/schema/codemeta-2.0",
"https://w3id.org/software-iodata",
"https://raw.githubusercontent.com/jantman/repostatus.org/master/badges/latest/ontology.jsonld",
"https://schema.org",
"https://w3id.org/software-types"
],
"@type": "SoftwareSourceCode",
"author": [
{
"@id": "https://orcid.org/0000-0001-9016-9720",
"@type": "Person",
"familyName": "Maury",
"givenName": "Olivier"
}
],
"codeRepository": "https://forgemia.inra.fr/olivier.maury/pom2metadata",
"description": "A simple tool to create or update metadata project files from pom.xml.",
"identifier": "pom2metadata",
"issueTracker": "https://forgemia.inra.fr/olivier.maury/pom2metadata/-/issues",
"license": "http://spdx.org/licenses/GPL-3.0-only",
"name": "pom2metadata",
"operatingSystem": "OS Independent",
"runtimePlatform": "Python 3",
"softwareRequirements": [
{
"@type": "SoftwareApplication",
"identifier": "codemetapy",
"name": "codemetapy",
"runtimePlatform": "Python 3",
"version": ">= 2.5.0"
},
{
"@type": "SoftwareApplication",
"identifier": "pyyaml",
"name": "pyyaml",
"runtimePlatform": "Python 3",
"version": ">= 5.3.1"
}
],
"targetProduct": {
"@type": "CommandLineApplication",
"executableName": "pom2metadata",
"name": "pom2metadata",
"runtimePlatform": "Python 3"
},
"url": "https://forgemia.inra.fr/olivier.maury/pom2metadata"
}
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "pom2metadata"
dynamic = ["version"]
description = "A simple tool to create or update metadata project files from pom.xml."
readme = "README.md"
requires-python = ">=3.7"
keywords = []
license = "GPL-3.0-only"
authors = [
{ name = "Olivier Maury", email = "Olivier.Maury@inrae.fr" },
]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: OS Independent",
]
dependencies = [
"codemetapy>=2.5.0",
"pyyaml>=5.3.1"
]
[project.urls]
Homepage = "https://forgemia.inra.fr/olivier.maury/pom2metadata/"
Tracker = "https://forgemia.inra.fr/olivier.maury/pom2metadata/-/issues"
Source = "https://forgemia.inra.fr/olivier.maury/pom2metadata/"
[project.scripts]
pom2metadata = "pom2metadata:main"
[tool.hatch.version]
path = "src/pom2metadata/__about__.py"
[tool.hatch.envs.default]
python = "/usr/bin/python3.8"
[[tool.hatch.envs.all.matrix]]
python = ["3.7", "3.8", "3.9", "3.10", "3.11"]
# SPDX-FileCopyrightText: 2023-present Olivier Maury <Olivier.Maury@inrae.fr>
#
# SPDX-License-Identifier: GPL-3.0-only
__version__ = "0.1.0"
# -*- coding: UTF-8 -*-
import logging
import os.path
import sys
import yaml
from . import citation
from . import pomxml
from . import publiccode
def create_authors(pom: pomxml.Pom):
logging.info('Creating AUTHORS.md')
with open('AUTHORS.md', 'w') as file:
file.write('# Authors\n')
for x in pom.developers:
file.write('\n## %s\n' % x.name)
if x.gitlab_id is not None and x.gitlab is not None:
file.write('- GitLab : [%s](%s)\n' % (x.gitlab_id, x.gitlab))
if x.orcid is not None:
file.write('- [ORCID](%s)\n' % x.orcid)
def create_sbom():
logging.info('Creating SBOM (codemeta.json)')
try:
from codemeta import codemeta
except ModuleNotFoundError:
logging.error("Codemetapy not found. Try pip install codemetapy")
sys.exit(1)
sys.argv = sys.argv + ['pom.xml', '-O', 'codemeta.json']
codemeta.main()
def main():
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
pom = pomxml.parse('pom.xml')
create_authors(pom)
create_sbom()
citation.handle(pom)
publiccode.handle(pom)
logging.info('Done')
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from . import main
if __name__ == '__main__':
main()
# SPDX-FileCopyrightText: 2023-present Olivier Maury <Olivier.Maury@inrae.fr>
#
# SPDX-License-Identifier: GPL-3.0-only
import logging
import os.path
import yaml
from . import pomxml
def _create(path: str, pom: pomxml.Pom):
logging.info('Creating %s' % path)
citation = {'cff-version': '1.2.0'}
citation['message'] = 'You can cite %s by using the following metadata' % pom.name
citation['title'] = pom.name
citation['abstract'] = pom.description
citation['authors'] = []
citation['keywords'] = ['Java']
citation['version'] = pom.version
citation['doi'] = ''
citation['date-released'] = pom.release_date
citation['license'] = pom.lisense_name
citation['repository-code'] = pom.scm_url
with open(path, 'w') as file:
yaml.dump(citation, file, default_flow_style=False, sort_keys=False)
def _update(path: str, pom: pomxml.Pom):
logging.info('Updating %s' % path)
with open(path, 'r') as file:
citation = yaml.safe_load(file)
citation['date-released'] = pom.release_date
citation['repository-code'] = pom.scm_url
citation['version'] = pom.version
with open(path, 'w') as file:
yaml.dump(citation, file, default_flow_style=False, sort_keys=False)
def handle(pom: pomxml.Pom):
path = 'CITATION.cff'
if os.path.exists(path):
_update(path, pom)
else:
_create(path, pom)
# SPDX-FileCopyrightText: 2023-present Olivier Maury <Olivier.Maury@inrae.fr>
#
# SPDX-License-Identifier: GPL-3.0-only
from datetime import date
import logging
import os.path
import xml.etree.ElementTree as ET
class Developer:
name = None
gitlab = None
gitlab_id = None
orcid = None
class Pom:
description = {'en': ''}
developers = []
java_version = ''
license_name = ''
long_description = {}
name = ''
organization_name = ''
release_date = date.today()
scm_url = ''
url = ''
version = ''
def find_text(element: ET.Element, match: str) -> str:
found = element.find(match)
if found is not None:
return found.text
logging.warning('Element "%s" not found in %s' % (match, element))
return None
def find_text_with_lang(element: ET.Element, match: str) -> dict:
found = element.findall(match)
d = {}
if found is not None:
for e in found:
lang = e.get('{http://www.w3.org/XML/1998/namespace}lang')
if lang is None:
lang = 'en'
d[lang] = e.text
return d
logging.warning('Element "%s" not found in %s' % (match, element))
return d
def parse(path: str) -> Pom:
if not os.path.exists('pom.xml'):
logging.error('pom.xml does not exist in the current directory.')
sys.exit(1)
logging.info('Opening %s' % path)
tree = ET.parse(path)
root = tree.getroot()
pom = Pom()
pom.description['en'] = find_text(root, '{http://maven.apache.org/POM/4.0.0}description')
pom.name = find_text(root, '{http://maven.apache.org/POM/4.0.0}name')
pom_organization = root.find('{http://maven.apache.org/POM/4.0.0}organization')
pom.organization_name = find_text(pom_organization, '{http://maven.apache.org/POM/4.0.0}name')
pom.version = find_text(root, '{http://maven.apache.org/POM/4.0.0}version')
pom_scm = root.find('{http://maven.apache.org/POM/4.0.0}scm')
pom.scm_url = find_text(pom_scm, '{http://maven.apache.org/POM/4.0.0}url')
pom_properties = root.find('{http://maven.apache.org/POM/4.0.0}properties')
descriptions = find_text_with_lang(pom_properties, '{http://maven.apache.org/POM/4.0.0}metadata.description')
for lang in descriptions:
pom.description[lang] = descriptions[lang]
pom.java_version = find_text(pom_properties, '{http://maven.apache.org/POM/4.0.0}maven.compiler.source')
pom.long_description = find_text_with_lang(pom_properties, '{http://maven.apache.org/POM/4.0.0}metadata.long-description')
pom_licenses = root.find('{http://maven.apache.org/POM/4.0.0}licenses')
pom.license_name = ''
if pom_licenses is not None and len(pom_licenses) > 0:
pom.license_name = find_text(pom_licenses[0], '{http://maven.apache.org/POM/4.0.0}name')
pom.url = root.find('{http://maven.apache.org/POM/4.0.0}url').text
for developers in root.iter('{http://maven.apache.org/POM/4.0.0}developers'):
for x in developers.iter('{http://maven.apache.org/POM/4.0.0}developer'):
developer = Developer()
developer.name = x.find('{http://maven.apache.org/POM/4.0.0}name').text;
props = x.find('{http://maven.apache.org/POM/4.0.0}properties')
if props is not None:
developer.gitlab_id = find_text(props, '{http://maven.apache.org/POM/4.0.0}metadata.gitlab-id')
developer.gitlab = find_text(props, '{http://maven.apache.org/POM/4.0.0}metadata.gitlab')
developer.orcid = find_text(props, '{http://maven.apache.org/POM/4.0.0}metadata.orcid')
pom.developers.append(developer)
return pom
# SPDX-FileCopyrightText: 2023-present Olivier Maury <Olivier.Maury@inrae.fr>
#
# SPDX-License-Identifier: GPL-3.0-only
import logging
import os.path
import yaml
from . import pomxml
COMMENTS = """# This repository adheres to the publiccode.yml standard by including this
# metadata file that makes public software easily discoverable.
# More info at https://github.com/italia/publiccode.yml
"""
def _create(path: str, pom: pomxml.Pom):
logging.info('Creating %s' % path)
publiccode = {
'publiccodeYmlVersion': '0.2',
'categories': [],
'dependsOn': {
'open': [
{
'name': 'java',
'optional': False,
'versionMin': pom.java_version
},
],
},
'description': {},
'developmentStatus': 'development',
'landingURL': pom.url,
'legal': {
'authorsFile': 'AUTHORS.md',
'license': pom.license_name,
},
'name': pom.name,
'releaseDate': pom.release_date,
'softwareType': 'standalone/desktop',
'softwareVersion': pom.version,
'url': pom.scm_url,
'usedBy': [pom.organization_name]
}
for lang in pom.description:
publiccode['description'][lang] = {
'features': [],
'genericName': pom.name,
'shortDescription': pom.description[lang],
'longDescription': pom.long_description[lang],
}
with open(path, 'w') as file:
file.write(COMMENTS)
yaml.dump(publiccode, file, default_flow_style=False, sort_keys=False)
def _update(path: str, pom: pomxml.Pom):
logging.info('Updating %s' % path)
with open(path, 'r') as file:
publiccode = yaml.safe_load(file)
publiccode['landingURL'] = pom.url
publiccode['releaseDate'] = pom.release_date
publiccode['softwareVersion'] = pom.version
publiccode['url'] = pom.scm_url
for lang in pom.description:
publiccode['description'][lang]['shortDescription'] = pom.description[lang]
publiccode['description'][lang]['longDescription'] = pom.long_description[lang]
with open(path, 'w') as file:
yaml.dump(publiccode, file, default_flow_style=False, sort_keys=False)
def handle(pom: pomxml.Pom):
path = 'publiccode.yml'
if os.path.exists(path):
_update(path, pom)
else:
_create(path, pom)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment