From 4a95487d93acd6b73f249129c2cc5778aa5bab57 Mon Sep 17 00:00:00 2001 From: Stephan Schnabel Date: Mon, 23 Jun 2025 16:54:42 +0200 Subject: [PATCH] Moved repository to git.kokuwa.io --- .github/CODEOWNERS | 2 - .github/dependabot.yml | 14 - .github/settings.xml | 16 - .github/workflows/build.yaml | 68 ++- .github/workflows/lint.yaml | 23 + .github/workflows/pr.yaml | 58 --- .github/workflows/release.yaml | 50 --- .justfile | 21 + .woodpecker/deploy.yaml | 50 +++ .woodpecker/dockerhub.yaml | 17 + .woodpecker/lint.yaml | 26 ++ .woodpecker/maven/settings.xml | 33 ++ .woodpecker/release.yaml | 60 +++ .woodpecker/verify.yaml | 24 + .woodpecker/versions.yaml | 26 ++ Dockerfile | 15 + Dockerfile.dockerignore | 5 + LICENSE | 425 +++++++++++------- README.md | 19 +- pom.xml | 258 +++-------- renovate.json | 5 + .../metrics/junit/KeycloakExtension.java | 42 +- test/1.jar | Bin 0 -> 17006 bytes test/2.jar | Bin 0 -> 17006 bytes 24 files changed, 695 insertions(+), 562 deletions(-) delete mode 100644 .github/CODEOWNERS delete mode 100644 .github/dependabot.yml delete mode 100644 .github/settings.xml create mode 100644 .github/workflows/lint.yaml delete mode 100644 .github/workflows/pr.yaml delete mode 100644 .github/workflows/release.yaml create mode 100644 .justfile create mode 100644 .woodpecker/deploy.yaml create mode 100644 .woodpecker/dockerhub.yaml create mode 100644 .woodpecker/lint.yaml create mode 100644 .woodpecker/maven/settings.xml create mode 100644 .woodpecker/release.yaml create mode 100644 .woodpecker/verify.yaml create mode 100644 .woodpecker/versions.yaml create mode 100644 Dockerfile create mode 100644 Dockerfile.dockerignore create mode 100644 renovate.json create mode 100644 test/1.jar create mode 100644 test/2.jar diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 3f0ccf3..0000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax -* @sschnabe @rpahli @fabian-schlegel @jschwarze @wistefan @monotek diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 415bfca..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,14 +0,0 @@ -version: 2 -updates: - - package-ecosystem: maven - directory: / - schedule: - interval: daily - allow: - - dependency-name: io.kokuwa.maven:maven-parent - - dependency-name: org.keycloak:keycloak-quarkus-server - - package-ecosystem: github-actions - directory: / - schedule: - interval: monthly - day: monday diff --git a/.github/settings.xml b/.github/settings.xml deleted file mode 100644 index 44fd9ea..0000000 --- a/.github/settings.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - sonatype-nexus - ${env.SERVER_USERNAME} - ${env.SERVER_PASSWORD} - - - github.com - nope - ${env.GIT_ACTION_TOKEN} - - - diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4da10a5..5d30921 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,50 +1,42 @@ -name: Build +name: Verify on: - push: - branches: [main] + - pull_request + - push: + branches: [main] + +env: + MAVEN_ARGS: --batch-mode --color=always --no-transfer-progress jobs: build: runs-on: ubuntu-latest - env: - MAVEN_ARGS: --batch-mode --color=always --no-transfer-progress --settings=.github/settings.xml steps: - - name: docker/login-action docker.io - uses: docker/login-action@v3.4.0 - with: - registry: docker.io - username: ${{ secrets.DOCKERIO_USERNAME }} - password: ${{ secrets.DOCKERIO_TOKEN }} - - name: docker/login-action ghcr.io - uses: docker/login-action@v3.4.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GIT_ACTION_TOKEN }} - - run: git config --global user.name "${{ vars.KOKUWA_IO_BOT_NAME }}" - - run: git config --global user.email "${{ vars.KOKUWA_IO_BOT_EMAIL }}" - - uses: actions/checkout@v4 - with: - token: ${{ secrets.GIT_ACTION_TOKEN }} - - uses: crazy-max/ghaction-import-gpg@v6 - with: - gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} - passphrase: ${{ secrets.GPG_PASSPHRASE }} - git_user_signingkey: true - git_commit_gpgsign: true - - uses: actions/setup-java@v4 + - uses: actions/checkout@main + - uses: actions/setup-java@main with: distribution: temurin java-version: 17 cache: maven - server-id: sonatype-nexus - server-username: SERVER_USERNAME - server-password: SERVER_PASSWORD - gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} - gpg-passphrase: GPG_PASSPHRASE - - run: mvn $MAVEN_ARGS deploy - env: - SERVER_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }} - SERVER_PASSWORD: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} + - run: mvn verify + + versions: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + version: + - 22.0.5 + - 23.0.7 + - 24.0.5 + - 25.0.6 + - 26.2.5 + steps: + - uses: actions/checkout@main + - uses: actions/setup-java@main + with: + distribution: temurin + java-version: 17 + cache: maven + - run: mvn verify -Dversion.org.keycloak.test=${{ matrix.version }} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..af980fa --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,23 @@ +name: Lint + +on: push + +jobs: + + renovate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - uses: docker://kokuwaio/renovate-config-validator + + markdownlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - uses: docker://kokuwaio/markdownlint + + yamllint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - uses: docker://kokuwaio/yamllint diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml deleted file mode 100644 index 3548e88..0000000 --- a/.github/workflows/pr.yaml +++ /dev/null @@ -1,58 +0,0 @@ -name: PullRequest - -on: pull_request - -env: - MAVEN_ARGS: --batch-mode --color=always --no-transfer-progress - -jobs: - - yaml: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ibiqlik/action-yamllint@v3 - with: - format: colored - strict: true - - markdown: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: avto-dev/markdown-lint@v1 - with: - args: /github/workspace - - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 17 - cache: maven - - run: mvn $MAVEN_ARGS verify - - versions: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - version: - - 22.0.5 - - 23.0.7 - - 24.0.5 - - 25.0.6 - - 26.0.8 - - 26.1.5 - - 26.2.5 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 17 - cache: maven - - run: mvn $MAVEN_ARGS verify -Dcheck.skip -Dversion.org.keycloak.test=${{ matrix.version }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index e3240ed..0000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,50 +0,0 @@ -name: Release - -on: workflow_dispatch - -jobs: - build: - runs-on: ubuntu-latest - env: - MAVEN_ARGS: --batch-mode --color=always --no-transfer-progress --settings=.github/settings.xml - steps: - - name: docker/login-action docker.io - uses: docker/login-action@v3.4.0 - with: - registry: docker.io - username: ${{ secrets.DOCKERIO_USERNAME }} - password: ${{ secrets.DOCKERIO_TOKEN }} - - name: docker/login-action ghcr.io - uses: docker/login-action@v3.4.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GIT_ACTION_TOKEN }} - - run: git config --global user.name "${{ vars.KOKUWA_IO_BOT_NAME }}" - - run: git config --global user.email "${{ vars.KOKUWA_IO_BOT_EMAIL }}" - - uses: actions/checkout@v4 - with: - token: ${{ secrets.GIT_ACTION_TOKEN }} - - uses: crazy-max/ghaction-import-gpg@v6 - with: - gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} - passphrase: ${{ secrets.GPG_PASSPHRASE }} - git_user_signingkey: true - git_commit_gpgsign: true - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 17 - cache: maven - server-id: sonatype-nexus - server-username: SERVER_USERNAME - server-password: SERVER_PASSWORD - gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} - gpg-passphrase: GPG_PASSPHRASE - - run: mvn $MAVEN_ARGS release:prepare -Darguments="$MAVEN_ARGS" - - run: mvn $MAVEN_ARGS release:perform -Darguments="$MAVEN_ARGS" - env: - SERVER_USERNAME: ${{ secrets.SONATYPE_NEXUS_USERNAME }} - SERVER_PASSWORD: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} - GIT_ACTION_TOKEN: ${{ secrets.GIT_ACTION_TOKEN }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.justfile b/.justfile new file mode 100644 index 0000000..c751e60 --- /dev/null +++ b/.justfile @@ -0,0 +1,21 @@ +# https://just.systems/man/en/ + +[private] +@default: + just --list --unsorted + +# Run linter. +@lint: + docker run --rm --read-only --volume=$(pwd):$(pwd):ro --workdir=$(pwd) kokuwaio/hadolint + docker run --rm --read-only --volume=$(pwd):$(pwd):ro --workdir=$(pwd) kokuwaio/yamllint + docker run --rm --read-only --volume=$(pwd):$(pwd):rw --workdir=$(pwd) kokuwaio/markdownlint --fix + docker run --rm --read-only --volume=$(pwd):$(pwd):ro --workdir=$(pwd) kokuwaio/renovate + docker run --rm --read-only --volume=$(pwd):$(pwd):ro --workdir=$(pwd) woodpeckerci/woodpecker-cli lint + +# Build image with local docker daemon. +@build: + docker build . --tag=kokuwaio/keycloak-event-metrics:dev + +# Inspect image layers with `dive`. +@dive: build + dive build . diff --git a/.woodpecker/deploy.yaml b/.woodpecker/deploy.yaml new file mode 100644 index 0000000..050a6d4 --- /dev/null +++ b/.woodpecker/deploy.yaml @@ -0,0 +1,50 @@ +when: + instance: ci.kokuwa.io + repo: keycloak/keycloak-event-metrics + event: [manual, push] + branch: main1 + path: [.woodpecker/deploy.yaml, Dockerfile, pom.xml, src/main/**] + +services: + - name: dockerd + image: kokuwaio/dockerd + privileged: true + ports: [2375, 8080] + +steps: + + maven: + image: maven:3.9.10-eclipse-temurin-17 + commands: mvn deploy + environment: + MAVEN_ARGS: --batch-mode --color=always --no-transfer-progress --settings=.woodpecker/maven/settings.xml + MAVEN_GPG_KEY: {from_secret: woodpecker_gpg_key} + SONATYPE_ORG_USERNAME: {from_secret: sonatype_org_username} + SONATYPE_ORG_PASSWORD: {from_secret: sonatype_org_password} + + image: + image: kokuwaio/buildctl + settings: + name: + - docker.io/kokuwaio/keycloak-event-metrics:snapshot + - ghcr.io/kokuwaio/keycloak-event-metrics:snapshot + build-args: {MAVEN_MIRROR_CENTRAL: "${MAVEN_MIRROR_CENTRAL}"} + platform: [linux/amd64, linux/arm64] + auth: + "https://index.docker.io/v1/": + username: {from_secret: docker_io_username} + password: {from_secret: docker_io_password} + ghcr.io: + username: {from_secret: ghcr_io_username} + password: {from_secret: ghcr_io_password} + annotation: + org.opencontainers.image.title: Keycloak Metrics + org.opencontainers.image.description: Provides metrics for Keycloak user/admin events and user/client/session count. + org.opencontainers.image.url: $CI_REPO_URL + org.opencontainers.image.documentation: $CI_REPO_URL/README.md + org.opencontainers.image.source: $CI_REPO_CLONE_URL + org.opencontainers.image.revision: $CI_COMMIT_SHA + org.opencontainers.image.vendor: kokuwa.io + org.opencontainers.image.licenses: EUPL-1.2 + org.opencontainers.image.ref.name: kokuwaio/keycloak-event-metrics + org.opencontainers.image.version: snapshot diff --git a/.woodpecker/dockerhub.yaml b/.woodpecker/dockerhub.yaml new file mode 100644 index 0000000..a443560 --- /dev/null +++ b/.woodpecker/dockerhub.yaml @@ -0,0 +1,17 @@ +when: + instance: ci.kokuwa.io + repo: keycloak/keycloak-event-metrics + event: [manual, push] + branch: main + path: [.woodpecker/dockerhub.yaml, README.md] + +steps: + + metadata: + image: kokuwaio/dockerhub-metadata + settings: + repository: kokuwaio/keycloak-event-metrics + description-short: Provides metrics for Keycloak user/admin events and user/client/session count. + categories: monitoring-and-observability + username: {from_secret: dockerhub_username} + password: {from_secret: dockerhub_password} diff --git a/.woodpecker/lint.yaml b/.woodpecker/lint.yaml new file mode 100644 index 0000000..7de51db --- /dev/null +++ b/.woodpecker/lint.yaml @@ -0,0 +1,26 @@ +when: + event: [manual, pull_request, push] + branch: main + path: [.woodpecker/lint.yaml, renovate.json, Dockerfile, "**/*.y*ml", "**/*.md"] + +steps: + + renovate: + image: kokuwaio/renovate-config-validator + depends_on: [] + when: [path: [.woodpecker/lint.yaml, renovate.json]] + + yaml: + image: kokuwaio/yamllint + depends_on: [] + when: [path: [.woodpecker/lint.yaml, .yamllint.yaml, "**/*.y*ml"]] + + markdown: + image: kokuwaio/markdownlint + depends_on: [] + when: [path: [.woodpecker/lint.yaml, .markdownlint.yaml, "**/*.md"]] + + dockerfile: + image: kokuwaio/hadolint + depends_on: [] + when: [path: [.woodpecker/lint.yaml, Dockerfile]] diff --git a/.woodpecker/maven/settings.xml b/.woodpecker/maven/settings.xml new file mode 100644 index 0000000..4542205 --- /dev/null +++ b/.woodpecker/maven/settings.xml @@ -0,0 +1,33 @@ + + + false + /woodpecker/.m2 + + + git.kokuwa.io + ${env.FORGEJO_USERNAME} + ${env.FORGEJO_PASSWORD} + + + sonatype.org + ${env.SONATYPE_ORG_USERNAME} + ${env.SONATYPE_ORG_PASSWORD} + + + docker.io + ${env.DOCKER_IO_USERNAME} + ${env.DOCKER_IO_PASSWORD} + + + ghcr.io + ${env.GHCR_IO_USERNAME} + ${env.GHCR_IO_PASSWORD} + + + + + http://mirror.woodpecker.svc.cluster.local/maven2 + central + + + diff --git a/.woodpecker/release.yaml b/.woodpecker/release.yaml new file mode 100644 index 0000000..19d68cd --- /dev/null +++ b/.woodpecker/release.yaml @@ -0,0 +1,60 @@ +when: + instance: ci.kokuwa.io + repo: keycloak/keycloak-event-metrics + #event: deployment + branch: main + +steps: + + maven: + image: maven:3.9.10-eclipse-temurin-17 + commands: + # setup git + - git config user.email "$GIT_USER_EMAIL" + - git config user.name "$GIT_USER_NAME" + - git config gpg.format ssh + - git config user.signingkey /woodpecker/ci + - echo $GIT_SIGN_KEY > /woodpecker/ci + # release + - mvn release:prepare # release:perform + # write version to env file for image + - echo "VERSION=$(grep "scm.tag=" release.properties | cut -d= -f2)" > /woodpecker/maven.env + environment: + MAVEN_ARGS: --batch-mode --color=always --no-transfer-progress --settings=.woodpecker/maven/settings.xml -DautoPublish=false + MAVEN_GPG_KEY: {from_secret: woodpecker_gpg_key} + GIT_SIGN_KEY: {from_secret: woodpecker_sign_key} + FORGEJO_USERNAME: {from_secret: woodpecker_username} + FORGEJO_PASSWORD: {from_secret: woodpecker_password} + SONATYPE_ORG_USERNAME: {from_secret: sonatype_org_username} + SONATYPE_ORG_PASSWORD: {from_secret: sonatype_org_password} + + image: + image: kokuwaio/buildctl + settings: + env-file: fail + name: + - docker.io/kokuwaio/keycloak-event-metrics:latest + - docker.io/kokuwaio/keycloak-event-metrics:$VERSION + - ghcr.io/kokuwaio/keycloak-event-metrics:latest + - ghcr.io/kokuwaio/keycloak-event-metrics:$VERSION + build-args: {MAVEN_MIRROR_CENTRAL: "${MAVEN_MIRROR_CENTRAL}"} + platform: [linux/amd64, linux/arm64] + auth: + "https://index.docker.io/v1/": + username: {from_secret: docker_io_username} + password: {from_secret: docker_io_password} + ghcr.io: + username: {from_secret: ghcr_io_username} + password: {from_secret: ghcr_io_password} + annotation: + org.opencontainers.image.title: Keycloak Metrics + org.opencontainers.image.description: Provides metrics for Keycloak user/admin events and user/client/session count. + org.opencontainers.image.url: $CI_REPO_URL + org.opencontainers.image.documentation: $CI_REPO_URL/README.md + org.opencontainers.image.source: $CI_REPO_CLONE_URL + org.opencontainers.image.revision: $CI_COMMIT_SHA + org.opencontainers.image.vendor: kokuwa.io + org.opencontainers.image.licenses: EUPL-1.2 + org.opencontainers.image.ref.name: kokuwaio/keycloak-event-metrics + org.opencontainers.image.version: $VERSION + diff --git a/.woodpecker/verify.yaml b/.woodpecker/verify.yaml new file mode 100644 index 0000000..5d9002b --- /dev/null +++ b/.woodpecker/verify.yaml @@ -0,0 +1,24 @@ +when: + event: [manual, pull_request] + path: [.woodpecker/verify.yaml, pom.xml, src/**] + +services: + - name: dockerd + image: kokuwaio/dockerd + privileged: true + ports: [2375, 8080] + +steps: + + test: + image: maven:3.9.10-eclipse-temurin-17 + commands: mvn verify -P-deploy + environment: + MAVEN_ARGS: --batch-mode --color=always --no-transfer-progress --settings=.woodpecker/maven/settings.xml + + image: + image: kokuwaio/buildctl + settings: + platform: [linux/amd64, linux/arm64] + when: + instance: ci.kokuwa.io diff --git a/.woodpecker/versions.yaml b/.woodpecker/versions.yaml new file mode 100644 index 0000000..47a286e --- /dev/null +++ b/.woodpecker/versions.yaml @@ -0,0 +1,26 @@ +when: + event: [manual, pull_request] + path: [.woodpecker/versions.yaml, pom.xml, src/**] + +depends_on: [verify] +matrix: + KEYCLOAK_VERSION: + - 22.0.5 + - 23.0.7 + - 24.0.5 + - 25.0.6 + - 26.2.5 + +services: + - name: dockerd + image: kokuwaio/dockerd + privileged: true + ports: [2375, 8080] + +steps: + + test: + image: maven:3.9.10-eclipse-temurin-17 + commands: mvn verify -Dversion.org.keycloak.test="$KEYCLOAK_VERSION" -P-deploy,-check + environment: + MAVEN_ARGS: --batch-mode --color=always --no-transfer-progress --settings=.woodpecker/maven/settings.xml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6d8ad09 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM maven:3.9.10-eclipse-temurin-17 AS build +SHELL ["/usr/bin/bash", "-e", "-u", "-c"] +WORKDIR /build +ARG MAVEN_ARGS="--batch-mode --color=always --no-transfer-progress" +ARG MAVEN_MIRROR_CENTRAL +RUN mkdir "$HOME/.m2" && printf "\n\ +\n\ + /tmp/mvn-repo\n\ + %scentral\n\ +" "${MAVEN_MIRROR_CENTRAL:-https://repo.maven.apache.org/maven2}" > "$HOME/.m2/settings.xml" +COPY . . +RUN --mount=type=cache,target=/tmp/mvn-repo mvn package -DskipTests -P=-dev + +FROM busybox:1.37.0-uclibc +COPY --from=build --chmod=444 /build/target/keycloak-event-metrics.jar /opt/keycloak/providers/keycloak-event-metrics.jar diff --git a/Dockerfile.dockerignore b/Dockerfile.dockerignore new file mode 100644 index 0000000..744a668 --- /dev/null +++ b/Dockerfile.dockerignore @@ -0,0 +1,5 @@ +* +.* + +!pom.xml +!src/main/** diff --git a/LICENSE b/LICENSE index 261eeb9..dacd3ae 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,288 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + EUROPEAN UNION PUBLIC LICENCE v. 1.2 + EUPL © the European Union 2007, 2016 - 1. Definitions. +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined +below) which is provided under the terms of this Licence. Any use of the Work, +other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +The Work is provided under the terms of this Licence when the Licensor (as +defined below) has placed the following notice immediately following the +copyright notice for the Work: - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + Licensed under the EUPL - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. +or has expressed by any other means his willingness to license under the EUPL. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. +1. Definitions - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +In this Licence, the following terms have the following meaning: - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. +- ‘The Licence’: this Licence. - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). +- ‘The Original Work’: the work or software distributed or communicated by the + Licensor under this Licence, available as Source Code and also as Executable + Code as the case may be. - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. +- ‘Derivative Works’: the works or software that could be created by the + Licensee, based upon the Original Work or modifications thereof. This Licence + does not define the extent of modification or dependence on the Original Work + required in order to classify a work as a Derivative Work; this extent is + determined by copyright law applicable in the country mentioned in Article 15. - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." +- ‘The Work’: the Original Work or its Derivative Works. - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. +- ‘The Source Code’: the human-readable form of the Work which is the most + convenient for people to study and modify. - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. +- ‘The Executable Code’: any code which has generally been compiled and which is + meant to be interpreted by a computer as a program. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. +- ‘The Licensor’: the natural or legal person that distributes or communicates + the Work under the Licence. - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: +- ‘Contributor(s)’: any natural or legal person who modifies the Work under the + Licence, or otherwise contributes to the creation of a Derivative Work. - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and +- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of + the Work under the terms of the Licence. - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and +- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, + renting, distributing, communicating, transmitting, or otherwise making + available, online or offline, copies of the Work or providing access to its + essential functionalities at the disposal of any other natural or legal + person. - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and +2. Scope of the rights granted by the Licence - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +sublicensable licence to do the following, for the duration of copyright vested +in the Original Work: - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. +- use the Work in any circumstance and for all usage, +- reproduce the Work, +- modify the Work, and make Derivative Works based upon the Work, +- communicate to the public, including the right to make available or display + the Work or copies thereof to the public and perform publicly, as the case may + be, the Work, +- distribute the Work or copies thereof, +- lend and rent the Work or copies thereof, +- sublicense rights in the Work or copies thereof. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. +Those rights can be exercised on any media, supports and formats, whether now +known or later invented, as far as the applicable law permits so. - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make effective +the licence of the economic rights here above listed. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to +any patents held by the Licensor, to the extent necessary to make use of the +rights granted on the Work under this Licence. - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. +3. Communication of the Source Code - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. +The Licensor may provide the Work either in its Source Code form, or as +Executable Code. If the Work is provided as Executable Code, the Licensor +provides in addition a machine-readable copy of the Source Code of the Work +along with each copy of the Work that the Licensor distributes or indicates, in +a notice following the copyright notice attached to the Work, a repository where +the Source Code is easily and freely accessible for as long as the Licensor +continues to distribute or communicate the Work. - END OF TERMS AND CONDITIONS +4. Limitations on copyright - APPENDIX: How to apply the Apache License to your work. +Nothing in this Licence is intended to deprive the Licensee of the benefits from +any exception or limitation to the exclusive rights of the rights owners in the +Work, of the exhaustion of those rights or of other applicable limitations +thereto. - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. +5. Obligations of the Licensee - Copyright [yyyy] [name of copyright owner] +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: - 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 +Attribution right: The Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the +disclaimer of warranties. The Licensee must include a copy of such notices and a +copy of the Licence with every copy of the Work he/she distributes or +communicates. The Licensee must cause any Derivative Work to carry prominent +notices stating that the Work has been modified and the date of modification. - http://www.apache.org/licenses/LICENSE-2.0 +Copyleft clause: If the Licensee distributes or communicates copies of the +Original Works or Derivative Works, this Distribution or Communication will be +done under the terms of this Licence or of a later version of this Licence +unless the Original Work is expressly distributed only under this version of the +Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee +(becoming Licensor) cannot offer or impose any additional terms or conditions on +the Work or Derivative Work that alter or restrict the terms of the Licence. - 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. +Compatibility clause: If the Licensee Distributes or Communicates Derivative +Works or copies thereof based upon both the Work and another work licensed under +a Compatible Licence, this Distribution or Communication can be done under the +terms of this Compatible Licence. For the sake of this clause, ‘Compatible +Licence’ refers to the licences listed in the appendix attached to this Licence. +Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible +Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, +the Licensee will provide a machine-readable copy of the Source Code or indicate +a repository where this Source will be easily and freely available for as long +as the Licensee continues to distribute or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade names, +trademarks, service marks, or names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings +to the Work are owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent +Contributors grant You a licence to their contributions to the Work, under the +terms of this Licence. + +7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +Contributors. It is not a finished work and may therefore contain defects or +‘bugs’ inherent to this type of development. + +For the above reason, the Work is provided under the Licence on an ‘as is’ basis +and without warranties of any kind concerning the Work, including without +limitation merchantability, fitness for a particular purpose, absence of defects +or errors, accuracy, non-infringement of intellectual property rights other than +copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition +for the grant of any rights to the Work. + +8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the use +of the Work, including without limitation, damages for loss of goodwill, work +stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such damage. +However, the Licensor will be liable under statutory product liability laws as +far such laws apply to the Work. + +9. Additional agreements + +While distributing the Work, You may choose to conclude an additional agreement, +defining obligations or services consistent with this Licence. However, if +accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, +and only if You agree to indemnify, defend, and hold each Contributor harmless +for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ +placed under the bottom of a window displaying the text of this Licence or by +affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and +conditions by exercising any rights granted to You by Article 2 of this Licence, +such as the use of the Work, the creation by You of a Derivative Work or the +Distribution or Communication by You of the Work or copies thereof. + +11. Information to the public + +In case of any Distribution or Communication of the Work by means of electronic +communication by You (for example, by offering to download the Work from a +remote location) the distribution channel or media (for example, a website) must +at least provide to the public the information requested by the applicable law +regarding the Licensor, the Licence and the way it may be accessible, concluded, +stored and reproduced by the Licensee. + +12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon +any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has +received the Work from the Licensee under the Licence, provided such persons +remain in full compliance with the Licence. + +13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as a +whole. Such provision will be construed or reformed so as necessary to make it +valid and enforceable. + +The European Commission may publish other linguistic versions or new versions of +this Licence or updated versions of the Appendix, so far this is required and +reasonable, without reducing the scope of the rights granted by the Licence. New +versions of the Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version of +their choice. + +14. Jurisdiction + +Without prejudice to specific agreement between parties, + +- any litigation resulting from the interpretation of this License, arising + between the European Union institutions, bodies, offices or agencies, as a + Licensor, and any Licensee, will be subject to the jurisdiction of the Court + of Justice of the European Union, as laid down in article 272 of the Treaty on + the Functioning of the European Union, + +- any litigation arising between other parties and resulting from the + interpretation of this License, will be subject to the exclusive jurisdiction + of the competent court where the Licensor resides or conducts its primary + business. + +15. Applicable Law + +Without prejudice to specific agreement between parties, + +- this Licence shall be governed by the law of the European Union Member State + where the Licensor has his seat, resides or has his registered office, + +- this licence shall be governed by Belgian law if the Licensor has no seat, + residence or registered office inside a European Union Member State. + +Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: + +- GNU General Public License (GPL) v. 2, v. 3 +- GNU Affero General Public License (AGPL) v. 3 +- Open Software License (OSL) v. 2.1, v. 3.0 +- Eclipse Public License (EPL) v. 1.0 +- CeCILL v. 2.0, v. 2.1 +- Mozilla Public Licence (MPL) v. 2 +- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for + works other than software +- European Union Public Licence (EUPL) v. 1.1, v. 1.2 +- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong + Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above +licences without producing a new version of the EUPL, as long as they provide +the rights granted in Article 2 of this Licence and protect the covered Source +Code from exclusive appropriation. + +All other changes or additions to this Appendix require the production of a new +EUPL version. diff --git a/README.md b/README.md index d57d9b6..cd59d4d 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ # Keycloak Metrics -Provides metrics for Keycloak user/admin events and user/client/session count. Tested on Keycloak [22-26](.github/workflows/pr.yaml#L48-L53). +Provides metrics for Keycloak user/admin events and user/client/session count. Tested on Keycloak [22-26](.woodpecker/verify.yaml#L7-L11). -[![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/kokuwaio/keycloak-event-metrics.svg?label=License)](http://www.apache.org/licenses/) [![Maven Central](https://img.shields.io/maven-central/v/io.kokuwa.keycloak/keycloak-event-metrics.svg?label=Maven%20Central)](https://central.sonatype.com/search?namespace=io.kokuwa.keycloak&q=keycloak-event-metrics) -[![Build](https://img.shields.io/github/actions/workflow/status/kokuwaio/keycloak-event-metrics/build.yaml?label=Build)](https://github.com/kokuwaio/keycloak-event-metrics/actions/workflows/build.yaml) +[![pulls](https://img.shields.io/docker/pulls/kokuwaio/keycloak-event-metrics)](https://hub.docker.com/r/kokuwaio/keycloak-event-metrics) +[![size](https://img.shields.io/docker/image-size/kokuwaio/keycloak-event-metrics)](https://hub.docker.com/r/kokuwaio/keycloak-event-metrics) +[![dockerfile](https://img.shields.io/badge/source-Dockerfile%20-blue)](https://git.kokuwa.io/keycloak/keycloak-event-metrics/src/branch/main/Dockerfile) +[![license](https://img.shields.io/badge/License-EUPL%201.2-blue)](https://git.kokuwa.io/keycloak/keycloak-event-metrics/src/branch/main/LICENSE) +[![prs](https://img.shields.io/gitea/pull-requests/open/keycloak/keycloak-event-metrics?gitea_url=https%3A%2F%2Fgit.kokuwa.io)](https://git.kokuwa.io/keycloak/keycloak-event-metrics/pulls) +[![issues](https://img.shields.io/gitea/issues/open/keycloak/keycloak-event-metrics?gitea_url=https%3A%2F%2Fgit.kokuwa.io)](https://git.kokuwa.io/keycloak/keycloak-event-metrics/issues) +[![build](https://ci.kokuwa.io/api/badges/keycloak/keycloak-event-metrics/status.svg)](https://ci.kokuwa.io/repos/keycloak/keycloak-event-metrics/) ## Why? @@ -114,7 +119,7 @@ If scrapping takes less than `KC_METRICS_STATS_INFO_THRESHOLD` duration will be ### Grafana Dashboard -Can be found here: [kokuwaio/keycloak keycloak-metrics.json](https://github.com/kokuwaio/keycloak/blob/main/src/test/k3s/dev/grafana/files/dashboards/keycloak-metrics.json) +Can be found here: [keycloak-metrics.json](https://git.kokuwa.io/keycloak/keycloak/blob/main/src/test/k3s/dev/grafana/files/dashboards/keycloak-metrics.json) ### Testcontainers @@ -131,7 +136,7 @@ This images are based on busybox, so you can use cp to copy the jar into your ke ### Docker -Check: [kokuwaio/keycloak](https://github.com/kokuwaio/keycloak) +Check: [kowaio/keycloak](https://git.kokuwa.io/keycloak/keycloak) Dockerfile: @@ -145,7 +150,7 @@ FROM debian:stable-slim AS metrics RUN apt-get -qq update RUN apt-get -qq install --yes --no-install-recommends ca-certificates wget -ARG METRICS_VERSION=1.0.0 +ARG METRICS_VERSION=2.0.0 ARG METRICS_FILE=keycloak-event-metrics-${METRICS_VERSION}.jar ARG METRICS_URL=https://repo1.maven.org/maven2/io/kokuwa/keycloak/keycloak-event-metrics/${METRICS_VERSION} @@ -159,7 +164,7 @@ RUN mv ${METRICS_FILE} /opt/keycloak/providers ### build keycloak with metrics ### -FROM quay.io/keycloak/keycloak:25.0.1 +FROM quay.io/keycloak/keycloak:25.2.5 ENV KEYCLOAK_ADMIN=admin ENV KEYCLOAK_ADMIN_PASSWORD=password diff --git a/pom.xml b/pom.xml index 8e26d7f..ac146cd 100644 --- a/pom.xml +++ b/pom.xml @@ -4,11 +4,11 @@ io.kokuwa.keycloak keycloak-event-metrics - 1.1.2-SNAPSHOT + 2.0.0-SNAPSHOT Keycloak Metrics Provides metrics for Keycloak user/admin events - https://github.com/kokuwaio/keycloak-event-metrics + https://git.kokuwa.io/keycloak/keycloak-event-metrics 2023 Kokuwa.io @@ -16,8 +16,8 @@ - Apache-2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt + EUPL-1.2 + https://eupl.eu/1.2/en repo @@ -33,27 +33,23 @@ - https://github.com/kokuwaio/keycloak-event-metrics - scm:git:https://github.com/kokuwaio/keycloak-event-metrics.git - scm:git:https://github.com/kokuwaio/keycloak-event-metrics.git + https://git.kokuwa.io/keycloak/keycloak-event-metrics + scm:git:https://git.kokuwa.io/keycloak/keycloak-event-metrics.git + scm:git:https://git.kokuwa.io/keycloak/keycloak-event-metrics.git HEAD - github - https://github.com/kokuwaio/keycloak-event-metrics/issues + forgejo + https://git.kokuwa.io/keycloak/keycloak-event-metrics/issues - github - https://github.com/kokuwaio/keycloak-event-metrics/actions + woodpecker + https://ci.kokuwa.io/repos/keycloak/keycloak-event-metrics - - sonatype-nexus - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - sonatype-nexus - https://oss.sonatype.org/content/repositories/snapshots/ + sonatype.org + https://central.sonatype.com/repository/maven-snapshots/ @@ -83,28 +79,9 @@ - 3.4.1 - 3.14.0 - 3.1.4 - 3.2.7 - 3.1.4 - 3.4.2 - 3.11.2 - 3.1.1 - 3.3.1 - 3.21.0 - 3.3.1 - 3.5.3 - 1.4.0 - 2.18.0 - 1.7.0 - 2.26.0 - 1.12.0 - 0.46.0 - + snapshot 26.2.5 ${version.org.keycloak} - 1.18.3 @@ -153,6 +130,15 @@ org.jboss.resteasy resteasy-multipart-provider + + + com.sun.istack + istack-commons-tools + + + com.sun.istack + istack-commons-runtime + @@ -219,6 +205,7 @@ + ${project.artifactId} ${project.basedir}/src/test/resources @@ -227,15 +214,10 @@ - - org.apache.maven.plugins - maven-clean-plugin - ${version.org.apache.maven.plugins.clean} - org.apache.maven.plugins maven-compiler-plugin - ${version.org.apache.maven.plugins.compiler} + 3.14.0 ${maven.compiler.compilerArgument} @@ -243,41 +225,41 @@ org.apache.maven.plugins maven-deploy-plugin - ${version.org.apache.maven.plugins.deploy} + 3.1.4 org.apache.maven.plugins maven-failsafe-plugin - ${version.org.apache.maven.plugins.surefire} + 3.5.3 org.apache.maven.plugins maven-gpg-plugin - ${version.org.apache.maven.plugins.gpg} + 3.2.7 org.apache.maven.plugins maven-install-plugin - ${version.org.apache.maven.plugins.install} + 3.1.4 org.apache.maven.plugins maven-jar-plugin - ${version.org.apache.maven.plugins.jar} + 3.4.2 org.apache.maven.plugins maven-javadoc-plugin - ${version.org.apache.maven.plugins.javadoc} + 3.11.2 org.apache.maven.plugins maven-release-plugin - ${version.org.apache.maven.plugins.release} + 3.1.1 - clean verify + test check - deploy + deploy -DskipITs deploy,release true @{prefix} prepare release @{releaseLabel} [CI SKIP] @@ -287,7 +269,7 @@ org.apache.maven.plugins maven-resources-plugin - ${version.org.apache.maven.plugins.resources} + 3.3.1 ${project.build.propertiesEncoding} @@ -295,43 +277,32 @@ org.apache.maven.plugins maven-site-plugin - ${version.org.apache.maven.plugins.site} + 3.21.0 org.apache.maven.plugins maven-source-plugin - ${version.org.apache.maven.plugins.source} + 3.3.1 org.apache.maven.plugins maven-surefire-plugin - ${version.org.apache.maven.plugins.surefire} + 3.5.3 org.codehaus.mojo tidy-maven-plugin - ${version.org.codehaus.mojo.tidy} + 1.4.0 - org.codehaus.mojo - versions-maven-plugin - ${version.org.codehaus.mojo.versions} - - false - - ^.*-(alpha|beta|M)-?[0-9]+$ - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${version.org.sonatype.plugins.nexus} + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 net.revelc.code.formatter formatter-maven-plugin - ${version.net.revelc.code.formatter} + 2.26.0 ${formatter.configFile} @@ -339,12 +310,7 @@ net.revelc.code impsort-maven-plugin - ${version.net.revelc.code.impsort} - - - io.fabric8 - docker-maven-plugin - ${version.io.fabric8.docker} + 1.12.0 @@ -496,7 +462,7 @@ - jar-no-fork + jar @@ -507,109 +473,12 @@ - javadoc-no-fork + jar - - - io.fabric8 - docker-maven-plugin - - - build - install - - build - - - - push - deploy - - push - - - - - build - - - docker.io/kokuwaio/keycloak-event-metrics:${project.version} - - docker.io/library/busybox:1.37.0-uclibc@sha256:cc57e0ff4b6d3138931ff5c7180d18078813300e2508a25fb767a4d36df30d4d - - ${file.separator} - - - - ${project.build.directory}/${project.build.finalName}.jar - ${project.artifactId}.jar - 444 - - - - - - - ${project.name} - ${project.description} - ${project.url} - ${project.scm.url} - ${project.organization.name} - https://github.com/orgs/kokuwaio/people - ${project.licenses[0].name} - ${project.version} - docker.io/library/busybox:1.37.0-uclibc - sha256:cc57e0ff4b6d3138931ff5c7180d18078813300e2508a25fb767a4d36df30d4d - - - - - ghcr.io/kokuwaio/keycloak-event-metrics:${project.version} - - docker.io/library/busybox:1.37.0-uclibc@sha256:cc57e0ff4b6d3138931ff5c7180d18078813300e2508a25fb767a4d36df30d4d - - ${file.separator} - - - - ${project.build.directory}/${project.build.finalName}.jar - ${project.artifactId}.jar - 444 - - - - - - - ${project.name} - ${project.description} - ${project.url} - ${project.scm.url} - ${project.organization.name} - https://github.com/orgs/kokuwaio/people - ${project.licenses[0].name} - ${project.version} - docker.io/library/busybox:1.37.0-uclibc - sha256:cc57e0ff4b6d3138931ff5c7180d18078813300e2508a25fb767a4d36df30d4d - - - - - - - - - - - - release - - - org.apache.maven.plugins @@ -619,22 +488,33 @@ sign + + bc + - - - org.sonatype.plugins - nexus-staging-maven-plugin - true - - sonatype-nexus - https://oss.sonatype.org/ - true - - - + + + + + release + + ${project.version} + + + + + org.sonatype.central + central-publishing-maven-plugin + true + + sonatype.org + true + published + + diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..dc88820 --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["local>infrastructure/renovate-config", ":reviewer(stephan.schnabel)"], + "pinDigests": false +} diff --git a/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakExtension.java b/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakExtension.java index 91f013f..aa179f7 100644 --- a/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakExtension.java +++ b/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakExtension.java @@ -15,6 +15,7 @@ import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolver; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.token.TokenService; +import org.testcontainers.containers.FixedHostPortGenericContainer; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.MountableFile; @@ -45,30 +46,33 @@ public class KeycloakExtension implements BeforeAllCallback, ParameterResolver { throw new Exception("Failed to read properties", e); } var version = properties.getProperty("version"); + var image = "quay.io/keycloak/keycloak:" + version; var jar = properties.getProperty("jar"); var timeout = properties.getProperty("timeout"); - // create and start container + // create and start container - use fixed port in ci - @SuppressWarnings("resource") - var container = new GenericContainer<>("quay.io/keycloak/keycloak:" + version) - .withEnv("KEYCLOAK_ADMIN", "admin") - .withEnv("KEYCLOAK_ADMIN_PASSWORD", "password") - .withEnv("KC_LOG_LEVEL", "io.kokuwa:trace") - // otherwise port 9000 will be used, with this config we can test different keycloak versions - .withEnv("KC_LEGACY_OBSERVABILITY_INTERFACE", "true") - .withEnv("KC_HEALTH_ENABLED", "true") - .withEnv("KC_METRICS_ENABLED", "true") - .withEnv("KC_METRICS_STATS_ENABLED", "true") - .withEnv("KC_METRICS_STATS_INTERVAL", "PT1s") - .withCopyFileToContainer(MountableFile.forHostPath(jar), "/opt/keycloak/providers/metrics.jar") - .withLogConsumer(out -> System.out.print(out.getUtf8String())) - .withExposedPorts(8080) - .withStartupTimeout(Duration.parse(timeout)) - .waitingFor(Wait.forHttp("/health").forPort(8080)) - .withCommand("start-dev"); + @SuppressWarnings({ "resource", "deprecation" }) + var container = (System.getenv("CI") == null + ? new GenericContainer<>(image).withExposedPorts(8080) + : new FixedHostPortGenericContainer<>(image).withFixedExposedPort(8080, 8080)); try { - container.start(); + container + .withEnv("KEYCLOAK_ADMIN", "admin") + .withEnv("KEYCLOAK_ADMIN_PASSWORD", "password") + .withEnv("KC_LOG_LEVEL", "io.kokuwa:trace") + // otherwise port 9000 will be used, with this config we can test different keycloak versions + .withEnv("KC_LEGACY_OBSERVABILITY_INTERFACE", "true") + .withEnv("KC_HEALTH_ENABLED", "true") + .withEnv("KC_METRICS_ENABLED", "true") + .withEnv("KC_METRICS_STATS_ENABLED", "true") + .withEnv("KC_METRICS_STATS_INTERVAL", "PT1s") + .withCopyFileToContainer(MountableFile.forHostPath(jar), "/opt/keycloak/providers/metrics.jar") + .withLogConsumer(out -> System.out.print(out.getUtf8String())) + .withStartupTimeout(Duration.parse(timeout)) + .waitingFor(Wait.forHttp("/health").forPort(8080).withStartupTimeout(Duration.ofMinutes(10))) + .withCommand("start-dev") + .start(); } catch (RuntimeException e) { throw new Exception("Failed to start keycloak", e); } diff --git a/test/1.jar b/test/1.jar new file mode 100644 index 0000000000000000000000000000000000000000..0a4f7c28afb325511afe77a91432c6945ec2f309 GIT binary patch literal 17006 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 TE5>;FnRzMH_WxeB4)p&30+yz5 literal 0 HcmV?d00001 diff --git a/test/2.jar b/test/2.jar new file mode 100644 index 0000000000000000000000000000000000000000..0a4f7c28afb325511afe77a91432c6945ec2f309 GIT binary patch literal 17006 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 TE5>;FnRzMH_WxeB4)p&30+yz5 literal 0 HcmV?d00001