# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2015-2022 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Implement the zip file source handler."""
import os
import zipfile
from pathlib import Path
from typing import List, Optional
from craft_parts.dirs import ProjectDirs
from . import errors
from .base import FileSourceHandler
[docs]class ZipSource(FileSourceHandler):
"""The zip file source handler."""
# pylint: disable=too-many-arguments
def __init__(
self,
source: str,
part_src_dir: Path,
*,
cache_dir: Path,
project_dirs: ProjectDirs,
source_tag: Optional[str] = None,
source_branch: Optional[str] = None,
source_commit: Optional[str] = None,
source_depth: Optional[int] = None,
source_checksum: Optional[str] = None,
source_submodules: Optional[List[str]] = None,
ignore_patterns: Optional[List[str]] = None,
):
super().__init__(
source,
part_src_dir,
cache_dir=cache_dir,
source_tag=source_tag,
source_branch=source_branch,
source_commit=source_commit,
source_depth=source_depth,
source_checksum=source_checksum,
source_submodules=source_submodules,
project_dirs=project_dirs,
ignore_patterns=ignore_patterns,
)
if source_tag:
raise errors.InvalidSourceOption(source_type="zip", option="source-tag")
if source_commit:
raise errors.InvalidSourceOption(source_type="zip", option="source-commit")
if source_branch:
raise errors.InvalidSourceOption(source_type="zip", option="source-branch")
if source_depth:
raise errors.InvalidSourceOption(source_type="zip", option="source-depth")
# pylint: enable=too-many-arguments
[docs] def provision(
self,
dst: Path,
keep: bool = False,
src: Optional[Path] = None,
) -> None:
"""Extract zip file contents to the part source dir."""
if src:
zip_file = src
else:
zip_file = self.part_src_dir / os.path.basename(self.source)
# Workaround for: https://bugs.python.org/issue15795
with zipfile.ZipFile(zip_file, "r") as zipf:
for info in zipf.infolist():
extracted_file = zipf.extract(info.filename, path=dst)
# Extract the mode from the file. Note that external_attr is
# a four-byte value, where the high two bytes represent UNIX
# permissions and file type bits, and the low two bytes contain
# MS-DOS FAT file attributes. Keep the mode to permissions
# only-- no sticky bit, uid bit, or gid bit.
mode = info.external_attr >> 16 & 0x1FF
# If the zip file was created on a non-unix system, it's
# possible for the mode to end up being zero. That makes it
# pretty useless, so ignore it if so.
if mode:
os.chmod(extracted_file, mode)
if not keep:
os.remove(zip_file)