From 8e42b400cae534e9be4f56751e4472bd7d3004b0 Mon Sep 17 00:00:00 2001 From: Aliaksandr Budzko <abudzko@takima.fr> Date: Tue, 3 Aug 2021 17:03:06 +0200 Subject: [PATCH] initiate repo and add examle --- .gitlab-ci.yml | 21 ++++++++ README.md | 7 ++- build/build-image.sh | 45 ++++++++++++++++++ build/parse_yaml.sh | 17 +++++++ resources/code_palindrome_test/Dockerfile | 6 +++ resources/code_palindrome_test/challenge.yaml | 14 ++++++ .../code_palindrome_test/docs/briefing.md | 7 +++ .../code_palindrome_test/docs/fr/briefing.md | 7 +++ .../code_palindrome_test/docs/fr/hint1.md | 3 ++ .../code_palindrome_test/docs/fr/hint2.md | 3 ++ resources/code_palindrome_test/docs/hint1.md | 3 ++ resources/code_palindrome_test/docs/hint2.md | 3 ++ resources/code_palindrome_test/run.sh | 8 ++++ .../src/main/java/app/Logger.java | 38 +++++++++++++++ .../src/main/java/app/Run.java | 17 +++++++ .../src/main/java/app/Solve.java | 31 ++++++++++++ .../src/main/java/success/Palindrome.java | 10 ++++ .../src/main/java/template/Palindrome.java | 10 ++++ resources/code_palindrome_test/thumbnail.png | Bin 0 -> 14095 bytes 19 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100755 build/build-image.sh create mode 100644 build/parse_yaml.sh create mode 100755 resources/code_palindrome_test/Dockerfile create mode 100755 resources/code_palindrome_test/challenge.yaml create mode 100755 resources/code_palindrome_test/docs/briefing.md create mode 100644 resources/code_palindrome_test/docs/fr/briefing.md create mode 100644 resources/code_palindrome_test/docs/fr/hint1.md create mode 100644 resources/code_palindrome_test/docs/fr/hint2.md create mode 100755 resources/code_palindrome_test/docs/hint1.md create mode 100644 resources/code_palindrome_test/docs/hint2.md create mode 100755 resources/code_palindrome_test/run.sh create mode 100755 resources/code_palindrome_test/src/main/java/app/Logger.java create mode 100755 resources/code_palindrome_test/src/main/java/app/Run.java create mode 100755 resources/code_palindrome_test/src/main/java/app/Solve.java create mode 100644 resources/code_palindrome_test/src/main/java/success/Palindrome.java create mode 100644 resources/code_palindrome_test/src/main/java/template/Palindrome.java create mode 100755 resources/code_palindrome_test/thumbnail.png diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..ced011b --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,21 @@ +stages: + - build + +build-images: + services: + - docker:19.03.12-dind + tags: + - api + stage: build + image: registry.e-biz.fr/apuret/deadlock-public/ci-deadlock-cli:1.2.4 + script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - ./build/build-image.sh $CI_REGISTRY_IMAGE + cache: + paths: + - resources/*/runner + - resources/*/.hash + only: + - master + tags: + - docker diff --git a/README.md b/README.md index 0f91ff9..69a1ee5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ -# Deadlock challenges example +# Example of a private Deadlock challenges repository + +Feel free to inspire take it as a template and add your challenges. + +You can see how to create a Deadlock challenge [here](https://deadlock-resources.github.io/challenge-documentation/). -An example custom Deadlock custom challenges repository. \ No newline at end of file diff --git a/build/build-image.sh b/build/build-image.sh new file mode 100755 index 0000000..2d37e84 --- /dev/null +++ b/build/build-image.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# include parse_yaml function +. ./build/parse_yaml.sh + +# Verify that docker daemon is up +until docker ps > /dev/null; do + echo "Waiting for docker daemon to wake up..." + sleep 2 +done + +# $1 gitlab registry name + +for d in resources/*/; do + # read yaml file + eval $(parse_yaml "$d/challenge.yaml" "mission_"); + IMAGE_NAME="$mission_name:$mission_version"; + echo "============================================================================="; + echo "> Checking if image $IMAGE_NAME exists in registry <"; + # Careful experimental feature (docker manifest inspect), can be change in the future. + docker manifest inspect "$1/$IMAGE_NAME" > /dev/null && echo "> Found.. continue" && continue || true; + + # go into the folder to keep relative path when executing script + cd $d + + [ -f build.sh ] && echo "> Building challenge <" && ./build.sh + [ -f entry.rs ] && echo "> Building runner for metamorph challenge <" && dcli build . + echo "> Building Docker image $IMAGE_NAME <"; + docker build . -q -t "$1/$IMAGE_NAME" && docker push "$1/$IMAGE_NAME" || exit 1 + if [ -d "./services" ]; then + for s in ./services/*/; do + echo ">>> Checking service image $s <"; + eval $(parse_yaml "./service.yaml" "service_"); + IMAGE_SERVICE_NAME="$mission_name/$service_name:$mission_version"; + docker manifest inspect "$1/$IMAGE_SERVICE_NAME" > /dev/null && echo "> Found.. continue" && continue || true; + echo ">>> Building service image $s <"; + docker build $s -q -t "$1/$IMAGE_SERVICE_NAME"; + docker push "$1/$IMAGE_SERVICE_NAME"; + done + fi + + # back to previous folder + cd - +done + diff --git a/build/parse_yaml.sh b/build/parse_yaml.sh new file mode 100644 index 0000000..ca35ceb --- /dev/null +++ b/build/parse_yaml.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +parse_yaml() { + local prefix=$2 + local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034') + sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \ + -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 | + awk -F$fs '{ + indent = length($1)/2; + vname[indent] = $2; + for (i in vname) {if (i > indent) {delete vname[i]}} + if (length($3) > 0) { + vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")} + printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3); + } + }' +} \ No newline at end of file diff --git a/resources/code_palindrome_test/Dockerfile b/resources/code_palindrome_test/Dockerfile new file mode 100755 index 0000000..df536fa --- /dev/null +++ b/resources/code_palindrome_test/Dockerfile @@ -0,0 +1,6 @@ +FROM openjdk:8-jdk-slim +WORKDIR / +COPY src src +COPY run.sh / +RUN chmod +x run.sh +ENTRYPOINT ["/run.sh"] diff --git a/resources/code_palindrome_test/challenge.yaml b/resources/code_palindrome_test/challenge.yaml new file mode 100755 index 0000000..325b215 --- /dev/null +++ b/resources/code_palindrome_test/challenge.yaml @@ -0,0 +1,14 @@ +version: 1.0 +name: code_palindrome_test +label: Example exercice - palindrome +description: Can you read it from whatever side +level: jarjarbinks +type: CODING +xp: + java: 1 + programming: 1 +coding: + templateDirectory: src/main/java/template + successDirectory: src/main/java/success + target: Palindrome.java + editorMode: java diff --git a/resources/code_palindrome_test/docs/briefing.md b/resources/code_palindrome_test/docs/briefing.md new file mode 100755 index 0000000..a8a6eae --- /dev/null +++ b/resources/code_palindrome_test/docs/briefing.md @@ -0,0 +1,7 @@ +# Palindrome + +A Palindrome is a word that can be read indifferently from left or right or from right to left and keeps the same meaning. + +Exemple: eye + +You have to check if a given word is a palindrome or not. \ No newline at end of file diff --git a/resources/code_palindrome_test/docs/fr/briefing.md b/resources/code_palindrome_test/docs/fr/briefing.md new file mode 100644 index 0000000..84a29eb --- /dev/null +++ b/resources/code_palindrome_test/docs/fr/briefing.md @@ -0,0 +1,7 @@ +# Palindrome + +Un palindrome est un mot qui peut être lu indifféremment de gauche à droite ou de droite à gauche et qui conserve le même sens. + +Exemple: elle + +Tu dois vérifier pour un mot donné s'il s'agit d'un palindrome ou non. \ No newline at end of file diff --git a/resources/code_palindrome_test/docs/fr/hint1.md b/resources/code_palindrome_test/docs/fr/hint1.md new file mode 100644 index 0000000..d99e48e --- /dev/null +++ b/resources/code_palindrome_test/docs/fr/hint1.md @@ -0,0 +1,3 @@ +# Indice 1 + +Regarde du côté du StringBuilder. diff --git a/resources/code_palindrome_test/docs/fr/hint2.md b/resources/code_palindrome_test/docs/fr/hint2.md new file mode 100644 index 0000000..8fa0a36 --- /dev/null +++ b/resources/code_palindrome_test/docs/fr/hint2.md @@ -0,0 +1,3 @@ +# Indice 2 + +Tu peux aussi t'aider de l'API Stream pour les String. \ No newline at end of file diff --git a/resources/code_palindrome_test/docs/hint1.md b/resources/code_palindrome_test/docs/hint1.md new file mode 100755 index 0000000..aa044e6 --- /dev/null +++ b/resources/code_palindrome_test/docs/hint1.md @@ -0,0 +1,3 @@ +# Hint 1 + +Look into the functions of StringBuilder. diff --git a/resources/code_palindrome_test/docs/hint2.md b/resources/code_palindrome_test/docs/hint2.md new file mode 100644 index 0000000..5f3ebc3 --- /dev/null +++ b/resources/code_palindrome_test/docs/hint2.md @@ -0,0 +1,3 @@ +# Hint 2 + +You can check the stream API for Strings. \ No newline at end of file diff --git a/resources/code_palindrome_test/run.sh b/resources/code_palindrome_test/run.sh new file mode 100755 index 0000000..6bf4306 --- /dev/null +++ b/resources/code_palindrome_test/run.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e +echo "Compiling java code" +cd src/main/java +javac -Xmaxerrs 1 app/*.java +echo "Executing code" +java -Xms32m -Xmx64m app/$1 +echo "Done" diff --git a/resources/code_palindrome_test/src/main/java/app/Logger.java b/resources/code_palindrome_test/src/main/java/app/Logger.java new file mode 100755 index 0000000..f4ed299 --- /dev/null +++ b/resources/code_palindrome_test/src/main/java/app/Logger.java @@ -0,0 +1,38 @@ +package app; + +public class Logger { + + public static void log(String message) { + System.out.println("----------------------------------------------------"); + System.out.println(message); + System.out.println("----------------------------------------------------"); + } + + public static void logSuccess() { + System.out.println("----------------------------------------------------"); + System.out.println("Good job!"); + System.out.println("----------------------------------------------------"); + } + + public static void logNoMatch(Object... args) { + System.err.println("--------------------------------------------------------------------------"); + System.err.println(String.format("You have to try again, the word %s evaluated to %s, expecting %s", args[0], + args[1], args[2])); + System.err.println("--------------------------------------------------------------------------"); + } + + public static void logException(Object... args) { + System.err.println("----------------------------------------------------"); + System.err.println("An error occurred during runtime."); + System.err.println("Details:" + args[0]); + } + + public static void logException(Throwable throwable) { + System.err.println("----------------------------------------------------"); + System.err.println("Something bad happened!"); + System.err.println(throwable.getMessage()); + throwable.printStackTrace(); + System.err.println("----------------------------------------------------"); + } + +} diff --git a/resources/code_palindrome_test/src/main/java/app/Run.java b/resources/code_palindrome_test/src/main/java/app/Run.java new file mode 100755 index 0000000..ff2ff8b --- /dev/null +++ b/resources/code_palindrome_test/src/main/java/app/Run.java @@ -0,0 +1,17 @@ +package app; + +import static template.Palindrome.isPalindrome;; + +public class Run { + + public static void main(String[] args) { + String[] words = { "pop", "non", "eye", "hello" }; + try { + for (String n : words) { + Logger.log("Input : " + n + " - Output : " + isPalindrome(n)); + } + } catch (RuntimeException e) { + Logger.logException(e); + } + } +} diff --git a/resources/code_palindrome_test/src/main/java/app/Solve.java b/resources/code_palindrome_test/src/main/java/app/Solve.java new file mode 100755 index 0000000..c2dfd9e --- /dev/null +++ b/resources/code_palindrome_test/src/main/java/app/Solve.java @@ -0,0 +1,31 @@ +package app; + +import static template.Palindrome.isPalindrome; +import static success.Palindrome.isPalindrome; + +import java.lang.Math; +import java.util.*; + +public class Solve { + + public static void main(String[] args) { + List<String> words = Arrays.asList("level", "deadlock", "java", "madam", "check", "rotator", "kayak", "anna"); + Collections.shuffle(words); + + try { + for (String word : words) { + boolean userValue = template.Palindrome.isPalindrome(word); + boolean expectedValue = success.Palindrome.isPalindrome(word); + if (expectedValue != userValue) { + Logger.logNoMatch(word, userValue, expectedValue); + System.exit(1); + } + } + // all test passed successfully + Logger.logSuccess(); + } catch (RuntimeException e) { + Logger.logException(e); + System.exit(1); + } + } +} diff --git a/resources/code_palindrome_test/src/main/java/success/Palindrome.java b/resources/code_palindrome_test/src/main/java/success/Palindrome.java new file mode 100644 index 0000000..630767b --- /dev/null +++ b/resources/code_palindrome_test/src/main/java/success/Palindrome.java @@ -0,0 +1,10 @@ +package success; + +import java.util.*; + +public class Palindrome { + + public static boolean isPalindrome(String word) { + return word.equals(new StringBuilder(word).reverse().toString()); + } +} diff --git a/resources/code_palindrome_test/src/main/java/template/Palindrome.java b/resources/code_palindrome_test/src/main/java/template/Palindrome.java new file mode 100644 index 0000000..da39943 --- /dev/null +++ b/resources/code_palindrome_test/src/main/java/template/Palindrome.java @@ -0,0 +1,10 @@ +package template; + +import java.util.*; + +public class Palindrome { + + public static boolean isPalindrome(String word) { + return false; + } +} diff --git a/resources/code_palindrome_test/thumbnail.png b/resources/code_palindrome_test/thumbnail.png new file mode 100755 index 0000000000000000000000000000000000000000..30f95b3a5e7b468f3dcc0638c29899c9493c4b85 GIT binary patch literal 14095 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 literal 0 HcmV?d00001 -- GitLab