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
No related branches found
No related tags found
No related merge requests found
Showing
with 1302 additions and 192 deletions
variables:
BIN_FOLDER: /binary
GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_FORCE_HTTPS: "true"
SECURE_FILES_DOWNLOAD_PATH: './'
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:
# 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}
# 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
- 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 stapler staple ${BIN_NAME}.pkg
#Clean folder
- 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:
- Build_stage
- Sign_stage
- build
- sign
- clean-same-release
- clean-dev-release
- upload
- release
build:
stage: Build_stage
build:test:
stage: build
tags:
- build
variables:
GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_FORCE_HTTPS: "true"
SECURE_FILES_DOWNLOAD_PATH: './'
image: node:latest
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 .env
# Install dependencies
- npm install
- npm run build
# Build binaries
- npx pkg . -t node18-macos-arm64 --output $BIN_FOLDER/macOS/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public
- npx pkg . -t node18-macos-x64 --output $BIN_FOLDER/macOS/x64/$BIN_NAME --no-bytecode --public-packages "*" --public
- npx pkg . -t node18-linuxstatic-arm64 --output $BIN_FOLDER/linux/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public
- npx pkg . -t node18-linuxstatic-x64 --output $BIN_FOLDER/linux/x64/$BIN_NAME --no-bytecode --public-packages "*" --public
- npx pkg . -t node18-win-arm64 --output $BIN_FOLDER/Windows/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public
- 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]
- !reference [.build_cli, script]
rules:
- if: '$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_COMMIT_BRANCH !~ /^v[0-9]+(\.[0-9]+)*$/'
build:version:
stage: build
tags:
- build
image: node:latest
script:
- apt update
- apt install -y jq
- !reference [.get_version, script]
- mkdir -p $ARTIFACTS_FOLDER
- echo $VERSION > $VERSION_FILE
- !reference [.build_cli, script]
artifacts:
untracked: true
paths:
- $BIN_FOLDER/*
- $ARTIFACTS_FOLDER/*
expire_in: 1 hour
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
sign_macos:
stage: Sign_stage
sign:macos:
stage: sign
tags:
- macos_signing
dependencies:
- build
- build:version
script:
- VERSION=$(cat $VERSION_FILE)
- security unlock-keychain -p $SIGN_KEYCHAIN_PASSWORD $SIGN_LOGIN_KEYCHAIN_PATH
- cd $BIN_FOLDER/macOS/arm64
- !reference [.macOS-sign_and_pkg, script]
- cd $BIN_FOLDER/macOS/x64
- !reference [.macOS-sign_and_pkg, script]
- cd $BIN_FOLDER_MACOS_ARM64
- !reference [.sign_macos, script]
- cd $BIN_FOLDER_MACOS_X64
- !reference [.sign_macos, script]
artifacts:
paths:
- $BIN_FOLDER/*
expire_in: 1 week
- $ARTIFACTS_FOLDER/*
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",
"version" : "1.0.1",
"version" : "2.0.0",
"main" : "dist/app.js",
"bin" : {
"dojo": "./dist/app.js"
......
import { Command } from 'commander';
import EnonceCommand from './enonce/EnonceCommand';
import SessionCommand from './session/SessionCommand';
import ExerciceCommand from './exercice/ExerciceCommand';
import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig';
import AssignmentCommand from './assignment/AssignmentCommand';
import ExerciseCommand from './exercise/ExerciseCommand';
class CommanderApp {
......@@ -12,7 +12,7 @@ class CommanderApp {
this.program
.name('dojo')
.description('CLI of the Dojo application')
.version('1.0.1')
.version('2.0.0')
.showHelpAfterError()
.configureHelp({
showGlobalOptions: true,
......@@ -32,8 +32,8 @@ class CommanderApp {
private registerCommands() {
SessionCommand.registerOnCommand(this.program);
EnonceCommand.registerOnCommand(this.program);
ExerciceCommand.registerOnCommand(this.program);
AssignmentCommand.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';
import GitlabManager from '../../managers/GitlabManager';
import GitlabUser from '../../shared/types/Gitlab/GitlabUser';
import DojoBackendManager from '../../managers/DojoBackendManager';
import Enonce from '../../sharedByClients/models/Enonce';
import Toolbox from '../../shared/helpers/Toolbox';
import AccessesHelper from '../../helpers/AccessesHelper';
import Assignment from '../../sharedByClients/models/Assignment';
class EnonceCreateCommand extends CommanderCommand {
class AssignmentCreateCommand extends CommanderCommand {
protected commandName: string = 'create';
protected defineCommand() {
this.command
.description('create a new repository for an enonce')
.requiredOption('-n, --name <name>', 'name of the enonce')
.description('create a new repository for an assignment')
.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('-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)')
......@@ -39,12 +39,12 @@ class EnonceCreateCommand extends CommanderCommand {
return;
}
const enonceGetSpinner: ora.Ora = ora('Checking enonce name availability').start();
if ( await DojoBackendManager.getEnonce(options.name) ) {
enonceGetSpinner.fail(`Enonce name "${ options.name }" is already taken. Please choose another one.`);
const assignmentGetSpinner: ora.Ora = ora('Checking assignment name availability').start();
if ( await DojoBackendManager.getAssignment(options.name) ) {
assignmentGetSpinner.fail(`Assignment name "${ options.name }" is already taken. Please choose another one.`);
return;
}
enonceGetSpinner.succeed(`Enonce name "${ options.name }" is available`);
assignmentGetSpinner.succeed(`Assignment name "${ options.name }" is available`);
if ( options.template ) {
templateIdOrNamespace = options.template;
......@@ -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 {
const enonce: Enonce = await DojoBackendManager.createEnonce(options.name, members, templateIdOrNamespace);
const assignment: Assignment = await DojoBackendManager.createAssignment(options.name, members, templateIdOrNamespace);
const oraInfo = (message: string) => {
ora({
......@@ -73,10 +73,10 @@ class EnonceCreateCommand extends CommanderCommand {
}).start().info();
};
oraInfo(`${ chalk.magenta('Name:') } ${ enonce.name }`);
oraInfo(`${ chalk.magenta('Web URL:') } ${ enonce.gitlabCreationInfo.web_url }`);
oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ enonce.gitlabCreationInfo.http_url_to_repo }`);
oraInfo(`${ chalk.magenta('SSH Repo:') } ${ enonce.gitlabCreationInfo.ssh_url_to_repo }`);
oraInfo(`${ chalk.magenta('Name:') } ${ assignment.name }`);
oraInfo(`${ chalk.magenta('Web URL:') } ${ assignment.gitlabCreationInfo.web_url }`);
oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ assignment.gitlabCreationInfo.http_url_to_repo }`);
oraInfo(`${ chalk.magenta('SSH Repo:') } ${ assignment.gitlabCreationInfo.ssh_url_to_repo }`);
} catch ( error ) {
return;
}
......@@ -85,4 +85,4 @@ class EnonceCreateCommand extends CommanderCommand {
}
export default new EnonceCreateCommand();
\ No newline at end of file
export default new AssignmentCreateCommand();
\ 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 inquirer from 'inquirer';
import Enonce from '../../sharedByClients/models/Enonce';
import chalk from 'chalk';
import SessionManager from '../../managers/SessionManager';
import ora from 'ora';
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 defineCommand() {
this.command
.description('publish an enonce')
.argument('<name or url>', 'name or url (http/s or ssh) of the enonce')
.description('publish an assignment')
.argument('<name or url>', 'name or url (http/s or ssh) of the assignment')
.option('-f, --force', 'don\'t ask for confirmation')
.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 ) {
options.force = (await inquirer.prompt({
type : 'confirm',
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;
}
......@@ -31,7 +31,7 @@ abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand {
return;
}
let enonce!: Enonce | undefined;
let assignment!: Assignment | undefined;
{
console.log(chalk.cyan('Please wait while we verify and retrieve data...'));
......@@ -40,38 +40,38 @@ abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand {
return;
}
ora('Checking enonce:').start().info();
ora('Checking assignment:').start().info();
ora({
text : enonceNameOrUrl,
text : assignmentNameOrUrl,
indent: 4
}).start().info();
const enonceGetSpinner: ora.Ora = ora({
text : 'Checking if enonce exists',
const assignmentGetSpinner: ora.Ora = ora({
text : 'Checking if assignment exists',
indent: 8
}).start();
enonce = await DojoBackendManager.getEnonce(enonceNameOrUrl);
if ( !enonce ) {
enonceGetSpinner.fail(`The enonce doesn't exists`);
assignment = await DojoBackendManager.getAssignment(assignmentNameOrUrl);
if ( !assignment ) {
assignmentGetSpinner.fail(`The assignment doesn't exists`);
return;
}
enonceGetSpinner.succeed(`The enonce exists`);
assignmentGetSpinner.succeed(`The assignment exists`);
const enonceCheckAccessSpinner: ora.Ora = ora({
const assignmentCheckAccessSpinner: ora.Ora = ora({
text : 'Checking accesses',
indent: 8
}).start();
if ( !enonce.staff ) {
enonceCheckAccessSpinner.fail(`You are not in the staff of this enonce`);
if ( !assignment.staff ) {
assignmentCheckAccessSpinner.fail(`You are not in the staff of this assignment`);
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 {
await DojoBackendManager.changeEnoncePublishedStatus(enonce, this.publish);
await DojoBackendManager.changeAssignmentPublishedStatus(assignment, this.publish);
} catch ( error ) {
return;
}
......@@ -80,4 +80,4 @@ abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand {
}
export default EnoncePublishUnpublishCommandBase;
\ No newline at end of file
export default AssignmentPublishUnpublishCommandBase;
\ 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 ExerciceCreateCommand from './ExerciceCreateCommand';
import ExerciceRunCommand from './ExerciceRunCommand';
import ExerciseCreateCommand from './ExerciseCreateCommand';
import ExerciseRunCommand from './ExerciseRunCommand';
class ExerciceCommand extends CommanderCommand {
protected commandName: string = 'exercice';
class ExerciseCommand extends CommanderCommand {
protected commandName: string = 'exercise';
protected defineCommand() {
this.command
.description('manage an exercice');
.description('manage an exercise');
}
protected defineSubCommands() {
ExerciceCreateCommand.registerOnCommand(this.command);
ExerciceRunCommand.registerOnCommand(this.command);
ExerciseCreateCommand.registerOnCommand(this.command);
ExerciseRunCommand.registerOnCommand(this.command);
}
protected async commandAction(options: any): Promise<void> { }
}
export default new ExerciceCommand();
\ No newline at end of file
export default new ExerciseCommand();
\ No newline at end of file
......@@ -2,20 +2,20 @@ import CommanderCommand from '../CommanderCommand';
import chalk from 'chalk';
import GitlabManager from '../../managers/GitlabManager';
import GitlabUser from '../../shared/types/Gitlab/GitlabUser';
import Enonce from '../../sharedByClients/models/Enonce';
import ora from 'ora';
import DojoBackendManager from '../../managers/DojoBackendManager';
import Exercice from '../../sharedByClients/models/Exercice';
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 defineCommand() {
this.command
.description('create a new exercice from an enonce')
.requiredOption('-e, --enonce <value>', 'enonce source (Dojo enonce ID, Dojo enonce name or Gitlab enonce URL)')
.description('create a new exercise from an assignment')
.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('-u, --members_username <usernames...>', 'list of gitlab members username (group\'s student) to add to the repository')
.action(this.commandAction.bind(this));
......@@ -23,7 +23,7 @@ class ExerciceCreateCommand extends CommanderCommand {
protected async commandAction(options: any): Promise<void> {
let members!: Array<GitlabUser> | false;
let enonce!: Enonce | undefined;
let assignment!: Assignment | undefined;
// Check access and retrieve data
{
......@@ -38,35 +38,35 @@ class ExerciceCreateCommand extends CommanderCommand {
return;
}
ora('Checking enonce:').start().info();
const enonceGetSpinner: ora.Ora = ora({
text : 'Checking if enonce exists',
ora('Checking assignment:').start().info();
const assignmentGetSpinner: ora.Ora = ora({
text : 'Checking if assignment exists',
indent: 4
}).start();
enonce = await DojoBackendManager.getEnonce(options.enonce);
if ( !enonce ) {
enonceGetSpinner.fail(`Enonce "${ options.enonce }" doesn't exists`);
assignment = await DojoBackendManager.getAssignment(options.assignment);
if ( !assignment ) {
assignmentGetSpinner.fail(`Assignment "${ options.assignment }" doesn't exists`);
return;
}
enonceGetSpinner.succeed(`Enonce "${ options.enonce }" exists`);
assignmentGetSpinner.succeed(`Assignment "${ options.assignment }" exists`);
const enoncePublishedSpinner: ora.Ora = ora({
text : 'Checking if enonce is published',
const assignmentPublishedSpinner: ora.Ora = ora({
text : 'Checking if assignment is published',
indent: 4
}).start();
if ( !enonce.published ) {
enoncePublishedSpinner.fail(`Enonce "${ enonce.name }" isn't published`);
if ( !assignment.published ) {
assignmentPublishedSpinner.fail(`Assignment "${ assignment.name }" isn't published`);
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 {
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) => {
ora({
......@@ -75,11 +75,11 @@ class ExerciceCreateCommand extends CommanderCommand {
}).start().info();
};
oraInfo(`${ chalk.magenta('Id:') } ${ exercice.id }`);
oraInfo(`${ chalk.magenta('Name:') } ${ exercice.name }`);
oraInfo(`${ chalk.magenta('Web URL:') } ${ exercice.gitlabCreationInfo.web_url }`);
oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ exercice.gitlabCreationInfo.http_url_to_repo }`);
oraInfo(`${ chalk.magenta('SSH Repo:') } ${ exercice.gitlabCreationInfo.ssh_url_to_repo }`);
oraInfo(`${ chalk.magenta('Id:') } ${ exercise.id }`);
oraInfo(`${ chalk.magenta('Name:') } ${ exercise.name }`);
oraInfo(`${ chalk.magenta('Web URL:') } ${ exercise.gitlabCreationInfo.web_url }`);
oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ exercise.gitlabCreationInfo.http_url_to_repo }`);
oraInfo(`${ chalk.magenta('SSH Repo:') } ${ exercise.gitlabCreationInfo.ssh_url_to_repo }`);
} catch ( error ) {
return;
}
......@@ -88,4 +88,4 @@ class ExerciceCreateCommand extends CommanderCommand {
}
export default new ExerciceCreateCommand();
\ No newline at end of file
export default new ExerciseCreateCommand();
\ No newline at end of file
......@@ -4,29 +4,29 @@ import fs from 'node:fs';
import ora from 'ora';
import util from 'util';
import { exec } from 'child_process';
import SharedEnonceHelper from '../../shared/helpers/Dojo/SharedEnonceHelper';
import EnonceFile from '../../shared/types/Dojo/EnonceFile';
import chalk from 'chalk';
import ExerciceDockerCompose from '../../sharedByClients/helpers/Dojo/ExerciceDockerCompose';
import * as os from 'os';
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 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);
class ExerciceRunCommand extends CommanderCommand {
class ExerciseRunCommand extends CommanderCommand {
protected commandName: string = 'run';
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 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() }`;
......@@ -34,8 +34,8 @@ class ExerciceRunCommand extends CommanderCommand {
protected defineCommand() {
this.command
.description('locally run an exercice')
.option('-p, --path <value>', 'exercice path', Config.folders.defaultLocalExercice)
.description('locally run an exercise')
.option('-p, --path <value>', 'exercise path', Config.folders.defaultLocalExercise)
.action(this.commandAction.bind(this));
}
......@@ -47,61 +47,61 @@ class ExerciceRunCommand extends CommanderCommand {
}
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 exerciceDockerCompose: ExerciceDockerCompose;
let exerciceResultsValidation: ExerciceResultsValidation;
let assignmentFile: AssignmentFile;
let exerciseDockerCompose: ExerciseDockerCompose;
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...'));
// Create result temp folder
fs.mkdirSync(this.folderResultsVolume, { recursive: true });
fs.mkdirSync(this.folderResultsDojo, { recursive: true });
fs.mkdirSync(this.folderResultsExercice, { recursive: true });
fs.mkdirSync(this.folderResultsExercise, { recursive: true });
ora({
text : `Checking exercice content:`,
text : `Checking exercise content:`,
indent: 4
}).start().info();
// Exercice folder
// Exercise folder
{
const spinner: ora.Ora = ora({
text : `Checking exercice folder`,
text : `Checking exercise folder`,
indent: 8
}).start();
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 ) {
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;
}
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({
text : `Checking ${ Config.enonce.filename } file`,
text : `Checking ${ Config.assignment.filename } file`,
indent: 8
}).start();
const validationResults = SharedEnonceHelper.validateDescriptionFile(`${ options.path }/${ Config.enonce.filename }`);
const validationResults = SharedAssignmentHelper.validateDescriptionFile(`${ options.path }/${ Config.assignment.filename }`);
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;
} 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
......@@ -112,7 +112,7 @@ class ExerciceRunCommand extends CommanderCommand {
}).start();
try {
await execAsync(`cd "${ Config.folders.defaultLocalExercice }";docker ps`);
await execAsync(`cd "${ Config.folders.defaultLocalExercise }";docker ps`);
} catch ( error ) {
spinner.fail(`The Docker deamon is not running`);
return;
......@@ -125,27 +125,27 @@ class ExerciceRunCommand extends CommanderCommand {
// 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);
exerciceDockerCompose = new ExerciceDockerCompose(this.projectName, enonceFile, localExercicePath, [ composeOverridePath ]);
exerciseDockerCompose = new ExerciseDockerCompose(this.projectName, assignmentFile, localExercisePath, [ composeOverridePath ]);
try {
await new Promise<void>((resolve, reject) => {
let spinner: ora.Ora;
exerciceDockerCompose.events.on('step', (name: string, message: string) => {
exerciseDockerCompose.events.on('step', (name: string, message: string) => {
spinner = ora({
text : message,
indent: 4
}).start();
});
exerciceDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
exerciseDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
if ( error ) {
spinner.fail(message);
} else {
......@@ -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();
});
exerciceDockerCompose.run(true);
exerciseDockerCompose.run(true);
});
} catch ( error ) { }
fs.rmSync(composeOverridePath);
fs.writeFileSync(this.fileComposeLogs, exerciceDockerCompose.allLogs);
fs.writeFileSync(this.fileComposeLogs, exerciseDockerCompose.allLogs);
if ( !exerciceDockerCompose.success ) {
if ( !exerciseDockerCompose.success ) {
this.displayExecutionLogs();
return;
}
......@@ -175,20 +175,20 @@ class ExerciceRunCommand extends CommanderCommand {
{
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 {
await new Promise<void>((resolve, reject) => {
let spinner: ora.Ora;
exerciceResultsValidation.events.on('step', (name: string, message: string) => {
exerciseResultsValidation.events.on('step', (name: string, message: string) => {
spinner = ora({
text : message,
indent: 4
}).start();
});
exerciceResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => {
exerciseResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => {
if ( error ) {
if ( stepName == 'CHECK_SIZE' ) {
spinner.warn(message);
......@@ -200,11 +200,11 @@ class ExerciceRunCommand extends CommanderCommand {
}
});
exerciceResultsValidation.events.on('finished', (success: boolean, exitCode: number) => {
success || exitCode == ExerciceCheckerError.EXERCICE_RESULTS_FOLDER_TOO_BIG ? resolve() : reject();
exerciseResultsValidation.events.on('finished', (success: boolean, exitCode: number) => {
success || exitCode == ExerciseCheckerError.EXERCISE_RESULTS_FOLDER_TOO_BIG ? resolve() : reject();
});
exerciceResultsValidation.run();
exerciseResultsValidation.run();
});
} catch ( error ) {
this.displayExecutionLogs();
......@@ -215,7 +215,7 @@ class ExerciceRunCommand extends CommanderCommand {
// Step 4: Display results + Volume location
{
ClientsSharedExerciceHelper.displayExecutionResults(exerciceResultsValidation.exerciceResults!, exerciceDockerCompose.exitCode, {
ClientsSharedExerciseHelper.displayExecutionResults(exerciseResultsValidation.exerciseResults!, exerciseDockerCompose.exitCode, {
INFO : chalk.bold,
SUCCESS: chalk.green,
FAILURE: chalk.red
......@@ -225,4 +225,4 @@ class ExerciceRunCommand extends CommanderCommand {
}
export default new ExerciceRunCommand();
\ No newline at end of file
export default new ExerciseRunCommand();
\ No newline at end of file
......@@ -7,14 +7,14 @@ class Config {
};
public readonly folders: {
defaultLocalExercice: string
defaultLocalExercise: string
};
public enonce: {
public assignment: {
filename: string
};
public readonly exercice: {
public readonly exercise: {
neededFiles: Array<string>
};
......@@ -25,15 +25,15 @@ class Config {
};
this.folders = {
defaultLocalExercice: process.env.LOCAL_EXERCICE_DEFAULT_FOLDER || './'
defaultLocalExercise: process.env.LOCAL_EXERCISE_DEFAULT_FOLDER || './'
};
this.enonce = {
filename: process.env.ENONCE_FILENAME || ''
this.assignment = {
filename: process.env.ASSIGNMENT_FILENAME || ''
};
this.exercice = {
neededFiles: JSON.parse(process.env.EXERCICE_NEEDED_FILES || '[]')
this.exercise = {
neededFiles: JSON.parse(process.env.EXERCISE_NEEDED_FILES || '[]')
};
}
}
......
......@@ -2,11 +2,11 @@ import axios, { AxiosError } from 'axios';
import ora from 'ora';
import ApiRoute from '../sharedByClients/types/Dojo/ApiRoute';
import { StatusCodes } from 'http-status-codes';
import Enonce from '../sharedByClients/models/Enonce';
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 Assignment from '../sharedByClients/models/Assignment';
import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse';
import Exercise from '../sharedByClients/models/Exercise';
class DojoBackendManager {
......@@ -14,9 +14,9 @@ class DojoBackendManager {
return `${ ClientsSharedConfig.apiURL }${ route }`;
}
public async getEnonce(nameOrUrl: string): Promise<Enonce | undefined> {
public async getAssignment(nameOrUrl: string): Promise<Assignment | undefined> {
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 ) {
return undefined;
}
......@@ -59,21 +59,21 @@ class DojoBackendManager {
}
}
public async createEnonce(name: string, members: Array<GitlabUser>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Enonce> {
const spinner: ora.Ora = ora('Creating enonce...');
public async createAssignment(name: string, members: Array<GitlabUser>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Assignment> {
const spinner: ora.Ora = ora('Creating assignment...');
if ( verbose ) {
spinner.start();
}
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,
members: JSON.stringify(members)
}, templateIdOrNamespace ? { template: templateIdOrNamespace } : {}));
if ( verbose ) {
spinner.succeed(`Enonce successfully created`);
spinner.succeed(`Assignment successfully created`);
}
return response.data.data;
......@@ -82,13 +82,13 @@ class DojoBackendManager {
if ( error instanceof AxiosError ) {
if ( error.response ) {
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 {
spinner.fail(`Enonce creation error: ${ error.response.statusText }`);
spinner.fail(`Assignment creation error: ${ error.response.statusText }`);
}
}
} else {
spinner.fail(`Enonce creation error: unknown error`);
spinner.fail(`Assignment creation error: unknown error`);
}
}
......@@ -96,18 +96,18 @@ class DojoBackendManager {
}
}
public async createExercice(enonceName: string, members: Array<GitlabUser>, verbose: boolean = true): Promise<Exercice> {
const spinner: ora.Ora = ora('Creating exercice...');
public async createExercise(assignmentName: string, members: Array<GitlabUser>, verbose: boolean = true): Promise<Exercise> {
const spinner: ora.Ora = ora('Creating exercise...');
if ( verbose ) {
spinner.start();
}
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 ) {
spinner.succeed(`Exercice successfully created`);
spinner.succeed(`Exercise successfully created`);
}
return response.data.data;
......@@ -116,13 +116,13 @@ class DojoBackendManager {
if ( error instanceof AxiosError ) {
if ( error.response ) {
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 {
spinner.fail(`Exercice creation error: ${ error.response.statusText }`);
spinner.fail(`Exercise creation error: ${ error.response.statusText }`);
}
}
} else {
spinner.fail(`Exercice creation error: unknown error`);
spinner.fail(`Exercise creation error: unknown error`);
}
}
......@@ -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...');
if ( verbose ) {
......@@ -138,19 +138,19 @@ class DojoBackendManager {
}
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 ) {
spinner.succeed(`Enonce ${ enonce.name } successfully ${ publish ? 'published' : 'unpublished' }`);
spinner.succeed(`Assignment ${ assignment.name } successfully ${ publish ? 'published' : 'unpublished' }`);
}
return;
} catch ( error ) {
if ( verbose ) {
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 {
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 to comment