From 7f210248f3fe552237b91683e2c514b893de28be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:00:11 +0000 Subject: [PATCH 1/4] Initial plan From 993b796db37262f693e04716a06a13d0f05708e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:05:06 +0000 Subject: [PATCH 2/4] Add OpenTofu provider integration test Co-authored-by: jonesbusy <825750+jonesbusy@users.noreply.github.com> --- src/test/java/land/oras/OpenTofuITCase.java | 88 +++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/test/java/land/oras/OpenTofuITCase.java diff --git a/src/test/java/land/oras/OpenTofuITCase.java b/src/test/java/land/oras/OpenTofuITCase.java new file mode 100644 index 00000000..6691aedb --- /dev/null +++ b/src/test/java/land/oras/OpenTofuITCase.java @@ -0,0 +1,88 @@ +/*- + * =LICENSE= + * ORAS Java SDK + * === + * Copyright (C) 2024 - 2026 ORAS + * === + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * =LICENSEEND= + */ + +package land.oras; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import land.oras.utils.ZotUnsecureContainer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +@Execution(ExecutionMode.CONCURRENT) +class OpenTofuITCase { + + @Container + private final ZotUnsecureContainer unsecureRegistry = new ZotUnsecureContainer().withStartupAttempts(3); + + /** + * This test demonstrates how to assemble an OpenTofu provider OCI artifact. + * See https://opentofu.org/docs/cli/oci_registries/provider-mirror/ for the specification. + */ + @Test + void shouldAssembleOpenTofuProviderArtifact() { + + // The provider zip archive for linux/amd64 + Path providerZip = Paths.get("src/test/resources/archives") + .resolve("terraform-provider-aws_5.0.0_linux_amd64.zip"); + String configMediaType = "application/vnd.opentofu.provider"; + String contentMediaType = "application/zip"; + + // Create objects for the linux/amd64 platform manifest + Config config = Config.empty().withMediaType(configMediaType); + Layer layer = Layer.fromFile(providerZip).withMediaType(contentMediaType); + Manifest manifest = Manifest.empty().withConfig(config).withLayers(List.of(layer)); + + // Push config, layer and manifest to registry + Registry registry = Registry.builder() + .insecure() + .withRegistry(unsecureRegistry.getRegistry()) + .build(); + + // The manifest is pushed without a version tag; the index will be tagged + ContainerRef manifestRef = ContainerRef.parse("terraform-provider-aws:5.0.0-linux-amd64"); + registry.pushConfig(manifestRef, config); + registry.pushBlob(manifestRef, providerZip); + Manifest pushedManifest = registry.pushManifest(manifestRef, manifest); + + // Ensure the manifest was pushed successfully + assertNotNull(pushedManifest); + + // Build the index with the manifest descriptor, annotated with the linux/amd64 platform + ManifestDescriptor manifestDescriptor = + pushedManifest.getDescriptor().withPlatform(Platform.linuxAmd64()); + Index index = Index.fromManifests(List.of(manifestDescriptor)); + + // Push the index with the provider version tag + ContainerRef indexRef = ContainerRef.parse("terraform-provider-aws:5.0.0"); + Index pushedIndex = registry.pushIndex(indexRef, index); + + // Ensure the index was pushed and contains the linux/amd64 manifest + assertNotNull(pushedIndex); + assertNotNull(pushedIndex.findUnique(Platform.linuxAmd64())); + } +} From a4d575696415862288f4776078fb3defbb9b1028 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:05:34 +0000 Subject: [PATCH 3/4] Add OpenTofu provider zip test resource Co-authored-by: jonesbusy <825750+jonesbusy@users.noreply.github.com> --- .../terraform-provider-aws_5.0.0_linux_amd64.zip | Bin 0 -> 275 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/test/resources/archives/terraform-provider-aws_5.0.0_linux_amd64.zip diff --git a/src/test/resources/archives/terraform-provider-aws_5.0.0_linux_amd64.zip b/src/test/resources/archives/terraform-provider-aws_5.0.0_linux_amd64.zip new file mode 100644 index 0000000000000000000000000000000000000000..c4b9c11914c5ed1d0106d5d3ee771a419beff0f9 GIT binary patch literal 275 zcmWIWW@h1H00HZa@R-fnt+RE3Y!KF9kYOlEEhOQh)>K-F*6Ab;bdSwe#1(>M{KoyBal}cOz-i%Cg%(xsR0dgk;BM@(C1hI&B9xKFoXif|8 SW@Q7J!w7_VK)N5qVE_QkWk)jr literal 0 HcmV?d00001 From 9bf596969bd5fd91ac8e709678d4d7e3a6f082d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:11:55 +0000 Subject: [PATCH 4/4] Use artifactType for Manifest and Index instead of config mediaType Co-authored-by: jonesbusy <825750+jonesbusy@users.noreply.github.com> --- src/main/java/land/oras/Index.java | 10 ++++++++++ src/test/java/land/oras/IndexTest.java | 11 +++++++++++ src/test/java/land/oras/OpenTofuITCase.java | 13 +++++++++---- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/land/oras/Index.java b/src/main/java/land/oras/Index.java index f0cbef31..6cf4a0ff 100644 --- a/src/main/java/land/oras/Index.java +++ b/src/main/java/land/oras/Index.java @@ -230,6 +230,16 @@ public Subject getSubject() { return subject; } + /** + * Return a new index with the given artifact type + * @param artifactType The artifact type + * @return The index + */ + public Index withArtifactType(String artifactType) { + return new Index( + schemaVersion, mediaType, artifactType, manifests, annotations, subject, descriptor, registry, json); + } + /** * Return a new index with the given subject * @param subject The subject diff --git a/src/test/java/land/oras/IndexTest.java b/src/test/java/land/oras/IndexTest.java index 261f098e..174383a4 100644 --- a/src/test/java/land/oras/IndexTest.java +++ b/src/test/java/land/oras/IndexTest.java @@ -147,6 +147,17 @@ void shouldAddManifests() { assertNull(notFound); } + @Test + void shouldAddArtifactType() { + Index index = Index.fromManifests(List.of()); + index = index.withArtifactType("application/vnd.opentofu.provider"); + assertNotNull(index.getArtifactType()); + assertEquals("application/vnd.opentofu.provider", index.getArtifactType().getMediaType()); + assertEquals( + "{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1+json\",\"artifactType\":\"application/vnd.opentofu.provider\",\"manifests\":[]}", + index.toJson()); + } + @Test void shouldAddSubject() { Index index = Index.fromManifests(List.of()); diff --git a/src/test/java/land/oras/OpenTofuITCase.java b/src/test/java/land/oras/OpenTofuITCase.java index 6691aedb..eddb4aa8 100644 --- a/src/test/java/land/oras/OpenTofuITCase.java +++ b/src/test/java/land/oras/OpenTofuITCase.java @@ -49,13 +49,17 @@ void shouldAssembleOpenTofuProviderArtifact() { // The provider zip archive for linux/amd64 Path providerZip = Paths.get("src/test/resources/archives") .resolve("terraform-provider-aws_5.0.0_linux_amd64.zip"); - String configMediaType = "application/vnd.opentofu.provider"; + String artifactType = "application/vnd.opentofu.provider"; String contentMediaType = "application/zip"; // Create objects for the linux/amd64 platform manifest - Config config = Config.empty().withMediaType(configMediaType); + // The artifact type identifies the manifest as an OpenTofu provider; config is left empty + Config config = Config.empty(); Layer layer = Layer.fromFile(providerZip).withMediaType(contentMediaType); - Manifest manifest = Manifest.empty().withConfig(config).withLayers(List.of(layer)); + Manifest manifest = Manifest.empty() + .withArtifactType(ArtifactType.from(artifactType)) + .withConfig(config) + .withLayers(List.of(layer)); // Push config, layer and manifest to registry Registry registry = Registry.builder() @@ -73,9 +77,10 @@ void shouldAssembleOpenTofuProviderArtifact() { assertNotNull(pushedManifest); // Build the index with the manifest descriptor, annotated with the linux/amd64 platform + // The index artifact type also identifies the whole provider release as an OpenTofu provider ManifestDescriptor manifestDescriptor = pushedManifest.getDescriptor().withPlatform(Platform.linuxAmd64()); - Index index = Index.fromManifests(List.of(manifestDescriptor)); + Index index = Index.fromManifests(List.of(manifestDescriptor)).withArtifactType(artifactType); // Push the index with the provider version tag ContainerRef indexRef = ContainerRef.parse("terraform-provider-aws:5.0.0");