Skip to content

Commit 8f3a697

Browse files
author
Evgeny Torbin
committed
Merge branch 'feat/use_manifest_filters_with_gitignore' into 'main'
feat: Use manifest filters with gitignore file Closes PACMAN-1089 See merge request espressif/idf-component-manager!494
2 parents 069e6bf + 2c46479 commit 8f3a697

File tree

6 files changed

+98
-89
lines changed

6 files changed

+98
-89
lines changed

docs/en/guides/packaging_components.rst

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -171,46 +171,56 @@ In CI/CD pipelines, using environment variables to log in is more convenient. Yo
171171
Filter Component Files
172172
======================
173173

174-
As a component developer, you may want to specify which files from the component directory will be uploaded to the ESP Component Registry. There are two ways to achieve this: either by `using a .gitignore file`_ or by `using manifest filters`_.
174+
As a component developer, you may want to specify which files from the component directory will be uploaded to the ESP Component Registry. This can be achieved by using `manifest filters`_ and a `.gitignore file`_.
175175

176-
.. warning::
176+
Manifest Filters
177+
----------------
178+
179+
Your ``idf_component.yml`` manifest may have ``files`` section with ``include`` and ``exclude`` filters. For example:
180+
181+
.. code:: yaml
182+
183+
files:
184+
exclude:
185+
- "*.py" # Exclude all Python files
186+
- "**/*.list" # Exclude `.list` files in all directories
187+
- "big_dir/**/*" # Exclude `big_dir` directory and its content
188+
include:
189+
- "**/.DS_Store" # Include files excluded by default
177190
178-
You are not allowed to use both methods simultaneously.
191+
Files and directories that are excluded by default can be found `here <https://github.com/espressif/idf-component-manager/blob/main/idf_component_tools/file_tools.py#L16>`_.
179192

180-
Using a .gitignore File
181-
-----------------------
193+
.gitignore File
194+
---------------
182195

183-
First, you need to specify the ``use_gitignore`` option in the ``idf_component.yml`` manifest file.
196+
If you have a ``.gitignore`` file in your component directory, you can use it to filter files. All you need to do, is to specify the ``use_gitignore`` option in the ``idf_component.yml`` manifest file.
184197

185198
.. code:: yaml
186199
187200
files:
188201
use_gitignore: true
189202
190-
Then, patterns specified in the ``.gitignore`` file will be automatically excluded before packaging or uploading the component.
203+
Patterns specified in the ``.gitignore`` file will be automatically excluded before packaging or uploading the component.
191204

192205
.. code:: yaml
193206
194207
test_dir/ # Exclude files in all `test_dir` directories (including the directories themselves)
195208
196-
More information on how ``.gitignore`` works can be found in the `official documentation <https://git-scm.com/docs/gitignore/en>`_.
209+
More information on how ``.gitignore`` works can be found in the `official documentation <https://git-scm.com/docs/gitignore>`_.
197210

198-
Using Manifest Filters
199-
----------------------
200-
201-
In this case, your ``idf_component.yml`` manifest may have ``include`` and ``exclude`` filters. For example:
211+
You can also use both manifest filters and a ``.gitignore`` file. In this case, the patterns from the ``.gitignore`` file will be applied first. Example:
202212

203213
.. code:: yaml
204214
205215
files:
216+
use_gitignore: true
206217
exclude:
207-
- "*.py" # Exclude all Python files
208-
- "**/*.list" # Exclude `.list` files in all directories
209-
- "big_dir/**/*" # Exclude `big_dir` directory and its content
218+
- ".env" # Exclude `.env` file
210219
include:
211-
- "**/.DS_Store" # Include files excluded by default
220+
- "test_dir/**/*" # Include all files in `test_dir` directory
221+
# which were excluded by `.gitignore`
212222
213-
Files and directories that are excluded by default can be found `here <https://github.com/espressif/idf-component-manager/blob/main/idf_component_tools/file_tools.py#L16>`_.
223+
When using ``.gitignore``, files specified `here <https://github.com/espressif/idf-component-manager/blob/main/idf_component_tools/file_tools.py#L16>`_ will not be excluded by default.
214224

215225
.. warning::
216226

docs/en/reference/manifest_file.rst

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -141,32 +141,53 @@ A dictionary with the following options:
141141
- ``include``: List of patterns to include.
142142
- ``exclude``: List of patterns to exclude.
143143

144-
.. note::
145-
146-
You cannot use ``.gitignore`` and ``include``/``exclude`` rules at the same time.
147-
148144
Examples:
149145

150146
#. Use ``.gitignore`` to exclude files:
151147

152-
.. code:: yaml
148+
.. code:: yaml
153149
154-
files:
155-
use_gitignore: true
150+
files:
151+
use_gitignore: true
156152
157-
2. Use ``include`` and ``exclude`` patterns:
153+
#. Use ``include`` and ``exclude`` patterns:
158154

159-
.. code:: yaml
155+
.. code:: yaml
156+
157+
files:
158+
exclude:
159+
- "*.py" # Exclude all Python files
160+
- "**/*.list" # Exclude `.list` files in all directories
161+
- "big_dir/**/*" # Exclude `big_dir` directory and its content
162+
include:
163+
- "**/.DS_Store" # Include files excluded by default
164+
165+
#. Use both. Consider the following ``.gitignore`` file:
166+
167+
.. code:: text
160168
161-
files:
162-
exclude:
163-
- "*.py" # Exclude all Python files
164-
- "**/*.list" # Exclude `.list` files in all directories
165-
- "big_dir/**/*" # Exclude `big_dir` directory and its content
166-
include:
167-
- "**/.DS_Store" # Include files excluded by default
169+
test_dir/
168170
169-
..
171+
Then manifest ``idf_component.yml`` might look like:
172+
173+
.. code:: yaml
174+
175+
files:
176+
use_gitignore: true
177+
exclude:
178+
- ".env" # Exclude `.env` file
179+
include:
180+
- "test_dir/**/*" # Include all files in `test_dir` directory
181+
# which were excluded by `.gitignore`
182+
183+
Filters are applied in the following order:
184+
185+
#. All files are included.
186+
#. If ``use_gitignore`` is set to ``true``, files are excluded using the ``.gitignore`` file. Otherwise the default exclusion list is used.
187+
#. If ``exclude`` field is set, files are excluded using the patterns in the ``exclude`` field.
188+
#. If ``include`` field is set, files are included using the patterns in the ``include`` field, even if they were excluded in the previous steps.
189+
190+
Note that the ``include`` field could be used to override files in ``exclude`` field, ``.gitignore`` file and default exclusion list.
170191

171192
This field is optional and can be omitted if the component contains all files in the root directory with the default list of exceptions.
172193

@@ -181,18 +202,6 @@ A list of files and directories excluded by default:
181202

182203
|DEFAULT_EXCLUDE|
183204

184-
Here is an example on how to use the ``files`` field. Note that the ``include`` field could be used to override the default exclusion list.
185-
186-
.. code:: yaml
187-
188-
files:
189-
exclude:
190-
- "*.py" # Exclude all Python files
191-
- "**/*.list" # Exclude `.list` files in all directories
192-
- "big_dir/**/*" # Exclude `big_dir` directory and its content
193-
include:
194-
- "**/.DS_Store" # Include files excluded by default
195-
196205
.. _manifest-examples:
197206

198207
``examples``

idf_component_tools/file_tools.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
1+
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
22
# SPDX-License-Identifier: Apache-2.0
33
"""Set of tools and constants to work with files and directories"""
44

@@ -119,29 +119,25 @@ def exclude_all_directories():
119119
include_paths('**/*')
120120

121121
if use_gitignore:
122-
# Exclude user patterns
123-
for pattern in exclude:
124-
exclude_paths(pattern)
125-
126122
# Exclude .gitignore patterns
127123
exclude_gitignore = gitignore_ignored_files(base_path)
128124
paths.difference_update(exclude_gitignore)
129125
exclude_all_directories()
130126
else:
131-
# Exclude all defaults, including directories
127+
# Exclude defaults
132128
if exclude_default:
133129
for pattern in DEFAULT_EXCLUDE:
134130
exclude_paths(pattern)
135131

136-
# Exclude user patterns
137-
for pattern in exclude:
138-
exclude_paths(pattern)
132+
# Exclude manifest patterns
133+
for pattern in exclude:
134+
exclude_paths(pattern)
139135

140-
exclude_all_directories()
136+
exclude_all_directories()
141137

142-
# Include everything that was explicitly added
143-
for pattern in include:
144-
include_paths(pattern)
138+
# Include manifest patterns
139+
for pattern in include:
140+
include_paths(pattern)
145141

146142
return paths
147143

idf_component_tools/manifest/models.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
1+
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
22
# SPDX-License-Identifier: Apache-2.0
33
import typing as t
44
import warnings
@@ -355,20 +355,6 @@ class FilesField(BaseModel):
355355
include: UniqueStrListField = []
356356
exclude: UniqueStrListField = []
357357

358-
@model_validator(mode='before') # type: ignore
359-
@classmethod
360-
def validate_files(cls, data: t.Any) -> t.Any:
361-
if not isinstance(data, dict):
362-
return data
363-
364-
if 'use_gitignore' in data and data['use_gitignore']:
365-
if 'exclude' in data or 'include' in data:
366-
raise ValueError(
367-
'Invalid field "files": Cannot use ".gitignore" and "exclude"/"include" at the same time'
368-
)
369-
370-
return data
371-
372358

373359
class RepositoryInfoField(BaseModel):
374360
commit_sha: Annotated[str, Field(pattern=COMMIT_ID_RE)] = None # type: ignore

tests/manifest/test_validator.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
1+
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
22
# SPDX-License-Identifier: Apache-2.0
33
import logging
44
import os
@@ -172,19 +172,6 @@ def test_validate_files_invalid_list(self, valid_manifest):
172172

173173
assert errors == ['Invalid field "files:include": Input should be a valid list']
174174

175-
def test_validate_files_invalid_keys_combination(self, valid_manifest):
176-
valid_manifest['files']['use_gitignore'] = True
177-
errors = Manifest.validate_manifest(valid_manifest)
178-
179-
assert errors == [
180-
'Invalid field "files": Cannot use ".gitignore" and "exclude"/"include" at the same time'
181-
]
182-
183-
def test_validate_files_valid_keys_combination(self, valid_manifest):
184-
valid_manifest['files']['use_gitignore'] = False
185-
errors = Manifest.validate_manifest(valid_manifest)
186-
assert not errors
187-
188175
@pytest.mark.parametrize(
189176
'invalid_tag',
190177
[

tests/test_file_tools.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
1+
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
22
# SPDX-License-Identifier: Apache-2.0
33
import logging
44
import os
@@ -225,6 +225,27 @@ def test_no_default_exclude_with_gitignore(assets_path):
225225
}
226226

227227

228+
def test_include_files_excluded_by_gitignore(assets_path):
229+
create_gitignore(assets_path, ['examples', '1.txt'])
230+
examples_dir = assets_path / 'examples'
231+
examples_dir.mkdir()
232+
(examples_dir / 'test.txt').write_text('1')
233+
test_example = examples_dir / 'test_example'
234+
test_example.mkdir()
235+
(test_example / 'test.txt').write_text('1')
236+
237+
assert filtered_paths(assets_path, use_gitignore=True, include=['examples/**/*', '1.txt']) == {
238+
assets_path / 'examples' / 'test_example' / 'test.txt',
239+
assets_path / 'examples' / 'test_example',
240+
assets_path / 'examples' / 'test.txt',
241+
assets_path / 'ignore.dir' / 'file.txt',
242+
assets_path / 'ignore.me',
243+
assets_path / '1.txt',
244+
assets_path / '.gitignore',
245+
assets_path / '.gitlab-ci.yml',
246+
}
247+
248+
228249
def test_check_suspicious_component_files(release_component_path, tmp_path, caplog):
229250
sub = str(tmp_path / 'sub')
230251
shutil.copytree(release_component_path, sub)

0 commit comments

Comments
 (0)