From abc51027356892befdd76bfe3568ade4088d06c4 Mon Sep 17 00:00:00 2001 From: Jan Frank Date: Wed, 5 Oct 2022 09:53:59 +0200 Subject: [PATCH 1/7] WIP. First version with basic test --- .github/workflows/main.yaml | 4 +- build/package/Dockerfile.sbt-toolset | 45 +++++++ build/package/scripts/build-sbt.sh | 87 +++++++++++++ .../sonar-project.properties.d/sbt.properties | 5 + .../images/templates/bc-ods-sbt-toolset.yaml | 30 +++++ .../images/templates/is-ods-sbt-toolset.yaml | 10 ++ .../tasks/templates/task-ods-build-sbt.yaml | 122 ++++++++++++++++++ .../charts/tasks/values.docs.yaml | 1 + deploy/ods-pipeline/values.yaml | 1 + test/tasks/ods-build-sbt_test.go | 46 +++++++ .../workspaces/sbt-sample-app/.gitignore | 9 ++ .../workspaces/sbt-sample-app/.scalafmt.conf | 4 + .../app/controllers/HomeController.scala | 21 +++ .../sbt-sample-app/app/views/index.scala.html | 5 + .../sbt-sample-app/app/views/main.scala.html | 25 ++++ .../workspaces/sbt-sample-app/build.sbt | 20 +++ .../sbt-sample-app/conf/application.conf | 2 + .../sbt-sample-app/conf/logback.xml | 42 ++++++ .../workspaces/sbt-sample-app/conf/messages | 1 + .../workspaces/sbt-sample-app/conf/routes | 10 ++ .../sbt-sample-app/docker/Dockerfile | 13 ++ .../sbt-sample-app/project/build.properties | 1 + .../sbt-sample-app/project/plugins.sbt | 6 + .../sbt-sample-app/public/images/favicon.png | Bin 0 -> 687 bytes .../sbt-sample-app/public/javascripts/main.js | 0 .../public/stylesheets/main.css | 0 .../test/controllers/HomeControllerSpec.scala | 44 +++++++ 27 files changed, 552 insertions(+), 2 deletions(-) create mode 100644 build/package/Dockerfile.sbt-toolset create mode 100755 build/package/scripts/build-sbt.sh create mode 100644 build/package/sonar-project.properties.d/sbt.properties create mode 100644 deploy/ods-pipeline/charts/images/templates/bc-ods-sbt-toolset.yaml create mode 100644 deploy/ods-pipeline/charts/images/templates/is-ods-sbt-toolset.yaml create mode 100644 deploy/ods-pipeline/charts/tasks/templates/task-ods-build-sbt.yaml create mode 100644 test/tasks/ods-build-sbt_test.go create mode 100644 test/testdata/workspaces/sbt-sample-app/.gitignore create mode 100644 test/testdata/workspaces/sbt-sample-app/.scalafmt.conf create mode 100644 test/testdata/workspaces/sbt-sample-app/app/controllers/HomeController.scala create mode 100644 test/testdata/workspaces/sbt-sample-app/app/views/index.scala.html create mode 100644 test/testdata/workspaces/sbt-sample-app/app/views/main.scala.html create mode 100644 test/testdata/workspaces/sbt-sample-app/build.sbt create mode 100644 test/testdata/workspaces/sbt-sample-app/conf/application.conf create mode 100644 test/testdata/workspaces/sbt-sample-app/conf/logback.xml create mode 100644 test/testdata/workspaces/sbt-sample-app/conf/messages create mode 100644 test/testdata/workspaces/sbt-sample-app/conf/routes create mode 100644 test/testdata/workspaces/sbt-sample-app/docker/Dockerfile create mode 100644 test/testdata/workspaces/sbt-sample-app/project/build.properties create mode 100644 test/testdata/workspaces/sbt-sample-app/project/plugins.sbt create mode 100644 test/testdata/workspaces/sbt-sample-app/public/images/favicon.png create mode 100644 test/testdata/workspaces/sbt-sample-app/public/javascripts/main.js create mode 100644 test/testdata/workspaces/sbt-sample-app/public/stylesheets/main.css create mode 100644 test/testdata/workspaces/sbt-sample-app/test/controllers/HomeControllerSpec.scala diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index b4c8a9fb..8d2e06c3 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: true matrix: - image: ["buildah", "finish", "go-toolset", "gradle-toolset", "helm", "sonar", "start", "pipeline-manager", "python-toolset", "node16-npm-toolset"] + image: ["buildah", "finish", "go-toolset", "gradle-toolset", "helm", "sonar", "start", "pipeline-manager", "python-toolset", "node16-npm-toolset", "sbt-toolset"] steps: - name: Checkout @@ -53,7 +53,7 @@ jobs: runs-on: ubuntu-latest needs: build-images env: - IMAGES: buildah finish go-toolset gradle-toolset helm sonar start pipeline-manager python-toolset node16-npm-toolset + IMAGES: buildah finish go-toolset gradle-toolset helm sonar start pipeline-manager python-toolset node16-npm-toolset sbt-toolset steps: - name: Download image artifacts diff --git a/build/package/Dockerfile.sbt-toolset b/build/package/Dockerfile.sbt-toolset new file mode 100644 index 00000000..d25e8152 --- /dev/null +++ b/build/package/Dockerfile.sbt-toolset @@ -0,0 +1,45 @@ +FROM registry.access.redhat.com/ubi8/openjdk-11:1.13 + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +ARG SBT_VERSION=1.7.1 +ARG SBT_SHA256=2ae094410b518acc46d9cdcdc4e138b4c27cd5040fc91d48d2d823fe84984294 + +ENV LANG=en_US.UTF-8 \ + LC_ALL=en_US.UTF-8 + +USER root + +RUN microdnf install --nodocs openssl-${OPENSSL_VERSION}* git-${GIT_VERSION}* && microdnf clean all + +RUN SBT_INSTALL_DIR=/tmp/sbt-install && \ + mkdir -p $SBT_INSTALL_DIR && \ + cd $SBT_INSTALL_DIR && \ + curl -LO https://github.com/sbt/sbt/releases/download/v${SBT_VERSION}/sbt-${SBT_VERSION}.zip && \ + echo $SBT_SHA256 sbt-${SBT_VERSION}.zip | sha256sum -c - && \ + unzip -d /opt sbt-${SBT_VERSION}.zip && \ + ln -s /opt/sbt/bin/sbt /usr/local/bin/sbt && \ + rm -rf $SBT_INSTALL_DIR + +RUN sbt -v sbtVersion && \ + rm -rf project target + +# Add scripts +COPY build/package/scripts/cache-build.sh /usr/local/bin/cache-build +COPY build/package/scripts/copy-build-if-cached.sh /usr/local/bin/copy-build-if-cached +COPY build/package/scripts/copy-artifacts.sh /usr/local/bin/copy-artifacts +COPY build/package/scripts/build-sbt.sh /usr/local/bin/build-sbt +COPY build/package/scripts/supply-sonar-project-properties-default.sh /usr/local/bin/supply-sonar-project-properties-default +# TODO set sbt http proxy COPY build/package/scripts/set-sbt-proxy.sh /usr/local/bin/set-sbt-proxy +RUN chmod +x /usr/local/bin/build-sbt && \ + chmod +x /usr/local/bin/cache-build && \ + chmod +x /usr/local/bin/copy-build-if-cached && \ + chmod +x /usr/local/bin/copy-artifacts && \ + chmod +x /usr/local/bin/supply-sonar-project-properties-default && \ + chown -R 1001:0 /tmp/.sbt +# TODO set sbt http proxy chmod +x /usr/local/bin/set-sbt-proxy # TODO set sbt http proxy + +# Add sonar-project.properties +COPY build/package/sonar-project.properties.d/sbt.properties /usr/local/default-sonar-project.properties + +USER 1001 diff --git a/build/package/scripts/build-sbt.sh b/build/package/scripts/build-sbt.sh new file mode 100755 index 00000000..b5e5f66e --- /dev/null +++ b/build/package/scripts/build-sbt.sh @@ -0,0 +1,87 @@ +#!/bin/bash +set -eu + +OUTPUT_DIR="docker" +WORKING_DIR="." +ROOT_DIR=$(pwd) +export ARTIFACTS_DIR=$ROOT_DIR/.ods/artifacts + +# might be needed for several task executions that would publish to the same artefact path... +ARTIFACT_PREFIX= +DEBUG="${DEBUG:-false}" + +while [[ "$#" -gt 0 ]]; do + case $1 in + + --working-dir) WORKING_DIR="$2"; shift;; + --working-dir=*) WORKING_DIR="${1#*=}";; + + --output-dir) OUTPUT_DIR="$2"; shift;; + --output-dir=*) OUTPUT_DIR="${1#*=}";; + + *) echo "Unknown parameter passed: $1"; exit 1;; +esac; shift; done + +if [ "${WORKING_DIR}" != "." ]; then + WORKING_DIR="${ROOT_DIR}" + ARTIFACT_PREFIX="${WORKING_DIR/\//-}-" +fi + +if [ "${DEBUG}" == "true" ]; then + set -x +fi + +echo "Using NEXUS_URL=$NEXUS_URL" +echo "Using ARTIFACTS_DIR=$ARTIFACTS_DIR" + +echo +cd "${WORKING_DIR}" +echo "Working on SBT project in '${WORKING_DIR}'..." +echo +export ODS_OUTPUT_DIR=${OUTPUT_DIR} +echo "Exported env var 'ODS_OUTPUT_DIR' with value '${OUTPUT_DIR}'" +echo +echo "Building (Compile and Test) ..." +# shellcheck disable=SC2086 +# old command: "sbt clean scalafmtSbtCheck scalafmtCheckAll coverage test coverageReport copyDockerFiles" +# plugins needed by the build: scalafmt scoverage native-packager +# TODO: with scoverage the build needs to run two times, one time with coverage and one time without, otherwise the production code +# will end up instrumented in production... This is due to the fact that scoverage has no way for on-the-fly instrumentation which only happens in memory + +# check format of sbt and source files, activate coverage and test with coverage report +sbt -no-colors -v clean scalafmtSbtCheck scalafmtCheckAll coverage test coverageReport + +# copy reports +echo "Verifying unit test report was generated ..." +BUILD_DIR="target" +UNIT_TEST_RESULT_DIR="${BUILD_DIR}/test-reports" +if [ -d "${UNIT_TEST_RESULT_DIR}" ]; then + UNIT_TEST_ARTIFACTS_DIR="${ARTIFACTS_DIR}/xunit-reports" + mkdir -p "${UNIT_TEST_ARTIFACTS_DIR}" + cp "${UNIT_TEST_RESULT_DIR}/"*.xml "${UNIT_TEST_ARTIFACTS_DIR}/${ARTIFACT_PREFIX}" +else + echo "Build failed: no unit test results found in ${UNIT_TEST_RESULT_DIR}" + exit 1 +fi + +echo "Verifying unit test coverage report was generated ..." +COVERAGE_RESULT_DIR="${BUILD_DIR}/scala-2.13" +if [ -d "${COVERAGE_RESULT_DIR}" ]; then + CODE_COVERAGE_ARTIFACTS_DIR="${ARTIFACTS_DIR}/code-coverage" + mkdir -p "${CODE_COVERAGE_ARTIFACTS_DIR}" + cp "${COVERAGE_RESULT_DIR}/scoverage-report/scoverage.xml" "${CODE_COVERAGE_ARTIFACTS_DIR}/${ARTIFACT_PREFIX}scoverage.xml" +else + echo "Build failed: no unit test coverage report was found in ${COVERAGE_RESULT_DIR}" + exit 1 +fi + +# create a clean binary as the previous compiled sources where instrumented for the coverage report +sbt -no-colors -v clean stage + +STAGING_DIR="${BUILD_DIR}/universal/stage" +echo "Copying contents of ${STAGING_DIR} to ${OUTPUT_DIR}/dist ..." +cp -r "${STAGING_DIR}/." "${OUTPUT_DIR}/dist" + +# TODO oder alles in einem command: +# sbt clean coverage test coverageReport coverageOff clean compile / publishMyReports / copy stuff to docker... +# dann muss man nur dafür sorgen, dass die reports vor dem 2. clean gesaved werden... diff --git a/build/package/sonar-project.properties.d/sbt.properties b/build/package/sonar-project.properties.d/sbt.properties new file mode 100644 index 00000000..77835a62 --- /dev/null +++ b/build/package/sonar-project.properties.d/sbt.properties @@ -0,0 +1,5 @@ +# this is just a fallback sonar properties. A matching one should be provided by each project +sonar.sources=app +sonar.sourceEncoding=UTF-8 +sonar.scala.version=2.13 +sonar.scala.scapegoat.disable=true diff --git a/deploy/ods-pipeline/charts/images/templates/bc-ods-sbt-toolset.yaml b/deploy/ods-pipeline/charts/images/templates/bc-ods-sbt-toolset.yaml new file mode 100644 index 00000000..b2aa2e41 --- /dev/null +++ b/deploy/ods-pipeline/charts/images/templates/bc-ods-sbt-toolset.yaml @@ -0,0 +1,30 @@ +{{if or .Values.global.enabledTasks.buildSbt .Values.sbtToolset}} +kind: BuildConfig +apiVersion: build.openshift.io/v1 +metadata: + name: ods-sbt-toolset + labels: + {{- include "chart.labels" . | nindent 4}} +spec: + nodeSelector: null + output: + to: + kind: ImageStreamTag + name: 'ods-sbt-toolset:{{.Values.global.imageTag | default .Chart.AppVersion}}' + resources: {} + successfulBuildsHistoryLimit: 5 + failedBuildsHistoryLimit: 5 + postCommit: {} + strategy: + type: Docker + dockerStrategy: + buildArgs: + - name: imageTag + value: '{{.Values.global.imageTag | default .Chart.AppVersion}}' + - name: privateCertServer + value: '{{.Values.privateCertServer}}' + source: + dockerfile: |- + {{- .Files.Get "docker/Dockerfile.sbt-toolset" | nindent 6}} + runPolicy: Serial +{{end}} diff --git a/deploy/ods-pipeline/charts/images/templates/is-ods-sbt-toolset.yaml b/deploy/ods-pipeline/charts/images/templates/is-ods-sbt-toolset.yaml new file mode 100644 index 00000000..e9bbaa99 --- /dev/null +++ b/deploy/ods-pipeline/charts/images/templates/is-ods-sbt-toolset.yaml @@ -0,0 +1,10 @@ +{{if or .Values.global.enabledTasks.buildSbt .Values.sbtToolset}} +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: ods-sbt-toolset + labels: + {{- include "chart.labels" . | nindent 4}} + annotations: + "helm.sh/resource-policy": keep +{{end}} diff --git a/deploy/ods-pipeline/charts/tasks/templates/task-ods-build-sbt.yaml b/deploy/ods-pipeline/charts/tasks/templates/task-ods-build-sbt.yaml new file mode 100644 index 00000000..8f5a6d99 --- /dev/null +++ b/deploy/ods-pipeline/charts/tasks/templates/task-ods-build-sbt.yaml @@ -0,0 +1,122 @@ +{{if .Values.global.enabledTasks.buildSbt }} +apiVersion: tekton.dev/v1beta1 +kind: '{{default "Task" .Values.global.taskKind}}' +metadata: + name: '{{default "ods" .Values.taskPrefix}}-build-sbt{{- include "taskSuffix" .}}' + annotations: + "helm.sh/resource-policy": keep +spec: + description: | + sbt build - TBD + params: + - name: working-dir + description: | + Working directory. The path must be relative to the root of the repository, + without leading `./` and trailing `/`. + type: string + default: "." + - name: output-dir + description: >- + Path to the directory into which the resulting build artifact should be copied, relative to `working-dir`. + This directory may then later be used as Docker context for example. + type: string + default: docker + - name: cache-build + description: >- + If enabled tasks uses or populates cache with the output dir contents (and artifacts) so that + a build can be skipped if the `working-dir` contents did not change. + For single build repos enabling build caching has limited benefits. For multi build repos enabling this is recommended unless the build is dependant on files outside of the working directory. See ADR caching-build-tasks for more details and workarounds. + type: string + default: "false" + - name: build-script + description: >- + Build script to execute. The + link:https://github.com/opendevstack/ods-pipeline/blob/master/build/package/scripts/build-sbt.sh[default script] + is located in the container image. If you specify a relative path + instead, it will be resolved from the workspace. See the task definition + for details how the build script is invoked. + type: string + default: "/usr/local/bin/build-sbt" + - name: sonar-quality-gate + description: Whether the SonarQube quality gate needs to pass for the task to succeed. + type: string + default: "false" + - name: sonar-skip + description: Whether to skip SonarQube analysis or not. + type: string + default: "false" + results: + - description: The cache location that the build task used. If caching is not enabled this will be an empty string. + name: build-reused-from-location + {{- with ((.Values.sbt).sidecars) }} + sidecars: + {{- toYaml . | nindent 4 }} + {{- end }} + steps: + - name: build-sbt-binary + # Image is built from build/package/Dockerfile.sbt-toolset. + image: '{{.Values.registry}}/{{default .Release.Namespace .Values.namespace}}/ods-sbt-toolset:{{.Values.global.imageTag | default .Chart.AppVersion}}' + env: + - name: DEBUG + valueFrom: + configMapKeyRef: + key: debug + name: ods-pipeline + - name: HOME + value: '/tekton/home' + - name: CI + value: "true" + - name: NEXUS_URL + valueFrom: + configMapKeyRef: + key: url + name: ods-nexus + - name: NEXUS_USERNAME + valueFrom: + secretKeyRef: + key: username + name: ods-nexus-auth + - name: NEXUS_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: ods-nexus-auth + resources: + {{- (.Values.sbt).resources | default dict | toYaml | nindent 8 }} + script: | + supply-sonar-project-properties-default + echo -n "" > $(results.build-reused-from-location.path) + cache_build_key=sbt + if copy-build-if-cached \ + --cache-build=$(params.cache-build) \ + --cache-build-key="$cache_build_key" \ + --cache-location-used-path=$(results.build-reused-from-location.path) \ + --working-dir=$(params.working-dir) \ + --output-dir=$(params.output-dir) \ + --debug=${DEBUG} ; then + exit 0 + fi + # Default build script is build/package/scripts/build-sbt.sh. + set +e + $(params.build-script) \ + --working-dir=$(params.working-dir) \ + --output-dir=$(params.output-dir) + build_exit=$? + set -e + copy-artifacts --debug=${DEBUG} + if [ $build_exit -ne 0 ]; then + exit $build_exit + fi + if [ "$(params.cache-build)" == "true" ]; then + cache-build \ + --cache-build-key="$cache_build_key" \ + --cache-location-used-path=$(results.build-reused-from-location.path) \ + --working-dir=$(params.working-dir) \ + --output-dir=$(params.output-dir) \ + --debug=${DEBUG} + fi + workingDir: $(workspaces.source.path) + {{- include "sonar-step" . | indent 4}} + workspaces: + - name: source +{{end}} diff --git a/deploy/ods-pipeline/charts/tasks/values.docs.yaml b/deploy/ods-pipeline/charts/tasks/values.docs.yaml index 585202c0..27b65505 100644 --- a/deploy/ods-pipeline/charts/tasks/values.docs.yaml +++ b/deploy/ods-pipeline/charts/tasks/values.docs.yaml @@ -7,6 +7,7 @@ global: buildGradle: true buildPython: true buildNPM: true + buildSbt: true packageImage: true deployHelm: true diff --git a/deploy/ods-pipeline/values.yaml b/deploy/ods-pipeline/values.yaml index 71e443db..2984ae2c 100644 --- a/deploy/ods-pipeline/values.yaml +++ b/deploy/ods-pipeline/values.yaml @@ -16,6 +16,7 @@ global: buildGradle: true buildPython: true buildNPM: true + buildSbt: true packageImage: true deployHelm: true diff --git a/test/tasks/ods-build-sbt_test.go b/test/tasks/ods-build-sbt_test.go new file mode 100644 index 00000000..574dde35 --- /dev/null +++ b/test/tasks/ods-build-sbt_test.go @@ -0,0 +1,46 @@ +package tasks + +import ( + "path/filepath" + "testing" + "time" + + "github.com/opendevstack/pipeline/pkg/pipelinectxt" + + "github.com/opendevstack/pipeline/pkg/tasktesting" +) + +func TestTaskODSBuildSbt(t *testing.T) { + runTaskTestCases(t, + "ods-build-sbt", + []tasktesting.Service{ + tasktesting.Nexus, + tasktesting.SonarQube, + }, + map[string]tasktesting.TestCase{ + "task should build sbt sample app": { + Timeout: 10 * time.Minute, + WorkspaceDirMapping: map[string]string{"source": "sbt-sample-app"}, + PreRunFunc: func(t *testing.T, ctxt *tasktesting.TaskRunContext) { + wsDir := ctxt.Workspaces["source"] + ctxt.ODS = tasktesting.SetupGitRepo(t, ctxt.Namespace, wsDir) + ctxt.Params = map[string]string{ + "sonar-quality-gate": "true", + } + }, + WantRunSuccess: true, + PostRunFunc: func(t *testing.T, ctxt *tasktesting.TaskRunContext) { + wsDir := ctxt.Workspaces["source"] + + checkFilesExist(t, wsDir, + "docker/Dockerfile", + "docker/dist", + filepath.Join(pipelinectxt.XUnitReportsPath, "TEST-controllers.HomeControllerSpec.xml"), + filepath.Join(pipelinectxt.CodeCoveragesPath, "scoverage.xml"), + filepath.Join(pipelinectxt.SonarAnalysisPath, "analysis-report.md"), + filepath.Join(pipelinectxt.SonarAnalysisPath, "issues-report.csv"), + ) + }, + }, + }) +} diff --git a/test/testdata/workspaces/sbt-sample-app/.gitignore b/test/testdata/workspaces/sbt-sample-app/.gitignore new file mode 100644 index 00000000..dce73038 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/.gitignore @@ -0,0 +1,9 @@ +logs +target +/.bsp +/.idea +/.idea_modules +/.classpath +/.project +/.settings +/RUNNING_PID diff --git a/test/testdata/workspaces/sbt-sample-app/.scalafmt.conf b/test/testdata/workspaces/sbt-sample-app/.scalafmt.conf new file mode 100644 index 00000000..f60df222 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/.scalafmt.conf @@ -0,0 +1,4 @@ +version = 3.5.8 +runner.dialect = scala213source3 +align.preset = more // For pretty alignment. +maxColumn = 100 // For my wide 30" display. \ No newline at end of file diff --git a/test/testdata/workspaces/sbt-sample-app/app/controllers/HomeController.scala b/test/testdata/workspaces/sbt-sample-app/app/controllers/HomeController.scala new file mode 100644 index 00000000..11fd5901 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/app/controllers/HomeController.scala @@ -0,0 +1,21 @@ +package controllers + +import javax.inject._ +import play.api._ +import play.api.mvc._ + +/** This controller creates an `Action` to handle HTTP requests to the application's home page. + */ +@Singleton +class HomeController @Inject() (val controllerComponents: ControllerComponents) + extends BaseController { + + /** Create an Action to render an HTML page. + * + * The configuration in the `routes` file means that this method will be called when the + * application receives a `GET` request with a path of `/`. + */ + def index() = Action { implicit request: Request[AnyContent] => + Ok(views.html.index()) + } +} diff --git a/test/testdata/workspaces/sbt-sample-app/app/views/index.scala.html b/test/testdata/workspaces/sbt-sample-app/app/views/index.scala.html new file mode 100644 index 00000000..68d37fb1 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/app/views/index.scala.html @@ -0,0 +1,5 @@ +@() + +@main("Welcome to Play") { +

Welcome to Play!

+} diff --git a/test/testdata/workspaces/sbt-sample-app/app/views/main.scala.html b/test/testdata/workspaces/sbt-sample-app/app/views/main.scala.html new file mode 100644 index 00000000..808a8b89 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/app/views/main.scala.html @@ -0,0 +1,25 @@ +@* + * This template is called from the `index` template. This template + * handles the rendering of the page header and body tags. It takes + * two arguments, a `String` for the title of the page and an `Html` + * object to insert into the body of the page. + *@ +@(title: String)(content: Html) + + + + + @* Here's where we render the page title `String`. *@ + @title + + + + + + @* And here's where we render the `Html` object containing + * the page content. *@ + @content + + + + diff --git a/test/testdata/workspaces/sbt-sample-app/build.sbt b/test/testdata/workspaces/sbt-sample-app/build.sbt new file mode 100644 index 00000000..833cc2ae --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/build.sbt @@ -0,0 +1,20 @@ +name := """sbt-sample-app""" +organization := "org.opendevstack.pipeline.sbt" + +version := "1.0-SNAPSHOT" + +lazy val root = (project in file(".")).enablePlugins(PlayScala) + +scalaVersion := "2.13.9" + +// this fixes the problem with different versions of scala-xml in twirl and the scoverage sbt plugin :F +libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always + +libraryDependencies += guice +libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test + +// Adds additional packages into Twirl +//TwirlKeys.templateImports += "org.opendevstack.pipeline.sbt.controllers._" + +// Adds additional packages into conf/routes +// play.sbt.routes.RoutesKeys.routesImport += "org.opendevstack.pipeline.sbt.binders._" diff --git a/test/testdata/workspaces/sbt-sample-app/conf/application.conf b/test/testdata/workspaces/sbt-sample-app/conf/application.conf new file mode 100644 index 00000000..43980c55 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/conf/application.conf @@ -0,0 +1,2 @@ +# https://www.playframework.com/documentation/latest/Configuration +play.http.secret.key="supersecret!!!" \ No newline at end of file diff --git a/test/testdata/workspaces/sbt-sample-app/conf/logback.xml b/test/testdata/workspaces/sbt-sample-app/conf/logback.xml new file mode 100644 index 00000000..95730295 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/conf/logback.xml @@ -0,0 +1,42 @@ + + + + + + + ${application.home:-.}/logs/application.log + + UTF-8 + + %d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) %cyan(%logger{36}) %magenta(%X{akkaSource}) %msg%n + + + + + + true + + UTF-8 + + %d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) %cyan(%logger{36}) %magenta(%X{akkaSource}) %msg%n + + + + + + + + + + + + + + + + + + + + + diff --git a/test/testdata/workspaces/sbt-sample-app/conf/messages b/test/testdata/workspaces/sbt-sample-app/conf/messages new file mode 100644 index 00000000..0226738a --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/conf/messages @@ -0,0 +1 @@ +# https://www.playframework.com/documentation/latest/ScalaI18N diff --git a/test/testdata/workspaces/sbt-sample-app/conf/routes b/test/testdata/workspaces/sbt-sample-app/conf/routes new file mode 100644 index 00000000..60e8169b --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/conf/routes @@ -0,0 +1,10 @@ +# Routes +# This file defines all application routes (Higher priority routes first) +# https://www.playframework.com/documentation/latest/ScalaRouting +# ~~~~ + +# An example controller showing a sample home page +GET / controllers.HomeController.index() + +# Map static resources from the /public folder to the /assets URL path +GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) diff --git a/test/testdata/workspaces/sbt-sample-app/docker/Dockerfile b/test/testdata/workspaces/sbt-sample-app/docker/Dockerfile new file mode 100644 index 00000000..4d7e1967 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/docker/Dockerfile @@ -0,0 +1,13 @@ +FROM registry.access.redhat.com/ubi8/openjdk-11:1.13 + +WORKDIR /opt/sbt-sample-app +COPY dist /opt/sbt-sample-app + +USER root +RUN chown -R jboss: /opt/sbt-sample-app +USER jboss + +EXPOSE 9000 + +ENTRYPOINT ["/opt/sbt-sample-app/bin/sbt-sample-app"] +CMD [] diff --git a/test/testdata/workspaces/sbt-sample-app/project/build.properties b/test/testdata/workspaces/sbt-sample-app/project/build.properties new file mode 100644 index 00000000..22af2628 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.7.1 diff --git a/test/testdata/workspaces/sbt-sample-app/project/plugins.sbt b/test/testdata/workspaces/sbt-sample-app/project/plugins.sbt new file mode 100644 index 00000000..21847b48 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/project/plugins.sbt @@ -0,0 +1,6 @@ +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.16") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.4") + +// this fixes the problem with different versions of scala-xml in twirl and the scoverage sbt plugin :F +libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always diff --git a/test/testdata/workspaces/sbt-sample-app/public/images/favicon.png b/test/testdata/workspaces/sbt-sample-app/public/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c7d92d2ae47434d9a61c90bc205e099b673b9dd5 GIT binary patch literal 687 zcmV;g0#N;lP)ezT{T_ZJ?}AL z5NC{NW(ESID=>(O3&Eg8 zmA9J&6c`h4_f6L;=bU>_H8aNG`kfvCj9zomNt)?O;rzWqZs0LEt%1WB218%1fo9uB zsW^yhBR7C(mqN%GEK9&msg0~ zWY?#bf4q8G-~2KttQZ($odJvy&_-~f?9*ThK@fwR$U^1)p*8=_+^3BXx0$i1BC8XC zr21u6D5nVK&^!dOAw&|1E;qC3uFNj3*Jj#&%Oje@0D-nhfmM*o%^5f}-pxQ07(95H z3|LoV>V19w#rLgmRmtVy9!T3M3FUE3><0T8&b3yEsWcLW`0(=1+qsqc(k(ymBLK0h zK!6(6$7MX~M`-QA2$wk7n(7hhkJ}4Rwi-Vd(_ZFX1Yk7TXuB0IJYpo@kLb2G8m)E{ z`9v=!hi}fOytKckfN^C@6+Z*+MVI9-W_p@_3yyR#UYc0FTpD}i#k>c!wYCS)4v@E$ zchZCo=zV@)`v^$;V18ixdjFMY#q^2$wEX%{f(XD8POnsn$bpbClpC@hPxjzyO>pY|*pF3UU2tYcCN?rUk{Sskej70Mmu9vPwMYhO1m{AxAt(zqDT|0jP7FaX=6 V`?~}E4H^Id002ovPDHLkV1hC)G==~G literal 0 HcmV?d00001 diff --git a/test/testdata/workspaces/sbt-sample-app/public/javascripts/main.js b/test/testdata/workspaces/sbt-sample-app/public/javascripts/main.js new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/workspaces/sbt-sample-app/public/stylesheets/main.css b/test/testdata/workspaces/sbt-sample-app/public/stylesheets/main.css new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/workspaces/sbt-sample-app/test/controllers/HomeControllerSpec.scala b/test/testdata/workspaces/sbt-sample-app/test/controllers/HomeControllerSpec.scala new file mode 100644 index 00000000..7e00e475 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/test/controllers/HomeControllerSpec.scala @@ -0,0 +1,44 @@ +package controllers + +import org.scalatestplus.play._ +import org.scalatestplus.play.guice._ +import play.api.test._ +import play.api.test.Helpers._ + +/** Add your spec here. You can mock out a whole application including requests, plugins etc. + * + * For more information, see + * https://www.playframework.com/documentation/latest/ScalaTestingWithScalaTest + */ +class HomeControllerSpec extends PlaySpec with GuiceOneAppPerTest with Injecting { + + "HomeController GET" should { + + "render the index page from a new instance of controller" in { + val controller = new HomeController(stubControllerComponents()) + val home = controller.index().apply(FakeRequest(GET, "/")) + + status(home) mustBe OK + contentType(home) mustBe Some("text/html") + contentAsString(home) must include("Welcome to Play") + } + + "render the index page from the application" in { + val controller = inject[HomeController] + val home = controller.index().apply(FakeRequest(GET, "/")) + + status(home) mustBe OK + contentType(home) mustBe Some("text/html") + contentAsString(home) must include("Welcome to Play") + } + + "render the index page from the router" in { + val request = FakeRequest(GET, "/") + val home = route(app, request).get + + status(home) mustBe OK + contentType(home) mustBe Some("text/html") + contentAsString(home) must include("Welcome to Play") + } + } +} From 7f9575472a6b254bba4fab6b23281f9902c7842a Mon Sep 17 00:00:00 2001 From: Jan Frank Date: Tue, 18 Oct 2022 19:33:50 +0200 Subject: [PATCH 2/7] replace sample app with scala seed app --- build/package/Dockerfile.sbt-toolset | 4 +- test/tasks/ods-build-sbt_test.go | 2 +- .../app/controllers/HomeController.scala | 21 --------- .../sbt-sample-app/app/views/index.scala.html | 5 -- .../sbt-sample-app/app/views/main.scala.html | 25 ---------- .../workspaces/sbt-sample-app/build.sbt | 29 +++++------- .../sbt-sample-app/conf/application.conf | 2 - .../sbt-sample-app/conf/logback.xml | 42 ----------------- .../workspaces/sbt-sample-app/conf/messages | 1 - .../workspaces/sbt-sample-app/conf/routes | 10 ---- .../sbt-sample-app/project/Dependencies.scala | 5 ++ .../sbt-sample-app/project/build.properties | 2 +- .../sbt-sample-app/project/plugins.sbt | 6 +-- .../sbt-sample-app/public/images/favicon.png | Bin 687 -> 0 bytes .../sbt-sample-app/public/javascripts/main.js | 0 .../public/stylesheets/main.css | 0 .../src/main/scala/example/Hello.scala | 9 ++++ .../src/test/scala/example/HelloSpec.scala | 10 ++++ .../test/controllers/HomeControllerSpec.scala | 44 ------------------ 19 files changed, 43 insertions(+), 174 deletions(-) delete mode 100644 test/testdata/workspaces/sbt-sample-app/app/controllers/HomeController.scala delete mode 100644 test/testdata/workspaces/sbt-sample-app/app/views/index.scala.html delete mode 100644 test/testdata/workspaces/sbt-sample-app/app/views/main.scala.html delete mode 100644 test/testdata/workspaces/sbt-sample-app/conf/application.conf delete mode 100644 test/testdata/workspaces/sbt-sample-app/conf/logback.xml delete mode 100644 test/testdata/workspaces/sbt-sample-app/conf/messages delete mode 100644 test/testdata/workspaces/sbt-sample-app/conf/routes create mode 100644 test/testdata/workspaces/sbt-sample-app/project/Dependencies.scala delete mode 100644 test/testdata/workspaces/sbt-sample-app/public/images/favicon.png delete mode 100644 test/testdata/workspaces/sbt-sample-app/public/javascripts/main.js delete mode 100644 test/testdata/workspaces/sbt-sample-app/public/stylesheets/main.css create mode 100644 test/testdata/workspaces/sbt-sample-app/src/main/scala/example/Hello.scala create mode 100644 test/testdata/workspaces/sbt-sample-app/src/test/scala/example/HelloSpec.scala delete mode 100644 test/testdata/workspaces/sbt-sample-app/test/controllers/HomeControllerSpec.scala diff --git a/build/package/Dockerfile.sbt-toolset b/build/package/Dockerfile.sbt-toolset index d25e8152..1876d89c 100644 --- a/build/package/Dockerfile.sbt-toolset +++ b/build/package/Dockerfile.sbt-toolset @@ -2,8 +2,8 @@ FROM registry.access.redhat.com/ubi8/openjdk-11:1.13 SHELL ["/bin/bash", "-o", "pipefail", "-c"] -ARG SBT_VERSION=1.7.1 -ARG SBT_SHA256=2ae094410b518acc46d9cdcdc4e138b4c27cd5040fc91d48d2d823fe84984294 +ARG SBT_VERSION=1.7.2 +ARG SBT_SHA256=e9e3814b2a5a83734d02bf8f1dd8ac285620e601e2f9b1c0fa18c8b38d0dabe3 ENV LANG=en_US.UTF-8 \ LC_ALL=en_US.UTF-8 diff --git a/test/tasks/ods-build-sbt_test.go b/test/tasks/ods-build-sbt_test.go index 574dde35..d62402da 100644 --- a/test/tasks/ods-build-sbt_test.go +++ b/test/tasks/ods-build-sbt_test.go @@ -35,7 +35,7 @@ func TestTaskODSBuildSbt(t *testing.T) { checkFilesExist(t, wsDir, "docker/Dockerfile", "docker/dist", - filepath.Join(pipelinectxt.XUnitReportsPath, "TEST-controllers.HomeControllerSpec.xml"), + filepath.Join(pipelinectxt.XUnitReportsPath, "TEST-example.HelloSpec.xml"), filepath.Join(pipelinectxt.CodeCoveragesPath, "scoverage.xml"), filepath.Join(pipelinectxt.SonarAnalysisPath, "analysis-report.md"), filepath.Join(pipelinectxt.SonarAnalysisPath, "issues-report.csv"), diff --git a/test/testdata/workspaces/sbt-sample-app/app/controllers/HomeController.scala b/test/testdata/workspaces/sbt-sample-app/app/controllers/HomeController.scala deleted file mode 100644 index 11fd5901..00000000 --- a/test/testdata/workspaces/sbt-sample-app/app/controllers/HomeController.scala +++ /dev/null @@ -1,21 +0,0 @@ -package controllers - -import javax.inject._ -import play.api._ -import play.api.mvc._ - -/** This controller creates an `Action` to handle HTTP requests to the application's home page. - */ -@Singleton -class HomeController @Inject() (val controllerComponents: ControllerComponents) - extends BaseController { - - /** Create an Action to render an HTML page. - * - * The configuration in the `routes` file means that this method will be called when the - * application receives a `GET` request with a path of `/`. - */ - def index() = Action { implicit request: Request[AnyContent] => - Ok(views.html.index()) - } -} diff --git a/test/testdata/workspaces/sbt-sample-app/app/views/index.scala.html b/test/testdata/workspaces/sbt-sample-app/app/views/index.scala.html deleted file mode 100644 index 68d37fb1..00000000 --- a/test/testdata/workspaces/sbt-sample-app/app/views/index.scala.html +++ /dev/null @@ -1,5 +0,0 @@ -@() - -@main("Welcome to Play") { -

Welcome to Play!

-} diff --git a/test/testdata/workspaces/sbt-sample-app/app/views/main.scala.html b/test/testdata/workspaces/sbt-sample-app/app/views/main.scala.html deleted file mode 100644 index 808a8b89..00000000 --- a/test/testdata/workspaces/sbt-sample-app/app/views/main.scala.html +++ /dev/null @@ -1,25 +0,0 @@ -@* - * This template is called from the `index` template. This template - * handles the rendering of the page header and body tags. It takes - * two arguments, a `String` for the title of the page and an `Html` - * object to insert into the body of the page. - *@ -@(title: String)(content: Html) - - - - - @* Here's where we render the page title `String`. *@ - @title - - - - - - @* And here's where we render the `Html` object containing - * the page content. *@ - @content - - - - diff --git a/test/testdata/workspaces/sbt-sample-app/build.sbt b/test/testdata/workspaces/sbt-sample-app/build.sbt index 833cc2ae..0d8151b3 100644 --- a/test/testdata/workspaces/sbt-sample-app/build.sbt +++ b/test/testdata/workspaces/sbt-sample-app/build.sbt @@ -1,20 +1,15 @@ -name := """sbt-sample-app""" -organization := "org.opendevstack.pipeline.sbt" +import Dependencies._ -version := "1.0-SNAPSHOT" +ThisBuild / scalaVersion := "2.13.9" +ThisBuild / version := "0.1.0-SNAPSHOT" +ThisBuild / organization := "com.example" +ThisBuild / organizationName := "example" -lazy val root = (project in file(".")).enablePlugins(PlayScala) +lazy val root = (project in file(".")) + .enablePlugins(JavaAppPackaging) + .settings( + name := "scala-seed", + libraryDependencies += scalaTest % Test + ) -scalaVersion := "2.13.9" - -// this fixes the problem with different versions of scala-xml in twirl and the scoverage sbt plugin :F -libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always - -libraryDependencies += guice -libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test - -// Adds additional packages into Twirl -//TwirlKeys.templateImports += "org.opendevstack.pipeline.sbt.controllers._" - -// Adds additional packages into conf/routes -// play.sbt.routes.RoutesKeys.routesImport += "org.opendevstack.pipeline.sbt.binders._" +// See https://www.scala-sbt.org/1.x/docs/Using-Sonatype.html for instructions on how to publish to Sonatype. diff --git a/test/testdata/workspaces/sbt-sample-app/conf/application.conf b/test/testdata/workspaces/sbt-sample-app/conf/application.conf deleted file mode 100644 index 43980c55..00000000 --- a/test/testdata/workspaces/sbt-sample-app/conf/application.conf +++ /dev/null @@ -1,2 +0,0 @@ -# https://www.playframework.com/documentation/latest/Configuration -play.http.secret.key="supersecret!!!" \ No newline at end of file diff --git a/test/testdata/workspaces/sbt-sample-app/conf/logback.xml b/test/testdata/workspaces/sbt-sample-app/conf/logback.xml deleted file mode 100644 index 95730295..00000000 --- a/test/testdata/workspaces/sbt-sample-app/conf/logback.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - ${application.home:-.}/logs/application.log - - UTF-8 - - %d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) %cyan(%logger{36}) %magenta(%X{akkaSource}) %msg%n - - - - - - true - - UTF-8 - - %d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) %cyan(%logger{36}) %magenta(%X{akkaSource}) %msg%n - - - - - - - - - - - - - - - - - - - - - diff --git a/test/testdata/workspaces/sbt-sample-app/conf/messages b/test/testdata/workspaces/sbt-sample-app/conf/messages deleted file mode 100644 index 0226738a..00000000 --- a/test/testdata/workspaces/sbt-sample-app/conf/messages +++ /dev/null @@ -1 +0,0 @@ -# https://www.playframework.com/documentation/latest/ScalaI18N diff --git a/test/testdata/workspaces/sbt-sample-app/conf/routes b/test/testdata/workspaces/sbt-sample-app/conf/routes deleted file mode 100644 index 60e8169b..00000000 --- a/test/testdata/workspaces/sbt-sample-app/conf/routes +++ /dev/null @@ -1,10 +0,0 @@ -# Routes -# This file defines all application routes (Higher priority routes first) -# https://www.playframework.com/documentation/latest/ScalaRouting -# ~~~~ - -# An example controller showing a sample home page -GET / controllers.HomeController.index() - -# Map static resources from the /public folder to the /assets URL path -GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) diff --git a/test/testdata/workspaces/sbt-sample-app/project/Dependencies.scala b/test/testdata/workspaces/sbt-sample-app/project/Dependencies.scala new file mode 100644 index 00000000..c436f5b7 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/project/Dependencies.scala @@ -0,0 +1,5 @@ +import sbt._ + +object Dependencies { + lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.2.14" +} diff --git a/test/testdata/workspaces/sbt-sample-app/project/build.properties b/test/testdata/workspaces/sbt-sample-app/project/build.properties index 22af2628..563a014d 100644 --- a/test/testdata/workspaces/sbt-sample-app/project/build.properties +++ b/test/testdata/workspaces/sbt-sample-app/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.1 +sbt.version=1.7.2 diff --git a/test/testdata/workspaces/sbt-sample-app/project/plugins.sbt b/test/testdata/workspaces/sbt-sample-app/project/plugins.sbt index 21847b48..3ba18c46 100644 --- a/test/testdata/workspaces/sbt-sample-app/project/plugins.sbt +++ b/test/testdata/workspaces/sbt-sample-app/project/plugins.sbt @@ -1,6 +1,6 @@ -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.16") -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.4") +addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.4") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.5") // this fixes the problem with different versions of scala-xml in twirl and the scoverage sbt plugin :F libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always diff --git a/test/testdata/workspaces/sbt-sample-app/public/images/favicon.png b/test/testdata/workspaces/sbt-sample-app/public/images/favicon.png deleted file mode 100644 index c7d92d2ae47434d9a61c90bc205e099b673b9dd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 687 zcmV;g0#N;lP)ezT{T_ZJ?}AL z5NC{NW(ESID=>(O3&Eg8 zmA9J&6c`h4_f6L;=bU>_H8aNG`kfvCj9zomNt)?O;rzWqZs0LEt%1WB218%1fo9uB zsW^yhBR7C(mqN%GEK9&msg0~ zWY?#bf4q8G-~2KttQZ($odJvy&_-~f?9*ThK@fwR$U^1)p*8=_+^3BXx0$i1BC8XC zr21u6D5nVK&^!dOAw&|1E;qC3uFNj3*Jj#&%Oje@0D-nhfmM*o%^5f}-pxQ07(95H z3|LoV>V19w#rLgmRmtVy9!T3M3FUE3><0T8&b3yEsWcLW`0(=1+qsqc(k(ymBLK0h zK!6(6$7MX~M`-QA2$wk7n(7hhkJ}4Rwi-Vd(_ZFX1Yk7TXuB0IJYpo@kLb2G8m)E{ z`9v=!hi}fOytKckfN^C@6+Z*+MVI9-W_p@_3yyR#UYc0FTpD}i#k>c!wYCS)4v@E$ zchZCo=zV@)`v^$;V18ixdjFMY#q^2$wEX%{f(XD8POnsn$bpbClpC@hPxjzyO>pY|*pF3UU2tYcCN?rUk{Sskej70Mmu9vPwMYhO1m{AxAt(zqDT|0jP7FaX=6 V`?~}E4H^Id002ovPDHLkV1hC)G==~G diff --git a/test/testdata/workspaces/sbt-sample-app/public/javascripts/main.js b/test/testdata/workspaces/sbt-sample-app/public/javascripts/main.js deleted file mode 100644 index e69de29b..00000000 diff --git a/test/testdata/workspaces/sbt-sample-app/public/stylesheets/main.css b/test/testdata/workspaces/sbt-sample-app/public/stylesheets/main.css deleted file mode 100644 index e69de29b..00000000 diff --git a/test/testdata/workspaces/sbt-sample-app/src/main/scala/example/Hello.scala b/test/testdata/workspaces/sbt-sample-app/src/main/scala/example/Hello.scala new file mode 100644 index 00000000..80ea40a9 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/src/main/scala/example/Hello.scala @@ -0,0 +1,9 @@ +package example + +object Hello extends Greeting with App { + println(greeting) +} + +trait Greeting { + lazy val greeting: String = "hello" +} diff --git a/test/testdata/workspaces/sbt-sample-app/src/test/scala/example/HelloSpec.scala b/test/testdata/workspaces/sbt-sample-app/src/test/scala/example/HelloSpec.scala new file mode 100644 index 00000000..a0069d92 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/src/test/scala/example/HelloSpec.scala @@ -0,0 +1,10 @@ +package example + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class HelloSpec extends AnyFlatSpec with Matchers { + "The Hello object" should "say hello" in { + Hello.greeting shouldEqual "hello" + } +} diff --git a/test/testdata/workspaces/sbt-sample-app/test/controllers/HomeControllerSpec.scala b/test/testdata/workspaces/sbt-sample-app/test/controllers/HomeControllerSpec.scala deleted file mode 100644 index 7e00e475..00000000 --- a/test/testdata/workspaces/sbt-sample-app/test/controllers/HomeControllerSpec.scala +++ /dev/null @@ -1,44 +0,0 @@ -package controllers - -import org.scalatestplus.play._ -import org.scalatestplus.play.guice._ -import play.api.test._ -import play.api.test.Helpers._ - -/** Add your spec here. You can mock out a whole application including requests, plugins etc. - * - * For more information, see - * https://www.playframework.com/documentation/latest/ScalaTestingWithScalaTest - */ -class HomeControllerSpec extends PlaySpec with GuiceOneAppPerTest with Injecting { - - "HomeController GET" should { - - "render the index page from a new instance of controller" in { - val controller = new HomeController(stubControllerComponents()) - val home = controller.index().apply(FakeRequest(GET, "/")) - - status(home) mustBe OK - contentType(home) mustBe Some("text/html") - contentAsString(home) must include("Welcome to Play") - } - - "render the index page from the application" in { - val controller = inject[HomeController] - val home = controller.index().apply(FakeRequest(GET, "/")) - - status(home) mustBe OK - contentType(home) mustBe Some("text/html") - contentAsString(home) must include("Welcome to Play") - } - - "render the index page from the router" in { - val request = FakeRequest(GET, "/") - val home = route(app, request).get - - status(home) mustBe OK - contentType(home) mustBe Some("text/html") - contentAsString(home) must include("Welcome to Play") - } - } -} From 55b791f4ca77c0c2fe722f29505d16e6ac7b8820 Mon Sep 17 00:00:00 2001 From: Jan Frank Date: Tue, 18 Oct 2022 20:33:56 +0200 Subject: [PATCH 3/7] fix sonar.sources --- build/package/sonar-project.properties.d/sbt.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package/sonar-project.properties.d/sbt.properties b/build/package/sonar-project.properties.d/sbt.properties index 77835a62..f2a9a2b9 100644 --- a/build/package/sonar-project.properties.d/sbt.properties +++ b/build/package/sonar-project.properties.d/sbt.properties @@ -1,5 +1,5 @@ # this is just a fallback sonar properties. A matching one should be provided by each project -sonar.sources=app +sonar.sources=src sonar.sourceEncoding=UTF-8 sonar.scala.version=2.13 sonar.scala.scapegoat.disable=true From 60b13e22a1ec30ac97db3209ec236c58acc6279c Mon Sep 17 00:00:00 2001 From: Jan Frank Date: Wed, 19 Oct 2022 14:32:16 +0200 Subject: [PATCH 4/7] adding sbt opts and timestamped logs --- build/package/Dockerfile.sbt-toolset | 9 +++- build/package/files/sbtopts | 48 +++++++++++++++++++ build/package/scripts/build-sbt.sh | 12 +++-- .../workspaces/sbt-sample-app/build.sbt | 2 +- 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 build/package/files/sbtopts diff --git a/build/package/Dockerfile.sbt-toolset b/build/package/Dockerfile.sbt-toolset index 1876d89c..496c3266 100644 --- a/build/package/Dockerfile.sbt-toolset +++ b/build/package/Dockerfile.sbt-toolset @@ -6,11 +6,13 @@ ARG SBT_VERSION=1.7.2 ARG SBT_SHA256=e9e3814b2a5a83734d02bf8f1dd8ac285620e601e2f9b1c0fa18c8b38d0dabe3 ENV LANG=en_US.UTF-8 \ - LC_ALL=en_US.UTF-8 + LC_ALL=en_US.UTF-8 \ + HOME=/home/jboss \ + COURSIER_CACHE=/home/jboss/.cache/coursier/v1 USER root -RUN microdnf install --nodocs openssl-${OPENSSL_VERSION}* git-${GIT_VERSION}* && microdnf clean all +RUN microdnf install --nodocs openssl-${OPENSSL_VERSION}* git-${GIT_VERSION}* vim && microdnf clean all RUN SBT_INSTALL_DIR=/tmp/sbt-install && \ mkdir -p $SBT_INSTALL_DIR && \ @@ -21,6 +23,8 @@ RUN SBT_INSTALL_DIR=/tmp/sbt-install && \ ln -s /opt/sbt/bin/sbt /usr/local/bin/sbt && \ rm -rf $SBT_INSTALL_DIR +COPY build/package/files/sbtopts /opt/sbt/conf/sbtopts + RUN sbt -v sbtVersion && \ rm -rf project target @@ -36,6 +40,7 @@ RUN chmod +x /usr/local/bin/build-sbt && \ chmod +x /usr/local/bin/copy-build-if-cached && \ chmod +x /usr/local/bin/copy-artifacts && \ chmod +x /usr/local/bin/supply-sonar-project-properties-default && \ + chown -R 1001:0 /home/jboss && \ chown -R 1001:0 /tmp/.sbt # TODO set sbt http proxy chmod +x /usr/local/bin/set-sbt-proxy # TODO set sbt http proxy diff --git a/build/package/files/sbtopts b/build/package/files/sbtopts new file mode 100644 index 00000000..674615ee --- /dev/null +++ b/build/package/files/sbtopts @@ -0,0 +1,48 @@ +# ------------------------------------------------ # +# The SBT Configuration file. # +# ------------------------------------------------ # + + +# Disable ANSI color codes +# +-no-colors + +# Starts sbt even if the current directory contains no sbt project. +# +-sbt-create + +# Path to global settings/plugins directory (default: ~/.sbt) +# +-sbt-dir /home/jboss/.sbt + +# Path to shared boot directory (default: ~/.sbt/boot in 0.11 series) +# +-sbt-boot /home/jboss/.sbt/boot + +# Path to local Ivy repository (default: ~/.ivy2) +# +-ivy /home/jboss/.ivy2 + +# set memory options +# +#-mem + +# Use local caches for projects, no sharing. +# +#-no-share + +# Put SBT in offline mode. +# +#-offline + +# Sets the SBT version to use. +#-sbt-version 0.11.3 + +# Scala version (default: latest release) +# +#-scala-home +#-scala-version + +# java version (default: java from PATH, currently $(java -version |& grep version)) +# +#-java-home diff --git a/build/package/scripts/build-sbt.sh b/build/package/scripts/build-sbt.sh index b5e5f66e..4a7a8a36 100755 --- a/build/package/scripts/build-sbt.sh +++ b/build/package/scripts/build-sbt.sh @@ -1,6 +1,10 @@ #!/bin/bash set -eu +function timestamped() { + echo "$(date "+%Y/%m/%d %H:%M:%S") $1" +} + OUTPUT_DIR="docker" WORKING_DIR="." ROOT_DIR=$(pwd) @@ -49,10 +53,11 @@ echo "Building (Compile and Test) ..." # will end up instrumented in production... This is due to the fact that scoverage has no way for on-the-fly instrumentation which only happens in memory # check format of sbt and source files, activate coverage and test with coverage report +timestamped "run tests and coverage" sbt -no-colors -v clean scalafmtSbtCheck scalafmtCheckAll coverage test coverageReport # copy reports -echo "Verifying unit test report was generated ..." +timestamped "Verifying unit test report was generated ..." BUILD_DIR="target" UNIT_TEST_RESULT_DIR="${BUILD_DIR}/test-reports" if [ -d "${UNIT_TEST_RESULT_DIR}" ]; then @@ -64,7 +69,7 @@ else exit 1 fi -echo "Verifying unit test coverage report was generated ..." +timestamped "Verifying unit test coverage report was generated ..." COVERAGE_RESULT_DIR="${BUILD_DIR}/scala-2.13" if [ -d "${COVERAGE_RESULT_DIR}" ]; then CODE_COVERAGE_ARTIFACTS_DIR="${ARTIFACTS_DIR}/code-coverage" @@ -76,10 +81,11 @@ else fi # create a clean binary as the previous compiled sources where instrumented for the coverage report +timestamped "creating build artefacts" sbt -no-colors -v clean stage STAGING_DIR="${BUILD_DIR}/universal/stage" -echo "Copying contents of ${STAGING_DIR} to ${OUTPUT_DIR}/dist ..." +timestamped "Copying contents of ${STAGING_DIR} to ${OUTPUT_DIR}/dist ..." cp -r "${STAGING_DIR}/." "${OUTPUT_DIR}/dist" # TODO oder alles in einem command: diff --git a/test/testdata/workspaces/sbt-sample-app/build.sbt b/test/testdata/workspaces/sbt-sample-app/build.sbt index 0d8151b3..426f5984 100644 --- a/test/testdata/workspaces/sbt-sample-app/build.sbt +++ b/test/testdata/workspaces/sbt-sample-app/build.sbt @@ -1,6 +1,6 @@ import Dependencies._ -ThisBuild / scalaVersion := "2.13.9" +ThisBuild / scalaVersion := "2.13.10" ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / organization := "com.example" ThisBuild / organizationName := "example" From cd6b733ab2113357d51facd04904d391aa54fa6e Mon Sep 17 00:00:00 2001 From: Jan Frank Date: Wed, 19 Oct 2022 17:16:08 +0200 Subject: [PATCH 5/7] force to create compiler bridges already on image creation --- build/package/Dockerfile.sbt-toolset | 10 +++++++--- build/package/files/{ => sbt}/sbtopts | 1 + build/package/files/sbt/test/test.scala | 3 +++ 3 files changed, 11 insertions(+), 3 deletions(-) rename build/package/files/{ => sbt}/sbtopts (97%) create mode 100644 build/package/files/sbt/test/test.scala diff --git a/build/package/Dockerfile.sbt-toolset b/build/package/Dockerfile.sbt-toolset index 496c3266..d93db109 100644 --- a/build/package/Dockerfile.sbt-toolset +++ b/build/package/Dockerfile.sbt-toolset @@ -12,6 +12,8 @@ ENV LANG=en_US.UTF-8 \ USER root +ADD build/package/files/sbt/test /tmp/sbttest + RUN microdnf install --nodocs openssl-${OPENSSL_VERSION}* git-${GIT_VERSION}* vim && microdnf clean all RUN SBT_INSTALL_DIR=/tmp/sbt-install && \ @@ -23,10 +25,12 @@ RUN SBT_INSTALL_DIR=/tmp/sbt-install && \ ln -s /opt/sbt/bin/sbt /usr/local/bin/sbt && \ rm -rf $SBT_INSTALL_DIR -COPY build/package/files/sbtopts /opt/sbt/conf/sbtopts +COPY build/package/files/sbt/sbtopts /opt/sbt/conf/sbtopts -RUN sbt -v sbtVersion && \ - rm -rf project target +RUN cd /tmp/sbttest && \ + sbt 'set scalaVersion := "2.12.16"' compile && \ + sbt 'set scalaVersion := "2.13.10"' compile && \ + rm -rf /tmp/sbttest # Add scripts COPY build/package/scripts/cache-build.sh /usr/local/bin/cache-build diff --git a/build/package/files/sbtopts b/build/package/files/sbt/sbtopts similarity index 97% rename from build/package/files/sbtopts rename to build/package/files/sbt/sbtopts index 674615ee..a650ebc1 100644 --- a/build/package/files/sbtopts +++ b/build/package/files/sbt/sbtopts @@ -46,3 +46,4 @@ # java version (default: java from PATH, currently $(java -version |& grep version)) # #-java-home +-Duser.home=/home/jboss diff --git a/build/package/files/sbt/test/test.scala b/build/package/files/sbt/test/test.scala new file mode 100644 index 00000000..cbc2b638 --- /dev/null +++ b/build/package/files/sbt/test/test.scala @@ -0,0 +1,3 @@ +object Test { + val x = "nothing here" +} From 8bf0ff84f5be24b130a22fc522d4da5199cae26c Mon Sep 17 00:00:00 2001 From: Jan Frank Date: Thu, 20 Oct 2022 13:13:12 +0200 Subject: [PATCH 6/7] avoid calling sbt multiple times by adding an auto plugin that copies the reports in between instrumented and final build --- build/package/scripts/build-sbt.sh | 41 +++++--------- .../project/OdsPipelinePlugin.scala | 54 +++++++++++++++++++ 2 files changed, 67 insertions(+), 28 deletions(-) create mode 100644 test/testdata/workspaces/sbt-sample-app/project/OdsPipelinePlugin.scala diff --git a/build/package/scripts/build-sbt.sh b/build/package/scripts/build-sbt.sh index 4a7a8a36..34e47d37 100755 --- a/build/package/scripts/build-sbt.sh +++ b/build/package/scripts/build-sbt.sh @@ -47,47 +47,32 @@ echo "Exported env var 'ODS_OUTPUT_DIR' with value '${OUTPUT_DIR}'" echo echo "Building (Compile and Test) ..." # shellcheck disable=SC2086 -# old command: "sbt clean scalafmtSbtCheck scalafmtCheckAll coverage test coverageReport copyDockerFiles" -# plugins needed by the build: scalafmt scoverage native-packager -# TODO: with scoverage the build needs to run two times, one time with coverage and one time without, otherwise the production code -# will end up instrumented in production... This is due to the fact that scoverage has no way for on-the-fly instrumentation which only happens in memory # check format of sbt and source files, activate coverage and test with coverage report timestamped "run tests and coverage" -sbt -no-colors -v clean scalafmtSbtCheck scalafmtCheckAll coverage test coverageReport +UNIT_TEST_ARTIFACTS_DIR="${ARTIFACTS_DIR}/xunit-reports" +CODE_COVERAGE_ARTIFACTS_DIR="${ARTIFACTS_DIR}/code-coverage" +export UNIT_TEST_RESULT_DIR="${UNIT_TEST_ARTIFACTS_DIR}/${ARTIFACT_PREFIX}" +export CODE_COVERAGE_TARGET_FILE="${CODE_COVERAGE_ARTIFACTS_DIR}/${ARTIFACT_PREFIX}scoverage.xml" +sbt -no-colors -v clean scalafmtSbtCheck scalafmtCheckAll coverage test coverageReport copyOdsReports clean stage -# copy reports timestamped "Verifying unit test report was generated ..." -BUILD_DIR="target" -UNIT_TEST_RESULT_DIR="${BUILD_DIR}/test-reports" -if [ -d "${UNIT_TEST_RESULT_DIR}" ]; then - UNIT_TEST_ARTIFACTS_DIR="${ARTIFACTS_DIR}/xunit-reports" - mkdir -p "${UNIT_TEST_ARTIFACTS_DIR}" - cp "${UNIT_TEST_RESULT_DIR}/"*.xml "${UNIT_TEST_ARTIFACTS_DIR}/${ARTIFACT_PREFIX}" +if ls "${UNIT_TEST_RESULT_DIR}"*.xml >/dev/null 2>&1 ; then + timestamped "unit test results exist under ${UNIT_TEST_RESULT_DIR}" else - echo "Build failed: no unit test results found in ${UNIT_TEST_RESULT_DIR}" + timestamped "Build failed: no unit test results found in ${UNIT_TEST_RESULT_DIR}" exit 1 fi -timestamped "Verifying unit test coverage report was generated ..." -COVERAGE_RESULT_DIR="${BUILD_DIR}/scala-2.13" -if [ -d "${COVERAGE_RESULT_DIR}" ]; then - CODE_COVERAGE_ARTIFACTS_DIR="${ARTIFACTS_DIR}/code-coverage" - mkdir -p "${CODE_COVERAGE_ARTIFACTS_DIR}" - cp "${COVERAGE_RESULT_DIR}/scoverage-report/scoverage.xml" "${CODE_COVERAGE_ARTIFACTS_DIR}/${ARTIFACT_PREFIX}scoverage.xml" +timestamped "Verifying unit test coverage report was generated ..." +if [ -f "${CODE_COVERAGE_TARGET_FILE}" ]; then + timestamped "unit test coverage report was found at ${CODE_COVERAGE_TARGET_FILE}" else - echo "Build failed: no unit test coverage report was found in ${COVERAGE_RESULT_DIR}" + timestamped "Build failed: no unit test coverage report was found at ${CODE_COVERAGE_TARGET_FILE}" exit 1 fi -# create a clean binary as the previous compiled sources where instrumented for the coverage report -timestamped "creating build artefacts" -sbt -no-colors -v clean stage - +BUILD_DIR="target" STAGING_DIR="${BUILD_DIR}/universal/stage" timestamped "Copying contents of ${STAGING_DIR} to ${OUTPUT_DIR}/dist ..." cp -r "${STAGING_DIR}/." "${OUTPUT_DIR}/dist" - -# TODO oder alles in einem command: -# sbt clean coverage test coverageReport coverageOff clean compile / publishMyReports / copy stuff to docker... -# dann muss man nur dafür sorgen, dass die reports vor dem 2. clean gesaved werden... diff --git a/test/testdata/workspaces/sbt-sample-app/project/OdsPipelinePlugin.scala b/test/testdata/workspaces/sbt-sample-app/project/OdsPipelinePlugin.scala new file mode 100644 index 00000000..5713dd57 --- /dev/null +++ b/test/testdata/workspaces/sbt-sample-app/project/OdsPipelinePlugin.scala @@ -0,0 +1,54 @@ +import sbt.Keys.streams +import sbt.io.IO +import sbt.plugins.JUnitXmlReportPlugin.autoImport.testReportsDirectory +import sbt.{AutoPlugin, Setting, Test, file, taskKey} +import scoverage.ScoverageKeys.coverageDataDir +import sbt._ + +object OdsPipelinePlugin extends AutoPlugin { + override def trigger = allRequirements + + object autoImport { + val copyOdsTestReports = taskKey[Unit]( + "copy test reports to the expected ods test report directory (UNIT_TEST_RESULT_DIR)" + ) + val copyOdsTestCoverageReport = taskKey[Unit]( + "copy test coverage report to the expected location (CODE_COVERAGE_TARGET_FILE)" + ) + val copyOdsReports = + taskKey[Unit]("copy all ods reports to the appropriate directories (defined by the envs)") + } + + import autoImport._ + + override lazy val projectSettings: Seq[Setting[_]] = Seq( + copyOdsTestReports := { + val log = streams.value.log + + sys.env.get("UNIT_TEST_RESULT_DIR") match { + case Some(targetPath) => { + val testReportDir = (Test / testReportsDirectory).value + log.info(s"copying ${testReportDir.listFiles().length} test report(s) to $targetPath") + IO.copyDirectory(testReportDir, file(targetPath)) + } + case None => log.info("no env (UNIT_TEST_RESULT_DIR) set, doing nothing ...") + } + }, + copyOdsTestCoverageReport := { + val log = streams.value.log + + sys.env.get("CODE_COVERAGE_TARGET_FILE") match { + case Some(targetFilePath) => { + val scoverageReportFile = coverageDataDir.value / "scoverage-report" / "scoverage.xml" + log.info(s"copying $scoverageReportFile to $targetFilePath") + IO.copyFile(scoverageReportFile, file(targetFilePath)) + } + case None => log.info("no env (CODE_COVERAGE_TARGET_FILE) set, doing nothing ...") + } + }, + copyOdsReports := { + copyOdsTestReports.value + copyOdsTestCoverageReport.value + } + ) +} From 6ec6a318907455245c90d57a5a6402254ee43b97 Mon Sep 17 00:00:00 2001 From: Jan Frank Date: Thu, 20 Oct 2022 16:43:23 +0200 Subject: [PATCH 7/7] reduce sbt-toolset image size --- build/package/Dockerfile.sbt-toolset | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/build/package/Dockerfile.sbt-toolset b/build/package/Dockerfile.sbt-toolset index d93db109..ef2000d7 100644 --- a/build/package/Dockerfile.sbt-toolset +++ b/build/package/Dockerfile.sbt-toolset @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi8/openjdk-11:1.13 +FROM registry.access.redhat.com/ubi8/openjdk-11:1.14 SHELL ["/bin/bash", "-o", "pipefail", "-c"] @@ -12,9 +12,9 @@ ENV LANG=en_US.UTF-8 \ USER root -ADD build/package/files/sbt/test /tmp/sbttest +COPY build/package/files/sbt/test /tmp/sbttest -RUN microdnf install --nodocs openssl-${OPENSSL_VERSION}* git-${GIT_VERSION}* vim && microdnf clean all +RUN microdnf install --nodocs openssl-${OPENSSL_VERSION}* git-${GIT_VERSION}* vim unzip && microdnf clean all RUN SBT_INSTALL_DIR=/tmp/sbt-install && \ mkdir -p $SBT_INSTALL_DIR && \ @@ -26,20 +26,22 @@ RUN SBT_INSTALL_DIR=/tmp/sbt-install && \ rm -rf $SBT_INSTALL_DIR COPY build/package/files/sbt/sbtopts /opt/sbt/conf/sbtopts - -RUN cd /tmp/sbttest && \ - sbt 'set scalaVersion := "2.12.16"' compile && \ - sbt 'set scalaVersion := "2.13.10"' compile && \ - rm -rf /tmp/sbttest - # Add scripts COPY build/package/scripts/cache-build.sh /usr/local/bin/cache-build COPY build/package/scripts/copy-build-if-cached.sh /usr/local/bin/copy-build-if-cached COPY build/package/scripts/copy-artifacts.sh /usr/local/bin/copy-artifacts COPY build/package/scripts/build-sbt.sh /usr/local/bin/build-sbt COPY build/package/scripts/supply-sonar-project-properties-default.sh /usr/local/bin/supply-sonar-project-properties-default + +# Add sonar-project.properties +COPY build/package/sonar-project.properties.d/sbt.properties /usr/local/default-sonar-project.properties + # TODO set sbt http proxy COPY build/package/scripts/set-sbt-proxy.sh /usr/local/bin/set-sbt-proxy -RUN chmod +x /usr/local/bin/build-sbt && \ +RUN cd /tmp/sbttest && \ + sbt 'set scalaVersion := "2.12.16"' compile && \ + sbt 'set scalaVersion := "2.13.10"' compile && \ + rm -rf /tmp/sbttest && \ + chmod +x /usr/local/bin/build-sbt && \ chmod +x /usr/local/bin/cache-build && \ chmod +x /usr/local/bin/copy-build-if-cached && \ chmod +x /usr/local/bin/copy-artifacts && \ @@ -48,7 +50,4 @@ RUN chmod +x /usr/local/bin/build-sbt && \ chown -R 1001:0 /tmp/.sbt # TODO set sbt http proxy chmod +x /usr/local/bin/set-sbt-proxy # TODO set sbt http proxy -# Add sonar-project.properties -COPY build/package/sonar-project.properties.d/sbt.properties /usr/local/default-sonar-project.properties - USER 1001