Compare commits

...

24 Commits

Author SHA1 Message Date
zvecr 2de8cf606c Convert to f-strings 2026-04-22 23:13:12 +01:00
Joel Challis 1f6bb7dab0 Fix badge link in README.md 2026-04-21 00:45:36 +01:00
Joel Challis 2eb3279945 Align README with other repos 2026-04-21 00:41:11 +01:00
Joel Challis 8fd7af98d9 Remove qmk_fpm references (#233) 2026-04-21 00:21:40 +01:00
Joel Challis 240ccd781a Remove the release script 2026-04-21 00:18:36 +01:00
Joel Challis af9e8063f6 Remove Windows environment checks (#232) 2026-04-20 18:18:03 +01:00
dependabot[bot] 3054dad82b Bump docker/setup-buildx-action from 3 to 4 (#226)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-13 20:45:31 +00:00
dependabot[bot] 39a271132b Bump docker/build-push-action from 6.18.0 to 7.0.0 (#227)
* Bump docker/build-push-action from 6.18.0 to 7.0.0

Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.18.0 to 7.0.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.18.0...v7.0.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Apply suggestions from code review

Co-authored-by: Joel Challis <git@zvecr.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joel Challis <git@zvecr.com>
2026-03-13 20:45:16 +00:00
dependabot[bot] c19c21449e Bump docker/login-action from 3 to 4 (#225)
Bumps [docker/login-action](https://github.com/docker/login-action) from 3 to 4.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-13 20:44:55 +00:00
dependabot[bot] f245cb7899 Bump docker/setup-qemu-action from 3 to 4 (#224)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-13 20:44:19 +00:00
dependabot[bot] 0b5866159a Bump WyriHaximus/github-action-get-previous-tag from 1 to 2 (#221)
Bumps [WyriHaximus/github-action-get-previous-tag](https://github.com/wyrihaximus/github-action-get-previous-tag) from 1 to 2.
- [Release notes](https://github.com/wyrihaximus/github-action-get-previous-tag/releases)
- [Commits](https://github.com/wyrihaximus/github-action-get-previous-tag/compare/v1...v2)

---
updated-dependencies:
- dependency-name: WyriHaximus/github-action-get-previous-tag
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-13 20:44:01 +00:00
QMK Bot 0dd2280781 New release: 1.1.8 → 1.2.0 2025-11-27 12:59:23 +00:00
Nick Brassel c049da0c6f Align QMK distrib search with qmk_firmware. (#219) 2025-11-27 23:35:47 +11:00
Joel Challis 7b69bd6f11 Align published container tags (#218) 2025-11-27 22:37:12 +11:00
Joel Challis c594e5df09 Build Dockerfile for PRs (#217) 2025-11-27 22:36:57 +11:00
Joel Challis 915bb40473 Align order of userspace search (#205) 2025-11-27 22:36:42 +11:00
Joel Challis af1403fc41 Fix relative paths if qmk_firmware exists elsewhere (#206) 2025-11-27 22:36:27 +11:00
Nick Brassel 13e5f5c855 Global install of QMK bootstrap distribution. (#216) 2025-11-27 07:07:38 +00:00
Nick Brassel f393ad0053 Use bootstrapper for build environment installation. (#210)
* Use bootstrapper for build environment installation.

* Publishing tags.

* Path.

* Minimisation of final image.

* Minimisation of final image.

* QMK distribution $PATH manipulation, to match qmk_firmware.

* QMK distribution $PATH manipulation, to match qmk_firmware.

* Fixup library search path on macOS.

* Escape hatch for path prefix.

* Add new environment variables.
2025-11-26 07:28:03 +11:00
dependabot[bot] 947e49a15d Bump actions/checkout from 5 to 6 (#214)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-25 19:51:30 +00:00
dependabot[bot] cd3ed07cd0 Bump github/codeql-action from 3 to 4 (#213)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-16 00:35:07 +01:00
dependabot[bot] 558e248e20 Bump actions/setup-python from 5 to 6 (#212)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-15 22:56:37 +01:00
dependabot[bot] cd1e98e9d0 Bump actions/checkout from 4 to 5 (#211)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-15 22:54:34 +01:00
dependabot[bot] 8c2ae297ed Bump docker/build-push-action from 6.16.0 to 6.18.0 (#208)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.16.0 to 6.18.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.16.0...v6.18.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 6.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-11 19:20:56 +01:00
20 changed files with 264 additions and 207 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 1.1.8 current_version = 1.2.0
commit = True commit = True
tag = True tag = True
tag_name = {new_version} tag_name = {new_version}
+5 -8
View File
@@ -15,7 +15,7 @@ jobs:
QMK_HOME: ~/qmk_firmware QMK_HOME: ~/qmk_firmware
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Install dependencies - name: Install dependencies
run: apt-get update && apt-get install -y python3-venv run: apt-get update && apt-get install -y python3-venv
@@ -32,16 +32,13 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [macos-latest, ubuntu-latest] os: [macos-latest, ubuntu-latest]
python-version: ['3.9', '3.10', '3.11', '3.12'] python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
include:
- os: macos-13
python-version: '3.9'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
@@ -56,7 +53,7 @@ jobs:
QMK_HOME: $HOME/qmk_firmware QMK_HOME: $HOME/qmk_firmware
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: (MSYS2) Setup and install dependencies - name: (MSYS2) Setup and install dependencies
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
+4 -4
View File
@@ -39,11 +39,11 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,7 +54,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v3 uses: github/codeql-action/autobuild@v4
# ️ Command-line programs to run using the OS shell. # ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@@ -68,4 +68,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v4
+42
View File
@@ -0,0 +1,42 @@
name: Test Docker Image
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.9'
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- uses: actions/checkout@v6
- name: Run ci_tests
run: ./ci_tests
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
pip install setuptools wheel
pip install -r requirements-dev.txt
- name: Build Python
run: |
python3 -m build
- name: Build Container
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
+12 -8
View File
@@ -10,43 +10,43 @@ jobs:
steps: steps:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.9' python-version: '3.9'
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- name: 'Get Previous tag' - name: 'Get Previous tag'
id: previoustag id: previoustag
uses: "WyriHaximus/github-action-get-previous-tag@v1" uses: "WyriHaximus/github-action-get-previous-tag@v2"
- name: Download previous artifact - name: Download previous artifact
run: | run: |
pip download -d "${GITHUB_WORKSPACE}/dist" qmk=="${{ steps.previoustag.outputs.tag }}" pip download -d "${GITHUB_WORKSPACE}/dist" qmk=="${{ steps.previoustag.outputs.tag }}"
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and Push to Docker Hub - name: Build and Push to Docker Hub
uses: docker/build-push-action@v6.16.0 uses: docker/build-push-action@v7
with: with:
context: . context: .
push: true push: true
@@ -54,3 +54,7 @@ jobs:
tags: | tags: |
ghcr.io/qmk/qmk_cli:latest ghcr.io/qmk/qmk_cli:latest
qmkfm/qmk_cli:latest qmkfm/qmk_cli:latest
ghcr.io/qmk/qmk_cli:${{ github.sha }}
qmkfm/qmk_cli:${{ github.sha }}
ghcr.io/qmk/qmk_cli:${{ steps.previoustag.outputs.tag }}
qmkfm/qmk_cli:${{ steps.previoustag.outputs.tag }}
+12 -11
View File
@@ -18,17 +18,17 @@ jobs:
steps: steps:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.9' python-version: '3.9'
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Run ci_tests - name: Run ci_tests
run: ./ci_tests run: ./ci_tests
@@ -43,6 +43,7 @@ jobs:
run: | run: |
git config --local user.email "hello@qmk.fm" git config --local user.email "hello@qmk.fm"
git config --local user.name "QMK Bot" git config --local user.name "QMK Bot"
bumpversion --dry-run --list ${{ github.event.inputs.version_part }} | grep new_version= | sed "s/new_version/NEW_CLI_VERSION/" >> "$GITHUB_ENV"
bumpversion ${{ github.event.inputs.version_part }} bumpversion ${{ github.event.inputs.version_part }}
- name: Push changes - name: Push changes
@@ -61,20 +62,20 @@ jobs:
twine upload dist/* twine upload dist/*
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
username: ${{ secrets.DOCKER_HUB_USERNAME }} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and Push to Docker Hub - name: Build and Push to Docker Hub
uses: docker/build-push-action@v6.16.0 uses: docker/build-push-action@v7
with: with:
context: . context: .
push: true push: true
@@ -82,8 +83,8 @@ jobs:
tags: | tags: |
ghcr.io/qmk/qmk_cli:latest ghcr.io/qmk/qmk_cli:latest
qmkfm/qmk_cli:latest qmkfm/qmk_cli:latest
ghcr.io/qmk/qmk_cli:${{ github.sha }}
qmkfm/qmk_cli:${{ github.sha }}
ghcr.io/qmk/qmk_cli:${{ env.NEW_CLI_VERSION }}
qmkfm/qmk_cli:${{ env.NEW_CLI_VERSION }}
- name: Trigger OS package builds
run: ./trigger_packages
env:
QMK_BOT_TOKEN: ${{ secrets.QMK_BOT_TOKEN }}
+27 -5
View File
@@ -1,13 +1,35 @@
FROM ghcr.io/qmk/qmk_base_container:latest FROM ghcr.io/qmk/qmk_base_container:latest as builder
# Copy package in # Copy package in
ADD dist /tmp/dist ADD dist /tmp/dist
# Install QMK CLI via bootstrap script
RUN mkdir -p /opt/uv/bin \
&& export UV_PYTHON_INSTALL_DIR=/opt/uv/bin \
&& /bin/bash -c "curl -fsSL https://install.qmk.fm | sh -s -- --confirm --uv-install-dir=/usr/local/bin --uv-tool-dir=/opt/uv/tools --qmk-distrib-dir=/opt/qmk"
# Do the equivalent of entering the virtual environment
ENV PATH=/opt/qmk/bin:/opt/uv/tools/qmk/bin:/usr/local/bin:$PATH \
VIRTUAL_ENV=/opt/uv/tools/qmk
# Install python packages # Install python packages
RUN python3 -m pip uninstall -y qmk || true RUN python3 -m pip uninstall -y qmk || true
RUN python3 -m pip install --upgrade pip setuptools wheel nose2 && \ RUN python3 -m pip install --upgrade pip setuptools wheel nose2 && \
python3 -m pip install /tmp/dist/qmk-*.whl && \ python3 -m pip install /tmp/dist/qmk-*.whl
rm -rf /tmp/dist
# Allow the uv-installed python to be usable by others
RUN chmod -R go=u,go-w /opt/uv /opt/qmk
# 2nd stage so we don't have /tmp/dist in the final image
FROM ghcr.io/qmk/qmk_base_container:latest
COPY --from=builder /opt/uv /opt/uv
COPY --from=builder /opt/qmk /opt/qmk
# Do the equivalent of entering the virtual environment
ENV PATH=/opt/qmk/bin:/opt/uv/tools/qmk/bin:$PATH \
VIRTUAL_ENV=/opt/uv/tools/qmk \
QMK_DISTRIB_DIR=/opt/qmk \
QMK_HOME=/qmk_firmware \
QMK_USERSPACE=/qmk_userspace
# Set the default location for qmk_firmware
ENV QMK_HOME /qmk_firmware
+15 -17
View File
@@ -1,9 +1,12 @@
# QMK CLI # QMK CLI
[![CLI Setup](https://github.com/qmk/qmk_cli/actions/workflows/cli_setup.yml/badge.svg)](https://github.com/qmk/qmk_cli/actions/workflows/cli_setup.yml)
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 [![Latest Release](https://img.shields.io/github/v/tag/qmk/qmk_cli?color=3D87CE&label=Latest&sort=semver&style=for-the-badge)](https://github.com/qmk/qmk_cli/tags)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/qmk/qmk_cli/cli_setup.yml?logo=github&style=for-the-badge)](https://github.com/qmk/qmk_cli/actions?query=workflow%3ACLI+branch%3Amaster)
[![Discord](https://img.shields.io/discord/440868230475677696.svg?logo=discord&logoColor=white&color=7289DA&style=for-the-badge)](https://discord.gg/qmk)
## Features
* Interact with your qmk_firmware tree from any location * Interact with your qmk_firmware tree from any location
* Use `qmk clone` to pull down anyone's `qmk_firmware` fork * Use `qmk clone` to pull down anyone's `qmk_firmware` fork
@@ -17,30 +20,25 @@ A program to help users work with [QMK Firmware](https://qmk.fm/).
* `qmk lint` * `qmk lint`
* ...and many more! * ...and many more!
# Packages ## Quickstart
Follow our [documentation to get started](https://docs.qmk.fm/newbs_getting_started).
## Packages
We provide "install and go" packages for many Operating Systems. We provide "install and go" packages for many Operating Systems.
## Linux ### macOS
Packages for several distributions available here: https://github.com/qmk/qmk_fpm
## macOS
Using [Homebrew](https://brew.sh): Using [Homebrew](https://brew.sh):
brew install qmk/qmk/qmk brew install qmk/qmk/qmk
## Windows ### Windows
Download our custom MSYS2 installer here: https://msys.qmk.fm/ Download our custom MSYS2 installer here: https://msys.qmk.fm/
# Quickstart ## Building
* `python3 -m pip install qmk`
* `qmk setup`
# Building
We follow PEP517, you can install using [build](https://pypi.org/project/build/): We follow PEP517, you can install using [build](https://pypi.org/project/build/):
@@ -54,6 +52,6 @@ Build:
You can read more about working with PEP517 packages in the [Python Packaging User Guide](https://packaging.python.org/guides/distributing-packages-using-setuptools/#packaging-your-project). You can read more about working with PEP517 packages in the [Python Packaging User Guide](https://packaging.python.org/guides/distributing-packages-using-setuptools/#packaging-your-project).
# Documentation ## Documentation
Full documentation: <https://docs.qmk.fm/#/tutorial> Full documentation: <https://docs.qmk.fm/#/tutorial>
+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__ = '1.1.8' __version__ = '1.2.0'
+7 -12
View File
@@ -4,10 +4,6 @@ import subprocess
from milc import cli from milc import cli
default_repo = 'qmk_firmware'
default_fork = 'qmk/' + default_repo
default_branch = 'master'
def git_clone(url, destination, branch): def git_clone(url, destination, branch):
git_clone = [ git_clone = [
@@ -18,7 +14,7 @@ def git_clone(url, destination, branch):
url, url,
str(destination), str(destination),
] ]
cli.log.debug('Git clone command: %s', git_clone) cli.log.debug(f'Git clone command: {git_clone}', )
try: try:
with subprocess.Popen(git_clone, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True, encoding='utf-8') as p: with subprocess.Popen(git_clone, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True, encoding='utf-8') as p:
@@ -28,13 +24,12 @@ def git_clone(url, destination, branch):
except Exception as e: except Exception as e:
git_cmd = ' '.join([s.replace(' ', r'\ ') for s in git_clone]) git_cmd = ' '.join([s.replace(' ', r'\ ') for s in git_clone])
cli.log.error("Could not run '%s': %s: %s", git_cmd, e.__class__.__name__, e) cli.log.error(f'Could not run "{git_cmd}": {e.__class__}: {e}')
return False return False
if p.returncode == 0: if p.returncode != 0:
cli.log.info('Successfully cloned %s to %s!', url, destination) cli.log.error(f'git clone exited {p.returncode}')
return True
else:
cli.log.error('git clone exited %d', p.returncode)
return False return False
cli.log.info(f'Successfully cloned {url} to {destination}!')
return True
+13 -3
View File
@@ -8,6 +8,16 @@ from pathlib import Path
from milc import cli from milc import cli
def AbsPath(arg):
"""Resolve relative paths to the original working directory.
"""
arg = Path(arg)
if not arg.is_absolute():
return (Path(os.environ['ORIG_CWD']) / arg).absolute()
return arg
def is_qmk_firmware(qmk_firmware): def is_qmk_firmware(qmk_firmware):
"""Returns True if the given Path() is a qmk_firmware clone. """Returns True if the given Path() is a qmk_firmware clone.
""" """
@@ -81,15 +91,15 @@ def find_qmk_userspace():
if in_qmk_userspace(): if in_qmk_userspace():
return in_qmk_userspace() return in_qmk_userspace()
if cli.config.user.overlay_dir:
return Path(cli.config.user.overlay_dir).expanduser().resolve()
if 'QMK_USERSPACE' in os.environ: if 'QMK_USERSPACE' in os.environ:
path = Path(os.environ['QMK_USERSPACE']).expanduser() path = Path(os.environ['QMK_USERSPACE']).expanduser()
if path.exists(): if path.exists():
return path.resolve() return path.resolve()
return path return path
if cli.config.user.overlay_dir:
return Path(cli.config.user.overlay_dir).expanduser().resolve()
return Path.home() / 'qmk_userspace' return Path.home() / 'qmk_userspace'
+27 -24
View File
@@ -4,6 +4,7 @@
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.
""" """
import os import os
from pathlib import Path
import shlex import shlex
import subprocess import subprocess
import sys import sys
@@ -11,10 +12,33 @@ from platform import platform
from traceback import print_exc from traceback import print_exc
import milc import milc
import platformdirs
from . import __version__ from . import __version__
from .helpers import find_qmk_firmware, is_qmk_firmware, find_qmk_userspace, is_qmk_userspace from .helpers import find_qmk_firmware, is_qmk_firmware, find_qmk_userspace, is_qmk_userspace
def _get_default_distrib_path():
if 'windows' in platform().lower():
try:
result = milc.cli.run(['cygpath', '-w', '/opt/qmk'])
if result.returncode == 0:
return result.stdout.strip()
except Exception:
pass
return platformdirs.user_data_dir('qmk')
# Ensure the QMK distribution is on the `$PATH` if present. This must be kept in sync with qmk/qmk_firmware.
QMK_DISTRIB_DIR = Path(os.environ.get('QMK_DISTRIB_DIR', _get_default_distrib_path()))
if QMK_DISTRIB_DIR.exists():
os.environ['PATH'] = str(QMK_DISTRIB_DIR / 'bin') + os.pathsep + os.environ['PATH']
# Prepend any user-defined path prefix
if 'QMK_PATH_PREFIX' in os.environ:
os.environ['PATH'] = os.environ['QMK_PATH_PREFIX'] + os.pathsep + os.environ['PATH']
milc.cli.milc_options(version=__version__) milc.cli.milc_options(version=__version__)
milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}' milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}'
@@ -49,27 +73,6 @@ def main():
print('Warning: Your Python version is out of date! Some subcommands may not work!') print('Warning: Your Python version is out of date! Some subcommands may not work!')
print('Please upgrade to Python 3.9 or later.') print('Please upgrade to Python 3.9 or later.')
if 'windows' in platform().lower():
msystem = os.environ.get('MSYSTEM', '')
# Assume the environment isn't workable by default
env_ok = False
# Check if we're using the mingw64/msys2 environment
if 'mingw64' in sys.executable and 'MINGW64' in msystem:
env_ok = True
# Check if we're using a `uv`-based environment
if '\\uv\\' in sys.executable or '/uv/' in sys.executable:
env_ok = True
# If none of the options above were true, then we're in an unsupported environment. Bomb out.
if not env_ok:
print('ERROR: It seems you are not using the MINGW64 terminal.')
print('Please close this terminal and open a new MSYS2 MinGW 64-bit terminal.')
print('Python: %s, MSYSTEM: %s' % (sys.executable, msystem))
exit(1)
# Environment setup # Environment setup
qmk_userspace = find_qmk_userspace() qmk_userspace = find_qmk_userspace()
qmk_firmware = find_qmk_firmware() qmk_firmware = find_qmk_firmware()
@@ -93,9 +96,9 @@ def main():
except ImportError as e: except ImportError as e:
if qmk_firmware.name != 'qmk_firmware': if qmk_firmware.name != 'qmk_firmware':
print('Warning: %s does not end in "qmk_firmware". Do you need to set QMK_HOME to "%s/qmk_firmware"?' % (qmk_firmware, qmk_firmware)) print(f'Warning: {qmk_firmware} does not end in "qmk_firmware". Do you need to set QMK_HOME to "{qmk_firmware}/qmk_firmware"?')
print('Error: %s: %s', (e.__class__.__name__, e)) print(f'Error: {e.__class__}: {e}')
print_exc() print_exc()
sys.exit(1) sys.exit(1)
@@ -107,7 +110,7 @@ def main():
elif return_code is not True and isinstance(return_code, int): elif return_code is not True and isinstance(return_code, int):
if return_code < 0 or return_code > 255: if return_code < 0 or return_code > 255:
milc.cli.log.error('Invalid return_code: %d', return_code) milc.cli.log.error(f'Invalid return_code: {return_code}')
exit(255) exit(255)
exit(return_code) exit(return_code)
+6 -9
View File
@@ -5,6 +5,7 @@ from pathlib import Path
from milc import cli from milc import cli
from qmk_cli.git import git_clone from qmk_cli.git import git_clone
from qmk_cli.helpers import AbsPath
default_repo = 'qmk_firmware' default_repo = 'qmk_firmware'
default_fork = 'qmk/' + default_repo default_fork = 'qmk/' + default_repo
@@ -12,20 +13,16 @@ default_branch = 'master'
@cli.argument('--baseurl', arg_only=True, default='https://github.com', help='The URL all git operations start from (Default: https://github.com)') @cli.argument('--baseurl', arg_only=True, default='https://github.com', help='The URL all git operations start from (Default: https://github.com)')
@cli.argument('-b', '--branch', arg_only=True, default=default_branch, help='The branch to clone. Default: %s' % default_branch) @cli.argument('-b', '--branch', arg_only=True, default=default_branch, help=f'The branch to clone. Default: {default_branch}')
@cli.argument('destination', arg_only=True, default=None, nargs='?', help='The directory to clone to. Default: (current directory)') @cli.argument('destination', arg_only=True, default=Path(os.environ['ORIG_CWD']) / default_repo, type=AbsPath, nargs='?', help='The directory to clone to. Default: (current directory)')
@cli.argument('fork', arg_only=True, default=default_fork, nargs='?', help='The qmk_firmware fork to clone. Default: %s' % default_fork) @cli.argument('fork', arg_only=True, default=default_fork, nargs='?', help=f'The qmk_firmware fork to clone. Default: {default_fork}')
@cli.subcommand('Clone a qmk_firmware fork.') @cli.subcommand('Clone a qmk_firmware fork.')
def clone(cli): def clone(cli):
if not cli.args.destination:
cli.args.destination = os.path.join(os.environ['ORIG_CWD'], default_fork.split('/')[-1])
qmk_firmware = Path(cli.args.destination)
git_url = '/'.join((cli.args.baseurl, cli.args.fork)) git_url = '/'.join((cli.args.baseurl, cli.args.fork))
# Exists (but not an empty dir) # Exists (but not an empty dir)
if qmk_firmware.exists() and any(qmk_firmware.iterdir()): if cli.args.destination.exists() and any(cli.args.destination.iterdir()):
cli.log.error('Destination already exists: %s', cli.args.destination) cli.log.error(f'Destination already exists: {cli.args.destination}')
exit(1) exit(1)
return git_clone(git_url, cli.args.destination, cli.args.branch) return git_clone(git_url, cli.args.destination, cli.args.branch)
+60 -37
View File
@@ -57,6 +57,26 @@ KNOWN_BOOTLOADERS = {
} }
def _get_next_color():
"""Cycles to the next available log color.
"""
color = LOG_COLOR['colors'][LOG_COLOR['next']]
LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
return color
def _gen_header(dev):
"""Generate a consistent logging segment.
"""
return f'{dev["color"]}{dev["manufacturer_string"]} {dev["product_string"]}{{style_reset_all}} ({dev["color"]}{int2hex(dev["vendor_id"])}:{int2hex(dev["product_id"])}:{dev["index"]}{{style_reset_all}})'
def int2hex(number):
"""Returns a string representation of the number as hex.
"""
return f'{number:04X}'
def install_deps(): def install_deps():
"""Install the necessary dependencies for qmk console. """Install the necessary dependencies for qmk console.
""" """
@@ -69,9 +89,10 @@ def install_deps():
elif 'windows' in this_platform: elif 'windows' in this_platform:
command = ['pacboy', 'sync', '--needed', '--noconfirm', '--disable-download-timeout', 'hidapi:x'] command = ['pacboy', 'sync', '--needed', '--noconfirm', '--disable-download-timeout', 'hidapi:x']
else: else:
cli.log.error('Unsupported platform: %s', this_platform) cli.log.error(f'Unsupported platform: {this_platform}')
if yesno("Would you like to run `%s` to install the necessary package?", ' '.join(command)): cmd_str = ' '.join(command)
if yesno(f'Would you like to run "{cmd_str}" to install the necessary package?'):
cli.run(command, capture_output=False) cli.run(command, capture_output=False)
return True return True
@@ -85,7 +106,7 @@ def import_usb_core():
return usb.core return usb.core
except ImportError as e: except ImportError as e:
cli.log.error('Could not import usb.core: %s', e) cli.log.error(f'Could not import usb.core: {e}')
if install_deps(): if install_deps():
return import_usb_core() return import_usb_core()
@@ -96,17 +117,31 @@ def import_usb_core():
def import_hid(): def import_hid():
"""Attempts to import the hid module. """Attempts to import the hid module.
""" """
import os
old_cwd = os.getcwd()
try: try:
# macOS library search paths don't include homebrew by default when launched via a `uv`-based deployment.
# The `hid` python module does a search and attempts to load a set of known library names, but doesn't qualify the path, nor gives the option to do so.
# To work around this, we chdir to the homebrew lib directory before importing hid, then chdir back to where we were.
if 'darwin' in platform().lower() or 'macos' in platform().lower():
for path in ('/opt/homebrew/lib', '/usr/local/lib'):
lib_search = Path(path) / 'libhidapi.dylib'
if lib_search.exists():
os.chdir(path)
break
import hid import hid
return hid return hid
except ImportError as e: except ImportError as e:
cli.log.error('Could not import hid: %s', e) cli.log.error(f'Could not import hid: {e}')
if install_deps(): if install_deps():
return import_hid() return import_hid()
raise raise
finally:
os.chdir(old_cwd)
class MonitorDevice(object): class MonitorDevice(object):
@@ -117,8 +152,7 @@ class MonitorDevice(object):
self.device = self.hid.Device(path=hid_device['path']) self.device = self.hid.Device(path=hid_device['path'])
self.current_line = '' self.current_line = ''
cli.log.info('Console Connected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', hid_device) cli.log.info(f'Console Connected: {_gen_header(hid_device)}')
def read(self, size, encoding='ascii', timeout=1): def read(self, size, encoding='ascii', timeout=1):
"""Read size bytes from the device. """Read size bytes from the device.
""" """
@@ -138,12 +172,10 @@ class MonitorDevice(object):
def run_forever(self): def run_forever(self):
while True: while True:
try: try:
message = {**self.hid_device, 'text': self.read_line()} text = self.read_line()
identifier = (int2hex(message['vendor_id']), int2hex(message['product_id'])) if self.numeric else (message['manufacturer_string'], message['product_string']) ts = f'{{style_dim}}{{fg_green}}{strftime(cli.config.general.datetime_fmt)}{{style_reset_all}}' if cli.args.timestamp else ''
message['identifier'] = ':'.join(identifier)
message['ts'] = '{style_dim}{fg_green}%s{style_reset_all} ' % (strftime(cli.config.general.datetime_fmt),) if cli.args.timestamp else ''
cli.echo('%s', '%(ts)s%(color)s%(identifier)s:%(index)d{style_reset_all}: %(text)s' % message) cli.echo(f'{ts}{_gen_header(self.hid_device)}: {text}')
except self.hid.HIDException: except self.hid.HIDException:
break break
@@ -167,13 +199,12 @@ class FindDevices(object):
try: try:
for device in list(live_devices): for device in list(live_devices):
if not live_devices[device]['thread'].is_alive(): if not live_devices[device]['thread'].is_alive():
cli.log.info('Console Disconnected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', live_devices[device]) cli.log.info(f'Console Disconnected: {_gen_header(live_devices[device])}')
del live_devices[device] del live_devices[device]
for device in self.find_devices(): for device in self.find_devices():
if device['path'] not in live_devices: if device['path'] not in live_devices:
device['color'] = LOG_COLOR['colors'][LOG_COLOR['next']] device['color'] = _get_next_color()
LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
live_devices[device['path']] = device live_devices[device['path']] = device
try: try:
@@ -182,9 +213,7 @@ class FindDevices(object):
device['thread'].start() device['thread'].start()
except Exception as e: except Exception as e:
device['e'] = e cli.log.error(f'Could not connect to {_gen_header(device)}: {e.__class__.__name__}: {e}')
device['e_name'] = e.__class__.__name__
cli.log.error("Could not connect to %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all}): %(e_name)s: %(e)s", device)
if cli.config.general.verbose: if cli.config.general.verbose:
cli.log.exception(e) cli.log.exception(e)
del live_devices[device['path']] del live_devices[device['path']]
@@ -195,7 +224,7 @@ class FindDevices(object):
live_bootloaders[device.address]._qmk_found = True live_bootloaders[device.address]._qmk_found = True
else: else:
name = KNOWN_BOOTLOADERS[(int2hex(device.idVendor), int2hex(device.idProduct))] name = KNOWN_BOOTLOADERS[(int2hex(device.idVendor), int2hex(device.idProduct))]
cli.log.info('Bootloader Connected: {style_bright}{fg_magenta}%s', name) cli.log.info(f'Bootloader Connected: {{style_bright}}{{fg_magenta}}{name}')
device._qmk_found = True device._qmk_found = True
live_bootloaders[device.address] = device live_bootloaders[device.address] = device
@@ -204,7 +233,7 @@ class FindDevices(object):
live_bootloaders[device]._qmk_found = False live_bootloaders[device]._qmk_found = False
else: else:
name = KNOWN_BOOTLOADERS[(int2hex(live_bootloaders[device].idVendor), int2hex(live_bootloaders[device].idProduct))] name = KNOWN_BOOTLOADERS[(int2hex(live_bootloaders[device].idVendor), int2hex(live_bootloaders[device].idProduct))]
cli.log.info('Bootloader Disconnected: {style_bright}{fg_magenta}%s', name) cli.log.info(f'Bootloader Disconnected: {{style_bright}}{{fg_magenta}}{name}')
del live_bootloaders[device] del live_bootloaders[device]
sleep(.1) sleep(.1)
@@ -271,23 +300,17 @@ class FindDevices(object):
# Add index numbers # Add index numbers
device_index = {} device_index = {}
for device in devices: for device in devices:
id = ':'.join((int2hex(device['vendor_id']), int2hex(device['product_id']))) ident = ':'.join((int2hex(device['vendor_id']), int2hex(device['product_id'])))
if id not in device_index: if ident not in device_index:
device_index[id] = 0 device_index[ident] = 0
device_index[id] += 1 device_index[ident] += 1
device['index'] = device_index[id] device['index'] = device_index[ident]
return devices return devices
def int2hex(number):
"""Returns a string representation of the number as hex.
"""
return "%04X" % number
def list_devices(device_finder): def list_devices(device_finder):
"""Show the user a nicely formatted list of devices. """Show the user a nicely formatted list of devices.
""" """
@@ -296,9 +319,8 @@ def list_devices(device_finder):
if devices: if devices:
cli.log.info('Available devices:') cli.log.info('Available devices:')
for dev in devices: for dev in devices:
color = LOG_COLOR['colors'][LOG_COLOR['next']] color = _get_next_color()
LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors']) cli.log.info(f'\t{color}{int2hex(dev["vendor_id"])}:{int2hex(dev["product_id"])}:{dev["index"]}{{style_reset_all}}\t{dev["manufacturer_string"]} {dev["product_string"]}')
cli.log.info("\t%s%s:%s:%d{style_reset_all}\t%s %s", color, int2hex(dev['vendor_id']), int2hex(dev['product_id']), dev['index'], dev['manufacturer_string'], dev['product_string'])
if cli.args.bootloaders: if cli.args.bootloaders:
bootloaders = device_finder.find_bootloaders() bootloaders = device_finder.find_bootloaders()
@@ -307,7 +329,8 @@ def list_devices(device_finder):
cli.log.info('Available Bootloaders:') cli.log.info('Available Bootloaders:')
for dev in bootloaders: for dev in bootloaders:
cli.log.info("\t%s:%s\t%s", int2hex(dev.idVendor), int2hex(dev.idProduct), KNOWN_BOOTLOADERS[(int2hex(dev.idVendor), int2hex(dev.idProduct))]) color = _get_next_color()
cli.log.info(f'\t{color}{int2hex(dev.idVendor)}:{int2hex(dev.idProduct)}{{style_reset_all}}\t{KNOWN_BOOTLOADERS[(int2hex(dev.idVendor), int2hex(dev.idProduct))]}')
@cli.argument('--bootloaders', arg_only=True, default=True, action='store_boolean', help='displaying bootloaders.') @cli.argument('--bootloaders', arg_only=True, default=True, action='store_boolean', help='displaying bootloaders.')
@@ -334,17 +357,17 @@ def console(cli):
vid, pid, index = device vid, pid, index = device
if not index.isdigit(): if not index.isdigit():
cli.log.error('Device index must be a number! Got "%s" instead.', index) cli.log.error(f'Device index must be a number! Got "{index}" instead.')
exit(1) exit(1)
index = int(index) index = int(index)
if index < 1: if index < 1:
cli.log.error('Device index must be greater than 0! Got %s', index) cli.log.error(f'Device index must be greater than 0! Got "{index}".')
exit(1) exit(1)
else: else:
cli.log.error('Invalid format for device, expected "<pid>:<vid>[:<index>]" but got "%s".', cli.config.console.device) cli.log.error(f'Invalid format for device, expected "<pid>:<vid>[:<index>]" but got "{cli.config.console.device}".')
cli.print_help() cli.print_help()
exit(1) exit(1)
+6 -4
View File
@@ -10,12 +10,14 @@ from qmk_cli.helpers import is_qmk_firmware, is_qmk_userspace
@cli.argument('var', arg_only=True, default=None, nargs='?', help='Optional variable to query') @cli.argument('var', arg_only=True, default=None, nargs='?', help='Optional variable to query')
@cli.subcommand('Prints environment information.') @cli.subcommand('Prints environment information.')
def env(cli): def env(cli):
home = os.environ.get('QMK_HOME', "") home = os.environ.get('QMK_HOME', '')
userspace = os.environ.get('QMK_USERSPACE', "") userspace = os.environ.get('QMK_USERSPACE', '')
data = { data = {
'QMK_HOME': home, 'QMK_HOME': home,
'QMK_FIRMWARE': home if is_qmk_firmware(Path(home)) else "", 'QMK_FIRMWARE': home if is_qmk_firmware(Path(home)) else '',
'QMK_USERSPACE': userspace if is_qmk_userspace(Path(userspace)) else "" 'QMK_USERSPACE': userspace if is_qmk_userspace(Path(userspace)) else '',
'QMK_DISTRIB_DIR': os.environ.get('QMK_DISTRIB_DIR', ''),
'QMK_PATH_PREFIX': os.environ.get('QMK_PATH_PREFIX', '')
} }
# Now munge the current cli config # Now munge the current cli config
+24 -25
View File
@@ -1,7 +1,6 @@
"""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
@@ -10,7 +9,7 @@ from shutil import rmtree
from milc import cli from milc import cli
from milc.questions import choice, question, yesno from milc.questions import choice, question, yesno
from qmk_cli.git import git_clone from qmk_cli.git import git_clone
from qmk_cli.helpers import is_qmk_firmware from qmk_cli.helpers import AbsPath, is_qmk_firmware
default_base = 'https://github.com' default_base = 'https://github.com'
default_repo = 'qmk_firmware' default_repo = 'qmk_firmware'
@@ -36,13 +35,13 @@ def git_upstream(destination):
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.info('Added %s as remote upstream.', git_url) cli.log.error(f'git remote exited {p.returncode}')
return True
else:
cli.log.error('%s exited %d', ' '.join(git_cmd), p.returncode)
return False return False
cli.log.info(f'Added {git_url} as remote upstream.')
return True
def git_clone_fork(fork, branch, force=False): def git_clone_fork(fork, branch, force=False):
if force: if force:
@@ -57,16 +56,16 @@ def git_clone_fork(fork, branch, force=False):
@cli.argument('-n', '--no', arg_only=True, action='store_true', help='Answer no 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('-y', '--yes', arg_only=True, action='store_true', help='Answer yes to all questions')
@cli.argument('--baseurl', arg_only=True, default=default_base, help='The URL all git operations start from. Default: %s' % default_base) @cli.argument('--baseurl', arg_only=True, default=default_base, help=f'The URL all git operations start from. Default: {default_base}')
@cli.argument('-b', '--branch', arg_only=True, default=default_branch, help='The branch to clone. Default: %s' % default_branch) @cli.argument('-b', '--branch', arg_only=True, default=default_branch, help=f'The branch to clone. Default: {default_branch}')
@cli.argument('-H', '--home', arg_only=True, default=Path(os.environ['QMK_HOME']), type=Path, help='The location for QMK Firmware. Default: %s' % os.environ['QMK_HOME']) @cli.argument('-H', '--home', arg_only=True, default=Path(os.environ['QMK_HOME']), type=AbsPath, help=f'The location for QMK Firmware. Default: {os.environ["QMK_HOME"]}')
@cli.argument('fork', arg_only=True, default=default_fork, nargs='?', help='The qmk_firmware fork to clone. Default: %s' % default_fork) @cli.argument('fork', arg_only=True, default=default_fork, nargs='?', help=f'The qmk_firmware fork to clone. Default: {default_fork}')
@cli.subcommand('Setup your computer for qmk_firmware.') @cli.subcommand('Setup your computer for qmk_firmware.')
def setup(cli): def setup(cli):
"""Guide the user through setting up their QMK environment. """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))) clone_prompt = f'Would you like to clone {{fg_cyan}}{cli.args.fork}{{fg_reset}} to {{fg_cyan}}{cli.args.home}{{fg_reset}}?'
home_prompt = 'Would you like to set {fg_cyan}%s{fg_reset} as your QMK home?' % (shlex.quote(str(cli.args.home)),) home_prompt = f'Would you like to set {{fg_cyan}}{cli.args.home}{{fg_reset}} as your QMK home?'
# Sanity checks # Sanity checks
if cli.args.yes and cli.args.no: if cli.args.yes and cli.args.no:
@@ -77,25 +76,25 @@ def setup(cli):
# If it exists, ask the user what to do with it # If it exists, ask the user what to do with it
# If it doesn't exist, offer to check it out # If it doesn't exist, offer to check it out
if is_qmk_firmware(cli.args.home): if is_qmk_firmware(cli.args.home):
cli.log.info('Found qmk_firmware at %s.', str(cli.args.home)) cli.log.info(f'Found qmk_firmware at "{cli.args.home}".')
found_prompt = "What do you want to do?" found_prompt = 'What do you want to do?'
found_options = [ found_options = [
f"Delete and reclone {cli.args.fork}", f'Delete and reclone {cli.args.fork}',
"Delete and clone a different fork", 'Delete and clone a different fork',
"Keep it and continue" 'Keep it and continue'
] ]
delete_confirm = "WARNING: This will delete your current qmk_firmware directory. Proceed?" delete_confirm = 'WARNING: This will delete your current qmk_firmware directory. Proceed?'
found_action = choice(found_prompt, options=found_options, default=2) found_action = choice(found_prompt, options=found_options, default=2)
if found_action == f"Delete and reclone {cli.args.fork}": if found_action == f'Delete and reclone {cli.args.fork}':
if not yesno(delete_confirm, default=False): if not yesno(delete_confirm, default=False):
exit(1) exit(1)
git_clone_fork(cli.args.fork, cli.args.branch, force=True) git_clone_fork(cli.args.fork, cli.args.branch, force=True)
elif found_action == "Delete and clone a different fork": elif found_action == 'Delete and clone a different fork':
fork_name = question("Enter the name of the fork:", default=cli.args.fork) fork_name = question('Enter the name of the fork:', default=cli.args.fork)
branch_name = question("Enter the branch name to clone:", default=cli.args.branch) branch_name = question('Enter the branch name to clone:', default=cli.args.branch)
if not yesno(delete_confirm, default=False): if not yesno(delete_confirm, default=False):
exit(1) exit(1)
@@ -107,9 +106,9 @@ def setup(cli):
path_str = str(cli.args.home) path_str = str(cli.args.home)
if cli.args.home.name != 'qmk_firmware': if cli.args.home.name != 'qmk_firmware':
cli.log.warning('Warning: %s does not end in "qmk_firmware". Did you mean to use "--home %s/qmk_firmware"?' % (path_str, path_str)) cli.log.warning(f'Warning: {path_str} does not end in "qmk_firmware". Did you mean to use "--home {path_str}/qmk_firmware"?')
cli.log.error("Path '%s' exists but is not a qmk_firmware clone!", path_str) cli.log.error(f'Path "{path_str}" exists but is not a qmk_firmware clone!')
exit(1) exit(1)
else: else:
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
# Increment the verison number and release a new version of qmk_cli.
#
# Required packages: pip3 install bumpversion twine
echo "Use the github action instead!"
exit 1
-1
View File
@@ -1,4 +1,3 @@
build build
bumpversion bumpversion
requests
twine twine
+2 -1
View File
@@ -9,7 +9,7 @@ ignore = E501,E226
[metadata] [metadata]
name = qmk name = qmk
version = 1.1.8 version = 1.2.0
author = skullydazed author = skullydazed
author_email = skullydazed@gmail.com author_email = skullydazed@gmail.com
description = A program to help users work with QMK Firmware. description = A program to help users work with QMK Firmware.
@@ -38,6 +38,7 @@ classifiers =
install_requires = install_requires =
hid hid
milc>=1.9.0 milc>=1.9.0
platformdirs
pyusb pyusb
# qmk_firmware packages # qmk_firmware packages
dotty-dict dotty-dict
-29
View File
@@ -1,29 +0,0 @@
#!/usr/bin/env python3
"""Trigger workflows that build QMK packages.
"""
from os import environ
from pathlib import Path
import requests
# Pull in environment vars
gh_user = environ.get('GH_USERNAME', 'qmk-bot')
gh_pat = environ.get('QMK_BOT_TOKEN', '')
gh_repo_owner = environ.get('REPO_OWNER', 'qmk')
gh_repo_name = environ.get('REPO_NAME', 'qmk_fpm')
gh_ref = environ.get('BRANCH_NAME', 'main')
gh_workflow_ids = environ.get('WORKFLOW_IDS', 'all-trigger.yml').split(',')
gh_api_url = environ.get('GITHUB_API_URL', 'https://api.github.com')
gh_workflow_args = {'ref': gh_ref}
gh_workflow_headers = {'Accept': 'application/vnd.github.v3+json'}
for gh_workflow_id in gh_workflow_ids:
gh_workflow_endpoint = f'{gh_api_url}/repos/{gh_repo_owner}/{gh_repo_name}/actions/workflows/{gh_workflow_id}/dispatches'
print(f'Triggering {gh_workflow_id} workflow at: {gh_workflow_endpoint}')
workflow_dispatch = requests.post(gh_workflow_endpoint, headers=gh_workflow_headers, json=gh_workflow_args, auth=(gh_user, gh_pat))
if workflow_dispatch.status_code != 204:
print(f'Error from GitHub API, status_code {workflow_dispatch.status_code}!')
print(workflow_dispatch.text)
exit(1)
exit(0)