mirror of
https://github.com/lilydjwg/nvchecker.git
synced 2025-03-10 06:14:02 +00:00
port tools.py and change record files to use json format
because special characters like spaces broke the old format.
This commit is contained in:
parent
4f515d75db
commit
6a6d5df682
6 changed files with 69 additions and 65 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,3 @@
|
||||||
records/
|
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
/build/
|
/build/
|
||||||
|
|
31
README.rst
31
README.rst
|
@ -126,30 +126,11 @@ There are several backward-incompatible changes from the previous 1.x version.
|
||||||
2. The configuration file format has been changed from ini to `toml`_. You can use the ``scripts/ini2toml`` script in this repo to convert your old configuration files. However, comments and formatting will be lost.
|
2. The configuration file format has been changed from ini to `toml`_. You can use the ``scripts/ini2toml`` script in this repo to convert your old configuration files. However, comments and formatting will be lost.
|
||||||
3. Several options have been renamed. ``max_concurrent`` to ``max_concurrency``, and all option names have their ``-`` be replaced with ``_``.
|
3. Several options have been renamed. ``max_concurrent`` to ``max_concurrency``, and all option names have their ``-`` be replaced with ``_``.
|
||||||
4. All software configuration tables need a ``source`` option to specify which source is to be used rather than being figured out from option names in use. This enables additional source plugins to be discovered.
|
4. All software configuration tables need a ``source`` option to specify which source is to be used rather than being figured out from option names in use. This enables additional source plugins to be discovered.
|
||||||
|
5. The version record files have been changed to use JSON format (the old format will be converted on writing).
|
||||||
|
|
||||||
Version Record Files
|
Version Record Files
|
||||||
====================
|
====================
|
||||||
Version record files record which version of the software you know or is available. They are simple key-value pairs of ``(name, version)`` separated by a space::
|
Version record files record which version of the software you know or is available. They are a simple JSON object mapping software names to known versions.
|
||||||
|
|
||||||
fcitx 4.2.7
|
|
||||||
google-chrome 27.0.1453.93-200836
|
|
||||||
vim 7.3.1024
|
|
||||||
|
|
||||||
Say you've got a version record file called ``old_ver.txt`` which records all your watched software and their versions, as well as some configuration entries. To update it using ``nvchecker``::
|
|
||||||
|
|
||||||
nvchecker -c source.toml
|
|
||||||
|
|
||||||
See what are updated with ``nvcmp``::
|
|
||||||
|
|
||||||
nvcmp -c source.toml
|
|
||||||
|
|
||||||
Manually compare the two files for updates (assuming they are sorted alphabetically; files generated by ``nvchecker`` are already sorted)::
|
|
||||||
|
|
||||||
comm -13 old_ver.txt new_ver.txt
|
|
||||||
# or say that in English:
|
|
||||||
comm -13 old_ver.txt new_ver.txt | awk '{print $1 " has updated to version " $2 "."}'
|
|
||||||
# show both old and new versions
|
|
||||||
join old_ver.txt new_ver.txt | awk '$2 != $3'
|
|
||||||
|
|
||||||
The ``nvtake`` Command
|
The ``nvtake`` Command
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -159,6 +140,14 @@ This helps when you have known (and processed) some of the updated software, but
|
||||||
|
|
||||||
This command will help most if you specify where you version record files are in your config file. See below for how to use a config file.
|
This command will help most if you specify where you version record files are in your config file. See below for how to use a config file.
|
||||||
|
|
||||||
|
The ``nvcmp`` Command
|
||||||
|
----------------------
|
||||||
|
This command compares the ``newver`` file with the ``oldver`` one and prints out any differences as updates, e.g.::
|
||||||
|
|
||||||
|
$ nvcmp -c sample_source.toml
|
||||||
|
Sparkle Test App None -> 2.0
|
||||||
|
test 0.0 -> 0.1
|
||||||
|
|
||||||
Configuration Files
|
Configuration Files
|
||||||
===================
|
===================
|
||||||
The software version source files are in `toml`_ format. The *key name* is the name of the software. Following fields are used to tell nvchecker how to determine the current version of that software.
|
The software version source files are in `toml`_ format. The *key name* is the name of the software. Following fields are used to tell nvchecker how to determine the current version of that software.
|
||||||
|
|
|
@ -30,16 +30,11 @@ def main() -> None:
|
||||||
if core.process_common_arguments(args):
|
if core.process_common_arguments(args):
|
||||||
return
|
return
|
||||||
|
|
||||||
if not args.file:
|
try:
|
||||||
try:
|
entries, options = core.load_file(
|
||||||
file = open(core.get_default_config())
|
args.file, use_keymanager=bool(args.keyfile))
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
sys.exit('version configuration file not given and default does not exist')
|
sys.exit('version configuration file not given and default does not exist')
|
||||||
else:
|
|
||||||
file = args.file
|
|
||||||
|
|
||||||
entries, options = core.load_file(
|
|
||||||
file, use_keymanager=bool(args.keyfile))
|
|
||||||
|
|
||||||
if args.keyfile:
|
if args.keyfile:
|
||||||
keymanager = KeyManager(Path(args.keyfile))
|
keymanager = KeyManager(Path(args.keyfile))
|
||||||
|
|
|
@ -10,7 +10,7 @@ from asyncio import Queue
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
from typing import (
|
from typing import (
|
||||||
TextIO, Tuple, NamedTuple, Optional, List, Union,
|
Tuple, NamedTuple, Optional, List, Union,
|
||||||
cast, Dict, Awaitable, Sequence,
|
cast, Dict, Awaitable, Sequence,
|
||||||
)
|
)
|
||||||
import types
|
import types
|
||||||
|
@ -18,6 +18,7 @@ from pathlib import Path
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
import re
|
import re
|
||||||
import contextvars
|
import contextvars
|
||||||
|
import json
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
import toml
|
import toml
|
||||||
|
@ -51,9 +52,11 @@ def add_common_arguments(parser: argparse.ArgumentParser) -> None:
|
||||||
help='specify fd to send json logs to. stdout by default')
|
help='specify fd to send json logs to. stdout by default')
|
||||||
parser.add_argument('-V', '--version', action='store_true',
|
parser.add_argument('-V', '--version', action='store_true',
|
||||||
help='show version and exit')
|
help='show version and exit')
|
||||||
|
default_config = get_default_config()
|
||||||
parser.add_argument('-c', '--file',
|
parser.add_argument('-c', '--file',
|
||||||
metavar='FILE', type=open,
|
metavar='FILE', type=str,
|
||||||
help='software version configuration file [default: %s]' % get_default_config())
|
default=default_config,
|
||||||
|
help='software version configuration file [default: %s]' % default_config)
|
||||||
|
|
||||||
def process_common_arguments(args: argparse.Namespace) -> bool:
|
def process_common_arguments(args: argparse.Namespace) -> bool:
|
||||||
'''return True if should stop'''
|
'''return True if should stop'''
|
||||||
|
@ -109,23 +112,26 @@ def safe_overwrite(fname: str, data: Union[bytes, str], *,
|
||||||
os.rename(tmpname, fname)
|
os.rename(tmpname, fname)
|
||||||
|
|
||||||
def read_verfile(file: Path) -> VersData:
|
def read_verfile(file: Path) -> VersData:
|
||||||
v = {}
|
|
||||||
try:
|
try:
|
||||||
with open(file) as f:
|
with open(file) as f:
|
||||||
for l in f:
|
data = f.read()
|
||||||
name, ver = l.rstrip().split(None, 1)
|
|
||||||
v[name] = ver
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
return {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
v = json.loads(data)
|
||||||
|
except json.decoder.JSONDecodeError:
|
||||||
|
# old format
|
||||||
|
v = {}
|
||||||
|
for l in data.splitlines():
|
||||||
|
name, ver = l.rstrip().split(None, 1)
|
||||||
|
v[name] = ver
|
||||||
|
|
||||||
return v
|
return v
|
||||||
|
|
||||||
def write_verfile(file: Path, versions: VersData) -> None:
|
def write_verfile(file: Path, versions: VersData) -> None:
|
||||||
# sort using only alphanums, as done by the sort command,
|
data = json.dumps(versions, ensure_ascii=False) + '\n'
|
||||||
# and needed by comm command
|
safe_overwrite(str(file), data)
|
||||||
data = ['%s %s\n' % item
|
|
||||||
for item in sorted(versions.items(), key=lambda i: (''.join(filter(str.isalnum, i[0])), i[1]))]
|
|
||||||
safe_overwrite(
|
|
||||||
str(file), ''.join(data), method='writelines')
|
|
||||||
|
|
||||||
class Options(NamedTuple):
|
class Options(NamedTuple):
|
||||||
ver_files: Optional[Tuple[Path, Path]]
|
ver_files: Optional[Tuple[Path, Path]]
|
||||||
|
@ -134,7 +140,7 @@ class Options(NamedTuple):
|
||||||
keymanager: KeyManager
|
keymanager: KeyManager
|
||||||
|
|
||||||
def load_file(
|
def load_file(
|
||||||
file: TextIO, *,
|
file: str, *,
|
||||||
use_keymanager: bool,
|
use_keymanager: bool,
|
||||||
) -> Tuple[Entries, Options]:
|
) -> Tuple[Entries, Options]:
|
||||||
config = toml.load(file)
|
config = toml.load(file)
|
||||||
|
@ -143,7 +149,7 @@ def load_file(
|
||||||
|
|
||||||
if '__config__' in config:
|
if '__config__' in config:
|
||||||
c = config.pop('__config__')
|
c = config.pop('__config__')
|
||||||
d = Path(file.name).parent
|
d = Path(file).parent
|
||||||
|
|
||||||
if 'oldver' in c and 'newver' in c:
|
if 'oldver' in c and 'newver' in c:
|
||||||
oldver_s = os.path.expandvars(
|
oldver_s = os.path.expandvars(
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
# vim: se sw=2:
|
# vim: se sw=2:
|
||||||
# MIT licensed
|
# MIT licensed
|
||||||
# Copyright (c) 2013-2017 lilydjwg <lilydjwg@gmail.com>, et al.
|
# Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
|
||||||
import argparse
|
import argparse
|
||||||
import structlog
|
import structlog
|
||||||
|
|
||||||
|
@ -11,7 +10,7 @@ from . import core
|
||||||
|
|
||||||
logger = structlog.get_logger(logger_name=__name__)
|
logger = structlog.get_logger(logger_name=__name__)
|
||||||
|
|
||||||
def take():
|
def take() -> None:
|
||||||
parser = argparse.ArgumentParser(description='update version records of nvchecker')
|
parser = argparse.ArgumentParser(description='update version records of nvchecker')
|
||||||
core.add_common_arguments(parser)
|
core.add_common_arguments(parser)
|
||||||
parser.add_argument('--all', action='store_true',
|
parser.add_argument('--all', action='store_true',
|
||||||
|
@ -24,15 +23,19 @@ def take():
|
||||||
if core.process_common_arguments(args):
|
if core.process_common_arguments(args):
|
||||||
return
|
return
|
||||||
|
|
||||||
s = core.Source(args.file)
|
opt = core.load_file(args.file, use_keymanager=False)[1]
|
||||||
if not s.oldver or not s.newver:
|
if opt.ver_files is None:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
"doesn't have both 'oldver' and 'newver' set.", source=s,
|
"doesn't have 'oldver' and 'newver' set.",
|
||||||
|
source=args.file,
|
||||||
)
|
)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
else:
|
||||||
|
oldverf = opt.ver_files[0]
|
||||||
|
newverf = opt.ver_files[1]
|
||||||
|
|
||||||
oldvers = core.read_verfile(s.oldver)
|
oldvers = core.read_verfile(oldverf)
|
||||||
newvers = core.read_verfile(s.newver)
|
newvers = core.read_verfile(newverf)
|
||||||
|
|
||||||
if args.all:
|
if args.all:
|
||||||
oldvers.update(newvers)
|
oldvers.update(newvers)
|
||||||
|
@ -51,21 +54,33 @@ def take():
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.rename(s.oldver, s.oldver + '~')
|
oldverf.rename(
|
||||||
|
oldverf.with_name(oldverf.name + '~'),
|
||||||
|
)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
core.write_verfile(s.oldver, oldvers)
|
core.write_verfile(oldverf, oldvers)
|
||||||
|
|
||||||
def cmp():
|
def cmp() -> None:
|
||||||
parser = argparse.ArgumentParser(description='compare version records of nvchecker')
|
parser = argparse.ArgumentParser(description='compare version records of nvchecker')
|
||||||
core.add_common_arguments(parser)
|
core.add_common_arguments(parser)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if core.process_common_arguments(args):
|
if core.process_common_arguments(args):
|
||||||
return
|
return
|
||||||
|
|
||||||
s = core.Source(args.file)
|
opt = core.load_file(args.file, use_keymanager=False)[1]
|
||||||
oldvers = core.read_verfile(s.oldver) if s.oldver else {}
|
if opt.ver_files is None:
|
||||||
newvers = core.read_verfile(s.newver)
|
logger.critical(
|
||||||
|
"doesn't have 'oldver' and 'newver' set.",
|
||||||
|
source=args.file,
|
||||||
|
)
|
||||||
|
sys.exit(2)
|
||||||
|
else:
|
||||||
|
oldverf = opt.ver_files[0]
|
||||||
|
newverf = opt.ver_files[1]
|
||||||
|
|
||||||
|
oldvers = core.read_verfile(oldverf)
|
||||||
|
newvers = core.read_verfile(newverf)
|
||||||
for name, newver in sorted(newvers.items()):
|
for name, newver in sorted(newvers.items()):
|
||||||
oldver = oldvers.get(name, None)
|
oldver = oldvers.get(name, None)
|
||||||
if oldver != newver:
|
if oldver != newver:
|
|
@ -1,6 +1,6 @@
|
||||||
[__config__]
|
[__config__]
|
||||||
oldver = "old_ver.txt"
|
oldver = "old_ver.json"
|
||||||
newver = "new_ver.txt"
|
newver = "new_ver.json"
|
||||||
|
|
||||||
[vim]
|
[vim]
|
||||||
source = "regex"
|
source = "regex"
|
||||||
|
|
Loading…
Add table
Reference in a new issue