From c8bb7b03ec149df77f68247fa15199beaac5b1ca Mon Sep 17 00:00:00 2001 From: Vlad Savelyev Date: Fri, 29 Nov 2024 15:01:03 +0100 Subject: [PATCH 1/9] Workflow to sync changelog with Seqera docs (#761) Use release metadata Remove python script Use app Fix parameters Add workflow_dispatch to test Update .github/workflows/seqera_docs_changelog.yml Co-authored-by: Gavin Test signed commit Test signed commit --- .github/workflows/seqera_docs_changelog.yml | 61 +++++++++++++++++++++ .gitignore | 3 + 2 files changed, 64 insertions(+) create mode 100644 .github/workflows/seqera_docs_changelog.yml diff --git a/.github/workflows/seqera_docs_changelog.yml b/.github/workflows/seqera_docs_changelog.yml new file mode 100644 index 000000000..44236e6e7 --- /dev/null +++ b/.github/workflows/seqera_docs_changelog.yml @@ -0,0 +1,61 @@ +name: Push changelog to Seqera Docs +on: + release: + types: [published] + workflow_dispatch: + inputs: + release_name: + description: 'Release version (e.g. 1.0.0)' + required: true + release_body: + description: 'Release changelog content' + required: true + +jobs: + update-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Clone seqeralabs/docs + run: | + git clone https://github.com/seqeralabs/docs.git seqeralabs-docs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create changelog file + run: | + mkdir -p seqeralabs-docs/changelog/wave + cat << EOF > seqeralabs-docs/changelog/wave/${{ github.event.release.name || inputs.release_name }}.mdx + --- + title: Wave ${{ github.event.release.name || inputs.release_name }} + date: $(date +%Y-%m-%d) + tags: [wave] + --- + + ${{ github.event.release.body || inputs.release_body }} + EOF + + - uses: actions/create-github-app-token@v1 + id: generate-token + with: + app-id: ${{ secrets.DOCS_BOT_APP_ID }} + private-key: ${{ secrets.DOCS_BOT_APP_PRIVATE_KEY }} + owner: seqeralabs + repositories: docs + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ steps.generate-token.outputs.token }} + branch-token: ${{ steps.generate-token.outputs.token }} + path: seqeralabs-docs + commit-message: "Changelog: Wave ${{ github.event.release.name }}" + title: "Changelog: Wave ${{ github.event.release.name }}" + body: | + This PR adds the changelog for Wave ${{ github.event.release.name }} to the Seqera documentation. + + This is an automated PR created from the Wave repository. + branch: changelog-wave-${{ github.event.release.name }} + base: master + delete-branch: true diff --git a/.gitignore b/.gitignore index 41b2ff645..be360a502 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ deployment-url.txt tsp-output/ node_modules/ package-lock.json + +# Seqera Docs clone +seqeralabs-docs \ No newline at end of file From 513a1ef2644edf03284f09d51738939269fe1844 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 30 Nov 2024 09:48:08 +0100 Subject: [PATCH 2/9] Fix handling error for known http statuses Signed-off-by: Paolo Di Tommaso --- src/main/groovy/io/seqera/wave/ErrorHandler.groovy | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/groovy/io/seqera/wave/ErrorHandler.groovy b/src/main/groovy/io/seqera/wave/ErrorHandler.groovy index 0d872918b..69a4ab430 100644 --- a/src/main/groovy/io/seqera/wave/ErrorHandler.groovy +++ b/src/main/groovy/io/seqera/wave/ErrorHandler.groovy @@ -24,6 +24,7 @@ import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse import io.micronaut.http.HttpResponseFactory import io.micronaut.http.HttpStatus +import io.micronaut.http.exceptions.HttpStatusException import io.micronaut.security.authentication.AuthorizationException import io.seqera.wave.exception.BuildTimeoutException import io.seqera.wave.exception.DockerRegistryException @@ -55,8 +56,9 @@ class ErrorHandler { def HttpResponse handle(HttpRequest httpRequest, Throwable t, Mapper responseFactory) { final errId = LongRndKey.rndHex() final request = httpRequest?.toString() + final knownException = t instanceof WaveException || t instanceof HttpStatusException def msg = t.message - if( t instanceof WaveException && msg ) { + if( knownException && msg ) { // the the error cause if( t.cause ) msg += " - Cause: ${t.cause.message ?: t.cause}".toString() // render the message for logging @@ -81,6 +83,13 @@ class ErrorHandler { log.error(render, t) } + if( t instanceof HttpStatusException ) { + final body = (t.body.isPresent() ? t.body.get() : t.message) as T + return HttpResponse + .status(t.status) + .body(body) + } + if( t instanceof RegistryForwardException ) { // report this error as it has been returned by the target registry return HttpResponse From 61357048619ec0545e0c711be37c52d6183dc4d1 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 30 Nov 2024 10:02:49 +0100 Subject: [PATCH 3/9] Remove unused stream-executor Signed-off-by: Paolo Di Tommaso --- src/main/resources/application.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4c7e565b8..0be853787 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -37,20 +37,11 @@ micronaut: enabled: true # http client configuration # https://docs.micronaut.io/latest/guide/configurationreference.html#io.micronaut.http.client.DefaultHttpClientConfiguration - executors: - stream-executor: - type: FIXED - number-of-threads: 16 - netty: - event-loops: - stream-pool: - executor: 'stream-executor' http: services: stream-client: read-timeout: '30s' read-idle-timeout: '120s' - event-loop-group: 'stream-pool' security: enabled: true intercept-url-map: From 1c36fd4c3e2f4aeb95b8d4a9fa2886e99f8ca672 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 30 Nov 2024 22:45:00 +0100 Subject: [PATCH 4/9] Improve container validation Signed-off-by: Paolo Di Tommaso --- .../io/seqera/wave/model/ContainerCoordinates.groovy | 3 ++- .../wave/service/validation/ValidationServiceImpl.groovy | 4 ++-- .../wave/controller/ContainerControllerTest.groovy | 4 ++-- .../wave/service/validation/ValidationServiceTest.groovy | 9 ++++++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/groovy/io/seqera/wave/model/ContainerCoordinates.groovy b/src/main/groovy/io/seqera/wave/model/ContainerCoordinates.groovy index 7dbd76fd3..0a0d30f1a 100644 --- a/src/main/groovy/io/seqera/wave/model/ContainerCoordinates.groovy +++ b/src/main/groovy/io/seqera/wave/model/ContainerCoordinates.groovy @@ -53,7 +53,8 @@ class ContainerCoordinates implements ContainerPath { static ContainerCoordinates parse(String path) { if( !path ) throw new IllegalArgumentException("Container image name is not provided") - + if( path.contains(' ') ) + throw new IllegalArgumentException("Invalid container name - offending image: '$path'") final scheme = StringUtils.getUrlProtocol(path) if( scheme ) { if( scheme!='oras') throw new IllegalArgumentException("Invalid container scheme: '$scheme' - offending image: '$path'") diff --git a/src/main/groovy/io/seqera/wave/service/validation/ValidationServiceImpl.groovy b/src/main/groovy/io/seqera/wave/service/validation/ValidationServiceImpl.groovy index e87a51e4c..225cc925e 100644 --- a/src/main/groovy/io/seqera/wave/service/validation/ValidationServiceImpl.groovy +++ b/src/main/groovy/io/seqera/wave/service/validation/ValidationServiceImpl.groovy @@ -68,14 +68,14 @@ class ValidationServiceImpl implements ValidationService { // check does not start with a protocol prefix final prot = StringUtils.getUrlProtocol(name) if( prot ) { - return "Invalid container repository name — offending value: $name" + return "Invalid container repository name — offending value: '$name'" } try { ContainerCoordinates.parse(name) } catch (IllegalArgumentException e) { - return "Invalid container image name — offending value: $name" + return "Invalid container image name — offending value: '$name'" } return null } diff --git a/src/test/groovy/io/seqera/wave/controller/ContainerControllerTest.groovy b/src/test/groovy/io/seqera/wave/controller/ContainerControllerTest.groovy index d582316b8..a3e266086 100644 --- a/src/test/groovy/io/seqera/wave/controller/ContainerControllerTest.groovy +++ b/src/test/groovy/io/seqera/wave/controller/ContainerControllerTest.groovy @@ -453,13 +453,13 @@ class ContainerControllerTest extends Specification { controller.validateContainerRequest(new SubmitContainerTokenRequest(containerImage: 'http://docker.io/foo:latest')) then: err = thrown(BadRequestException) - err.message == 'Invalid container repository name — offending value: http://docker.io/foo:latest' + err.message == "Invalid container repository name — offending value: 'http://docker.io/foo:latest'" when: controller.validateContainerRequest(new SubmitContainerTokenRequest(containerImage: 'http:docker.io/foo:latest')) then: err = thrown(BadRequestException) - err.message == 'Invalid container image name — offending value: http:docker.io/foo:latest' + err.message == "Invalid container image name — offending value: 'http:docker.io/foo:latest'" } diff --git a/src/test/groovy/io/seqera/wave/service/validation/ValidationServiceTest.groovy b/src/test/groovy/io/seqera/wave/service/validation/ValidationServiceTest.groovy index 4ed710744..32b6c35ad 100644 --- a/src/test/groovy/io/seqera/wave/service/validation/ValidationServiceTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/validation/ValidationServiceTest.groovy @@ -80,9 +80,12 @@ class ValidationServiceTest extends Specification { 'quay.io:80/foo:latest' | null 'localhost:8000/foo:latest' | null and: - 'docker:quay.io/foo:latest' | 'Invalid container image name — offending value: docker:quay.io/foo:latest' - 'http://quay.io/foo:latest' | 'Invalid container repository name — offending value: http://quay.io/foo:latest' - 'http://quay.io/foo:latest' | 'Invalid container repository name — offending value: http://quay.io/foo:latest' + 'docker:quay.io/foo:latest' | "Invalid container image name — offending value: 'docker:quay.io/foo:latest'" + 'http://quay.io/foo:latest' | "Invalid container repository name — offending value: 'http://quay.io/foo:latest'" + 'http://quay.io/foo:latest' | "Invalid container repository name — offending value: 'http://quay.io/foo:latest'" + 'ubuntu: latest' | "Invalid container image name — offending value: 'ubuntu: latest'" + 'ubuntu:latest ' | "Invalid container image name — offending value: 'ubuntu:latest '" + ' ' | "Invalid container image name — offending value: ' '" } @Unroll From ec5c67fec57b7fa460ced3869239b3ede337c6da Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 30 Nov 2024 23:42:11 +0100 Subject: [PATCH 5/9] Add slf4j backend for Jaav sys logger Signed-off-by: Paolo Di Tommaso --- .../main/groovy/io.seqera.wave.java-library-conventions.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/groovy/io.seqera.wave.java-library-conventions.gradle b/buildSrc/src/main/groovy/io.seqera.wave.java-library-conventions.gradle index ae835180f..5b7eb0d1e 100644 --- a/buildSrc/src/main/groovy/io.seqera.wave.java-library-conventions.gradle +++ b/buildSrc/src/main/groovy/io.seqera.wave.java-library-conventions.gradle @@ -41,7 +41,7 @@ java { dependencies { implementation 'org.slf4j:slf4j-api:2.0.16' - implementation 'org.slf4j:jul-to-slf4j:2.0.16' + implementation 'org.slf4j:slf4j-jdk-platform-logging:2.0.16' testImplementation 'ch.qos.logback:logback-core:1.5.12' testImplementation 'ch.qos.logback:logback-classic:1.5.12' From 2fd351d34f2e36d3e757278664f2698b872e6dd6 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 30 Nov 2024 23:50:13 +0100 Subject: [PATCH 6/9] Suppress caffeine log warnings Signed-off-by: Paolo Di Tommaso --- src/main/resources/application.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0be853787..d7c23ecbc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -87,12 +87,5 @@ jackson: logger: levels: io.seqera: "DEBUG" - io.micronaut.retry.intercept.RecoveryInterceptor: "OFF" -# ^^^^^^^^^^^^^^^^^^^^^ -# Disable logs of `RecoveryInterceptor`, as they have been found to be noisy. -# Declarative `io.micronaut.http.client.annotation.@Client`s are annotated with `io.micronaut.retry.annotation@Recoverable` -# and throw an exception on every error response by default. `RecoveryInterceptor` ends up logging those exceptions -# even if they are handled and no actual recovery/fallback logic gets to take place. -# TODO remove once the project is updated to Micronaut 4.x, as @Client won't be annotated with @Recoverable anymore -# See https://github.com/micronaut-projects/micronaut-core/issues/3719; https://github.com/micronaut-projects/micronaut-core/pull/8235 + com.github.benmanes.caffeine.cache.LocalAsyncCache: "ERROR" ... From 6fb214a408fc1ab8a35e0e8e0db5deb73750c934 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 30 Nov 2024 23:51:28 +0100 Subject: [PATCH 7/9] Unwrap Failsafe target exception Signed-off-by: Paolo Di Tommaso --- src/main/groovy/io/seqera/wave/util/Retryable.groovy | 7 ++++++- src/test/groovy/io/seqera/wave/util/RetryableTest.groovy | 6 ++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/groovy/io/seqera/wave/util/Retryable.groovy b/src/main/groovy/io/seqera/wave/util/Retryable.groovy index db4a6f9eb..703d33900 100644 --- a/src/main/groovy/io/seqera/wave/util/Retryable.groovy +++ b/src/main/groovy/io/seqera/wave/util/Retryable.groovy @@ -24,6 +24,7 @@ import java.util.function.Consumer import java.util.function.Predicate import dev.failsafe.Failsafe +import dev.failsafe.FailsafeException import dev.failsafe.RetryPolicy import dev.failsafe.RetryPolicyBuilder import dev.failsafe.event.EventListener @@ -137,7 +138,11 @@ class Retryable { R apply(CheckedSupplier action) { final policy = retryPolicy() - return Failsafe.with(policy).get(action) + try { + return Failsafe.with(policy).get(action) + } catch (FailsafeException e) { + throw e.cause + } } static Retryable of(Config config) { diff --git a/src/test/groovy/io/seqera/wave/util/RetryableTest.groovy b/src/test/groovy/io/seqera/wave/util/RetryableTest.groovy index 70c2fd6d7..3828dc8c1 100644 --- a/src/test/groovy/io/seqera/wave/util/RetryableTest.groovy +++ b/src/test/groovy/io/seqera/wave/util/RetryableTest.groovy @@ -22,9 +22,7 @@ import spock.lang.Specification import java.time.Duration -import dev.failsafe.FailsafeException import groovy.util.logging.Slf4j - /** * * @author Paolo Di Tommaso @@ -65,8 +63,8 @@ class RetryableTest extends Specification { when: retryable.apply(()-> {throw new IOException("Oops failed!")}) then: - def e = thrown(FailsafeException) - e.cause instanceof IOException + def e = thrown(IOException) + e.message == 'Oops failed!' } def 'should validate config' () { From daae53de09d3abb0c9daa88c2455078e3ab97a47 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sun, 1 Dec 2024 10:53:01 +0100 Subject: [PATCH 8/9] Improve job manager docs [ci skip] Signed-off-by: Paolo Di Tommaso --- .../io/seqera/wave/service/job/JobManager.groovy | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/groovy/io/seqera/wave/service/job/JobManager.groovy b/src/main/groovy/io/seqera/wave/service/job/JobManager.groovy index 3b6f7cce2..16a3c52e7 100644 --- a/src/main/groovy/io/seqera/wave/service/job/JobManager.groovy +++ b/src/main/groovy/io/seqera/wave/service/job/JobManager.groovy @@ -73,6 +73,16 @@ class JobManager { queue.addConsumer((job)-> processJob(job)) } + /** + * Process a job entry aorrding the state modelled by the {@link JobSpec} object. + * + * @param jobSpec + * A {@link JobSpec} object representing the job to be processed + * @return + * {@code true} to signal the process has been processed successfully and it should + * be removed from the underlying queue, or {@code false} if the job execution has + * not yet completed. + */ protected boolean processJob(JobSpec jobSpec) { try { return processJob0(jobSpec) From 1e2ed2091cf3a06ef33a1021283a01c9e6a670d2 Mon Sep 17 00:00:00 2001 From: Vlad Savelyev Date: Mon, 2 Dec 2024 11:00:11 +0100 Subject: [PATCH 9/9] Fix docs sync changelog for manual dispatch trigger (#766) --- .github/workflows/seqera_docs_changelog.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/seqera_docs_changelog.yml b/.github/workflows/seqera_docs_changelog.yml index 44236e6e7..e38ead83d 100644 --- a/.github/workflows/seqera_docs_changelog.yml +++ b/.github/workflows/seqera_docs_changelog.yml @@ -50,12 +50,12 @@ jobs: token: ${{ steps.generate-token.outputs.token }} branch-token: ${{ steps.generate-token.outputs.token }} path: seqeralabs-docs - commit-message: "Changelog: Wave ${{ github.event.release.name }}" - title: "Changelog: Wave ${{ github.event.release.name }}" + commit-message: "Changelog: Wave ${{ github.event.release.name || inputs.release_name }}" + title: "Changelog: Wave ${{ github.event.release.name || inputs.release_name }}" body: | - This PR adds the changelog for Wave ${{ github.event.release.name }} to the Seqera documentation. + This PR adds the changelog for Wave ${{ github.event.release.name || inputs.release_name }} to the Seqera documentation. This is an automated PR created from the Wave repository. - branch: changelog-wave-${{ github.event.release.name }} + branch: changelog-wave-${{ github.event.release.name || inputs.release_name }} base: master delete-branch: true