From b1b82aec0893296cc05f13051497bc011ab0a354 Mon Sep 17 00:00:00 2001 From: Fortune Ngwenya Date: Tue, 17 Feb 2026 21:03:06 +0200 Subject: [PATCH 1/7] ci: stop swallowing errors in release step 5 (tag/release creation)\n\nReplace Out-Null with captured output so git tag, git push, and\ngh release errors are printed in red when they fail. This makes\nrelease failures diagnosable instead of silent." --- run-on-release-push.ps1 | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/run-on-release-push.ps1 b/run-on-release-push.ps1 index 6fe3d944..10bd7dce 100644 --- a/run-on-release-push.ps1 +++ b/run-on-release-push.ps1 @@ -373,13 +373,22 @@ if (-not (Test-PreviousStepsPassed)) { $tagName = "$($script:Version)" Write-Task "Creating Git tag $tagName..." - git tag -a $tagName -m "Release $tagName" 2>&1 | Out-Null + $tagOutput = git tag -a $tagName -m "Release $tagName" 2>&1 $tagCreated = $LASTEXITCODE -eq 0 + if (-not $tagCreated) { + Write-Host " Tag creation error: $tagOutput" -ForegroundColor Red + } + if ($tagCreated) { Write-Task "Pushing tag to origin..." - git push origin $tagName 2>&1 | Out-Null + $pushOutput = git push origin $tagName 2>&1 $tagPushed = $LASTEXITCODE -eq 0 + + if (-not $tagPushed) { + Write-Host " Tag push error: $pushOutput" -ForegroundColor Red + } + Write-TaskResult "Git tag $tagName" $tagPushed $script:Results["5. Git Tag"] = $tagPushed } else { @@ -390,8 +399,13 @@ if (-not (Test-PreviousStepsPassed)) { if ($script:Results["5. Git Tag"]) { Write-Task "Creating GitHub Release..." $releaseNotes = "" - gh release create $tagName --title "$tagName" --notes $releaseNotes "target/$script:JarName" 2>&1 | Out-Null + $releaseOutput = gh release create $tagName --title "$tagName" --notes $releaseNotes "target/$script:JarName" 2>&1 $releaseCreated = $LASTEXITCODE -eq 0 + + if (-not $releaseCreated) { + Write-Host " Release creation error: $releaseOutput" -ForegroundColor Red + } + Write-TaskResult "GitHub Release $tagName" $releaseCreated $script:Results["5. GitHub Release"] = $releaseCreated } else { From eb7259cfebb337194de0d7d9cd3bc5070dcfdaa5 Mon Sep 17 00:00:00 2001 From: Fortune Ngwenya Date: Tue, 17 Feb 2026 21:07:56 +0200 Subject: [PATCH 2/7] Badge : -> coverage -> Coverage --- run-on-develop-push.ps1 | 2 +- run-on-release-push.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/run-on-develop-push.ps1 b/run-on-develop-push.ps1 index a65e902a..7f97ba50 100644 --- a/run-on-develop-push.ps1 +++ b/run-on-develop-push.ps1 @@ -164,7 +164,7 @@ if ($testsPassed) { } } $color = if ($coveragePercent -ge 80) { "brightgreen" } elseif ($coveragePercent -ge 60) { "green" } elseif ($coveragePercent -ge 40) { "yellow" } else { "red" } - $badgeJson = @{ schemaVersion = 1; label = "coverage"; message = "$coveragePercent%"; color = $color } | ConvertTo-Json + $badgeJson = @{ schemaVersion = 1; label = "Coverage"; message = "$coveragePercent%"; color = $color } | ConvertTo-Json Set-Content -Path "coverage-badge.json" -Value $badgeJson -Encoding UTF8 Write-TaskResult "Coverage: $coveragePercent% (badge updated)" $true } diff --git a/run-on-release-push.ps1 b/run-on-release-push.ps1 index 10bd7dce..40d8c6b7 100644 --- a/run-on-release-push.ps1 +++ b/run-on-release-push.ps1 @@ -199,7 +199,7 @@ if ($testsPassed) { } } $color = if ($coveragePercent -ge 80) { "brightgreen" } elseif ($coveragePercent -ge 60) { "green" } elseif ($coveragePercent -ge 40) { "yellow" } else { "red" } - $badgeJson = @{ schemaVersion = 1; label = "coverage"; message = "$coveragePercent%"; color = $color } | ConvertTo-Json + $badgeJson = @{ schemaVersion = 1; label = "Coverage"; message = "$coveragePercent%"; color = $color } | ConvertTo-Json Set-Content -Path "coverage-badge.json" -Value $badgeJson -Encoding UTF8 Write-TaskResult "Coverage: $coveragePercent% (badge updated)" $true } From bbb6734636d7440c437561c36d6e4d4bbe9b24ba Mon Sep 17 00:00:00 2001 From: Fortune Ngwenya Date: Tue, 17 Feb 2026 21:27:06 +0200 Subject: [PATCH 3/7] docs: add Listed On section to README\n\nAdd recognition section above Credits linking to keycloak.org/extensions\nand awesome-keycloak where KETE is now listed." --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 45203a79..68f4e48f 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,11 @@ docker compose up -d | **Ko-fi** | One-time / Recurring | [Donate using Ko-fi](https://ko-fi.com/FortuneN) | | **Liberapay** | Recurring | [Donate using Liberapay](https://liberapay.com/FortuneN) | +## Listed On + +- [Keycloak Extensions](https://www.keycloak.org/extensions) — Official Keycloak extensions directory +- [Awesome Keycloak](https://github.com/thomasdarimont/awesome-keycloak) — Community-curated list of Keycloak resources + ## Credits | Library | Description | From 051ea0d3f3143a6c711697d1e64b88394bc540f1 Mon Sep 17 00:00:00 2001 From: Fortune Ngwenya Date: Tue, 17 Feb 2026 21:28:18 +0200 Subject: [PATCH 4/7] docs: rename Listed On to Recognition in README" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68f4e48f..597686e9 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ docker compose up -d | **Ko-fi** | One-time / Recurring | [Donate using Ko-fi](https://ko-fi.com/FortuneN) | | **Liberapay** | Recurring | [Donate using Liberapay](https://liberapay.com/FortuneN) | -## Listed On +## Recognition - [Keycloak Extensions](https://www.keycloak.org/extensions) — Official Keycloak extensions directory - [Awesome Keycloak](https://github.com/thomasdarimont/awesome-keycloak) — Community-curated list of Keycloak resources From d64ddbc42aec82380529b8e38cc9e0f9622cb52d Mon Sep 17 00:00:00 2001 From: Fortune Ngwenya Date: Tue, 17 Feb 2026 21:30:10 +0200 Subject: [PATCH 5/7] docs: convert Recognition section to table format in README" --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 597686e9..6648425f 100644 --- a/README.md +++ b/README.md @@ -169,8 +169,10 @@ docker compose up -d ## Recognition -- [Keycloak Extensions](https://www.keycloak.org/extensions) — Official Keycloak extensions directory -- [Awesome Keycloak](https://github.com/thomasdarimont/awesome-keycloak) — Community-curated list of Keycloak resources +| Directory | Description | +|-----------|-------------| +| [Keycloak Extensions](https://www.keycloak.org/extensions) | Official Keycloak extensions directory | +| [Awesome Keycloak](https://github.com/thomasdarimont/awesome-keycloak) | Community-curated list of Keycloak resources | ## Credits From 058ac3f8723198b0521f1d6aeab311b830e789bd Mon Sep 17 00:00:00 2001 From: Fortune Ngwenya Date: Tue, 17 Feb 2026 21:32:12 +0200 Subject: [PATCH 6/7] docs: rename Recognition table header from Directory to Source" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6648425f..a03106b9 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ docker compose up -d ## Recognition -| Directory | Description | +| Source | Description | |-----------|-------------| | [Keycloak Extensions](https://www.keycloak.org/extensions) | Official Keycloak extensions directory | | [Awesome Keycloak](https://github.com/thomasdarimont/awesome-keycloak) | Community-curated list of Keycloak resources | From b41d6a3ff40d0faf33ffb900885aaa7e31054ddd Mon Sep 17 00:00:00 2001 From: Fortune Ngwenya Date: Mon, 23 Mar 2026 08:49:44 +0200 Subject: [PATCH 7/7] =?UTF-8?q?#22=20-=20GcpPubSubDestination=20initializa?= =?UTF-8?q?tion=20fails=20=E2=80=94=20health=20check=20GETs=20Pub/Sub=20ro?= =?UTF-8?q?ot=20URL=20which=20returns=20404?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 25 ++++++++++++ .../GcpCloudTasksDestination.java | 40 +++++++++++++++---- .../gcppubsub/GcpPubSubDestination.java | 24 ++++++++--- .../gcpcloudtasksdestination/sendTests.java | 4 ++ 4 files changed, 80 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 8ef4d62b..8101f821 100644 --- a/pom.xml +++ b/pom.xml @@ -923,6 +923,11 @@ org.apache.commons.logging kete.org.apache.commons.logging + + + org.apache.commons.compress + kete.org.apache.commons.compress + javassist @@ -1069,6 +1074,11 @@ com.microsoft.aad kete.com.microsoft.aad + + + com.sun.jna + kete.com.sun.jna + com.google.api.client @@ -1078,6 +1088,16 @@ com.google.api.services kete.com.google.api.services + + + com.google.api + kete.com.google.api + + + + org.apache.http + kete.org.apache.http + com.google.cloud @@ -1131,6 +1151,11 @@ io.grpc kete.io.grpc + + + org.codehaus.mojo.animal_sniffer + kete.org.codehaus.mojo.animal_sniffer + com.google.auto.value diff --git a/src/main/java/io/github/fortunen/kete/destinations/gcpcloudtasks/GcpCloudTasksDestination.java b/src/main/java/io/github/fortunen/kete/destinations/gcpcloudtasks/GcpCloudTasksDestination.java index 5352538c..d4c04eee 100644 --- a/src/main/java/io/github/fortunen/kete/destinations/gcpcloudtasks/GcpCloudTasksDestination.java +++ b/src/main/java/io/github/fortunen/kete/destinations/gcpcloudtasks/GcpCloudTasksDestination.java @@ -5,11 +5,11 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; - import java.util.concurrent.TimeUnit; import com.google.cloud.tasks.v2.CloudTasksGrpc; import com.google.cloud.tasks.v2.CreateTaskRequest; +import com.google.cloud.tasks.v2.GetQueueRequest; import com.google.cloud.tasks.v2.HttpMethod; import com.google.cloud.tasks.v2.HttpRequest; import com.google.cloud.tasks.v2.ListQueuesRequest; @@ -23,7 +23,11 @@ import io.github.fortunen.kete.utils.TemplateUtils; import io.github.fortunen.kete.utils.ValidationUtils; import io.grpc.ManagedChannel; +import io.grpc.Metadata; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; import io.grpc.auth.MoreCallCredentials; +import io.grpc.stub.MetadataUtils; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -68,14 +72,32 @@ public void doInitialize() { // verify connection - var parent = "projects/" + config.getProject() + "/locations/" + config.getLocation(); + var deadlinedStub = stub.withDeadlineAfter(timeout.toSeconds(), TimeUnit.SECONDS); - var listRequest = ListQueuesRequest.newBuilder() - .setParent(parent) - .setPageSize(1) - .build(); + try { + + if (isQueueTemplated) { + var parent = "projects/" + config.getProject() + "/locations/" + config.getLocation(); + var listRequest = ListQueuesRequest.newBuilder().setParent(parent).setPageSize(1).build(); + var metadata = new Metadata(); + metadata.put(Metadata.Key.of("x-goog-request-params", Metadata.ASCII_STRING_MARSHALLER), "parent=" + parent); + deadlinedStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)).listQueues(listRequest); + } else { + var queueName = parentPathPrefix + queue; + var getRequest = GetQueueRequest.newBuilder().setName(queueName).build(); + var metadata = new Metadata(); + metadata.put(Metadata.Key.of("x-goog-request-params", Metadata.ASCII_STRING_MARSHALLER), "name=" + queueName); + deadlinedStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)).getQueue(getRequest); + } - stub.withDeadlineAfter(timeout.toSeconds(), TimeUnit.SECONDS).listQueues(listRequest); + } catch (StatusRuntimeException exception) { + + if (exception.getStatus().getCode() != Status.Code.PERMISSION_DENIED) { + throw exception; + } + + // connected but no read permission, no problem + } } @Override @@ -126,7 +148,9 @@ public void doSend(EventMessage message) { .setParent(parentPath) .build(); - stub.createTask(request); + var metadata = new Metadata(); + metadata.put(Metadata.Key.of("x-goog-request-params", Metadata.ASCII_STRING_MARSHALLER), "parent=" + parentPath); + stub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)).createTask(request); } @Override diff --git a/src/main/java/io/github/fortunen/kete/destinations/gcppubsub/GcpPubSubDestination.java b/src/main/java/io/github/fortunen/kete/destinations/gcppubsub/GcpPubSubDestination.java index 42da38d2..da575ed9 100644 --- a/src/main/java/io/github/fortunen/kete/destinations/gcppubsub/GcpPubSubDestination.java +++ b/src/main/java/io/github/fortunen/kete/destinations/gcppubsub/GcpPubSubDestination.java @@ -8,7 +8,9 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import com.google.api.client.http.GenericUrl; +import org.apache.http.HttpStatus; + +import com.google.api.client.http.HttpResponseException; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.pubsub.Pubsub; import com.google.api.services.pubsub.model.PublishRequest; @@ -67,10 +69,22 @@ public void doInitialize() { // verify connection - httpTransport - .createRequestFactory() - .buildGetRequest(new GenericUrl(config.getUrl())) - .execute(); + try { + + if (isTopicTemplated) { + pubsub.projects().topics().list("projects/" + config.getProject()).setPageSize(1).execute(); + } else { + pubsub.projects().topics().get(publishTopicPrefix + topic).execute(); + } + + } catch (HttpResponseException exception) { + + if (exception.getStatusCode() != HttpStatus.SC_FORBIDDEN) { + throw exception; + } + + // connected but no read permission, no problem + } } @Override diff --git a/src/test/java/io/github/fortunen/kete/unittests/destinations/gcpcloudtasksdestination/sendTests.java b/src/test/java/io/github/fortunen/kete/unittests/destinations/gcpcloudtasksdestination/sendTests.java index 88c36b0e..843c8cfc 100644 --- a/src/test/java/io/github/fortunen/kete/unittests/destinations/gcpcloudtasksdestination/sendTests.java +++ b/src/test/java/io/github/fortunen/kete/unittests/destinations/gcpcloudtasksdestination/sendTests.java @@ -2,8 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.nio.charset.StandardCharsets; import java.util.Set; @@ -17,6 +19,7 @@ import com.google.cloud.tasks.v2.CreateTaskRequest; import io.github.fortunen.kete.Constants; +import io.grpc.ClientInterceptor; import io.github.fortunen.kete.EventMessage; import io.github.fortunen.kete.destinations.gcpcloudtasks.GcpCloudTasksDestination; import io.github.fortunen.kete.destinations.gcpcloudtasks.GcpCloudTasksDestinationConfig; @@ -42,6 +45,7 @@ public void shouldCreateTaskWithCorrectFields() { // arrange var stub = mock(CloudTasksGrpc.CloudTasksBlockingStub.class); + when(stub.withInterceptors(any(ClientInterceptor[].class))).thenReturn(stub); destination.setConfig(mock(GcpCloudTasksDestinationConfig.class)); destination.setStub(stub); destination.setQueue("my-queue");