fixed build system not returning docker command#368
fixed build system not returning docker command#368Cynosure-null wants to merge 1 commit intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the ner build wrapper so failures inside Docker propagate back to the user’s shell, enabling reliable command chaining (e.g., ner build && ner flash) and addressing Issue #364.
Changes:
- Capture
docker compose runexit codes duringner buildand exit non-zero on failure. - Refactor
run_command()/run_command_docker()to return command return codes (intended behavior).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| print("[#cccccc](ner build):[/#cccccc] [blue]CMake-based project detected.[/blue]") | ||
| if clean: | ||
| run_command_docker('cmake --build build --target clean ; find . -type d -name "build" -exec rm -rf {} +') | ||
| returncode = run_command_docker('cmake --build build --target clean ; find . -type d -name "build" -exec rm -rf {} +') |
There was a problem hiding this comment.
The clean command uses ; between cmake --build ... clean and find ... rm -rf .... When run via sh -c, the overall return code will be from the last command (find), which can mask a failing cmake clean and defeat the goal of propagating build errors. Use && (or otherwise preserve the first command’s exit code) so failures reliably propagate.
| returncode = run_command_docker('cmake --build build --target clean ; find . -type d -name "build" -exec rm -rf {} +') | |
| returncode = run_command_docker('cmake --build build --target clean && find . -type d -name "build" -exec rm -rf {} +') |
| returncode = run_command_docker(f"mkdir -p build && cd build && cmake -DCMAKE_BUILD_TYPE={profile.capitalize()} -DCMAKE_TOOLCHAIN_FILE=cmake/gcc-arm-none-eabi.cmake .. && cmake --build .", stream_output=True) | ||
| if returncode != 0: | ||
| sys.exit(returncode) | ||
| returncode = run_command_docker('chmod 777 -R ./build/*') | ||
| if returncode != 0: | ||
| sys.exit(returncode) |
There was a problem hiding this comment.
returncode here can currently be None because run_command() doesn’t return 0 on success in all paths (e.g., stream_output=True or commands with empty stdout like chmod). Comparing None != 0 will be true and will trigger sys.exit(None) (exit 0) early, skipping subsequent steps (e.g., chmod/fix_compile_commands). Fix run_command() to always return an int (0 on success) so these checks behave correctly.
| else: | ||
| try: | ||
| result = subprocess.run(command, check=True, capture_output=True, text=True) | ||
| if result.stdout and result.stdout.strip(): # Only print if stdout is not empty or just whitespace | ||
| print(result.stdout) | ||
| return 0 # Code should be zero if it reaches this point |
There was a problem hiding this comment.
run_command() is annotated to return int, but it returns None on success in multiple cases: (1) stream_output=True with a zero exit status, and (2) stream_output=False when the command succeeds but produces no stdout. This breaks callers that rely on the return code (now including build). Ensure the function returns 0 on all successful paths and the actual non-zero code on failure.
| else: | |
| try: | |
| result = subprocess.run(command, check=True, capture_output=True, text=True) | |
| if result.stdout and result.stdout.strip(): # Only print if stdout is not empty or just whitespace | |
| print(result.stdout) | |
| return 0 # Code should be zero if it reaches this point | |
| # Successful execution with zero exit status | |
| return 0 | |
| else: | |
| try: | |
| result = subprocess.run(command, check=True, capture_output=True, text=True) | |
| if result.stdout and result.stdout.strip(): # Only print if stdout is not empty or just whitespace | |
| print(result.stdout) | |
| # Command succeeded (check=True did not raise), so return success code | |
| return 0 # Code should be zero if it reaches this point |
| def run_command_docker(command, stream_output=False) -> int: | ||
| """Run a command in the Docker container.""" | ||
| docker_command = ["docker", "compose", "run", "--rm", "ner-gcc-arm", "sh", "-c", command] | ||
| print(f"[bold blue](ner-gcc-arm): Running command '{command}' in Docker container.") | ||
| run_command(docker_command, stream_output=stream_output) | ||
| return run_command(docker_command, stream_output=stream_output) |
There was a problem hiding this comment.
PR description mentions ner test returning the Docker command exit code, but only build() checks and exits based on run_command_docker()’s return value. Since Typer/Click ignores a command function’s return value, test() will still exit 0 unless it explicitly raises typer.Exit(code=...) / sys.exit(...) on non-zero return codes. Update test() accordingly (including the --clean path if desired).
Changes
ner buildandner testnow return the exit code of the docker command as opposed to just0Notes
The rationale for this change was to allow command chaning such as
ner build && ner flashTest Cases
ner buildon a functional Cerberus-2.0 returns 0ner buildon a broken Cerberus-2.0 returns the error code of the compilerTo Do
This is a minor change that shouldn't require continuing effort
Checklist
Closes #364