Skip to content

Commit 06debb5

Browse files
committed
feat(docs): enhance security in command execution and improve error handling in scripts
1 parent a0f8b37 commit 06debb5

File tree

3 files changed

+125
-23
lines changed

3 files changed

+125
-23
lines changed

.github/scripts/docs_build_examples.py

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,23 +70,41 @@
7070
SKETCH_UTILS = SCRIPT_DIR / "sketch_utils.sh"
7171

7272

73-
def run_cmd(cmd, check=True, capture_output=False, text=True):
74-
"""Execute a shell command with error handling."""
73+
def run_sketch_utils(subcommand, *args, check=True, capture_output=False, text=True):
74+
"""Execute sketch_utils.sh with a controlled subcommand.
75+
76+
Security:
77+
* Uses a fixed script path (SKETCH_UTILS)
78+
* Only allows known subcommands
79+
* Always calls subprocess.run with shell=False and a list of arguments
80+
"""
81+
allowed_subcommands = {"check_requirements", "install_libs", "build"}
82+
if subcommand not in allowed_subcommands:
83+
raise ValueError(f"Unsupported sketch_utils.sh subcommand: {subcommand}")
84+
85+
cmd = [str(SKETCH_UTILS), subcommand]
86+
for arg in args:
87+
if not isinstance(arg, (str, os.PathLike)):
88+
raise ValueError(f"Invalid argument type for sketch_utils.sh: {type(arg)!r}")
89+
cmd.append(str(arg))
90+
7591
try:
7692
return subprocess.run(
77-
cmd, check=check, capture_output=capture_output, text=text
93+
cmd,
94+
check=check,
95+
capture_output=capture_output,
96+
text=text,
97+
shell=False, # explicit for static analyzers
7898
)
7999
except subprocess.CalledProcessError as e:
80-
# CalledProcessError is raised only when check=True and the command exits non-zero
81100
print(f"ERROR: Command failed: {' '.join(cmd)}")
82101
print(f"Exit code: {e.returncode}")
83-
if hasattr(e, "stdout") and e.stdout:
102+
if e.stdout:
84103
print("--- stdout ---")
85104
print(e.stdout)
86-
if hasattr(e, "stderr") and e.stderr:
105+
if e.stderr:
87106
print("--- stderr ---")
88107
print(e.stderr)
89-
# Exit the whole script with the same return code to mimic shell behavior
90108
sys.exit(e.returncode)
91109
except FileNotFoundError:
92110
print(f"ERROR: Command not found: {cmd[0]}")
@@ -103,24 +121,27 @@ def check_requirements(sketch_dir, sdkconfig_path):
103121
Returns:
104122
bool: True if requirements are met, False otherwise
105123
"""
106-
cmd = [str(SKETCH_UTILS), "check_requirements", sketch_dir, str(sdkconfig_path)]
107124
try:
108-
res = run_cmd(cmd, check=False, capture_output=True)
125+
res = run_sketch_utils(
126+
"check_requirements",
127+
sketch_dir,
128+
str(sdkconfig_path),
129+
check=False,
130+
capture_output=True,
131+
)
109132
return res.returncode == 0
110133
except Exception:
111134
return False
112135

113136

114137
def install_libs(*args):
115138
"""Install Arduino libraries using sketch_utils.sh"""
116-
cmd = [str(SKETCH_UTILS), "install_libs"] + list(args)
117-
return run_cmd(cmd, check=False)
139+
return run_sketch_utils("install_libs", *args, check=False)
118140

119141

120142
def build_sketch(args_list):
121143
"""Build a sketch using sketch_utils.sh"""
122-
cmd = [str(SKETCH_UTILS), "build"] + args_list
123-
return run_cmd(cmd, check=False)
144+
return run_sketch_utils("build", *args_list, check=False)
124145

125146

126147
def parse_args(argv):
@@ -260,8 +281,8 @@ def cleanup_binaries():
260281
if not os.listdir(root):
261282
try:
262283
os.rmdir(root)
263-
except Exception:
264-
pass
284+
except Exception as e:
285+
print(f"WARNING: Failed to remove empty directory {root}: {e}")
265286
print("Cleanup completed")
266287

267288

@@ -284,7 +305,8 @@ def find_examples_with_upload_binary():
284305
data = yaml.safe_load(ci_yml.read_text())
285306
if "upload-binary" in data and data["upload-binary"]:
286307
res.append(str(ino))
287-
except Exception:
308+
except Exception as e:
309+
print(f"WARNING: Failed to parse ci.yml for {ci_yml}: {e}")
288310
continue
289311
return res
290312

docs/en/conf.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,3 @@
3131

3232
# Tracking ID for Google Analytics
3333
google_analytics_id = "G-F58JM78930"
34-
35-
# Documentation embedding settings
36-
# docs_embed_public_root = "https://docs.espressif.com/projects/arduino-esp32-wokwi-test"
37-
# docs_embed_esp32_relative_root = "../.."
38-
# docs_embed_launchpad_url = "https://espressif.github.io/esp-launchpad/"
39-
# docs_embed_github_base_url = "https://github.com/JakubAndrysek/arduino-esp32"
40-
# docs_embed_github_branch = "docs-embed"

docs/en/contributing.rst

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,93 @@ Also:
109109
const char * WIFI_FTM_SSID = "WiFi_FTM_Responder"; // SSID of AP that has FTM Enabled
110110
const char * WIFI_FTM_PASS = "ftm_responder"; // STA Password
111111
112+
113+
Examples
114+
********
115+
116+
All libraries in the Arduino ESP32 core has its own examples. You can found them in the ``libraries/<library_name>/examples/`` folder.
117+
118+
The purpose of these examples is to demonstrate how to use the library features and provide a starting point for users.
119+
120+
If you want to show the example in the documentation, you have two options:
121+
1. link just the source code file in the documentation using the ``literalinclude`` directive:
122+
123+
.. code-block:: rst
124+
125+
.. literalinclude:: ../../../libraries/<library_name>/examples/<example_name>/<example_name>.ino
126+
:language: arduino
127+
128+
2. Source code with Wokwi simulation embedded in the documentation using the ``wokwi-example`` directive:
129+
130+
.. code-block:: rst
131+
132+
.. wokwi-example:: libraries/<library_name>/examples/<example_name>/<example_name>.ino
133+
134+
To enable compiling the example in the CI system, you need to add a ``ci.yml`` file in the same folder as the sketch.
135+
The ``ci.yml`` file is used to specify some configurations for the CI system, like required configurations, supported targets, and more.
136+
You can enable compilation of the example by adding targets under the ``upload-binary`` directive in the ``ci.yml`` file.
137+
138+
Here is an example of a ``ci.yml`` file that enables compilation for ESP32 and ESP32-S3 targets.
139+
This configuration adds default diagrams for Wokwi simulations with just dev boards.
140+
141+
.. code-block:: yaml
142+
143+
upload-binary:
144+
targets:
145+
- esp32
146+
- esp32s3
147+
148+
If you want to add custom diagrams for Wokwi simulations, you can add the ``diagram.<target>.json`` file in the same folder as the sketch.
149+
The ``<target>`` is the target name (e.g., ``esp32``, ``esp32s3``, etc.). You can create the diagram using ``docs-embed`` tool installed together with documentation building tools.
150+
151+
To create the diagram, run the ``docs-embed init-diagram --platforms esp32`` command in the sketch folder.
152+
You can edit them and before you run the documentation build command, you have to convert the diagram config to the ``ci.yml`` file format by running:
153+
154+
.. code-block:: bash
155+
156+
docs-embed ci-from-diagram
157+
# OR
158+
docs-embed ci-from-diagram --override
159+
160+
There is also opposite command to generate diagram from ``ci.yml`` file:
161+
162+
.. code-block:: bash
163+
164+
docs-embed diagram-from-ci
165+
166+
167+
The documentation building tools is working only with `ci.yml` files, so `diagram.<target>.json` files are just for configuration using GUI tool.
168+
169+
Please keep in mind that the ``ci.yml`` does not store the chip and its position on the diagram, just the components and their connections (to save space).
170+
The chip is added automatically and positioned vertically in the center of the diagram (same as the default behavior of the `docs-embed init-diagram` command).
171+
172+
To run the documentation build command locally and in the CI system, you need to add those environment variables:
173+
174+
* ``DOCS_EMBED_ABOUT_WOKWI_URL``: URL to the info about Wokwi (default: ``https://docs.espressif.com/projects/arduino-esp32/en/latest/third_party/wokwi.html``)
175+
* ``DOCS_EMBED_BINARIES_DIR``: Path to the folder where the pre-compiled binaries are stored (default: ``_static/binaries``)
176+
* ``DOCS_EMBED_LAUNCHPAD_URL``: URL to the launchpad page (default: ``https://espressif.github.io/esp-launchpad/``)
177+
* ``DOCS_EMBED_WOKWI_VIEWER_URL``: URL to the Wokwi iframe viewer (default: ``https://wokwi.com/experimental/viewer``)
178+
179+
180+
CI/CD
181+
*****
182+
183+
This repository uses GitHub Actions for Continuous Integration and Continuous Deployment (CI/CD).
184+
If you forked the repository, you can enable them under the `Actions` tab in your forked repository.
185+
186+
To enable building the documentation, you need to set up additional secrets and environment variables in your forked repository.
187+
188+
Secrets
189+
^^^^^^^^^^^^
190+
* ``DOCS_SERVER``: The server where the documentation will be deployed
191+
* ``DOCS_PATH``: The path where the documentation will be deployed on the server
192+
193+
194+
Variables
195+
^^^^^^^^^
196+
197+
They are described above in the `Examples` section.
198+
112199
Testing
113200
*******
114201

0 commit comments

Comments
 (0)