mirror of
https://github.com/platformio/platformio-core.git
synced 2025-07-30 10:07:14 +02:00
Added support for “scripts” in package manifest // Resolve #485
This commit is contained in:
@ -24,9 +24,10 @@ PlatformIO Core 5
|
|||||||
* `pio pkg uninstall <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_uninstall.html>`_ - uninstall the project dependencies or custom packages
|
* `pio pkg uninstall <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_uninstall.html>`_ - uninstall the project dependencies or custom packages
|
||||||
* `pio pkg update <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_update.html>`__ - update the project dependencies or custom packages
|
* `pio pkg update <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_update.html>`__ - update the project dependencies or custom packages
|
||||||
|
|
||||||
- Added support for multi-licensed packages in `library.json <https://docs.platformio.org/en/latest/librarymanager/config.html#license>`__ using SPDX Expressions (`issue #4037 <https://github.com/platformio/platformio-core/issues/4037>`_)
|
- Added support for `"scripts" <https://docs.platformio.org/en/latest/librarymanager/config.html#scripts>`__ in package manifest (`issue #485 <https://github.com/platformio/platformio-core/issues/485>`_)
|
||||||
- Automatically install dependencies of the local (private) libraries (`issue #2910 <https://github.com/platformio/platformio-core/issues/2910>`_)
|
- Added support for `multi-licensed <https://docs.platformio.org/en/latest/librarymanager/config.html#license>`__ packages using SPDX Expressions (`issue #4037 <https://github.com/platformio/platformio-core/issues/4037>`_)
|
||||||
- Added support for dependencies declared in a "tool" type package
|
- Added support for `"dependencies" <https://docs.platformio.org/en/latest/librarymanager/config.html#dependencies>`__ declared in a "tool" package manifest
|
||||||
|
- Automatically install dependencies of the local (private) project libraries (`issue #2910 <https://github.com/platformio/platformio-core/issues/2910>`_)
|
||||||
- Ignore files according to the patterns declared in ".gitignore" when using the `pio package pack <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_pack.html>`__ command (`issue #4188 <https://github.com/platformio/platformio-core/issues/4188>`_)
|
- Ignore files according to the patterns declared in ".gitignore" when using the `pio package pack <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_pack.html>`__ command (`issue #4188 <https://github.com/platformio/platformio-core/issues/4188>`_)
|
||||||
- Dropped automatic updates of global libraries and development platforms (`issue #4179 <https://github.com/platformio/platformio-core/issues/4179>`_)
|
- Dropped automatic updates of global libraries and development platforms (`issue #4179 <https://github.com/platformio/platformio-core/issues/4179>`_)
|
||||||
- Dropped support for the "pythonPackages" field in "platform.json" manifest in favor of `Extra Python Dependencies <https://docs.platformio.org/en/latest/scripting/examples/extra_python_packages.html>`__
|
- Dropped support for the "pythonPackages" field in "platform.json" manifest in favor of `Extra Python Dependencies <https://docs.platformio.org/en/latest/scripting/examples/extra_python_packages.html>`__
|
||||||
|
2
docs
2
docs
Submodule docs updated: 1e6df4bb83...a0c38a9138
@ -102,6 +102,8 @@ class PackageManagerInstallMixin(object):
|
|||||||
% (spec.humanize(), util.get_systype())
|
% (spec.humanize(), util.get_systype())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.call_pkg_script(pkg, "postinstall")
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
click.style(
|
click.style(
|
||||||
"{name}@{version} has been installed!".format(**pkg.metadata.as_dict()),
|
"{name}@{version} has been installed!".format(**pkg.metadata.as_dict()),
|
||||||
|
@ -40,6 +40,8 @@ class PackageManagerUninstallMixin(object):
|
|||||||
% (click.style(pkg.metadata.name, fg="cyan"), pkg.metadata.version)
|
% (click.style(pkg.metadata.name, fg="cyan"), pkg.metadata.version)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.call_pkg_script(pkg, "preuninstall")
|
||||||
|
|
||||||
# firstly, remove dependencies
|
# firstly, remove dependencies
|
||||||
if not skip_dependencies:
|
if not skip_dependencies:
|
||||||
self.uninstall_dependencies(pkg)
|
self.uninstall_dependencies(pkg)
|
||||||
|
@ -14,12 +14,13 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import semantic_version
|
import semantic_version
|
||||||
|
|
||||||
from platformio import util
|
from platformio import fs, util
|
||||||
from platformio.commands import PlatformioCLI
|
from platformio.commands import PlatformioCLI
|
||||||
from platformio.compat import ci_strings_are_equal
|
from platformio.compat import ci_strings_are_equal
|
||||||
from platformio.package.exception import ManifestException, MissingPackageManifestError
|
from platformio.package.exception import ManifestException, MissingPackageManifestError
|
||||||
@ -37,6 +38,7 @@ from platformio.package.meta import (
|
|||||||
PackageSpec,
|
PackageSpec,
|
||||||
PackageType,
|
PackageType,
|
||||||
)
|
)
|
||||||
|
from platformio.proc import get_pythonexe_path
|
||||||
from platformio.project.helpers import get_project_cache_dir
|
from platformio.project.helpers import get_project_cache_dir
|
||||||
|
|
||||||
|
|
||||||
@ -302,3 +304,31 @@ class BasePackageManager( # pylint: disable=too-many-public-methods,too-many-in
|
|||||||
name=dependency.get("name"),
|
name=dependency.get("name"),
|
||||||
requirements=dependency.get("version"),
|
requirements=dependency.get("version"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def call_pkg_script(self, pkg, event):
|
||||||
|
manifest = None
|
||||||
|
try:
|
||||||
|
manifest = self.load_manifest(pkg)
|
||||||
|
except MissingPackageManifestError:
|
||||||
|
pass
|
||||||
|
scripts = (manifest or {}).get("scripts")
|
||||||
|
if not scripts or not isinstance(scripts, dict):
|
||||||
|
return
|
||||||
|
cmd = scripts.get(event)
|
||||||
|
if not cmd:
|
||||||
|
return
|
||||||
|
shell = False
|
||||||
|
if not isinstance(cmd, list):
|
||||||
|
shell = True
|
||||||
|
cmd = [cmd]
|
||||||
|
os.environ["PIO_PYTHON_EXE"] = get_pythonexe_path()
|
||||||
|
with fs.cd(pkg.path):
|
||||||
|
if os.path.isfile(cmd[0]) and cmd[0].endswith(".py"):
|
||||||
|
cmd = [os.environ["PIO_PYTHON_EXE"]] + cmd
|
||||||
|
subprocess.run(
|
||||||
|
" ".join(cmd) if shell else cmd,
|
||||||
|
cwd=pkg.path,
|
||||||
|
shell=shell,
|
||||||
|
env=os.environ,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
@ -152,6 +152,21 @@ class ExampleSchema(StrictSchema):
|
|||||||
files = StrictListField(fields.Str, required=True)
|
files = StrictListField(fields.Str, required=True)
|
||||||
|
|
||||||
|
|
||||||
|
# Fields
|
||||||
|
|
||||||
|
|
||||||
|
class ScriptField(fields.Field):
|
||||||
|
def _deserialize(self, value, attr, data, **kwargs):
|
||||||
|
if isinstance(value, (str, list)):
|
||||||
|
return value
|
||||||
|
raise ValidationError(
|
||||||
|
"Script value must be a command (string) or list of arguments"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Scheme
|
||||||
|
|
||||||
|
|
||||||
class ManifestSchema(BaseSchema):
|
class ManifestSchema(BaseSchema):
|
||||||
# Required fields
|
# Required fields
|
||||||
name = fields.Str(
|
name = fields.Str(
|
||||||
@ -173,6 +188,10 @@ class ManifestSchema(BaseSchema):
|
|||||||
license = fields.Str(validate=validate.Length(min=1, max=255))
|
license = fields.Str(validate=validate.Length(min=1, max=255))
|
||||||
repository = fields.Nested(RepositorySchema)
|
repository = fields.Nested(RepositorySchema)
|
||||||
dependencies = fields.Nested(DependencySchema, many=True)
|
dependencies = fields.Nested(DependencySchema, many=True)
|
||||||
|
scripts = fields.Dict(
|
||||||
|
keys=fields.Str(validate=validate.OneOf(["postinstall", "preuninstall"])),
|
||||||
|
values=ScriptField(),
|
||||||
|
)
|
||||||
|
|
||||||
# library.json
|
# library.json
|
||||||
export = fields.Nested(ExportSchema)
|
export = fields.Nested(ExportSchema)
|
||||||
|
@ -50,20 +50,20 @@ def test_project(clirunner, validate_cliresult, isolated_pio_core, tmp_path):
|
|||||||
)
|
)
|
||||||
validate_cliresult(result)
|
validate_cliresult(result)
|
||||||
assert all(token in result.output for token in ("baremetal", "devkit"))
|
assert all(token in result.output for token in ("baremetal", "devkit"))
|
||||||
assert result.output.count("Platform atmelavr@3.4.0") == 2
|
assert result.output.count("Platform atmelavr @ 3.4.0") == 2
|
||||||
assert (
|
assert (
|
||||||
result.output.count(
|
result.output.count(
|
||||||
"toolchain-atmelavr@1.70300.191015 (required: "
|
"toolchain-atmelavr @ 1.70300.191015 (required: "
|
||||||
"platformio/toolchain-atmelavr @ ~1.70300.0)"
|
"platformio/toolchain-atmelavr @ ~1.70300.0)"
|
||||||
)
|
)
|
||||||
== 2
|
== 2
|
||||||
)
|
)
|
||||||
assert result.output.count("Libraries") == 1
|
assert result.output.count("Libraries") == 1
|
||||||
assert (
|
assert (
|
||||||
"ArduinoJson@6.19.0+sha.9693fd2 (required: "
|
"ArduinoJson @ 6.19.0+sha.9693fd2 (required: "
|
||||||
"git+https://github.com/bblanchon/ArduinoJson.git#v6.19.0)"
|
"git+https://github.com/bblanchon/ArduinoJson.git#v6.19.0)"
|
||||||
) in result.output
|
) in result.output
|
||||||
assert "OneWire@2" in result.output
|
assert "OneWire @ 2" in result.output
|
||||||
|
|
||||||
# test "baremetal"
|
# test "baremetal"
|
||||||
result = clirunner.invoke(
|
result = clirunner.invoke(
|
||||||
@ -71,7 +71,7 @@ def test_project(clirunner, validate_cliresult, isolated_pio_core, tmp_path):
|
|||||||
["-d", str(project_dir), "-e", "baremetal"],
|
["-d", str(project_dir), "-e", "baremetal"],
|
||||||
)
|
)
|
||||||
validate_cliresult(result)
|
validate_cliresult(result)
|
||||||
assert "Platform atmelavr@3" in result.output
|
assert "Platform atmelavr @ 3" in result.output
|
||||||
assert "Libraries" not in result.output
|
assert "Libraries" not in result.output
|
||||||
|
|
||||||
# filter by "tool" package
|
# filter by "tool" package
|
||||||
@ -100,7 +100,7 @@ def test_project(clirunner, validate_cliresult, isolated_pio_core, tmp_path):
|
|||||||
def test_global_packages(clirunner, validate_cliresult, isolated_pio_core, tmp_path):
|
def test_global_packages(clirunner, validate_cliresult, isolated_pio_core, tmp_path):
|
||||||
result = clirunner.invoke(package_list_cmd, ["-g"])
|
result = clirunner.invoke(package_list_cmd, ["-g"])
|
||||||
validate_cliresult(result)
|
validate_cliresult(result)
|
||||||
assert "atmelavr@3" in result.output
|
assert "atmelavr @ 3" in result.output
|
||||||
assert "framework-arduino-avr-attiny" in result.output
|
assert "framework-arduino-avr-attiny" in result.output
|
||||||
|
|
||||||
# only tools
|
# only tools
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import semantic_version
|
import semantic_version
|
||||||
@ -290,6 +291,44 @@ def test_install_force(isolated_pio_core, tmpdir_factory):
|
|||||||
assert pkg.metadata.version.major > 5
|
assert pkg.metadata.version.major > 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_scripts(isolated_pio_core, tmp_path: Path):
|
||||||
|
pkg_dir = tmp_path / "foo"
|
||||||
|
scripts_dir = pkg_dir / "scripts"
|
||||||
|
scripts_dir.mkdir(parents=True)
|
||||||
|
(scripts_dir / "script.py").write_text(
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
action = "postinstall" if len(sys.argv) == 1 else sys.argv[1]
|
||||||
|
Path("%s.flag" % action).touch()
|
||||||
|
|
||||||
|
if action == "preuninstall":
|
||||||
|
Path("../%s.flag" % action).touch()
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
(pkg_dir / "library.json").write_text(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"postinstall": "scripts/script.py",
|
||||||
|
"preuninstall2": ["scripts/script.py", "preuninstall"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
storage_dir = tmp_path / "storage"
|
||||||
|
lm = LibraryPackageManager(str(storage_dir))
|
||||||
|
lm.set_log_level(logging.ERROR)
|
||||||
|
lm.install("file://%s" % str(pkg_dir))
|
||||||
|
assert os.path.isfile(os.path.join(lm.get_package("foo").path, "postinstall.flag"))
|
||||||
|
lm.uninstall("foo")
|
||||||
|
(storage_dir / "preuninstall.flag").is_file()
|
||||||
|
|
||||||
|
|
||||||
def test_get_installed(isolated_pio_core, tmpdir_factory):
|
def test_get_installed(isolated_pio_core, tmpdir_factory):
|
||||||
storage_dir = tmpdir_factory.mktemp("storage")
|
storage_dir = tmpdir_factory.mktemp("storage")
|
||||||
pm = ToolPackageManager(str(storage_dir))
|
pm = ToolPackageManager(str(storage_dir))
|
||||||
|
@ -322,6 +322,9 @@ def test_library_json_schema():
|
|||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": "*",
|
"platforms": "*",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"postinstall": "script.py"
|
||||||
|
},
|
||||||
"examples": [
|
"examples": [
|
||||||
{
|
{
|
||||||
"name": "JsonConfigFile",
|
"name": "JsonConfigFile",
|
||||||
@ -372,6 +375,7 @@ def test_library_json_schema():
|
|||||||
"frameworks": ["arduino"],
|
"frameworks": ["arduino"],
|
||||||
"platforms": ["*"],
|
"platforms": ["*"],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"scripts": {"postinstall": "script.py"},
|
||||||
"examples": [
|
"examples": [
|
||||||
{
|
{
|
||||||
"name": "JsonConfigFile",
|
"name": "JsonConfigFile",
|
||||||
|
Reference in New Issue
Block a user