diff --git a/.github/workflows/smoke-java.yaml b/.github/workflows/smoke-java.yaml index 94f996321..7fc624536 100644 --- a/.github/workflows/smoke-java.yaml +++ b/.github/workflows/smoke-java.yaml @@ -1,4 +1,4 @@ -name: Smoke test "java" build +name: Smoke test "java" and "java-8" build on: workflow_dispatch: @@ -8,6 +8,7 @@ on: pull_request: paths: - src/java/** + - src/java-8/** - .github/workflows/smoke-java.yaml - .github/actions/** - build/** @@ -21,10 +22,18 @@ jobs: id: checkout uses: actions/checkout@v3 - - name: Smoke test + - name: Smoke test java env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} id: smoke_test_java uses: ./.github/actions/smoke-test with: image: java + + - name: Smoke test java-8 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + id: smoke_test_java_8 + uses: ./.github/actions/smoke-test + with: + image: java-8 diff --git a/src/java-8/.devcontainer.json b/src/java-8/.devcontainer.json new file mode 100644 index 000000000..5f6b08ff2 --- /dev/null +++ b/src/java-8/.devcontainer.json @@ -0,0 +1,42 @@ +{ + "name": "Java 8", + "build": { + "dockerfile": "Dockerfile" + }, + "features": { + "ghcr.io/devcontainers/features/java:1": { + "version": "8" + }, + "ghcr.io/devcontainers/features/node:1": "none" + }, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "java.import.gradle.java.home": "/usr/local/sdkman/candidates/java/current", + "java.configuration.runtimes": [ + { + "default": true, + "name": "JavaSE-1.8", + "path": "/usr/local/sdkman/candidates/java/current" + } + ] + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "vscjava.vscode-java-pack" + ] + } + }, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "java -version", + + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} diff --git a/src/java-8/.npmignore b/src/java-8/.npmignore new file mode 100644 index 000000000..1b2665281 --- /dev/null +++ b/src/java-8/.npmignore @@ -0,0 +1,5 @@ +README.md +test-project +history +manifest.json +.npmignore diff --git a/src/java-8/Dockerfile b/src/java-8/Dockerfile new file mode 100644 index 000000000..025a6be8d --- /dev/null +++ b/src/java-8/Dockerfile @@ -0,0 +1,7 @@ +# [Choice] Debian OS version (use bullseye on local arm64/Apple Silicon): buster, bullseye +ARG VARIANT="bullseye" +FROM mcr.microsoft.com/devcontainers/base:${VARIANT} + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends diff --git a/src/java-8/README.md b/src/java-8/README.md new file mode 100644 index 000000000..febd5344b --- /dev/null +++ b/src/java-8/README.md @@ -0,0 +1,76 @@ +# Java 8 + +## Summary + +*Develop Java applications. Includes the JDK 8 and Java extensions.* + +| Metadata | Value | +|----------|-------| +| *Categories* | Core, Languages | +| *Image type* | Dockerfile | +| *Published images* | mcr.microsoft.com/devcontainers/java:8 | +| *Available image variants* | 8 / 8-buster, 8-bullseye ([full list](https://mcr.microsoft.com/v2/devcontainers/java/tags/list)) | +| *Published image architecture(s)* | x86-64, arm64/aarch64 for `bullseye` variant | +| *Container host OS support* | Linux, macOS, Windows | +| *Container OS* | Debian | +| *Languages, platforms* | Java | + +See **[history](history)** for information on the contents of published images. + +## Using this image + +> **Note:** A version of this [image for **newer JDKs**](../java) is also available! + +You can directly reference pre-built versions of `Dockerfile` by using the `image` property in `.devcontainer.json` or updating the `FROM` statement in your own `Dockerfile` to one of the following. An example `Dockerfile` is included in this repository. + +- `mcr.microsoft.com/devcontainers/java:8` (or `8-bullseye`, `8-buster` to pin to an OS version) + +You can decide how often you want updates by referencing a [semantic version](https://semver.org/) of each image. For example: + +- `mcr.microsoft.com/devcontainers/java:1-8` (or `1-8-bullseye`, `1-8-buster` to pin to an OS version) +- `mcr.microsoft.com/devcontainers/java:1.0-8` (or `1.0-8-bullseye`, `1.0-8-buster` to pin to an OS version) +- `mcr.microsoft.com/devcontainers/java:1.0.0-8` (or `1.0.0-8-bullseye`, `1.0.0-8-buster` to pin to an OS version) + +However, we only do security patching on the latest [non-breaking, in support](https://github.com/microsoft/vscode-dev-containers/issues/532) versions of images (e.g. `1-8`). You may want to run `apt-get update && apt-get upgrade` in your Dockerfile if you lock to a more specific version to at least pick up OS security updates. + +See [history](history) for information on the contents of each version and [here for a complete list of available tags](https://mcr.microsoft.com/v2/devcontainers/java/tags/list). + +Alternatively, you can use the contents of `Dockerfile` to fully customize your container's contents or to build it for a container host architecture not supported by the image. + +### Installing Maven or Gradle + +You can use the [Java Feature](https://github.com/devcontainers/features/tree/main/src/java) to install `maven` and `gradle` in `.devcontainer.json`: + +```json +{ + "features": { + "ghcr.io/devcontainers/features/java:1": { + "version": "none", + "installGradle": "true", + "installMaven": "true" + } + } +} +``` + +### Installing Node.js + +Given JavaScript front-end web client code written for use in conjunction with a Java back-end often requires the use of Node.js-based utilities to build, this container also includes `nvm` so that you can easily install Node.js. + +Also, you can use a [Node Feature](https://github.com/devcontainers/features/tree/main/src/node) to install any version of Node by adding the following to `.devcontainer.json`: + +```json +{ + "features": { + "ghcr.io/devcontainers/features/node:1": { + "version": "latest" + } + } +} +``` + +## License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the MIT License. See [LICENSE](https://github.com/devcontainers/images/blob/main/LICENSE). diff --git a/src/java-8/manifest.json b/src/java-8/manifest.json new file mode 100644 index 000000000..7c6f54d2a --- /dev/null +++ b/src/java-8/manifest.json @@ -0,0 +1,61 @@ +{ + "version": "1.0.0", + "variants": [ + "bullseye", + "buster" + ], + "build": { + "latest": false, + "rootDistro": "debian", + "architectures": { + "bullseye": [ + "linux/amd64", + "linux/arm64" + ], + "buster": [ + "linux/amd64" + ] + }, + "tags": [ + "java:${VERSION}-8-${VARIANT}" + ], + "variantTags": { + "buster": [ + "java:${VERSION}-8", + "java:${VERSION}-8-jdk-buster" + ], + "bullseye": [ + "java:${VERSION}-8-jdk-bullseye" + ] + } + }, + "dependencies": { + "image": "buildpack-deps:${BASE_IMAGE_VERSION_CODENAME}-curl", + "imageLink": "https://hub.docker.com/_/buildpack-deps", + "apt": [ + { + "cgIgnore": false, + "name": "yarn" + } + ], + "git": { + "Oh My Zsh!": "/home/vscode/.oh-my-zsh", + "nvm": "/usr/local/share/nvm" + }, + "other": { + "SDKMAN!": null, + "Eclipse Temurin": { + "markdownIgnore": true, + "versionCommand": "/usr/local/sdkman/candidates/java/current/bin/java -version 2>&1 | grep -ozP '^openjdk\\sversion\\s\\\"\\K[^\\\"]+' | tr '\\0' '\\n'", + "downloadUrl": "https://adoptium.net/" + } + }, + "languages": { + "Java": { + "cgIgnore": true, + "versionCommand": "/usr/local/sdkman/candidates/java/current/bin/java -version 2>&1 | grep -ozP '^openjdk\\sversion\\s\\\"\\K[^\\\"]+' | tr '\\0' '\\n' && /usr/local/openjdk-*/bin/java --version | grep -ozP 'openjdk\\s+\\K[0-9]+\\.[0-9]+\\.[0-9]+' | tr '\\0' '\\n'", + "path": "/usr/local/sdkman/candidates/java/current
/usr/local" + } + } + } +} \ No newline at end of file diff --git a/src/java-8/test-project/.classpath b/src/java-8/test-project/.classpath new file mode 100644 index 000000000..f0257c5a5 --- /dev/null +++ b/src/java-8/test-project/.classpath @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java-8/test-project/.gitignore b/src/java-8/test-project/.gitignore new file mode 100644 index 000000000..1de565933 --- /dev/null +++ b/src/java-8/test-project/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/src/java-8/test-project/.project b/src/java-8/test-project/.project new file mode 100644 index 000000000..700a32c79 --- /dev/null +++ b/src/java-8/test-project/.project @@ -0,0 +1,23 @@ + + + my-app + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/src/java-8/test-project/.settings/org.eclipse.jdt.apt.core.prefs b/src/java-8/test-project/.settings/org.eclipse.jdt.apt.core.prefs new file mode 100644 index 000000000..d4313d4b2 --- /dev/null +++ b/src/java-8/test-project/.settings/org.eclipse.jdt.apt.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.apt.aptEnabled=false diff --git a/src/java-8/test-project/.settings/org.eclipse.jdt.core.prefs b/src/java-8/test-project/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..1b6e1ef22 --- /dev/null +++ b/src/java-8/test-project/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.processAnnotations=disabled +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/src/java-8/test-project/.settings/org.eclipse.m2e.core.prefs b/src/java-8/test-project/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/src/java-8/test-project/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/src/java-8/test-project/pom.xml b/src/java-8/test-project/pom.xml new file mode 100644 index 000000000..c9342ef25 --- /dev/null +++ b/src/java-8/test-project/pom.xml @@ -0,0 +1,56 @@ + + 4.0.0 + com.mycompany.app + my-app + jar + 1.0-SNAPSHOT + my-app + http://maven.apache.org + + + 8 + 8 + + + + + junit + junit + 4.13.1 + test + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M3 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 8 + 8 + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.mycompany.app.App + + + + + + + + diff --git a/src/java-8/test-project/src/main/java/com/mycompany/app/App.java b/src/java-8/test-project/src/main/java/com/mycompany/app/App.java new file mode 100644 index 000000000..357d9f90c --- /dev/null +++ b/src/java-8/test-project/src/main/java/com/mycompany/app/App.java @@ -0,0 +1,17 @@ +/*------------------------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. + *-------------------------------------------------------------------------------------------------------------*/ + +package com.mycompany.app; + +import static javax.xml.XMLConstants.XML_NS_PREFIX; + +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello Remote World!" ); + System.out.println("The XML namespace prefix is: " + XML_NS_PREFIX); + } +} diff --git a/src/java-8/test-project/src/test/java/com/mycompany/app/AppTest.java b/src/java-8/test-project/src/test/java/com/mycompany/app/AppTest.java new file mode 100644 index 000000000..7b48ad4b4 --- /dev/null +++ b/src/java-8/test-project/src/test/java/com/mycompany/app/AppTest.java @@ -0,0 +1,28 @@ +/*-------------------------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. + *-------------------------------------------------------------------------------------------------------------*/ + +package com.mycompany.app; + +import org.junit.Test; +import static org.junit.Assert.*; + + +public class AppTest +{ + public AppTest() { + } + + @Test + public void testApp() + { + assertTrue( true ); + } + + @Test + public void testMore() + { + assertTrue( true ); + } +} diff --git a/src/java-8/test-project/test-utils.sh b/src/java-8/test-project/test-utils.sh new file mode 100644 index 000000000..6ce8ef4cb --- /dev/null +++ b/src/java-8/test-project/test-utils.sh @@ -0,0 +1,148 @@ +#!/bin/bash +SCRIPT_FOLDER="$(cd "$(dirname $0)" && pwd)" +USERNAME=${1:-vscode} + +if [ -z $HOME ]; then + HOME="/root" +fi + +FAILED=() + +echoStderr() +{ + echo "$@" 1>&2 +} + +check() { + LABEL=$1 + shift + echo -e "\n🧪 Testing $LABEL" + if "$@"; then + echo "✅ Passed!" + return 0 + else + echoStderr "❌ $LABEL check failed." + FAILED+=("$LABEL") + return 1 + fi +} + +checkMultiple() { + PASSED=0 + LABEL="$1" + echo -e "\n🧪 Testing $LABEL." + shift; MINIMUMPASSED=$1 + shift; EXPRESSION="$1" + while [ "$EXPRESSION" != "" ]; do + if $EXPRESSION; then ((PASSED++)); fi + shift; EXPRESSION=$1 + done + if [ $PASSED -ge $MINIMUMPASSED ]; then + echo "✅ Passed!" + return 0 + else + echoStderr "❌ $LABEL check failed." + FAILED+=("$LABEL") + return 1 + fi +} + +checkOSPackages() { + LABEL=$1 + shift + echo -e "\n🧪 Testing $LABEL" + if dpkg-query --show -f='${Package}: ${Version}\n' "$@"; then + echo "✅ Passed!" + return 0 + else + echoStderr "❌ $LABEL check failed." + FAILED+=("$LABEL") + return 1 + fi +} + +checkExtension() { + # Happens asynchronusly, so keep retrying 10 times with an increasing delay + EXTN_ID="$1" + TIMEOUT_SECONDS="${2:-10}" + RETRY_COUNT=0 + echo -e -n "\n🧪 Looking for extension $1 for maximum of ${TIMEOUT_SECONDS}s" + until [ "${RETRY_COUNT}" -eq "${TIMEOUT_SECONDS}" ] || \ + [ ! -e $HOME/.vscode-server/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-server-insiders/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-test-server/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-remote/extensions/${EXTN_ID}* ] + do + sleep 1s + (( RETRY_COUNT++ )) + echo -n "." + done + + if [ ${RETRY_COUNT} -lt ${TIMEOUT_SECONDS} ]; then + echo -e "\n✅ Passed!" + return 0 + else + echoStderr -e "\n❌ Extension $EXTN_ID not found." + FAILED+=("$LABEL") + return 1 + fi +} + +checkCommon() +{ + PACKAGE_LIST="apt-utils \ + git \ + openssh-client \ + less \ + iproute2 \ + procps \ + curl \ + wget \ + unzip \ + nano \ + jq \ + lsb-release \ + ca-certificates \ + apt-transport-https \ + dialog \ + gnupg2 \ + libc6 \ + libgcc1 \ + libgssapi-krb5-2 \ + liblttng-ust0 \ + libstdc++6 \ + zlib1g \ + locales \ + sudo" + + # Actual tests + checkOSPackages "common-os-packages" ${PACKAGE_LIST} + check "non-root-user" id ${USERNAME} + check "locale" [ $(locale -a | grep en_US.utf8) ] + check "sudo" sudo echo "sudo works." + check "zsh" zsh --version + check "oh-my-zsh" [ -d "$HOME/.oh-my-zsh" ] + check "login-shell-path" [ -f "/etc/profile.d/00-restore-env.sh" ] + check "code" which code +} + +reportResults() { + if [ ${#FAILED[@]} -ne 0 ]; then + echoStderr -e "\n💥 Failed tests: ${FAILED[@]}" + exit 1 + else + echo -e "\n💯 All passed!" + exit 0 + fi +} + +fixTestProjectFolderPrivs() { + if [ "${USERNAME}" != "root" ]; then + TEST_PROJECT_FOLDER="${1:-$SCRIPT_FOLDER}" + FOLDER_USER="$(stat -c '%U' "${TEST_PROJECT_FOLDER}")" + if [ "${FOLDER_USER}" != "${USERNAME}" ]; then + echoStderr "WARNING: Test project folder is owned by ${FOLDER_USER}. Updating to ${USERNAME}." + sudo chown -R ${USERNAME} "${TEST_PROJECT_FOLDER}" + fi + fi +} \ No newline at end of file diff --git a/src/java-8/test-project/test.sh b/src/java-8/test-project/test.sh new file mode 100755 index 000000000..745f8149e --- /dev/null +++ b/src/java-8/test-project/test.sh @@ -0,0 +1,30 @@ +#!/bin/bash +cd $(dirname "$0") + +source test-utils.sh vscode + +# Run common tests +checkCommon + +# Prep +echo -e "\nGetting Maven wrapper..." +curl -sSL https://github.com/takari/maven-wrapper/archive/maven-wrapper-0.5.5.tar.gz| tar -xzf - +mv maven-wrapper-maven-wrapper-0.5.5/mvnw mvnw +mv maven-wrapper-maven-wrapper-0.5.5/.mvn .mvn +rm -rf mv maven-wrapper-maven-wrapper-0.5.5 + +# Image specific tests +checkExtension "vscjava.vscode-java-pack" +check "java" java -version +check "build-and-test-jar" ./mvnw -q package +check "test-project" java -jar target/my-app-1.0-SNAPSHOT.jar + +# Clean up +rm -f mvnw +rm -rf .mvn + +"checki path" +which java + +# Report result +reportResults diff --git a/src/java/.devcontainer.json b/src/java/.devcontainer.json index 26cb68a96..b0e3c385c 100644 --- a/src/java/.devcontainer.json +++ b/src/java/.devcontainer.json @@ -5,9 +5,7 @@ }, "features": { "ghcr.io/devcontainers/features/java:1": { - "version": "none", - "installGradle": "true", - "installMaven": "true" + "version": "none" }, "ghcr.io/devcontainers/features/node:1": "none" }, diff --git a/src/java/README.md b/src/java/README.md index 1074da446..5d06e4325 100644 --- a/src/java/README.md +++ b/src/java/README.md @@ -19,6 +19,8 @@ See **[history](history)** for information on the contents of published images. ## Using this image +> **Note:** A version of this [image for **JDK 8**](../java-8) is also available! + You can directly reference pre-built versions of `Dockerfile` by using the `image` property in `.devcontainer.json` or updating the `FROM` statement in your own `Dockerfile` to one of the following. An example `Dockerfile` is included in this repository. - `mcr.microsoft.com/devcontainers/java` (latest) @@ -39,18 +41,32 @@ Alternatively, you can use the contents of `Dockerfile` to fully customize your ### Installing Maven or Gradle -This image has latest version of Maven and Gradle installed by default. +You can use the [Java Feature](https://github.com/devcontainers/features/tree/main/src/java) to install `maven` and `gradle` in `.devcontainer.json`: + +```json +{ + "features": { + "ghcr.io/devcontainers/features/java:1": { + "version": "none", + "installGradle": "true", + "installMaven": "true" + } + } +} +``` ### Installing Node.js Given JavaScript front-end web client code written for use in conjunction with a Java back-end often requires the use of Node.js-based utilities to build, this container also includes `nvm` so that you can easily install Node.js. -Also, you can use a [Node feature](https://github.com/devcontainers/features/tree/main/src/node) to install any version of Node by adding the following to `.devcontainer.json`: +Also, you can use a [Node Feature](https://github.com/devcontainers/features/tree/main/src/node) to install any version of Node by adding the following to `.devcontainer.json`: ```json { "features": { - "ghcr.io/devcontainers/features/node:1": "latest" + "ghcr.io/devcontainers/features/node:1": { + "version": "latest" + } } } ``` diff --git a/src/java/test-project/test.sh b/src/java/test-project/test.sh index 4ddaca854..4ee632a43 100755 --- a/src/java/test-project/test.sh +++ b/src/java/test-project/test.sh @@ -15,8 +15,6 @@ rm -rf mv maven-wrapper-maven-wrapper-0.5.5 # Image specific tests check "java" java -version -check "gradle" gradle -g /tmp --version -check "maven" mvn --version check "build-and-test-jar" ./mvnw -q package check "test-project" java -jar target/my-app-1.0-SNAPSHOT.jar check "nvm" bash -c ". /usr/local/share/nvm/nvm.sh && nvm install 10"