diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2aceab5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,17 @@ +#git +.git +.gitattributes +.gitignore +.github/ +#IDE +.idea +#build +.gradle/ +build +#data +production_db +#misc +CHANGELOG +LICENSE +temp +docker diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml new file mode 100644 index 0000000..5e6d616 --- /dev/null +++ b/.github/workflows/docker_publish.yml @@ -0,0 +1,54 @@ +name: Create and publish a Docker image + +# Configures this workflow to run every time a change is pushed to the branch called `release`. +on: + push: + branches: ['main'] + +# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds. +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./docker + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + + # This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see [Using artifact attestations to establish provenance for builds](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds). + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v3 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true \ No newline at end of file diff --git a/.github/workflows/docker_testing.yml b/.github/workflows/docker_testing.yml new file mode 100644 index 0000000..776904a --- /dev/null +++ b/.github/workflows/docker_testing.yml @@ -0,0 +1,86 @@ +name: Build and Test Dockerized App + +on: + push: + pull_request: + branches: [ main, development ] + +jobs: + build-and-test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + java: [17, 21] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: ${{ matrix.java }} + + - name: Grant execute permission for Gradle wrapper (Unix) + if: runner.os != 'Windows' + run: chmod +x gradlew + + # Build Docker image instead of bootable JAR + - name: Build Docker image + run: | + docker build -f docker/Dockerfile -t wap-server:${{ matrix.os }}-${{ matrix.java }} . + + # Run Docker container (detached, expose port 8080) + - name: Run Docker container + run: | + docker run -d -p 8080:8080 --name wap-server \ + wap-server:${{ matrix.os }}-${{ matrix.java }} + echo "Wait for wap server to be healthy before proceeding to tests" + while true; do + docker ps -a + if ! docker ps | grep -q wap-server; then + echo "Docker container stopped unexpectedly. Aborting." + exit 1 + fi + if curl -f http://localhost:8080; then + echo "Service is running." + break + fi + echo "Waiting for the service to be ready..." + docker logs --tail 20 wap-server + sleep 5 + done + + - name: hurl install + uses: gacts/install-hurl@v1.3.0 + with: {disable-cache: true} + + - name: hurl CRUD tests (windows) + if: runner.os == 'Windows' + shell: bash + run: | + for file in ./integration_tests/CRUD/*.hurl; do + hurl --variable host=http://localhost:8080 --test "$file" --verbose --error-format=long --continue-on-error --report-html hurlreports + done + + - name: hurl tests (other) + if: runner.os != 'Windows' + run: hurl --variable host=http://localhost:8080 --test ./integration_tests/CRUD/*.hurl --verbose --error-format=long --continue-on-error --report-html hurlreports + + # Stop and clean up container + - name: Stop Docker container + if: always() + run: | + docker logs wap-server + docker stop wap-server + docker rm wap-server + + # Upload artifacts (optional: you may want to upload Docker logs instead of JARs) + - name: Upload Docker logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: docker_logs_jdk${{ matrix.java }}_${{ matrix.os }} + path: ./hurlreports diff --git a/README.md b/README.md index 3fa2585..29fb166 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,21 @@ This project contains a server for creating and managing annotations based on th the complete Web Annotation Protocol (WAP). The service is realized as microservice using Spring Boot and can be operated standalone. -## How to build +## How to build and use + +### Docker + +``` +docker build -f docker/Dockerfile -t wap-server . +``` + +To run the application at `http://localhost:`, use: + +``` +docker run -d -p :8080 -e WAPBASEPATH=http://localhost: wap-server +``` + +### From source To install the application from source, see [howtos](howtos/summary.md). diff --git a/build.gradle b/build.gradle index 8e45f92..66dbd63 100644 --- a/build.gradle +++ b/build.gradle @@ -188,3 +188,7 @@ tasks.register('printProjectName') { println "${project.name}" } } + +gradleLint { + skipForTask('printProjectName') +} diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..bc180c2 --- /dev/null +++ b/build.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# Check no of parameters. +if [ "$#" -ne 1 ]; then + echo "Illegal number of parameters!" + usage +fi + +INSTALLATION_DIRECTORY=$1 +ACTUAL_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +# Check if directory exists + if [ ! -d "$INSTALLATION_DIRECTORY" ]; then + # Create directory if it doesn't exists. + mkdir -p "$INSTALLATION_DIRECTORY" + if [ $? -ne 0 ]; then + echo "Error creating directory '$INSTALLATION_DIRECTORY'!" + echo "Please make sure that you have the correct access permissions for the specified directory." + exit 1 + fi + fi + # Check if directory is empty + if [ ! -z "$(ls -A "$INSTALLATION_DIRECTORY")" ]; then + echo "Directory '$INSTALLATION_DIRECTORY' is not empty!" + echo "Please enter an empty or a new directory!" + exit 1 + fi + # Convert variable of installation directory to an absolute path + cd "$INSTALLATION_DIRECTORY" + INSTALLATION_DIRECTORY=`pwd` + cd "$ACTUAL_DIR" + +APP_NAME=`./gradlew -q printProjectName` +APP_NAME=${APP_NAME##*$'\n'} +echo "$APP_NAME" + +echo Build service... +./gradlew -Dprofile=minimal clean build + +echo "Copy jar file to '$INSTALLATION_DIRECTORY'..." +find . -name "$APP_NAME.jar" -exec cp '{}' "$INSTALLATION_DIRECTORY" \; + +# Create run script +echo "$INSTALLATION_DIRECTORY" +cd "$INSTALLATION_DIRECTORY" + +# Determine name of jar file. +jarFile=(`ls $APP_NAME.jar`) + +java -jar $jarFile "--create-config" + +echo '#!/bin/sh' >> run.sh +echo 'pwd' >> run.sh +echo 'ls' >> run.sh +echo 'cat run.sh' >> run.sh +echo "################################################################################" >> run.sh +echo "# Define jar file" >> run.sh +echo "################################################################################" >> run.sh +echo jarFile=$jarFile >> run.sh +echo " " >> run.sh +echo "################################################################################" >> run.sh +echo "# Start micro service" >> run.sh +echo "################################################################################" >> run.sh +echo 'java -jar $jarFile' >> run.sh + +# make script executable +chmod 755 run.sh \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..84b0c56 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,34 @@ +FROM eclipse-temurin:17-jdk +ARG SERVER_NAME_DEFAULT=wapserver + +ARG PORT_DEFAULT=8080 +ARG SPARQ_DEFAULT=3330 +ARG HOST_DEFAULT=localhost +ARG ROOT_DIRECTORY_DEFAULT=/spring + +# Install git as additional requirement +RUN apt-get -y update && \ + apt-get -y upgrade && \ + apt-get install -y --no-install-recommends git bash && \ + apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +ENV SERVICE_DIRECTORY=${ROOT_DIRECTORY_DEFAULT}/${SERVER_NAME_DEFAULT} +RUN mkdir -p /git +WORKDIR /git +COPY .. . +# Build service in given directory +RUN echo $SERVICE_DIRECTORY +RUN bash ./build.sh $SERVICE_DIRECTORY + +#You can use this to set config variables or mount in an application.properties file +ENV SPRING_APPLICATION_JSON "{\"WapPort\": ${PORT_DEFAULT}, \"Hostname\": \"${HOST_DEFAULT}\"}" + +EXPOSE ${PORT_DEFAULT} +EXPOSE ${SPARQL_DEFAULT} +WORKDIR $SERVICE_DIRECTORY +COPY ../profiles ./profiles/ +COPY ../schemas ./schemas/ +COPY ../webcontent ./webcontent/ +COPY ../doc ./doc/ +ENTRYPOINT ["/spring/wapserver/run.sh"] \ No newline at end of file