Skip to content
Snippets Groups Projects
Commit 32c4c4ae authored by michael.minelli's avatar michael.minelli
Browse files

Merge branch 'v2.0.0' into main

parents 4ec83752 e6a31edc
Branches
Tags
No related merge requests found
Pipeline #26318 passed
Showing
with 1302 additions and 192 deletions
variables: variables:
BIN_FOLDER: /binary GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_FORCE_HTTPS: "true"
SECURE_FILES_DOWNLOAD_PATH: './'
BIN_NAME: dojo BIN_NAME: dojo
.macOS-sign_and_pkg: MACOS_PKG_EXTENSION: '.pkg'
MACOS_PKG_BIN_NAME: "${BIN_NAME}${MACOS_PKG_EXTENSION}"
ARTIFACTS_FOLDER: /artifacts
BIN_FOLDER_MACOS: $ARTIFACTS_FOLDER/macOS
BIN_FOLDER_LINUX: $ARTIFACTS_FOLDER/Linux
BIN_FOLDER_WINDOWS: $ARTIFACTS_FOLDER/Windows
BIN_FOLDER_MACOS_ARM64: $BIN_FOLDER_MACOS/arm64
BIN_FOLDER_MACOS_X64: $BIN_FOLDER_MACOS/x64
BIN_FILE_MACOS_ARM64: $BIN_FOLDER_MACOS_ARM64/$BIN_NAME
BIN_FILE_MACOS_X64: $BIN_FOLDER_MACOS_X64/$BIN_NAME
BIN_PKG_FILE_MACOS_ARM64: $BIN_FOLDER_MACOS_ARM64/$MACOS_PKG_BIN_NAME
BIN_PKG_FILE_MACOS_X64: $BIN_FOLDER_MACOS_X64/$MACOS_PKG_BIN_NAME
BIN_FILE_LINUX_ARM64: $BIN_FOLDER_LINUX/arm64/$BIN_NAME
BIN_FILE_LINUX_X64: $BIN_FOLDER_LINUX/x64/$BIN_NAME
BIN_FILE_WINDOWS_ARM64: $BIN_FOLDER_WINDOWS/arm64/$BIN_NAME.exe
BIN_FILE_WINDOWS_X64: $BIN_FOLDER_WINDOWS/x64/$BIN_NAME.exe
VERSION_FILE: $ARTIFACTS_FOLDER/VERSION
VERSION_DEV_SUFFIX: '-dev'
PROJECT_FOLDER: NodeApp
PACKAGE_REGISTRY_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${BIN_NAME}"
.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:
- PACKAGE_URL_MACOS_ARM64_BIN="${PACKAGE_REGISTRY_URL}_macOS-arm64/${VERSION}/${BIN_NAME}"
- PACKAGE_URL_MACOS_X64_BIN="${PACKAGE_REGISTRY_URL}_macOS-x64/${VERSION}/${BIN_NAME}"
- PACKAGE_URL_MACOS_ARM64_PKG="${PACKAGE_REGISTRY_URL}_macOS-pkg-arm64/${VERSION}/${MACOS_PKG_BIN_NAME}"
- PACKAGE_URL_MACOS_X64_PKG="${PACKAGE_REGISTRY_URL}_macOS-pkg-x64/${VERSION}/${MACOS_PKG_BIN_NAME}"
- PACKAGE_URL_LINUX_ARM64_BIN="${PACKAGE_REGISTRY_URL}_Linux-arm64/${VERSION}/${BIN_NAME}"
- PACKAGE_URL_LINUX_X64_BIN="${PACKAGE_REGISTRY_URL}_Linux-x64/${VERSION}/${BIN_NAME}"
- PACKAGE_URL_WINDOWS_ARM64_BIN="${PACKAGE_REGISTRY_URL}_Windows-arm64/${VERSION}/${BIN_NAME}"
- PACKAGE_URL_WINDOWS_X64_BIN="${PACKAGE_REGISTRY_URL}_Windows-x64/${VERSION}/${BIN_NAME}"
.build_cli:
script:
- cd NodeApp
# Download secure files
- curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
- mv env_$VERSION .env
# Install dependencies
- npm install
- npm run build
# Build binaries
## macOS
- npx pkg . -t node18-macos-arm64 --output $BIN_FILE_MACOS_ARM64 --no-bytecode --public-packages "*" --public
- npx pkg . -t node18-macos-x64 --output $BIN_FILE_MACOS_X64 --no-bytecode --public-packages "*" --public
## Linux
- npx pkg . -t node18-linuxstatic-arm64 --output $BIN_FILE_LINUX_ARM64 --no-bytecode --public-packages "*" --public
- npx pkg . -t node18-linuxstatic-x64 --output $BIN_FILE_LINUX_X64 --no-bytecode --public-packages "*" --public
## Windows
- npx pkg . -t node18-win-arm64 --output $BIN_FILE_WINDOWS_ARM64 --no-bytecode --public-packages "*" --public
- npx pkg . -t node18-win-x64 --output $BIN_FILE_WINDOWS_X64 --no-bytecode --public-packages "*" --public
.sign_macos:
script: script:
# Sign excecutable # Sign excecutable
- codesign --force --options=runtime --entitlements ../../../Resources/macApp/Signing/entitlements.plist --sign $SIGN_DEV_ID_APP --keychain $SIGN_LOGIN_KEYCHAIN_PATH --timestamp ${BIN_NAME} - codesign --force --options=runtime --entitlements ../../../Resources/macApp/Signing/entitlements.plist --sign $SIGN_DEV_ID_APP --keychain $SIGN_LOGIN_KEYCHAIN_PATH --timestamp ${BIN_NAME}
# Package and notarize the app # Package and notarize the app
- xcrun notarytool store-credentials --apple-id $SIGN_APPLE_ID --team-id $SIGN_TEAM_ID --password $SIGN_APP_PASSWORD --keychain $SIGN_LOGIN_KEYCHAIN_PATH $SIGN_KEYCHAIN_PROFILE - xcrun notarytool store-credentials --apple-id $SIGN_APPLE_ID --team-id $SIGN_TEAM_ID --password $SIGN_APP_PASSWORD --keychain $SIGN_LOGIN_KEYCHAIN_PATH $SIGN_KEYCHAIN_PROFILE
- ditto ${BIN_NAME} ${BIN_NAME}_pkg/usr/local/bin/ - ditto ${BIN_NAME} ${BIN_NAME}_pkg/usr/local/bin/
- productbuild --identifier $SIGN_IDENTIFIER --version $APP_VERSION --sign $SIGN_DEV_ID_INST --keychain $SIGN_LOGIN_KEYCHAIN_PATH --timestamp --root ${BIN_NAME}_pkg / ${BIN_NAME}.pkg - productbuild --identifier $SIGN_IDENTIFIER --version $VERSION --sign $SIGN_DEV_ID_INST --keychain $SIGN_LOGIN_KEYCHAIN_PATH --timestamp --root ${BIN_NAME}_pkg / ${MACOS_PKG_BIN_NAME}
- xcrun notarytool submit ${BIN_NAME}.pkg --keychain $SIGN_LOGIN_KEYCHAIN_PATH --keychain-profile $SIGN_KEYCHAIN_PROFILE --wait - xcrun notarytool submit ${BIN_NAME}.pkg --keychain $SIGN_LOGIN_KEYCHAIN_PATH --keychain-profile $SIGN_KEYCHAIN_PROFILE --wait
- xcrun stapler staple ${BIN_NAME}.pkg - xcrun stapler staple ${BIN_NAME}.pkg
#Clean folder #Clean folder
- rm -Rf ${BIN_NAME}_pkg - rm -Rf ${BIN_NAME}_pkg
.clean_release:
script:
# Delete release if it already exists
- 'curl --request DELETE --header "JOB-TOKEN: $CI_JOB_TOKEN" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/releases/${VERSION}"'
image: node:latest .clean_packages:
script:
# Get all packages of the project
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/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
cache:
paths:
- NodeApp/node_modules/
stages: stages:
- Build_stage - build
- Sign_stage - sign
- clean-same-release
- clean-dev-release
- upload
- release
build: build:test:
stage: Build_stage stage: build
tags: tags:
- build - build
variables: image: node:latest
GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_FORCE_HTTPS: "true"
SECURE_FILES_DOWNLOAD_PATH: './'
script: script:
- cd NodeApp - apt update
# Download secure files - apt install -y jq
- curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash - !reference [.get_version, script]
- mv env .env - !reference [.build_cli, script]
# Install dependencies rules:
- npm install - if: '$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_COMMIT_BRANCH !~ /^v[0-9]+(\.[0-9]+)*$/'
- npm run build
# Build binaries build:version:
- npx pkg . -t node18-macos-arm64 --output $BIN_FOLDER/macOS/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public stage: build
- npx pkg . -t node18-macos-x64 --output $BIN_FOLDER/macOS/x64/$BIN_NAME --no-bytecode --public-packages "*" --public tags:
- npx pkg . -t node18-linuxstatic-arm64 --output $BIN_FOLDER/linux/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public - build
- npx pkg . -t node18-linuxstatic-x64 --output $BIN_FOLDER/linux/x64/$BIN_NAME --no-bytecode --public-packages "*" --public image: node:latest
- npx pkg . -t node18-win-arm64 --output $BIN_FOLDER/Windows/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public script:
- npx pkg . -t node18-win-x64 --output $BIN_FOLDER/Windows/x64/$BIN_NAME --no-bytecode --public-packages "*" --public - apt update
- apt install -y jq
- !reference [.get_version, script]
- mkdir -p $ARTIFACTS_FOLDER
- echo $VERSION > $VERSION_FILE
- !reference [.build_cli, script]
artifacts: artifacts:
untracked: true untracked: true
paths: paths:
- $BIN_FOLDER/* - $ARTIFACTS_FOLDER/*
expire_in: 1 hour expire_in: 1 hour
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
sign_macos: sign:macos:
stage: Sign_stage stage: sign
tags: tags:
- macos_signing - macos_signing
dependencies: dependencies:
- build - build:version
script: script:
- VERSION=$(cat $VERSION_FILE)
- security unlock-keychain -p $SIGN_KEYCHAIN_PASSWORD $SIGN_LOGIN_KEYCHAIN_PATH - security unlock-keychain -p $SIGN_KEYCHAIN_PASSWORD $SIGN_LOGIN_KEYCHAIN_PATH
- cd $BIN_FOLDER/macOS/arm64 - cd $BIN_FOLDER_MACOS_ARM64
- !reference [.macOS-sign_and_pkg, script] - !reference [.sign_macos, script]
- cd $BIN_FOLDER/macOS/x64 - cd $BIN_FOLDER_MACOS_X64
- !reference [.macOS-sign_and_pkg, script] - !reference [.sign_macos, script]
artifacts: artifacts:
paths: paths:
- $BIN_FOLDER/* - $ARTIFACTS_FOLDER/*
expire_in: 1 week expire_in: 1 day
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
clean:release:
stage: clean-same-release
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_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
clean:packages:
stage: clean-same-release
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_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
clean-dev:release:
stage: clean-dev-release
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-dev-release
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'
upload:packages:
stage: upload
tags:
- gitlab_package
dependencies:
- sign:macos
image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
script:
- !reference [.get_version, script]
- !reference [.get_packages_url, script]
#macOS
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${BIN_FILE_MACOS_ARM64} "${PACKAGE_URL_MACOS_ARM64_BIN}";'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${BIN_FILE_MACOS_X64} "${PACKAGE_URL_MACOS_X64_BIN}";'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${BIN_PKG_FILE_MACOS_ARM64} "${PACKAGE_URL_MACOS_ARM64_PKG}";'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${BIN_PKG_FILE_MACOS_X64} "${PACKAGE_URL_MACOS_X64_PKG}";'
#Linux
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${BIN_FILE_LINUX_ARM64} "${PACKAGE_URL_LINUX_ARM64_BIN}";'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${BIN_FILE_LINUX_X64} "${PACKAGE_URL_LINUX_X64_BIN}";'
#Windows
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${BIN_FILE_WINDOWS_ARM64} "${PACKAGE_URL_WINDOWS_ARM64_BIN}";'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${BIN_FILE_WINDOWS_X64} "${PACKAGE_URL_WINDOWS_X64_BIN}";'
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
release_job:
stage: release
tags:
- release
image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
script:
- !reference [.get_version, script]
- !reference [.get_packages_url, script]
- echo 'Running release_job'
- 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`
- >
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": "Windows (ARM64) binary",
"url": "'${PACKAGE_URL_WINDOWS_ARM64_BIN}'",
},{
"name": "Windows (x64) binary",
"url": "'${PACKAGE_URL_WINDOWS_X64_BIN}'",
},{
"name": "Linux (ARM64) binary",
"url": "'${PACKAGE_URL_LINUX_ARM64_BIN}'",
},{
"name": "Linux (x64) binary",
"url": "'${PACKAGE_URL_LINUX_X64_BIN}'",
},{
"name": "macOS (Intel) binary",
"url": "'${PACKAGE_URL_MACOS_X64_BIN}'",
},{
"name": "macOS (Apple Silicon) binary",
"url": "'${PACKAGE_URL_MACOS_ARM64_BIN}'",
},{
"name": "macOS (Intel) package",
"url": "'${PACKAGE_URL_MACOS_X64_PKG}'",
},{
"name": "macOS (Apple Silicon) package",
"url": "'${PACKAGE_URL_MACOS_ARM64_PKG}'",
}
]
}
}')
- >
curl --data "${RELEASE_DATA}" \
--header "Content-Type: application/json" \
--header "JOB-TOKEN: $CI_JOB_TOKEN" \
--request POST "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/releases"
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
\ No newline at end of file
# Changelog
<!--
### ✨ Feature
### 🤏 Minor change
### 🎨 Interface
### 🐛 Bugfix
### 🔒 Security
### 🚀‍️ CI / CD
### 🔨 Internal / Developers
### 📚 Documentation
**💥 Breaking:**
**⚠️ Deprecation:**
-->
## 2.0.0 (?)
### ✨ Feature
- Added license: AGPLv3
### 🎨 Interface
- **💥 Breaking:** Renamed `enonce` to `assignment`
- **💥 Breaking:** Renamed `exercice` to `exercise`
### 🔨 Internal / Developers
- Auto release by pipeline
- For vX.Y.Z branch create release and tag with `-dev` suffix
- For main branch create definitive release and remove `-dev` releases
## 1.0.1 (2023-08-12)
### ✨ Feature
- 🎉 Initial release of the project
\ No newline at end of file
LICENSE 0 → 100644
This diff is collapsed.
Subproject commit 57997f6ff4ad2d2e23e03f86d997f64463cc898d Subproject commit 4d703a2dd39ec0c2b71bbbbda8900588c4e360bd
{ {
"name" : "dojo_cli", "name" : "dojo_cli",
"version" : "1.0.1", "version" : "2.0.0",
"main" : "dist/app.js", "main" : "dist/app.js",
"bin" : { "bin" : {
"dojo": "./dist/app.js" "dojo": "./dist/app.js"
......
import { Command } from 'commander'; import { Command } from 'commander';
import EnonceCommand from './enonce/EnonceCommand';
import SessionCommand from './session/SessionCommand'; import SessionCommand from './session/SessionCommand';
import ExerciceCommand from './exercice/ExerciceCommand';
import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig';
import AssignmentCommand from './assignment/AssignmentCommand';
import ExerciseCommand from './exercise/ExerciseCommand';
class CommanderApp { class CommanderApp {
...@@ -12,7 +12,7 @@ class CommanderApp { ...@@ -12,7 +12,7 @@ class CommanderApp {
this.program this.program
.name('dojo') .name('dojo')
.description('CLI of the Dojo application') .description('CLI of the Dojo application')
.version('1.0.1') .version('2.0.0')
.showHelpAfterError() .showHelpAfterError()
.configureHelp({ .configureHelp({
showGlobalOptions: true, showGlobalOptions: true,
...@@ -32,8 +32,8 @@ class CommanderApp { ...@@ -32,8 +32,8 @@ class CommanderApp {
private registerCommands() { private registerCommands() {
SessionCommand.registerOnCommand(this.program); SessionCommand.registerOnCommand(this.program);
EnonceCommand.registerOnCommand(this.program); AssignmentCommand.registerOnCommand(this.program);
ExerciceCommand.registerOnCommand(this.program); ExerciseCommand.registerOnCommand(this.program);
} }
} }
......
import CommanderCommand from '../CommanderCommand';
import AssignmentCreateCommand from './AssignmentCreateCommand';
import AssignmentPublishCommand from './AssignmentPublishCommand';
import AssignmentUnpublishCommand from './AssignmentUnpublishCommand';
class AssignmentCommand extends CommanderCommand {
protected commandName: string = 'assignment';
protected defineCommand() {
this.command
.description('manage an assignment');
}
protected defineSubCommands() {
AssignmentCreateCommand.registerOnCommand(this.command);
AssignmentPublishCommand.registerOnCommand(this.command);
AssignmentUnpublishCommand.registerOnCommand(this.command);
}
protected async commandAction(options: any): Promise<void> { }
}
export default new AssignmentCommand();
\ No newline at end of file
...@@ -4,18 +4,18 @@ import ora from 'ora'; ...@@ -4,18 +4,18 @@ import ora from 'ora';
import GitlabManager from '../../managers/GitlabManager'; import GitlabManager from '../../managers/GitlabManager';
import GitlabUser from '../../shared/types/Gitlab/GitlabUser'; import GitlabUser from '../../shared/types/Gitlab/GitlabUser';
import DojoBackendManager from '../../managers/DojoBackendManager'; import DojoBackendManager from '../../managers/DojoBackendManager';
import Enonce from '../../sharedByClients/models/Enonce';
import Toolbox from '../../shared/helpers/Toolbox'; import Toolbox from '../../shared/helpers/Toolbox';
import AccessesHelper from '../../helpers/AccessesHelper'; import AccessesHelper from '../../helpers/AccessesHelper';
import Assignment from '../../sharedByClients/models/Assignment';
class EnonceCreateCommand extends CommanderCommand { class AssignmentCreateCommand extends CommanderCommand {
protected commandName: string = 'create'; protected commandName: string = 'create';
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('create a new repository for an enonce') .description('create a new repository for an assignment')
.requiredOption('-n, --name <name>', 'name of the enonce') .requiredOption('-n, --name <name>', 'name of the assignment')
.option('-i, --members_id <ids...>', 'list of gitlab members ids (teaching staff) to add to the repository') .option('-i, --members_id <ids...>', 'list of gitlab members ids (teaching staff) to add to the repository')
.option('-u, --members_username <usernames...>', 'list of gitlab members username (teaching staff) to add to the repository') .option('-u, --members_username <usernames...>', 'list of gitlab members username (teaching staff) to add to the repository')
.option('-t, --template <string>', 'id or url of the template (http/s and ssh urls are possible)') .option('-t, --template <string>', 'id or url of the template (http/s and ssh urls are possible)')
...@@ -39,12 +39,12 @@ class EnonceCreateCommand extends CommanderCommand { ...@@ -39,12 +39,12 @@ class EnonceCreateCommand extends CommanderCommand {
return; return;
} }
const enonceGetSpinner: ora.Ora = ora('Checking enonce name availability').start(); const assignmentGetSpinner: ora.Ora = ora('Checking assignment name availability').start();
if ( await DojoBackendManager.getEnonce(options.name) ) { if ( await DojoBackendManager.getAssignment(options.name) ) {
enonceGetSpinner.fail(`Enonce name "${ options.name }" is already taken. Please choose another one.`); assignmentGetSpinner.fail(`Assignment name "${ options.name }" is already taken. Please choose another one.`);
return; return;
} }
enonceGetSpinner.succeed(`Enonce name "${ options.name }" is available`); assignmentGetSpinner.succeed(`Assignment name "${ options.name }" is available`);
if ( options.template ) { if ( options.template ) {
templateIdOrNamespace = options.template; templateIdOrNamespace = options.template;
...@@ -59,12 +59,12 @@ class EnonceCreateCommand extends CommanderCommand { ...@@ -59,12 +59,12 @@ class EnonceCreateCommand extends CommanderCommand {
} }
} }
// Create the enonce // Create the assignment
{ {
console.log(chalk.cyan('Please wait while we are creating the enonce...')); console.log(chalk.cyan('Please wait while we are creating the assignment...'));
try { try {
const enonce: Enonce = await DojoBackendManager.createEnonce(options.name, members, templateIdOrNamespace); const assignment: Assignment = await DojoBackendManager.createAssignment(options.name, members, templateIdOrNamespace);
const oraInfo = (message: string) => { const oraInfo = (message: string) => {
ora({ ora({
...@@ -73,10 +73,10 @@ class EnonceCreateCommand extends CommanderCommand { ...@@ -73,10 +73,10 @@ class EnonceCreateCommand extends CommanderCommand {
}).start().info(); }).start().info();
}; };
oraInfo(`${ chalk.magenta('Name:') } ${ enonce.name }`); oraInfo(`${ chalk.magenta('Name:') } ${ assignment.name }`);
oraInfo(`${ chalk.magenta('Web URL:') } ${ enonce.gitlabCreationInfo.web_url }`); oraInfo(`${ chalk.magenta('Web URL:') } ${ assignment.gitlabCreationInfo.web_url }`);
oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ enonce.gitlabCreationInfo.http_url_to_repo }`); oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ assignment.gitlabCreationInfo.http_url_to_repo }`);
oraInfo(`${ chalk.magenta('SSH Repo:') } ${ enonce.gitlabCreationInfo.ssh_url_to_repo }`); oraInfo(`${ chalk.magenta('SSH Repo:') } ${ assignment.gitlabCreationInfo.ssh_url_to_repo }`);
} catch ( error ) { } catch ( error ) {
return; return;
} }
...@@ -85,4 +85,4 @@ class EnonceCreateCommand extends CommanderCommand { ...@@ -85,4 +85,4 @@ class EnonceCreateCommand extends CommanderCommand {
} }
export default new EnonceCreateCommand(); export default new AssignmentCreateCommand();
\ No newline at end of file \ No newline at end of file
import AssignmentPublishUnpublishCommandBase from './AssignmentPublishUnpublishCommandBase';
class AssignmentPublishCommand extends AssignmentPublishUnpublishCommandBase {
protected commandName: string = 'publish';
protected publish: boolean = true;
}
export default new AssignmentPublishCommand();
\ No newline at end of file
import CommanderCommand from '../CommanderCommand'; import CommanderCommand from '../CommanderCommand';
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import Enonce from '../../sharedByClients/models/Enonce';
import chalk from 'chalk'; import chalk from 'chalk';
import SessionManager from '../../managers/SessionManager'; import SessionManager from '../../managers/SessionManager';
import ora from 'ora'; import ora from 'ora';
import DojoBackendManager from '../../managers/DojoBackendManager'; import DojoBackendManager from '../../managers/DojoBackendManager';
import Assignment from '../../sharedByClients/models/Assignment';
abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand { abstract class AssignmentPublishUnpublishCommandBase extends CommanderCommand {
protected abstract publish: boolean; protected abstract publish: boolean;
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('publish an enonce') .description('publish an assignment')
.argument('<name or url>', 'name or url (http/s or ssh) of the enonce') .argument('<name or url>', 'name or url (http/s or ssh) of the assignment')
.option('-f, --force', 'don\'t ask for confirmation') .option('-f, --force', 'don\'t ask for confirmation')
.action(this.commandAction.bind(this)); .action(this.commandAction.bind(this));
} }
protected async commandAction(enonceNameOrUrl: string, options: { force: boolean }): Promise<void> { protected async commandAction(assignmentNameOrUrl: string, options: { force: boolean }): Promise<void> {
if ( !options.force ) { if ( !options.force ) {
options.force = (await inquirer.prompt({ options.force = (await inquirer.prompt({
type : 'confirm', type : 'confirm',
name : 'force', name : 'force',
message: this.publish ? 'Are you sure you want to publish this enonce?' : 'Are you sure you want to unpublish this enonce?' message: this.publish ? 'Are you sure you want to publish this assignment?' : 'Are you sure you want to unpublish this assignment?'
})).force; })).force;
} }
...@@ -31,7 +31,7 @@ abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand { ...@@ -31,7 +31,7 @@ abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand {
return; return;
} }
let enonce!: Enonce | undefined; let assignment!: Assignment | undefined;
{ {
console.log(chalk.cyan('Please wait while we verify and retrieve data...')); console.log(chalk.cyan('Please wait while we verify and retrieve data...'));
...@@ -40,38 +40,38 @@ abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand { ...@@ -40,38 +40,38 @@ abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand {
return; return;
} }
ora('Checking enonce:').start().info(); ora('Checking assignment:').start().info();
ora({ ora({
text : enonceNameOrUrl, text : assignmentNameOrUrl,
indent: 4 indent: 4
}).start().info(); }).start().info();
const enonceGetSpinner: ora.Ora = ora({ const assignmentGetSpinner: ora.Ora = ora({
text : 'Checking if enonce exists', text : 'Checking if assignment exists',
indent: 8 indent: 8
}).start(); }).start();
enonce = await DojoBackendManager.getEnonce(enonceNameOrUrl); assignment = await DojoBackendManager.getAssignment(assignmentNameOrUrl);
if ( !enonce ) { if ( !assignment ) {
enonceGetSpinner.fail(`The enonce doesn't exists`); assignmentGetSpinner.fail(`The assignment doesn't exists`);
return; return;
} }
enonceGetSpinner.succeed(`The enonce exists`); assignmentGetSpinner.succeed(`The assignment exists`);
const enonceCheckAccessSpinner: ora.Ora = ora({ const assignmentCheckAccessSpinner: ora.Ora = ora({
text : 'Checking accesses', text : 'Checking accesses',
indent: 8 indent: 8
}).start(); }).start();
if ( !enonce.staff ) { if ( !assignment.staff ) {
enonceCheckAccessSpinner.fail(`You are not in the staff of this enonce`); assignmentCheckAccessSpinner.fail(`You are not in the staff of this assignment`);
return; return;
} }
enonceCheckAccessSpinner.succeed(`You are in the staff of this enonce`); assignmentCheckAccessSpinner.succeed(`You are in the staff of this assignment`);
} }
{ {
console.log(chalk.cyan(`Please wait while we ${ this.publish ? 'publish' : 'unpublish' } the enonce...`)); console.log(chalk.cyan(`Please wait while we ${ this.publish ? 'publish' : 'unpublish' } the assignment...`));
try { try {
await DojoBackendManager.changeEnoncePublishedStatus(enonce, this.publish); await DojoBackendManager.changeAssignmentPublishedStatus(assignment, this.publish);
} catch ( error ) { } catch ( error ) {
return; return;
} }
...@@ -80,4 +80,4 @@ abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand { ...@@ -80,4 +80,4 @@ abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand {
} }
export default EnoncePublishUnpublishCommandBase; export default AssignmentPublishUnpublishCommandBase;
\ No newline at end of file \ No newline at end of file
import AssignmentPublishUnpublishCommandBase from './AssignmentPublishUnpublishCommandBase';
class AssignmentUnpublishCommand extends AssignmentPublishUnpublishCommandBase {
protected commandName: string = 'unpublish';
protected publish: boolean = false;
}
export default new AssignmentUnpublishCommand();
\ No newline at end of file
import CommanderCommand from '../CommanderCommand';
import EnonceCreateCommand from './EnonceCreateCommand';
import EnoncePublishCommand from './EnoncePublishCommand';
import EnonceUnpublishCommand from './EnonceUnpublishCommand';
class EnonceCommand extends CommanderCommand {
protected commandName: string = 'enonce';
protected defineCommand() {
this.command
.description('manage an enonce');
}
protected defineSubCommands() {
EnonceCreateCommand.registerOnCommand(this.command);
EnoncePublishCommand.registerOnCommand(this.command);
EnonceUnpublishCommand.registerOnCommand(this.command);
}
protected async commandAction(options: any): Promise<void> { }
}
export default new EnonceCommand();
\ No newline at end of file
import EnoncePublishUnpublishCommandBase from './EnoncePublishUnpublishCommandBase';
class EnoncePublishCommand extends EnoncePublishUnpublishCommandBase {
protected commandName: string = 'publish';
protected publish: boolean = true;
}
export default new EnoncePublishCommand();
\ No newline at end of file
import EnoncePublishUnpublishCommandBase from './EnoncePublishUnpublishCommandBase';
class EnonceUnpublishCommand extends EnoncePublishUnpublishCommandBase {
protected commandName: string = 'unpublish';
protected publish: boolean = false;
}
export default new EnonceUnpublishCommand();
\ No newline at end of file
import CommanderCommand from '../CommanderCommand'; import CommanderCommand from '../CommanderCommand';
import ExerciceCreateCommand from './ExerciceCreateCommand'; import ExerciseCreateCommand from './ExerciseCreateCommand';
import ExerciceRunCommand from './ExerciceRunCommand'; import ExerciseRunCommand from './ExerciseRunCommand';
class ExerciceCommand extends CommanderCommand { class ExerciseCommand extends CommanderCommand {
protected commandName: string = 'exercice'; protected commandName: string = 'exercise';
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('manage an exercice'); .description('manage an exercise');
} }
protected defineSubCommands() { protected defineSubCommands() {
ExerciceCreateCommand.registerOnCommand(this.command); ExerciseCreateCommand.registerOnCommand(this.command);
ExerciceRunCommand.registerOnCommand(this.command); ExerciseRunCommand.registerOnCommand(this.command);
} }
protected async commandAction(options: any): Promise<void> { } protected async commandAction(options: any): Promise<void> { }
} }
export default new ExerciceCommand(); export default new ExerciseCommand();
\ No newline at end of file \ No newline at end of file
...@@ -2,20 +2,20 @@ import CommanderCommand from '../CommanderCommand'; ...@@ -2,20 +2,20 @@ import CommanderCommand from '../CommanderCommand';
import chalk from 'chalk'; import chalk from 'chalk';
import GitlabManager from '../../managers/GitlabManager'; import GitlabManager from '../../managers/GitlabManager';
import GitlabUser from '../../shared/types/Gitlab/GitlabUser'; import GitlabUser from '../../shared/types/Gitlab/GitlabUser';
import Enonce from '../../sharedByClients/models/Enonce';
import ora from 'ora'; import ora from 'ora';
import DojoBackendManager from '../../managers/DojoBackendManager'; import DojoBackendManager from '../../managers/DojoBackendManager';
import Exercice from '../../sharedByClients/models/Exercice';
import AccessesHelper from '../../helpers/AccessesHelper'; import AccessesHelper from '../../helpers/AccessesHelper';
import Assignment from '../../sharedByClients/models/Assignment';
import Exercise from '../../sharedByClients/models/Exercise';
class ExerciceCreateCommand extends CommanderCommand { class ExerciseCreateCommand extends CommanderCommand {
protected commandName: string = 'create'; protected commandName: string = 'create';
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('create a new exercice from an enonce') .description('create a new exercise from an assignment')
.requiredOption('-e, --enonce <value>', 'enonce source (Dojo enonce ID, Dojo enonce name or Gitlab enonce URL)') .requiredOption('-a, --assignment <value>', 'assignment source (Dojo assignment ID, Dojo assignment name or Gitlab assignment URL)')
.option('-i, --members_id <ids...>', 'list of gitlab members ids (group\'s student) to add to the repository') .option('-i, --members_id <ids...>', 'list of gitlab members ids (group\'s student) to add to the repository')
.option('-u, --members_username <usernames...>', 'list of gitlab members username (group\'s student) to add to the repository') .option('-u, --members_username <usernames...>', 'list of gitlab members username (group\'s student) to add to the repository')
.action(this.commandAction.bind(this)); .action(this.commandAction.bind(this));
...@@ -23,7 +23,7 @@ class ExerciceCreateCommand extends CommanderCommand { ...@@ -23,7 +23,7 @@ class ExerciceCreateCommand extends CommanderCommand {
protected async commandAction(options: any): Promise<void> { protected async commandAction(options: any): Promise<void> {
let members!: Array<GitlabUser> | false; let members!: Array<GitlabUser> | false;
let enonce!: Enonce | undefined; let assignment!: Assignment | undefined;
// Check access and retrieve data // Check access and retrieve data
{ {
...@@ -38,35 +38,35 @@ class ExerciceCreateCommand extends CommanderCommand { ...@@ -38,35 +38,35 @@ class ExerciceCreateCommand extends CommanderCommand {
return; return;
} }
ora('Checking enonce:').start().info(); ora('Checking assignment:').start().info();
const enonceGetSpinner: ora.Ora = ora({ const assignmentGetSpinner: ora.Ora = ora({
text : 'Checking if enonce exists', text : 'Checking if assignment exists',
indent: 4 indent: 4
}).start(); }).start();
enonce = await DojoBackendManager.getEnonce(options.enonce); assignment = await DojoBackendManager.getAssignment(options.assignment);
if ( !enonce ) { if ( !assignment ) {
enonceGetSpinner.fail(`Enonce "${ options.enonce }" doesn't exists`); assignmentGetSpinner.fail(`Assignment "${ options.assignment }" doesn't exists`);
return; return;
} }
enonceGetSpinner.succeed(`Enonce "${ options.enonce }" exists`); assignmentGetSpinner.succeed(`Assignment "${ options.assignment }" exists`);
const enoncePublishedSpinner: ora.Ora = ora({ const assignmentPublishedSpinner: ora.Ora = ora({
text : 'Checking if enonce is published', text : 'Checking if assignment is published',
indent: 4 indent: 4
}).start(); }).start();
if ( !enonce.published ) { if ( !assignment.published ) {
enoncePublishedSpinner.fail(`Enonce "${ enonce.name }" isn't published`); assignmentPublishedSpinner.fail(`Assignment "${ assignment.name }" isn't published`);
return; return;
} }
enoncePublishedSpinner.succeed(`Enonce "${ enonce.name }" is published`); assignmentPublishedSpinner.succeed(`Assignment "${ assignment.name }" is published`);
} }
//Create the exercice //Create the exercise
{ {
console.log(chalk.cyan('Please wait while we are creating the exercice...')); console.log(chalk.cyan('Please wait while we are creating the exercise...'));
try { try {
const exercice: Exercice = await DojoBackendManager.createExercice((enonce as Enonce).name, members); const exercise: Exercise = await DojoBackendManager.createExercise((assignment as Assignment).name, members);
const oraInfo = (message: string) => { const oraInfo = (message: string) => {
ora({ ora({
...@@ -75,11 +75,11 @@ class ExerciceCreateCommand extends CommanderCommand { ...@@ -75,11 +75,11 @@ class ExerciceCreateCommand extends CommanderCommand {
}).start().info(); }).start().info();
}; };
oraInfo(`${ chalk.magenta('Id:') } ${ exercice.id }`); oraInfo(`${ chalk.magenta('Id:') } ${ exercise.id }`);
oraInfo(`${ chalk.magenta('Name:') } ${ exercice.name }`); oraInfo(`${ chalk.magenta('Name:') } ${ exercise.name }`);
oraInfo(`${ chalk.magenta('Web URL:') } ${ exercice.gitlabCreationInfo.web_url }`); oraInfo(`${ chalk.magenta('Web URL:') } ${ exercise.gitlabCreationInfo.web_url }`);
oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ exercice.gitlabCreationInfo.http_url_to_repo }`); oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ exercise.gitlabCreationInfo.http_url_to_repo }`);
oraInfo(`${ chalk.magenta('SSH Repo:') } ${ exercice.gitlabCreationInfo.ssh_url_to_repo }`); oraInfo(`${ chalk.magenta('SSH Repo:') } ${ exercise.gitlabCreationInfo.ssh_url_to_repo }`);
} catch ( error ) { } catch ( error ) {
return; return;
} }
...@@ -88,4 +88,4 @@ class ExerciceCreateCommand extends CommanderCommand { ...@@ -88,4 +88,4 @@ class ExerciceCreateCommand extends CommanderCommand {
} }
export default new ExerciceCreateCommand(); export default new ExerciseCreateCommand();
\ No newline at end of file \ No newline at end of file
...@@ -4,29 +4,29 @@ import fs from 'node:fs'; ...@@ -4,29 +4,29 @@ import fs from 'node:fs';
import ora from 'ora'; import ora from 'ora';
import util from 'util'; import util from 'util';
import { exec } from 'child_process'; import { exec } from 'child_process';
import SharedEnonceHelper from '../../shared/helpers/Dojo/SharedEnonceHelper';
import EnonceFile from '../../shared/types/Dojo/EnonceFile';
import chalk from 'chalk'; import chalk from 'chalk';
import ExerciceDockerCompose from '../../sharedByClients/helpers/Dojo/ExerciceDockerCompose';
import * as os from 'os'; import * as os from 'os';
import path from 'path'; import path from 'path';
import ExerciceResultsValidation from '../../sharedByClients/helpers/Dojo/ExerciceResultsValidation';
import ExerciceCheckerError from '../../shared/types/Dojo/ExerciceCheckerError';
import ClientsSharedExerciceHelper from '../../sharedByClients/helpers/Dojo/ClientsSharedExerciceHelper';
import ClientsSharedConfig from '../../sharedByClients/config/ClientsSharedConfig'; import ClientsSharedConfig from '../../sharedByClients/config/ClientsSharedConfig';
import AssignmentFile from '../../shared/types/Dojo/AssignmentFile';
import ExerciseDockerCompose from '../../sharedByClients/helpers/Dojo/ExerciseDockerCompose';
import ExerciseResultsValidation from '../../sharedByClients/helpers/Dojo/ExerciseResultsValidation';
import SharedAssignmentHelper from '../../shared/helpers/Dojo/SharedAssignmentHelper';
import ExerciseCheckerError from '../../shared/types/Dojo/ExerciseCheckerError';
import ClientsSharedExerciseHelper from '../../sharedByClients/helpers/Dojo/ClientsSharedExerciseHelper';
const execAsync = util.promisify(exec); const execAsync = util.promisify(exec);
class ExerciceRunCommand extends CommanderCommand { class ExerciseRunCommand extends CommanderCommand {
protected commandName: string = 'run'; protected commandName: string = 'run';
private readonly dateISOString: string = (new Date()).toISOString().replace(/:/g, '_').replace(/\./g, '_'); private readonly dateISOString: string = (new Date()).toISOString().replace(/:/g, '_').replace(/\./g, '_');
private readonly folderResultsVolume: string = path.join(os.homedir(), 'DojoExecutions', `dojo_execLogs_${ this.dateISOString }`); private readonly folderResultsVolume: string = path.join(os.homedir(), 'DojoExecutions', `dojo_execLogs_${ this.dateISOString }`);
private readonly folderResultsDojo: string = path.join(this.folderResultsVolume, `Dojo/`); private readonly folderResultsDojo: string = path.join(this.folderResultsVolume, `Dojo/`);
private readonly folderResultsExercice: string = path.join(this.folderResultsVolume, `Exercice/`); private readonly folderResultsExercise: string = path.join(this.folderResultsVolume, `Exercise/`);
private readonly projectName: string = `${ ClientsSharedConfig.dockerCompose.projectName }_${ this.dateISOString.toLowerCase() }`; private readonly projectName: string = `${ ClientsSharedConfig.dockerCompose.projectName }_${ this.dateISOString.toLowerCase() }`;
...@@ -34,8 +34,8 @@ class ExerciceRunCommand extends CommanderCommand { ...@@ -34,8 +34,8 @@ class ExerciceRunCommand extends CommanderCommand {
protected defineCommand() { protected defineCommand() {
this.command this.command
.description('locally run an exercice') .description('locally run an exercise')
.option('-p, --path <value>', 'exercice path', Config.folders.defaultLocalExercice) .option('-p, --path <value>', 'exercise path', Config.folders.defaultLocalExercise)
.action(this.commandAction.bind(this)); .action(this.commandAction.bind(this));
} }
...@@ -47,61 +47,61 @@ class ExerciceRunCommand extends CommanderCommand { ...@@ -47,61 +47,61 @@ class ExerciceRunCommand extends CommanderCommand {
} }
protected async commandAction(options: any): Promise<void> { protected async commandAction(options: any): Promise<void> {
const localExercicePath = options.path ?? Config.folders.defaultLocalExercice; const localExercisePath = options.path ?? Config.folders.defaultLocalExercise;
let enonceFile: EnonceFile; let assignmentFile: AssignmentFile;
let exerciceDockerCompose: ExerciceDockerCompose; let exerciseDockerCompose: ExerciseDockerCompose;
let exerciceResultsValidation: ExerciceResultsValidation; let exerciseResultsValidation: ExerciseResultsValidation;
// Step 1: Check requirements (if it's an exercice folder and if Docker deamon is running) // Step 1: Check requirements (if it's an exercise folder and if Docker deamon is running)
{ {
console.log(chalk.cyan('Please wait while we are checking and creating dependencies...')); console.log(chalk.cyan('Please wait while we are checking and creating dependencies...'));
// Create result temp folder // Create result temp folder
fs.mkdirSync(this.folderResultsVolume, { recursive: true }); fs.mkdirSync(this.folderResultsVolume, { recursive: true });
fs.mkdirSync(this.folderResultsDojo, { recursive: true }); fs.mkdirSync(this.folderResultsDojo, { recursive: true });
fs.mkdirSync(this.folderResultsExercice, { recursive: true }); fs.mkdirSync(this.folderResultsExercise, { recursive: true });
ora({ ora({
text : `Checking exercice content:`, text : `Checking exercise content:`,
indent: 4 indent: 4
}).start().info(); }).start().info();
// Exercice folder // Exercise folder
{ {
const spinner: ora.Ora = ora({ const spinner: ora.Ora = ora({
text : `Checking exercice folder`, text : `Checking exercise folder`,
indent: 8 indent: 8
}).start(); }).start();
const files = fs.readdirSync(options.path); const files = fs.readdirSync(options.path);
const missingFiles = Config.exercice.neededFiles.map((file: string): [ string, boolean ] => [ file, files.includes(file) ]).filter((file: [ string, boolean ]) => !file[1]); const missingFiles = Config.exercise.neededFiles.map((file: string): [ string, boolean ] => [ file, files.includes(file) ]).filter((file: [ string, boolean ]) => !file[1]);
if ( missingFiles.length > 0 ) { if ( missingFiles.length > 0 ) {
spinner.fail(`The exercice folder is missing the following files: ${ missingFiles.map((file: [ string, boolean ]) => file[0]).join(', ') }`); spinner.fail(`The exercise folder is missing the following files: ${ missingFiles.map((file: [ string, boolean ]) => file[0]).join(', ') }`);
return; return;
} }
spinner.succeed(`The exercice folder contains all the needed files`); spinner.succeed(`The exercise folder contains all the needed files`);
} }
// dojo.enonce validity // dojo.assignment validity
{ {
const spinner: ora.Ora = ora({ const spinner: ora.Ora = ora({
text : `Checking ${ Config.enonce.filename } file`, text : `Checking ${ Config.assignment.filename } file`,
indent: 8 indent: 8
}).start(); }).start();
const validationResults = SharedEnonceHelper.validateDescriptionFile(`${ options.path }/${ Config.enonce.filename }`); const validationResults = SharedAssignmentHelper.validateDescriptionFile(`${ options.path }/${ Config.assignment.filename }`);
if ( !validationResults.isValid ) { if ( !validationResults.isValid ) {
spinner.fail(`The ${ Config.enonce.filename } file is invalid: ${ JSON.stringify(validationResults.errors) }`); spinner.fail(`The ${ Config.assignment.filename } file is invalid: ${ JSON.stringify(validationResults.errors) }`);
return; return;
} else { } else {
enonceFile = validationResults.results!; assignmentFile = validationResults.results!;
} }
spinner.succeed(`The ${ Config.enonce.filename } file is valid`); spinner.succeed(`The ${ Config.assignment.filename } file is valid`);
} }
// Docker deamon // Docker deamon
...@@ -112,7 +112,7 @@ class ExerciceRunCommand extends CommanderCommand { ...@@ -112,7 +112,7 @@ class ExerciceRunCommand extends CommanderCommand {
}).start(); }).start();
try { try {
await execAsync(`cd "${ Config.folders.defaultLocalExercice }";docker ps`); await execAsync(`cd "${ Config.folders.defaultLocalExercise }";docker ps`);
} catch ( error ) { } catch ( error ) {
spinner.fail(`The Docker deamon is not running`); spinner.fail(`The Docker deamon is not running`);
return; return;
...@@ -125,27 +125,27 @@ class ExerciceRunCommand extends CommanderCommand { ...@@ -125,27 +125,27 @@ class ExerciceRunCommand extends CommanderCommand {
// Step 2: Run docker-compose file // Step 2: Run docker-compose file
{ {
console.log(chalk.cyan('Please wait while we are running the exercice...')); console.log(chalk.cyan('Please wait while we are running the exercise...'));
const composeOverridePath: string = path.join(localExercicePath, 'docker-compose-override.yml'); const composeOverridePath: string = path.join(localExercisePath, 'docker-compose-override.yml');
const composeOverride = fs.readFileSync(path.join(__dirname, '../../../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', enonceFile.result.volume).replace('{{MOUNT_PATH}}', this.folderResultsExercice); const composeOverride = fs.readFileSync(path.join(__dirname, '../../../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', assignmentFile.result.volume).replace('{{MOUNT_PATH}}', this.folderResultsExercise);
fs.writeFileSync(composeOverridePath, composeOverride); fs.writeFileSync(composeOverridePath, composeOverride);
exerciceDockerCompose = new ExerciceDockerCompose(this.projectName, enonceFile, localExercicePath, [ composeOverridePath ]); exerciseDockerCompose = new ExerciseDockerCompose(this.projectName, assignmentFile, localExercisePath, [ composeOverridePath ]);
try { try {
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
let spinner: ora.Ora; let spinner: ora.Ora;
exerciceDockerCompose.events.on('step', (name: string, message: string) => { exerciseDockerCompose.events.on('step', (name: string, message: string) => {
spinner = ora({ spinner = ora({
text : message, text : message,
indent: 4 indent: 4
}).start(); }).start();
}); });
exerciceDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => { exerciseDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
if ( error ) { if ( error ) {
spinner.fail(message); spinner.fail(message);
} else { } else {
...@@ -153,18 +153,18 @@ class ExerciceRunCommand extends CommanderCommand { ...@@ -153,18 +153,18 @@ class ExerciceRunCommand extends CommanderCommand {
} }
}); });
exerciceDockerCompose.events.on('finished', (success: boolean, exitCode: number) => { exerciseDockerCompose.events.on('finished', (success: boolean, exitCode: number) => {
success ? resolve() : reject(); success ? resolve() : reject();
}); });
exerciceDockerCompose.run(true); exerciseDockerCompose.run(true);
}); });
} catch ( error ) { } } catch ( error ) { }
fs.rmSync(composeOverridePath); fs.rmSync(composeOverridePath);
fs.writeFileSync(this.fileComposeLogs, exerciceDockerCompose.allLogs); fs.writeFileSync(this.fileComposeLogs, exerciseDockerCompose.allLogs);
if ( !exerciceDockerCompose.success ) { if ( !exerciseDockerCompose.success ) {
this.displayExecutionLogs(); this.displayExecutionLogs();
return; return;
} }
...@@ -175,20 +175,20 @@ class ExerciceRunCommand extends CommanderCommand { ...@@ -175,20 +175,20 @@ class ExerciceRunCommand extends CommanderCommand {
{ {
console.log(chalk.cyan('Please wait while we are checking the results...')); console.log(chalk.cyan('Please wait while we are checking the results...'));
exerciceResultsValidation = new ExerciceResultsValidation(this.folderResultsDojo, this.folderResultsExercice); exerciseResultsValidation = new ExerciseResultsValidation(this.folderResultsDojo, this.folderResultsExercise);
try { try {
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
let spinner: ora.Ora; let spinner: ora.Ora;
exerciceResultsValidation.events.on('step', (name: string, message: string) => { exerciseResultsValidation.events.on('step', (name: string, message: string) => {
spinner = ora({ spinner = ora({
text : message, text : message,
indent: 4 indent: 4
}).start(); }).start();
}); });
exerciceResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => { exerciseResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => {
if ( error ) { if ( error ) {
if ( stepName == 'CHECK_SIZE' ) { if ( stepName == 'CHECK_SIZE' ) {
spinner.warn(message); spinner.warn(message);
...@@ -200,11 +200,11 @@ class ExerciceRunCommand extends CommanderCommand { ...@@ -200,11 +200,11 @@ class ExerciceRunCommand extends CommanderCommand {
} }
}); });
exerciceResultsValidation.events.on('finished', (success: boolean, exitCode: number) => { exerciseResultsValidation.events.on('finished', (success: boolean, exitCode: number) => {
success || exitCode == ExerciceCheckerError.EXERCICE_RESULTS_FOLDER_TOO_BIG ? resolve() : reject(); success || exitCode == ExerciseCheckerError.EXERCISE_RESULTS_FOLDER_TOO_BIG ? resolve() : reject();
}); });
exerciceResultsValidation.run(); exerciseResultsValidation.run();
}); });
} catch ( error ) { } catch ( error ) {
this.displayExecutionLogs(); this.displayExecutionLogs();
...@@ -215,7 +215,7 @@ class ExerciceRunCommand extends CommanderCommand { ...@@ -215,7 +215,7 @@ class ExerciceRunCommand extends CommanderCommand {
// Step 4: Display results + Volume location // Step 4: Display results + Volume location
{ {
ClientsSharedExerciceHelper.displayExecutionResults(exerciceResultsValidation.exerciceResults!, exerciceDockerCompose.exitCode, { ClientsSharedExerciseHelper.displayExecutionResults(exerciseResultsValidation.exerciseResults!, exerciseDockerCompose.exitCode, {
INFO : chalk.bold, INFO : chalk.bold,
SUCCESS: chalk.green, SUCCESS: chalk.green,
FAILURE: chalk.red FAILURE: chalk.red
...@@ -225,4 +225,4 @@ class ExerciceRunCommand extends CommanderCommand { ...@@ -225,4 +225,4 @@ class ExerciceRunCommand extends CommanderCommand {
} }
export default new ExerciceRunCommand(); export default new ExerciseRunCommand();
\ No newline at end of file \ No newline at end of file
...@@ -7,14 +7,14 @@ class Config { ...@@ -7,14 +7,14 @@ class Config {
}; };
public readonly folders: { public readonly folders: {
defaultLocalExercice: string defaultLocalExercise: string
}; };
public enonce: { public assignment: {
filename: string filename: string
}; };
public readonly exercice: { public readonly exercise: {
neededFiles: Array<string> neededFiles: Array<string>
}; };
...@@ -25,15 +25,15 @@ class Config { ...@@ -25,15 +25,15 @@ class Config {
}; };
this.folders = { this.folders = {
defaultLocalExercice: process.env.LOCAL_EXERCICE_DEFAULT_FOLDER || './' defaultLocalExercise: process.env.LOCAL_EXERCISE_DEFAULT_FOLDER || './'
}; };
this.enonce = { this.assignment = {
filename: process.env.ENONCE_FILENAME || '' filename: process.env.ASSIGNMENT_FILENAME || ''
}; };
this.exercice = { this.exercise = {
neededFiles: JSON.parse(process.env.EXERCICE_NEEDED_FILES || '[]') neededFiles: JSON.parse(process.env.EXERCISE_NEEDED_FILES || '[]')
}; };
} }
} }
......
...@@ -2,11 +2,11 @@ import axios, { AxiosError } from 'axios'; ...@@ -2,11 +2,11 @@ import axios, { AxiosError } from 'axios';
import ora from 'ora'; import ora from 'ora';
import ApiRoute from '../sharedByClients/types/Dojo/ApiRoute'; import ApiRoute from '../sharedByClients/types/Dojo/ApiRoute';
import { StatusCodes } from 'http-status-codes'; import { StatusCodes } from 'http-status-codes';
import Enonce from '../sharedByClients/models/Enonce';
import GitlabUser from '../shared/types/Gitlab/GitlabUser'; import GitlabUser from '../shared/types/Gitlab/GitlabUser';
import Exercice from '../sharedByClients/models/Exercice';
import DojoResponse from '../shared/types/Dojo/DojoResponse';
import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig';
import Assignment from '../sharedByClients/models/Assignment';
import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse';
import Exercise from '../sharedByClients/models/Exercise';
class DojoBackendManager { class DojoBackendManager {
...@@ -14,9 +14,9 @@ class DojoBackendManager { ...@@ -14,9 +14,9 @@ class DojoBackendManager {
return `${ ClientsSharedConfig.apiURL }${ route }`; return `${ ClientsSharedConfig.apiURL }${ route }`;
} }
public async getEnonce(nameOrUrl: string): Promise<Enonce | undefined> { public async getAssignment(nameOrUrl: string): Promise<Assignment | undefined> {
try { try {
return (await axios.get<DojoResponse<Enonce>>(this.getApiUrl(ApiRoute.ENONCE_GET).replace('{{nameOrUrl}}', encodeURIComponent(nameOrUrl)))).data.data; return (await axios.get<DojoBackendResponse<Assignment>>(this.getApiUrl(ApiRoute.ASSIGNMENT_GET).replace('{{nameOrUrl}}', encodeURIComponent(nameOrUrl)))).data.data;
} catch ( error ) { } catch ( error ) {
return undefined; return undefined;
} }
...@@ -59,21 +59,21 @@ class DojoBackendManager { ...@@ -59,21 +59,21 @@ class DojoBackendManager {
} }
} }
public async createEnonce(name: string, members: Array<GitlabUser>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Enonce> { public async createAssignment(name: string, members: Array<GitlabUser>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Assignment> {
const spinner: ora.Ora = ora('Creating enonce...'); const spinner: ora.Ora = ora('Creating assignment...');
if ( verbose ) { if ( verbose ) {
spinner.start(); spinner.start();
} }
try { try {
const response = await axios.post<DojoResponse<Enonce>>(this.getApiUrl(ApiRoute.ENONCE_CREATE), Object.assign({ const response = await axios.post<DojoBackendResponse<Assignment>>(this.getApiUrl(ApiRoute.ASSIGNMENT_CREATE), Object.assign({
name : name, name : name,
members: JSON.stringify(members) members: JSON.stringify(members)
}, templateIdOrNamespace ? { template: templateIdOrNamespace } : {})); }, templateIdOrNamespace ? { template: templateIdOrNamespace } : {}));
if ( verbose ) { if ( verbose ) {
spinner.succeed(`Enonce successfully created`); spinner.succeed(`Assignment successfully created`);
} }
return response.data.data; return response.data.data;
...@@ -82,13 +82,13 @@ class DojoBackendManager { ...@@ -82,13 +82,13 @@ class DojoBackendManager {
if ( error instanceof AxiosError ) { if ( error instanceof AxiosError ) {
if ( error.response ) { if ( error.response ) {
if ( error.response.status === StatusCodes.CONFLICT ) { if ( error.response.status === StatusCodes.CONFLICT ) {
spinner.fail(`The enonce name is already used. Please choose another one.`); spinner.fail(`The assignment name is already used. Please choose another one.`);
} else { } else {
spinner.fail(`Enonce creation error: ${ error.response.statusText }`); spinner.fail(`Assignment creation error: ${ error.response.statusText }`);
} }
} }
} else { } else {
spinner.fail(`Enonce creation error: unknown error`); spinner.fail(`Assignment creation error: unknown error`);
} }
} }
...@@ -96,18 +96,18 @@ class DojoBackendManager { ...@@ -96,18 +96,18 @@ class DojoBackendManager {
} }
} }
public async createExercice(enonceName: string, members: Array<GitlabUser>, verbose: boolean = true): Promise<Exercice> { public async createExercise(assignmentName: string, members: Array<GitlabUser>, verbose: boolean = true): Promise<Exercise> {
const spinner: ora.Ora = ora('Creating exercice...'); const spinner: ora.Ora = ora('Creating exercise...');
if ( verbose ) { if ( verbose ) {
spinner.start(); spinner.start();
} }
try { try {
const response = await axios.post<DojoResponse<Exercice>>(this.getApiUrl(ApiRoute.EXERCICE_CREATE).replace('{{nameOrUrl}}', encodeURIComponent(enonceName)), { members: JSON.stringify(members) }); const response = await axios.post<DojoBackendResponse<Exercise>>(this.getApiUrl(ApiRoute.EXERCISE_CREATE).replace('{{nameOrUrl}}', encodeURIComponent(assignmentName)), { members: JSON.stringify(members) });
if ( verbose ) { if ( verbose ) {
spinner.succeed(`Exercice successfully created`); spinner.succeed(`Exercise successfully created`);
} }
return response.data.data; return response.data.data;
...@@ -116,13 +116,13 @@ class DojoBackendManager { ...@@ -116,13 +116,13 @@ class DojoBackendManager {
if ( error instanceof AxiosError ) { if ( error instanceof AxiosError ) {
if ( error.response ) { if ( error.response ) {
if ( error.response.status === StatusCodes.CONFLICT ) { if ( error.response.status === StatusCodes.CONFLICT ) {
spinner.fail(`You've reached the max number of exercice of this enonce.`); spinner.fail(`You've already reached the max number of exercise of this assignment.`);
} else { } else {
spinner.fail(`Exercice creation error: ${ error.response.statusText }`); spinner.fail(`Exercise creation error: ${ error.response.statusText }`);
} }
} }
} else { } else {
spinner.fail(`Exercice creation error: unknown error`); spinner.fail(`Exercise creation error: unknown error`);
} }
} }
...@@ -130,7 +130,7 @@ class DojoBackendManager { ...@@ -130,7 +130,7 @@ class DojoBackendManager {
} }
} }
public async changeEnoncePublishedStatus(enonce: Enonce, publish: boolean, verbose: boolean = true) { public async changeAssignmentPublishedStatus(assignment: Assignment, publish: boolean, verbose: boolean = true) {
const spinner: ora.Ora = ora('Changing published status...'); const spinner: ora.Ora = ora('Changing published status...');
if ( verbose ) { if ( verbose ) {
...@@ -138,19 +138,19 @@ class DojoBackendManager { ...@@ -138,19 +138,19 @@ class DojoBackendManager {
} }
try { try {
await axios.patch<DojoResponse<null>>(this.getApiUrl(publish ? ApiRoute.ENONCE_PUBLISH : ApiRoute.ENONCE_UNPUBLISH).replace('{{nameOrUrl}}', encodeURIComponent(enonce.name)), {}); await axios.patch<DojoBackendResponse<null>>(this.getApiUrl(publish ? ApiRoute.ASSIGNMENT_PUBLISH : ApiRoute.ASSIGNMENT_UNPUBLISH).replace('{{nameOrUrl}}', encodeURIComponent(assignment.name)), {});
if ( verbose ) { if ( verbose ) {
spinner.succeed(`Enonce ${ enonce.name } successfully ${ publish ? 'published' : 'unpublished' }`); spinner.succeed(`Assignment ${ assignment.name } successfully ${ publish ? 'published' : 'unpublished' }`);
} }
return; return;
} catch ( error ) { } catch ( error ) {
if ( verbose ) { if ( verbose ) {
if ( error instanceof AxiosError && error.response ) { if ( error instanceof AxiosError && error.response ) {
spinner.fail(`Enonce visibility change error: ${ error.response.statusText }`); spinner.fail(`Assignment visibility change error: ${ error.response.statusText }`);
} else { } else {
spinner.fail(`Enonce visibility change error: unknown error`); spinner.fail(`Assignment visibility change error: unknown error`);
} }
} }
......
Subproject commit f33e4e0c7b34f9060e8995550920d25cd3e73c40 Subproject commit 8d7e3ca0cca10e874ac48e19e47da8a1491ccba7
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment