From 27a8a0d4bb99fad84af43572dec05573f820e5eb Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Wed, 8 Oct 2025 16:51:06 +0800 Subject: [PATCH 1/2] Add headless mode and enhance GitHub Actions This introduces headless mode for menuconfig to enable non-interactive configuration in CI/CD pipelines. This allows menuconfig to load configs without requiring a terminal or curses interface. --- .github/workflows/test.yml | 48 +++++++++++++++++++++++++++++++++----- menuconfig.py | 15 ++++++++++-- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 326a705..501ba8f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,8 +18,9 @@ jobs: strategy: fail-fast: false matrix: - # NOTE: Testing of the Windows targets are currently disabled because - # the test script is simply not ready for it. + # NOTE: Full testsuite on Windows is disabled because the test script + # requires Linux kernel source tree. However, headless mode tests + # are now enabled for Windows. target: # Python 3.12 - python: '3.12' @@ -28,9 +29,10 @@ jobs: - python: '3.12' os: macOS builder: macos-15 - # - python: '3.12' - # os: Windows - # builder: windows-2022 + - python: '3.12' + os: Windows + builder: windows-2022 + headless-only: true steps: - name: Set up environment @@ -58,6 +60,8 @@ jobs: pip install --user setuptools wheel - name: Check out Linux source code + # Skip for Windows (headless-only mode) + if: ${{ matrix.target.headless-only != true }} uses: actions/checkout@v5 # On Windows, checkout of 'aux.c' is expected to fail because ... Windows. continue-on-error: true @@ -68,12 +72,44 @@ jobs: - name: Check out Kconfiglib source code uses: actions/checkout@v5 with: - path: Kconfiglib + # Windows (headless-only): checkout to root directory + # Linux/macOS (full test): checkout to Kconfiglib subdirectory + path: ${{ matrix.target.headless-only && '.' || 'Kconfiglib' }} - name: Apply Linux Kconfig Makefile patch + # Skip for Windows (headless-only mode) + if: ${{ matrix.target.headless-only != true }} run: | git apply Kconfiglib/makefile.patch - name: Run testsuite + # Skip for Windows (headless-only mode) + if: ${{ matrix.target.headless-only != true }} run: | Kconfiglib/tests/reltest python + + - name: Test headless mode + # Use root dir for Windows, Kconfiglib subdir for Linux/macOS + working-directory: ${{ matrix.target.headless-only && '.' || 'Kconfiglib' }} + run: | + python -c " + from kconfiglib import Kconfig + import menuconfig + + print('Testing headless mode...') + kconf = Kconfig('examples/Kmenuconfig') + menuconfig.menuconfig(kconf, headless=True) + print('✅ Headless mode test passed') + " + + - name: Install windows-curses (Windows only) + if: matrix.target.os == 'Windows' + run: | + pip install windows-curses + + - name: Test menuconfig import (Windows Python 3.12) + if: matrix.target.os == 'Windows' + # Use root dir for Windows (headless-only mode) + working-directory: ${{ matrix.target.headless-only && '.' || 'Kconfiglib' }} + run: | + python -c "import menuconfig; print('✅ menuconfig import successful')" diff --git a/menuconfig.py b/menuconfig.py index ff1224d..f4aed51 100755 --- a/menuconfig.py +++ b/menuconfig.py @@ -729,15 +729,22 @@ def _main(): menuconfig(standard_kconfig(__doc__)) -def menuconfig(kconf): +def menuconfig(kconf, headless=False): """ Launches the configuration interface, returning after the user exits. kconf: Kconfig instance to be configured + + headless: + If True, run in headless mode without launching the terminal interface. + This is useful for testing, CI/CD pipelines, and automated configuration + processing. In headless mode, the function only loads the configuration + and returns immediately without user interaction. """ # Import curses at runtime to avoid issues in headless environments - _ensure_curses() + if not headless: + _ensure_curses() global _kconf global _conf_filename @@ -785,6 +792,10 @@ def menuconfig(kconf): if _CHANGE_C_LC_CTYPE_TO_UTF8: _change_c_lc_ctype_to_utf8() + # In headless mode, just load the configuration and return + if headless: + return + # Get rid of the delay between pressing ESC and jumping to the parent menu, # unless the user has set ESCDELAY (see ncurses(3)). This makes the UI much # smoother to work with. From 16c88a3f9974092a0e84fc78e66bf3f1dce0fc8a Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Wed, 8 Oct 2025 18:33:58 +0800 Subject: [PATCH 2/2] Fix Windows regression: use heredoc instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes regression in headless mode test that caused Windows CI/CD to fail with IndentationError. It replaces python -c multi-line string with heredoc to eliminate YAML indentation issues. Problem: - YAML run: | preserves leading whitespace from indentation - Multi-line Python code in -c had leading spaces on each line - Python interpreter rejected code with unexpected indent - Error: "Process completed with exit code 1" on Windows Root cause: python -c " from kconfiglib import Kconfig # ← Leading spaces from YAML import menuconfig # ← Causes IndentationError ... " Solution: - Use heredoc (python << 'EOF') instead of python -c - Heredoc is the standard bash method for multi-line input - Python receives clean code from stdin without leading spaces - Maintains code readability with proper formatting python << 'EOF' from kconfiglib import Kconfig import menuconfig ... EOF --- .github/workflows/test.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 501ba8f..1131929 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -92,15 +92,14 @@ jobs: # Use root dir for Windows, Kconfiglib subdir for Linux/macOS working-directory: ${{ matrix.target.headless-only && '.' || 'Kconfiglib' }} run: | - python -c " + python << 'EOF' from kconfiglib import Kconfig import menuconfig - print('Testing headless mode...') kconf = Kconfig('examples/Kmenuconfig') menuconfig.menuconfig(kconf, headless=True) - print('✅ Headless mode test passed') - " + print('Headless mode test passed') + EOF - name: Install windows-curses (Windows only) if: matrix.target.os == 'Windows' @@ -112,4 +111,4 @@ jobs: # Use root dir for Windows (headless-only mode) working-directory: ${{ matrix.target.headless-only && '.' || 'Kconfiglib' }} run: | - python -c "import menuconfig; print('✅ menuconfig import successful')" + python -c "import menuconfig; print('menuconfig import successful')"