diff --git a/ExpressAPI/assets/assignment_gitlab_ci.yml b/ExpressAPI/assets/assignment_gitlab_ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ad20a046e037406773064a11f934ebcf69e42f77
--- /dev/null
+++ b/ExpressAPI/assets/assignment_gitlab_ci.yml
@@ -0,0 +1,26 @@
+###################################################################################################################
+# DO NOT MODIFY THIS FILE
+# This file is the ci/cd pipeline that will be used to test your assignment
+###################################################################################################################
+
+variables:
+    GIT_SUBMODULE_STRATEGY: recursive
+    GIT_SUBMODULE_FORCE_HTTPS: "true"
+    DOCKER_HOST: tcp://docker:2375
+    DOCKER_TLS_CERTDIR:
+    DOCKER_DRIVER: overlay2
+
+stages:
+    - dojo
+
+dojo:
+    stage: dojo
+    tags:
+        - dojo_assignment
+    services:
+        - docker:dind
+    image:
+        name: dojohesso/dojo_assignment_checker:latest
+    script:
+        - dojo_assignment_checker
+    allow_failure: false
\ No newline at end of file
diff --git a/ExpressAPI/package-lock.json b/ExpressAPI/package-lock.json
index 2a4fa5581141b6bddd9339f35135f03ed768b67c..8f963c7eac7601ac218372bff36cd4a480d1dc0e 100644
--- a/ExpressAPI/package-lock.json
+++ b/ExpressAPI/package-lock.json
@@ -16,6 +16,7 @@
                 "compression": "^1.7.4",
                 "cors": "^2.8.5",
                 "dotenv": "^16.3.1",
+                "dotenv-expand": "^10.0.0",
                 "express": "^4.18.2",
                 "express-validator": "^7.0.1",
                 "form-data": "^4.0.0",
@@ -1623,6 +1624,14 @@
                 "url": "https://github.com/motdotla/dotenv?sponsor=1"
             }
         },
+        "node_modules/dotenv-expand": {
+            "version": "10.0.0",
+            "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz",
+            "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==",
+            "engines": {
+                "node": ">=12"
+            }
+        },
         "node_modules/dotenv-vault": {
             "version": "1.25.0",
             "resolved": "https://registry.npmjs.org/dotenv-vault/-/dotenv-vault-1.25.0.tgz",
diff --git a/ExpressAPI/package.json b/ExpressAPI/package.json
index 83a0dd6c7453a2906dc2b2b6f0915ed64602a7fb..c251fe54ccd93983fb524e22c7c20c6ff99e0388 100644
--- a/ExpressAPI/package.json
+++ b/ExpressAPI/package.json
@@ -28,6 +28,7 @@
         "compression"      : "^1.7.4",
         "cors"             : "^2.8.5",
         "dotenv"           : "^16.3.1",
+        "dotenv-expand"    : "^10.0.0",
         "express"          : "^4.18.2",
         "express-validator": "^7.0.1",
         "form-data"        : "^4.0.0",
diff --git a/ExpressAPI/src/app.ts b/ExpressAPI/src/app.ts
index 1250af3da79a7764038ae9c16bc9934e2a4a53ed..bfbc0494636c237af8dae3185fc2670eb0c51b67 100644
--- a/ExpressAPI/src/app.ts
+++ b/ExpressAPI/src/app.ts
@@ -3,10 +3,12 @@
 const path = require('node:path');
 
 if ( process.env.NODE_ENV && process.env.NODE_ENV === 'production' ) {
-    require('dotenv').config();
+    const myEnv = require('dotenv').config();
+    require('dotenv-expand').expand(myEnv);
 } else {
     require('dotenv').config({ path: path.join(__dirname, '../.env.keys') });
-    require('dotenv').config({ DOTENV_KEY: process.env.DOTENV_KEY_DEVELOPMENT });
+    const myEnv = require('dotenv').config({ DOTENV_KEY: process.env.DOTENV_KEY_DEVELOPMENT });
+    require('dotenv-expand').expand(myEnv);
 }
 
 require('./shared/helpers/TypeScriptExtensions'); // ATTENTION : This line MUST be after the dotenv.config() calls
diff --git a/ExpressAPI/src/config/Config.ts b/ExpressAPI/src/config/Config.ts
index 49161651818ac940bc706b709375255564acaaff..574f9b2ea0e987c896a5075d4ac6dac7f7dbac55 100644
--- a/ExpressAPI/src/config/Config.ts
+++ b/ExpressAPI/src/config/Config.ts
@@ -24,7 +24,7 @@ class Config {
     };
 
     public readonly gitlab: {
-        apiURL: string; urls: Array<string>; account: { id: number; username: string; token: string; }; group: { root: number; templates: number; assignments: number; exercises: number; }, badges: { pipeline: ConfigGitlabBadge }
+        urls: Array<string>; account: { id: number; username: string; token: string; }; group: { root: number; templates: number; assignments: number; exercises: number; }, badges: { pipeline: ConfigGitlabBadge }
     };
 
     public readonly assignment: {
@@ -55,7 +55,6 @@ class Config {
         };
 
         this.gitlab = {
-            apiURL : process.env.GITLAB_API_URL || '',
             urls   : JSON5.parse(process.env.GITLAB_URLS || '[]'),
             account: {
                 id      : Number(process.env.GITLAB_DOJO_ACCOUNT_ID || 0),
diff --git a/ExpressAPI/src/managers/GitlabManager.ts b/ExpressAPI/src/managers/GitlabManager.ts
index fc80949e8f8a4878cf3d891a3467c907ecd09c2a..fca3977ce07f14eada038511f9106ccf539fba0a 100644
--- a/ExpressAPI/src/managers/GitlabManager.ts
+++ b/ExpressAPI/src/managers/GitlabManager.ts
@@ -11,11 +11,12 @@ import parseLinkHeader   from 'parse-link-header';
 import GitlabFile        from '../shared/types/Gitlab/GitlabFile';
 import express           from 'express';
 import GitlabRoute       from '../shared/types/Gitlab/GitlabRoute';
+import SharedConfig      from '../shared/config/SharedConfig';
 
 
 class GitlabManager {
     private getApiUrl(route: GitlabRoute): string {
-        return `${ Config.gitlab.apiURL }${ route }`;
+        return `${ SharedConfig.gitlab.apiURL }${ route }`;
     }
 
     public async getUserById(id: number): Promise<GitlabUser | undefined> {
diff --git a/ExpressAPI/src/managers/HttpManager.ts b/ExpressAPI/src/managers/HttpManager.ts
index 80da4d4ec8abf644fa67a3bc6503f462e7625a07..6d04c68f0b0536bfa9bead9a6e78978bf5946c9b 100644
--- a/ExpressAPI/src/managers/HttpManager.ts
+++ b/ExpressAPI/src/managers/HttpManager.ts
@@ -2,6 +2,7 @@ import axios, { AxiosError, AxiosRequestHeaders } from 'axios';
 import Config                                     from '../config/Config';
 import FormData                                   from 'form-data';
 import logger                                     from '../shared/logging/WinstonLogger';
+import SharedConfig                               from '../shared/config/SharedConfig';
 
 
 class HttpManager {
@@ -16,7 +17,7 @@ class HttpManager {
                 config.headers = { ...config.headers, ...(config.data as FormData).getHeaders() } as AxiosRequestHeaders;
             }
 
-            if ( config.url && config.url.indexOf(Config.gitlab.apiURL) !== -1 ) {
+            if ( config.url && config.url.indexOf(SharedConfig.gitlab.apiURL) !== -1 ) {
                 config.headers['PRIVATE-TOKEN'] = Config.gitlab.account.token;
             }
 
diff --git a/ExpressAPI/src/routes/AssignmentRoutes.ts b/ExpressAPI/src/routes/AssignmentRoutes.ts
index af42e42fe67513b9c926e4c9de601aaed55b2e6a..0e8926263d792b77a20c60ca36b1c64134a5f3d5 100644
--- a/ExpressAPI/src/routes/AssignmentRoutes.ts
+++ b/ExpressAPI/src/routes/AssignmentRoutes.ts
@@ -20,16 +20,26 @@ import db                             from '../helpers/DatabaseHelper';
 import { Assignment }                 from '../types/DatabaseTypes';
 import AssignmentManager              from '../managers/AssignmentManager';
 import GitlabVisibility               from '../shared/types/Gitlab/GitlabVisibility';
+import fs                             from 'fs';
+import path                           from 'path';
+import SharedAssignmentHelper         from '../shared/helpers/Dojo/SharedAssignmentHelper';
 
 
 class AssignmentRoutes implements RoutesManager {
     private readonly assignmentValidator: ExpressValidator.Schema = {
-        name       : {
-            trim: true, notEmpty: true
-        }, members : {
-            trim: true, notEmpty: true, customSanitizer: DojoValidators.jsonSanitizer
-        }, template: {
-            trim: true, custom: DojoValidators.templateUrlValidator, customSanitizer: DojoValidators.templateUrlSanitizer
+        name    : {
+            trim    : true,
+            notEmpty: true
+        },
+        members : {
+            trim           : true,
+            notEmpty       : true,
+            customSanitizer: DojoValidators.jsonSanitizer
+        },
+        template: {
+            trim           : true,
+            custom         : DojoValidators.templateUrlValidator,
+            customSanitizer: DojoValidators.templateUrlSanitizer
         }
     };
 
@@ -78,6 +88,8 @@ class AssignmentRoutes implements RoutesManager {
             repository = await GitlabManager.createRepository(params.name, Config.assignment.default.description.replace('{{ASSIGNMENT_NAME}}', params.name), Config.assignment.default.visibility, Config.assignment.default.initReadme, Config.gitlab.group.assignments, Config.assignment.default.sharedRunnersEnabled, Config.assignment.default.wikiEnabled, params.template);
 
             await GitlabManager.protectBranch(repository.id, '*', true, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER);
+
+            await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status');
         } catch ( error ) {
             if ( error instanceof AxiosError ) {
                 if ( error.response?.data.message.name && error.response.data.message.name == 'has already been taken' ) {
@@ -91,6 +103,18 @@ class AssignmentRoutes implements RoutesManager {
             return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
         }
 
+        try {
+            await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/assignment_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)');
+        } catch ( error ) {
+            logger.error(error);
+
+            if ( error instanceof AxiosError ) {
+                return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send();
+            }
+
+            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
+        }
+
         try {
             await Promise.all(params.members.map(member => member.id).map(async (memberId: number): Promise<GitlabMember | false> => {
                 try {
@@ -102,12 +126,20 @@ class AssignmentRoutes implements RoutesManager {
 
             const assignment: Assignment = await db.assignment.create({
                                                                           data: {
-                                                                              name: repository.name, gitlabId: repository.id, gitlabLink: repository.web_url, gitlabCreationInfo: repository as unknown as Prisma.JsonObject, gitlabLastInfo: repository as unknown as Prisma.JsonObject, gitlabLastInfoDate: new Date(), staff: {
+                                                                              name              : repository.name,
+                                                                              gitlabId          : repository.id,
+                                                                              gitlabLink        : repository.web_url,
+                                                                              gitlabCreationInfo: repository as unknown as Prisma.JsonObject,
+                                                                              gitlabLastInfo    : repository as unknown as Prisma.JsonObject,
+                                                                              gitlabLastInfoDate: new Date(),
+                                                                              staff             : {
                                                                                   connectOrCreate: [ ...params.members.map(gitlabUser => {
                                                                                       return {
-                                                                                          create  : {
-                                                                                              gitlabId: gitlabUser.id, firstname: gitlabUser.name
-                                                                                          }, where: {
+                                                                                          create: {
+                                                                                              gitlabId : gitlabUser.id,
+                                                                                              firstname: gitlabUser.name
+                                                                                          },
+                                                                                          where : {
                                                                                               gitlabId: gitlabUser.id
                                                                                           }
                                                                                       };
@@ -129,15 +161,23 @@ class AssignmentRoutes implements RoutesManager {
 
     private changeAssignmentPublishedStatus(publish: boolean): (req: express.Request, res: express.Response) => Promise<void> {
         return async (req: express.Request, res: express.Response): Promise<void> => {
+            if ( publish ) {
+                const isPublishable = await SharedAssignmentHelper.isPublishable(req.boundParams.assignment!.gitlabId);
+                if ( !isPublishable.isPublishable ) {
+                    return req.session.sendResponse(res, StatusCodes.BAD_REQUEST, { lastPipeline: isPublishable.lastPipeline }, isPublishable.status?.message, isPublishable.status?.code);
+                }
+            }
+
             try {
                 await GitlabManager.changeRepositoryVisibility(req.boundParams.assignment!.gitlabId, publish ? GitlabVisibility.INTERNAL : GitlabVisibility.PRIVATE);
 
                 await db.assignment.update({
-                                               where  : {
+                                               where: {
                                                    name: req.boundParams.assignment!.name
-                                               }, data: {
-                        published: publish
-                    }
+                                               },
+                                               data : {
+                                                   published: publish
+                                               }
                                            });
 
                 req.session.sendResponse(res, StatusCodes.OK);
diff --git a/ExpressAPI/src/shared b/ExpressAPI/src/shared
index 8424367748a6fc47f8da10b85e7663f3f7d07620..efe1bf313f57d1826faf935c183d37a0835f8c2d 160000
--- a/ExpressAPI/src/shared
+++ b/ExpressAPI/src/shared
@@ -1 +1 @@
-Subproject commit 8424367748a6fc47f8da10b85e7663f3f7d07620
+Subproject commit efe1bf313f57d1826faf935c183d37a0835f8c2d