buildstream-plugins 2.5.0.dev0__tar.gz → 2.8.0.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/.asf.yaml +13 -1
  2. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/NEWS +16 -0
  3. {buildstream-plugins-2.5.0.dev0/src/buildstream_plugins.egg-info → buildstream_plugins-2.8.0.dev0}/PKG-INFO +14 -3
  4. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/pyproject.toml +2 -7
  5. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/setup.py +1 -1
  6. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/__init__.py +1 -1
  7. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sources/cargo.py +36 -4
  8. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sources/docker.py +91 -40
  9. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0/src/buildstream_plugins.egg-info}/PKG-INFO +14 -3
  10. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/COMMITTERS.rst +0 -0
  11. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/LICENSE +0 -0
  12. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/MANIFEST.in +0 -0
  13. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/NOTICE +0 -0
  14. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/README.rst +0 -0
  15. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/project.conf +0 -0
  16. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/requirements/mypy-requirements.txt +0 -0
  17. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/requirements/plugin-requirements.txt +0 -0
  18. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/requirements/test-requirements.txt +0 -0
  19. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/setup.cfg +0 -0
  20. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/__init__.py +0 -0
  21. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/autotools.py +0 -0
  22. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/autotools.yaml +0 -0
  23. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/cmake.py +0 -0
  24. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/cmake.yaml +0 -0
  25. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/make.py +0 -0
  26. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/make.yaml +0 -0
  27. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/meson.py +0 -0
  28. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/meson.yaml +0 -0
  29. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/pip.py +0 -0
  30. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/pip.yaml +0 -0
  31. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/setuptools.py +0 -0
  32. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/elements/setuptools.yaml +0 -0
  33. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sourcemirrors/__init__.py +0 -0
  34. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sourcemirrors/gitlab_lfs_mirror.py +0 -0
  35. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sourcemirrors/simple_mirror.py +0 -0
  36. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sources/__init__.py +0 -0
  37. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sources/_utils.py +0 -0
  38. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sources/bzr.py +0 -0
  39. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sources/git.py +0 -0
  40. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sources/patch.py +0 -0
  41. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins/sources/pip.py +0 -0
  42. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins.egg-info/SOURCES.txt +0 -0
  43. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins.egg-info/dependency_links.txt +0 -0
  44. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins.egg-info/entry_points.txt +0 -0
  45. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins.egg-info/not-zip-safe +0 -0
  46. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins.egg-info/requires.txt +0 -0
  47. {buildstream-plugins-2.5.0.dev0 → buildstream_plugins-2.8.0.dev0}/src/buildstream_plugins.egg-info/top_level.txt +0 -0
@@ -1,4 +1,3 @@
1
-
2
1
  #
3
2
  # Avoid diluting the dev mailing list with meta stuff,
4
3
  # redirect it all to the commit list.
@@ -40,3 +39,16 @@ github:
40
39
  # Enable pages publishing
41
40
  ghp_branch: gh-pages
42
41
  ghp_path: /docs
42
+ rulesets:
43
+ - name: "Default Branch Protection"
44
+ type: branch
45
+ branches:
46
+ includes:
47
+ - "~DEFAULT_BRANCH"
48
+ - "release/*"
49
+ - "rel/*"
50
+ excludes: []
51
+ bypass_teams:
52
+ - root
53
+ restrict_deletion: true
54
+ restrict_force_push: true
@@ -1,3 +1,19 @@
1
+ =========================
2
+ buildstream-plugins 2.8.0
3
+ =========================
4
+
5
+ o docker: support OCI images (#105)
6
+
7
+ =========================
8
+ buildstream-plugins 2.7.0
9
+ =========================
10
+
11
+ o cargo: Support source provenance (#98)
12
+
13
+ o Fix multiple issues in the Docker source plugin (#96)
14
+
15
+ o Support Python 3.14 and drop support for Python 3.9 (#94, #97)
16
+
1
17
  =========================
2
18
  buildstream-plugins 2.5.0
3
19
  =========================
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: buildstream-plugins
3
- Version: 2.5.0.dev0
3
+ Version: 2.8.0.dev0
4
4
  Summary: A collection of plugins for BuildStream.
5
5
  Home-page: https://buildstream.build
6
6
  Author: The Apache Software Foundation
@@ -15,17 +15,28 @@ Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: Apache Software License
16
16
  Classifier: Operating System :: POSIX
17
17
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.9
19
18
  Classifier: Programming Language :: Python :: 3.10
20
19
  Classifier: Programming Language :: Python :: 3.11
21
20
  Classifier: Programming Language :: Python :: 3.12
22
21
  Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
23
  Classifier: Topic :: Software Development :: Build Tools
24
24
  Description-Content-Type: text/x-rst; charset=UTF-8
25
25
  License-File: LICENSE
26
26
  License-File: NOTICE
27
27
  Provides-Extra: cargo
28
28
  Requires-Dist: tomli; python_version < "3.11" and extra == "cargo"
29
+ Dynamic: author
30
+ Dynamic: author-email
31
+ Dynamic: classifier
32
+ Dynamic: description
33
+ Dynamic: description-content-type
34
+ Dynamic: home-page
35
+ Dynamic: license
36
+ Dynamic: license-file
37
+ Dynamic: project-url
38
+ Dynamic: provides-extra
39
+ Dynamic: summary
29
40
 
30
41
  BuildStream Plugins
31
42
  ===================
@@ -12,13 +12,8 @@
12
12
  # limitations under the License.
13
13
  #
14
14
  [build-system]
15
- requires = [
16
- # We need at least version 36.6.0 that introduced "build_meta"
17
- "setuptools>=36.6.0",
18
- # In order to build wheels, and as required by PEP 517
19
- "wheel",
20
- "Cython"
21
- ]
15
+ # We need at least version 36.6.0 that introduced "build_meta"
16
+ requires = ["setuptools>=36.6.0"]
22
17
  build-backend = "setuptools.build_meta"
23
18
 
24
19
  [tool.black]
@@ -53,11 +53,11 @@ setup(
53
53
  "License :: OSI Approved :: Apache Software License",
54
54
  "Operating System :: POSIX",
55
55
  "Programming Language :: Python :: 3",
56
- "Programming Language :: Python :: 3.9",
57
56
  "Programming Language :: Python :: 3.10",
58
57
  "Programming Language :: Python :: 3.11",
59
58
  "Programming Language :: Python :: 3.12",
60
59
  "Programming Language :: Python :: 3.13",
60
+ "Programming Language :: Python :: 3.14",
61
61
  "Topic :: Software Development :: Build Tools",
62
62
  ],
63
63
  description="A collection of plugins for BuildStream.",
@@ -15,4 +15,4 @@
15
15
  #
16
16
  # Remember to adjust this version number before tagging releases
17
17
  #
18
- __version__ = "2.5.0.dev0"
18
+ __version__ = "2.8.0.dev0"
@@ -74,6 +74,10 @@ Further, the cargo source reports the `SourceInfoMedium.REMOTE_FILE
74
74
  *version_type*, for which it reports the checksum of the archive as the *version*.
75
75
 
76
76
  The versions extracted from the Cargo.lock at tracking time are used to report the *guess_version*.
77
+
78
+ Additional source info can be specified using the `provenance` field for each referenced crate as per
79
+ `built-in source provenance documentation
80
+ <https://docs.buildstream.build/master/buildstream.source.html#built-in-functionality>`_.
77
81
  """
78
82
 
79
83
  import json
@@ -99,7 +103,7 @@ from ._utils import download_file
99
103
  #
100
104
  try:
101
105
  from buildstream import SourceInfoMedium, SourceVersionType
102
- except:
106
+ except ImportError:
103
107
  pass
104
108
 
105
109
 
@@ -125,13 +129,14 @@ _default_vendor_config_template = (
125
129
  # sha (str|None): The sha256 checksum of the downloaded crate
126
130
  #
127
131
  class Crate(SourceFetcher):
128
- def __init__(self, cargo, name, version, sha=None):
132
+ def __init__(self, cargo, name, version, sha=None, provenance=None):
129
133
  super().__init__()
130
134
 
131
135
  self.cargo = cargo
132
136
  self.name = name
133
137
  self.version = str(version)
134
138
  self.sha = sha
139
+ self.provenance = provenance
135
140
  self.mark_download_url(cargo.url)
136
141
 
137
142
  ########################################################
@@ -159,7 +164,12 @@ class Crate(SourceFetcher):
159
164
  def get_source_info(self):
160
165
  url, _ = self._get_url()
161
166
  return self.cargo.create_source_info(
162
- url, SourceInfoMedium.REMOTE_FILE, SourceVersionType.SHA256, self.sha, version_guess=self.version
167
+ url,
168
+ SourceInfoMedium.REMOTE_FILE,
169
+ SourceVersionType.SHA256,
170
+ self.sha,
171
+ version_guess=self.version,
172
+ provenance_node=self.provenance,
163
173
  )
164
174
 
165
175
  ########################################################
@@ -368,16 +378,29 @@ class CargoSource(Source):
368
378
  # We need the Cargo.lock file to construct our ref at track time
369
379
  BST_REQUIRES_PREVIOUS_SOURCES_TRACK = True
370
380
 
381
+ BST_CUSTOM_SOURCE_PROVENANCE = True
382
+
371
383
  ########################################################
372
384
  # Plugin/Source API method implementations #
373
385
  ########################################################
374
386
  def configure(self, node):
375
-
376
387
  # The url before any aliasing
377
388
  #
378
389
  self.original_url = node.get_str("url", "https://static.crates.io/crates")
379
390
  self.cargo_lock = node.get_str("cargo-lock", "Cargo.lock")
380
391
  self.vendor_dir = node.get_str("vendor-dir", "crates")
392
+ self.provenance = {}
393
+
394
+ # Because source provenance info lives in the ref it would be
395
+ # removed upon source track, create a backup here so it is
396
+ # available to be added back in when setting the ref
397
+ ref = node.get_sequence("ref", None)
398
+ if ref is not None:
399
+ for crate in ref:
400
+ if "provenance" in crate:
401
+ crate_name = crate.get_str("name")
402
+ provenance = crate.get_mapping("provenance")
403
+ self.provenance[crate_name] = provenance.strip_node_info()
381
404
 
382
405
  # If the specified URL is just an alias, require the alias to resolve
383
406
  # to a URL with a trailing slash. Otherwise, append a trailing slash if
@@ -413,6 +436,14 @@ class CargoSource(Source):
413
436
  return self.ref
414
437
 
415
438
  def set_ref(self, ref, node):
439
+ def add_provenance(crate):
440
+ if crate["name"] in self.provenance:
441
+ crate["provenance"] = self.provenance[crate["name"]]
442
+ return crate
443
+
444
+ # add provenance back in
445
+ ref = list(map(add_provenance, ref))
446
+
416
447
  node["ref"] = ref
417
448
  self._recompute_crates(node.get_sequence("ref"))
418
449
 
@@ -513,6 +544,7 @@ class CargoSource(Source):
513
544
  crate.get_str("name"),
514
545
  crate.get_str("version"),
515
546
  sha=crate.get_str("sha", None),
547
+ provenance=crate.get_mapping("provenance", None),
516
548
  )
517
549
  for crate in refs
518
550
  ]
@@ -21,8 +21,8 @@
21
21
  #
22
22
 
23
23
  """
24
- docker - stage files from Docker images
25
- =======================================
24
+ docker - stage files from Docker/OCI images
25
+ ===========================================
26
26
 
27
27
  **Usage:**
28
28
 
@@ -107,6 +107,8 @@ import shutil
107
107
  import tarfile
108
108
  import urllib.parse
109
109
 
110
+ from collections import OrderedDict
111
+
110
112
  import requests
111
113
 
112
114
  from buildstream import Source, SourceError
@@ -115,6 +117,7 @@ from buildstream.utils import (
115
117
  sha256sum,
116
118
  link_files,
117
119
  move_atomic,
120
+ get_umask,
118
121
  )
119
122
 
120
123
  #
@@ -279,6 +282,8 @@ class DockerRegistryV2Client:
279
282
  accept_types = [
280
283
  "application/vnd.docker.distribution.manifest.v2+json",
281
284
  "application/vnd.docker.distribution.manifest.list.v2+json",
285
+ "application/vnd.oci.image.index.v1+json",
286
+ "application/vnd.oci.image.manifest.v1+json",
282
287
  ]
283
288
 
284
289
  manifest_url = urljoin(image_path, "manifests", urllib.parse.quote(reference))
@@ -316,7 +321,10 @@ class DockerRegistryV2Client:
316
321
  manifest=response.text,
317
322
  )
318
323
 
319
- if manifest["mediaType"] == "application/vnd.docker.distribution.manifest.list.v2+json":
324
+ if manifest["mediaType"] in (
325
+ "application/vnd.docker.distribution.manifest.list.v2+json",
326
+ "application/vnd.oci.image.index.v1+json",
327
+ ):
320
328
  # This is a "fat manifest", we need to narrow down to a specific
321
329
  # architecture.
322
330
  for sub in manifest["manifests"]:
@@ -332,7 +340,10 @@ class DockerRegistryV2Client:
332
340
  "No images found for architecture {}, OS {}".format(architecture, os_),
333
341
  manifest=response.text,
334
342
  )
335
- if manifest["mediaType"] == "application/vnd.docker.distribution.manifest.v2+json":
343
+ if manifest["mediaType"] in (
344
+ "application/vnd.docker.distribution.manifest.v2+json",
345
+ "application/vnd.oci.image.manifest.v1+json",
346
+ ):
336
347
  return response.text, our_digest
337
348
  else:
338
349
  raise DockerManifestError(
@@ -363,23 +374,26 @@ class DockerRegistryV2Client:
363
374
 
364
375
  class ReadableTarInfo(tarfile.TarInfo):
365
376
  """
366
- The goal is to override`TarFile`'s `extractall` semantics by ensuring that on extraction, the
367
- files are readable by the owner of the file. This is done by over-riding the accessor for the
368
- mode` attribute in `TarInfo`, class that encapsulates the internal meta-data of the tarball,
377
+ The goal is to override `TarFile`'s `extractall` semantics by ensuring that on extraction, the
378
+ files are readable by the owner of the file. This is done by overriding the accessor for the
379
+ `mode` attribute in `TarInfo`, the class that encapsulates the internal meta-data of the tarball,
369
380
  so that the owner-read bit is always set.
370
381
  """
371
382
 
372
- # The mode attribute is not declared as a property and so
373
- # this trips up the static type checker, mark this as "ignore"
374
- #
383
+ # https://github.com/python/mypy/issues/4125
375
384
  @property # type: ignore
376
385
  def mode(self):
377
- # ensure file is readable by owner
378
- return self.__permission | 0o400
386
+ # Respect umask instead of the file mode stored in the archive.
387
+ # The only bit used from the embedded mode is the executable bit for files.
388
+ umask = get_umask()
389
+ if self.isdir() or bool(self.__permission & 0o100):
390
+ return 0o777 & ~umask
391
+ else:
392
+ return 0o666 & ~umask
379
393
 
380
394
  @mode.setter
381
395
  def mode(self, permission):
382
- self.__permission = permission
396
+ self.__permission = permission # pylint: disable=attribute-defined-outside-init
383
397
 
384
398
 
385
399
  class DockerSource(Source):
@@ -570,7 +584,10 @@ class DockerSource(Source):
570
584
  raise SourceError(e) from e
571
585
 
572
586
  for layer in manifest["layers"]:
573
- if layer["mediaType"] != "application/vnd.docker.image.rootfs.diff.tar.gzip":
587
+ if layer["mediaType"] not in (
588
+ "application/vnd.docker.image.rootfs.diff.tar.gzip",
589
+ "application/vnd.oci.image.layer.v1.tar+gzip",
590
+ ):
574
591
  raise SourceError("Unsupported layer type: {}".format(layer["mediaType"]))
575
592
 
576
593
  layer_digest = layer["digest"]
@@ -605,31 +622,54 @@ class DockerSource(Source):
605
622
  raise SourceError("Unable to load manifest: {}".format(e)) from e
606
623
 
607
624
  try:
625
+ layer_members = []
626
+
627
+ # Create a list of members to extract from each layer
608
628
  for layer in manifest["layers"]:
609
629
  layer_digest = layer["digest"]
610
630
  blob_path = os.path.join(mirror_dir, layer_digest + ".tar.gz")
611
631
 
612
632
  self._verify_blob(blob_path, expected_digest=layer_digest)
613
- (
614
- extract_fileset,
615
- white_out_fileset,
616
- ) = self._get_extract_and_remove_files(blob_path)
617
-
618
- # remove files associated with whiteouts
619
- for white_out_file in white_out_fileset:
620
- white_out_file = os.path.join(directory, white_out_file)
621
- os.remove(white_out_file)
622
633
 
623
- # extract files for the current layer
634
+ members_dict, whiteout_paths, opaque_whiteout_dirs = self._get_members_and_whiteout_paths(blob_path)
635
+
636
+ # Process opaque whiteouts: remove members under the corresponding directories from previous layers
637
+ for _, prev_members_dict in layer_members:
638
+ members_to_remove = []
639
+ for member_path in prev_members_dict:
640
+ # Check if this member should be removed by any opaque whiteout
641
+ for opaque_whiteout_dir in opaque_whiteout_dirs:
642
+ if member_path.startswith(opaque_whiteout_dir):
643
+ members_to_remove.append(member_path)
644
+ break
645
+ for member_path in members_to_remove:
646
+ del prev_members_dict[member_path]
647
+
648
+ # Process whiteouts: remove corresponding members from previous layers
649
+ for _, prev_members_dict in layer_members:
650
+ for whiteout_path in whiteout_paths:
651
+ prev_members_dict.pop(whiteout_path, None)
652
+
653
+ layer_members.append((blob_path, members_dict))
654
+
655
+ # Extract files for each layer
656
+ for blob_path, members_dict in layer_members:
657
+ if not members_dict:
658
+ # No files to extract from this layer
659
+ continue
660
+
661
+ # Extract files for the current layer
624
662
  with tarfile.open(blob_path, tarinfo=ReadableTarInfo) as tar:
625
663
  with self.tempdir() as td:
664
+ members = list(members_dict.values())
665
+
626
666
  if hasattr(tarfile, "tar_filter"):
627
667
  # Python 3.12+ (and older versions with backports)
628
668
  tar.extraction_filter = tarfile.tar_filter
629
669
  else:
630
- for member in extract_fileset:
670
+ for member in members:
631
671
  self._assert_tarinfo_safe(member, td)
632
- tar.extractall(path=td, members=extract_fileset)
672
+ tar.extractall(path=td, members=members)
633
673
  link_files(td, directory)
634
674
 
635
675
  except (OSError, SourceError, tarfile.TarError) as e:
@@ -658,14 +698,14 @@ class DockerSource(Source):
658
698
  ]
659
699
 
660
700
  @staticmethod
661
- def _get_extract_and_remove_files(layer_tar_path):
701
+ def _get_members_and_whiteout_paths(layer_tar_path):
662
702
  """Return the set of files to remove and extract for a given layer
663
703
 
664
- :param layer_tar_path: The path where a layer has been extracted
665
- :return: Tuple of filesets
666
- - extract_fileset: files to extract into staging directory
667
- - delete_fileset: files to remove from staging directory as the current layer
668
- contains a whiteout corresponding to a staged file in the previous layers
704
+ :param layer_tar_path: The path to the layer tar.gz file
705
+ :return: Tuple of (members_dict, whiteout_paths, opaque_whiteout_dirs)
706
+ - members_dict: OrderedDict of TarInfo members to extract into staging directory
707
+ - whiteout_paths: list of file paths that should be removed from previous layers
708
+ - opaque_whiteout_dirs: list of directory paths whose contents should be removed from previous layers
669
709
 
670
710
  """
671
711
 
@@ -677,7 +717,7 @@ class DockerSource(Source):
677
717
  """
678
718
  # whiteout files have the syntax of `*/.wh.*`
679
719
  file_name = os.path.basename(white_out_file)
680
- path = os.path.join(os.path.dirname(white_out_file), file_name.split(".wh.")[1])
720
+ path = os.path.join(os.path.dirname(white_out_file), file_name.split(".wh.", maxsplit=1)[1])
681
721
  return path
682
722
 
683
723
  def is_regular_file(info):
@@ -688,16 +728,27 @@ class DockerSource(Source):
688
728
  """
689
729
  return not (info.name.startswith("dev/") or info.isdev())
690
730
 
691
- with tarfile.open(layer_tar_path) as tar:
692
- extract_fileset = []
693
- delete_fileset = []
731
+ with tarfile.open(layer_tar_path, tarinfo=ReadableTarInfo) as tar:
732
+ members_dict = OrderedDict()
733
+ whiteout_paths = []
734
+ opaque_whiteout_dirs = []
735
+
694
736
  for member in tar.getmembers():
695
- if os.path.basename(member.name).startswith(".wh."):
696
- delete_fileset.append(strip_wh(member.name))
737
+ member_basename = os.path.basename(member.name)
738
+ if member_basename == ".wh..wh..opq":
739
+ # Opaque whiteout file
740
+ opaque_whiteout_dir = os.path.dirname(member.name)
741
+ if opaque_whiteout_dir != "":
742
+ # Add "/" to facilitate path prefix matching
743
+ opaque_whiteout_dir += "/"
744
+ opaque_whiteout_dirs.append(opaque_whiteout_dir)
745
+ elif member_basename.startswith(".wh."):
746
+ # Whiteout file
747
+ whiteout_paths.append(strip_wh(member.name))
697
748
  elif is_regular_file(member):
698
- extract_fileset.append(member)
749
+ members_dict[member.name] = member
699
750
 
700
- return extract_fileset, delete_fileset
751
+ return members_dict, whiteout_paths, opaque_whiteout_dirs
701
752
 
702
753
  # Assert that a tarfile is safe to extract; specifically, make
703
754
  # sure that we don't do anything outside of the target
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: buildstream-plugins
3
- Version: 2.5.0.dev0
3
+ Version: 2.8.0.dev0
4
4
  Summary: A collection of plugins for BuildStream.
5
5
  Home-page: https://buildstream.build
6
6
  Author: The Apache Software Foundation
@@ -15,17 +15,28 @@ Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: Apache Software License
16
16
  Classifier: Operating System :: POSIX
17
17
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.9
19
18
  Classifier: Programming Language :: Python :: 3.10
20
19
  Classifier: Programming Language :: Python :: 3.11
21
20
  Classifier: Programming Language :: Python :: 3.12
22
21
  Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
23
  Classifier: Topic :: Software Development :: Build Tools
24
24
  Description-Content-Type: text/x-rst; charset=UTF-8
25
25
  License-File: LICENSE
26
26
  License-File: NOTICE
27
27
  Provides-Extra: cargo
28
28
  Requires-Dist: tomli; python_version < "3.11" and extra == "cargo"
29
+ Dynamic: author
30
+ Dynamic: author-email
31
+ Dynamic: classifier
32
+ Dynamic: description
33
+ Dynamic: description-content-type
34
+ Dynamic: home-page
35
+ Dynamic: license
36
+ Dynamic: license-file
37
+ Dynamic: project-url
38
+ Dynamic: provides-extra
39
+ Dynamic: summary
29
40
 
30
41
  BuildStream Plugins
31
42
  ===================