Skip to content

Commit ea30f79

Browse files
committed
add base_name to package metadata
1 parent ffac618 commit ea30f79

File tree

1 file changed

+52
-44
lines changed

1 file changed

+52
-44
lines changed

src/penguin/__main__.py

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,9 @@ def _add_file(file_path):
351351
# Write the version file as structured YAML
352352
version_file_path = os.path.join(temp_dir, ".penguin_packaged_version")
353353
package_metadata = {
354-
"format_version": 1,
355-
"penguin_version": VERSION
354+
"format_version": 2,
355+
"penguin_version": VERSION,
356+
"base_name": base_name
356357
}
357358
with open(version_file_path, "w") as f:
358359
yaml.dump(package_metadata, f, default_flow_style=False, sort_keys=False)
@@ -914,41 +915,10 @@ def unpackage(ctx, archive, output, force):
914915
else:
915916
output = os.path.abspath(output)
916917

917-
# Check if extraction would create a directory that already exists
918-
# First peek at what's in the archive to see if it has a top-level directory
919-
try:
920-
result = subprocess.run(
921-
["tar", "-tzf", archive_path],
922-
capture_output=True,
923-
text=True,
924-
check=True
925-
)
926-
first_entry = result.stdout.split('\n')[0]
927-
# If first entry is a directory (ends with /), that's our top-level
928-
if first_entry.endswith('/'):
929-
top_level_dir = first_entry.rstrip('/')
930-
target_dir = os.path.join(output, top_level_dir)
931-
else:
932-
# No top-level directory in archive, will extract directly
933-
target_dir = output
934-
except subprocess.CalledProcessError as e:
935-
logger.error(f"Failed to read archive: {e}")
936-
exit(1)
937-
938-
if os.path.exists(target_dir):
939-
if os.listdir(target_dir): # Directory exists and is not empty
940-
if force:
941-
logger.info(f"Deleting existing directory: {target_dir}")
942-
shutil.rmtree(target_dir, ignore_errors=True)
943-
else:
944-
raise ValueError(
945-
f"Output directory exists and is not empty: {target_dir}. Use --force to delete."
946-
)
947-
948918
# Ensure output directory exists
949919
os.makedirs(output, exist_ok=True)
950920

951-
# Verify the archive contains the version file
921+
# Verify the archive contains the version file and extract metadata
952922
try:
953923
result = subprocess.run(
954924
["tar", "-tzf", archive_path, ".penguin_packaged_version"],
@@ -958,18 +928,56 @@ def unpackage(ctx, archive, output, force):
958928
)
959929
if result.returncode != 0:
960930
raise ValueError(
961-
f"Archive is not a valid penguin package: missing .penguin_packaged_version file. "
962-
f"This archive was not created with 'penguin package'."
931+
"Archive is not a valid penguin package: missing .penguin_packaged_version file. "
932+
"This archive was not created with 'penguin package'."
963933
)
964934
except subprocess.CalledProcessError as e:
965935
logger.error(f"Failed to verify archive: {e}")
966936
exit(1)
967937

968-
logger.info(f"Extracting {archive} to {output}...")
938+
# Extract metadata to determine the base_name
939+
with tempfile.TemporaryDirectory() as temp_extract_dir:
940+
try:
941+
subprocess.run(
942+
["tar", "-I", "pigz", "-xf", archive_path, "-C", temp_extract_dir, ".penguin_packaged_version"],
943+
check=True
944+
)
945+
metadata_path = os.path.join(temp_extract_dir, ".penguin_packaged_version")
946+
with open(metadata_path, "r") as f:
947+
metadata = yaml.safe_load(f) or {}
948+
949+
base_name = metadata.get("base_name")
950+
if not base_name:
951+
# Fall back to archive filename without .tar.gz
952+
base_name = os.path.basename(archive_path)
953+
if base_name.endswith(".tar.gz"):
954+
base_name = base_name[:-7]
955+
logger.warning(f"No base_name in metadata, using archive filename: {base_name}")
956+
except (subprocess.CalledProcessError, yaml.YAMLError) as e:
957+
logger.error(f"Failed to extract metadata: {e}")
958+
exit(1)
959+
960+
# Set target directory based on metadata
961+
target_dir = os.path.join(output, base_name)
962+
963+
# Check if target directory exists
964+
if os.path.exists(target_dir):
965+
if force:
966+
logger.info(f"Deleting existing directory: {target_dir}")
967+
shutil.rmtree(target_dir, ignore_errors=True)
968+
else:
969+
raise ValueError(
970+
f"Output directory already exists: {target_dir}. Use --force to delete."
971+
)
972+
973+
logger.info(f"Extracting {archive} to {target_dir}...")
974+
975+
# Create the target directory
976+
os.makedirs(target_dir, exist_ok=True)
969977

970978
try:
971979
subprocess.run(
972-
["tar", "-I", "pigz", "-xf", archive_path, "-C", output],
980+
["tar", "-I", "pigz", "-xf", archive_path, "-C", target_dir],
973981
check=True
974982
)
975983
except subprocess.CalledProcessError as e:
@@ -979,12 +987,11 @@ def unpackage(ctx, archive, output, force):
979987
logger.info(f"Successfully extracted to {target_dir}")
980988

981989
# Verify it's a valid penguin project
982-
if os.path.isdir(target_dir):
983-
config_path = os.path.join(target_dir, "config.yaml")
984-
if not os.path.exists(config_path):
985-
logger.warning(f"Extracted directory does not contain config.yaml")
986-
else:
987-
logger.info(f"Project ready at {target_dir}")
990+
config_path = os.path.join(target_dir, "config.yaml")
991+
if not os.path.exists(config_path):
992+
logger.warning("Extracted directory does not contain config.yaml")
993+
else:
994+
logger.info(f"Project ready at {target_dir}")
988995

989996

990997
@cli.command(hidden=True)
@@ -996,6 +1003,7 @@ def export(ctx, project_dir, out):
9961003
"""Alias for package"""
9971004
ctx.invoke(package, project_dir=project_dir, out=out)
9981005

1006+
9991007
@cli.command(name="import", hidden=True)
10001008
@click.argument("archive", type=click.Path(exists=True))
10011009
@click.option("-o", "--output", type=str, default="./projects")

0 commit comments

Comments
 (0)