diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml
new file mode 100644
index 00000000..61432a46
--- /dev/null
+++ b/.github/actions/build-image/action.yaml
@@ -0,0 +1,58 @@
+name: build-image
+description: "Builds and pushes a docker image"
+
+inputs:
+ registry:
+ description: "Image registry to push image to"
+ required: true
+ default: ghcr.io
+ registry-username:
+ description: "Username to authenticate against image registry"
+ required: true
+ registry-password:
+ description: "Password to authenticate against image registry"
+ required: true
+ path:
+ description: "Path to the Dockerfile to build image from"
+ required: true
+ image-name:
+ description: "Name to give the image"
+ required: true
+ image-tags:
+ description: "Tags to tag image with"
+ required: false
+ default: |
+ type=raw,value=latest
+ image-labels:
+ description: "Labels to add to image"
+ required: false
+ default: |
+ org.opencontainers.image.description=See ${{ github.server_url }}/${{ github.repository }}
+
+runs:
+ using: "composite"
+ steps:
+ - name: Download a single artifact
+ uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
+ with:
+ name: target
+ - name: Login to Registry
+ uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
+ with:
+ registry: ${{ inputs.registry }}
+ username: ${{ inputs.registry-username }}
+ password: ${{ inputs.registry-password }}
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
+ with:
+ images: "${{ inputs.registry }}/${{ github.repository }}/${{ inputs.image-name }}"
+ tags: ${{inputs.image-tags}}
+ labels: ${{inputs.image-labels}}
+ - name: Build and push image
+ uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0
+ with:
+ context: ./${{ inputs.path }}
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
diff --git a/.github/actions/build-maven/action.yaml b/.github/actions/build-maven/action.yaml
new file mode 100644
index 00000000..20ff38a7
--- /dev/null
+++ b/.github/actions/build-maven/action.yaml
@@ -0,0 +1,101 @@
+name: ""
+description: ""
+
+inputs:
+ module:
+ description: 'Module to build'
+ required: true
+ release:
+ description: 'Release?'
+ default: 'false'
+ release-version:
+ description: 'Release version'
+ required: false
+ default: "X.Y.Z"
+ next-version:
+ description: "Next version to use after release"
+ required: false
+ default: "X.Y.Z-SNAPSHOT"
+ java-version:
+ description: "Jave version used for building"
+ required: false
+ default: "21"
+ gpg-private-key:
+ description: "Gpg private key used for signing packages for maven central release"
+ required: false
+ gpg-passphrase:
+ description: "Gpg passphrase for private key"
+ required: false
+ sonatype-username:
+ description: "Sonatype username for maven central release"
+ required: false
+ sonatype-password:
+ description: "Sonatype password for maven central release"
+ required: false
+
+runs:
+ using: "composite"
+ steps:
+ - name: Set up JDK
+ uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2
+ with:
+ java-version: ${{ inputs.java-version }}
+ distribution: "temurin"
+ cache: "maven"
+ cache-dependency-path: "./${{ inputs.module }}/pom.xml"
+ server-id: "central"
+ server-username: CENTRAL_USERNAME
+ server-password: CENTRAL_PASSWORD
+ gpg-private-key: ${{ inputs.gpg-private-key }}
+ gpg-passphrase: SIGN_KEY_PASS
+ - name: Maven build
+ if: ${{ inputs.release != 'true' }}
+ shell: bash
+ run: mvn -f ./${{ inputs.module }}/pom.xml --batch-mode clean install
+ - name: Maven release
+ if: ${{ inputs.release == 'true' }}
+ shell: bash
+ run: |
+ git config --global user.email "github-actions@github.com"
+ git config --global user.name "GitHub Actions"
+ mvn release:prepare -f ./${{ inputs.module }}/pom.xml -B -DreleaseVersion=${{ inputs.release-version }} -DdevelopmentVersion=${{ inputs.next-version }} -DpushChanges=false -DremoteTagging=false
+ mvn release:perform -f ./${{ inputs.module }}/pom.xml -DlocalCheckout=true
+ env:
+ SIGN_KEY_PASS: ${{ inputs.gpg-passphrase }}
+ CENTRAL_USERNAME: ${{ inputs.sonatype-username }}
+ CENTRAL_PASSWORD: ${{ inputs.sonatype-password }}
+ - name: "Upload target artifacts"
+ uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
+ with:
+ name: target
+ path: "**/target"
+ retention-days: 5
+ - name: Push changes to new branch
+ if: ${{ inputs.release == 'true' }}
+ shell: bash
+ run: |
+ git checkout -b ${{ github.ref_name }}-version-bump
+ git push --force origin ${{ github.ref_name }}-version-bump
+ - name: Create pull request
+ if: ${{ inputs.release == 'true' }}
+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ with:
+ script: |
+ const { repo, owner } = context.repo;
+ const pullResult = await github.rest.pulls.create({
+ title: 'chore: bump release version ${{ github.ref_name }}',
+ owner,
+ repo,
+ head: '${{ github.ref_name }}-version-bump',
+ base: '${{ github.ref_name }}',
+ body: [
+ 'This PR is auto-generated'
+ ].join('\n')
+ });
+ await github.rest.issues.addAssignees({
+ owner,
+ repo,
+ issue_number: pullResult.data.number,
+ assignees: ['${{ github.actor }}'],
+ });
+ console.log(`Pull Request created: ${pullResult.data.html_url}`);
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 0ba12742..533d4f2a 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -3,21 +3,21 @@
"Type: Bug":
- head-branch: [ '^bug', 'bug' ]
"Type: Maintenance":
- - head-branch: [ '^maint', 'maintenance' ]
+ - head-branch: [ '^maint', 'maintenance', '^refact' ]
"Type: Documentation":
- head-branch: [ '^docs', "documentation" ]
- changed-files:
- - any-glob-to-any-file: ['docs/**']
+ - any-glob-to-any-file: [ 'docs/**' ]
"Type: Dependency":
- head-branch: [ '^dep', 'dependency' ]
"Type: Security":
- head-branch: [ '^sec', 'security' ]
"Component: API-Gateway":
- changed-files:
- - any-glob-to-any-file: ['refarch-gateway/**']
-"Component: Integration":
+ - any-glob-to-any-file: [ 'refarch-gateway/**' ]
+"Component: Integrations":
- changed-files:
- - any-glob-to-any-file: ['refarch-integrations/**']
+ - any-glob-to-any-file: [ 'refarch-integrations/**' ]
"Component: CLI":
- changed-files:
- - any-glob-to-any-file: ['refarch-cli/**']
\ No newline at end of file
+ - any-glob-to-any-file: [ 'refarch-cli/**' ]
diff --git a/.github/release.yml b/.github/release.yml
index b6b1cf92..cd3e0221 100644
--- a/.github/release.yml
+++ b/.github/release.yml
@@ -1,23 +1,100 @@
changelog:
categories:
- - title: 🎉 New Features
+ # refarch-gateway
+ - title: Gateway - 🎉 New Features
labels:
- "Type: Feature"
- - title: 🐞 Bug Fixes
+ exclude: &filter-gateway
+ labels:
+ - "Component: Integrations"
+ - "Component: CLI"
+ - title: Gateway - 🐞 Bug Fixes
labels:
- "Type: Bug"
- - title: 🔨 Maintenance
+ exclude: *filter-gateway
+ - title: Gateway - 🔨 Maintenance
labels:
- "Type: Maintenance"
- - title: 📔 Documentation
+ exclude: *filter-gateway
+ - title: Gateway - 📔 Documentation
labels:
- "Type: Documentation"
- - title: ⬆️ Dependency Upgrades
+ exclude: *filter-gateway
+ - title: Gateway - ⬆️ Dependency Upgrades
labels:
- "Type: Dependency"
- - title: 🔒️ Security
+ exclude: *filter-gateway
+ - title: Gateway - 🔒️ Security
labels:
- "Type: Security"
- - title: 💥 Breaking Changes
+ exclude: *filter-gateway
+ - title: Gateway - 💥 Breaking Changes
labels:
- - BREAKING
\ No newline at end of file
+ - BREAKING
+ exclude: *filter-gateway
+
+ # refarch-integrations
+ - title: Integrations - 🎉 New Features
+ labels:
+ - "Type: Feature"
+ exclude: &filter-integrations
+ labels:
+ - "Component: API-Gateway"
+ - "Component: CLI"
+ - title: Integrations - 🐞 Bug Fixes
+ labels:
+ - "Type: Bug"
+ exclude: *filter-integrations
+ - title: Integrations - 🔨 Maintenance
+ labels:
+ - "Type: Maintenance"
+ exclude: *filter-integrations
+ - title: Integrations - 📔 Documentation
+ labels:
+ - "Type: Documentation"
+ exclude: *filter-integrations
+ - title: Integrations - ⬆️ Dependency Upgrades
+ labels:
+ - "Type: Dependency"
+ exclude: *filter-integrations
+ - title: Integrations - 🔒️ Security
+ labels:
+ - "Type: Security"
+ exclude: *filter-integrations
+ - title: Integrations - 💥 Breaking Changes
+ labels:
+ - BREAKING
+ exclude: *filter-integrations
+
+ # refarch-cli
+ - title: CLI - 🎉 New Features
+ labels:
+ - "Type: Feature"
+ exclude: &filter-cli
+ labels:
+ - "Component: API-Gateway"
+ - "Component: Integrations"
+ - title: CLI - 🐞 Bug Fixes
+ labels:
+ - "Type: Bug"
+ exclude: *filter-cli
+ - title: CLI - 🔨 Maintenance
+ labels:
+ - "Type: Maintenance"
+ exclude: *filter-cli
+ - title: CLI - 📔 Documentation
+ labels:
+ - "Type: Documentation"
+ exclude: *filter-cli
+ - title: CLI - ⬆️ Dependency Upgrades
+ labels:
+ - "Type: Dependency"
+ exclude: *filter-cli
+ - title: CLI - 🔒️ Security
+ labels:
+ - "Type: Security"
+ exclude: *filter-cli
+ - title: CLI - 💥 Breaking Changes
+ labels:
+ - BREAKING
+ exclude: *filter-cli
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
deleted file mode 100644
index 7bc4e489..00000000
--- a/.github/workflows/build.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: build
-
-on:
- pull_request:
- push:
- branches:
- - main
-
-jobs:
- maven:
- uses: ./.github/workflows/maven.yaml
- with:
- build-images: ${{ github.ref_name == 'main' }}
- release-version: dev
- secrets: inherit
diff --git a/.github/workflows/build_gateway.yaml b/.github/workflows/build_gateway.yaml
new file mode 100644
index 00000000..0cb42e60
--- /dev/null
+++ b/.github/workflows/build_gateway.yaml
@@ -0,0 +1,35 @@
+name: build-gateway
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+
+jobs:
+ build-maven:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - name: Maven build
+ uses: ./.github/actions/build-maven
+ with:
+ module: refarch-gateway
+
+ build-images:
+ if: github.ref_name == 'main'
+ needs: build-maven
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - name: Build and push image
+ uses: ./.github/actions/build-image
+ with:
+ registry-username: ${{ github.actor }}
+ registry-password: ${{ secrets.GITHUB_TOKEN }}
+ path: ./refarch-gateway
+ image-name: refarch-gateway
+ image-tags: |
+ type=raw,value=dev
diff --git a/.github/workflows/build_integrations.yaml b/.github/workflows/build_integrations.yaml
new file mode 100644
index 00000000..f21c1c66
--- /dev/null
+++ b/.github/workflows/build_integrations.yaml
@@ -0,0 +1,40 @@
+name: build-integrations
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+
+jobs:
+ build-maven:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - name: Maven build
+ uses: ./.github/actions/build-maven
+ with:
+ module: refarch-integrations
+
+ build-images:
+ if: github.ref_name == 'main'
+ needs: build-maven
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ include:
+ - name: s3-integration-rest-service
+ path: ./refarch-integrations/refarch-s3-integration/refarch-s3-integration-rest/refarch-s3-integration-rest-service
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - name: Build and push image
+ uses: ./.github/actions/build-image
+ with:
+ registry-username: ${{ github.actor }}
+ registry-password: ${{ secrets.GITHUB_TOKEN }}
+ path: ${{ matrix.path }}
+ image-name: ${{ matrix.name }}
+ image-tags: |
+ type=raw,value=dev
diff --git a/.github/workflows/maven.yaml b/.github/workflows/maven.yaml
deleted file mode 100644
index 432956ce..00000000
--- a/.github/workflows/maven.yaml
+++ /dev/null
@@ -1,132 +0,0 @@
-env:
- JAVA_VERSION: 21
- REGISTRY: ghcr.io
- TZ: Europe/Berlin
-
-on:
- workflow_call:
- inputs:
- snapshot-release:
- description: 'Snapshot release?'
- type: boolean
- default: true
- build-images:
- description: 'Build and push images?'
- type: boolean
- default: false
- release-version:
- description: 'Release version'
- type: string
- required: false
- default: "X.Y.Z"
- next-version:
- description: "Next version to use after release."
- type: string
- required: false
- default: "X.Y.Z-SNAPSHOT"
-
-jobs:
- build-maven:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout code
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- - name: Set up JDK
- uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2
- with:
- java-version: ${{ env.JAVA_VERSION }}
- distribution: "temurin"
- cache: "maven"
- server-id: "central"
- server-username: CENTRAL_USERNAME
- server-password: CENTRAL_PASSWORD
- gpg-private-key: ${{ secrets.gpg_private_key }}
- gpg-passphrase: SIGN_KEY_PASS
- - name: Maven build
- if: ${{ inputs.snapshot-release != false }}
- run: mvn --batch-mode clean install
- - name: Maven release
- if: ${{ inputs.snapshot-release == false }}
- run: |
- git config --global user.email "github-actions@github.com"
- git config --global user.name "GitHub Actions"
- mvn release:prepare -B -DreleaseVersion=${{ inputs.release-version }} -DdevelopmentVersion=${{ inputs.next-version }} -DpushChanges=false -DremoteTagging=false
- mvn release:perform -DlocalCheckout=true
- env:
- SIGN_KEY_PASS: ${{ secrets.gpg_passphrase }}
- CENTRAL_USERNAME: ${{ secrets.sonatype_username }}
- CENTRAL_PASSWORD: ${{ secrets.sonatype_password }}
- - name: "Upload target artifacts"
- uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
- with:
- name: target
- path: "**/target"
- retention-days: 5
- - name: Push changes to new branch
- if: ${{ inputs.snapshot-release == false }}
- run: |
- git checkout -b ${{ github.ref_name }}-version-bump
- git push --force origin ${{ github.ref_name }}-version-bump
- - name: Create pull request
- if: ${{ inputs.snapshot-release == false }}
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
- with:
- script: |
- const { repo, owner } = context.repo;
- const pullResult = await github.rest.pulls.create({
- title: 'chore: bump release version ${{ github.ref_name }}',
- owner,
- repo,
- head: '${{ github.ref_name }}-version-bump',
- base: '${{ github.ref_name }}',
- body: [
- 'This PR is auto-generated'
- ].join('\n')
- });
- await github.rest.issues.addAssignees({
- owner,
- repo,
- issue_number: pullResult.data.number,
- assignees: ['${{ github.actor }}'],
- });
- console.log(`Pull Request created: ${pullResult.data.html_url}`);
-
- build-images:
- if: inputs.build-images == true
- needs: build-maven
- runs-on: ubuntu-latest
- strategy:
- matrix:
- include:
- - name: refarch-gateway
- path: ./refarch-gateway
- - name: s3-integration-rest-service
- path: ./refarch-integrations/refarch-s3-integration/refarch-s3-integration-rest/refarch-s3-integration-rest-service
- steps:
- - name: Checkout code
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- - name: Download target artifacts
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
- with:
- name: target
- - name: Login to Registry
- uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
- with:
- registry: ${{ env.REGISTRY }}
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
- - name: Extract metadata (tags, labels) for image
- id: meta
- uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
- with:
- images: "${{ env.REGISTRY }}/${{ github.repository }}/${{ matrix.name }}"
- tags: |
- type=raw,value=${{ inputs.release-version }}
- type=raw,value=latest,enable=${{ inputs.snapshot-release == false }}
- - name: Build and push image
- uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0
- with:
- context: ${{ matrix.path }}
- push: true
- tags: ${{ steps.meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 743fcada..7bb35ce8 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -3,8 +3,15 @@ name: release
on:
workflow_dispatch:
inputs:
- snapshot-release:
- description: 'Snapshot release?'
+ module:
+ description: 'Module to release'
+ type: choice
+ required: true
+ options:
+ - refarch-gateway
+ - refarch-integrations
+ release:
+ description: 'Release?'
type: boolean
default: true
release-version:
@@ -13,30 +20,80 @@ on:
required: true
default: "X.Y.Z"
next-version:
- description: "Next version to use after release."
+ description: "Next version to use after release"
type: string
required: true
default: "X.Y.Z-SNAPSHOT"
jobs:
- build:
- uses: ./.github/workflows/maven.yaml
- with:
- snapshot-release: ${{ inputs.snapshot-release != false }}
- build-images: true
- release-version: ${{ inputs.release-version }}
- secrets: inherit
+ build-maven:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - name: Maven build and release
+ uses: ./.github/actions/build-maven
+ with:
+ module: ${{ inputs.module }}
+ release: ${{ inputs.release }}
+ release-version: ${{ inputs.release-version }}
+ next-version: ${{ inputs.next-version }}
+ gpg-private-key: ${{ secrets.gpg_private_key }}
+ gpg-passphrase: ${{ secrets.gpg_passphrase }}
+ sonatype-username: ${{ secrets.sonatype_username }}
+ sonatype-password: ${{ secrets.sonatype_password }}
+
+ build-images-gateway:
+ if: inputs.module == 'refarch-gateway'
+ needs: build-maven
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - name: Build and push image
+ uses: ./.github/actions/build-image
+ with:
+ registry-username: ${{ github.actor }}
+ registry-password: ${{ secrets.GITHUB_TOKEN }}
+ path: ./refarch-gateway
+ image-name: refarch-gateway
+ image-tags: |
+ type=raw,value=${{ inputs.release-version }}
+ type=raw,value=latest,enable=${{ inputs.release == true }}
+
+ build-images-integrations:
+ if: inputs.module == 'refarch-integrations'
+ needs: build-maven
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ include:
+ - name: s3-integration-rest-service
+ path: ./refarch-integrations/refarch-s3-integration/refarch-s3-integration-rest/refarch-s3-integration-rest-service
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - name: Build and push images
+ uses: ./.github/actions/build-image
+ with:
+ registry-username: ${{ github.actor }}
+ registry-password: ${{ secrets.GITHUB_TOKEN }}
+ path: ${{ matrix.path }}
+ image-name: ${{ matrix.name }}
+ image-tags: |
+ type=raw,value=${{ inputs.release-version }}
+ type=raw,value=latest,enable=${{ inputs.release == true }}
create-github-release:
- if: ${{ inputs.snapshot-release == false }}
- needs: build
+ if: ${{ inputs.release == true }}
+ needs: build-maven
runs-on: ubuntu-latest
steps:
- name: Create GitHub Release
id: create_release
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
with:
- tag_name: ${{ github.event.inputs.release-version }}
+ tag_name: "${{ inputs.module }}_${{ github.event.inputs.release-version }}"
draft: false
prerelease: false
generate_release_notes: true
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 692ed95d..00000000
--- a/pom.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
- 4.0.0
-
- de.muenchen
- refarch
- refarch
- Collection of different ready to use RefArch components
- 1.1.0-SNAPSHOT
- pom
-
-
- refarch-gateway
- refarch-integrations
-
-
-
- https://github.com/it-at-m/refarch.git
- scm:git:https://github.com/it-at-m/refarch.git
- scm:git:https://github.com/it-at-m/refarch.git
- HEAD
-
-
-
- MIT
-
-
-
-
- it@M
- opensource@muenchen.de
- https://github.com/it-at-m
-
-
-
diff --git a/refarch-gateway/pom.xml b/refarch-gateway/pom.xml
index 6295d921..ee75ca5a 100644
--- a/refarch-gateway/pom.xml
+++ b/refarch-gateway/pom.xml
@@ -37,6 +37,9 @@
0.8.12
+
+ 3.1.1
+
3.17.0
@@ -181,6 +184,22 @@
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ ${maven-release-plugin.version}
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+
+ true
+
+