Compare commits

..

43 Commits

Author SHA1 Message Date
Zach White 5a777c5de9 New release: 0.0.34 → 0.0.35 2020-05-27 20:58:33 -07:00
Zach White c51c898562 use cli.echo, not cli.args.echo. 2020-05-27 20:51:28 -07:00
Erovia 08ea23b087 Fix possibility of recursive execution 2020-05-26 21:43:15 +02:00
Erovia 0105405d83 Checking for the 'quantum' directory is better for long term 2020-05-10 17:41:30 +02:00
Erovia 92c57e2a46 Fix possibility of recursive execution 2020-05-10 17:30:30 +02:00
skullY 30c89ec8e0 New release: 0.0.33 → 0.0.34 2020-04-29 10:39:57 -07:00
Zach White a7a7bd9f08 improve the qmk setup flow (#20)
* improve the qmk setup flow

* Apply suggestions from code review

Co-Authored-By: Erovia <Erovia@users.noreply.github.com>

* Update qmk_cli/subcommands/setup.py

* explicity set encoding to utf-8

* polishing

Co-authored-by: Erovia <Erovia@users.noreply.github.com>
2020-04-29 10:38:56 -07:00
skullY 5229ce14e3 New release: 0.0.32 → 0.0.33 2020-04-15 13:53:35 -07:00
Erovia 8fa960f25e Merge pull request #19 from qmk/fix_store_true_false 2020-04-15 22:49:17 +02:00
skullY 1481dfe157 Make arg_only apply only to the subcommand it is part of 2020-04-15 12:07:15 -07:00
skullY ac39b9bb0f Fix setting config values for store_true and store_false 2020-04-15 09:30:49 -07:00
skullY 44a446308f New release: 0.0.31 → 0.0.32 2020-04-11 10:59:02 -07:00
Erovia 90bdf52416 Merge pull request #18 from Erovia/cli/win_env_check
Add warning message for MSYS users
2020-04-11 19:56:04 +02:00
Erovia 191980052e Add warning message for MSYS users 2020-04-11 19:44:56 +02:00
skullydazed 5fbdd95c4b Fix indentation 2020-04-11 10:31:54 -07:00
skullydazed 1002360e20 Update qmk_cli/script_qmk.py
Co-Authored-By: Erovia <Erovia@users.noreply.github.com>
2020-04-11 10:31:54 -07:00
Joel Challis a03aa1cbba Update qmk_cli/subcommands/setup.py
Co-Authored-By: skullydazed <skullydazed@users.noreply.github.com>
2020-04-11 10:31:54 -07:00
zvecr a0b692d718 Handle failed import with exception instead 2020-04-11 10:31:54 -07:00
zvecr ab7d0c6e46 Protect against empty QMK_HOME folder 2020-04-11 10:31:54 -07:00
skullY 73c903b8b6 Add --no flag 2020-04-11 09:57:31 -07:00
skullydazed 8f2185af32 Update qmk_cli/subcommands/setup.py 2020-04-11 09:57:31 -07:00
Erovia bae2f1f1ee Fix argument passing to doctor subcommand
When 'qmk setup' is started with the '-y' argument, this argument should be specified for 'qmk doctor' as well.
2020-04-11 09:57:31 -07:00
Zach White 120ce557f1 New release: 0.0.30 → 0.0.31 2020-03-26 07:51:42 -07:00
Erovia a15b0d6460 Fix WinError 193 on MSYS2 2020-03-26 07:51:17 -07:00
skullY f0dafaf068 New release: 0.0.29 → 0.0.30 2020-03-10 11:37:44 -07:00
skullY 3414bdcaa3 add nose2 to requirements 2020-03-10 11:37:31 -07:00
Mikkel Jeppesen c8567d8098 Made bade in the readme nicer 2020-02-25 14:33:37 -08:00
skullY dc3ca88deb New release: 0.0.28 → 0.0.29 2020-02-21 12:03:55 -08:00
skullY a4e9c34387 Add a warning when run on old python versions 2020-02-21 12:03:46 -08:00
Mikkel Jeppesen 742e1ce0e1 Added CI badge to readme 2020-02-21 11:13:59 -08:00
Mikkel Jeppesen 39df759f57 Switched to python 3.6 as minimum version, and remove paths condition on PR trigger 2020-02-21 11:13:59 -08:00
Mikkel Jeppesen b694c4284d removed debug echo 2020-02-21 11:13:59 -08:00
Mikkel Jeppesen e184710036 Fixed script path, checking env var 2020-02-21 11:13:59 -08:00
Mikkel Jeppesen d67addde2d moved msys script to root 2020-02-21 11:13:59 -08:00
Mikkel Jeppesen 29541559d0 Update .github/workflows/cli_setup.yml
Co-Authored-By: skullydazed <skullydazed@users.noreply.github.com>
2020-02-21 11:13:59 -08:00
Mikkel Jeppesen db0b8cb9a0 Added workflow to test qmk cli setup on OSX, Linux and MSYS2 2020-02-21 11:13:59 -08:00
skullY 6a0e62a078 Remove notes for things that are done 2020-02-21 11:13:41 -08:00
skullY bbc5ffd6c9 New release: 0.0.27 → 0.0.28 2020-02-13 19:09:10 -08:00
Zach White e0adba4e68 Prevent release from erroring on first run 2020-02-13 18:50:00 -08:00
Zach White 56bcb12ed6 Add support for determining where a config option came from 2020-02-13 18:49:13 -08:00
Mikkel Jeppesen 897fe121e2 Replace rename with replace 2020-02-13 18:48:02 -08:00
skullY 1a723e488f New release: 0.0.26 → 0.0.27 2020-01-26 11:40:14 -08:00
skullY bf502d26fa sync milc with qmk_firmware 2020-01-26 11:40:00 -08:00
13 changed files with 294 additions and 115 deletions
+68
View File
@@ -0,0 +1,68 @@
name: CLI Setup
on:
push:
branches:
- master
pull_request:
jobs:
test_cli_linux_macos:
runs-on: ${{ matrix.os }}
env:
QMK_HOME: ~/qmk_firmware
strategy:
matrix:
os: [macos-latest, ubuntu-latest]
python-version: [3.6, 3.7, 3.8]
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install python dependencies
run: |
python -m pip install wheel
echo "::add-path::$HOME/.local/bin"
- name: Install QMK CLI from source
run: |
python3 -m pip install -r requirements.txt
python setup.py sdist bdist_wheel
cd ..
python3 -m pip install --force-reinstall --no-index --no-deps --prefix=~/.local --find-links qmk_cli/dist qmk
- name: Run qmk setup -y
run: qmk setup -y
test_cli_win:
runs-on: windows-latest
strategy:
matrix:
python-version: [3.6, 3.8]
steps:
- uses: actions/checkout@v1
- name: Set up MSYS2
uses: qmk/setup-msys2@v1
with:
update: true
- name: (MSYS2) Install git and python3
run: msys2do pacman -S git python3-pip --noconfirm
- name: (MSYS2) Upgrade pip and setuptools and install wheel
run: |
msys2do python3 -m pip install --upgrade pip setuptools
msys2do python3 -m pip install wheel
- name: (MSYS2) Install QMK CLI from source
run: msys2do ./setup_msys.sh $(cygpath "${{ github.workspace }}")
- name: (MSYS2) Run qmk setup -y
run: msys2do qmk setup -y
+1 -1
View File
@@ -1,5 +1,5 @@
# QMK CLI # QMK CLI
[![CLI Setup](https://github.com/qmk/qmk_cli/workflows/CLI%20Setup/badge.svg)](https://github.com/qmk/qmk_cli/actions?query=workflow%3A%22CLI+Setup%22)
A program to help users work with [QMK Firmware](https://qmk.fm/). A program to help users work with [QMK Firmware](https://qmk.fm/).
# Features # Features
+136 -73
View File
@@ -178,8 +178,9 @@ class ConfigurationSection(Configuration):
def __getitem__(self, key): def __getitem__(self, key):
"""Returns a config value, pulling from the `user` section as a fallback. """Returns a config value, pulling from the `user` section as a fallback.
This is called when the attribute is accessed either via the get method or through [ ] index.
""" """
if key in self._config: if key in self._config and self._config.get(key) is not None:
return self._config[key] return self._config[key]
elif key in self.parent.user: elif key in self.parent.user:
@@ -187,6 +188,15 @@ class ConfigurationSection(Configuration):
return None return None
def __getattr__(self, key):
"""Returns the config value from the `user` section.
This is called when the attribute is accessed via dot notation but does not exists.
"""
if key in self.parent.user:
return self.parent.user[key]
return None
def handle_store_boolean(self, *args, **kwargs): def handle_store_boolean(self, *args, **kwargs):
"""Does the add_argument for action='store_boolean'. """Does the add_argument for action='store_boolean'.
@@ -232,15 +242,24 @@ class SubparserWrapper(object):
This also stores the default for the argument in `self.cli.default_arguments`. This also stores the default for the argument in `self.cli.default_arguments`.
""" """
if 'action' in kwargs and kwargs['action'] == 'store_boolean': if kwargs.get('action') == 'store_boolean':
# Store boolean will call us again with the enable/disable flag arguments # Store boolean will call us again with the enable/disable flag arguments
return handle_store_boolean(self, *args, **kwargs) return handle_store_boolean(self, *args, **kwargs)
self.cli.acquire_lock() self.cli.acquire_lock()
argument_name = self.cli.get_argument_name(*args, **kwargs)
self.subparser.add_argument(*args, **kwargs) self.subparser.add_argument(*args, **kwargs)
if kwargs.get('action') == 'store_false':
self.cli._config_store_false.append(argument_name)
if kwargs.get('action') == 'store_true':
self.cli._config_store_true.append(argument_name)
if self.submodule not in self.cli.default_arguments: if self.submodule not in self.cli.default_arguments:
self.cli.default_arguments[self.submodule] = {} self.cli.default_arguments[self.submodule] = {}
self.cli.default_arguments[self.submodule][self.cli.get_argument_name(*args, **kwargs)] = kwargs.get('default') self.cli.default_arguments[self.submodule][argument_name] = kwargs.get('default')
self.cli.release_lock() self.cli.release_lock()
@@ -258,12 +277,14 @@ class MILC(object):
# Define some basic info # Define some basic info
self.acquire_lock() self.acquire_lock()
self._config_store_true = []
self._config_store_false = []
self._description = None self._description = None
self._entrypoint = None self._entrypoint = None
self._inside_context_manager = False self._inside_context_manager = False
self.ansi = ansi_colors self.ansi = ansi_colors
self.arg_only = [] self.arg_only = {}
self.config = None self.config = self.config_source = None
self.config_file = None self.config_file = None
self.default_arguments = {} self.default_arguments = {}
self.version = 'unknown' self.version = 'unknown'
@@ -274,7 +295,7 @@ class MILC(object):
self.prog_name = self.prog_name.split('/')[-1] self.prog_name = self.prog_name.split('/')[-1]
# Initialize all the things # Initialize all the things
self.read_config_file() self.initialize_config()
self.initialize_argparse() self.initialize_argparse()
self.initialize_logging() self.initialize_logging()
@@ -367,7 +388,7 @@ class MILC(object):
self.add_argument('--log-file', help='File to write log messages to') self.add_argument('--log-file', help='File to write log messages to')
self.add_argument('--color', action='store_boolean', default=True, help='color in output') self.add_argument('--color', action='store_boolean', default=True, help='color in output')
self.add_argument('--config-file', help='The location for the configuration file') self.add_argument('--config-file', help='The location for the configuration file')
self.arg_only.append('config_file') self.arg_only['config_file'] = ['general']
def add_subparsers(self, title='Sub-commands', **kwargs): def add_subparsers(self, title='Sub-commands', **kwargs):
if self._inside_context_manager: if self._inside_context_manager:
@@ -417,17 +438,20 @@ class MILC(object):
raise RuntimeError('You must run this before the with statement!') raise RuntimeError('You must run this before the with statement!')
def argument_function(handler): def argument_function(handler):
if 'arg_only' in kwargs and kwargs['arg_only']: subcommand_name = handler.__name__.replace("_", "-")
if kwargs.get('arg_only'):
arg_name = self.get_argument_name(*args, **kwargs) arg_name = self.get_argument_name(*args, **kwargs)
self.arg_only.append(arg_name) if arg_name not in self.arg_only:
self.arg_only[arg_name] = []
self.arg_only[arg_name].append(subcommand_name)
del kwargs['arg_only'] del kwargs['arg_only']
name = handler.__name__.replace("_", "-")
if handler is self._entrypoint: if handler is self._entrypoint:
self.add_argument(*args, **kwargs) self.add_argument(*args, **kwargs)
elif name in self.subcommands: elif subcommand_name in self.subcommands:
self.subcommands[name].add_argument(*args, **kwargs) self.subcommands[subcommand_name].add_argument(*args, **kwargs)
else: else:
raise RuntimeError('Decorated function is not entrypoint or subcommand!') raise RuntimeError('Decorated function is not entrypoint or subcommand!')
@@ -458,21 +482,20 @@ class MILC(object):
self.release_lock() self.release_lock()
def read_config_file(self): def read_config_file(self, config_file):
"""Read in the configuration file and store it in self.config. """Read in the configuration file and return Configuration objects for it and the config_source.
""" """
self.acquire_lock() config = Configuration()
self.config = Configuration() config_source = Configuration()
self.config_file = self.find_config_file()
if self.config_file and self.config_file.exists(): if config_file.exists():
config = RawConfigParser(self.config) raw_config = RawConfigParser()
config.read(str(self.config_file)) raw_config.read(str(config_file))
# Iterate over the config file options and write them into self.config # Iterate over the config file options and write them into config
for section in config.sections(): for section in raw_config.sections():
for option in config.options(section): for option in raw_config.options(section):
value = config.get(section, option) value = raw_config.get(section, option)
# Coerce values into useful datatypes # Coerce values into useful datatypes
if value.lower() in ['1', 'yes', 'true', 'on']: if value.lower() in ['1', 'yes', 'true', 'on']:
@@ -487,8 +510,17 @@ class MILC(object):
else: else:
value = int(value) value = int(value)
self.config[section][option] = value config[section][option] = value
config_source[section][option] = 'config_file'
return config, config_source
def initialize_config(self):
"""Read in the configuration file and store it in self.config.
"""
self.acquire_lock()
self.config_file = self.find_config_file()
self.config, self.config_source = self.read_config_file(self.config_file)
self.release_lock() self.release_lock()
def merge_args_into_config(self): def merge_args_into_config(self):
@@ -499,30 +531,88 @@ class MILC(object):
if argument in ('subparsers', 'entrypoint'): if argument in ('subparsers', 'entrypoint'):
continue continue
if argument not in self.arg_only: # Find the argument's section
# Find the argument's section # Underscores in command's names are converted to dashes during initialization.
if self._entrypoint.__name__ in self.default_arguments and argument in self.default_arguments[self._entrypoint.__name__]: # TODO(Erovia) Find a better solution
argument_found = True entrypoint_name = self._entrypoint.__name__.replace("_", "-")
section = self._entrypoint.__name__ if entrypoint_name in self.default_arguments and argument in self.default_arguments[entrypoint_name]:
if argument in self.default_arguments['general']: argument_found = True
argument_found = True section = self._entrypoint.__name__
section = 'general' if argument in self.default_arguments['general']:
argument_found = True
section = 'general'
if not argument_found: if not argument_found:
raise RuntimeError('Could not find argument in `self.default_arguments`. This should be impossible!') raise RuntimeError('Could not find argument in `self.default_arguments`. This should be impossible!')
exit(1) exit(1)
if argument not in self.arg_only or section not in self.arg_only[argument]:
# Determine the arg value and source
arg_value = getattr(self.args, argument)
if argument in self._config_store_true and arg_value:
passed_on_cmdline = True
elif argument in self._config_store_false and not arg_value:
passed_on_cmdline = True
elif arg_value is not None:
passed_on_cmdline = True
else:
passed_on_cmdline = False
# Merge this argument into self.config # Merge this argument into self.config
if argument in self.default_arguments: if passed_on_cmdline and (argument in self.default_arguments['general'] or argument in self.default_arguments[entrypoint_name] or argument not in self.config[entrypoint_name]):
arg_value = getattr(self.args, argument) self.config[section][argument] = arg_value
if arg_value: self.config_source[section][argument] = 'argument'
self.config[section][argument] = arg_value
else:
if argument not in self.config[section]:
self.config[section][argument] = getattr(self.args, argument)
self.release_lock() self.release_lock()
def _save_config_file(self, config):
"""Write config to disk.
"""
# Generate a sanitized version of our running configuration
sane_config = RawConfigParser()
for section_name, section in config._config.items():
sane_config.add_section(section_name)
for option_name, value in section.items():
if section_name == 'general':
if option_name in ['config_file']:
continue
if value is not None:
sane_config.set(section_name, option_name, str(value))
config_dir = self.config_file.parent
if not config_dir.exists():
config_dir.mkdir(parents=True, exist_ok=True)
# Write the config file atomically.
self.acquire_lock()
with NamedTemporaryFile(mode='w', dir=str(config_dir), delete=False) as tmpfile:
sane_config.write(tmpfile)
if os.path.getsize(tmpfile.name) > 0:
os.replace(tmpfile.name, str(self.config_file))
else:
self.log.warning('Config file saving failed, not replacing %s with %s.', str(self.config_file), tmpfile.name)
self.release_lock()
def write_config_option(self, section, option):
"""Save a single config option to the config file.
"""
if not self.config_file:
self.log.warning('%s.config_file not set, not saving config!', self.__class__.__name__)
return
config, config_source = self.read_config_file(self.config_file)
if section in config and option in config[section] and config[section][option] is None:
del(config[section][option])
else:
config[section][option] = str(self.config[section][option])
self._save_config_file(config)
# Housekeeping
self.log.info('Wrote configuration to %s', shlex.quote(str(self.config_file)))
def save_config(self): def save_config(self):
"""Save the current configuration to the config file. """Save the current configuration to the config file.
""" """
@@ -532,36 +622,9 @@ class MILC(object):
self.log.warning('%s.config_file file not set, not saving config!', self.__class__.__name__) self.log.warning('%s.config_file file not set, not saving config!', self.__class__.__name__)
return return
self.acquire_lock() # Write config to disk
self._save_config_file(self.config)
# Generate a sanitized version of our running configuration self.log.info('Wrote configuration to %s', shlex.quote(str(self.config_file)))
config = RawConfigParser()
for section_name, section in self.config._config.items():
config.add_section(section_name)
for option_name, value in section.items():
if section_name == 'general':
if option_name in ['config_file']:
continue
if value is not None:
config.set(section_name, option_name, str(value))
# Write out the config file
config_dir = self.config_file.parent
if not config_dir.exists():
config_dir.mkdir(parents=True, exist_ok=True)
with NamedTemporaryFile(mode='w', dir=str(config_dir), delete=False) as tmpfile:
config.write(tmpfile)
# Move the new config file into place atomically
if os.path.getsize(tmpfile.name) > 0:
os.rename(tmpfile.name, str(self.config_file))
else:
self.log.warning('Config file saving failed, not replacing %s with %s.', str(self.config_file), tmpfile.name)
# Housekeeping
self.release_lock()
cli.log.info('Wrote configuration to %s', shlex.quote(str(self.config_file)))
def __call__(self): def __call__(self):
"""Execute the entrypoint function. """Execute the entrypoint function.
+1 -1
View File
@@ -1,3 +1,3 @@
"""A program to help you work with qmk_firmware.""" """A program to help you work with qmk_firmware."""
__version__ = '0.0.26' __version__ = '0.0.35'
+5 -5
View File
@@ -9,7 +9,7 @@ default_fork = 'qmk/' + default_repo
default_branch = 'master' default_branch = 'master'
def clone(url, destination, branch): def git_clone(url, destination, branch):
git_clone = [ git_clone = [
'git', 'git',
'clone', 'clone',
@@ -20,13 +20,13 @@ def clone(url, destination, branch):
] ]
cli.log.debug('Git clone command: %s', git_clone) cli.log.debug('Git clone command: %s', git_clone)
with subprocess.Popen(git_clone, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) as p: with subprocess.Popen(git_clone, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True, encoding='utf-8') as p:
for line in p.stdout: for line in p.stdout:
print(line, end='') print(line, end='')
if p.returncode != 0: if p.returncode != 0:
cli.log.error('git clone exited %d', p.returncode) cli.log.error('git clone exited %d', p.returncode)
return False
else: else:
cli.log.info('Successfully cloned %s to %s!', cli.args.fork, cli.args.destination) cli.log.info('Successfully cloned %s to %s!', url, destination)
return True
return p.returncode
+4 -3
View File
@@ -1,6 +1,6 @@
"""Useful helper functions. """Useful helper functions.
""" """
from milc import cli from milc import cli, format_ansi
def question(question, boolean=True, default=''): def question(question, boolean=True, default=''):
@@ -18,10 +18,11 @@ def question(question, boolean=True, default=''):
else: else:
answer_key = 'y/n' answer_key = 'y/n'
prompt = '*** %s [%s] ' % (question, answer_key) prompt = format_ansi('\n*** %s [%s] ' % (question, answer_key))
while True: while True:
answer = input(prompt) answer = input(prompt)
print()
if answer == '' and default.lower() == 'y': if answer == '' and default.lower() == 'y':
answer = 'y' answer = 'y'
elif answer == '' and default.lower() == 'n': elif answer == '' and default.lower() == 'n':
@@ -32,4 +33,4 @@ def question(question, boolean=True, default=''):
elif answer.lower() in ['n', 'no']: elif answer.lower() in ['n', 'no']:
return False return False
else: else:
cli.args.echo('Invalid answer!') cli.echo('Invalid answer!')
+24 -6
View File
@@ -2,9 +2,6 @@
"""CLI wrapper for running QMK commands. """CLI wrapper for running QMK commands.
This program can be run from anywhere, with or without a qmk_firmware repository. If a qmk_firmware repository can be located we will use that to augment our available subcommands. This program can be run from anywhere, with or without a qmk_firmware repository. If a qmk_firmware repository can be located we will use that to augment our available subcommands.
FIXME(skullydazed/anyone): --help shows underscores where we want dashes in subcommands (EG json_keymap instead of json-keymap)
TODO(skullydazed/anyone): Need a way to filter some subcommands from --help (EG `qmk hello`)
""" """
import argparse import argparse
import os import os
@@ -12,6 +9,7 @@ import subprocess
import sys import sys
from functools import lru_cache from functools import lru_cache
from pathlib import Path from pathlib import Path
import platform
import milc import milc
@@ -32,8 +30,13 @@ def in_qmk_firmware():
cur_dir = Path.cwd() cur_dir = Path.cwd()
while len(cur_dir.parents) > 0: while len(cur_dir.parents) > 0:
found_bin = cur_dir / 'bin' / 'qmk' found_bin = cur_dir / 'bin' / 'qmk'
if found_bin.is_file(): # An additional check for something that exists in the root of qmk_firmware,
command = [found_bin.as_posix(), '--version'] # but not in the script's install directory, to avoid recursive execution,
# if started from install directory.
# e.g.: cd ~/.local/bin && ./qmk
found_quantum = cur_dir / 'quantum'
if found_bin.is_file() and found_quantum.is_dir():
command = [sys.executable, found_bin.as_posix()]
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode == 0: if result.returncode == 0:
@@ -67,6 +70,16 @@ def find_qmk_firmware():
def main(): def main():
"""Setup the environment before dispatching to the entrypoint. """Setup the environment before dispatching to the entrypoint.
""" """
# Warn if they use an outdated python version
if sys.version_info < (3, 6):
print('Warning: Your Python version is out of date! Some subcommands may not work!')
print('Please upgrade to Python 3.6 or later.')
if 'windows' in platform.platform().lower():
if 'mingw64' not in sys.executable or 'mingw64' not in os.environ.get('MSYSTEM_PREFIX', ''):
print('Warning: It seems you are not using the MINGW64 terminal.')
print('While the MSYS one can work, too, we recommend/support you start "MSYS2 MinGW 64-bit".\n')
# Environment setup # Environment setup
import qmk_cli import qmk_cli
milc.cli.version = qmk_cli.__version__ milc.cli.version = qmk_cli.__version__
@@ -80,7 +93,12 @@ def main():
if qmk_firmware.exists(): if qmk_firmware.exists():
os.chdir(str(qmk_firmware)) os.chdir(str(qmk_firmware))
sys.path.append(str(qmk_firmware / 'lib' / 'python')) sys.path.append(str(qmk_firmware / 'lib' / 'python'))
import qmk.cli try:
import qmk.cli
except ImportError:
print('Error: %s is too old or not set up correctly!' % qmk_firmware)
print('Please update it or remove it completely before continuing.')
sys.exit(1)
# Call the entrypoint # Call the entrypoint
milc.cli() milc.cli()
+2 -3
View File
@@ -4,7 +4,7 @@ import os
from pathlib import Path from pathlib import Path
from milc import cli from milc import cli
from qmk_cli.git import clone as git_clone from qmk_cli.git import git_clone
default_repo = 'qmk_firmware' default_repo = 'qmk_firmware'
default_fork = 'qmk/' + default_repo default_fork = 'qmk/' + default_repo
@@ -27,5 +27,4 @@ def clone(cli):
cli.log.error('Destination already exists: %s', cli.args.destination) cli.log.error('Destination already exists: %s', cli.args.destination)
exit(1) exit(1)
success = git_clone(git_url, cli.args.destination, cli.config.clone.branch) return git_clone(git_url, cli.args.destination, cli.config.clone.branch)
exit(0 if success else 1)
+35 -21
View File
@@ -1,12 +1,13 @@
"""Setup qmk_firmware on your computer. """Setup qmk_firmware on your computer.
""" """
import os import os
import shlex
import subprocess import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
from milc import cli from milc import cli
from qmk_cli.git import clone from qmk_cli.git import git_clone
from qmk_cli.helpers import question from qmk_cli.helpers import question
default_repo = 'qmk_firmware' default_repo = 'qmk_firmware'
@@ -14,35 +15,48 @@ default_fork = 'qmk/' + default_repo
default_branch = 'master' default_branch = 'master'
@cli.argument('-y', '--yes', action='store_true', help='Answer yes to all questions') @cli.argument('-n', '--no', arg_only=True, action='store_true', help='Answer no to all questions')
@cli.argument('-y', '--yes', arg_only=True, action='store_true', help='Answer yes to all questions')
@cli.argument('--baseurl', default='https://github.com', help='The URL all git operations start from') @cli.argument('--baseurl', default='https://github.com', help='The URL all git operations start from')
@cli.argument('-b', '--branch', default=default_branch, help='The branch to clone') @cli.argument('-b', '--branch', default=default_branch, help='The branch to clone')
@cli.argument('destination', default=os.environ['QMK_HOME'], nargs='?', help='The directory to clone to') @cli.argument('-H', '--home', default=Path(os.environ['QMK_HOME']), type=Path, help='The location for QMK Firmware. Defaults to %s' % (os.environ['QMK_HOME'],))
@cli.argument('fork', default=default_fork, nargs='?', help='The qmk_firmware fork to clone') @cli.argument('fork', default=default_fork, nargs='?', help='The qmk_firmware fork to clone')
@cli.subcommand('Setup your computer for qmk_firmware.') @cli.subcommand('Setup your computer for qmk_firmware.')
def setup(cli): def setup(cli):
qmk_firmware = Path(cli.args.destination) """Guide the user through setting up their QMK environment.
"""
clone_prompt = 'Would you like to clone {fg_cyan}%s{fg_reset} to {fg_cyan}%s{fg_reset}?' % (cli.args.fork, shlex.quote(str(cli.args.home)))
home_prompt = 'Would you like to set {fg_cyan}%s{fg_reset} as your QMK home?' % (shlex.quote(str(cli.args.home)),)
# Sanity checks
if cli.args.yes and cli.args.no:
cli.log.error("Can't use both --yes and --no at the same time.")
exit(1)
# Check on qmk_firmware, and if it doesn't exist offer to check it out. # Check on qmk_firmware, and if it doesn't exist offer to check it out.
if qmk_firmware.exists(): if (cli.args.home / 'Makefile').exists():
cli.log.info('Found qmk_firmware at %s.', str(qmk_firmware)) cli.log.info('Found qmk_firmware at %s.', str(cli.args.home))
else: else:
cli.log.error('qmk_firmware not found!') cli.log.error('Could not find qmk_firmware!')
if question('Would you like to clone %s?' % cli.args.fork): if question(clone_prompt):
git_url = '/'.join((cli.config.setup.baseurl, cli.args.fork)) git_url = '/'.join((cli.config.setup.baseurl, cli.args.fork))
clone(git_url, cli.args.destination, cli.config.setup.branch) result = git_clone(git_url, cli.args.home, cli.config.setup.branch)
if result != 0:
exit(1)
# Offer to set `user.qmk_home` for them.
if cli.args.home != os.environ['QMK_HOME'] and question(home_prompt):
cli.config['user']['qmk_home'] = str(cli.args.home.absolute())
cli.write_config_option('user', 'qmk_home')
# Run `qmk_firmware/bin/qmk doctor` to check the rest of the environment out # Run `qmk_firmware/bin/qmk doctor` to check the rest of the environment out
if qmk_firmware.exists(): qmk_bin = cli.args.home / 'bin/qmk'
qmk_bin = qmk_firmware / 'bin' / 'qmk' doctor_cmd = [sys.executable, qmk_bin, 'doctor']
doctor = subprocess.run([sys.executable, qmk_bin, 'doctor'])
if doctor.returncode != 0:
cli.log.error('Your build environment is not setup completely.')
if question('Would you like to run util/qmk_install?'): if cli.args.yes:
curdir = os.getcwd() doctor_cmd.append('--yes')
os.chdir(str(qmk_firmware))
process = subprocess.run(['util/qmk_install.sh']) if cli.args.no:
os.chdir(curdir) doctor_cmd.append('--no')
if process.returncode == 0:
cli.log.info('QMK setup complete!') subprocess.run(doctor_cmd)
+1 -1
View File
@@ -12,7 +12,7 @@ TWINE_USERNAME=$PYPI_USERNAME
export FLIT_USERNAME TWINE_USERNAME export FLIT_USERNAME TWINE_USERNAME
rm dist/* rm -f dist/*
bumpversion patch bumpversion patch
git push origin master --tags git push origin master --tags
+1 -1
View File
@@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 0.0.26 current_version = 0.0.35
commit = True commit = True
tag = True tag = True
tag_name = {new_version} tag_name = {new_version}
+1
View File
@@ -46,6 +46,7 @@ if __name__ == "__main__":
"colorama", "colorama",
"flake8", "flake8",
"hjson", "hjson",
"nose2",
"yapf" "yapf"
], ],
) )
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
cp -r $1 ~/qmk_cli
export PATH=~/.local/bin:$PATH
echo "PATH=$PATH" >> ~/.bashrc
export QMK_HOME=~/qmk_firmware
export CLI_DIR=~/qmk_cli
cd $CLI_DIR
python3 -m pip install -r requirements.txt
python3 setup.py sdist bdist_wheel
cd ~
python3 -m pip install --force-reinstall --no-index --no-deps --prefix=~/.local --find-links qmk_cli/dist qmk