Переглянути джерело

fix(cli): load .env file on each access to getEnv to ensure updated values

Nicolas Meienberger 1 рік тому
батько
коміт
26bb80e743

+ 13 - 17
packages/cli/src/executors/app/app.executors.ts

@@ -7,19 +7,9 @@ import { copyDataDir, generateEnvFile } from './app.helpers';
 import { fileLogger } from '@/utils/logger/file-logger';
 
 export class AppExecutors {
-  private readonly rootFolderHost: string;
-
-  private readonly storagePath: string;
-
-  private readonly appsRepoId: string;
-
   private readonly logger;
 
   constructor() {
-    const { rootFolderHost, storagePath, appsRepoId } = getEnv();
-    this.rootFolderHost = rootFolderHost;
-    this.storagePath = storagePath;
-    this.appsRepoId = appsRepoId;
     this.logger = fileLogger;
   }
 
@@ -33,10 +23,12 @@ export class AppExecutors {
   };
 
   private getAppPaths = (appId: string) => {
-    const appDataDirPath = path.join(this.storagePath, 'app-data', appId);
-    const appDirPath = path.join(this.rootFolderHost, 'apps', appId);
+    const { rootFolderHost, storagePath, appsRepoId } = getEnv();
+
+    const appDataDirPath = path.join(storagePath, 'app-data', appId);
+    const appDirPath = path.join(rootFolderHost, 'apps', appId);
     const configJsonPath = path.join(appDirPath, 'config.json');
-    const repoPath = path.join(this.rootFolderHost, 'repos', this.appsRepoId, 'apps', appId);
+    const repoPath = path.join(rootFolderHost, 'repos', appsRepoId, 'apps', appId);
 
     return { appDataDirPath, appDirPath, configJsonPath, repoPath };
   };
@@ -69,8 +61,10 @@ export class AppExecutors {
    * @param {string} appId - App id
    */
   private ensureAppDir = async (appId: string) => {
+    const { rootFolderHost } = getEnv();
+
     const { appDirPath, repoPath } = this.getAppPaths(appId);
-    const dockerFilePath = path.join(this.rootFolderHost, 'apps', appId, 'docker-compose.yml');
+    const dockerFilePath = path.join(rootFolderHost, 'apps', appId, 'docker-compose.yml');
 
     if (!(await pathExists(dockerFilePath))) {
       // delete eventual app folder if exists
@@ -90,15 +84,17 @@ export class AppExecutors {
    */
   public installApp = async (appId: string, config: Record<string, unknown>) => {
     try {
+      const { rootFolderHost, appsRepoId } = getEnv();
+
       const { appDirPath, repoPath, appDataDirPath } = this.getAppPaths(appId);
       this.logger.info(`Installing app ${appId}`);
 
       // Check if app exists in repo
-      const apps = await fs.promises.readdir(path.join(this.rootFolderHost, 'repos', this.appsRepoId, 'apps'));
+      const apps = await fs.promises.readdir(path.join(rootFolderHost, 'repos', appsRepoId, 'apps'));
 
       if (!apps.includes(appId)) {
-        this.logger.error(`App ${appId} not found in repo ${this.appsRepoId}`);
-        return { success: false, message: `App ${appId} not found in repo ${this.appsRepoId}` };
+        this.logger.error(`App ${appId} not found in repo ${appsRepoId}`);
+        return { success: false, message: `App ${appId} not found in repo ${appsRepoId}` };
       }
 
       // Delete app folder if exists

+ 6 - 6
packages/cli/src/executors/repo/repo.executors.ts

@@ -9,13 +9,9 @@ import { fileLogger } from '@/utils/logger/file-logger';
 const execAsync = promisify(exec);
 
 export class RepoExecutors {
-  private readonly rootFolderHost: string;
-
   private readonly logger;
 
   constructor() {
-    const { rootFolderHost } = getEnv();
-    this.rootFolderHost = rootFolderHost;
     this.logger = fileLogger;
   }
 
@@ -39,8 +35,10 @@ export class RepoExecutors {
    */
   public cloneRepo = async (repoUrl: string) => {
     try {
+      const { rootFolderHost } = getEnv();
+
       const repoHash = getRepoHash(repoUrl);
-      const repoPath = path.join(this.rootFolderHost, 'repos', repoHash);
+      const repoPath = path.join(rootFolderHost, 'repos', repoHash);
 
       if (await pathExists(repoPath)) {
         this.logger.info(`Repo ${repoUrl} already exists`);
@@ -65,8 +63,10 @@ export class RepoExecutors {
    */
   public pullRepo = async (repoUrl: string) => {
     try {
+      const { rootFolderHost } = getEnv();
+
       const repoHash = getRepoHash(repoUrl);
-      const repoPath = path.join(this.rootFolderHost, 'repos', repoHash);
+      const repoPath = path.join(rootFolderHost, 'repos', repoHash);
 
       if (!(await pathExists(repoPath))) {
         this.logger.info(`Repo ${repoUrl} does not exist`);

+ 8 - 0
packages/cli/src/executors/system/system.executors.ts

@@ -8,6 +8,7 @@ import { exec, spawn } from 'child_process';
 import si from 'systeminformation';
 import { Stream } from 'stream';
 import { promisify } from 'util';
+import dotenv from 'dotenv';
 import { AppExecutors } from '../app/app.executors';
 import { copySystemFiles, generateSystemEnvFile, generateTlsCertificates } from './system.helpers';
 import { TerminalSpinner } from '@/utils/logger/terminal-spinner';
@@ -60,6 +61,7 @@ export class SystemExecutors {
       path.join(rootFolderHost, 'state'),
       path.join(rootFolderHost, '.env'),
       path.join(rootFolderHost, 'docker-compose.yml'),
+      path.join(rootFolderHost, 'VERSION'),
     ];
 
     // Give permission to read and write to all files and folders for the current user
@@ -138,13 +140,19 @@ export class SystemExecutors {
       spinner.start();
       spinner.setMessage('Copying system files...');
       await copySystemFiles();
+
       spinner.done('System files copied');
 
+      await this.ensureFilePermissions(this.rootFolder);
+
       spinner.setMessage('Generating system env file...');
       spinner.start();
       const envMap = await generateSystemEnvFile();
       spinner.done('System env file generated');
 
+      // Reload env variables after generating the env file
+      dotenv.config({ path: this.envFile, override: true });
+
       // Stop and Remove container tipi if exists
       spinner.setMessage('Stopping and removing containers...');
       spinner.start();

+ 23 - 0
packages/cli/src/executors/system/system.helpers.ts

@@ -179,6 +179,29 @@ export const generateSystemEnvFile = async () => {
   return envMap;
 };
 
+/**
+ * Sets the value of an environment variable in the .env file
+ *
+ * @param {string} key - The key of the environment variable
+ * @param {string} value - The value of the environment variable
+ */
+export const setEnvVariable = async (key: EnvKeys, value: string) => {
+  const rootFolder = process.cwd();
+
+  const envFilePath = path.join(rootFolder, '.env');
+
+  if (!(await pathExists(envFilePath))) {
+    await fs.promises.writeFile(envFilePath, '');
+  }
+
+  const envFile = await fs.promises.readFile(envFilePath, 'utf-8');
+  const envMap: Map<EnvKeys, string> = envStringToMap(envFile);
+
+  envMap.set(key, value);
+
+  await fs.promises.writeFile(envFilePath, envMapToString(envMap));
+};
+
 /**
  * Copies the system files from the assets folder to the current working directory
  */

+ 2 - 2
packages/cli/src/utils/environment/environment.ts

@@ -2,9 +2,9 @@ import { z } from 'zod';
 import dotenv from 'dotenv';
 
 if (process.env.NODE_ENV === 'development') {
-  dotenv.config({ path: '.env.dev' });
+  dotenv.config({ path: '.env.dev', override: true });
 } else {
-  dotenv.config();
+  dotenv.config({ override: true });
 }
 
 const environmentSchema = z

+ 3 - 3
scripts/install.sh

@@ -177,12 +177,12 @@ if [[ "${UPDATE}" == "false" ]]; then
 fi
 
 curl --location "$URL" -o ./runtipi-cli
-sudo chmod +x ./runtipi-cli
+chmod +x ./runtipi-cli
 
 # Check if user is in docker group
 if ! groups | grep -q docker; then
-  echo "User is not in docker group. Please make sure your user is in the docker group and restart the script."
-  echo "You can add your user to the docker group by running: 'sudo usermod -aG docker $USER' and then exit the shell and restart the script."
+  echo "User is not in docker group. Please make sure your user is allowed to run docker commands and restart the script."
+  echo "See https://docs.docker.com/engine/install/linux-postinstall/ for more information."
   exit 1
 fi