diff --git a/.gitignore b/.gitignore index 43a8d1508996bab2a79fd3dd26f21cbc919a37d3..530db921a751c03ed4845be0de44338fd51a3ba4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ aws.xml workspace.xml +.gitlab-ci-local +Wiki/.idea + +ExerciseChecker/src/config/Version.ts ############################ MacOS # General diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 471286723583c6d63f77dba3973d953982d876ae..2f80fec09fcd49c2fe534a7a9f345d89d862e0c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,350 +1,8 @@ -variables: - GIT_SUBMODULE_STRATEGY: recursive - GIT_SUBMODULE_FORCE_HTTPS: "true" +include: "ExerciseChecker/.gitlab-ci/**.yml" - DOCKER_HOST: tcp://docker:2375 - DOCKER_TLS_CERTDIR: - DOCKER_DRIVER: overlay2 - - PROJECT_NAME: DojoExerciseChecker - - VERSION_DEV_SUFFIX: '-dev' - - GITLAB_API_PROJECT_URL: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID} - - DOCKERFILE: Dockerfile_ExerciseChecker - DOCKER_PLATFORMS: linux/amd64,linux/arm64/v8 - - PROJECT_FOLDER: ExerciseChecker - - PACKAGE_REGISTRY_URL: "${GITLAB_API_PROJECT_URL}/packages/generic/${PROJECT_NAME}" - - WIKI_FOLDER: Wiki - - -.get_version: - script: - - IS_DEV=$([[ $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ]] && echo false || echo true) - - VERSION=$(jq -r .version $PROJECT_FOLDER/package.json)$([[ $IS_DEV == true ]] && echo $VERSION_DEV_SUFFIX || echo '') - - -.get_packages_url: - script: - # Wiki - - WIKI_ARCHIVE_NAME="${PROJECT_NAME}_Wiki_${VERSION}.tar.xz" - - PACKAGE_URL_WIKI="${PACKAGE_REGISTRY_URL}_Wiki/${VERSION}/${WIKI_ARCHIVE_NAME}" - - -.init_dind_script: - script: - # Install dependencies - - apk update - - apk add git - - apk add jq - - apk add curl - - # Init docker buildx - - docker login -u $DOCKER_REGISTRY_USER -p $DOCKER_REGISTRY_PASSWORD $DOCKER_REGISTRY - - docker buildx create --use - - # Get version from package.json - - !reference [.get_version, script] - - CONTAINER_IMAGE=$DOCKER_REGISTRY_IMAGE:$VERSION - - -.build_script: - script: - - !reference [ .init_dind_script, script ] - - mkdir -p $ARTIFACTS_FOLDER - - # Decrypt env vars for production - - apk add npm sed - - cd $PROJECT_FOLDER - - sed -i -r "s/\{\{VERSION\}\}/${VERSION}/g" src/app.ts - - | - if [ $CI_COMMIT_REF_PROTECTED == "true" ]; then - echo "Decrypt production env vars" - sed -i -r "s/(DOTENV_KEY[ ]*:[ ]*[\'\"\`])[^'\"\`]*([\'\"\`])([ ]*\,)?//g" src/app.ts - sed -i -r "s/,[\ \n]*\}/\}/g" src/app.ts - npx dotenv-vault local decrypt "${DOTENV_PROD_KEY}" > .env - - fi - - cd .. - - # Need to build for each platform separately because of multi-stage builds (docker buildx don't use cache same way as docker build) - - > - platform_array=$(echo $DOCKER_PLATFORMS | tr "," "\n"); - for platform in $platform_array; do - echo "Buildind for : $platform" - docker buildx build --pull --platform $platform --file $DOCKERFILE --tag $CONTAINER_IMAGE . - done - - -.clean_release: - script: - # Delete release if it already exists - - 'curl --request DELETE --header "JOB-TOKEN: $CI_JOB_TOKEN" "${GITLAB_API_PROJECT_URL}/releases/${VERSION}"' - - # Delete tag if it already exists (use private-token because job-token don't have permission to delete tags) - - 'curl --request DELETE --header "PRIVATE-TOKEN: $GITLAB_PROJECT_ACCESS_TOKEN" "${GITLAB_API_PROJECT_URL}/repository/tags/${VERSION}"' - - -.clean_packages: - script: - # Get all packages of the project - - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" "${GITLAB_API_PROJECT_URL}/packages" > gitlabPackages.json' - - # Filter and select packages to delete (based on version) - - packagesToDelete=`jq -r '.[] | select(.version=="'${VERSION}'") | ._links.delete_api_path' gitlabPackages.json` - - # Delete packages by calling Gitlab API - - > - for deletePath in $packagesToDelete; do - echo "Deleting package at path : ${deletePath}" - curl --request DELETE --header "JOB-TOKEN: $CI_JOB_TOKEN" "${deletePath}" - done - - -stages: - - test - - build - - clean - - upload - - release - - -test:build: - stage: test - tags: - - build - - dind - image: docker:latest - services: - - docker:dind - script: - - !reference [ .build_script, script ] - rules: - - if: '$CI_COMMIT_TAG =~ "/^$/" && $CI_COMMIT_REF_PROTECTED != "true"' - - -build:version: - stage: build - tags: - - build - - dind - image: docker:latest - services: - - docker:dind - script: - - !reference [ .build_script, script ] - - # Here docker buildx can use cached images created in previous step - - docker buildx build --platform $DOCKER_PLATFORMS --file $DOCKERFILE --push --tag $CONTAINER_IMAGE . - rules: - - if: '$CI_COMMIT_REF_PROTECTED == "true"' - - -clean:release: - stage: clean - tags: - - gitlab_clean - image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest +lint_pass: + only: + variables: + - $FOO == "bar" script: - - !reference [.get_version, script] - - !reference [.clean_release, script] - rules: - - if: '$CI_COMMIT_REF_PROTECTED == "true"' - - -clean:packages: - stage: clean - tags: - - gitlab_clean - image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest - script: - - !reference [.get_version, script] - - !reference [.clean_packages, script] - rules: - - if: '$CI_COMMIT_REF_PROTECTED == "true"' - - -clean:dev:release: - stage: clean - tags: - - gitlab_clean - image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest - script: - - !reference [.get_version, script] - - VERSION="${VERSION}${VERSION_DEV_SUFFIX}" - - !reference [.clean_release, script] - rules: - - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - - -clean:dev:packages: - stage: clean - tags: - - gitlab_clean - image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest - script: - - !reference [.get_version, script] - - VERSION="${VERSION}${VERSION_DEV_SUFFIX}" - - !reference [.clean_packages, script] - rules: - - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - - -clean:dev:dockerhub: - stage: clean - tags: - - clean - - dind - image: docker:latest - services: - - docker:dind - script: - - !reference [.init_dind_script, script] - - VERSION="${VERSION}${VERSION_DEV_SUFFIX}" - - # Remove dev tag - ## Login to dockerhub (get JWT token) - - GET_TOKEN_DATA=$(jq --null-input --arg username "$DOCKER_REGISTRY_USER" --arg password "$DOCKER_REGISTRY_PASSWORD" '{"username":$username,"password":$password}') - - > - TOKEN=`curl --data "${GET_TOKEN_DATA}" \ - --header "Content-Type: application/json" \ - --header "JOB-TOKEN: $CI_JOB_TOKEN" \ - "https://hub.docker.com/v2/users/login/" | jq -r .token` - - ## Delete tag - - 'curl --header "Authorization: JWT ${TOKEN}" --request DELETE "https://hub.docker.com/v2/repositories/${DOCKER_REGISTRY_IMAGE}/tags/${VERSION}/"' - rules: - - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - - -upload:packages:wiki: - stage: upload - tags: - - gitlab_package - image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest - script: - # Install dependencies - - apk update - - apk add xz - - - !reference [.get_version, script] - - !reference [.get_packages_url, script] - - # Create archive - - mkdir -p $ARTIFACTS_FOLDER - - WIKI_ARCHIVE_PATH="${ARTIFACTS_FOLDER}/${WIKI_ARCHIVE_NAME}" - - tar -v -c -C "${CI_PROJECT_DIR}/${WIKI_FOLDER}" -J -f "${WIKI_ARCHIVE_PATH}" . # Ubuntu: tar --verbose --create --cd wiki-test-2 --xz --file file.tar.bz2 - - # Send package - - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${WIKI_ARCHIVE_PATH} "${PACKAGE_URL_WIKI}";' - rules: - - if: '$CI_COMMIT_REF_PROTECTED == "true"' - - -release:dockerhub:latest: - stage: release - tags: - - release - - dind - image: docker:latest - services: - - docker:dind - script: - - !reference [.init_dind_script, script] - - # Push latest tag - - docker buildx imagetools create $CONTAINER_IMAGE --tag $DOCKER_REGISTRY_IMAGE:latest - rules: - - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - - -release:wiki: - stage: release - tags: - - release - image: alpine:latest - script: - - apk update - - apk add git - - apk add jq - - - !reference [.get_version, script] - - # Define URL for the wiki in terms of project-agnostic predefined variables - - WIKI_URL="${CI_SERVER_PROTOCOL}://project_${CI_PROJECT_ID}_bot:${GITLAB_PROJECT_ACCESS_TOKEN}@${CI_SERVER_HOST}:${CI_SERVER_PORT}/${CI_PROJECT_PATH}.wiki.git" - - # Clone this project's wiki under /tmp - - rm -rf "/tmp/${CI_PROJECT_NAME}.wiki" - - cd /tmp - - git clone "${WIKI_URL}" - - # Enter the cloned repo - - cd "${CI_PROJECT_NAME}.wiki" - - # Update the file - - mv .git/ ../ - - rm -rf ./* - - mv ../.git/ ./ - - cp "${CI_PROJECT_DIR}/.gitignore" . - - cp -R "${CI_PROJECT_DIR}/${WIKI_FOLDER}/." . - - # Set committer info - - git config user.name "$GITLAB_USER_NAME" - - git config user.email "$GITLAB_USER_EMAIL" - - # Commit the gitignore file - - git add ".gitignore" - - git commit -m "Add gitignore file" || true - - # Commit the file - - git add . - - git commit -m "${VERSION}" || true - - # Push the change back to the master branch of the wiki - - git push origin "HEAD:main" - rules: - - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - - -release:gitlab: - stage: release - tags: - - release - image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest - script: - - !reference [.get_version, script] - - echo 'Running release_job' - - !reference [.get_packages_url, script] - - # Extract description from CHANGELOG.md - - CHANGELOG_LINE_START=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{print NR; exit;}' CHANGELOG.md` - - CHANGELOG_LINE_END=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{ count++; if(count>1) {print NR; exit;}}' CHANGELOG.md` - - DESCRIPTION=`awk 'NR > '$CHANGELOG_LINE_START' && NR < '$CHANGELOG_LINE_END'' CHANGELOG.md` - - # Create Release (can't be done by release_step of gitlab image because it don't have access to env var defined in script_step) - - > - RELEASE_DATA=$(jq --null-input --arg version "$VERSION" --arg description "# Changelog (version $VERSION) $DESCRIPTION" --arg tag_name "$VERSION" --arg ref "$CI_COMMIT_SHORT_SHA" '{ - "name": $version, - "description": $description, - "tag_name": $tag_name, - "ref": $ref, - "assets": { - "links": [ - { - "name": "Wiki", - "url": "'${PACKAGE_URL_WIKI}'", - } - ] - } - }') - - > - curl --data "${RELEASE_DATA}" \ - --header "Content-Type: application/json" \ - --header "JOB-TOKEN: $CI_JOB_TOKEN" \ - --request POST "${GITLAB_API_PROJECT_URL}/releases" - rules: - - if: '$CI_COMMIT_REF_PROTECTED == "true"' \ No newline at end of file + - echo \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 97411b8b9a7741ac37fa46c151e40fb83b48ec6e..9deade6d9271a41db904e6617d50df6985c69185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,19 @@ **⚠️ Deprecation:** --> -## 2.1.0 (?) +## 2.2.0 (2023-10-16) + +### ✨ Feature +- `results.json` file is now optional (if the teaching staff don't want to provide test details) + - The exercise will be considered as valid if the container exit code is 0 + - The `results.json` file will be construct / completed with the container exit code + - The `volume` argument of `dojo_assignment.json` is now optional (if the teaching staff don't want to provide `results.json` file or other files) + +### 🔨 Internal / Developers +- Enhancement in pipelines by splitting them into several files + + +## 2.1.0 (2023-09-29) ### 🎨 Interface - **💥 Breaking:** Renamed `dojo.enonce` (or `dojo.assignment`) file to `dojo_assignment.json` diff --git a/ExerciseChecker/.env.vault b/ExerciseChecker/.env.vault index 191e70d5b3df7008634ca6ccb25494aaa513b4ab..805a49510414790d46a647460afab00ef34e66f8 100644 --- a/ExerciseChecker/.env.vault +++ b/ExerciseChecker/.env.vault @@ -4,8 +4,8 @@ #/--------------------------------------------------/ # development -DOTENV_VAULT_DEVELOPMENT="Ir7CPeMh8qd9jAgmy8RaSTlY3OA4Kw6xHB+j7AJjfM07H2koRKpOUP5Fsa+XBYp1rjTCl5p2063/H3ed2VmhUwIHxa+CUwHOagy+kKLYHBVzEdSrE6EK96+ihyGwjkrFRdRRWar74TxaCsxoNpLUz0HtK1fbJZMFUhgJOklBcw8BdH6mrq3RI8+tkSx5nOTJTGo0OhilsAZTeAbOdcZQNIcIfbZVMLD1X3YocqitLyAURzfyEegp3XOHfpOr+45/Be4HaakylijLggn73sS/fDr/yo2IRHTz7il/Y8IO3kUMJnSXoqQM1EPzqiYEtIFsDW2aVjLnbDdM2BxlNe+Cv50yglPINfkOMJ3ZBsELjkDBI9N5wLW9qyoF5zxaw4jfnGTPuVsM1FhozEGTBg64GtJ82WI0ftDjt06E1jkMB9hWbYS5pKBWHq/w2A+TfatZjn9QmgEuiO5Q8RW6srojTsuuMjJwVC9YCzjzvxC0YJulfMCBYuPW1cVagp6CkA5soo7iaEfJmt++GhWkYGBMq9EeezTl5GEarpUBYdLYlwfICsgP1E+9lF5dud+zWnrGzBVeePBmcVO86C/SZici/3HkORY68NoqbswV5AXJyACFT6Qf+z3k8CLkrCvfti6xvAAf4ivorbnihB0RX4znE4e70qCU2w3QjS+cwuHuC8sEp4SpqxR+kP2+ktfA5pw08yoFg4zrSTwuvJN8OnLyryo0VODCWA1V6Bom/imtCLn2nRuYQhO0AYiAtvyWOTbeduQ7rixeYc98j80sVHU7PY4hFbF7NVnWUY+nhzJ3rbK1y9q7iLLkpivsRmeosIIhVtoSR+Lur/hYZ2ReFyA6OHofxdcIqKiphG4HLZl9lCq6a+UlRAOqN98XABGmJ1KhCTsFxTTuGahMU1r556O/4NJFnubPU1DH80n71AB4HdD2qCxs4Tm4fDVs3cDv7fak" +DOTENV_VAULT_DEVELOPMENT="+tvA0uHhVW7Czj260K5arwAA4mmVBQC/83i6YXPo7fEEoiBvXZCPSovZ23phpqYx5LmqgtTQALb5gisUqSJfrjwDYABKk0tfrkCvrFayUxv5NuFSd2RVliE1JJ5nZgVeAdcy01igGgQOdFzS5Ze/Gy5iEP+C7Nq8HflazeQrlAXlIGNlPe77v+bSIce1Itf9dj8/sB5xz6wwRY5TSF+CM1OI8dTTrKOruxKXVQP0ztpUsGHUjQvowYR7JHeXQOivn8phUTWjR4vZskTBGGe/s0DiIQ5qQq+HR2LFxVEmzOI0eHY5vSEkZ9pAYmUX9GB0uJcAwdInSiLWkgC12t+7FuMfHM7JtE6pI0NECQwZIWm1DZSurYr8wIiYCg4Ds8xsHFPE+CmJma3jfZzx9aE/RN2AM7fbvKWVLakPB3OQK9SrPaM6jWVRfkaEqUDqggX4gJy98SAHznYhGamsQBxWa7wXRrlXC844SDCoZVsql22UNy/hRnXjGKonHhlm6MQE4y07btcfRkPF696Mvjue8Y/loTS0GGLZXFr+3LcRLcJTTwDT88tdNbQd5QiaFTTMY7pDAFeDke5M7VqYlFcrAtb8+9TGtZOXBgnYXmSpF+KKw4JwY6v7rw9wAGE3W++xI70stmCZU+M5/TDLO5SoWzcqlWJEe2VMI6QZmegzPGymc5g47ujdwvWKg1JZlLcrXjDMBi2/2Mv4H95PYpt6rEpY+ZHtNaO/kI6hRbqUvIZ8frL1dM5DihtoIaHpADnSQ0YfWlk01LA6Jm1BFBJr7RUHLw/S+avzMlbHnjAvxL36dUagvwynDQFapwhFrqLiq9vTmM+8tPQeOuGeutJlJgvDgGxd6hmiHDImrknZtNQiZ0hkVCoVgwopRe/bxlfGAqbpZUvs6eu9CFT6poWOVUvq2lilRlUQXESs7CP599cpLYgu3zViQB7wG659bAK+CNWsJlDoYN4dfT5Bw+P/6pTta/9hegT6f0/ui8p4oRK2bsJMCv8+502M52beU4GjBIjNTepFauCi9LcxIMzXclscedj0qlE=" # production -DOTENV_VAULT_PRODUCTION="NEaGKlOKDlL652EG43Iid/7P4ybOxkmc3t+6xgKS11Kevzs7zocA8cjJqfkjBAFdMu1oZbiLO+dzXgqEVHmu+66Qr8CDMufQsNXgpO6viLUx/FlDpeY1IYJVma/W1GwEP+5AW2BhrT7MJnBidzx5wHomjy+CAYlMoV3c4Q/obJQcomq65jn5KSgaiFcyLqitbUESJhxAS5adRXvtkdbqaXR5yCRtXF2OS50HW544/9qf9e9hqwELHrd1fYOJqYuE/zbZV3ZGgwgK7Ui0tv8MU4uN6LzpTTZVWdK4cdMuQTtX23rTWg9J9WcAj5M8jpo309VFPPxN3SfzoQHVb9+RlL+XEH92EwRFFevtKdE07Wz5ZbUbtiKAqkqaKiusjSALasoT+mW5LFWjFdFCDJKof98w00kR7c6PoTDBw50Zh5/z+UPn4uXT8ZOKV7ooxGQLATrBP+4k7pyBx2bYCo/o0tbgSJ4uTv+Wo2eVwUXrptxwmp6Tow50NAdcvKJCQmDe43e6PMsyX+9GO6CbRywvS/2aLffXRly+txqkr0B9/suMwuQwyEv8LOVR9Q2z3MC6leQ3VAaGxNGLKEdtJFgn+31JnciGy6HQABDZ6Agc/Tux4PBOOWy0yq0mGQztIk/0dj+WMro=" +DOTENV_VAULT_PRODUCTION="bhLJF6f7KLRNI6witxN4RNPaylwg/Z+3VB52htArsNhYUck4OhUMaKZRCAdAo90JCd+ap3xyvipfqdGsbwXBq79VVcKDNzW9pSkYGBZvNpTPk2xnGAfZmAhgZmz4AX1Jbmb6rjpTRDemL8ocsEqVlb0KsxhcPjALjVvHeDxbWUiFc4MsnNo7L26PRNFpV1aVQ6F1u90WymMsEBE6lGnArXbnyREMM3tqq7fBxczg2um8dt50MRTYCWpG+7/Hdp7TnusoRSYU5Qs6Y0kI3UQxLUXuP+QN9ktI7+6RJ1aki31XojTggtKPXskVvhzZeU5k5ZGYJSn46BTQNFqx7KpLPKo9cTNNZiIV7wVmNyUGmOQfCAdyN14TN8/0LDs3QnglQd4XUkYHlTPFQFbUFlGIvh8ErtOcgH3cu7sfO0z2diZFvHI5fAjuUJdjTSGtP07aI1pXUMHU4YHUX7W8ycf4wP9k91tynd0YLLeXagEuSnfXx5ev/iguvh9BZV4jy328igHB4Cxh0yY24hClzIKc2fqghvLMkafBUID/JMjG/N/AdHxaw/UiXsvK4oJUHpYKLC83UeGugMonfppZdIzPpTrji487SI04zIVCJGxf1bXMGJWHNycff5Ehi5MNHqiDDkwY6OzsGPUyzrTC4/KHGyQwMbdBmrmLEWqlzDoLprm9j0CuAavAHj3eaFnuiOKhs1B8Qb3LosCDPJuErJavI/g2gj59YkiIDLjnUQ==" diff --git a/ExerciseChecker/.gitlab-ci/00_vars.yml b/ExerciseChecker/.gitlab-ci/00_vars.yml new file mode 100644 index 0000000000000000000000000000000000000000..46cb27b058b7ab53199e24bc6288172dcdf94417 --- /dev/null +++ b/ExerciseChecker/.gitlab-ci/00_vars.yml @@ -0,0 +1,22 @@ +variables: + GIT_SUBMODULE_STRATEGY: recursive + GIT_SUBMODULE_FORCE_HTTPS: "true" + + DOCKER_HOST: tcp://docker:2375 + DOCKER_TLS_CERTDIR: + DOCKER_DRIVER: overlay2 + + PROJECT_NAME: DojoExerciseChecker + + VERSION_DEV_SUFFIX: '-dev' + + GITLAB_API_PROJECT_URL: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID} + + DOCKERFILE: Dockerfile_ExerciseChecker + DOCKER_PLATFORMS: linux/amd64,linux/arm64/v8 + + PROJECT_FOLDER: ExerciseChecker + + PACKAGE_REGISTRY_URL: "${GITLAB_API_PROJECT_URL}/packages/generic/${PROJECT_NAME}" + + WIKI_FOLDER: Wiki \ No newline at end of file diff --git a/ExerciseChecker/.gitlab-ci/01_functions.yml b/ExerciseChecker/.gitlab-ci/01_functions.yml new file mode 100644 index 0000000000000000000000000000000000000000..f0675a514e20658a7d87932f65073487c89cc028 --- /dev/null +++ b/ExerciseChecker/.gitlab-ci/01_functions.yml @@ -0,0 +1,84 @@ +.get_version: + script: + - IS_DEV=$([[ $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ]] && echo false || echo true) + - VERSION=$(jq -r .version $PROJECT_FOLDER/package.json)$([[ $IS_DEV == true ]] && echo $VERSION_DEV_SUFFIX || echo '') + + +.get_packages_url: + script: + # Wiki + - WIKI_ARCHIVE_NAME="${PROJECT_NAME}_Wiki_${VERSION}.tar.xz" + - PACKAGE_URL_WIKI="${PACKAGE_REGISTRY_URL}_Wiki/${VERSION}/${WIKI_ARCHIVE_NAME}" + + +.init_dind_script: + script: + # Install dependencies + - apk update + - apk add git + - apk add jq + - apk add curl + + # Init docker buildx + - | + if [ $CI_COMMIT_REF_PROTECTED == "true" ]; then + docker login -u $DOCKER_REGISTRY_USER -p $DOCKER_REGISTRY_PASSWORD $DOCKER_REGISTRY + fi + - docker buildx create --use + + # Get version from package.json + - !reference [ .get_version, script ] + - CONTAINER_IMAGE=$DOCKER_REGISTRY_IMAGE:$VERSION + + +.build_script: + script: + - !reference [ .init_dind_script, script ] + - mkdir -p $ARTIFACTS_FOLDER + + # Decrypt env vars for production + - apk add npm sed + - cd $PROJECT_FOLDER + - sed -i -r "s/\{\{VERSION\}\}/${VERSION}/g" src/app.ts + - | + if [ $CI_COMMIT_REF_PROTECTED == "true" ]; then + echo "Decrypt production env vars" + sed -i -r "s/(DOTENV_KEY[ ]*:[ ]*[\'\"\`])[^'\"\`]*([\'\"\`])([ ]*\,)?//g" src/app.ts + sed -i -r "s/,[\ \n]*\}/\}/g" src/app.ts + npx dotenv-vault local decrypt "${DOTENV_PROD_KEY}" > .env + + fi + - cd .. + + # Need to build for each platform separately because of multi-stage builds (docker buildx don't use cache same way as docker build) + - > + platform_array=$(echo $DOCKER_PLATFORMS | tr "," "\n"); + for platform in $platform_array; do + echo "Buildind for : $platform" + docker buildx build --pull --platform $platform --file $DOCKERFILE --tag $CONTAINER_IMAGE . + done + + +.clean_release: + script: + # Delete release if it already exists + - 'curl --request DELETE --header "JOB-TOKEN: $CI_JOB_TOKEN" "${GITLAB_API_PROJECT_URL}/releases/${VERSION}"' + + # Delete tag if it already exists (use private-token because job-token don't have permission to delete tags) + - 'curl --request DELETE --header "PRIVATE-TOKEN: $GITLAB_PROJECT_ACCESS_TOKEN" "${GITLAB_API_PROJECT_URL}/repository/tags/${VERSION}"' + + +.clean_packages: + script: + # Get all packages of the project + - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" "${GITLAB_API_PROJECT_URL}/packages" > gitlabPackages.json' + + # Filter and select packages to delete (based on version) + - packagesToDelete=`jq -r '.[] | select(.version=="'${VERSION}'") | ._links.delete_api_path' gitlabPackages.json` + + # Delete packages by calling Gitlab API + - > + for deletePath in $packagesToDelete; do + echo "Deleting package at path : ${deletePath}" + curl --request DELETE --header "JOB-TOKEN: $CI_JOB_TOKEN" "${deletePath}" + done \ No newline at end of file diff --git a/ExerciseChecker/.gitlab-ci/03_stages.yml b/ExerciseChecker/.gitlab-ci/03_stages.yml new file mode 100644 index 0000000000000000000000000000000000000000..1b20f1ed1fa05c19ee6d8145ccd33bcc39c85d9d --- /dev/null +++ b/ExerciseChecker/.gitlab-ci/03_stages.yml @@ -0,0 +1,6 @@ +stages: + - test + - build + - clean + - upload + - release \ No newline at end of file diff --git a/ExerciseChecker/.gitlab-ci/04_stageTest.yml b/ExerciseChecker/.gitlab-ci/04_stageTest.yml new file mode 100644 index 0000000000000000000000000000000000000000..a1b564295624124e216ae7bba18ac6950590eccb --- /dev/null +++ b/ExerciseChecker/.gitlab-ci/04_stageTest.yml @@ -0,0 +1,12 @@ +test:build: + stage: test + tags: + - build + - dind + image: docker:latest + services: + - docker:dind + script: + - !reference [ .build_script, script ] + rules: + - if: '$CI_COMMIT_TAG =~ "/^$/" && $CI_COMMIT_REF_PROTECTED != "true"' \ No newline at end of file diff --git a/ExerciseChecker/.gitlab-ci/05_stageBuild.yml b/ExerciseChecker/.gitlab-ci/05_stageBuild.yml new file mode 100644 index 0000000000000000000000000000000000000000..e2acdd1042c77d84b45e63e3b8b2cdaf217d92bc --- /dev/null +++ b/ExerciseChecker/.gitlab-ci/05_stageBuild.yml @@ -0,0 +1,15 @@ +build:version: + stage: build + tags: + - build + - dind + image: docker:latest + services: + - docker:dind + script: + - !reference [ .build_script, script ] + + # Here docker buildx can use cached images created in previous step + - docker buildx build --platform $DOCKER_PLATFORMS --file $DOCKERFILE --push --tag $CONTAINER_IMAGE . + rules: + - if: '$CI_COMMIT_REF_PROTECTED == "true"' \ No newline at end of file diff --git a/ExerciseChecker/.gitlab-ci/06_stageClean.yml b/ExerciseChecker/.gitlab-ci/06_stageClean.yml new file mode 100644 index 0000000000000000000000000000000000000000..f8b6dba94adccbefeda89205f59a704b7f6d3875 --- /dev/null +++ b/ExerciseChecker/.gitlab-ci/06_stageClean.yml @@ -0,0 +1,75 @@ +clean:release: + stage: clean + tags: + - gitlab_clean + image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest + script: + - !reference [ .get_version, script ] + - !reference [ .clean_release, script ] + rules: + - if: '$CI_COMMIT_REF_PROTECTED == "true"' + + +clean:packages: + stage: clean + tags: + - gitlab_clean + image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest + script: + - !reference [ .get_version, script ] + - !reference [ .clean_packages, script ] + rules: + - if: '$CI_COMMIT_REF_PROTECTED == "true"' + + +clean:dev:release: + stage: clean + tags: + - gitlab_clean + image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest + script: + - !reference [ .get_version, script ] + - VERSION="${VERSION}${VERSION_DEV_SUFFIX}" + - !reference [ .clean_release, script ] + rules: + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + + +clean:dev:packages: + stage: clean + tags: + - gitlab_clean + image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest + script: + - !reference [ .get_version, script ] + - VERSION="${VERSION}${VERSION_DEV_SUFFIX}" + - !reference [ .clean_packages, script ] + rules: + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + + +clean:dev:dockerhub: + stage: clean + tags: + - clean + - dind + image: docker:latest + services: + - docker:dind + script: + - !reference [ .init_dind_script, script ] + - VERSION="${VERSION}${VERSION_DEV_SUFFIX}" + + # Remove dev tag + ## Login to dockerhub (get JWT token) + - GET_TOKEN_DATA=$(jq --null-input --arg username "$DOCKER_REGISTRY_USER" --arg password "$DOCKER_REGISTRY_PASSWORD" '{"username":$username,"password":$password}') + - > + TOKEN=`curl --data "${GET_TOKEN_DATA}" \ + --header "Content-Type: application/json" \ + --header "JOB-TOKEN: $CI_JOB_TOKEN" \ + "https://hub.docker.com/v2/users/login/" | jq -r .token` + + ## Delete tag + - 'curl --header "Authorization: JWT ${TOKEN}" --request DELETE "https://hub.docker.com/v2/repositories/${DOCKER_REGISTRY_IMAGE}/tags/${VERSION}/"' + rules: + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' \ No newline at end of file diff --git a/ExerciseChecker/.gitlab-ci/07_stageUpload.yml b/ExerciseChecker/.gitlab-ci/07_stageUpload.yml new file mode 100644 index 0000000000000000000000000000000000000000..4c9bd456380ebbbbaa2e0498321c89e76427e16c --- /dev/null +++ b/ExerciseChecker/.gitlab-ci/07_stageUpload.yml @@ -0,0 +1,22 @@ +upload:packages:wiki: + stage: upload + tags: + - gitlab_package + image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest + script: + # Install dependencies + - apk update + - apk add xz + + - !reference [ .get_version, script ] + - !reference [ .get_packages_url, script ] + + # Create archive + - mkdir -p $ARTIFACTS_FOLDER + - WIKI_ARCHIVE_PATH="${ARTIFACTS_FOLDER}/${WIKI_ARCHIVE_NAME}" + - tar -v -c -C "${CI_PROJECT_DIR}/${WIKI_FOLDER}" -J -f "${WIKI_ARCHIVE_PATH}" . # Ubuntu: tar --verbose --create --cd wiki-test-2 --xz --file file.tar.bz2 + + # Send package + - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${WIKI_ARCHIVE_PATH} "${PACKAGE_URL_WIKI}";' + rules: + - if: '$CI_COMMIT_REF_PROTECTED == "true"' \ No newline at end of file diff --git a/ExerciseChecker/.gitlab-ci/08_stageRelease.yml b/ExerciseChecker/.gitlab-ci/08_stageRelease.yml new file mode 100644 index 0000000000000000000000000000000000000000..325d173cd88c4336e573d1dd33f6ffbe7745c653 --- /dev/null +++ b/ExerciseChecker/.gitlab-ci/08_stageRelease.yml @@ -0,0 +1,103 @@ +release:dockerhub:latest: + stage: release + tags: + - release + - dind + image: docker:latest + services: + - docker:dind + script: + - !reference [ .init_dind_script, script ] + + # Push latest tag + - docker buildx imagetools create $CONTAINER_IMAGE --tag $DOCKER_REGISTRY_IMAGE:latest + rules: + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + + +release:wiki: + stage: release + tags: + - release + image: alpine:latest + script: + - apk update + - apk add git + - apk add jq + + - !reference [ .get_version, script ] + + # Define URL for the wiki in terms of project-agnostic predefined variables + - WIKI_URL="${CI_SERVER_PROTOCOL}://project_${CI_PROJECT_ID}_bot:${GITLAB_PROJECT_ACCESS_TOKEN}@${CI_SERVER_HOST}:${CI_SERVER_PORT}/${CI_PROJECT_PATH}.wiki.git" + + # Clone this project's wiki under /tmp + - rm -rf "/tmp/${CI_PROJECT_NAME}.wiki" + - cd /tmp + - git clone "${WIKI_URL}" + + # Enter the cloned repo + - cd "${CI_PROJECT_NAME}.wiki" + + # Update the file + - mv .git/ ../ + - rm -rf ./* + - mv ../.git/ ./ + - cp "${CI_PROJECT_DIR}/.gitignore" . + - cp -R "${CI_PROJECT_DIR}/${WIKI_FOLDER}/." . + + # Set committer info + - git config user.name "$GITLAB_USER_NAME" + - git config user.email "$GITLAB_USER_EMAIL" + + # Commit the gitignore file + - git add ".gitignore" + - git commit -m "Add gitignore file" || true + + # Commit the file + - git add . + - git commit -m "${VERSION}" || true + + # Push the change back to the master branch of the wiki + - git push origin "HEAD:main" + rules: + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + + +release:gitlab: + stage: release + tags: + - release + image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest + script: + - !reference [ .get_version, script ] + - echo 'Running release_job' + - !reference [ .get_packages_url, script ] + + # Extract description from CHANGELOG.md + - CHANGELOG_LINE_START=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{print NR; exit;}' CHANGELOG.md` + - CHANGELOG_LINE_END=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{ count++; if(count>1) {print NR; exit;}}' CHANGELOG.md` + - DESCRIPTION=`awk 'NR > '$CHANGELOG_LINE_START' && NR < '$CHANGELOG_LINE_END'' CHANGELOG.md` + + # Create Release (can't be done by release_step of gitlab image because it don't have access to env var defined in script_step) + - > + RELEASE_DATA=$(jq --null-input --arg version "$VERSION" --arg description "# Changelog (version $VERSION) $DESCRIPTION" --arg tag_name "$VERSION" --arg ref "$CI_COMMIT_SHORT_SHA" '{ + "name": $version, + "description": $description, + "tag_name": $tag_name, + "ref": $ref, + "assets": { + "links": [ + { + "name": "Wiki", + "url": "'${PACKAGE_URL_WIKI}'", + } + ] + } + }') + - > + curl --data "${RELEASE_DATA}" \ + --header "Content-Type: application/json" \ + --header "JOB-TOKEN: $CI_JOB_TOKEN" \ + --request POST "${GITLAB_API_PROJECT_URL}/releases" + rules: + - if: '$CI_COMMIT_REF_PROTECTED == "true"' \ No newline at end of file diff --git a/ExerciseChecker/package-lock.json b/ExerciseChecker/package-lock.json index d23a2e213bdbd4d41e61a63691411928a7b7f6ea..9fbb82057e944f1175429e8f46a8abf09e272532 100644 --- a/ExerciseChecker/package-lock.json +++ b/ExerciseChecker/package-lock.json @@ -1,12 +1,13 @@ { "name": "dojo_exercise_checker", - "version": "2.1.0", + "version": "2.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dojo_exercise_checker", - "version": "2.1.0", + "version": "2.2.0", + "license": "AGPLv3", "dependencies": { "ajv": "^8.12.0", "axios": "^1.4.0", @@ -14,12 +15,14 @@ "chalk": "^4.1.2", "dockerode": "^3.3.5", "dotenv": "^16.3.1", + "dotenv-expand": "^10.0.0", "fs-extra": "^11.1.1", "http-status-codes": "^2.2.0", "json5": "^2.2.3", "ora": "^5.4.1", "tar-stream": "^3.1.6", - "winston": "^3.10.0" + "winston": "^3.10.0", + "yaml": "^2.3.2" }, "bin": { "dirmanager": "dist/app.js" @@ -31,6 +34,7 @@ "@types/node": "^18.17.1", "@types/tar-stream": "^2.2.2", "dotenv-vault": "^1.25.0", + "genversion": "^3.1.1", "pkg": "^5.8.1", "tiny-typed-emitter": "^2.1.0", "ts-node": "^10.9.1", @@ -61,9 +65,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", - "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -299,9 +303,9 @@ "dev": true }, "node_modules/@oclif/plugin-help": { - "version": "5.2.19", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-5.2.19.tgz", - "integrity": "sha512-gf6/dFtzMJ8RA4ovlBCBGJsZsd4jPXhYWJho+Gh6KmA+Ev9LupoExbE0qT+a2uHJyHEvIg4uX/MBW3qdERD/8g==", + "version": "5.2.20", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-5.2.20.tgz", + "integrity": "sha512-u+GXX/KAGL9S10LxAwNUaWdzbEBARJ92ogmM7g3gDVud2HioCmvWQCDohNRVZ9GYV9oKwZ/M8xwd6a1d95rEKQ==", "dev": true, "dependencies": { "@oclif/core": "^2.15.0" @@ -365,9 +369,9 @@ } }, "node_modules/@oclif/plugin-not-found": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-2.4.1.tgz", - "integrity": "sha512-LqW7qpw5Q8ploRiup2jEIMQJXcxHP1tpwj45GApKQMe7GRdGdRdjBT9Tu+U2tdEgMqgMplAIhOsYCx2nc2nMSw==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-2.4.3.tgz", + "integrity": "sha512-nIyaR4y692frwh7wIHZ3fb+2L6XEecQwRDIb4zbEam0TvaVmBQWZoColQyWA84ljFBPZ8XWiQyTz+ixSwdRkqg==", "dev": true, "dependencies": { "@oclif/core": "^2.15.0", @@ -433,9 +437,9 @@ } }, "node_modules/@oclif/plugin-update": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@oclif/plugin-update/-/plugin-update-3.2.3.tgz", - "integrity": "sha512-JVKwp4ysG9GU4RmG59MZYMunz8onRI+wEQzJThyYkUFd0VfZviYt2FHsyoNtxi30l0tInC8APgKp1pCCO4e+FQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@oclif/plugin-update/-/plugin-update-3.2.4.tgz", + "integrity": "sha512-41G7NTKND+yTpb8LHlvlMIcNoaEUIIJuEwju9igL+ME/pN/53opeXgFV2IjjeFiexXj50OfesY9OQ6lqOZHw+g==", "dev": true, "dependencies": { "@oclif/core": "^2.11.8", @@ -553,9 +557,9 @@ } }, "node_modules/@oclif/plugin-warn-if-update-available": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-2.1.0.tgz", - "integrity": "sha512-liTWd/qSIqALsikr88CAB9o2xGFt0LdT5REbhxtrx16/trRmkxQ+0RHK1FieGZAzEENx/4D3YcC/Y67a0uyO0g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-2.1.1.tgz", + "integrity": "sha512-y7eSzT6R5bmTIJbiMMXgOlbBpcWXGlVhNeQJBLBCCy1+90Wbjyqf6uvY0i2WcO4sh/THTJ20qCW80j3XUlgDTA==", "dev": true, "dependencies": { "@oclif/core": "^2.15.0", @@ -624,9 +628,9 @@ } }, "node_modules/@oclif/screen": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-3.0.6.tgz", - "integrity": "sha512-nEv7dFPxCrWrvK6dQ8zya0/Kb54EXVcwIKV9capjSa89ZDoOo+qH0YSo4/eQVECXgW3eUvgKLDIcIt62YBk0HA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-3.0.7.tgz", + "integrity": "sha512-jQBPHcMh5rcIPKdqA6xlzioLOmkaVnjg2MVyjMzBKV8hDhLWNSiZqx7NAWXpP70v2LFvGdVoV8BSbK9iID3eHg==", "dev": true, "engines": { "node": ">=12.0.0" @@ -657,18 +661,18 @@ "dev": true }, "node_modules/@types/cli-progress": { - "version": "3.11.2", - "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.2.tgz", - "integrity": "sha512-Yt/8rEJalfa9ve2SbfQnwFHrc9QF52JIZYHW3FDaTMpkCvnns26ueKiPHDxyJ0CS//IqjMINTx7R5Xa7k7uFHQ==", + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.3.tgz", + "integrity": "sha512-/+C9xAdVtc+g5yHHkGBThgAA8rYpi5B+2ve3wLtybYj0JHEBs57ivR4x/zGfSsplRnV+psE91Nfin1soNKqz5Q==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/docker-modem": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.3.tgz", - "integrity": "sha512-i1A2Etnav7uHizZ87vUf4EqwJehY3JOcTfBS0pGBlO+HQ0jg2lUMCaJRg9VQM8ldZkpYdIfsenxcTOCpwxPXEg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.4.tgz", + "integrity": "sha512-INK4TOrJ9hbgaSqHA1HaEOCcYVftJRH0v03gCg6R57JGKgltkDvdFYBtoN4lHrJ3h8aF1upvEPN2eWVLIvKStQ==", "dev": true, "dependencies": { "@types/node": "*", @@ -676,9 +680,9 @@ } }, "node_modules/@types/dockerode": { - "version": "3.3.19", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.19.tgz", - "integrity": "sha512-7CC5yIpQi+bHXwDK43b/deYXteP3Lem9gdocVVHJPSRJJLMfbiOchQV3rDmAPkMw+n3GIVj7m1six3JW+VcwwA==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.20.tgz", + "integrity": "sha512-Q+1e3z6SPWXR/Sk+WIyJFVsSDg78S7MDaGcwAh1WKlveO1tVO8TF1rOzJir5GLnqzEdUbclFKlw/4rhwESxwPw==", "dev": true, "dependencies": { "@types/docker-modem": "*", @@ -686,9 +690,9 @@ } }, "node_modules/@types/fs-extra": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.1.tgz", - "integrity": "sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.2.tgz", + "integrity": "sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ==", "dev": true, "dependencies": { "@types/jsonfile": "*", @@ -696,30 +700,30 @@ } }, "node_modules/@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.6.tgz", + "integrity": "sha512-ACTuifTSIIbyksx2HTon3aFtCKWcID7/h3XEmRpDYdMCXxPbl+m9GteOJeaAkiAta/NJaSFuA7ahZ0NkwajDSw==", "dev": true }, "node_modules/@types/jsonfile": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.1.tgz", - "integrity": "sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.2.tgz", + "integrity": "sha512-8t92P+oeW4d/CRQfJaSqEwXujrhH4OEeHRjGU3v1Q8mUS8GPF3yiX26sw4svv6faL2HfBtGTe2xWIoVgN3dy9w==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/node": { - "version": "18.17.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.15.tgz", - "integrity": "sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==", + "version": "18.18.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.3.tgz", + "integrity": "sha512-0OVfGupTl3NBFr8+iXpfZ8NR7jfFO+P1Q+IO/q0wbo02wYkP5gy36phojeYWpLQ6WAMjl+VfmqUk2YbUfp0irA==", "dev": true }, "node_modules/@types/ssh2": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.11.13.tgz", - "integrity": "sha512-08WbG68HvQ2YVi74n2iSUnYHYpUdFc/s2IsI0BHBdJwaqYJpWlVv9elL0tYShTv60yr0ObdxJR5NrCRiGJ/0CQ==", + "version": "1.11.14", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.11.14.tgz", + "integrity": "sha512-O/U38mvV4jVVrdtZz8KpmitkmeD/PUDeDNNueQhm34166dmaqb1iZ3sfarSxBArM2/iX4PZVJY3EOta0Zks9hw==", "dev": true, "dependencies": { "@types/node": "^18.11.18" @@ -911,9 +915,9 @@ } }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -1152,9 +1156,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", + "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", "engines": { "node": ">=6" }, @@ -1257,6 +1261,15 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1439,6 +1452,14 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "engines": { + "node": ">=12" + } + }, "node_modules/dotenv-vault": { "version": "1.25.0", "resolved": "https://registry.npmjs.org/dotenv-vault/-/dotenv-vault-1.25.0.tgz", @@ -1513,12 +1534,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/error-ex/node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1709,15 +1724,24 @@ "node": ">=8" } }, + "node_modules/find-package": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-package/-/find-package-1.0.0.tgz", + "integrity": "sha512-yVn71XCCaNgxz58ERTl8nA/8YYtIQDY9mHSrgFBfiFtdNNfY0h183Vh8BRkKxD8x9TUw3ec290uJKhDVxqGZBw==", + "dev": true, + "dependencies": { + "parents": "^1.0.1" + } + }, "node_modules/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "funding": [ { "type": "individual", @@ -1804,11 +1828,21 @@ "node": ">=14.14" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "node_modules/genversion": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/genversion/-/genversion-3.1.1.tgz", + "integrity": "sha512-/H861PMsihhjgX2qqhTN8egM11V04imhA+3JRFY3jjPua2Sy1NqaqqQPjSP8rdM9jZoKpFhVj9g3Fs9XPCjBYQ==", + "dev": true, + "dependencies": { + "commander": "^7.2.0", + "find-package": "^1.0.0" + }, + "bin": { + "genversion": "bin/genversion.js" + }, + "engines": { + "node": ">=10.0.0" + } }, "node_modules/get-caller-file": { "version": "2.0.5", @@ -1872,13 +1906,10 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, "engines": { "node": ">= 0.4.0" } @@ -1909,9 +1940,9 @@ } }, "node_modules/http-status-codes": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.2.0.tgz", - "integrity": "sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==" }, "node_modules/https-proxy-agent": { "version": "5.0.1", @@ -2052,9 +2083,10 @@ } }, "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "node_modules/is-core-module": { "version": "2.9.0", @@ -2614,6 +2646,15 @@ "node": ">=8" } }, + "node_modules/parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha512-mXKF3xkoUt5td2DoxpLmtOmZvko9VfFpwRwkKDHSNvgmpLAeBo18YDhcPbBzJq+QLCHMbGOfzia2cX4U+0v9Mg==", + "dev": true, + "dependencies": { + "path-platform": "~0.11.15" + } + }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -2652,6 +2693,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha512-Y30dB6rab1A/nfEKsZxmr01nUotHX0c/ZiIAsCTatEe1CmS5Pm5He7fZ195bPT7RdquoaL8lLxFCMQi/bS7IJg==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -2926,9 +2976,9 @@ } }, "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", @@ -3143,6 +3193,11 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -3673,6 +3728,14 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yaml": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", + "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/ExerciseChecker/package.json b/ExerciseChecker/package.json index 0a3e7d1c606ac714ee1f3d3837183e52b1a56a8a..4553b1e9794ce1707fb2e2fadf0702abe61fcfbb 100644 --- a/ExerciseChecker/package.json +++ b/ExerciseChecker/package.json @@ -1,6 +1,9 @@ { "name" : "dojo_exercise_checker", - "version" : "2.1.0", + "description" : "App that check an exercise of the Dojo project", + "version" : "2.2.0", + "license" : "AGPLv3", + "author" : "Michaël Minelli <dojo@minelli.me>", "main" : "dist/app.js", "bin" : { "dirmanager": "./dist/app.js" @@ -18,9 +21,11 @@ ] }, "scripts" : { - "build" : "npx tsc", - "start:dev": "npx ts-node src/app.ts", - "test" : "echo \"Error: no test specified\" && exit 1" + "dotenv:build": "npx dotenv-vault local build", + "genversion" : "npx genversion -s -e src/config/Version.ts", + "build" : "npm run genversion; npx tsc", + "start:dev" : "npm run genversion; npx ts-node src/app.ts", + "test" : "echo \"Error: no test specified\" && exit 1" }, "dependencies" : { "ajv" : "^8.12.0", @@ -29,12 +34,14 @@ "chalk" : "^4.1.2", "dockerode" : "^3.3.5", "dotenv" : "^16.3.1", + "dotenv-expand" : "^10.0.0", "fs-extra" : "^11.1.1", "http-status-codes": "^2.2.0", "json5" : "^2.2.3", "ora" : "^5.4.1", "tar-stream" : "^3.1.6", - "winston" : "^3.10.0" + "winston" : "^3.10.0", + "yaml" : "^2.3.2" }, "devDependencies": { "@types/dockerode" : "^3.3.19", @@ -43,6 +50,7 @@ "@types/node" : "^18.17.1", "@types/tar-stream" : "^2.2.2", "dotenv-vault" : "^1.25.0", + "genversion" : "^3.1.1", "pkg" : "^5.8.1", "tiny-typed-emitter": "^2.1.0", "ts-node" : "^10.9.1", diff --git a/ExerciseChecker/src/app.ts b/ExerciseChecker/src/app.ts index 3d1df1d5211e9a544a1e773a05fab76fb0687e1f..63eef070ae3635dcf2c0245dd9e39337469df870 100644 --- a/ExerciseChecker/src/app.ts +++ b/ExerciseChecker/src/app.ts @@ -1,29 +1,30 @@ // Read from the .env file // ATTENTION : This lines MUST be the first of this file (except for the path import) const path = require('node:path'); -require('dotenv').config({ - path : path.join(__dirname, '../.env'), - DOTENV_KEY: 'dotenv://:key_bebfddf18e3dd9a0bafafe0e383313f75add1da6fbe41ea5fde51f37ef1776aa@dotenv.local/vault/.env.vault?environment=development' - }); +const myEnv = require('dotenv').config({ + path : path.join(__dirname, '../.env'), + DOTENV_KEY: 'dotenv://:key_bebfddf18e3dd9a0bafafe0e383313f75add1da6fbe41ea5fde51f37ef1776aa@dotenv.local/vault/.env.vault?environment=development' + }); +require('dotenv-expand').expand(myEnv); require('./shared/helpers/TypeScriptExtensions'); // ATTENTION : This line MUST be the second of this file -import ClientsSharedConfig from './sharedByClients/config/ClientsSharedConfig'; -import Styles from './types/Style'; -import Icon from './sharedByClients/types/Icon'; -import RecursiveFilesStats from './shared/helpers/recursiveFilesStats/RecursiveFilesStats'; -import Toolbox from './shared/helpers/Toolbox'; -import ExerciseCheckerError from './shared/types/Dojo/ExerciseCheckerError'; -import { exec } from 'child_process'; -import util from 'util'; -import fs from 'fs-extra'; -import HttpManager from './managers/HttpManager'; -import DojoBackendManager from './managers/DojoBackendManager'; -import Config from './config/Config'; -import ArchiveHelper from './shared/helpers/ArchiveHelper'; -import ExerciseDockerCompose from './sharedByClients/helpers/Dojo/ExerciseDockerCompose'; -import ExerciseResultsValidation from './sharedByClients/helpers/Dojo/ExerciseResultsValidation'; -import ExerciseAssignment from './sharedByClients/models/ExerciseAssignment'; -import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsSharedExerciseHelper'; +import ClientsSharedConfig from './sharedByClients/config/ClientsSharedConfig'; +import Styles from './types/Style'; +import Icon from './sharedByClients/types/Icon'; +import RecursiveFilesStats from './shared/helpers/recursiveFilesStats/RecursiveFilesStats'; +import Toolbox from './shared/helpers/Toolbox'; +import ExerciseCheckerError from './shared/types/Dojo/ExerciseCheckerError'; +import { exec } from 'child_process'; +import util from 'util'; +import fs from 'fs-extra'; +import HttpManager from './managers/HttpManager'; +import DojoBackendManager from './managers/DojoBackendManager'; +import Config from './config/Config'; +import ArchiveHelper from './shared/helpers/ArchiveHelper'; +import ExerciseDockerCompose from './sharedByClients/helpers/Dojo/ExerciseDockerCompose'; +import ExerciseResultsSanitizerAndValidator from './sharedByClients/helpers/Dojo/ExerciseResultsSanitizerAndValidator'; +import ExerciseAssignment from './sharedByClients/models/ExerciseAssignment'; +import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsSharedExerciseHelper'; (async () => { @@ -35,7 +36,10 @@ import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsS let exerciseAssignment: ExerciseAssignment | undefined; let exerciseDockerCompose: ExerciseDockerCompose; - let exerciseResultsValidation: ExerciseResultsValidation; + let exerciseResultsValidation: ExerciseResultsSanitizerAndValidator; + + let haveResultsVolume: boolean; + /* //////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 1: @@ -55,6 +59,8 @@ import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsS fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, immutableFile.content, { encoding: 'base64' }); }); + + haveResultsVolume = exerciseAssignment.assignmentFile.result.volume !== undefined; } @@ -65,12 +71,16 @@ import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsS - Get logs from linked services */ { + let composeFileOverride: string[] = []; const composeOverridePath: string = path.join(Config.folders.project, 'docker-compose-override.yml'); + if ( haveResultsVolume ) { + const composeOverride = fs.readFileSync(path.join(__dirname, '../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', exerciseAssignment.assignmentFile.result.volume!).replace('{{MOUNT_PATH}}', Config.folders.resultsExercise); + fs.writeFileSync(composeOverridePath, composeOverride); - const composeOverride = fs.readFileSync(path.join(__dirname, '../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', exerciseAssignment.assignmentFile.result.volume).replace('{{MOUNT_PATH}}', Config.folders.resultsExercise); - fs.writeFileSync(composeOverridePath, composeOverride); + composeFileOverride = [ composeOverridePath ]; + } - exerciseDockerCompose = new ExerciseDockerCompose(ClientsSharedConfig.dockerCompose.projectName, exerciseAssignment.assignmentFile, Config.folders.project, [ composeOverridePath ]); + exerciseDockerCompose = new ExerciseDockerCompose(ClientsSharedConfig.dockerCompose.projectName, exerciseAssignment.assignmentFile, Config.folders.project, composeFileOverride); try { await new Promise<void>((resolve, reject) => { @@ -92,7 +102,7 @@ import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsS }); } catch ( error ) { } - fs.rmSync(composeOverridePath); + fs.rmSync(composeOverridePath, { force: true }); fs.writeFileSync(path.join(Config.folders.resultsDojo, 'dockerComposeLogs.txt'), exerciseDockerCompose.allLogs); if ( !exerciseDockerCompose.success ) { @@ -104,7 +114,7 @@ import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsS //////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 3: Check content requirements and content size { - exerciseResultsValidation = new ExerciseResultsValidation(Config.folders.resultsDojo, Config.folders.resultsExercise); + exerciseResultsValidation = new ExerciseResultsSanitizerAndValidator(Config.folders.resultsDojo, Config.folders.resultsExercise, exerciseDockerCompose.exitCode); try { await new Promise<void>((resolve) => { @@ -146,7 +156,7 @@ import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsS liteStats : true }); - await DojoBackendManager.sendResults(exerciseDockerCompose.exitCode, commit, exerciseResultsValidation.exerciseResults!, files, await ArchiveHelper.getBase64(Config.folders.resultsVolume)); + await DojoBackendManager.sendResults(exerciseDockerCompose.exitCode, commit, exerciseResultsValidation.exerciseResults, files, await ArchiveHelper.getBase64(Config.folders.resultsVolume)); } catch ( error ) { console.error(Styles.ERROR(`${ Icon.ERROR } Error while uploading the results`)); console.error(JSON.stringify(error)); @@ -161,7 +171,7 @@ import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsS - Exit with container exit code */ { - ClientsSharedExerciseHelper.displayExecutionResults(exerciseResultsValidation.exerciseResults!, exerciseDockerCompose.exitCode, Styles, `\n\n${ Icon.INFO }️ More detailed logs and resources may be available in artifacts`); + ClientsSharedExerciseHelper.displayExecutionResults(exerciseResultsValidation.exerciseResults, exerciseDockerCompose.exitCode, Styles, `\n\n${ Icon.INFO }️ More detailed logs and resources may be available in artifacts`); process.exit(exerciseDockerCompose.exitCode); } diff --git a/ExerciseChecker/src/config/Config.ts b/ExerciseChecker/src/config/Config.ts index 9072ff8f963c7dd4cd392cd07ff6e3397caa6ce8..42c446229456f1c92c316a4e7b27b54779f73c7a 100644 --- a/ExerciseChecker/src/config/Config.ts +++ b/ExerciseChecker/src/config/Config.ts @@ -13,6 +13,12 @@ class Config { id: string; secret: string; }; + public readonly dockerhub: { + repositories: { + exerciseChecker: string + } + }; + constructor() { this.appName = process.env.APP_NAME || ''; @@ -28,6 +34,12 @@ class Config { id : process.env.DOJO_EXERCISE_ID || '', secret: process.env.DOJO_SECRET || '' }; + + this.dockerhub = { + repositories: { + exerciseChecker: process.env.DOCKERHUB_EXERCISE_CHECKER_REPOSITORY || '' + } + }; } private resetResultsVolume(): void { diff --git a/ExerciseChecker/src/managers/HttpManager.ts b/ExerciseChecker/src/managers/HttpManager.ts index fb113963ab8d37459ff72bf05d33827598889edd..54d6990ad2c31f3a0307a56b13f305b0285ee0db 100644 --- a/ExerciseChecker/src/managers/HttpManager.ts +++ b/ExerciseChecker/src/managers/HttpManager.ts @@ -2,6 +2,11 @@ import axios, { AxiosRequestHeaders } from 'axios'; import FormData from 'form-data'; import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; import Config from '../config/Config'; +import { version } from '../config/Version'; +import boxen from 'boxen'; +import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode'; +import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse'; +import { StatusCodes } from 'http-status-codes'; class HttpManager { @@ -12,6 +17,19 @@ class HttpManager { this.registerResponseInterceptor(); } + private requestError(message: string) { + console.log(boxen(message, { + title : 'Request error', + titleAlignment: 'center', + borderColor : 'red', + borderStyle : 'bold', + margin : 1, + padding : 1, + textAlignment : 'left' + })); + process.exit(1); + } + private registerRequestInterceptor() { axios.interceptors.request.use((config) => { if ( config.data instanceof FormData ) { @@ -26,6 +44,9 @@ class HttpManager { } config.headers.Authorization = `ExerciseSecret ${ Config.exercise.secret }`; + + config.headers['client'] = 'DojoExerciseChecker'; + config.headers['client-version'] = version; } return config; @@ -36,6 +57,25 @@ class HttpManager { axios.interceptors.response.use((response) => { return response; }, (error) => { + if ( error.response ) { + if ( error.response.status === StatusCodes.METHOD_NOT_ALLOWED && error.response.data ) { + const data: DojoBackendResponse<{}> = error.response.data; + + switch ( data.code ) { + case DojoStatusCode.CLIENT_NOT_SUPPORTED: + this.requestError('Client not recognized by the server. Please contact the administrator.'); + break; + case DojoStatusCode.CLIENT_VERSION_NOT_SUPPORTED: + this.requestError(`ExerciseChecker version not supported by the server.\nPlease check that the CI/CD pipeline use the "${ Config.dockerhub.repositories.exerciseChecker }:latest" image.\nIf yes, try again later and if the problem persists, please contact the administrator.`); + break; + default: + break; + } + } + } else { + this.requestError('Error connecting to the server. Please check your internet connection. If the problem persists, please contact the administrator.'); + } + return Promise.reject(error); }); } diff --git a/ExerciseChecker/src/shared b/ExerciseChecker/src/shared index 8d7e3ca0cca10e874ac48e19e47da8a1491ccba7..efe1bf313f57d1826faf935c183d37a0835f8c2d 160000 --- a/ExerciseChecker/src/shared +++ b/ExerciseChecker/src/shared @@ -1 +1 @@ -Subproject commit 8d7e3ca0cca10e874ac48e19e47da8a1491ccba7 +Subproject commit efe1bf313f57d1826faf935c183d37a0835f8c2d diff --git a/ExerciseChecker/src/sharedByClients b/ExerciseChecker/src/sharedByClients index 4ff3846e9415a6122b0b966be089eec3f0117f4f..d9379b055a4626e4b35cf4cc4a7429040a4aeaf7 160000 --- a/ExerciseChecker/src/sharedByClients +++ b/ExerciseChecker/src/sharedByClients @@ -1 +1 @@ -Subproject commit 4ff3846e9415a6122b0b966be089eec3f0117f4f +Subproject commit d9379b055a4626e4b35cf4cc4a7429040a4aeaf7