Skip to content

Commit d264f2a

Browse files
Add core checker. Resolve platformio#27 (platformio#40)
* Add core checker. Resolve platformio#27 * fixes. * fix * fix * refactoring * Update core.py Co-authored-by: Ivan Kravets <[email protected]>
1 parent 18c2bfc commit d264f2a

File tree

7 files changed

+159
-6
lines changed

7 files changed

+159
-6
lines changed

get-platformio.py

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

pioinstaller/__main__.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def cli(
4242
): # pylint:disable=too-many-arguments
4343
if verbose:
4444
logging.getLogger("pioinstaller").setLevel(logging.DEBUG)
45-
45+
ctx.obj["dev"] = dev
4646
if not ctx.invoked_subcommand:
4747
click.echo("Installer version: %s" % __version__)
4848
click.echo("Platform: %s" % platform.platform())
@@ -88,8 +88,28 @@ def python():
8888
)
8989

9090

91+
@check.command("core")
92+
@click.option("--auto-upgrade/--no-auto-upgrade", is_flag=True, default=True)
93+
@click.option("--version-requirements", default=None)
94+
@click.pass_context
95+
def core_check(ctx, auto_upgrade, version_requirements):
96+
try:
97+
path, version = core.check(
98+
dev=ctx.obj.get("dev", False),
99+
auto_upgrade=auto_upgrade,
100+
version_requirements=version_requirements,
101+
)
102+
click.secho(
103+
"Found compatible PlatformIO Core %s -> %s" % (version, path), fg="green",
104+
)
105+
except (exception.InvalidPlatformIOCore) as e:
106+
raise click.ClickException(
107+
"Compatible PlatformIO Core not found.\nReason: %s" % str(e)
108+
)
109+
110+
91111
def main():
92-
return cli() # pylint: disable=no-value-for-parameter
112+
return cli(obj={}) # pylint: disable=no-value-for-parameter, unexpected-keyword-arg
93113

94114

95115
if __name__ == "__main__":

pioinstaller/core.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,23 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import json
1516
import logging
1617
import os
18+
import platform
1719
import subprocess
20+
import sys
21+
import time
1822

1923
import click
24+
import semantic_version
2025

2126
from pioinstaller import exception, home, util
2227

2328
log = logging.getLogger(__name__)
2429

2530
PIO_CORE_DEVELOP_URL = "https://github.com/platformio/platformio/archive/develop.zip"
31+
UPDATE_INTERVAL = 60 * 60 * 24 * 3 # 3 days
2632

2733

2834
def get_core_dir():
@@ -112,3 +118,119 @@ def install_platformio_core(shutdown_piohome=True, develop=False, ignore_pythons
112118
fg="cyan",
113119
)
114120
return True
121+
122+
123+
def check(dev=False, auto_upgrade=False, version_requirements=None):
124+
125+
# pylint: disable=bad-option-value, import-outside-toplevel, unused-import, import-error, unused-variable, cyclic-import
126+
from pioinstaller import penv
127+
128+
platformio_exe = os.path.join(
129+
penv.get_penv_bin_dir(), "platformio.exe" if util.IS_WINDOWS else "platformio",
130+
)
131+
if not os.path.isfile(platformio_exe):
132+
raise exception.InvalidPlatformIOCore(
133+
"PlatformIO executable not found in `%s`" % penv.get_penv_bin_dir()
134+
)
135+
136+
if not os.path.isfile(os.path.join(penv.get_penv_dir(), "state.json")):
137+
raise exception.InvalidPlatformIOCore(
138+
"Could not found state.json file in `%s`"
139+
% os.path.join(penv.get_penv_dir(), "state.json")
140+
)
141+
142+
try:
143+
# pylint: disable=bad-option-value, import-outside-toplevel, unused-import, import-error, unused-variable, cyclic-import
144+
import platformio
145+
except ImportError:
146+
raise exception.InvalidPlatformIOCore("Could not import PlatformIO module")
147+
148+
pio_version = get_pio_version(platformio)
149+
if version_requirements:
150+
try:
151+
if pio_version in semantic_version.Spec(version_requirements):
152+
raise exception.InvalidPlatformIOCore(
153+
"PlatformIO Core version %s does not match version requirements %s."
154+
% (str(pio_version), version_requirements)
155+
)
156+
except ValueError:
157+
click.secho(
158+
"Invalid version requirements format: %s. "
159+
"More about Semantic Versioning: https://semver.org/"
160+
% version_requirements
161+
)
162+
163+
state = None
164+
165+
with open(os.path.join(penv.get_penv_dir(), "state.json")) as fp:
166+
state = json.load(fp)
167+
if state.get("platform") != platform.platform():
168+
raise exception.InvalidPlatformIOCore(
169+
"PlatformIO installed using another platform `%s`. Your platform: %s"
170+
% (state.get("platform"), platform.platform())
171+
)
172+
173+
try:
174+
subprocess.check_output([platformio_exe, "--version"], stderr=subprocess.STDOUT)
175+
except subprocess.CalledProcessError as e:
176+
error = e.output.decode()
177+
raise exception.InvalidPlatformIOCore(
178+
"Could not run `%s --version`.\nError: %s" % (platformio, str(error))
179+
)
180+
181+
result = {"platformio_exe": platformio_exe, "version": str(pio_version)}
182+
183+
if not auto_upgrade:
184+
return result
185+
186+
time_now = int(round(time.time()))
187+
188+
last_piocore_version_check = state.get("last_piocore_version_check")
189+
190+
if (
191+
last_piocore_version_check
192+
and (time_now - int(last_piocore_version_check)) < UPDATE_INTERVAL
193+
):
194+
return result
195+
196+
with open(os.path.join(penv.get_penv_dir(), "state.json"), "w") as fp:
197+
state["last_piocore_version_check"] = time_now
198+
json.dump(state, fp)
199+
200+
if not last_piocore_version_check:
201+
return result
202+
203+
dev = dev or pio_version.prerelease
204+
upgrade_core(platformio_exe, dev)
205+
206+
result["pio_version"] = get_pio_version(platformio)
207+
return result
208+
209+
210+
def get_pio_version(platformio):
211+
try:
212+
if sys.version_info[0] == 3:
213+
# pylint: disable=bad-option-value, import-outside-toplevel, unused-import, import-error, unused-variable, cyclic-import
214+
import importlib
215+
216+
importlib.reload(platformio) # pylint:disable=no-member
217+
else:
218+
reload(platformio)
219+
return semantic_version.Version(util.pepver_to_semver(platformio.__version__))
220+
except: # pylint:disable=bare-except
221+
return platformio.__version__
222+
223+
224+
def upgrade_core(platformio_exe, dev=False):
225+
command = [platformio_exe, "upgrade"]
226+
if dev:
227+
command.append("--dev")
228+
try:
229+
subprocess.check_output(
230+
command, stderr=subprocess.PIPE,
231+
)
232+
return True
233+
except Exception as e: # pylint:disable=broad-except
234+
raise exception.PIOInstallerException(
235+
"Could not upgrade PlatformIO Core: %s" % str(e)
236+
)

pioinstaller/exception.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@ class IncompatiblePythonError(PIOInstallerException):
3333
class DistutilsNotFound(PIOInstallerException):
3434

3535
MESSAGE = "Could not find distutils module"
36+
37+
38+
class InvalidPlatformIOCore(PIOInstallerException):
39+
40+
MESSAGE = "{0}"

pioinstaller/penv.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,14 @@ def add_state_info(python_exe, penv_dir):
146146
.decode()
147147
.strip()
148148
)
149-
json_info = {
149+
state = {
150150
"created_on": int(round(time.time())),
151151
"python": {"path": python_exe, "version": python_version,},
152152
"installer_version": __version__,
153153
"platform": platform.platform(),
154154
}
155155
with open(os.path.join(penv_dir, "state.json"), "w") as fp:
156-
json.dump(json_info, fp)
156+
json.dump(state, fp)
157157
return os.path.join(penv_dir, "state.json")
158158

159159

pioinstaller/util.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import logging
1717
import os
1818
import platform
19+
import re
1920
import shutil
2021
import stat
2122
import sys
@@ -125,3 +126,7 @@ def safe_remove_dir(path, raise_exception=False):
125126
except Exception as e: # pylint: disable=broad-except
126127
if raise_exception:
127128
raise e
129+
130+
131+
def pepver_to_semver(pepver):
132+
return re.sub(r"(\.\d+)\.?(dev|a|b|rc|post)", r"\1-\2.", pepver, 1)

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
# Core
3838
"click==7.0",
3939
"requests==2.23.0",
40-
"colorama==0.4.3"
40+
"colorama==0.4.3",
41+
"semantic-version==2.8.4"
4142
],
4243
packages=find_packages(),
4344
entry_points={

0 commit comments

Comments
 (0)