From c824ee75fabee8126e658d939b38457542072b5b Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Tue, 11 Aug 2026 11:19:07 +0200 Subject: [PATCH 1/3] Add support to `publishing_group` to S3 artifacts handling Signed-off-by: Tobias Wolf On-behalf-of: --- src/gardenlinux/s3/s3_artifacts.py | 5 +++++ .../ali-gardener_prod-amd64.s3_metadata.yaml | 1 + .../aws-gardener_prod-amd64.s3_metadata.yaml | 1 + .../aws-gardener_prod-arm64.s3_metadata.yaml | 1 + ...aws-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml | 1 + ...aws-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml | 1 + .../aws-gardener_prod_usi-amd64.s3_metadata.yaml | 1 + .../aws-gardener_prod_usi-arm64.s3_metadata.yaml | 1 + .../azure-gardener_prod-amd64.s3_metadata.yaml | 1 + .../azure-gardener_prod-arm64.s3_metadata.yaml | 1 + ...ure-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml | 1 + ...ure-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml | 1 + .../azure-gardener_prod_usi-amd64.s3_metadata.yaml | 1 + .../azure-gardener_prod_usi-arm64.s3_metadata.yaml | 1 + .../s3_bucket_artifacts/container-amd64.s3_metadata.yaml | 1 + .../s3_bucket_artifacts/container-arm64.s3_metadata.yaml | 1 + .../gcp-gardener_prod-amd64.s3_metadata.yaml | 1 + .../gcp-gardener_prod-arm64.s3_metadata.yaml | 1 + ...gcp-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml | 1 + ...gcp-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml | 1 + .../gcp-gardener_prod_usi-amd64.s3_metadata.yaml | 1 + .../gcp-gardener_prod_usi-arm64.s3_metadata.yaml | 1 + .../gdch-gardener_prod-amd64.s3_metadata.yaml | 1 + .../gdch-gardener_prod-arm64.s3_metadata.yaml | 1 + .../kvm-gardener_prod-amd64.s3_metadata.yaml | 1 + .../kvm-gardener_prod-arm64.s3_metadata.yaml | 1 + ...kvm-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml | 1 + ...kvm-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml | 1 + .../kvm-gardener_prod_usi-amd64.s3_metadata.yaml | 1 + .../kvm-gardener_prod_usi-arm64.s3_metadata.yaml | 1 + .../s3_bucket_artifacts/metal-capi-amd64.s3_metadata.yaml | 1 + .../s3_bucket_artifacts/metal-capi-arm64.s3_metadata.yaml | 1 + .../metal-gardener_prod-amd64.s3_metadata.yaml | 1 + .../metal-gardener_prod-arm64.s3_metadata.yaml | 1 + ...tal-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml | 1 + ...tal-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml | 1 + .../metal-gardener_prod_usi-amd64.s3_metadata.yaml | 1 + .../metal-gardener_prod_usi-arm64.s3_metadata.yaml | 1 + .../metal-gardener_pxe-amd64.s3_metadata.yaml | 1 + .../metal-gardener_pxe-arm64.s3_metadata.yaml | 1 + .../s3_bucket_artifacts/metal-vhost-amd64.s3_metadata.yaml | 1 + .../s3_bucket_artifacts/metal-vhost-arm64.s3_metadata.yaml | 1 + .../s3_bucket_artifacts/metal_pxe-amd64.s3_metadata.yaml | 1 + .../s3_bucket_artifacts/metal_pxe-arm64.s3_metadata.yaml | 1 + .../openstack-gardener_prod-amd64.s3_metadata.yaml | 1 + .../openstack-gardener_prod-arm64.s3_metadata.yaml | 1 + ...ack-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml | 1 + ...ack-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml | 1 + .../openstack-gardener_prod_usi-amd64.s3_metadata.yaml | 1 + .../openstack-gardener_prod_usi-arm64.s3_metadata.yaml | 1 + .../openstackbaremetal-gardener_prod-amd64.s3_metadata.yaml | 1 + .../openstackbaremetal-gardener_prod-arm64.s3_metadata.yaml | 1 + .../vmware-gardener_prod-amd64.s3_metadata.yaml | 1 + .../vmware-gardener_prod-arm64.s3_metadata.yaml | 1 + tests/s3/constants.py | 1 + 55 files changed, 59 insertions(+) diff --git a/src/gardenlinux/s3/s3_artifacts.py b/src/gardenlinux/s3/s3_artifacts.py index 07e68bd5..ddd8c674 100644 --- a/src/gardenlinux/s3/s3_artifacts.py +++ b/src/gardenlinux/s3/s3_artifacts.py @@ -148,6 +148,7 @@ def upload_from_directory( arch = cname_object.arch feature_set_list = cname_object.feature_set_list + publishing_group = "" release_timestamp = stat(release_file).st_ctime requirements_file = artifacts_dir.joinpath(f"{base_name}.requirements") require_uefi = None @@ -161,6 +162,9 @@ def upload_from_directory( if requirements_config.has_option(UNNAMED_SECTION, "arch"): arch = requirements_config.get(UNNAMED_SECTION, "arch") + if requirements_config.has_option(UNNAMED_SECTION, "publishing_group"): + publishing_group = requirements_config.get(UNNAMED_SECTION, "publishing_group") + if requirements_config.has_option(UNNAMED_SECTION, "uefi"): require_uefi = requirements_config.getboolean(UNNAMED_SECTION, "uefi") @@ -202,6 +206,7 @@ def upload_from_directory( "build_timestamp": datetime.fromtimestamp(release_timestamp), "logs": None, "modifiers": feature_set_list, + "publishing_group": publishing_group, "require_uefi": require_uefi, "secureboot": secureboot, "tpm2": tpm2, diff --git a/test-data/release_notes/s3_bucket_artifacts/ali-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/ali-gardener_prod-amd64.s3_metadata.yaml index 8429ecad..0a9b6385 100644 --- a/test-data/release_notes/s3_bucket_artifacts/ali-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/ali-gardener_prod-amd64.s3_metadata.yaml @@ -180,6 +180,7 @@ published_image_metadata: - image_id: m-gw86dlqmpaugljiykx91 image_name: gardenlinux-ali-gardener_prod-amd64-1877.3-75df9f40 region_id: eu-central-1 +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/ali-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod-amd64.s3_metadata.yaml index 6a39f83b..490103b0 100644 --- a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod-amd64.s3_metadata.yaml @@ -159,6 +159,7 @@ published_image_metadata: - ami_id: ami-05e1cc73d997d67b7 aws_region_id: cn-northwest-1 image_name: gardenlinux-aws-gardener_prod-amd64-1877.3-75df9f40 +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/aws-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod-arm64.s3_metadata.yaml index 0d207610..a11e8917 100644 --- a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod-arm64.s3_metadata.yaml @@ -147,6 +147,7 @@ published_image_metadata: - ami_id: ami-06fc0f74b500d2d82 aws_region_id: cn-northwest-1 image_name: gardenlinux-aws-gardener_prod-arm64-1877.3-75df9f40 +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/aws-gardener_prod-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml index 3488a130..3218b76f 100644 --- a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml @@ -257,6 +257,7 @@ published_image_metadata: - ami_id: ami-005f7dab618420a91 aws_region_id: eu-central-1 image_name: gardenlinux-aws-gardener_prod_tpm2_trustedboot-amd64-1877.3-75df9f40 +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/aws-gardener_prod_tpm2_trustedboot-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml index a0205400..738153dd 100644 --- a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml @@ -269,6 +269,7 @@ published_image_metadata: - ami_id: ami-0b15b442dd5e90d50 aws_region_id: eu-central-1 image_name: gardenlinux-aws-gardener_prod_tpm2_trustedboot-arm64-1877.3-75df9f40 +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/aws-gardener_prod_tpm2_trustedboot-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_usi-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_usi-amd64.s3_metadata.yaml index 6333e28b..71490119 100644 --- a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_usi-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_usi-amd64.s3_metadata.yaml @@ -185,6 +185,7 @@ published_image_metadata: - ami_id: ami-0cab977e76e274599 aws_region_id: cn-northwest-1 image_name: gardenlinux-aws-gardener_prod_usi-amd64-1877.3-75df9f40 +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/aws-gardener_prod_usi-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_usi-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_usi-arm64.s3_metadata.yaml index 4ceff914..b2c719f0 100644 --- a/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_usi-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/aws-gardener_prod_usi-arm64.s3_metadata.yaml @@ -173,6 +173,7 @@ published_image_metadata: - ami_id: ami-0a7fe5959bb23fab8 aws_region_id: cn-northwest-1 image_name: gardenlinux-aws-gardener_prod_usi-arm64-1877.3-75df9f40 +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/aws-gardener_prod_usi-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod-amd64.s3_metadata.yaml index 2578fee6..faaf8f91 100644 --- a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod-amd64.s3_metadata.yaml @@ -97,6 +97,7 @@ published_image_metadata: community_gallery_image_id: /CommunityGalleries/gardenlinux-8e6518fb-9ae0-4f66-abfd-9a06997e2492/Images/gardenlinux-nvme-gen2/Versions/1877.3.0 hyper_v_generation: V2 published_marketplace_images: [] +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/azure-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod-arm64.s3_metadata.yaml index 0bd048a7..9336fbe2 100644 --- a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod-arm64.s3_metadata.yaml @@ -103,6 +103,7 @@ published_image_metadata: community_gallery_image_id: /CommunityGalleries/gardenlinux-8e6518fb-9ae0-4f66-abfd-9a06997e2492/Images/gardenlinux-nvme-arm64-gen2/Versions/1877.3.0 hyper_v_generation: V2 published_marketplace_images: [] +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/azure-gardener_prod-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml index 016be01c..d63a1394 100644 --- a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml @@ -219,6 +219,7 @@ published_image_metadata: community_gallery_image_id: /CommunityGalleries/gardenlinux-8e6518fb-9ae0-4f66-abfd-9a06997e2492/Images/gardenlinux-nvme-gen2-usi-secureboot/Versions/1877.3.0 hyper_v_generation: V2 published_marketplace_images: [] +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/azure-gardener_prod_tpm2_trustedboot-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml index bef6f893..856b670f 100644 --- a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml @@ -219,6 +219,7 @@ published_image_metadata: community_gallery_image_id: /CommunityGalleries/gardenlinux-8e6518fb-9ae0-4f66-abfd-9a06997e2492/Images/gardenlinux-nvme-arm64-gen2-usi-secureboot/Versions/1877.3.0 hyper_v_generation: V2 published_marketplace_images: [] +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/azure-gardener_prod_tpm2_trustedboot-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_usi-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_usi-amd64.s3_metadata.yaml index c093e6a8..2460c45a 100644 --- a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_usi-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_usi-amd64.s3_metadata.yaml @@ -129,6 +129,7 @@ published_image_metadata: community_gallery_image_id: /CommunityGalleries/gardenlinux-8e6518fb-9ae0-4f66-abfd-9a06997e2492/Images/gardenlinux-nvme-gen2-usi/Versions/1877.3.0 hyper_v_generation: V2 published_marketplace_images: [] +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/azure-gardener_prod_usi-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_usi-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_usi-arm64.s3_metadata.yaml index eb877bd0..1c7ed61e 100644 --- a/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_usi-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/azure-gardener_prod_usi-arm64.s3_metadata.yaml @@ -141,6 +141,7 @@ published_image_metadata: community_gallery_image_id: /CommunityGalleries/gardenlinux-8e6518fb-9ae0-4f66-abfd-9a06997e2492/Images/gardenlinux-nvme-arm64-gen2-usi/Versions/1877.3.0 hyper_v_generation: V2 published_marketplace_images: [] +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/azure-gardener_prod_usi-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/container-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/container-amd64.s3_metadata.yaml index a10af111..dee1d9d8 100644 --- a/test-data/release_notes/s3_bucket_artifacts/container-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/container-amd64.s3_metadata.yaml @@ -83,6 +83,7 @@ paths: suffix: .requirements platform: container published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/container-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/container-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/container-arm64.s3_metadata.yaml index 0d681e21..0bed4768 100644 --- a/test-data/release_notes/s3_bucket_artifacts/container-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/container-arm64.s3_metadata.yaml @@ -83,6 +83,7 @@ paths: suffix: .manifest platform: container published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/container-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod-amd64.s3_metadata.yaml index cd887bb8..350e4acb 100644 --- a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod-amd64.s3_metadata.yaml @@ -97,6 +97,7 @@ platform: gcp published_image_metadata: gcp_image_name: gardenlinux-gcp-ff804026cbe7b5f2d6f729e4-1877-3-75df9f40 gcp_project_name: sap-se-gcp-gardenlinux +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/gcp-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod-arm64.s3_metadata.yaml index 48d219f6..086e9415 100644 --- a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod-arm64.s3_metadata.yaml @@ -85,6 +85,7 @@ platform: gcp published_image_metadata: gcp_image_name: gardenlinux-gcp-c8504d3c3e67cf2fc7c3408c-1877-3-75df9f40 gcp_project_name: sap-se-gcp-gardenlinux +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/gcp-gardener_prod-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml index baa8d6ba..94b05866 100644 --- a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml @@ -225,6 +225,7 @@ platform: gcp published_image_metadata: gcp_image_name: gardenlinux-gcp-b4636aa3660a8d166531aab9-1877-3-75df9f40 gcp_project_name: sap-se-gcp-gardenlinux +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/gcp-gardener_prod_tpm2_trustedboot-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml index 3449d965..cfb14536 100644 --- a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml @@ -213,6 +213,7 @@ platform: gcp published_image_metadata: gcp_image_name: gardenlinux-gcp-63fd9d7dd465420fd4e499ab-1877-3-75df9f40 gcp_project_name: sap-se-gcp-gardenlinux +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/gcp-gardener_prod_tpm2_trustedboot-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_usi-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_usi-amd64.s3_metadata.yaml index fc81c313..4da6aba1 100644 --- a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_usi-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_usi-amd64.s3_metadata.yaml @@ -123,6 +123,7 @@ platform: gcp published_image_metadata: gcp_image_name: gardenlinux-gcp-51db8a4be084c3b640095f4b-1877-3-75df9f40 gcp_project_name: sap-se-gcp-gardenlinux +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/gcp-gardener_prod_usi-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_usi-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_usi-arm64.s3_metadata.yaml index 5bb6a3a5..9d6c3db0 100644 --- a/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_usi-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/gcp-gardener_prod_usi-arm64.s3_metadata.yaml @@ -123,6 +123,7 @@ platform: gcp published_image_metadata: gcp_image_name: gardenlinux-gcp-c00f1e20ffeed4d8b80a76b9-1877-3-75df9f40 gcp_project_name: sap-se-gcp-gardenlinux +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/gcp-gardener_prod_usi-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/gdch-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/gdch-gardener_prod-amd64.s3_metadata.yaml index 63b88a03..679891fb 100644 --- a/test-data/release_notes/s3_bucket_artifacts/gdch-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/gdch-gardener_prod-amd64.s3_metadata.yaml @@ -83,6 +83,7 @@ paths: suffix: .manifest platform: gdch published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/gdch-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/gdch-gardener_prod-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/gdch-gardener_prod-arm64.s3_metadata.yaml index 4db93ea9..9f67ac79 100644 --- a/test-data/release_notes/s3_bucket_artifacts/gdch-gardener_prod-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/gdch-gardener_prod-arm64.s3_metadata.yaml @@ -95,6 +95,7 @@ paths: suffix: .requirements.log platform: gdch published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/gdch-gardener_prod-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod-amd64.s3_metadata.yaml index 5e4d78c1..8ca5c871 100644 --- a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod-amd64.s3_metadata.yaml @@ -84,6 +84,7 @@ paths: suffix: .raw platform: kvm published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/kvm-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod-arm64.s3_metadata.yaml index 9385c024..371b93e0 100644 --- a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod-arm64.s3_metadata.yaml @@ -84,6 +84,7 @@ paths: suffix: .tar platform: kvm published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/kvm-gardener_prod-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml index 1f718446..d1c1a566 100644 --- a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml @@ -200,6 +200,7 @@ paths: suffix: .esp.tar platform: kvm published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/kvm-gardener_prod_tpm2_trustedboot-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml index 7364209e..6c1ceac7 100644 --- a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml @@ -212,6 +212,7 @@ paths: suffix: .secureboot.kek.crt platform: kvm published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/kvm-gardener_prod_tpm2_trustedboot-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_usi-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_usi-amd64.s3_metadata.yaml index 9e102731..467d305c 100644 --- a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_usi-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_usi-amd64.s3_metadata.yaml @@ -110,6 +110,7 @@ paths: suffix: .requirements.log platform: kvm published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/kvm-gardener_prod_usi-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_usi-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_usi-arm64.s3_metadata.yaml index 202c7c7a..c57d8873 100644 --- a/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_usi-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/kvm-gardener_prod_usi-arm64.s3_metadata.yaml @@ -110,6 +110,7 @@ paths: suffix: .release.log platform: kvm published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/kvm-gardener_prod_usi-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-capi-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-capi-amd64.s3_metadata.yaml index 4971eaff..ae3a67ea 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-capi-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-capi-amd64.s3_metadata.yaml @@ -81,6 +81,7 @@ paths: suffix: .release platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-capi-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-capi-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-capi-arm64.s3_metadata.yaml index 713fa246..3761f058 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-capi-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-capi-arm64.s3_metadata.yaml @@ -93,6 +93,7 @@ paths: suffix: .chroot.test.xml platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-capi-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod-amd64.s3_metadata.yaml index 58faf69c..822df12b 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod-amd64.s3_metadata.yaml @@ -82,6 +82,7 @@ paths: suffix: .requirements platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod-arm64.s3_metadata.yaml index 6bea5a5a..8b0c1138 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod-arm64.s3_metadata.yaml @@ -82,6 +82,7 @@ paths: suffix: .release platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-gardener_prod-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml index 91fb6c40..0fd7e742 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml @@ -198,6 +198,7 @@ paths: suffix: .requirements platform: metal published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-gardener_prod_tpm2_trustedboot-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml index cb253fb8..935a357a 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml @@ -198,6 +198,7 @@ paths: suffix: .secureboot.kek.conf platform: metal published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-gardener_prod_tpm2_trustedboot-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_usi-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_usi-amd64.s3_metadata.yaml index 479b9ae3..2e1ec694 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_usi-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_usi-amd64.s3_metadata.yaml @@ -108,6 +108,7 @@ paths: suffix: .esp.tar.log platform: metal published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-gardener_prod_usi-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_usi-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_usi-arm64.s3_metadata.yaml index c923e335..37138a35 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_usi-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_prod_usi-arm64.s3_metadata.yaml @@ -120,6 +120,7 @@ paths: suffix: .release platform: metal published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-gardener_prod_usi-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_pxe-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_pxe-amd64.s3_metadata.yaml index 8f763306..1fc534aa 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_pxe-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_pxe-amd64.s3_metadata.yaml @@ -82,6 +82,7 @@ paths: suffix: .release platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-gardener_pxe-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_pxe-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_pxe-arm64.s3_metadata.yaml index 73732a72..d92cd924 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-gardener_pxe-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-gardener_pxe-arm64.s3_metadata.yaml @@ -82,6 +82,7 @@ paths: suffix: .tar.log platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-gardener_pxe-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-vhost-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-vhost-amd64.s3_metadata.yaml index 4dae4bd9..2e3d2c42 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-vhost-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-vhost-amd64.s3_metadata.yaml @@ -79,6 +79,7 @@ paths: suffix: .requirements.log platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-vhost-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal-vhost-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal-vhost-arm64.s3_metadata.yaml index 504c399a..d05a80cf 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal-vhost-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal-vhost-arm64.s3_metadata.yaml @@ -79,6 +79,7 @@ paths: suffix: .raw.log platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal-vhost-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal_pxe-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal_pxe-amd64.s3_metadata.yaml index 307f8594..256c4a98 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal_pxe-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal_pxe-amd64.s3_metadata.yaml @@ -80,6 +80,7 @@ paths: suffix: .manifest.log platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal_pxe-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/metal_pxe-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/metal_pxe-arm64.s3_metadata.yaml index a848e1d8..4f21a4a6 100644 --- a/test-data/release_notes/s3_bucket_artifacts/metal_pxe-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/metal_pxe-arm64.s3_metadata.yaml @@ -80,6 +80,7 @@ paths: suffix: .manifest.log platform: metal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/metal_pxe-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod-amd64.s3_metadata.yaml index 73d0db62..7e31617c 100644 --- a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod-amd64.s3_metadata.yaml @@ -141,6 +141,7 @@ published_image_metadata: - image_id: d3ac5df8-ce38-4a23-b611-dfef6b7a0db9 image_name: gardenlinux-1877.3 region_name: ap-sa-2 +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/openstack-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod-arm64.s3_metadata.yaml index 266ff39a..7fe93708 100644 --- a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod-arm64.s3_metadata.yaml @@ -95,6 +95,7 @@ paths: suffix: .release platform: openstack published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/openstack-gardener_prod-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml index 9f04ca2f..e552cda1 100644 --- a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_tpm2_trustedboot-amd64.s3_metadata.yaml @@ -235,6 +235,7 @@ paths: suffix: .vmdk platform: openstack published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/openstack-gardener_prod_tpm2_trustedboot-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml index 1665b1e8..365d1d44 100644 --- a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_tpm2_trustedboot-arm64.s3_metadata.yaml @@ -235,6 +235,7 @@ paths: suffix: .manifest.log platform: openstack published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/openstack-gardener_prod_tpm2_trustedboot-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_usi-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_usi-amd64.s3_metadata.yaml index a5dcb804..6ed384dc 100644 --- a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_usi-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_usi-amd64.s3_metadata.yaml @@ -191,6 +191,7 @@ published_image_metadata: - image_id: 510d1ff1-4fc6-49ec-ad2f-a0985217dd14 image_name: gardenlinux-1877.3 region_name: ap-sa-2 +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/openstack-gardener_prod_usi-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_usi-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_usi-arm64.s3_metadata.yaml index 3ad68665..83163eea 100644 --- a/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_usi-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/openstack-gardener_prod_usi-arm64.s3_metadata.yaml @@ -133,6 +133,7 @@ paths: suffix: .manifest platform: openstack published_image_metadata: null +publishing_group: '' require_uefi: true s3_bucket: gardenlinux-github-releases s3_key: meta/singles/openstack-gardener_prod_usi-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/openstackbaremetal-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/openstackbaremetal-gardener_prod-amd64.s3_metadata.yaml index 52be1a3a..888ec635 100644 --- a/test-data/release_notes/s3_bucket_artifacts/openstackbaremetal-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/openstackbaremetal-gardener_prod-amd64.s3_metadata.yaml @@ -141,6 +141,7 @@ published_image_metadata: - image_id: 617f5ae7-91fd-4149-b783-7a3701a5f420 image_name: gardenlinux-1877.3-baremetal region_name: ap-sa-2 +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/openstackbaremetal-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/openstackbaremetal-gardener_prod-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/openstackbaremetal-gardener_prod-arm64.s3_metadata.yaml index 51f2492a..3d76cd18 100644 --- a/test-data/release_notes/s3_bucket_artifacts/openstackbaremetal-gardener_prod-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/openstackbaremetal-gardener_prod-arm64.s3_metadata.yaml @@ -95,6 +95,7 @@ paths: suffix: .release platform: openstackbaremetal published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/openstackbaremetal-gardener_prod-arm64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/vmware-gardener_prod-amd64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/vmware-gardener_prod-amd64.s3_metadata.yaml index 92bd03a9..d7bdd122 100644 --- a/test-data/release_notes/s3_bucket_artifacts/vmware-gardener_prod-amd64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/vmware-gardener_prod-amd64.s3_metadata.yaml @@ -96,6 +96,7 @@ paths: suffix: .chroot.test.log platform: vmware published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/vmware-gardener_prod-amd64-1877.3-75df9f40 diff --git a/test-data/release_notes/s3_bucket_artifacts/vmware-gardener_prod-arm64.s3_metadata.yaml b/test-data/release_notes/s3_bucket_artifacts/vmware-gardener_prod-arm64.s3_metadata.yaml index 307d7ac0..06e16626 100644 --- a/test-data/release_notes/s3_bucket_artifacts/vmware-gardener_prod-arm64.s3_metadata.yaml +++ b/test-data/release_notes/s3_bucket_artifacts/vmware-gardener_prod-arm64.s3_metadata.yaml @@ -84,6 +84,7 @@ paths: suffix: .release.log platform: vmware published_image_metadata: null +publishing_group: '' require_uefi: false s3_bucket: gardenlinux-github-releases s3_key: meta/singles/vmware-gardener_prod-arm64-1877.3-75df9f40 diff --git a/tests/s3/constants.py b/tests/s3/constants.py index b8a06bf9..7dc6e02b 100644 --- a/tests/s3/constants.py +++ b/tests/s3/constants.py @@ -21,6 +21,7 @@ modifiers: - _usi - _trustedboot +publishing_group: '' require_uefi: true secureboot: true tpm2: false From 6ae8adce6fe43f5c5f7db7ab8d82b47b11cf1928 Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Wed, 25 Mar 2026 12:14:36 +0100 Subject: [PATCH 2/3] Remove no longer used GitHub actions Signed-off-by: Tobias Wolf On-behalf-of: SAP --- .github/actions/features_parse/action.yml | 18 ------------ .github/actions/flavors_parse/action.yml | 34 ----------------------- 2 files changed, 52 deletions(-) delete mode 100644 .github/actions/features_parse/action.yml delete mode 100644 .github/actions/flavors_parse/action.yml diff --git a/.github/actions/features_parse/action.yml b/.github/actions/features_parse/action.yml deleted file mode 100644 index 99c79a2f..00000000 --- a/.github/actions/features_parse/action.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: features_parse -description: Parses the given GardenLinux features parameters -inputs: - flags: - description: "Flags passed to `gl-features-parse`" - required: true -outputs: - result: - description: "features result" - value: ${{ steps.result.outputs.result }} -runs: - using: composite - steps: - - uses: gardenlinux/python-gardenlinux-lib/.github/actions/setup@0.10.11 - - id: result - shell: bash - run: | - echo "result=$(gl-features-parse ${{ inputs.flags }})" | tee -a $GITHUB_OUTPUT diff --git a/.github/actions/flavors_parse/action.yml b/.github/actions/flavors_parse/action.yml deleted file mode 100644 index b31b58a2..00000000 --- a/.github/actions/flavors_parse/action.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: flavors_parse -description: Parses the given GardenLinux flavors parameters -inputs: - flags: - description: "Flags passed to `gl-flavors-parse`" - required: true - flavors_matrix: - description: "Generated GitHub workflow flavors matrix" -outputs: - matrix: - description: "Flavors matrix" - value: ${{ steps.matrix.outputs.matrix }} -runs: - using: composite - steps: - - uses: gardenlinux/python-gardenlinux-lib/.github/actions/setup@0.10.11 - - id: matrix - shell: bash - run: | - MATRIX='${{ inputs.flavors_matrix }}' - - if [[ $(echo "${MATRIX}" | jq -r 'type') != 'object' ]]; then - FLAVORS=$(gl-flavors-parse ${{ inputs.flags }}) - MATRIX=$(jq -nc \ - --argjson flavors "$(echo $FLAVORS)" \ - '{ - include: ( - $flavors | reduce (to_entries[]) as $item ([]; . + ($item.value | map({"arch": $item.key, "flavor": .}))) - ) - }' - ) - fi - - echo "matrix=$MATRIX" | tee -a $GITHUB_OUTPUT From 0efc3429e4b753cf82c075a5de7e28bb0f791ec9 Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Mon, 7 Sep 2026 13:44:28 +0200 Subject: [PATCH 3/3] Implement changes for ADR034 and 035. Signed-off-by: Tobias Wolf On-behalf-of: --- pyproject.toml | 1 - src/gardenlinux/constants.py | 5 +- src/gardenlinux/features/__init__.py | 7 +- src/gardenlinux/features/__main__.py | 259 +++----- .../features/artifact_base_name.py | 436 +++++++++++++ src/gardenlinux/features/c_name.py | 235 +++++++ src/gardenlinux/features/cname.py | 613 ------------------ src/gardenlinux/features/cname_main.py | 131 ---- src/gardenlinux/features/flavor.py | 88 +++ src/gardenlinux/features/metadata_main.py | 49 +- src/gardenlinux/features/parser.py | 12 +- .../features/reproducibility/diff_parser.py | 118 ++-- .../reproducibility/markdown_formatter.py | 56 +- src/gardenlinux/features/versioned_flavor.py | 101 +++ .../github/release/release_images_metadata.py | 17 +- src/gardenlinux/oci/container.py | 40 +- src/gardenlinux/oci/image_manifest.py | 23 +- src/gardenlinux/s3/s3_artifacts.py | 74 ++- test-data/gardenlinux | 2 +- .../diff_files/compare_nightly.md | 4 +- .../compare_nightly_different_commit.md | 4 +- .../diff_files/different_commit.md | 4 +- .../diff_files/files_missing.md | 2 +- .../reproducibility/diff_files/one_issue.md | 4 +- .../reproducibility/diff_files/only_issues.md | 2 +- tests/features/constants.py | 6 +- tests/features/test_cname.py | 21 +- tests/features/test_cname_main.py | 159 ----- tests/features/test_main.py | 71 +- tests/features/test_metadata_main.py | 8 +- tests/features/test_parser.py | 8 +- tests/features/test_reproducibility.py | 178 ++--- tests/oci/test_image_manifest.py | 29 +- tests/s3/conftest.py | 8 +- tests/s3/constants.py | 3 +- tests/s3/test_main.py | 4 +- tests/s3/test_s3_artifacts.py | 116 ++-- 37 files changed, 1462 insertions(+), 1436 deletions(-) create mode 100644 src/gardenlinux/features/artifact_base_name.py create mode 100644 src/gardenlinux/features/c_name.py delete mode 100644 src/gardenlinux/features/cname.py delete mode 100644 src/gardenlinux/features/cname_main.py create mode 100644 src/gardenlinux/features/flavor.py create mode 100644 src/gardenlinux/features/versioned_flavor.py delete mode 100644 tests/features/test_cname_main.py diff --git a/pyproject.toml b/pyproject.toml index 6db81aed..b9502294 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,6 @@ sphinxcontrib-autoprogram = "^0.1.8" sphinx-markdown-builder = "^0.6.10" [tool.poetry.scripts] -gl-cname = "gardenlinux.features.cname_main:main" gl-feature-fs-diff = "gardenlinux.features.reproducibility.__main__:main" gl-features-parse = "gardenlinux.features.__main__:main" gl-flavors-parse = "gardenlinux.flavors.__main__:main" diff --git a/src/gardenlinux/constants.py b/src/gardenlinux/constants.py index 5c2fe8ed..d61101e6 100644 --- a/src/gardenlinux/constants.py +++ b/src/gardenlinux/constants.py @@ -4,9 +4,9 @@ ARCHS = ["amd64", "arm64"] # GardenLinux "bare" feature -BARE_FLAVOR_FEATURE_CONTENT = {"description": "Bare flavor", "type": "platform"} +BARE_CNAME_FEATURE_CONTENT = {"description": "Bare flavor", "type": "platform"} -BARE_FLAVOR_LIBC_FEATURE_CONTENT = { +BARE_CNAME_LIBC_FEATURE_CONTENT = { "description": "Bare libc feature", "type": "element", } @@ -147,6 +147,7 @@ GL_BUG_REPORT_URL = "https://github.com/gardenlinux/gardenlinux/issues" GL_COMMIT_SPECIAL_VALUES = ("local",) +GL_VERSION_SPECIAL_VALUES = ("today",) GL_CONTAINER_REGISTRY_BASE_URL = "ghcr.io/gardenlinux/gardenlinux" GL_DEB_REPO_BASE_URL = "https://packages.gardenlinux.io/gardenlinux" GL_DISTRIBUTION_NAME = "Garden Linux" diff --git a/src/gardenlinux/features/__init__.py b/src/gardenlinux/features/__init__.py index a6e7692e..ed61af2f 100644 --- a/src/gardenlinux/features/__init__.py +++ b/src/gardenlinux/features/__init__.py @@ -4,7 +4,10 @@ Features module """ -from .cname import CName +from .artifact_base_name import ArtifactBaseName +from .c_name import CName +from .flavor import Flavor from .parser import Parser +from .versioned_flavor import VersionedFlavor -__all__ = ["CName", "Parser"] +__all__ = ["ArtifactBaseName", "CName", "Flavor", "Parser", "VersionedFlavor"] diff --git a/src/gardenlinux/features/__main__.py b/src/gardenlinux/features/__main__.py index d0b7f8ad..43736524 100644 --- a/src/gardenlinux/features/__main__.py +++ b/src/gardenlinux/features/__main__.py @@ -6,31 +6,31 @@ """ import argparse -import logging import os import re from os import path from typing import Any, Set -from .cname import CName +from .artifact_base_name import ArtifactBaseName from .parser import Parser _ARGS_TYPE_ALLOWED = [ + "arch", + "artifact_base_name", "cname", - "cname_base", + "commit_id", "container_name", "container_tag", - "commit_id", + "elements", "features", - "platform", - "platforms", "flags", "flavor", - "elements", - "arch", + "graph", + "platform", + "platforms", "version", "version_and_commit_id", - "graph", + "versioned_flavor", ] RE_CAMEL_CASE_SPLITTER = re.compile("([A-Z]+|[a-z0-9])([A-Z])(?!$)") @@ -59,10 +59,15 @@ def get_parser() -> argparse.ArgumentParser: help="Target architecture (e.g., amd64, arm64). Overrides architecture from cname.", ) + parser.add_argument( + "--artifact-base-name", + dest="artifact_base_name", + help="Artifact base name to parse. Must be a valid GardenLinux artifact base name.", + ) + parser.add_argument( "--cname", dest="cname", - required=True, help="Canonical name (cname) to parse. Must be a valid GardenLinux canonical name.", ) @@ -79,21 +84,21 @@ def get_parser() -> argparse.ArgumentParser: ) parser.add_argument( - "--release-file", - dest="release_file", - help="Path to a release file containing cname metadata. Either --feature-dir or --release-file must be provided.", + "--flavor", + dest="flavor", + help="GardenLinux flavor name.", ) parser.add_argument( - "--default-arch", - dest="default_arch", - help="Default architecture to use if architecture cannot be determined from cname or other sources.", + "--release-file", + dest="release_file", + help="Path to a release file containing features metadata. Either --feature-dir or --release-file must be provided.", ) parser.add_argument( - "--default-version", - dest="default_version", - help="Default version to use if version cannot be determined from files or other sources.", + "--versioned-flavor", + dest="versioned_flavor", + help="GardenLinux versioned flavor name.", ) parser.add_argument( @@ -133,74 +138,47 @@ def main() -> None: parser = get_parser() args = parser.parse_args() - assert bool(args.feature_dir) or bool(args.release_file), ( - "Please provide either `--feature_dir` or `--release_file` argument" + assert args.feature_dir or args.release_file or os.environ.get("GL_ROOT_DIR"), ( + "Please provide either `--feature-dir` or `--release-file` argument" ) arch = args.arch commit_id_or_hash = args.commit - gardenlinux_root = path.dirname(args.feature_dir) + gardenlinux_root = os.environ.get("GL_ROOT_DIR", path.dirname(args.feature_dir)) version = args.version - if arch is None or arch == "": - arch = args.default_arch - - if gardenlinux_root == "": + if not gardenlinux_root: gardenlinux_root = "." - if version is None or version == "": - try: - version, commit_id_or_hash = get_version_and_commit_id_from_files( - gardenlinux_root - ) - except RuntimeError as exc: - logging.debug( - "Failed to parse version information for GL root '{0}': {1}".format( - gardenlinux_root, exc - ) - ) - - version = args.default_version + if not version or not commit_id_or_hash: + version, commit_id_or_hash = ( + ArtifactBaseName.get_version_and_commit_id_from_files(gardenlinux_root) + ) - cname = CName(args.cname, arch=arch, commit_hash=commit_id_or_hash, version=version) + abn_object = ArtifactBaseName.new_instance( + args.artifact_base_name, + cname=args.cname, + flavor=args.flavor, + versioned_flavor=args.versioned_flavor, + arch=arch, + version=version, + commit_id_or_hash=commit_id_or_hash, + ) if args.release_file is not None: - cname.load_from_release_file(args.release_file) - - arch = cname.arch - flavor = cname.flavor - commit_id_or_hash = cname.commit_id - version = cname.version - - if (arch is None or arch == "") and ( - args.type in ("cname", "container_name", "arch") - ): - raise RuntimeError( - "Architecture could not be determined and no default architecture set" - ) + abn_object.load_from_release_file(args.release_file) - if (commit_id_or_hash is None or commit_id_or_hash == "") and ( - args.type in ("container_tag", "commit_id", "version_and_commit_id") - ): - raise RuntimeError("Commit ID not specified") - - if (version is None or version == "") and ( - args.type - in ( - "container_tag", - "commit_id", - "version", - "version_and_commit_id", - ) - ): - raise RuntimeError("Version not specified and no default version set") + arch = abn_object.arch + cname = abn_object.cname + commit_id_or_hash = abn_object.commit_id + version = abn_object.version feature_dir_name = path.basename(args.feature_dir) if args.type == "arch": print(arch) elif args.type in ( - "cname_base", + "artifact_base_name", "cname", "container_name", "elements", @@ -210,50 +188,24 @@ def main() -> None: "graph", "platform", "platforms", + "versioned_flavor", ): if args.type == "graph" or len(args.ignore) > 0: features_parser = Parser(gardenlinux_root, feature_dir_name) print_output_from_features_parser( - args.type, cname, features_parser, flavor, args.ignore + args.type, abn_object, features_parser, cname, args.ignore ) else: - print_output_from_cname(args.type, cname) + print_output_from_abn_object(args.type, abn_object) elif args.type == "commit_id": - print(commit_id_or_hash[:8]) # type: ignore[index] + print(commit_id_or_hash[:8]) elif args.type == "container_tag": - print(re.sub("\\W+", "-", f"{version}-{commit_id_or_hash[:8]}")) # type: ignore[index] + print(re.sub("\\W+", "-", f"{version}-{commit_id_or_hash[:8]}")) elif args.type == "version": print(version) elif args.type == "version_and_commit_id": - print(f"{version}-{commit_id_or_hash[:8]}") # type: ignore[index] - - -def get_version_and_commit_id_from_files(gardenlinux_root: str) -> tuple[str, str]: - """ - Returns the version and commit ID based on files in the GardenLinux root directory. - - :param gardenlinux_root: GardenLinux root directory - - :return: (tuple) Version and commit ID if readable - :since: 0.7.0 - """ - - commit_hash = None - version = None - - if os.access(path.join(gardenlinux_root, "COMMIT"), os.R_OK): - with open(path.join(gardenlinux_root, "COMMIT"), "r") as fp: - commit_hash = fp.read().strip()[:8] - - if os.access(path.join(gardenlinux_root, "VERSION"), os.R_OK): - with open(path.join(gardenlinux_root, "VERSION"), "r") as fp: - version = fp.read().strip() - - if commit_hash is None or version is None: - raise RuntimeError("Failed to read version or commit ID from files") - - return (version, commit_hash) + print(f"{version}-{commit_id_or_hash[:8]}") def get_minimal_feature_set(graph: Any) -> Set[str]: @@ -269,7 +221,7 @@ def get_minimal_feature_set(graph: Any) -> Set[str]: return set([node for (node, degree) in graph.in_degree() if degree == 0]) -def graph_as_mermaid_markup(flavor: str | None, graph: Any) -> str: +def graph_as_mermaid_markup(cname: str | None, graph: Any) -> str: """ Generates a mermaid.js representation of the graph. This is helpful to identify dependencies between features. @@ -277,17 +229,17 @@ def graph_as_mermaid_markup(flavor: str | None, graph: Any) -> str: Syntax docs: https://mermaid.js.org/syntax/flowchart.html?id=flowcharts-basic-syntax - :param flavor: Flavor name - :param graph: networkx.Digraph + :param cname: GardenLinux canonical name + :param graph: networkx.Digraph :return: (str) mermaid.js representation :since: 0.7.0 """ - if flavor is None: - raise RuntimeError("Error while generating graph: Flavor is None!") + if cname is None: + raise RuntimeError("Error while generating graph: CName is None!") - markup = f"---\ntitle: Dependency Graph for Feature {flavor}\n---\ngraph TD;\n" + markup = f"---\ntitle: Dependency Graph for Feature {cname}\n---\ngraph TD;\n" for u, v in graph.edges: markup += f" {u}-->{v};\n" @@ -297,9 +249,9 @@ def graph_as_mermaid_markup(flavor: str | None, graph: Any) -> str: def print_output_from_features_parser( output_type: str, - cname_instance: CName, + abn_object: ArtifactBaseName, parser: Parser, - flavor: str, + cname: str, ignores_list: Set[str], ) -> None: """ @@ -307,7 +259,7 @@ def print_output_from_features_parser( :param output_type: Output type :param parser: Features parser - :param flavor: Flavor + :param cname: GardenLinux canonical name :param ignores_list: Features to ignore :since: 1.0.0 @@ -319,12 +271,12 @@ def additional_filter_func(node: str) -> bool: if output_type == "features": print( parser.filter_as_string( - flavor, additional_filter_func=additional_filter_func + cname, additional_filter_func=additional_filter_func ) ) elif output_type in ("platform", "platforms", "elements", "flags"): features_by_type = parser.filter_as_dict( - flavor, additional_filter_func=additional_filter_func + cname, additional_filter_func=additional_filter_func ) if output_type == "platform": @@ -336,34 +288,33 @@ def additional_filter_func(node: str) -> bool: elif output_type == "flags": print(",".join(features_by_type["flag"])) else: - graph = parser.filter(flavor, additional_filter_func=additional_filter_func) + graph = parser.filter(cname, additional_filter_func=additional_filter_func) sorted_features = Parser.sort_graph_nodes(graph) minimal_feature_set = get_minimal_feature_set(graph) sorted_minimal_features = Parser.subset(minimal_feature_set, sorted_features) - cname_base = Parser.get_flavor_from_feature_set(sorted_minimal_features) - - if output_type == "cname_base": - print(cname_base) - elif output_type == "cname": - cname = flavor - - if cname_instance.arch is not None: - cname += f"-{cname_instance.arch}" - - if cname_instance.version_and_commit_id is not None: - cname += f"-{cname_instance.version_and_commit_id}" - - print(cname) - elif output_type == "container_name": - print(RE_CAMEL_CASE_SPLITTER.sub("\\1_\\2", cname_base).lower()) - elif output_type == "graph": - print(graph_as_mermaid_markup(flavor, graph)) - - -def print_output_from_cname(output_type: str, cname_instance: CName) -> None: + cname = Parser.get_cname_from_feature_set(sorted_minimal_features) + + match output_type: + case "artifact_base_name": + print(f"{cname}-{abn_object.arch}-{abn_object.version_and_commit_id}") + case "cname": + print(cname) + case "container_name": + print(RE_CAMEL_CASE_SPLITTER.sub("\\1_\\2", cname).lower()) + case "flavor": + print(f"{cname}-{abn_object.arch}") + case "graph": + print(graph_as_mermaid_markup(cname, graph)) + case "versioned_flavor": + print(f"{cname}-{abn_object.arch}-{abn_object.version}") + + +def print_output_from_abn_object( + output_type: str, abn_object: ArtifactBaseName +) -> None: """ Prints output to stdout based on the given CName instance. @@ -373,33 +324,31 @@ def print_output_from_cname(output_type: str, cname_instance: CName) -> None: :since: 1.0.0 """ - if output_type in ("cname_base", "cname", "flavor"): - sorted_features = Parser.get_flavor_as_feature_set(cname_instance.flavor) - flavor = Parser.get_flavor_from_feature_set(sorted_features) - - if output_type in ("cname_base", "flavor"): - print(flavor) - else: - if cname_instance.version_and_commit_id is None: - raise RuntimeError( - "Version and commit ID can't be provided without appropriate input." - ) - - print( - f"{flavor}-{cname_instance.arch}-{cname_instance.version_and_commit_id}" - ) + if output_type in ("artifact_base_name", "cname", "flavor", "versioned_flavor"): + sorted_features = Parser.get_cname_as_feature_set(abn_object.cname) + cname = Parser.get_cname_from_feature_set(sorted_features) + + match output_type: + case "artifact_base_name": + print(f"{cname}-{abn_object.arch}-{abn_object.version_and_commit_id}") + case "cname": + print(cname) + case "flavor": + print(f"{cname}-{abn_object.arch}") + case "versioned_flavor": + print(f"{cname}-{abn_object.arch}-{abn_object.version}") elif output_type == "container_name": - print(RE_CAMEL_CASE_SPLITTER.sub("\\1-\\2", cname_instance.flavor).lower()) + print(RE_CAMEL_CASE_SPLITTER.sub("\\1-\\2", abn_object.cname).lower()) elif output_type == "platform": - print(cname_instance.platform) + print(abn_object.platform) elif output_type == "platforms": - print(cname_instance.feature_set_platform) + print(abn_object.feature_set_platform) elif output_type == "elements": - print(cname_instance.feature_set_element) + print(abn_object.feature_set_element) elif output_type == "features": - print(cname_instance.feature_set) + print(abn_object.feature_set) elif output_type == "flags": - print(cname_instance.feature_set_flag) + print(abn_object.feature_set_flag) if __name__ == "__main__": diff --git a/src/gardenlinux/features/artifact_base_name.py b/src/gardenlinux/features/artifact_base_name.py new file mode 100644 index 00000000..7daa0581 --- /dev/null +++ b/src/gardenlinux/features/artifact_base_name.py @@ -0,0 +1,436 @@ +# -*- coding: utf-8 -*- + +""" +Canonical name (cname) +""" + +import os +import re +from configparser import UNNAMED_SECTION, ConfigParser +from os import PathLike +from pathlib import Path +from typing import Optional, Self + +from ..constants import ( + GL_BUG_REPORT_URL, + GL_COMMIT_SPECIAL_VALUES, + GL_DISTRIBUTION_NAME, + GL_HOME_URL, + GL_RELEASE_ID, + GL_SUPPORT_URL, + GL_VERSION_SPECIAL_VALUES, +) +from .versioned_flavor import VersionedFlavor + + +class ArtifactBaseName(VersionedFlavor): + """ + Class to represent an artifact base name. It can be parsed from any file + type. + + :author: Garden Linux Maintainers + :copyright: Copyright 2026 SAP SE + :package: gardenlinux + :subpackage: features + :since: 1.0.0 + :license: https://www.apache.org/licenses/LICENSE-2.0 + Apache License, Version 2.0 + """ + + def __init__( + self, + artifact: str, + ): + """ + Constructor __init__(ArtifactBaseName) + + :param artifact: Artifact name + + :since: 1.0.0 + """ + + re_object = re.compile( + "([a-zA-Z0-9]+([\\_\\-][a-zA-Z0-9]+)*?)-([a-z0-9]+)-([a-z0-9.]+)-([a-z0-9]+)(\\.|$)" + ) + + re_match = re_object.match(artifact) + + assert re_match, f"Not a valid Garden Linux artifact: {artifact}" + + commit_id_or_hash = re_match[5] + + self._commit_id = commit_id_or_hash[:8] + self._commit_hash = None + + if ( + len(commit_id_or_hash) == 40 + or commit_id_or_hash in GL_COMMIT_SPECIAL_VALUES + ): # sha1 hex + self._commit_hash = commit_id_or_hash + + VersionedFlavor.__init__(self, re_match[1], re_match[3], re_match[4]) + + @property + def commit_hash(self) -> Optional[str]: + """ + Returns the commit hash if part of the artifact base name parsed. + + :return: (str) Commit hash + :since: 1.0.0 + """ + + return self._commit_hash + + @commit_hash.setter + def commit_hash(self, commit_hash: str) -> None: + """ + Sets the commit hash + + :param commit_hash: Commit hash + + :since: 1.0.0 + """ + + if self._commit_id is not None and not commit_hash.startswith(self._commit_id): + raise RuntimeError("Commit hash given differs from commit ID already set") + + self._commit_id = commit_hash[:8] + self._commit_hash = commit_hash + + @property + def commit_id(self) -> str: + """ + Returns the commit ID if part of the artifact base name parsed. + + :return: (str) Commit ID + :since: 1.0.0 + """ + + return self._commit_id + + @property + def release_metadata_string(self) -> str: + """ + Returns the release metadata describing the given CName instance. + + :return: (str) Release metadata describing the given CName instance + :since: 1.0.0 + """ + + commit_hash = self.commit_hash + commit_id = self.commit_id + platform_variant = self.platform_variant + version = self.version + + if commit_hash is None: + commit_hash = commit_id + + if platform_variant is None: + platform_variant = "" + + if version is None: + pretty_name = f"{GL_DISTRIBUTION_NAME} unsupported version" + version = "" + else: + pretty_name = f"{GL_DISTRIBUTION_NAME} {version}" + + metadata = f""" +ID={GL_RELEASE_ID} +ID_LIKE=debian +NAME="{GL_DISTRIBUTION_NAME}" +PRETTY_NAME="{pretty_name}" +IMAGE_VERSION={version} +VARIANT_ID="{self.flavor}" +HOME_URL="{GL_HOME_URL}" +SUPPORT_URL="{GL_SUPPORT_URL}" +BUG_REPORT_URL="{GL_BUG_REPORT_URL}" +GARDENLINUX_CNAME="{self.cname}" +GARDENLINUX_FEATURES="{self.feature_set}" +GARDENLINUX_FEATURES_PLATFORMS="{self.feature_set_platform}" +GARDENLINUX_FEATURES_ELEMENTS="{self.feature_set_element}" +GARDENLINUX_FEATURES_FLAGS="{self.feature_set_flag}" +GARDENLINUX_PLATFORM="{self.platform}" +GARDENLINUX_PLATFORM_VARIANT="{platform_variant}" +GARDENLINUX_VERSION="{version}" +GARDENLINUX_COMMIT_ID="{commit_id}" +GARDENLINUX_COMMIT_ID_LONG="{commit_hash}" + """.strip() + + return metadata + + @property + def version_and_commit_id(self) -> str: + """ + Returns the version and commit ID if part of the cname parsed. + + :return: (str) Version and commit ID + :since: 1.0.0 + """ + + return f"{self.version}-{self.commit_id}" + + @property + def versioned_flavor(self) -> VersionedFlavor: + """ + Returns the versioned flavor of the artifact base name. + + :return: (object) Garden Linux versioned flavor + :since: 1.0.0 + """ + + return self + + def __str__(self) -> str: + """ + Returns the Garden Linux artifact base name. + + :return: (str) Returns the Garden Linux artifact base name + :since: 1.0.0 + """ + + versioned_flavor = VersionedFlavor.__str__(self) + versioned_flavor += f"-{self.commit_id}" + + return versioned_flavor + + def _copy_from_instance(self, artifact_base_name: Self) -> None: + """ + Copies values from a given Garden Linux canonical name instance. + + :param abn_object: Garden Linux canonical name instance + + :since: 1.0.0 + """ + + VersionedFlavor._copy_from_instance(self, artifact_base_name) + + self._commit_hash = artifact_base_name.commit_hash + self._commit_id = artifact_base_name.commit_id + + def load_from_release_file(self, release_file: PathLike[str] | str) -> None: + """ + Loads and parses a release metadata file. + + :param release_file: Release metadata file + + :since: 1.0.0 + """ + + artifact_base_name = self.__class__.new_from_release_file(release_file) + + if ( + artifact_base_name.flavor != self.flavor + or ( + self._commit_id is not None + and self._commit_id != artifact_base_name.commit_id + ) + or ( + self._version is not None + and self._version != artifact_base_name.version + ) + or ( + not self._flag_frankenstein + and artifact_base_name.platform + not in artifact_base_name.feature_set_platform + ) + ): + raise RuntimeError( + f"Release metadata file given is invalid: {release_file} failed consistency check - {self} != {artifact_base_name}" + ) + + self._copy_from_instance(artifact_base_name) + + def save_to_release_file( + self, release_file: PathLike[str] | str, overwrite: Optional[bool] = False + ) -> None: + """ + Saves the release metadata file. + + :param release_file: Release metadata file + + :since: 1.0.0 + """ + + if not isinstance(release_file, PathLike): + release_file = Path(release_file) + + if not overwrite and release_file.exists(): # type: ignore[attr-defined] + raise RuntimeError( + f"Refused to overwrite existing release metadata file: {release_file}" + ) + + with release_file.open("w") as fp: # type: ignore[attr-defined] + fp.write(self.release_metadata_string) + + @staticmethod + def new_from_release_file(release_file: PathLike[str] | str) -> "ArtifactBaseName": + """ + Loads and parses a release metadata file. + + :param release_file: Release metadata file + + :since: 1.0.0 + """ + + if not isinstance(release_file, PathLike): + release_file = Path(release_file) + + if not release_file.exists(): # type: ignore[attr-defined] + raise RuntimeError( + f"Release metadata file given is invalid: {release_file}" + ) + + release_config = ConfigParser(allow_unnamed_section=True) + release_config.read(release_file) + + for release_field in ( + "VARIANT_ID", + "GARDENLINUX_COMMIT_ID_LONG", + "GARDENLINUX_FEATURES", + "GARDENLINUX_PLATFORM", + "GARDENLINUX_VERSION", + ): + if not release_config.has_option(UNNAMED_SECTION, release_field): + raise RuntimeError( + f"Release metadata file given is invalid: {release_file} misses {release_field}" + ) + + artifact_base_name = release_config.get(UNNAMED_SECTION, "VARIANT_ID").strip( + "\"'" + ) + + artifact_base_name += "-" + release_config.get( + UNNAMED_SECTION, "GARDENLINUX_VERSION" + ).strip("\"'") + + artifact_base_name += "-" + release_config.get( + UNNAMED_SECTION, "GARDENLINUX_COMMIT_ID_LONG" + ).strip("\"'") + + abn_object = ArtifactBaseName(artifact_base_name) + + abn_object._feature_set_cached = release_config.get( + UNNAMED_SECTION, "GARDENLINUX_FEATURES" + ).strip("\"'") + + if release_config.has_option(UNNAMED_SECTION, "GARDENLINUX_FEATURES_ELEMENTS"): + abn_object._feature_elements_cached = ( + release_config.get(UNNAMED_SECTION, "GARDENLINUX_FEATURES_ELEMENTS") + .strip("\"'") + .split(",") + ) + + if release_config.has_option(UNNAMED_SECTION, "GARDENLINUX_FEATURES_FLAGS"): + abn_object._feature_flags_cached = ( + release_config.get(UNNAMED_SECTION, "GARDENLINUX_FEATURES_FLAGS") + .strip("\"'") + .split(",") + ) + + if release_config.has_option(UNNAMED_SECTION, "GARDENLINUX_FEATURES_PLATFORMS"): + abn_object._feature_platforms_cached = ( + release_config.get(UNNAMED_SECTION, "GARDENLINUX_FEATURES_PLATFORMS") + .strip("\"'") + .split(",") + ) + + abn_object._platform_cached = release_config.get( + UNNAMED_SECTION, "GARDENLINUX_PLATFORM" + ).strip("\"'") + + if release_config.has_option(UNNAMED_SECTION, "GARDENLINUX_PLATFORM_VARIANT"): + abn_object._platform_variant_cached = release_config.get( + UNNAMED_SECTION, "GARDENLINUX_PLATFORM_VARIANT" + ).strip("\"'") + + return abn_object + + @staticmethod + def get_version_and_commit_id_from_files( + gardenlinux_root: Path | str, + ) -> tuple[Optional[str], Optional[str]]: + """ + Returns the version and commit ID based on files in the GardenLinux root directory. + + :param gardenlinux_root: GardenLinux root directory + + :return: (tuple) Version and commit ID if readable; None otherwise + :since: 0.7.0 + """ + + if not isinstance(gardenlinux_root, PathLike): + gardenlinux_root = Path(gardenlinux_root) + + commit_hash = None + version = None + + if os.access(gardenlinux_root.joinpath("COMMIT"), os.R_OK): + with gardenlinux_root.joinpath("COMMIT").open("r") as fp: + commit_hash = fp.read().strip()[:8] + + if os.access(gardenlinux_root.joinpath("VERSION"), os.R_OK): + with gardenlinux_root.joinpath("VERSION").open("r") as fp: + version = fp.read().strip() + + if commit_hash is None or version is None: + return (None, None) + + return (version, commit_hash) + + @staticmethod + def new_instance( + artifact_base_name: Optional[str] = None, + versioned_flavor: Optional[str] = None, + flavor: Optional[str] = None, + cname: Optional[str] = None, + arch: Optional[str] = None, + version: Optional[str] = None, + commit_id_or_hash: Optional[str] = None, + ) -> "ArtifactBaseName": + if not artifact_base_name: + if versioned_flavor: + if not commit_id_or_hash: + raise ValueError("Argument missing: commit_id_or_hash") + + artifact_base_name = versioned_flavor + f"-{commit_id_or_hash}" + elif flavor: + if not version: + raise ValueError("Argument missing: version") + if not commit_id_or_hash: + raise ValueError("Argument missing: commit_id_or_hash") + + artifact_base_name = flavor + f"-{version}-{commit_id_or_hash}" + elif cname: + if not arch: + raise ValueError("Argument missing: arch") + if not version: + raise ValueError("Argument missing: version") + if not commit_id_or_hash: + raise ValueError("Argument missing: commit_id_or_hash") + + artifact_base_name = cname + f"-{arch}-{version}-{commit_id_or_hash}" + else: + raise ValueError( + "Argument missing: At least one of artifact_base_name, cname, flavor or versioned_flavor" + ) + + abn_object = ArtifactBaseName(artifact_base_name) + + if cname and abn_object.cname != cname: + raise RuntimeError("CName does not match the given argument") + if arch and abn_object.arch != arch: + raise RuntimeError("Architecture does not match the given argument") + if ( + version + and version not in GL_VERSION_SPECIAL_VALUES + and abn_object.version != version + ): + raise RuntimeError("Version does not match the given argument") + + if commit_id_or_hash: + if abn_object.commit_hash and abn_object.commit_hash != commit_id_or_hash: + raise RuntimeError("Commit hash does not match the given argument") + elif abn_object.commit_id != commit_id_or_hash: + raise RuntimeError("Commit ID does not match the given argument") + + return abn_object diff --git a/src/gardenlinux/features/c_name.py b/src/gardenlinux/features/c_name.py new file mode 100644 index 00000000..c43e1091 --- /dev/null +++ b/src/gardenlinux/features/c_name.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- + +""" +Canonical name (cname) +""" + +from os import environ +from typing import Any, Dict, List, Optional, Self + +from ..constants import GL_PLATFORM_FRANKENSTEIN +from .parser import Parser + + +class CName(object): + """ + Class to represent a canonical name (cname). + + :author: Garden Linux Maintainers + :copyright: Copyright 2024 SAP SE + :package: gardenlinux + :subpackage: features + :since: 0.7.0 + :license: https://www.apache.org/licenses/LICENSE-2.0 + Apache License, Version 2.0 + """ + + def __init__( + self, + cname: str, + ): + """ + Constructor __init__(CName) + + :param cname: Canonical name to represent + + :since: 0.7.0 + """ + + self._cname = cname + self._feature_elements_cached: Optional[List[str]] = None + self._feature_flags_cached: Optional[List[str]] = None + self._feature_platforms_cached: Optional[List[str]] = None + self._feature_set_cached: Optional[str] = None + self._features_cached: Optional[Dict[str, Any]] = None + self._platform_cached: Optional[str] = None + self._platform_variant_cached: Optional[str] = None + + self._flag_frankenstein = bool(environ.get("GL_ALLOW_FRANKENSTEIN", False)) + + self._flag_multiple_platforms = bool( + environ.get("GL_ALLOW_MULTIPLE_PLATFORMS", False) + ) + + if self._flag_frankenstein: + self._flag_multiple_platforms = True + + @property + def cname(self) -> str: + """ + Returns the cname parsed. + + :return: (str) CName + :since: 0.7.0 + """ + + return self._cname + + @property + def features(self) -> Dict[str, Any]: + """ + Returns the features for the cname parsed. + + :return: (dict) Features of the cname + :since: 0.10.14 + """ + + if self._features_cached is None: + self._features_cached = Parser().filter_as_dict(self.cname) + + return self._features_cached + + @property + def feature_set(self) -> str: + """ + Returns the feature set for the cname parsed. + + :return: (str) Feature set of the cname + :since: 0.7.0 + """ + + if self._feature_set_cached is not None: + return self._feature_set_cached + + return Parser().filter_as_string(self.cname) + + @property + def feature_set_element(self) -> str: + """ + Returns the feature set of type "element" for the cname parsed. + + :return: (str) Feature set elements + :since: 1.0.0 + """ + + if self._feature_elements_cached is not None: + return ",".join(self._feature_elements_cached) + + return ",".join(self.features["element"]) + + @property + def feature_set_flag(self) -> str: + """ + Returns the feature set of type "flag" for the cname parsed. + + :return: (str) Feature set flags + :since: 1.0.0 + """ + + if self._feature_flags_cached is not None: + return ",".join(self._feature_flags_cached) + + return ",".join(self.features["flag"]) + + @property + def feature_set_platform(self) -> str: + """ + Returns the feature set of type "platform" for the cname parsed. + + :return: (str) Feature set platform + :since: 1.0.0 + """ + + if self._feature_platforms_cached is None: + platforms = self.features["platform"] + else: + platforms = self._feature_platforms_cached + + if self._flag_multiple_platforms: + return ",".join(platforms) + + assert len(platforms) < 2, "Only one platform is supported" + return platforms[0] # type: ignore[no-any-return] + + @property + def feature_set_list(self) -> List[str]: + """ + Returns the feature set for the cname parsed. + + :return: (list) Feature set list of the cname + :since: 0.10.12 + """ + + if self._feature_set_cached is not None: + return self._feature_set_cached.split(",") + + return Parser().filter_as_list(self.cname) + + @property + def platform(self) -> str: + """ + Returns the platform for the cname parsed. + + :return: (str) Platform + :since: 0.7.0 + """ + + if self._platform_cached is not None: + platforms = [self._platform_cached] + elif self._feature_platforms_cached is not None: + platforms = self._feature_platforms_cached + else: + platforms = self.features["platform"] + + if self._flag_frankenstein and len(platforms) > 1: + return GL_PLATFORM_FRANKENSTEIN + + if not self._flag_multiple_platforms: + assert len(platforms) < 2 + "Only one platform is supported" + + return platforms[0] + + @property + def platform_variant(self) -> Optional[str]: + """ + Returns the platform variant for the cname parsed. + + :return: (str) Platform variant + :since: 1.0.0 + """ + + if self._platform_variant_cached is not None: + return self._platform_variant_cached + + # @TODO: Platform variant is set by GardenLinux features to the release file. If not read or cached it is currently invisible for this library. + return None + + @platform_variant.setter + def platform_variant(self, variant: str) -> None: + """ + Sets the the platform variant + + :param variant: Platform variant + + :since: 1.0.0 + """ + + self._platform_variant_cached = variant + + def __str__(self) -> str: + """ + Returns the Garden Linux artifact base name. + + :return: (str) Returns the Garden Linux artifact base name + :since: 1.0.0 + """ + + return self.cname + + def _copy_from_instance(self, c_name: Self) -> None: + """ + Copies values from a given Garden Linux canonical name instance. + + :param cname_object: Garden Linux canonical name instance + + :since: 1.0.0 + """ + + self._cname = c_name.cname + self._feature_set_cached = c_name.feature_set + self._feature_elements_cached = c_name.feature_set_element.split(",") + self._feature_flags_cached = c_name.feature_set_flag.split(",") + self._feature_platforms_cached = c_name.feature_set_platform.split(",") + self._platform_cached = c_name.platform + self._platform_variant_cached = c_name.platform_variant diff --git a/src/gardenlinux/features/cname.py b/src/gardenlinux/features/cname.py deleted file mode 100644 index 98c9cc29..00000000 --- a/src/gardenlinux/features/cname.py +++ /dev/null @@ -1,613 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Canonical name (cname) -""" - -import re -from configparser import UNNAMED_SECTION, ConfigParser -from os import PathLike, environ -from pathlib import Path -from typing import Any, Dict, List, Optional, Self - -from ..constants import ( - ARCHS, - GL_BUG_REPORT_URL, - GL_DISTRIBUTION_NAME, - GL_HOME_URL, - GL_PLATFORM_FRANKENSTEIN, - GL_RELEASE_ID, - GL_SUPPORT_URL, -) -from .parser import Parser - - -class CName(object): - """ - Class to represent a canonical name (cname). - - :author: Garden Linux Maintainers - :copyright: Copyright 2024 SAP SE - :package: gardenlinux - :subpackage: features - :since: 0.7.0 - :license: https://www.apache.org/licenses/LICENSE-2.0 - Apache License, Version 2.0 - """ - - def __init__( - self, - cname: str, - arch: Optional[str] = None, - commit_hash: Optional[str] = None, - version: Optional[str] = None, - ): - """ - Constructor __init__(CName) - - :param cname: Canonical name to represent - :param arch: Architecture if not part of cname - :param commit_hash: Commit ID or hash if not part of cname - :param version: Version if not part of cname - - :since: 0.7.0 - """ - - self._arch = None - self._commit_hash = None - self._commit_id = None - self._feature_elements_cached: Optional[List[str]] = None - self._feature_flags_cached: Optional[List[str]] = None - self._feature_platforms_cached: Optional[List[str]] = None - self._feature_set_cached: Optional[str] = None - self._features_cached: Optional[Dict[str, Any]] = None - self._platform_cached: Optional[str] = None - self._platform_variant_cached: Optional[str] = None - self._flavor = "" - self._version = None - - self._flag_frankenstein = bool(environ.get("GL_ALLOW_FRANKENSTEIN", False)) - - self._flag_multiple_platforms = bool( - environ.get("GL_ALLOW_MULTIPLE_PLATFORMS", False) - ) - - if self._flag_frankenstein: - self._flag_multiple_platforms = True - - commit_id_or_hash = None - - if version is not None: - # Support version values formatted as - - if commit_hash is None: - re_match = re.match("([a-z0-9.]+)(-([a-z0-9]+))?$", version) - assert re_match, f"Not a valid version {version}" - - commit_id_or_hash = re_match[3] - version = re_match[1] - else: - commit_id_or_hash = commit_hash - - re_object = re.compile( - "([a-zA-Z0-9]+([\\_\\-][a-zA-Z0-9]+)*?)(-([a-z0-9]+)(-([a-z0-9.]+)-([a-z0-9]+))*)?$" - ) - - re_match = re_object.match(cname) - - # Workaround Garden Linux canonical names without mandatory final commit hash - if ( - not re_match - and commit_id_or_hash - and re.match( - "([a-zA-Z0-9]+([\\_\\-][a-zA-Z0-9]+)*?)(-([a-z0-9]+)(-([a-z0-9.]+))*)?$", - cname, - ) - ): - re_match = re_object.match(f"{cname}-{commit_id_or_hash}") - - assert re_match, f"Not a valid Garden Linux canonical name {cname}" - - if re_match.lastindex == 1: - self._flavor = re_match[1] - else: - if commit_id_or_hash is None: - commit_id_or_hash = re_match[7] - elif re_match.group(7) is not None: - assert commit_id_or_hash.startswith(re_match[7]), ( - f"Mismatch between Garden Linux canonical name '{cname}' and given commit ID '{commit_id_or_hash}' detected" - ) - - self._flavor = re_match[1] - self._version = re_match[6] - - if re_match[4] in ARCHS: - self._arch = re_match[4] - else: - self._flavor += re_match[3] - - if self._arch is None and arch is not None: - self._arch = arch - - if version is not None: - if self._version is None: - self._version = version - else: - assert version == self._version, ( - f"Mismatch between Garden Linux canonical name '{cname}' and given version '{version}' detected" - ) - - if commit_id_or_hash is not None: - self._commit_id = commit_id_or_hash[:8] - - if len(commit_id_or_hash) == 40 or commit_id_or_hash == "local": # sha1 hex - self._commit_hash = commit_id_or_hash - - @property - def arch(self) -> Optional[str]: - """ - Returns the architecture for the cname parsed. - - :return: (str) CName architecture - :since: 0.7.0 - """ - - return self._arch - - @property - def cname(self) -> str: - """ - Returns the cname parsed. - - :return: (str) CName - :since: 0.7.0 - """ - - cname = self._flavor - - if self._arch is not None: - cname += f"-{self._arch}" - - if self._commit_id is not None and self._version is not None: - cname += f"-{self.version_and_commit_id}" - - return cname - - @property - def commit_hash(self) -> Optional[str]: - """ - Returns the commit hash if part of the cname parsed. - - :return: (str) Commit hash - :since: 1.0.0 - """ - - return self._commit_hash - - @commit_hash.setter - def commit_hash(self, commit_hash: str) -> None: - """ - Sets the commit hash - - :param commit_hash: Commit hash - - :since: 1.0.0 - """ - - if self._commit_id is not None and not commit_hash.startswith(self._commit_id): - raise RuntimeError("Commit hash given differs from commit ID already set") - - self._commit_id = commit_hash[:8] - self._commit_hash = commit_hash - - @property - def commit_id(self) -> Optional[str]: - """ - Returns the commit ID if part of the cname parsed. - - :return: (str) Commit ID - :since: 0.7.0 - """ - - return self._commit_id - - @property - def flavor(self) -> str: - """ - Returns the flavor for the cname parsed. - - :return: (str) Flavor - :since: 0.7.0 - """ - - return self._flavor - - @property - def features(self) -> Dict[str, Any]: - """ - Returns the features for the cname parsed. - - :return: (dict) Features of the cname - :since: 0.10.14 - """ - - if self._features_cached is None: - self._features_cached = Parser().filter_as_dict(self.flavor) - - return self._features_cached - - @property - def feature_set(self) -> str: - """ - Returns the feature set for the cname parsed. - - :return: (str) Feature set of the cname - :since: 0.7.0 - """ - - if self._feature_set_cached is not None: - return self._feature_set_cached - - return Parser().filter_as_string(self.flavor) - - @property - def feature_set_element(self) -> str: - """ - Returns the feature set of type "element" for the cname parsed. - - :return: (str) Feature set elements - :since: 1.0.0 - """ - - if self._feature_elements_cached is not None: - return ",".join(self._feature_elements_cached) - - return ",".join(self.features["element"]) - - @property - def feature_set_flag(self) -> str: - """ - Returns the feature set of type "flag" for the cname parsed. - - :return: (str) Feature set flags - :since: 1.0.0 - """ - - if self._feature_flags_cached is not None: - return ",".join(self._feature_flags_cached) - - return ",".join(self.features["flag"]) - - @property - def feature_set_platform(self) -> str: - """ - Returns the feature set of type "platform" for the cname parsed. - - :return: (str) Feature set platform - :since: 1.0.0 - """ - - if self._feature_platforms_cached is None: - platforms = self.features["platform"] - else: - platforms = self._feature_platforms_cached - - if self._flag_multiple_platforms: - return ",".join(platforms) - - assert len(platforms) < 2 - "Only one platform is supported" - return platforms[0] # type: ignore[no-any-return] - - @property - def feature_set_list(self) -> List[str]: - """ - Returns the feature set for the cname parsed. - - :return: (list) Feature set list of the cname - :since: 0.10.12 - """ - - if self._feature_set_cached is not None: - return self._feature_set_cached.split(",") - - return Parser().filter_as_list(self.flavor) - - @property - def platform(self) -> str: - """ - Returns the platform for the cname parsed. - - :return: (str) Platform - :since: 0.7.0 - """ - - if self._platform_cached is not None: - platforms = [self._platform_cached] - elif self._feature_platforms_cached is not None: - platforms = self._feature_platforms_cached - else: - platforms = self.features["platform"] - - if self._flag_frankenstein and len(platforms) > 1: - return GL_PLATFORM_FRANKENSTEIN - - if not self._flag_multiple_platforms: - assert len(platforms) < 2 - "Only one platform is supported" - - return platforms[0] - - @property - def platform_variant(self) -> Optional[str]: - """ - Returns the platform variant for the cname parsed. - - :return: (str) Platform variant - :since: 1.0.0 - """ - - if self._platform_variant_cached is not None: - return self._platform_variant_cached - - # @TODO: Platform variant is set by GardenLinux features to the release file. If not read or cached it is currently invisible for this library. - return None - - @platform_variant.setter - def platform_variant(self, variant: str) -> None: - """ - Sets the the platform variant - - :param variant: Platform variant - - :since: 1.0.0 - """ - - self._platform_variant_cached = variant - - @property - def release_metadata_string(self) -> str: - """ - Returns the release metadata describing the given CName instance. - - :return: (str) Release metadata describing the given CName instance - :since: 1.0.0 - """ - - commit_hash = self.commit_hash - commit_id = self.commit_id - platform_variant = self.platform_variant - version = self.version - - if commit_id is None: - commit_id = "" - - if commit_hash is None: - commit_hash = commit_id - - if platform_variant is None: - platform_variant = "" - - if version is None: - pretty_name = f"{GL_DISTRIBUTION_NAME} unsupported version" - version = "" - else: - pretty_name = f"{GL_DISTRIBUTION_NAME} {version}" - - metadata = f""" -ID={GL_RELEASE_ID} -ID_LIKE=debian -NAME="{GL_DISTRIBUTION_NAME}" -PRETTY_NAME="{pretty_name}" -IMAGE_VERSION={version} -VARIANT_ID="{self.flavor}-{self.arch}" -HOME_URL="{GL_HOME_URL}" -SUPPORT_URL="{GL_SUPPORT_URL}" -BUG_REPORT_URL="{GL_BUG_REPORT_URL}" -GARDENLINUX_CNAME="{self.cname}" -GARDENLINUX_FEATURES="{self.feature_set}" -GARDENLINUX_FEATURES_PLATFORMS="{self.feature_set_platform}" -GARDENLINUX_FEATURES_ELEMENTS="{self.feature_set_element}" -GARDENLINUX_FEATURES_FLAGS="{self.feature_set_flag}" -GARDENLINUX_PLATFORM="{self.platform}" -GARDENLINUX_PLATFORM_VARIANT="{platform_variant}" -GARDENLINUX_VERSION="{version}" -GARDENLINUX_COMMIT_ID="{commit_id}" -GARDENLINUX_COMMIT_ID_LONG="{commit_hash}" - """.strip() - - return metadata - - @property - def version(self) -> Optional[str]: - """ - Returns the version if part of the cname parsed. - - :return: (str) Version - :since: 0.7.0 - """ - - return self._version - - @property - def version_and_commit_id(self) -> Optional[str]: - """ - Returns the version and commit ID if part of the cname parsed. - - :return: (str) Version and commit ID - :since: 0.7.0 - """ - - if self._version is None or self._commit_id is None: - return None - - return f"{self._version}-{self._commit_id}" - - @property - def version_epoch(self) -> Optional[int]: - """ - Returns the GardenLinux version epoch of the cname parsed. - - :return: (str) GardenLinux version epoch - :since: 1.0.0 - """ - - epoch = None - - if self._version is not None and "." in self._version: - epoch = int(self._version.split(".", 1)[0]) - - return epoch - - def _copy_from_cname_object(self, cname_object: Self) -> None: - """ - Copies values from a given Garden Linux canonical name instance. - - :param cname_object: Garden Linux canonical name instance - - :since: 1.0.0 - """ - - self._arch = cname_object.arch - self._commit_hash = cname_object.commit_hash - self._commit_id = cname_object.commit_id - self._feature_set_cached = cname_object.feature_set - self._feature_elements_cached = cname_object.feature_set_element.split(",") - self._feature_flags_cached = cname_object.feature_set_flag.split(",") - self._feature_platforms_cached = cname_object.feature_set_platform.split(",") - self._platform_cached = cname_object.platform - self._platform_variant_cached = cname_object.platform_variant - self._version = cname_object.version - - def load_from_release_file(self, release_file: PathLike[str] | str) -> None: - """ - Loads and parses a release metadata file. - - :param release_file: Release metadata file - - :since: 1.0.0 - """ - - cname_object = CName.new_from_release_file(release_file) - - if ( - cname_object.flavor != self.flavor - or ( - self._commit_id is not None - and self._commit_id != cname_object.commit_id - ) - or (self._version is not None and self._version != cname_object.version) - or ( - not self._flag_frankenstein - and cname_object.platform not in cname_object.feature_set_platform - ) - ): - raise RuntimeError( - f"Release metadata file given is invalid: {release_file} failed consistency check - {self.cname} != {cname_object.cname}" - ) - - self._copy_from_cname_object(cname_object) - - def save_to_release_file( - self, release_file: PathLike[str] | str, overwrite: Optional[bool] = False - ) -> None: - """ - Saves the release metadata file. - - :param release_file: Release metadata file - - :since: 1.0.0 - """ - - if not isinstance(release_file, PathLike): - release_file = Path(release_file) - - if not overwrite and release_file.exists(): # type: ignore[attr-defined] - raise RuntimeError( - f"Refused to overwrite existing release metadata file: {release_file}" - ) - - with release_file.open("w") as fp: # type: ignore[attr-defined] - fp.write(self.release_metadata_string) - - @staticmethod - def new_from_release_file(release_file: PathLike[str] | str) -> "CName": - """ - Loads and parses a release metadata file. - - :param release_file: Release metadata file - - :since: 0.10.10 - """ - - if not isinstance(release_file, PathLike): - release_file = Path(release_file) - - if not release_file.exists(): # type: ignore[attr-defined] - raise RuntimeError( - f"Release metadata file given is invalid: {release_file}" - ) - - release_config = ConfigParser(allow_unnamed_section=True) - release_config.read(release_file) - - for release_field in ( - "GARDENLINUX_CNAME", - "GARDENLINUX_COMMIT_ID_LONG", - "GARDENLINUX_FEATURES", - "GARDENLINUX_PLATFORM", - "GARDENLINUX_VERSION", - ): - if not release_config.has_option(UNNAMED_SECTION, release_field): - raise RuntimeError( - f"Release metadata file given is invalid: {release_file} misses {release_field}" - ) - - commit_hash = release_config.get( - UNNAMED_SECTION, "GARDENLINUX_COMMIT_ID_LONG" - ).strip("\"'") - - version = release_config.get(UNNAMED_SECTION, "GARDENLINUX_VERSION").strip( - "\"'" - ) - - cname_object = CName( - release_config.get(UNNAMED_SECTION, "GARDENLINUX_CNAME").strip("\"'"), - commit_hash=commit_hash, - version=version, - ) - - cname_object._feature_set_cached = release_config.get( - UNNAMED_SECTION, "GARDENLINUX_FEATURES" - ).strip("\"'") - - if release_config.has_option(UNNAMED_SECTION, "GARDENLINUX_FEATURES_ELEMENTS"): - cname_object._feature_elements_cached = ( - release_config.get(UNNAMED_SECTION, "GARDENLINUX_FEATURES_ELEMENTS") - .strip("\"'") - .split(",") - ) - - if release_config.has_option(UNNAMED_SECTION, "GARDENLINUX_FEATURES_FLAGS"): - cname_object._feature_flags_cached = ( - release_config.get(UNNAMED_SECTION, "GARDENLINUX_FEATURES_FLAGS") - .strip("\"'") - .split(",") - ) - - if release_config.has_option(UNNAMED_SECTION, "GARDENLINUX_FEATURES_PLATFORMS"): - cname_object._feature_platforms_cached = ( - release_config.get(UNNAMED_SECTION, "GARDENLINUX_FEATURES_PLATFORMS") - .strip("\"'") - .split(",") - ) - - cname_object._platform_cached = release_config.get( - UNNAMED_SECTION, "GARDENLINUX_PLATFORM" - ).strip("\"'") - - if release_config.has_option(UNNAMED_SECTION, "GARDENLINUX_PLATFORM_VARIANT"): - cname_object._platform_variant_cached = release_config.get( - UNNAMED_SECTION, "GARDENLINUX_PLATFORM_VARIANT" - ).strip("\"'") - - return cname_object diff --git a/src/gardenlinux/features/cname_main.py b/src/gardenlinux/features/cname_main.py deleted file mode 100644 index 5ce5998c..00000000 --- a/src/gardenlinux/features/cname_main.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -gl-cname main entrypoint -""" - -import argparse -import logging -import re -from os.path import basename, dirname - -from .__main__ import ( - get_minimal_feature_set, - get_version_and_commit_id_from_files, -) -from .cname import CName -from .parser import Parser - - -def get_parser() -> argparse.ArgumentParser: - """ - Get the argument parser for gl-cname. - Used for documentation generation. - - :return: ArgumentParser instance - :since: 1.0.0 - """ - - parser = argparse.ArgumentParser( - prog="gl-cname", - description="Generate a canonical name (cname) from feature sets.", - ) - - parser.add_argument( - "--arch", - dest="arch", - help="Target architecture (e.g., amd64, arm64). If not specified, will be determined from the cname or feature set.", - ) - - parser.add_argument( - "--commit", - dest="commit", - help="Git commit hash. If not specified, will be read from COMMIT file in the GardenLinux root directory.", - ) - - parser.add_argument( - "--feature-dir", - default="features", - help="Path to the features directory (default: 'features').", - ) - - parser.add_argument( - "--version", - dest="version", - help="Version string. If not specified, will be read from VERSION file in the GardenLinux root directory.", - ) - - parser.add_argument( - "cname", - help="Canonical name (cname) to process. Must be a valid GardenLinux canonical name format.", - ) - - return parser - - -def main() -> None: - """ - gl-cname main() - - :since: 0.7.0 - """ - - parser = get_parser() - args = parser.parse_args() - - re_match = re.match( - "([a-zA-Z0-9]+([\\_\\-][a-zA-Z0-9]+)*?)(-([a-z0-9]+)(-([a-z0-9.]+)-([a-z0-9]+))*)?$", - args.cname, - ) - - assert re_match, f"Not a valid Garden Linux canonical name {args.cname}" - - arch = args.arch - commit_id_or_hash = args.commit - gardenlinux_root = dirname(args.feature_dir) - version = args.version - - if gardenlinux_root == "": - gardenlinux_root = "." - - if not version: - try: - version, commit_id_or_hash = get_version_and_commit_id_from_files( - gardenlinux_root - ) - except RuntimeError as exc: - logging.warning( - "Failed to parse version information for GL root '{0}': {1}".format( - gardenlinux_root, exc - ) - ) - - cname = CName(args.cname, arch=arch, commit_hash=commit_id_or_hash, version=version) - - assert cname.arch, "Architecture could not be determined" - - feature_dir_name = basename(args.feature_dir) - - if gardenlinux_root == "": - gardenlinux_root = "." - - graph = Parser(gardenlinux_root, feature_dir_name).filter(cname.flavor) - - sorted_features = Parser.sort_graph_nodes(graph) - minimal_feature_set = get_minimal_feature_set(graph) - - sorted_minimal_features = Parser.subset(minimal_feature_set, sorted_features) - - generated_cname = Parser.get_flavor_from_feature_set(sorted_minimal_features) - - generated_cname += f"-{cname.arch}" - - if cname.version_and_commit_id is not None: - generated_cname += f"-{cname.version_and_commit_id}" - - print(generated_cname) - - -if __name__ == "__main__": - main() diff --git a/src/gardenlinux/features/flavor.py b/src/gardenlinux/features/flavor.py new file mode 100644 index 00000000..51170040 --- /dev/null +++ b/src/gardenlinux/features/flavor.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +""" +Canonical name (cname) +""" + +from typing import Self + +from .c_name import CName + + +class Flavor(CName): + """ + Class to represent a Garden Linux flavor. + + :author: Garden Linux Maintainers + :copyright: Copyright 2024 SAP SE + :package: gardenlinux + :subpackage: features + :since: 1.0.0 + :license: https://www.apache.org/licenses/LICENSE-2.0 + Apache License, Version 2.0 + """ + + def __init__( + self, + c_name: str, + arch: str, + ): + """ + Constructor __init__(Flavor) + + :param c_name: Canonical name to represent + :param arch: Architecture if not part of cname + + :since: 1.0.0 + """ + + CName.__init__(self, c_name) + self._arch = arch + + @property + def arch(self) -> str: + """ + Returns the architecture for the cname parsed. + + :return: (str) CName architecture + :since: 0.7.0 + """ + + return self._arch + + @property + def flavor(self) -> str: + """ + Returns the architecture for the cname parsed. + + :return: (str) CName architecture + :since: 0.7.0 + """ + + return Flavor.__str__(self) + + def __str__(self) -> str: + """ + Returns the Garden Linux artifact base name. + + :return: (str) Returns the Garden Linux artifact base name + :since: 1.0.0 + """ + + c_name = CName.__str__(self) + c_name += f"-{self.arch}" + + return c_name + + def _copy_from_instance(self, flavor: Self) -> None: + """ + Copies values from a given Garden Linux canonical name instance. + + :param cname_object: Garden Linux canonical name instance + + :since: 1.0.0 + """ + + CName._copy_from_instance(self, flavor) + + self._arch = flavor.arch diff --git a/src/gardenlinux/features/metadata_main.py b/src/gardenlinux/features/metadata_main.py index 9bb2c1d7..b455a0b5 100644 --- a/src/gardenlinux/features/metadata_main.py +++ b/src/gardenlinux/features/metadata_main.py @@ -6,8 +6,9 @@ """ import argparse +from os import environ -from .cname import CName +from .artifact_base_name import ArtifactBaseName _ARGS_ACTION_ALLOWED = [ "output-release-metadata", @@ -25,11 +26,22 @@ def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--arch", dest="arch") - parser.add_argument("--cname", required=True, dest="cname") - parser.add_argument("--commit-hash", dest="commit_hash") + parser.add_argument( + "--artifact-base-name", + dest="artifact_base_name", + help="Artifact base name to parse. Must be a valid GardenLinux artifact base name.", + ) + parser.add_argument("--cname", dest="cname") + parser.add_argument("--commit", dest="commit") + parser.add_argument("--flavor", dest="flavor", help="GardenLinux flavor name.") parser.add_argument("--release-file", dest="release_file") parser.add_argument("--overwrite-file", type=bool, dest="overwrite_file") parser.add_argument("--version", dest="version") + parser.add_argument( + "--versioned-flavor", + dest="versioned_flavor", + help="GardenLinux versioned flavor name.", + ) parser.add_argument( "action", @@ -40,20 +52,35 @@ def main() -> None: args = parser.parse_args() - cname = CName( - args.cname, arch=args.arch, commit_hash=args.commit_hash, version=args.version + commit_id_or_hash = args.commit + gardenlinux_root = environ.get("GL_ROOT_DIR") + version = args.version + + if not gardenlinux_root: + gardenlinux_root = "." + + if not version or not commit_id_or_hash: + version, commit_id_or_hash = ( + ArtifactBaseName.get_version_and_commit_id_from_files(gardenlinux_root) + ) + + abn_object = ArtifactBaseName.new_instance( + args.artifact_base_name, + cname=args.cname, + flavor=args.flavor, + versioned_flavor=args.versioned_flavor, + arch=args.arch, + version=args.version, + commit_id_or_hash=args.commit, ) - if args.commit_hash is not None: - cname.commit_hash = args.commit_hash - if args.action == "write": - cname.save_to_release_file(args.release_file, args.overwrite_file) + abn_object.save_to_release_file(args.release_file, args.overwrite_file) else: if args.release_file is not None: - cname.load_from_release_file(args.release_file) + abn_object.load_from_release_file(args.release_file) - print(cname.release_metadata_string) + print(abn_object.release_metadata_string) if __name__ == "__main__": diff --git a/src/gardenlinux/features/parser.py b/src/gardenlinux/features/parser.py index 497e66fb..88202eb4 100644 --- a/src/gardenlinux/features/parser.py +++ b/src/gardenlinux/features/parser.py @@ -14,7 +14,7 @@ import networkx import yaml -from ..constants import BARE_FLAVOR_FEATURE_CONTENT, BARE_FLAVOR_LIBC_FEATURE_CONTENT +from ..constants import BARE_CNAME_FEATURE_CONTENT, BARE_CNAME_LIBC_FEATURE_CONTENT from ..logger import LoggerSetup @@ -132,7 +132,7 @@ def filter( :since: 0.7.0 """ - feature_set = Parser.get_flavor_as_feature_set(cname) + feature_set = Parser.get_cname_as_feature_set(cname) return self.filter_based_on_feature_set( feature_set, ignore_excludes, additional_filter_func @@ -278,9 +278,9 @@ def filter_based_on_feature_set( # @TODO: Remove "special" handling once "bare" is a first-class citizen of the feature graph if "bare" in feature_set: if not self.graph.has_node("bare"): - self.graph.add_node("bare", content=BARE_FLAVOR_FEATURE_CONTENT) + self.graph.add_node("bare", content=BARE_CNAME_FEATURE_CONTENT) if not self.graph.has_node("libc"): - self.graph.add_node("libc", content=BARE_FLAVOR_LIBC_FEATURE_CONTENT) + self.graph.add_node("libc", content=BARE_CNAME_LIBC_FEATURE_CONTENT) for feature in feature_set: for node in networkx.descendants( @@ -363,7 +363,7 @@ def _read_feature_yaml(self, feature_yaml_file: str) -> Dict[str, Any]: return {"name": name, "content": content} @staticmethod - def get_flavor_from_feature_set(sorted_features: List[str]) -> str: + def get_cname_from_feature_set(sorted_features: List[str]) -> str: """ Get the base cname for the feature set given. @@ -378,7 +378,7 @@ def get_flavor_from_feature_set(sorted_features: List[str]) -> str: ) @staticmethod - def get_flavor_as_feature_set(cname: str) -> List[str]: + def get_cname_as_feature_set(cname: str) -> List[str]: """ Returns the features of a given canonical name. diff --git a/src/gardenlinux/features/reproducibility/diff_parser.py b/src/gardenlinux/features/reproducibility/diff_parser.py index 319622d0..41963711 100644 --- a/src/gardenlinux/features/reproducibility/diff_parser.py +++ b/src/gardenlinux/features/reproducibility/diff_parser.py @@ -19,7 +19,7 @@ class DiffParser(object): """ This class takes the differ_files results from the reproducibility check and detects problems - It also analyzes the features of the affected flavors + It also analyzes the features of the affected cnames :author: Garden Linux Maintainers :copyright: Copyright 2026 SAP SE @@ -56,12 +56,12 @@ def __init__( self._parser = Parser(gardenlinux_root, feature_dir_name, logger) self._feature_dir_name = Path(self._gardenlinux_root).joinpath(feature_dir_name) - self.all_flavors: set[str] = set() - self.reproducible_flavors: set[str] = set() + self.all_cnames: set[str] = set() + self.reproducible_cnames: set[str] = set() self.passed_by_whitelist: set[str] = set() - self.expected_falvors: set[str] = set() - self.missing_flavors: set[str] = set() - self.unexpected_falvors: set[str] = set() + self.expected_cnames: set[str] = set() + self.missing_cnames: set[str] = set() + self.unexpected_cnames: set[str] = set() def sort_features(self, graph: nx.DiGraph) -> list[str]: """ @@ -76,107 +76,107 @@ def sort_features(self, graph: nx.DiGraph) -> list[str]: def parse( self, - flavors_matrix: Dict[str, list[Dict[str, str]]], - bare_flavors_matrix: Dict[str, list[Dict[str, str]]], + cnames_matrix: Dict[str, list[Dict[str, str]]], + bare_cnames_matrix: Dict[str, list[Dict[str, str]]], diff_dir: PathLike[str] = Path("diffs"), ) -> None: """ Parses a diff result and sets the corresponding attributes - :param flavors_matrix: The flavors matrix to identify missing diff files - :param bare_flavors_matrix: The bare flavors matrix to identify missing diff files + :param cnames_matrix: The cnames matrix to identify missing diff files + :param bare_cnames_matrix: The bare cnames matrix to identify missing diff files :param diff_dir: Directory containing the diff files :since: 1.0.0 """ - self.all_flavors = set() - self.reproducible_flavors = set() + self.all_cnames = set() + self.reproducible_cnames = set() self.passed_by_whitelist = set() - non_reproducible_flavors = {} # {flavor: [files...]} + non_reproducible_cnames = {} # {cname: [files...]} diff_dir = Path(self._gardenlinux_root).joinpath(diff_dir) - self.expected_falvors = { - f"{variant['flavor']}-{variant['arch']}" - for variant in (flavors_matrix["include"] + bare_flavors_matrix["include"]) + self.expected_cnames = { + f"{variant['cname']}-{variant['arch']}" + for variant in (cnames_matrix["include"] + bare_cnames_matrix["include"]) } - for flavor in os.listdir(diff_dir): - if flavor.endswith(self._SUFFIX): - with open(diff_dir.joinpath(flavor), "r") as f: + for cname in os.listdir(diff_dir): + if cname.endswith(self._SUFFIX): + with open(diff_dir.joinpath(cname), "r") as f: content = f.read() - flavor = flavor.rstrip(self._SUFFIX) - self.all_flavors.add(flavor) + cname = cname.rstrip(self._SUFFIX) + self.all_cnames.add(cname) if content == "": - self.reproducible_flavors.add(flavor) + self.reproducible_cnames.add(cname) elif content == "whitelist\n": - self.reproducible_flavors.add(flavor) - self.passed_by_whitelist.add(flavor) + self.reproducible_cnames.add(cname) + self.passed_by_whitelist.add(cname) else: - non_reproducible_flavors[flavor] = content.split("\n")[:-1] - - self.missing_flavors = self.expected_falvors - self.all_flavors - self.unexpected_falvors = self.all_flavors - self.expected_falvors - - # Map files to flavors - affected_flavors: Dict[str, set[str]] = {} # {file: {flavors...}} - for flavor in non_reproducible_flavors: - for file in non_reproducible_flavors[flavor]: - if file not in affected_flavors: - affected_flavors[file] = set() - affected_flavors[file].add(flavor) - - # Merge files affected_flavors by the same flavors by mapping flavor sets to files - self._bundled: Dict[frozenset[str], set[str]] = {} # {{flavors...}: {files...}} - for file in affected_flavors: - if frozenset(affected_flavors[file]) not in self._bundled: - self._bundled[frozenset(affected_flavors[file])] = set() - self._bundled[frozenset(affected_flavors[file])].add(file) + non_reproducible_cnames[cname] = content.split("\n")[:-1] + + self.missing_cnames = self.expected_cnames - self.all_cnames + self.unexpected_cnames = self.all_cnames - self.expected_cnames + + # Map files to cnames + affected_cnames: Dict[str, set[str]] = {} # {file: {cnames...}} + for cname in non_reproducible_cnames: + for file in non_reproducible_cnames[cname]: + if file not in affected_cnames: + affected_cnames[file] = set() + affected_cnames[file].add(cname) + + # Merge files affected_cnames by the same cnames by mapping cname sets to files + self._bundled: Dict[frozenset[str], set[str]] = {} # {{cnames...}: {files...}} + for file in affected_cnames: + if frozenset(affected_cnames[file]) not in self._bundled: + self._bundled[frozenset(affected_cnames[file])] = set() + self._bundled[frozenset(affected_cnames[file])].add(file) def intersectionTrees( self, ) -> Dict[frozenset[str], tuple[frozenset[str], nx.DiGraph]]: """ - Intersects all features of the affected flavors and removes all features from unaffected flavors to identify features causing the issue + Intersects all features of the affected cnames and removes all features from unaffected cnames to identify features causing the issue - :return: (Dict[frozenset[str], tuple[frozenset[str], nx.DiGraph]]) Dict in the form of {{files...}: ({flavors..., intersectionTree})} + :return: (Dict[frozenset[str], tuple[frozenset[str], nx.DiGraph]]) Dict in the form of {{files...}: ({cnames..., intersectionTree})} :since: 1.0.0 """ - # Compute the intersecting features of the affected flavors and store them in a graph to allow hierarchical formatting + # Compute the intersecting features of the affected cnames and store them in a graph to allow hierarchical formatting trees = {} - for flavors in self._bundled: + for cnames in self._bundled: tree = None - # Compute the intersecting features of all affected flavors - for flavor in flavors: - # Ignore bare flavors, as they may not be affected due to removing the file and could therefore disrupt the analysis - if not flavor.startswith("bare-"): + # Compute the intersecting features of all affected cnames + for cname in cnames: + # Ignore bare cnames, as they may not be affected due to removing the file and could therefore disrupt the analysis + if not cname.startswith("bare-"): # Compute intersecting features tree = self._parser.filter( - self._remove_arch.sub("", flavor), + self._remove_arch.sub("", cname), additional_filter_func=tree.__contains__ if tree is not None else lambda _: True, ) - # Remove any features which are contained in unaffected flavors, as they cannot cause the problem + # Remove any features which are contained in unaffected cnames, as they cannot cause the problem if tree is not None: # Unfreeze tree tree = nx.DiGraph(tree) - unaffected = self.all_flavors - flavors + unaffected = self.all_cnames - cnames merged_features: set[str] = set() - for flavor in unaffected: - # Again, ignore bare flavors - if not flavor.startswith("bare-"): + for cname in unaffected: + # Again, ignore bare cnames + if not cname.startswith("bare-"): merged_features.update( - self._parser.filter(self._remove_arch.sub("", flavor)) + self._parser.filter(self._remove_arch.sub("", cname)) ) tree.remove_nodes_from(n for n in merged_features) else: tree = nx.DiGraph() - trees[frozenset(self._bundled[flavors])] = (flavors, tree) + trees[frozenset(self._bundled[cnames])] = (cnames, tree) return trees diff --git a/src/gardenlinux/features/reproducibility/markdown_formatter.py b/src/gardenlinux/features/reproducibility/markdown_formatter.py index 86e9f546..f1228618 100644 --- a/src/gardenlinux/features/reproducibility/markdown_formatter.py +++ b/src/gardenlinux/features/reproducibility/markdown_formatter.py @@ -28,7 +28,7 @@ class Nightly: class MarkdownFormatter(object): """ This class takes the diff-files results from the reproducibility check and generates a markdown of the result - The diff-files contain paths of files which were different when building the flavor two times + The diff-files contain paths of files which were different when building the cname two times They can be generated using the Comparator class :author: Garden Linux Maintainers @@ -48,8 +48,8 @@ class MarkdownFormatter(object): def __init__( self, - flavors_matrix: dict[str, list[dict[str, str]]], - bare_flavors_matrix: dict[str, list[dict[str, str]]], + cnames_matrix: dict[str, list[dict[str, str]]], + bare_cnames_matrix: dict[str, list[dict[str, str]]], diff_dir: PathLike[str] = Path("diffs"), nightly_stats: PathLike[str] = Path("nightly_stats.csv"), gardenlinux_root: Optional[str] = None, @@ -59,8 +59,8 @@ def __init__( """ Constructor __init__(MarkdownFormatter) - :param flavors_matrix: The flavors matrix to identify missing diff files - :param bare_flavors_matrix: The bare flavors matrix to identify missing diff files + :param cnames_matrix: The cnames matrix to identify missing diff files + :param bare_cnames_matrix: The bare cnames matrix to identify missing diff files :param diff_dir: Directory containing the diff-files :param nightly_stats: File containing infos about nightly runs :param gardenlinux_root: GardenLinux root directory @@ -71,7 +71,7 @@ def __init__( """ self._diff_parser = DiffParser(gardenlinux_root, feature_dir_name, logger) - self._diff_parser.parse(flavors_matrix, bare_flavors_matrix, diff_dir) + self._diff_parser.parse(cnames_matrix, bare_cnames_matrix, diff_dir) self._nightly_stats = Path(nightly_stats) @@ -189,20 +189,20 @@ def _header( successrate = round( 100 * ( - len(self._diff_parser.reproducible_flavors) - / len(self._diff_parser.expected_falvors) + len(self._diff_parser.reproducible_cnames) + / len(self._diff_parser.expected_cnames) ), 1, ) emoji = ( "✅" - if len(self._diff_parser.expected_falvors) - == len(self._diff_parser.reproducible_flavors) + if len(self._diff_parser.expected_cnames) + == len(self._diff_parser.reproducible_cnames) else ("⚠️" if successrate >= SUCCESS_TRESHOLD else "❌") ) - total_count = len(self._diff_parser.expected_falvors) + total_count = len(self._diff_parser.expected_cnames) problem_count = ( "" @@ -226,20 +226,20 @@ def _header( + "" ) - if len(self._diff_parser.unexpected_falvors) > 0: + if len(self._diff_parser.unexpected_cnames) > 0: # This should never happen, but print a warning if it somehow does explanation += ( "\n\n
⁉️ These flavors were not expected to appear in the results, please check for errors in the workflow\
"
-                + "
".join(sorted(self._diff_parser.unexpected_falvors)) + + "
".join(sorted(self._diff_parser.unexpected_cnames)) + "
" ) explanation += ( "" - if len(self._diff_parser.expected_falvors) - <= len(self._diff_parser.reproducible_flavors) - else "\n\n*The mentioned features are included in every affected flavor and not included in every unaffected flavor.*" + if len(self._diff_parser.expected_cnames) + <= len(self._diff_parser.reproducible_cnames) + else "\n\n*The mentioned features are included in every affected CName and not included in every unaffected cname.*" ) return header.format( @@ -268,25 +268,25 @@ def _table( rows = "" - if len(self._diff_parser.missing_flavors) > 0: + if len(self._diff_parser.missing_cnames) > 0: row = "|❌ Workflow run did not produce any results|" - row += f"**{round(100 * (len(self._diff_parser.missing_flavors) / len(self._diff_parser.expected_falvors)), 1)}%** affected
" - row += self._dropdown(self._diff_parser.missing_flavors) + row += f"**{round(100 * (len(self._diff_parser.missing_cnames) / len(self._diff_parser.expected_cnames)), 1)}%** affected
" + row += self._dropdown(self._diff_parser.missing_cnames) row += "|No analysis available|\n" rows += row - # Sort the problems by affected flavors in descending order and by files names for problems with the same number of affected flavors + # Sort the problems by affected cnames in descending order and by files names for problems with the same number of affected cnames # to get a derterministic ordering for testing def sorting_function(files: frozenset[str]) -> tuple[int, str]: return (-len(trees[files][0]), ",".join(sorted(files))) for files in sorted(trees, key=sorting_function): - flavors, tree = trees[files] + cnames, tree = trees[files] row = "|" row += self._dropdown(files) row += "|" - row += f"**{round(100 * (len(flavors) / len(self._diff_parser.expected_falvors)), 1)}%** affected
" - row += self._dropdown(flavors) + row += f"**{round(100 * (len(cnames) / len(self._diff_parser.expected_cnames)), 1)}%** affected
" + row += self._dropdown(cnames) row += "|" if len(tree) == 0: row += "No analysis available" @@ -295,20 +295,20 @@ def sorting_function(files: frozenset[str]) -> tuple[int, str]: row += "|\n" rows += row - if len(self._diff_parser.reproducible_flavors) > 0: + if len(self._diff_parser.reproducible_cnames) > 0: # Success row row = "|" row += "✅ No problems found" row += "|" - row += f"**{round(100 * (len(self._diff_parser.reproducible_flavors) / len(self._diff_parser.expected_falvors)), 1)}%**
" - row += self._dropdown(self._diff_parser.reproducible_flavors) + row += f"**{round(100 * (len(self._diff_parser.reproducible_cnames) / len(self._diff_parser.expected_cnames)), 1)}%**
" + row += self._dropdown(self._diff_parser.reproducible_cnames) row += "|" row += "-" row += "|\n" rows += row - if len(self._diff_parser.reproducible_flavors) < len( - self._diff_parser.expected_falvors + if len(self._diff_parser.reproducible_cnames) < len( + self._diff_parser.expected_cnames ): rows += "\n*To add affected files to the whitelist, edit `src/gardenlinux/features/reproducibility/nightly_whitelist.json` in python-gardenlinux-lib*\n" diff --git a/src/gardenlinux/features/versioned_flavor.py b/src/gardenlinux/features/versioned_flavor.py new file mode 100644 index 00000000..e3cab44d --- /dev/null +++ b/src/gardenlinux/features/versioned_flavor.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +""" +Canonical name (cname) +""" + +from typing import Optional, Self + +from .flavor import Flavor + + +class VersionedFlavor(Flavor): + """ + Class to represent a Garden Linux versioned flavor. + + :author: Garden Linux Maintainers + :copyright: Copyright 2024 SAP SE + :package: gardenlinux + :subpackage: features + :since: 1.0.0 + :license: https://www.apache.org/licenses/LICENSE-2.0 + Apache License, Version 2.0 + """ + + def __init__( + self, + flavor: str, + arch: str, + version: str, + ): + """ + Constructor __init__(VersionedFlavor) + + :param cname: Canonical name to represent + :param arch: Architecture if not part of cname + :param commit_hash: Commit ID or hash if not part of cname + :param version: Version if not part of cname + + :since: 1.0.0 + """ + + Flavor.__init__(self, flavor, arch) + + self._version = version + + @property + def version(self) -> str: + """ + Returns the version if part of the cname parsed. + + :return: (str) Version + :since: 0.7.0 + """ + + return self._version + + @property + def version_epoch(self) -> Optional[int]: + """ + Returns the GardenLinux version epoch of the cname parsed. + + :return: (str) GardenLinux version epoch + :since: 1.0.0 + """ + + epoch = None + + if self._version is not None and "." in self._version: + epoch = int(self._version.split(".", 1)[0]) + + return epoch + + def __str__(self) -> str: + """ + Returns the Garden Linux artifact base name. + + :return: (str) Returns the Garden Linux artifact base name + :since: 1.0.0 + """ + + assert self._version is not None, ( + "Can't represent a Garden Linux versioned flavor without a version" + ) + + flavor = Flavor.__str__(self) + flavor += f"-{self.version}" + + return flavor + + def _copy_from_instance(self, versioned_flavor: Self) -> None: + """ + Copies values from a given Garden Linux canonical name instance. + + :param cname_object: Garden Linux canonical name instance + + :since: 1.0.0 + """ + + Flavor._copy_from_instance(self, versioned_flavor) + + self._version = versioned_flavor.version diff --git a/src/gardenlinux/github/release/release_images_metadata.py b/src/gardenlinux/github/release/release_images_metadata.py index e1446374..569a04dd 100644 --- a/src/gardenlinux/github/release/release_images_metadata.py +++ b/src/gardenlinux/github/release/release_images_metadata.py @@ -18,7 +18,7 @@ from ...apt import DebsrcFile from ...constants import GL_DEB_REPO_BASE_URL, GLVD_BASE_URL, REQUESTS_TIMEOUTS -from ...features import CName +from ...features import ArtifactBaseName from ...flavors import Parser from ...git import Repository from ...logger import LoggerSetup @@ -153,31 +153,28 @@ def grouped_flavors_metadata( f"{flavor=} version={self._version} commitish={self._commitish}" ) - cname = CName( - flavor[1], - arch=flavor[0], - commit_hash=self._commitish, - version=self._version, + abn_object = ArtifactBaseName( + f"{flavor[1]}-{self._version}-{self._commitish}" ) try: release_object = list( s3_artifacts.bucket.objects.filter( - Prefix=f"meta/singles/{cname.cname}" + Prefix=f"meta/singles/{abn_object}" ) )[0] s3_artifacts.bucket.download_file( release_object.key, - str(Path(tmpdir, f"{cname.cname}.s3_metadata.yaml")), + str(Path(tmpdir, f"{abn_object}.s3_metadata.yaml")), ) except IndexError: self._logger.warning( - f"No artifacts found for flavor {cname.cname}, skipping..." + f"No artifacts found for flavor {abn_object}, skipping..." ) continue - with Path(tmpdir, f"{cname.cname}.s3_metadata.yaml").open("r") as file: + with Path(tmpdir, f"{abn_object}.s3_metadata.yaml").open("r") as file: s3_data = ReleaseImagesMetadata.parse_s3_metadata( yaml.load(file, Loader=yaml.SafeLoader) ) diff --git a/src/gardenlinux/oci/container.py b/src/gardenlinux/oci/container.py index f01195d7..ff090f48 100644 --- a/src/gardenlinux/oci/container.py +++ b/src/gardenlinux/oci/container.py @@ -22,7 +22,6 @@ from requests import HTTPError, Response from ..constants import OCI_IMAGE_INDEX_MEDIA_TYPE -from ..features.cname import CName from ..logger import LoggerSetup from .image_manifest import ImageManifest from .index import Index @@ -141,31 +140,32 @@ def generate_image_manifest( :since: 0.10.0 """ - cname_object = CName(cname, architecture, version) - - if architecture is None: - architecture = cname_object.arch - if version is None: - version = cname_object.version - if commit is None: - commit = cname_object.commit_id - if feature_set is None: - feature_set = cname_object.feature_set - - if commit is None: + if not commit: commit = "" manifest = ImageManifest() - manifest.version = version # type: ignore[assignment] manifest.cname = cname - manifest.arch = architecture # type: ignore[assignment] - manifest.feature_set = feature_set manifest.commit = commit + if architecture: + manifest.arch = architecture + else: + architecture = "" + + if feature_set: + manifest.feature_set = feature_set + else: + feature_set = "" + + if version: + manifest.version = version + else: + version = "" + description = ( - f"Image: {cname} " - f"Flavor: {cname_object.flavor} " + f"Image: {cname}-{architecture}-{version}-{commit}" + f"Flavor: {cname}-{architecture} " f"Architecture: {architecture} " f"Features: {feature_set} " f"Commit: {commit} " @@ -594,8 +594,8 @@ def read_manifest( else: manifest_type = ImageManifest - if architecture is None: - architecture = CName(cname, architecture, version).arch + if not architecture: + architecture = "" response = self._get_manifest_without_response_parsing( f"{self._container_version}-{cname}-{architecture}" diff --git a/src/gardenlinux/oci/image_manifest.py b/src/gardenlinux/oci/image_manifest.py index 324c16c8..ded6608a 100644 --- a/src/gardenlinux/oci/image_manifest.py +++ b/src/gardenlinux/oci/image_manifest.py @@ -13,7 +13,7 @@ from oras.defaults import annotation_title as ANNOTATION_TITLE from ..constants import GL_DISTRIBUTION_NAME, GL_REPOSITORY_URL -from ..features import CName +from ..features import Flavor from .layer import Layer from .manifest import Manifest from .platform import new_platform @@ -154,7 +154,26 @@ def flavor(self) -> str: :since: 0.7.0 """ - return CName(self.cname).flavor + return Flavor(self.cname, self.arch).flavor + + @flavor.setter + def flavor(self, value: str) -> None: + """ + Sets the GardenLinux flavor of the OCI image manifest. + + :param value: OCI image GardenLinux flavor + + :since: 1.0.0 + """ + + self._ensure_annotations_dict() + + if "-" not in value: + raise RuntimeError(f"GardenLinux flavor is invalid: {value}") + + cname, arch = value.rsplit("-", 1) + self["annotations"][ImageManifest.ANNOTATION_ARCH_KEY] = arch + self["annotations"][ImageManifest.ANNOTATION_CNAME_KEY] = cname @property def extended_dict(self) -> Dict[str, Any]: diff --git a/src/gardenlinux/s3/s3_artifacts.py b/src/gardenlinux/s3/s3_artifacts.py index ddd8c674..f25cb914 100644 --- a/src/gardenlinux/s3/s3_artifacts.py +++ b/src/gardenlinux/s3/s3_artifacts.py @@ -18,7 +18,7 @@ import yaml -from ..features import CName +from ..features import ArtifactBaseName from .bucket import Bucket @@ -67,13 +67,13 @@ def bucket(self) -> Bucket: return self._bucket def download_to_directory( - self, cname: str, artifacts_dir: PathLike[str] | str + self, artifact_base_name: str, artifacts_dir: PathLike[str] | str ) -> None: """ Download S3 artifacts to a given directory. - :param cname: Canonical name of the GardenLinux S3 artifacts - :param artifacts_dir: Path for the image artifacts + :param artifact_base_name: Artifact base name of the Garden Linux S3 artifacts + :param artifacts_dir: Path for the image artifacts :since: 0.8.0 """ @@ -84,14 +84,17 @@ def download_to_directory( raise RuntimeError(f"Artifacts directory given is invalid: {artifacts_dir}") release_object = list( - self._bucket.objects.filter(Prefix=f"meta/singles/{cname}") + self._bucket.objects.filter(Prefix=f"meta/singles/{artifact_base_name}") )[0] self._bucket.download_file( - release_object.key, str(artifacts_dir.joinpath(f"{cname}.s3_metadata.yaml")) + release_object.key, + str(artifacts_dir.joinpath(f"{artifact_base_name}.s3_metadata.yaml")), ) - for s3_object in self._bucket.objects.filter(Prefix=f"objects/{cname}").all(): + for s3_object in self._bucket.objects.filter( + Prefix=f"objects/{artifact_base_name}" + ).all(): self._bucket.download_file( s3_object.key, str(artifacts_dir.joinpath(basename(s3_object.key))) ) @@ -121,34 +124,43 @@ def upload_from_directory( release_file = artifacts_dir.joinpath(f"{base_name}.release") try: - cname_object = CName.new_from_release_file(release_file) + abn_object = ArtifactBaseName.new_from_release_file(release_file) except RuntimeError: if not release_file.exists(): raise RuntimeError( f"Release metadata file given is invalid: {release_file}" ) + base_abn_object = ArtifactBaseName(base_name) + release_config = ConfigParser(allow_unnamed_section=True) release_config.read(release_file) - cname_object = CName( - release_config.get(UNNAMED_SECTION, "GARDENLINUX_CNAME").strip("\"'"), - commit_hash=release_config.get( - UNNAMED_SECTION, "GARDENLINUX_COMMIT_ID_LONG" - ).strip("\"'"), - version=release_config.get( - UNNAMED_SECTION, "GARDENLINUX_VERSION" - ).strip("\"'"), - ) + artifact_base_name = release_config.get( + UNNAMED_SECTION, "GARDENLINUX_CNAME" + ).strip("\"'") + + artifact_base_name += "-" + base_abn_object.arch + + artifact_base_name += "-" + release_config.get( + UNNAMED_SECTION, "GARDENLINUX_VERSION" + ).strip("\"'") - if cname_object.version_and_commit_id is None: + artifact_base_name += "-" + release_config.get( + UNNAMED_SECTION, "GARDENLINUX_COMMIT_ID_LONG" + ).strip("\"'") + + abn_object = ArtifactBaseName(artifact_base_name) + + if abn_object.version_and_commit_id is None: raise RuntimeError( "Version information could not be determined from release file" ) - arch = cname_object.arch - feature_set_list = cname_object.feature_set_list + arch = abn_object.arch + feature_set_list = abn_object.feature_set_list publishing_group = "" + release_timestamp = stat(release_file).st_ctime requirements_file = artifacts_dir.joinpath(f"{base_name}.requirements") require_uefi = None @@ -163,7 +175,9 @@ def upload_from_directory( arch = requirements_config.get(UNNAMED_SECTION, "arch") if requirements_config.has_option(UNNAMED_SECTION, "publishing_group"): - publishing_group = requirements_config.get(UNNAMED_SECTION, "publishing_group") + publishing_group = requirements_config.get( + UNNAMED_SECTION, "publishing_group" + ) if requirements_config.has_option(UNNAMED_SECTION, "uefi"): require_uefi = requirements_config.getboolean(UNNAMED_SECTION, "uefi") @@ -194,13 +208,13 @@ def upload_from_directory( re_object = re.compile("[^a-zA-Z0-9\\s+\\-=.\\_:/@]") arch = re_object.sub("+", arch) - commit_id_or_hash = cname_object.commit_hash + commit_id_or_hash = abn_object.commit_hash if commit_id_or_hash is None: - commit_id_or_hash = cname_object.commit_id + commit_id_or_hash = abn_object.commit_id metadata = { - "platform": cname_object.platform, + "platform": abn_object.platform, "architecture": arch, "build_committish": commit_id_or_hash, "build_timestamp": datetime.fromtimestamp(release_timestamp), @@ -213,14 +227,14 @@ def upload_from_directory( "s3_bucket": self._bucket.name, "s3_key": f"meta/singles/{base_name}", "test_result": None, - "version": cname_object.version, + "version": abn_object.version, "paths": [], } - if cname_object.version_epoch is not None: - metadata["gardenlinux_epoch"] = cname_object.version_epoch + if abn_object.version_epoch is not None: + metadata["gardenlinux_epoch"] = abn_object.version_epoch - platform_variant = cname_object.platform_variant + platform_variant = abn_object.platform_variant if platform_variant is not None: metadata["platform_variant"] = platform_variant @@ -249,8 +263,8 @@ def upload_from_directory( s3_tags = { "architecture": arch, - "platform": re_object.sub("+", cname_object.platform), - "version": re_object.sub("+", cname_object.version), # type: ignore[arg-type] + "platform": re_object.sub("+", abn_object.platform), + "version": re_object.sub("+", abn_object.version), "committish": commit_id_or_hash, "md5sum": md5sum, "sha256sum": sha256sum, diff --git a/test-data/gardenlinux b/test-data/gardenlinux index 8c14e750..c15e9789 160000 --- a/test-data/gardenlinux +++ b/test-data/gardenlinux @@ -1 +1 @@ -Subproject commit 8c14e75011397cd6d16c1182f3377678c885b87a +Subproject commit c15e9789b16b0e64019b52b45fb6fa3b3c0f608c diff --git a/test-data/reproducibility/diff_files/compare_nightly.md b/test-data/reproducibility/diff_files/compare_nightly.md index 1f41fd03..36681ce4 100644 --- a/test-data/reproducibility/diff_files/compare_nightly.md +++ b/test-data/reproducibility/diff_files/compare_nightly.md @@ -9,11 +9,11 @@ Comparison of nightly **[#2405](https://github.com/gardenlinux/gardenlinux/actio
📃 These flavors only passed due to the nightly whitelist
aws-gardener_fips_prod-amd64
aws-gardener_fips_prod-arm64
aws-gardener_prod_tpm2_trustedboot-amd64
aws-gardener_prod_tpm2_trustedboot-arm64
-*The mentioned features are included in every affected flavor and not included in every unaffected flavor.* +*The mentioned features are included in every affected CName and not included in every unaffected cname.* | Affected Files | Flavors | Features Causing the Problem | |----------------|---------|------------------------------| -|`/etc/hostname`|**4.9%** affected
`container-amd64`
`container-arm64`
`container-pythonDev-amd64`
`container-pythonDev-arm64`|
container
| +|`/etc/hostname`|**4.9%** affected
`container-amd64`
`container-arm64`
`container-pythonDev-amd64`
`container-pythonDev-arm64`|
container:
_archgrouped
| |✅ No problems found|**95.1%**
ali-gardener_prod-amd64...`ali-gardener_prod-amd64`
`aws-gardener_fips_prod-amd64`
`aws-gardener_fips_prod-arm64`
`aws-gardener_prod-amd64`
`aws-gardener_prod-arm64`
`aws-gardener_prod_tpm2_trustedboot-amd64`
`aws-gardener_prod_tpm2_trustedboot-arm64`
`aws-gardener_prod_trustedboot-amd64`
`aws-gardener_prod_trustedboot-arm64`
`aws-gardener_prod_usi-amd64`
`aws-gardener_prod_usi-arm64`
`azure-gardener_prod-amd64`
`azure-gardener_prod-arm64`
`azure-gardener_prod_tpm2_trustedboot-amd64`
`azure-gardener_prod_tpm2_trustedboot-arm64`
`azure-gardener_prod_trustedboot-amd64`
`azure-gardener_prod_trustedboot-arm64`
`azure-gardener_prod_usi-amd64`
`azure-gardener_prod_usi-arm64`
`bare-libc-amd64`
`bare-libc-arm64`
`bare-nodejs-amd64`
`bare-nodejs-arm64`
`bare-python-amd64`
`bare-python-arm64`
`bare-sapmachine-amd64`
`bare-sapmachine-arm64`
`baremetal-capi-amd64`
`baremetal-capi-arm64`
`baremetal-gardener_prod-amd64`
`baremetal-gardener_prod-arm64`
`baremetal-gardener_prod_tpm2_trustedboot-amd64`
`baremetal-gardener_prod_tpm2_trustedboot-arm64`
`baremetal-gardener_prod_trustedboot-amd64`
`baremetal-gardener_prod_trustedboot-arm64`
`baremetal-gardener_prod_usi-amd64`
`baremetal-gardener_prod_usi-arm64`
`baremetal-gardener_pxe-amd64`
`baremetal-gardener_pxe-arm64`
`baremetal-vhost-amd64`
`baremetal-vhost-arm64`
`baremetal_pxe-amd64`
`baremetal_pxe-arm64`
`gcp-gardener_prod-amd64`
`gcp-gardener_prod-arm64`
`gcp-gardener_prod_tpm2_trustedboot-amd64`
`gcp-gardener_prod_tpm2_trustedboot-arm64`
`gcp-gardener_prod_trustedboot-amd64`
`gcp-gardener_prod_trustedboot-arm64`
`gcp-gardener_prod_usi-amd64`
`gcp-gardener_prod_usi-arm64`
`gdch-gardener_prod-amd64`
`gdch-gardener_prod-arm64`
`kvm-gardener_prod-amd64`
`kvm-gardener_prod-arm64`
`kvm-gardener_prod_tpm2_trustedboot-amd64`
`kvm-gardener_prod_tpm2_trustedboot-arm64`
`kvm-gardener_prod_trustedboot-amd64`
`kvm-gardener_prod_trustedboot-arm64`
`kvm-gardener_prod_usi-amd64`
`kvm-gardener_prod_usi-arm64`
`lima-amd64`
`lima-arm64`
`openstack-gardener_prod-amd64`
`openstack-gardener_prod-arm64`
`openstack-gardener_prod_tpm2_trustedboot-amd64`
`openstack-gardener_prod_tpm2_trustedboot-arm64`
`openstack-gardener_prod_trustedboot-amd64`
`openstack-gardener_prod_trustedboot-arm64`
`openstack-gardener_prod_usi-amd64`
`openstack-gardener_prod_usi-arm64`
`openstack-metal-gardener_prod-amd64`
`openstack-metal-gardener_prod-arm64`
`openstack-metal-gardener_prod_usi-amd64`
`openstack-metal-gardener_prod_usi-arm64`
`vmware-gardener_prod-amd64`
`vmware-gardener_prod-arm64`
|-| *To add affected files to the whitelist, edit `src/gardenlinux/features/reproducibility/nightly_whitelist.json` in python-gardenlinux-lib* diff --git a/test-data/reproducibility/diff_files/compare_nightly_different_commit.md b/test-data/reproducibility/diff_files/compare_nightly_different_commit.md index e6d3a430..5d42a49a 100644 --- a/test-data/reproducibility/diff_files/compare_nightly_different_commit.md +++ b/test-data/reproducibility/diff_files/compare_nightly_different_commit.md @@ -11,11 +11,11 @@ Comparison of nightly **[#2405](https://github.com/gardenlinux/gardenlinux/actio
📃 These flavors only passed due to the nightly whitelist
aws-gardener_fips_prod-amd64
aws-gardener_fips_prod-arm64
aws-gardener_prod_tpm2_trustedboot-amd64
aws-gardener_prod_tpm2_trustedboot-arm64
-*The mentioned features are included in every affected flavor and not included in every unaffected flavor.* +*The mentioned features are included in every affected CName and not included in every unaffected cname.* | Affected Files | Flavors | Features Causing the Problem | |----------------|---------|------------------------------| -|`/etc/hostname`|**4.9%** affected
`container-amd64`
`container-arm64`
`container-pythonDev-amd64`
`container-pythonDev-arm64`|
container
| +|`/etc/hostname`|**4.9%** affected
`container-amd64`
`container-arm64`
`container-pythonDev-amd64`
`container-pythonDev-arm64`|
container:
_archgrouped
| |✅ No problems found|**95.1%**
ali-gardener_prod-amd64...`ali-gardener_prod-amd64`
`aws-gardener_fips_prod-amd64`
`aws-gardener_fips_prod-arm64`
`aws-gardener_prod-amd64`
`aws-gardener_prod-arm64`
`aws-gardener_prod_tpm2_trustedboot-amd64`
`aws-gardener_prod_tpm2_trustedboot-arm64`
`aws-gardener_prod_trustedboot-amd64`
`aws-gardener_prod_trustedboot-arm64`
`aws-gardener_prod_usi-amd64`
`aws-gardener_prod_usi-arm64`
`azure-gardener_prod-amd64`
`azure-gardener_prod-arm64`
`azure-gardener_prod_tpm2_trustedboot-amd64`
`azure-gardener_prod_tpm2_trustedboot-arm64`
`azure-gardener_prod_trustedboot-amd64`
`azure-gardener_prod_trustedboot-arm64`
`azure-gardener_prod_usi-amd64`
`azure-gardener_prod_usi-arm64`
`bare-libc-amd64`
`bare-libc-arm64`
`bare-nodejs-amd64`
`bare-nodejs-arm64`
`bare-python-amd64`
`bare-python-arm64`
`bare-sapmachine-amd64`
`bare-sapmachine-arm64`
`baremetal-capi-amd64`
`baremetal-capi-arm64`
`baremetal-gardener_prod-amd64`
`baremetal-gardener_prod-arm64`
`baremetal-gardener_prod_tpm2_trustedboot-amd64`
`baremetal-gardener_prod_tpm2_trustedboot-arm64`
`baremetal-gardener_prod_trustedboot-amd64`
`baremetal-gardener_prod_trustedboot-arm64`
`baremetal-gardener_prod_usi-amd64`
`baremetal-gardener_prod_usi-arm64`
`baremetal-gardener_pxe-amd64`
`baremetal-gardener_pxe-arm64`
`baremetal-vhost-amd64`
`baremetal-vhost-arm64`
`baremetal_pxe-amd64`
`baremetal_pxe-arm64`
`gcp-gardener_prod-amd64`
`gcp-gardener_prod-arm64`
`gcp-gardener_prod_tpm2_trustedboot-amd64`
`gcp-gardener_prod_tpm2_trustedboot-arm64`
`gcp-gardener_prod_trustedboot-amd64`
`gcp-gardener_prod_trustedboot-arm64`
`gcp-gardener_prod_usi-amd64`
`gcp-gardener_prod_usi-arm64`
`gdch-gardener_prod-amd64`
`gdch-gardener_prod-arm64`
`kvm-gardener_prod-amd64`
`kvm-gardener_prod-arm64`
`kvm-gardener_prod_tpm2_trustedboot-amd64`
`kvm-gardener_prod_tpm2_trustedboot-arm64`
`kvm-gardener_prod_trustedboot-amd64`
`kvm-gardener_prod_trustedboot-arm64`
`kvm-gardener_prod_usi-amd64`
`kvm-gardener_prod_usi-arm64`
`lima-amd64`
`lima-arm64`
`openstack-gardener_prod-amd64`
`openstack-gardener_prod-arm64`
`openstack-gardener_prod_tpm2_trustedboot-amd64`
`openstack-gardener_prod_tpm2_trustedboot-arm64`
`openstack-gardener_prod_trustedboot-amd64`
`openstack-gardener_prod_trustedboot-arm64`
`openstack-gardener_prod_usi-amd64`
`openstack-gardener_prod_usi-arm64`
`openstack-metal-gardener_prod-amd64`
`openstack-metal-gardener_prod-arm64`
`openstack-metal-gardener_prod_usi-amd64`
`openstack-metal-gardener_prod_usi-arm64`
`vmware-gardener_prod-amd64`
`vmware-gardener_prod-arm64`
|-| *To add affected files to the whitelist, edit `src/gardenlinux/features/reproducibility/nightly_whitelist.json` in python-gardenlinux-lib* diff --git a/test-data/reproducibility/diff_files/different_commit.md b/test-data/reproducibility/diff_files/different_commit.md index c23088c5..6704c9cb 100644 --- a/test-data/reproducibility/diff_files/different_commit.md +++ b/test-data/reproducibility/diff_files/different_commit.md @@ -9,11 +9,11 @@ Comparison of the latest nightly **[#2365](https://github.com/gardenlinux/garden ⚠️ The build used different commits: `ed1934d` (#2365) != `095c739` (new build) -*The mentioned features are included in every affected flavor and not included in every unaffected flavor.* +*The mentioned features are included in every affected CName and not included in every unaffected cname.* | Affected Files | Flavors | Features Causing the Problem | |----------------|---------|------------------------------| -|`/etc/hostname`|**4.9%** affected
`container-amd64`
`container-arm64`
`container-pythonDev-amd64`
`container-pythonDev-arm64`|
container
| +|`/etc/hostname`|**4.9%** affected
`container-amd64`
`container-arm64`
`container-pythonDev-amd64`
`container-pythonDev-arm64`|
container:
_archgrouped
| |✅ No problems found|**95.1%**
ali-gardener_prod-amd64...`ali-gardener_prod-amd64`
`aws-gardener_fips_prod-amd64`
`aws-gardener_fips_prod-arm64`
`aws-gardener_prod-amd64`
`aws-gardener_prod-arm64`
`aws-gardener_prod_tpm2_trustedboot-amd64`
`aws-gardener_prod_tpm2_trustedboot-arm64`
`aws-gardener_prod_trustedboot-amd64`
`aws-gardener_prod_trustedboot-arm64`
`aws-gardener_prod_usi-amd64`
`aws-gardener_prod_usi-arm64`
`azure-gardener_prod-amd64`
`azure-gardener_prod-arm64`
`azure-gardener_prod_tpm2_trustedboot-amd64`
`azure-gardener_prod_tpm2_trustedboot-arm64`
`azure-gardener_prod_trustedboot-amd64`
`azure-gardener_prod_trustedboot-arm64`
`azure-gardener_prod_usi-amd64`
`azure-gardener_prod_usi-arm64`
`bare-libc-amd64`
`bare-libc-arm64`
`bare-nodejs-amd64`
`bare-nodejs-arm64`
`bare-python-amd64`
`bare-python-arm64`
`bare-sapmachine-amd64`
`bare-sapmachine-arm64`
`baremetal-capi-amd64`
`baremetal-capi-arm64`
`baremetal-gardener_prod-amd64`
`baremetal-gardener_prod-arm64`
`baremetal-gardener_prod_tpm2_trustedboot-amd64`
`baremetal-gardener_prod_tpm2_trustedboot-arm64`
`baremetal-gardener_prod_trustedboot-amd64`
`baremetal-gardener_prod_trustedboot-arm64`
`baremetal-gardener_prod_usi-amd64`
`baremetal-gardener_prod_usi-arm64`
`baremetal-gardener_pxe-amd64`
`baremetal-gardener_pxe-arm64`
`baremetal-vhost-amd64`
`baremetal-vhost-arm64`
`baremetal_pxe-amd64`
`baremetal_pxe-arm64`
`gcp-gardener_prod-amd64`
`gcp-gardener_prod-arm64`
`gcp-gardener_prod_tpm2_trustedboot-amd64`
`gcp-gardener_prod_tpm2_trustedboot-arm64`
`gcp-gardener_prod_trustedboot-amd64`
`gcp-gardener_prod_trustedboot-arm64`
`gcp-gardener_prod_usi-amd64`
`gcp-gardener_prod_usi-arm64`
`gdch-gardener_prod-amd64`
`gdch-gardener_prod-arm64`
`kvm-gardener_prod-amd64`
`kvm-gardener_prod-arm64`
`kvm-gardener_prod_tpm2_trustedboot-amd64`
`kvm-gardener_prod_tpm2_trustedboot-arm64`
`kvm-gardener_prod_trustedboot-amd64`
`kvm-gardener_prod_trustedboot-arm64`
`kvm-gardener_prod_usi-amd64`
`kvm-gardener_prod_usi-arm64`
`lima-amd64`
`lima-arm64`
`openstack-gardener_prod-amd64`
`openstack-gardener_prod-arm64`
`openstack-gardener_prod_tpm2_trustedboot-amd64`
`openstack-gardener_prod_tpm2_trustedboot-arm64`
`openstack-gardener_prod_trustedboot-amd64`
`openstack-gardener_prod_trustedboot-arm64`
`openstack-gardener_prod_usi-amd64`
`openstack-gardener_prod_usi-arm64`
`openstack-metal-gardener_prod-amd64`
`openstack-metal-gardener_prod-arm64`
`openstack-metal-gardener_prod_usi-amd64`
`openstack-metal-gardener_prod_usi-arm64`
`vmware-gardener_prod-amd64`
`vmware-gardener_prod-arm64`
|-| *To add affected files to the whitelist, edit `src/gardenlinux/features/reproducibility/nightly_whitelist.json` in python-gardenlinux-lib* diff --git a/test-data/reproducibility/diff_files/files_missing.md b/test-data/reproducibility/diff_files/files_missing.md index 81e249a3..e7aa370f 100644 --- a/test-data/reproducibility/diff_files/files_missing.md +++ b/test-data/reproducibility/diff_files/files_missing.md @@ -5,7 +5,7 @@ ## Detailed Result -*The mentioned features are included in every affected flavor and not included in every unaffected flavor.* +*The mentioned features are included in every affected CName and not included in every unaffected cname.* | Affected Files | Flavors | Features Causing the Problem | |----------------|---------|------------------------------| diff --git a/test-data/reproducibility/diff_files/one_issue.md b/test-data/reproducibility/diff_files/one_issue.md index 5c09d2ba..0069ae2a 100644 --- a/test-data/reproducibility/diff_files/one_issue.md +++ b/test-data/reproducibility/diff_files/one_issue.md @@ -5,11 +5,11 @@ ## Detailed Result -*The mentioned features are included in every affected flavor and not included in every unaffected flavor.* +*The mentioned features are included in every affected CName and not included in every unaffected cname.* | Affected Files | Flavors | Features Causing the Problem | |----------------|---------|------------------------------| -|`/etc/hostname`|**4.9%** affected
`container-amd64`
`container-arm64`
`container-pythonDev-amd64`
`container-pythonDev-arm64`|
container
| +|`/etc/hostname`|**4.9%** affected
`container-amd64`
`container-arm64`
`container-pythonDev-amd64`
`container-pythonDev-arm64`|
container:
_archgrouped
| |✅ No problems found|**95.1%**
ali-gardener_prod-amd64...`ali-gardener_prod-amd64`
`aws-gardener_fips_prod-amd64`
`aws-gardener_fips_prod-arm64`
`aws-gardener_prod-amd64`
`aws-gardener_prod-arm64`
`aws-gardener_prod_tpm2_trustedboot-amd64`
`aws-gardener_prod_tpm2_trustedboot-arm64`
`aws-gardener_prod_trustedboot-amd64`
`aws-gardener_prod_trustedboot-arm64`
`aws-gardener_prod_usi-amd64`
`aws-gardener_prod_usi-arm64`
`azure-gardener_prod-amd64`
`azure-gardener_prod-arm64`
`azure-gardener_prod_tpm2_trustedboot-amd64`
`azure-gardener_prod_tpm2_trustedboot-arm64`
`azure-gardener_prod_trustedboot-amd64`
`azure-gardener_prod_trustedboot-arm64`
`azure-gardener_prod_usi-amd64`
`azure-gardener_prod_usi-arm64`
`bare-libc-amd64`
`bare-libc-arm64`
`bare-nodejs-amd64`
`bare-nodejs-arm64`
`bare-python-amd64`
`bare-python-arm64`
`bare-sapmachine-amd64`
`bare-sapmachine-arm64`
`baremetal-capi-amd64`
`baremetal-capi-arm64`
`baremetal-gardener_prod-amd64`
`baremetal-gardener_prod-arm64`
`baremetal-gardener_prod_tpm2_trustedboot-amd64`
`baremetal-gardener_prod_tpm2_trustedboot-arm64`
`baremetal-gardener_prod_trustedboot-amd64`
`baremetal-gardener_prod_trustedboot-arm64`
`baremetal-gardener_prod_usi-amd64`
`baremetal-gardener_prod_usi-arm64`
`baremetal-gardener_pxe-amd64`
`baremetal-gardener_pxe-arm64`
`baremetal-vhost-amd64`
`baremetal-vhost-arm64`
`baremetal_pxe-amd64`
`baremetal_pxe-arm64`
`gcp-gardener_prod-amd64`
`gcp-gardener_prod-arm64`
`gcp-gardener_prod_tpm2_trustedboot-amd64`
`gcp-gardener_prod_tpm2_trustedboot-arm64`
`gcp-gardener_prod_trustedboot-amd64`
`gcp-gardener_prod_trustedboot-arm64`
`gcp-gardener_prod_usi-amd64`
`gcp-gardener_prod_usi-arm64`
`gdch-gardener_prod-amd64`
`gdch-gardener_prod-arm64`
`kvm-gardener_prod-amd64`
`kvm-gardener_prod-arm64`
`kvm-gardener_prod_tpm2_trustedboot-amd64`
`kvm-gardener_prod_tpm2_trustedboot-arm64`
`kvm-gardener_prod_trustedboot-amd64`
`kvm-gardener_prod_trustedboot-arm64`
`kvm-gardener_prod_usi-amd64`
`kvm-gardener_prod_usi-arm64`
`lima-amd64`
`lima-arm64`
`openstack-gardener_prod-amd64`
`openstack-gardener_prod-arm64`
`openstack-gardener_prod_tpm2_trustedboot-amd64`
`openstack-gardener_prod_tpm2_trustedboot-arm64`
`openstack-gardener_prod_trustedboot-amd64`
`openstack-gardener_prod_trustedboot-arm64`
`openstack-gardener_prod_usi-amd64`
`openstack-gardener_prod_usi-arm64`
`openstack-metal-gardener_prod-amd64`
`openstack-metal-gardener_prod-arm64`
`openstack-metal-gardener_prod_usi-amd64`
`openstack-metal-gardener_prod_usi-arm64`
`vmware-gardener_prod-amd64`
`vmware-gardener_prod-arm64`
|-| *To add affected files to the whitelist, edit `src/gardenlinux/features/reproducibility/nightly_whitelist.json` in python-gardenlinux-lib* diff --git a/test-data/reproducibility/diff_files/only_issues.md b/test-data/reproducibility/diff_files/only_issues.md index ec25f991..2cfacbfb 100644 --- a/test-data/reproducibility/diff_files/only_issues.md +++ b/test-data/reproducibility/diff_files/only_issues.md @@ -5,7 +5,7 @@ ## Detailed Result -*The mentioned features are included in every affected flavor and not included in every unaffected flavor.* +*The mentioned features are included in every affected CName and not included in every unaffected cname.* | Affected Files | Flavors | Features Causing the Problem | |----------------|---------|------------------------------| diff --git a/tests/features/constants.py b/tests/features/constants.py index e99ac286..aceeec26 100644 --- a/tests/features/constants.py +++ b/tests/features/constants.py @@ -18,11 +18,11 @@ def generate_container_amd64_release_metadata(version: str, commit_hash: str) -> HOME_URL="{GL_HOME_URL}" SUPPORT_URL="{GL_SUPPORT_URL}" BUG_REPORT_URL="{GL_BUG_REPORT_URL}" -GARDENLINUX_CNAME="container-amd64-{version}-{commit_hash}" -GARDENLINUX_FEATURES="_slim,base,container" +GARDENLINUX_CNAME="container" +GARDENLINUX_FEATURES="_archgrouped,_slim,base,container" GARDENLINUX_FEATURES_PLATFORMS="container" GARDENLINUX_FEATURES_ELEMENTS="base" -GARDENLINUX_FEATURES_FLAGS="_slim" +GARDENLINUX_FEATURES_FLAGS="_archgrouped,_slim" GARDENLINUX_PLATFORM="container" GARDENLINUX_PLATFORM_VARIANT="" GARDENLINUX_VERSION="{version}" diff --git a/tests/features/test_cname.py b/tests/features/test_cname.py index 3d173844..13b13b95 100644 --- a/tests/features/test_cname.py +++ b/tests/features/test_cname.py @@ -19,12 +19,12 @@ "metal_pxe", ), ( - "container-amd64", + "container", "container", ), ], ) -def test_cname_flavor(input_cname: str, expected_output: str) -> None: +def test_cname(input_cname: str, expected_output: str) -> None: """ Tests if cname returns the dict with expected features. @@ -32,19 +32,6 @@ def test_cname_flavor(input_cname: str, expected_output: str) -> None: features have changed since writing this test. In this case, update the expected output accordingly. You can print the output of cname so you have the dict in the expected format. """ - cname = CName(input_cname) - assert cname.flavor == expected_output - - -def test_cname_commit_id_setter() -> None: - """ - Tests cname setter for `commit_id` to verify a given ID before overwriting. - """ - cname = CName("container", arch="amd64", version="today", commit_hash="local") - - # Act / Assert - with pytest.raises( - RuntimeError, match="Commit hash given differs from commit ID already set" - ): - cname.commit_hash = "broken" + cname = CName(input_cname) + assert str(cname) == expected_output diff --git a/tests/features/test_cname_main.py b/tests/features/test_cname_main.py deleted file mode 100644 index 5a40c5fb..00000000 --- a/tests/features/test_cname_main.py +++ /dev/null @@ -1,159 +0,0 @@ -import logging -import sys -import types -from typing import Any, List, Tuple - -import networkx -import pytest - -import gardenlinux.features.cname_main as cname_main -from gardenlinux.features import Parser - - -def test_main_happy( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] -) -> None: - """ - Test the "Happy Path" of the main() function. - """ - # Arrange - argv = ["prog", "--arch", "amd64", "--version", "1.0-abc123", "flav-amd64"] - monkeypatch.setattr(sys, "argv", argv) - - class FakeGraph: - def in_degree(self) -> List[Tuple[str, int]]: - return [("f1", 0)] - - edges = [("f1", "f2")] - - class FakeParser(Parser): - def __init__(self, *a: Any, **k: Any): - pass - - def filter(self, *a: Any, **k: Any) -> networkx.Graph: - return FakeGraph() - - @staticmethod - def sort_graph_nodes(graph: networkx.Graph) -> List[str]: - return ["f1", "f2"] - - monkeypatch.setattr(cname_main, "Parser", FakeParser) - - # Act - cname_main.main() - - # Assert - out = capsys.readouterr().out - assert "f1" in out - assert "amd64" in out - - -def test_main_version_from_file( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] -) -> None: - """ - "Happy Path" test for grabbing the version and commit id from file in main(). - """ - # Arrange - argv = ["prog", "--arch", "amd64", "flav-amd64"] - monkeypatch.setattr(sys, "argv", argv) - - monkeypatch.setattr( - cname_main, - "get_version_and_commit_id_from_files", - lambda root: ("2.0", "abcdef12"), - ) - - class FakeParser(Parser): - def __init__(self, *a: Any, **k: Any): - pass - - def filter(self, *a: Any, **k: Any) -> networkx.Graph: - return types.SimpleNamespace(in_degree=lambda: [("f1", 0)], edges=[]) - - @staticmethod - def sort_graph_nodes(graph: networkx.Graph) -> List[str]: - return ["f1"] - - monkeypatch.setattr(cname_main, "Parser", FakeParser) - - # Act - cname_main.main() - - # Assert - assert "2.0-abcdef12" in capsys.readouterr().out - - -def test_cname_main_version_file_missing_warns( - monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture -) -> None: - """ - Check if a warning is logged when it fails to read version and commit id files. - - Specifically, this test simulates a scenario where the helper function - `get_version_and_commit_id_from_files` raises a RuntimeError, which would occur - if the expected version or commit files are missing or unreadable. - """ - # Arrange - argv = ["prog", "--arch", "amd64", "flav-amd64"] - monkeypatch.setattr(sys, "argv", argv) - - # Patch version fatch function to raise RuntimeError (Simulates missing files) - def raise_runtime(*args: Any, **kwargs: Any) -> None: - raise RuntimeError("missing") - - monkeypatch.setattr( - cname_main, "get_version_and_commit_id_from_files", raise_runtime - ) - - # Patch Parser for minimal valid graph - class FakeParser(Parser): - def __init__(self, *a: Any, **k: Any): - pass - - # Return object with in_degree method returning a node with zero dependencies - def filter(self, *a: Any, **k: Any) -> networkx.Graph: - return types.SimpleNamespace(in_degree=lambda: [("f1", 0)], edges=[]) - - @staticmethod - def sort_graph_nodes(graph: networkx.Graph) -> List[str]: - return ["f1"] - - monkeypatch.setattr(cname_main, "Parser", FakeParser) - - # Capture any logs with WARNING level - caplog.set_level(logging.WARNING) - - # Act - cname_main.main() - - # Assert - assert "Failed to parse version information" in caplog.text - - -def test_cname_main_invalid_cname_raises(monkeypatch: pytest.MonkeyPatch) -> None: - """ - Test if AssertionError is raised with an invalid or malformed cname. - """ - # Arrange - argv = ["prog", "--arch", "amd64", "--version", "1.0", "INVALID@NAME"] - monkeypatch.setattr(sys, "argv", argv) - - # Act / Assert - with pytest.raises(AssertionError): - cname_main.main() - - -def test_cname_main_missing_arch_in_cname_raises( - monkeypatch: pytest.MonkeyPatch, -) -> None: - """ - Test if an assertion error is raised when the arch argument is missing. - """ - # Arrange - argv = ["prog", "--version", "1.0", "flav"] - monkeypatch.setattr(sys, "argv", argv) - - # Act / Assert - with pytest.raises(AssertionError): - cname_main.main() diff --git a/tests/features/test_main.py b/tests/features/test_main.py index a0df4326..44d1adf8 100644 --- a/tests/features/test_main.py +++ b/tests/features/test_main.py @@ -7,7 +7,7 @@ import pytest import gardenlinux.features.__main__ as fema -from gardenlinux.features import CName +from gardenlinux.features import ArtifactBaseName from ..constants import GL_ROOT_DIR from .constants import generate_container_amd64_release_metadata @@ -40,7 +40,7 @@ class MockGraph: # Act / Assert with pytest.raises( - RuntimeError, match="Error while generating graph: Flavor is None!" + RuntimeError, match="Error while generating graph: CName is None!" ): fema.graph_as_mermaid_markup(None, MockGraph()) @@ -68,7 +68,9 @@ def test_get_version_and_commit_from_file(tmp_path: Path) -> None: version_file.write_text("1.2.3\n") # Act - version, commit = fema.get_version_and_commit_id_from_files(str(tmp_path)) + version, commit = ArtifactBaseName.get_version_and_commit_id_from_files( + str(tmp_path) + ) # Arrange assert version == "1.2.3" @@ -80,8 +82,10 @@ def test_get_version_missing_file_raises(tmp_path: Path) -> None: (tmp_path / "COMMIT").write_text("abcdef1234\n") # Act / Assert - with pytest.raises(RuntimeError): - fema.get_version_and_commit_id_from_files(str(tmp_path)) + assert ArtifactBaseName.get_version_and_commit_id_from_files(str(tmp_path)) == ( + None, + None, + ) # ------------------------------- @@ -91,7 +95,18 @@ def test_main_prints_arch( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange - argv = ["prog", "--arch", "amd64", "--cname", "flav", "--version", "1.0", "arch"] + argv = [ + "prog", + "--arch", + "amd64", + "--cname", + "flav", + "--version", + "1.0", + "--commit", + "local", + "arch", + ] monkeypatch.setattr(sys, "argv", argv) monkeypatch.setattr(fema, "Parser", lambda *a, **kw: None) @@ -115,6 +130,8 @@ def test_main_prints_container_name( "container-pythonDev", "--version", "1.0", + "--commit", + "local", "container_name", ] monkeypatch.setattr(sys, "argv", argv) @@ -141,7 +158,7 @@ def test_main_prints_container_tag( "--version", "1.0", "--commit", - "~post1", + "post1", "container_tag", ] monkeypatch.setattr(sys, "argv", argv) @@ -168,7 +185,8 @@ def test_main_prints_commit_id( ) # Patch get_version_and_commit_id_from_files monkeypatch.setattr( - fema, "get_version_and_commit_id_from_files", lambda root: ("1.2.3", "abcdef12") + "gardenlinux.features.artifact_base_name.ArtifactBaseName.get_version_and_commit_id_from_files", + lambda root: ("1.2.3", "abcdef12"), ) # Act @@ -190,16 +208,20 @@ def test_main_prints_flags_elements_platforms( "flav", "--version", "1.0", + "--commit", + "local", "flags", ] monkeypatch.setattr(sys, "argv", argv) - class FakeCName(CName): + class TestArtifactBaseName(ArtifactBaseName): def __init__(self, *a: Any, **k: Any): - CName.__init__(self, *a, **k) - self._feature_flags_cached = ["flag1"] + ArtifactBaseName.__init__(self, *a, **k) + self._features_cached = {"flag": ["flag1"]} - monkeypatch.setattr(fema, "CName", FakeCName) + monkeypatch.setattr( + "gardenlinux.features.artifact_base_name.ArtifactBaseName", TestArtifactBaseName + ) # Act fema.main() @@ -222,7 +244,8 @@ def test_main_prints_version( ) # Patch get_version_and_commit_id_from_files monkeypatch.setattr( - fema, "get_version_and_commit_id_from_files", lambda root: ("1.2.3", "abcdef12") + "gardenlinux.features.artifact_base_name.ArtifactBaseName.get_version_and_commit_id_from_files", + lambda root: ("1.2.3", "abcdef12"), ) # Act @@ -245,7 +268,8 @@ def test_main_prints_version_and_commit_id( ) # Patch get_version_and_commit_id_from_files monkeypatch.setattr( - fema, "get_version_and_commit_id_from_files", lambda root: ("1.2.3", "abcdef12") + "gardenlinux.features.artifact_base_name.ArtifactBaseName.get_version_and_commit_id_from_files", + lambda root: ("1.2.3", "abcdef12"), ) # Act @@ -261,7 +285,10 @@ def test_main_requires_cname(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(fema, "Parser", lambda *a, **kw: None) # Act / Assert - with pytest.raises(SystemExit): + with pytest.raises( + ValueError, + match="Argument missing: At least one of artifact_base_name, cname, flavor or versioned_flavor", + ): fema.main() @@ -272,7 +299,7 @@ def test_main_cname_raises_missing_commit_id(monkeypatch: pytest.MonkeyPatch) -> "prog", "--cname", "flav", - "--default-arch", + "--arch", "amd64", "--version", "1.0", @@ -281,7 +308,7 @@ def test_main_cname_raises_missing_commit_id(monkeypatch: pytest.MonkeyPatch) -> monkeypatch.setattr(sys, "argv", argv) # Act / Assert - with pytest.raises(RuntimeError, match="Version and commit ID"): + with pytest.raises(ValueError, match="Argument missing: version"): fema.main() @@ -292,7 +319,7 @@ def test_main_raises_no_arch_no_default(monkeypatch: pytest.MonkeyPatch) -> None monkeypatch.setattr(sys, "argv", argv) # Act / Assert - with pytest.raises(RuntimeError, match="Architecture could not be determined"): + with pytest.raises(ValueError, match="Argument missing: arch"): fema.main() @@ -314,7 +341,7 @@ def test_main_raises_missing_commit_id( monkeypatch.setattr(fema, "Parser", lambda *a, **kw: None) # Act / Assert - with pytest.raises(RuntimeError, match="Commit ID not specified"): + with pytest.raises(ValueError, match="Argument missing: version"): fema.main() @@ -389,7 +416,7 @@ def test_main_with_exclude_cname_print_features( ) -def test_cname_release_file( +def test_artifact_base_name_release_file( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: """ @@ -404,11 +431,11 @@ def test_cname_release_file( argv = [ "prog", - "--cname", + "--artifact-base-name", "container-amd64-today-local", "--release-file", str(os_release_file), - "cname", + "artifact_base_name", ] monkeypatch.setattr(sys, "argv", argv) diff --git a/tests/features/test_metadata_main.py b/tests/features/test_metadata_main.py index b8f0fc69..b93918d5 100644 --- a/tests/features/test_metadata_main.py +++ b/tests/features/test_metadata_main.py @@ -18,7 +18,7 @@ def test_main_output( # Arrange argv = [ "prog", - "--cname", + "--flavor", "container-amd64", "--version", "today", @@ -33,7 +33,7 @@ def test_main_output( # Assert expected = generate_container_amd64_release_metadata("today", "local") - assert expected == capsys.readouterr().out.strip() + assert capsys.readouterr().out.strip() == expected def test_main_write( @@ -47,7 +47,7 @@ def test_main_write( os_release_file = Path(tmpdir, "os_release") argv = [ "prog", - "--cname", + "--flavor", "container-amd64", "--version", "today", @@ -80,7 +80,7 @@ def test_main_validation(monkeypatch: pytest.MonkeyPatch) -> None: argv = [ "prog", - "--cname", + "--flavor", "base-python-amd64", "--version", "today", diff --git a/tests/features/test_parser.py b/tests/features/test_parser.py index 7bb49fe3..0b23976c 100644 --- a/tests/features/test_parser.py +++ b/tests/features/test_parser.py @@ -132,21 +132,21 @@ def test_parser_return_intersection_subset() -> None: assert result == ["a", "c"] -def test_get_flavor_from_feature_set() -> None: +def test_get_cname_from_feature_set() -> None: # Arrange sorted_features = ["base", "_hidden", "extra"] # Act - result = Parser.get_flavor_from_feature_set(sorted_features) + result = Parser.get_cname_from_feature_set(sorted_features) # Assert assert result == "base_hidden-extra" -def test_gget_flavor_from_feature_set_empty_raises() -> None: +def test_gget_cname_from_feature_set_empty_raises() -> None: # get_flavor with empty iterable raises TypeError with pytest.raises(TypeError): - Parser.get_flavor_from_feature_set([]) + Parser.get_cname_from_feature_set([]) def test_parser_subset_nomatch() -> None: diff --git a/tests/features/test_reproducibility.py b/tests/features/test_reproducibility.py index 7cc5cdd2..0a99d466 100644 --- a/tests/features/test_reproducibility.py +++ b/tests/features/test_reproducibility.py @@ -8,94 +8,94 @@ from gardenlinux.features.reproducibility.comparator import Comparator from gardenlinux.features.reproducibility.markdown_formatter import MarkdownFormatter -FLAVORS_MATRIX = { +CNAMES_MATRIX = { "include": [ - {"arch": "amd64", "flavor": "ali-gardener_prod"}, - {"arch": "amd64", "flavor": "aws-gardener_fips_prod"}, - {"arch": "amd64", "flavor": "aws-gardener_prod"}, - {"arch": "amd64", "flavor": "aws-gardener_prod_tpm2_trustedboot"}, - {"arch": "amd64", "flavor": "aws-gardener_prod_trustedboot"}, - {"arch": "amd64", "flavor": "aws-gardener_prod_usi"}, - {"arch": "amd64", "flavor": "azure-gardener_prod"}, - {"arch": "amd64", "flavor": "azure-gardener_prod_tpm2_trustedboot"}, - {"arch": "amd64", "flavor": "azure-gardener_prod_trustedboot"}, - {"arch": "amd64", "flavor": "azure-gardener_prod_usi"}, - {"arch": "amd64", "flavor": "baremetal-capi"}, - {"arch": "amd64", "flavor": "baremetal-gardener_prod"}, - {"arch": "amd64", "flavor": "baremetal-gardener_prod_tpm2_trustedboot"}, - {"arch": "amd64", "flavor": "baremetal-gardener_prod_trustedboot"}, - {"arch": "amd64", "flavor": "baremetal-gardener_prod_usi"}, - {"arch": "amd64", "flavor": "baremetal-gardener_pxe"}, - {"arch": "amd64", "flavor": "baremetal-vhost"}, - {"arch": "amd64", "flavor": "baremetal_pxe"}, - {"arch": "amd64", "flavor": "container"}, - {"arch": "amd64", "flavor": "container-pythonDev"}, - {"arch": "amd64", "flavor": "gcp-gardener_prod"}, - {"arch": "amd64", "flavor": "gcp-gardener_prod_tpm2_trustedboot"}, - {"arch": "amd64", "flavor": "gcp-gardener_prod_trustedboot"}, - {"arch": "amd64", "flavor": "gcp-gardener_prod_usi"}, - {"arch": "amd64", "flavor": "gdch-gardener_prod"}, - {"arch": "amd64", "flavor": "kvm-gardener_prod"}, - {"arch": "amd64", "flavor": "kvm-gardener_prod_tpm2_trustedboot"}, - {"arch": "amd64", "flavor": "kvm-gardener_prod_trustedboot"}, - {"arch": "amd64", "flavor": "kvm-gardener_prod_usi"}, - {"arch": "amd64", "flavor": "lima"}, - {"arch": "amd64", "flavor": "openstack-gardener_prod"}, - {"arch": "amd64", "flavor": "openstack-gardener_prod_tpm2_trustedboot"}, - {"arch": "amd64", "flavor": "openstack-gardener_prod_trustedboot"}, - {"arch": "amd64", "flavor": "openstack-gardener_prod_usi"}, - {"arch": "amd64", "flavor": "openstack-metal-gardener_prod"}, - {"arch": "amd64", "flavor": "openstack-metal-gardener_prod_usi"}, - {"arch": "amd64", "flavor": "vmware-gardener_prod"}, - {"arch": "arm64", "flavor": "aws-gardener_fips_prod"}, - {"arch": "arm64", "flavor": "aws-gardener_prod"}, - {"arch": "arm64", "flavor": "aws-gardener_prod_tpm2_trustedboot"}, - {"arch": "arm64", "flavor": "aws-gardener_prod_trustedboot"}, - {"arch": "arm64", "flavor": "aws-gardener_prod_usi"}, - {"arch": "arm64", "flavor": "azure-gardener_prod"}, - {"arch": "arm64", "flavor": "azure-gardener_prod_tpm2_trustedboot"}, - {"arch": "arm64", "flavor": "azure-gardener_prod_trustedboot"}, - {"arch": "arm64", "flavor": "azure-gardener_prod_usi"}, - {"arch": "arm64", "flavor": "baremetal-capi"}, - {"arch": "arm64", "flavor": "baremetal-gardener_prod"}, - {"arch": "arm64", "flavor": "baremetal-gardener_prod_tpm2_trustedboot"}, - {"arch": "arm64", "flavor": "baremetal-gardener_prod_trustedboot"}, - {"arch": "arm64", "flavor": "baremetal-gardener_prod_usi"}, - {"arch": "arm64", "flavor": "baremetal-gardener_pxe"}, - {"arch": "arm64", "flavor": "baremetal-vhost"}, - {"arch": "arm64", "flavor": "baremetal_pxe"}, - {"arch": "arm64", "flavor": "container"}, - {"arch": "arm64", "flavor": "container-pythonDev"}, - {"arch": "arm64", "flavor": "gcp-gardener_prod"}, - {"arch": "arm64", "flavor": "gcp-gardener_prod_tpm2_trustedboot"}, - {"arch": "arm64", "flavor": "gcp-gardener_prod_trustedboot"}, - {"arch": "arm64", "flavor": "gcp-gardener_prod_usi"}, - {"arch": "arm64", "flavor": "gdch-gardener_prod"}, - {"arch": "arm64", "flavor": "kvm-gardener_prod"}, - {"arch": "arm64", "flavor": "kvm-gardener_prod_tpm2_trustedboot"}, - {"arch": "arm64", "flavor": "kvm-gardener_prod_trustedboot"}, - {"arch": "arm64", "flavor": "kvm-gardener_prod_usi"}, - {"arch": "arm64", "flavor": "lima"}, - {"arch": "arm64", "flavor": "openstack-gardener_prod"}, - {"arch": "arm64", "flavor": "openstack-gardener_prod_tpm2_trustedboot"}, - {"arch": "arm64", "flavor": "openstack-gardener_prod_trustedboot"}, - {"arch": "arm64", "flavor": "openstack-gardener_prod_usi"}, - {"arch": "arm64", "flavor": "openstack-metal-gardener_prod"}, - {"arch": "arm64", "flavor": "openstack-metal-gardener_prod_usi"}, - {"arch": "arm64", "flavor": "vmware-gardener_prod"}, + {"arch": "amd64", "cname": "ali-gardener_prod"}, + {"arch": "amd64", "cname": "aws-gardener_fips_prod"}, + {"arch": "amd64", "cname": "aws-gardener_prod"}, + {"arch": "amd64", "cname": "aws-gardener_prod_tpm2_trustedboot"}, + {"arch": "amd64", "cname": "aws-gardener_prod_trustedboot"}, + {"arch": "amd64", "cname": "aws-gardener_prod_usi"}, + {"arch": "amd64", "cname": "azure-gardener_prod"}, + {"arch": "amd64", "cname": "azure-gardener_prod_tpm2_trustedboot"}, + {"arch": "amd64", "cname": "azure-gardener_prod_trustedboot"}, + {"arch": "amd64", "cname": "azure-gardener_prod_usi"}, + {"arch": "amd64", "cname": "baremetal-capi"}, + {"arch": "amd64", "cname": "baremetal-gardener_prod"}, + {"arch": "amd64", "cname": "baremetal-gardener_prod_tpm2_trustedboot"}, + {"arch": "amd64", "cname": "baremetal-gardener_prod_trustedboot"}, + {"arch": "amd64", "cname": "baremetal-gardener_prod_usi"}, + {"arch": "amd64", "cname": "baremetal-gardener_pxe"}, + {"arch": "amd64", "cname": "baremetal-vhost"}, + {"arch": "amd64", "cname": "baremetal_pxe"}, + {"arch": "amd64", "cname": "container"}, + {"arch": "amd64", "cname": "container-pythonDev"}, + {"arch": "amd64", "cname": "gcp-gardener_prod"}, + {"arch": "amd64", "cname": "gcp-gardener_prod_tpm2_trustedboot"}, + {"arch": "amd64", "cname": "gcp-gardener_prod_trustedboot"}, + {"arch": "amd64", "cname": "gcp-gardener_prod_usi"}, + {"arch": "amd64", "cname": "gdch-gardener_prod"}, + {"arch": "amd64", "cname": "kvm-gardener_prod"}, + {"arch": "amd64", "cname": "kvm-gardener_prod_tpm2_trustedboot"}, + {"arch": "amd64", "cname": "kvm-gardener_prod_trustedboot"}, + {"arch": "amd64", "cname": "kvm-gardener_prod_usi"}, + {"arch": "amd64", "cname": "lima"}, + {"arch": "amd64", "cname": "openstack-gardener_prod"}, + {"arch": "amd64", "cname": "openstack-gardener_prod_tpm2_trustedboot"}, + {"arch": "amd64", "cname": "openstack-gardener_prod_trustedboot"}, + {"arch": "amd64", "cname": "openstack-gardener_prod_usi"}, + {"arch": "amd64", "cname": "openstack-metal-gardener_prod"}, + {"arch": "amd64", "cname": "openstack-metal-gardener_prod_usi"}, + {"arch": "amd64", "cname": "vmware-gardener_prod"}, + {"arch": "arm64", "cname": "aws-gardener_fips_prod"}, + {"arch": "arm64", "cname": "aws-gardener_prod"}, + {"arch": "arm64", "cname": "aws-gardener_prod_tpm2_trustedboot"}, + {"arch": "arm64", "cname": "aws-gardener_prod_trustedboot"}, + {"arch": "arm64", "cname": "aws-gardener_prod_usi"}, + {"arch": "arm64", "cname": "azure-gardener_prod"}, + {"arch": "arm64", "cname": "azure-gardener_prod_tpm2_trustedboot"}, + {"arch": "arm64", "cname": "azure-gardener_prod_trustedboot"}, + {"arch": "arm64", "cname": "azure-gardener_prod_usi"}, + {"arch": "arm64", "cname": "baremetal-capi"}, + {"arch": "arm64", "cname": "baremetal-gardener_prod"}, + {"arch": "arm64", "cname": "baremetal-gardener_prod_tpm2_trustedboot"}, + {"arch": "arm64", "cname": "baremetal-gardener_prod_trustedboot"}, + {"arch": "arm64", "cname": "baremetal-gardener_prod_usi"}, + {"arch": "arm64", "cname": "baremetal-gardener_pxe"}, + {"arch": "arm64", "cname": "baremetal-vhost"}, + {"arch": "arm64", "cname": "baremetal_pxe"}, + {"arch": "arm64", "cname": "container"}, + {"arch": "arm64", "cname": "container-pythonDev"}, + {"arch": "arm64", "cname": "gcp-gardener_prod"}, + {"arch": "arm64", "cname": "gcp-gardener_prod_tpm2_trustedboot"}, + {"arch": "arm64", "cname": "gcp-gardener_prod_trustedboot"}, + {"arch": "arm64", "cname": "gcp-gardener_prod_usi"}, + {"arch": "arm64", "cname": "gdch-gardener_prod"}, + {"arch": "arm64", "cname": "kvm-gardener_prod"}, + {"arch": "arm64", "cname": "kvm-gardener_prod_tpm2_trustedboot"}, + {"arch": "arm64", "cname": "kvm-gardener_prod_trustedboot"}, + {"arch": "arm64", "cname": "kvm-gardener_prod_usi"}, + {"arch": "arm64", "cname": "lima"}, + {"arch": "arm64", "cname": "openstack-gardener_prod"}, + {"arch": "arm64", "cname": "openstack-gardener_prod_tpm2_trustedboot"}, + {"arch": "arm64", "cname": "openstack-gardener_prod_trustedboot"}, + {"arch": "arm64", "cname": "openstack-gardener_prod_usi"}, + {"arch": "arm64", "cname": "openstack-metal-gardener_prod"}, + {"arch": "arm64", "cname": "openstack-metal-gardener_prod_usi"}, + {"arch": "arm64", "cname": "vmware-gardener_prod"}, ] } -BARE_FLAVORS_MATRIX = { +BARE_CNAMES_MATRIX = { "include": [ - {"arch": "amd64", "flavor": "bare-libc"}, - {"arch": "amd64", "flavor": "bare-nodejs"}, - {"arch": "amd64", "flavor": "bare-python"}, - {"arch": "amd64", "flavor": "bare-sapmachine"}, - {"arch": "arm64", "flavor": "bare-libc"}, - {"arch": "arm64", "flavor": "bare-nodejs"}, - {"arch": "arm64", "flavor": "bare-python"}, - {"arch": "arm64", "flavor": "bare-sapmachine"}, + {"arch": "amd64", "cname": "bare-libc"}, + {"arch": "amd64", "cname": "bare-nodejs"}, + {"arch": "amd64", "cname": "bare-python"}, + {"arch": "amd64", "cname": "bare-sapmachine"}, + {"arch": "arm64", "cname": "bare-libc"}, + {"arch": "arm64", "cname": "bare-nodejs"}, + {"arch": "arm64", "cname": "bare-python"}, + {"arch": "arm64", "cname": "bare-sapmachine"}, ] } @@ -110,16 +110,16 @@ def test_formatter(i: str) -> None: if nightly_stats.is_file(): formatter = MarkdownFormatter( - FLAVORS_MATRIX, - BARE_FLAVORS_MATRIX, + CNAMES_MATRIX, + BARE_CNAMES_MATRIX, diff_files.joinpath(i), gardenlinux_root=str(gardenlinux_root), nightly_stats=nightly_stats, ) else: formatter = MarkdownFormatter( - FLAVORS_MATRIX, - BARE_FLAVORS_MATRIX, + CNAMES_MATRIX, + BARE_CNAMES_MATRIX, diff_files.joinpath(i), gardenlinux_root=str(gardenlinux_root), ) @@ -143,8 +143,8 @@ def test_formatter_main( str(gardenlinux_root.joinpath("features")), "--diff-dir", str(diff_files.joinpath(i)), - json.dumps(FLAVORS_MATRIX), - json.dumps(BARE_FLAVORS_MATRIX), + json.dumps(CNAMES_MATRIX), + json.dumps(BARE_CNAMES_MATRIX), ] if nightly_stats.is_file(): diff --git a/tests/oci/test_image_manifest.py b/tests/oci/test_image_manifest.py index 4ac89691..3d744c52 100644 --- a/tests/oci/test_image_manifest.py +++ b/tests/oci/test_image_manifest.py @@ -22,10 +22,16 @@ def test_ImageManifest_arch() -> None: def test_ImageManifest_cname() -> None: # Arrange - cname = "container-amd64-today-local" + arch = "amd64" + cname = "container" empty_manifest = ImageManifest() - manifest = ImageManifest(annotations={ImageManifest.ANNOTATION_CNAME_KEY: cname}) + manifest = ImageManifest( + annotations={ + ImageManifest.ANNOTATION_CNAME_KEY: cname, + ImageManifest.ANNOTATION_ARCH_KEY: arch, + } + ) # Assert with pytest.raises(RuntimeError): @@ -58,18 +64,25 @@ def test_ImageManifest_feature_set() -> None: def test_ImageManifest_flavor() -> None: # Arrange - flavor = "container" - cname = f"{flavor}-amd64-today-local" + arch = "amd64" + cname = "container" + flavor = f"{cname}-{arch}" empty_manifest = ImageManifest() - manifest = ImageManifest(annotations={ImageManifest.ANNOTATION_CNAME_KEY: cname}) + manifest = ImageManifest( + annotations={ + ImageManifest.ANNOTATION_CNAME_KEY: cname, + ImageManifest.ANNOTATION_ARCH_KEY: arch, + } + ) # Assert with pytest.raises(RuntimeError): - assert empty_manifest.flavor == flavor + assert empty_manifest.cname == cname - empty_manifest.cname = cname - assert empty_manifest.flavor == flavor + empty_manifest.flavor = flavor + assert empty_manifest.cname == cname + assert empty_manifest.arch == arch assert manifest.flavor == flavor diff --git a/tests/s3/conftest.py b/tests/s3/conftest.py index 8ba1fa09..9219fd06 100644 --- a/tests/s3/conftest.py +++ b/tests/s3/conftest.py @@ -20,10 +20,10 @@ class S3Env: s3: S3ServiceResource bucket_name: str tmp_path: Path - cname: str + artifact_base_name: str -def make_cname( +def make_artifact_base_name( flavor: str = "container_trustedboot_usi", arch: str = "amd64", version: str = "1234.1", @@ -64,5 +64,5 @@ def s3_setup( monkeypatch.setattr("gardenlinux.s3.s3_artifacts.file_digest", dummy_digest) - cname = make_cname() - yield S3Env(s3, BUCKET_NAME, tmp_path, cname) + artifact_base_name = make_artifact_base_name() + yield S3Env(s3, BUCKET_NAME, tmp_path, artifact_base_name) diff --git a/tests/s3/constants.py b/tests/s3/constants.py index 7dc6e02b..4f747cdf 100644 --- a/tests/s3/constants.py +++ b/tests/s3/constants.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- RELEASE_DATA = """ -GARDENLINUX_CNAME="container_trustedboot_usi-amd64-1234.1" +VARIANT_ID="container_trustedboot_usi-amd64" +GARDENLINUX_CNAME="container_trustedboot_usi" GARDENLINUX_VERSION=1234.1 GARDENLINUX_COMMIT_ID="abc123lo" GARDENLINUX_COMMIT_ID_LONG="abc123long" diff --git a/tests/s3/test_main.py b/tests/s3/test_main.py index 0d116bf5..e84505ab 100644 --- a/tests/s3/test_main.py +++ b/tests/s3/test_main.py @@ -85,10 +85,10 @@ def test_main_with_expected_result( str(env.tmp_path), "upload-artifacts-to-bucket", "--artifact-name", - env.cname, + env.artifact_base_name, ], ): - release_path = env.tmp_path / f"{env.cname}.release" + release_path = env.tmp_path / f"{env.artifact_base_name}.release" release_path.write_text(RELEASE_DATA) s3m.main() diff --git a/tests/s3/test_s3_artifacts.py b/tests/s3/test_s3_artifacts.py index 2d5eda59..645a99d0 100644 --- a/tests/s3/test_s3_artifacts.py +++ b/tests/s3/test_s3_artifacts.py @@ -37,19 +37,21 @@ def test_download_to_directory_success(s3_setup: S3Env) -> None: env = s3_setup bucket = env.s3.Bucket(env.bucket_name) - bucket.put_object(Key=f"meta/singles/{env.cname}", Body=b"metadata") - bucket.put_object(Key=f"objects/{env.cname}/file1", Body=b"data1") - bucket.put_object(Key=f"objects/{env.cname}/file2", Body=b"data2") + bucket.put_object(Key=f"meta/singles/{env.artifact_base_name}", Body=b"metadata") + bucket.put_object(Key=f"objects/{env.artifact_base_name}/file1", Body=b"data1") + bucket.put_object(Key=f"objects/{env.artifact_base_name}/file2", Body=b"data2") with TemporaryDirectory() as tmpdir: outdir = Path(tmpdir) # Act artifacts = S3Artifacts(env.bucket_name) - artifacts.download_to_directory(env.cname, outdir) + artifacts.download_to_directory(env.artifact_base_name, outdir) # Assert - assert (outdir / f"{env.cname}.s3_metadata.yaml").read_bytes() == b"metadata" + assert ( + outdir / f"{env.artifact_base_name}.s3_metadata.yaml" + ).read_bytes() == b"metadata" assert (outdir / "file1").read_bytes() == b"data1" assert (outdir / "file2").read_bytes() == b"data2" @@ -64,7 +66,9 @@ def test_download_to_directory_invalid_path(s3_setup: S3Env) -> None: # Act / Assert with pytest.raises(RuntimeError): - artifacts.download_to_directory(env.cname, "/invalid/path/does/not/exist") + artifacts.download_to_directory( + env.artifact_base_name, "/invalid/path/does/not/exist" + ) def test_download_to_directory_non_pathlike_raises(s3_setup: S3Env) -> None: @@ -72,7 +76,7 @@ def test_download_to_directory_non_pathlike_raises(s3_setup: S3Env) -> None: env = s3_setup artifacts = S3Artifacts(env.bucket_name) with pytest.raises(RuntimeError): - artifacts.download_to_directory(env.cname, "nopath") + artifacts.download_to_directory(env.artifact_base_name, "nopath") def test_download_to_directory_no_metadata_raises(s3_setup: S3Env) -> None: @@ -84,7 +88,7 @@ def test_download_to_directory_no_metadata_raises(s3_setup: S3Env) -> None: # Act / Assert with TemporaryDirectory() as tmpdir: with pytest.raises(IndexError): - artifacts.download_to_directory(env.cname, tmpdir) + artifacts.download_to_directory(env.artifact_base_name, tmpdir) def test_upload_from_directory_success(s3_setup: S3Env) -> None: @@ -94,30 +98,37 @@ def test_upload_from_directory_success(s3_setup: S3Env) -> None: # Arrange env = s3_setup - release_path = env.tmp_path / f"{env.cname}.release" + release_path = env.tmp_path / f"{env.artifact_base_name}.release" release_path.write_text(RELEASE_DATA) - for filename in [f"{env.cname}-file1", f"{env.cname}-file2", "container"]: + for filename in [ + f"{env.artifact_base_name}-file1", + f"{env.artifact_base_name}-file2", + "container", + ]: (env.tmp_path / filename).write_bytes(b"dummy content") # Act artifacts = S3Artifacts(env.bucket_name) - artifacts.upload_from_directory(env.cname, env.tmp_path) + artifacts.upload_from_directory(env.artifact_base_name, env.tmp_path) # Assert bucket = env.s3.Bucket(env.bucket_name) keys = [obj.key for obj in bucket.objects.all()] - assert f"objects/{env.cname}/{env.cname}-file1" in keys - assert f"objects/{env.cname}/{env.cname}-file2" in keys - assert f"meta/singles/{env.cname}" in keys + assert f"objects/{env.artifact_base_name}/{env.artifact_base_name}-file1" in keys + assert f"objects/{env.artifact_base_name}/{env.artifact_base_name}-file2" in keys + assert f"meta/singles/{env.artifact_base_name}" in keys - meta_obj = list(bucket.objects.filter(Prefix=f"meta/singles/{env.cname}"))[0] + meta_obj = list( + bucket.objects.filter(Prefix=f"meta/singles/{env.artifact_base_name}") + )[0] metadata = yaml.safe_load(meta_obj.get()["Body"].read()) assert metadata["require_uefi"] is True assert metadata["secureboot"] is True raw_tags_response = env.s3.meta.client.get_object_tagging( - Bucket=env.bucket_name, Key=f"objects/{env.cname}/{env.cname}-file1" + Bucket=env.bucket_name, + Key=f"objects/{env.artifact_base_name}/{env.artifact_base_name}-file1", ) tags = {tag["Key"]: tag["Value"] for tag in raw_tags_response["TagSet"]} assert tags["platform"] == "container" @@ -132,28 +143,34 @@ def test_upload_from_directory_with_delete(s3_setup: S3Env) -> None: bucket = env.s3.Bucket(env.bucket_name) # Arrange: create release and artifact files locally - release = env.tmp_path / f"{env.cname}.release" + release = env.tmp_path / f"{env.artifact_base_name}.release" release.write_text(RELEASE_DATA) - artifact = env.tmp_path / f"{env.cname}.kernel" + artifact = env.tmp_path / f"{env.artifact_base_name}.kernel" artifact.write_bytes(b"fake") # Arrange: put dummy existing objects to be deleted - bucket.put_object(Key=f"objects/{env.cname}/{artifact.name}", Body=b"old data") - bucket.put_object(Key=f"meta/singles/{env.cname}", Body=b"old metadata") + bucket.put_object( + Key=f"objects/{env.artifact_base_name}/{artifact.name}", Body=b"old data" + ) + bucket.put_object( + Key=f"meta/singles/{env.artifact_base_name}", Body=b"old metadata" + ) artifacts = S3Artifacts(env.bucket_name) # Act - artifacts.upload_from_directory(env.cname, env.tmp_path, delete_before_push=True) + artifacts.upload_from_directory( + env.artifact_base_name, env.tmp_path, delete_before_push=True + ) # Assert keys = [obj.key for obj in bucket.objects.all()] # The old key should no longer be present as old data (no duplicates) # but the new upload file key should exist (artifact uploaded) - assert f"objects/{env.cname}/{artifact.name}" in keys - assert f"meta/singles/{env.cname}" in keys + assert f"objects/{env.artifact_base_name}/{artifact.name}" in keys + assert f"meta/singles/{env.artifact_base_name}" in keys def test_upload_from_directory_invalid_dir_raises(s3_setup: S3Env) -> None: @@ -161,7 +178,7 @@ def test_upload_from_directory_invalid_dir_raises(s3_setup: S3Env) -> None: env = s3_setup artifacts = S3Artifacts(env.bucket_name) with pytest.raises(RuntimeError, match="invalid"): - artifacts.upload_from_directory(env.cname, "/invalid/path") + artifacts.upload_from_directory(env.artifact_base_name, "/invalid/path") def test_upload_from_directory_version_mismatch(s3_setup: S3Env) -> None: @@ -170,13 +187,13 @@ def test_upload_from_directory_version_mismatch(s3_setup: S3Env) -> None: """ # Arrange env = s3_setup - release_path = env.tmp_path / f"{env.cname}.release" + release_path = env.tmp_path / f"{env.artifact_base_name}.release" bad_data = RELEASE_DATA.replace("1234.1", "9999.9") release_path.write_text(bad_data) artifacts = S3Artifacts(env.bucket_name) # Act / Assert - artifacts.upload_from_directory(env.cname, env.tmp_path) + artifacts.upload_from_directory(env.artifact_base_name, env.tmp_path) def test_upload_from_directory_succeeds_because_of_release_file( @@ -199,7 +216,7 @@ def test_upload_from_directory_invalid_artifact_name(s3_setup: S3Env) -> None: """ # Arrange env = s3_setup - (env.tmp_path / f"{env.cname}.release").write_text(RELEASE_DATA) + (env.tmp_path / f"{env.artifact_base_name}.release").write_text(RELEASE_DATA) # Create "bad" artifact that does not start with cname bad_file = env.tmp_path / "no_match" @@ -208,11 +225,16 @@ def test_upload_from_directory_invalid_artifact_name(s3_setup: S3Env) -> None: artifacts = S3Artifacts(env.bucket_name) # Act - artifacts.upload_from_directory(env.cname, env.tmp_path) + artifacts.upload_from_directory(env.artifact_base_name, env.tmp_path) # Assert bucket = env.s3.Bucket(env.bucket_name) - assert len(list(bucket.objects.filter(Prefix=f"meta/singles/{env.cname}"))) == 1 + assert ( + len( + list(bucket.objects.filter(Prefix=f"meta/singles/{env.artifact_base_name}")) + ) + == 1 + ) def test_upload_from_directory_invalid_release_file_with_valid_cname( @@ -223,7 +245,7 @@ def test_upload_from_directory_invalid_release_file_with_valid_cname( """ # Arrange env = s3_setup - release_path = env.tmp_path / f"{env.cname}.release" + release_path = env.tmp_path / f"{env.artifact_base_name}.release" bad_data = RELEASE_DATA.replace( "GARDENLINUX_FEATURES_PLATFORMS=", "GARDENLINUX_FEATURES_PLATFORMS_UNDEFINED=" ) @@ -232,11 +254,16 @@ def test_upload_from_directory_invalid_release_file_with_valid_cname( artifacts = S3Artifacts(env.bucket_name) # Act - artifacts.upload_from_directory(env.cname, env.tmp_path) + artifacts.upload_from_directory(env.artifact_base_name, env.tmp_path) # Assert bucket = env.s3.Bucket(env.bucket_name) - assert len(list(bucket.objects.filter(Prefix=f"meta/singles/{env.cname}"))) == 1 + assert ( + len( + list(bucket.objects.filter(Prefix=f"meta/singles/{env.artifact_base_name}")) + ) + == 1 + ) def test_upload_from_directory_commit_mismatch(s3_setup: S3Env) -> None: @@ -245,13 +272,13 @@ def test_upload_from_directory_commit_mismatch(s3_setup: S3Env) -> None: """ # Arrange env = s3_setup - release_path = env.tmp_path / f"{env.cname}.release" + release_path = env.tmp_path / f"{env.artifact_base_name}.release" bad_data = RELEASE_DATA.replace("abc123", "wrong") release_path.write_text(bad_data) artifacts = S3Artifacts(env.bucket_name) # Act / Assert - artifacts.upload_from_directory(env.cname, env.tmp_path) + artifacts.upload_from_directory(env.artifact_base_name, env.tmp_path) def test_upload_from_directory_with_platform_variant(s3_setup: S3Env) -> None: @@ -260,20 +287,23 @@ def test_upload_from_directory_with_platform_variant(s3_setup: S3Env) -> None: """ # Arrange env = s3_setup - release_path = env.tmp_path / f"{env.cname}.release" + release_path = env.tmp_path / f"{env.artifact_base_name}.release" release_path.write_text( RELEASE_DATA.strip() + "\nGARDENLINUX_PLATFORM_VARIANT=test" ) + print(release_path.read_text()) # Act artifacts = S3Artifacts(env.bucket_name) - artifacts.upload_from_directory(env.cname, env.tmp_path) + artifacts.upload_from_directory(env.artifact_base_name, env.tmp_path) # Assert bucket = env.s3.Bucket(env.bucket_name) meta_obj = next( - o for o in bucket.objects.all() if o.key == f"meta/singles/{env.cname}" + o + for o in bucket.objects.all() + if o.key == f"meta/singles/{env.artifact_base_name}" ) metadata = yaml.safe_load(meta_obj.get()["Body"].read()) assert metadata["platform_variant"] == "test" @@ -283,21 +313,23 @@ def test_upload_directory_with_requirements_override(s3_setup: S3Env) -> None: """Ensure .requirements file values overide feature flag defaults.""" # Arrange env = s3_setup - (env.tmp_path / f"{env.cname}.release").write_text(RELEASE_DATA) - (env.tmp_path / f"{env.cname}.requirements").write_text( + (env.tmp_path / f"{env.artifact_base_name}.release").write_text(RELEASE_DATA) + (env.tmp_path / f"{env.artifact_base_name}.requirements").write_text( "uefi = false\nsecureboot = true\n" ) - artifact_file = env.tmp_path / f"{env.cname}-artifact" + artifact_file = env.tmp_path / f"{env.artifact_base_name}-artifact" artifact_file.write_bytes(b"abc") # Act artifacts = S3Artifacts(env.bucket_name) - artifacts.upload_from_directory(env.cname, env.tmp_path) + artifacts.upload_from_directory(env.artifact_base_name, env.tmp_path) # Assert bucket = env.s3.Bucket(env.bucket_name) meta_obj = next( - o for o in bucket.objects.all() if o.key == f"meta/singles/{env.cname}" + o + for o in bucket.objects.all() + if o.key == f"meta/singles/{env.artifact_base_name}" ) metadata = yaml.safe_load(meta_obj.get()["Body"].read()) assert metadata["require_uefi"] is False