liaoch commited on
Commit
e0c0f79
·
1 Parent(s): cbb35e4

Fix Dockerfile npm permissions and mermaid-cli usage: Use npx instead of global install

Browse files
Files changed (2) hide show
  1. Dockerfile +5 -6
  2. mermaid_renderer.py +18 -28
Dockerfile CHANGED
@@ -66,12 +66,6 @@ ENV HOME=/home/user \
66
  # Set the working directory to the user's home directory
67
  WORKDIR $HOME/app
68
 
69
- # Install mermaid-cli in user directory to avoid permission issues
70
- # Use --unsafe-perm if needed for permissions during global install
71
- RUN mkdir -p $HOME/.npm-global && \
72
- npm config set prefix '$HOME/.npm-global' && \
73
- npm install -g @mermaid-js/mermaid-cli --unsafe-perm=true
74
-
75
  # Copy the requirements file first to leverage Docker cache
76
  # Copy with proper ownership
77
  COPY --chown=user requirements.txt $HOME/app/
@@ -85,6 +79,11 @@ RUN $HOME/app/venv/bin/pip install --no-cache-dir -r $HOME/app/requirements.txt
85
  # Copy the rest of the application code into the container with proper ownership
86
  COPY --chown=user . $HOME/app
87
 
 
 
 
 
 
88
  # Make port 7860 available to the world outside this container (required for Hugging Face Spaces)
89
  EXPOSE 7860
90
 
 
66
  # Set the working directory to the user's home directory
67
  WORKDIR $HOME/app
68
 
 
 
 
 
 
 
69
  # Copy the requirements file first to leverage Docker cache
70
  # Copy with proper ownership
71
  COPY --chown=user requirements.txt $HOME/app/
 
79
  # Copy the rest of the application code into the container with proper ownership
80
  COPY --chown=user . $HOME/app
81
 
82
+ # Install mermaid-cli locally in the project directory to avoid permission issues
83
+ # This will be used via npx which comes with npm
84
+ RUN npm init -y && \
85
+ npm install @mermaid-js/mermaid-cli
86
+
87
  # Make port 7860 available to the world outside this container (required for Hugging Face Spaces)
88
  EXPOSE 7860
89
 
mermaid_renderer.py CHANGED
@@ -17,7 +17,7 @@ class MermaidRenderer:
17
  self._check_dependencies()
18
 
19
  def _check_dependencies(self):
20
- """Check if Node.js and @mermaid-js/mermaid-cli are installed"""
21
  try:
22
  subprocess.run(["node", "--version"], capture_output=True, check=True, text=True)
23
  logging.info("Node.js found.")
@@ -27,23 +27,13 @@ class MermaidRenderer:
27
  # Consider raising an exception or handling this state in the Flask app.
28
  raise RuntimeError("Error: Node.js is not installed or not found in PATH.")
29
 
30
- # Check if @mermaid-js/mermaid-cli is installed globally
31
- # Note: Checking global npm packages can be slow and sometimes unreliable.
32
- # A better approach in production might be to ensure it's installed during deployment.
33
  try:
34
- result = subprocess.run(["mmdc", "--version"], capture_output=True, check=True, text=True)
35
- logging.info(f"@mermaid-js/mermaid-cli found: {result.stdout.strip()}")
36
- except (subprocess.SubprocessError, FileNotFoundError):
37
- logging.warning("@mermaid-js/mermaid-cli (mmdc) not found or failed to execute. Attempting installation...")
38
- # Attempt installation if not found (consider security implications)
39
- try:
40
- # Using '--unsafe-perm' might be needed in some environments, but use with caution.
41
- install_cmd = ["npm", "install", "-g", "@mermaid-js/mermaid-cli"]
42
- subprocess.run(install_cmd, check=True, capture_output=True, text=True)
43
- logging.info("@mermaid-js/mermaid-cli installed successfully via npm.")
44
- except subprocess.SubprocessError as install_error:
45
- logging.error(f"Failed to install @mermaid-js/mermaid-cli: {install_error.stderr}")
46
- raise RuntimeError("Error: @mermaid-js/mermaid-cli (mmdc) is not installed and automatic installation failed. Please install manually: npm install -g @mermaid-js/mermaid-cli")
47
 
48
  def render(self, mermaid_code, output_format="png", theme="default"):
49
  """
@@ -80,7 +70,7 @@ class MermaidRenderer:
80
  temp_input_file.close() # Close file before passing to subprocess
81
 
82
  cmd = [
83
- "mmdc",
84
  "-i", input_path,
85
  "-o", output_path,
86
  "-t", theme,
@@ -93,7 +83,7 @@ class MermaidRenderer:
93
  result = subprocess.run(cmd, check=True, capture_output=True, text=True)
94
  logging.info(f"mmdc execution successful. Output saved to: {output_path}")
95
  if result.stderr:
96
- logging.warning(f"mmdc stderr: {result.stderr}")
97
 
98
  # Return paths for Flask to handle; caller must delete files
99
  return output_path, input_path
@@ -105,14 +95,14 @@ class MermaidRenderer:
105
  temp_output_file.close()
106
  os.unlink(output_path)
107
  if os.path.exists(input_path): # Input file might already be closed/deleted
108
- os.unlink(input_path)
109
  raise RuntimeError(f"Error rendering diagram: {e.stderr or e}")
110
  except Exception as e:
111
- # Catch any other unexpected errors
112
- logging.error(f"Unexpected error during rendering: {e}")
113
- # Ensure cleanup
114
- temp_input_file.close()
115
- temp_output_file.close()
116
- if os.path.exists(input_path): os.unlink(input_path)
117
- if os.path.exists(output_path): os.unlink(output_path)
118
- raise # Re-raise the caught exception
 
17
  self._check_dependencies()
18
 
19
  def _check_dependencies(self):
20
+ """Check if Node.js and npx are installed"""
21
  try:
22
  subprocess.run(["node", "--version"], capture_output=True, check=True, text=True)
23
  logging.info("Node.js found.")
 
27
  # Consider raising an exception or handling this state in the Flask app.
28
  raise RuntimeError("Error: Node.js is not installed or not found in PATH.")
29
 
30
+ # Check if npx is available
 
 
31
  try:
32
+ subprocess.run(["npx", "--version"], capture_output=True, check=True, text=True)
33
+ logging.info("npx found.")
34
+ except (subprocess.SubprocessError, FileNotFoundError) as e:
35
+ logging.error(f"npx check failed: {e}")
36
+ raise RuntimeError("Error: npx is not installed or not found in PATH.")
 
 
 
 
 
 
 
 
37
 
38
  def render(self, mermaid_code, output_format="png", theme="default"):
39
  """
 
70
  temp_input_file.close() # Close file before passing to subprocess
71
 
72
  cmd = [
73
+ "npx", "@mermaid-js/mermaid-cli", "mmdc",
74
  "-i", input_path,
75
  "-o", output_path,
76
  "-t", theme,
 
83
  result = subprocess.run(cmd, check=True, capture_output=True, text=True)
84
  logging.info(f"mmdc execution successful. Output saved to: {output_path}")
85
  if result.stderr:
86
+ logging.warning(f"mmdc stderr: {result.stderr}")
87
 
88
  # Return paths for Flask to handle; caller must delete files
89
  return output_path, input_path
 
95
  temp_output_file.close()
96
  os.unlink(output_path)
97
  if os.path.exists(input_path): # Input file might already be closed/deleted
98
+ os.unlink(input_path)
99
  raise RuntimeError(f"Error rendering diagram: {e.stderr or e}")
100
  except Exception as e:
101
+ # Catch any other unexpected errors
102
+ logging.error(f"Unexpected error during rendering: {e}")
103
+ # Ensure cleanup
104
+ temp_input_file.close()
105
+ temp_output_file.close()
106
+ if os.path.exists(input_path): os.unlink(input_path)
107
+ if os.path.exists(output_path): os.unlink(output_path)
108
+ raise # Re-raise the caught exception