⚗️(front) conditionaly install blocknote xl packages

The blocknote xl packages are licensed under AGPL v3. We want to allow a
reuser to build the project without them. For this, in a first step we
don't list these packages in the dependencies but install them using a
postinstall script.
This script is a node script looking in every package.json files under
the apps directory, look if an `extraDependencies` key exists and if yes
hten will install the listed dependencies after prompting the user if
wants or not to install them.
Also an environment variable AUTO_INSTALL_EXTRA_DEPS can be used to
explicitly install all the dependencies or to explicitly not install
them. This will help to build an image with and without these
dependencies. Moreover we also need thisin a CI context.
This commit is contained in:
Manuel Raynaud
2025-04-06 14:06:07 +02:00
parent c11d59c434
commit 401913297e
7 changed files with 2749 additions and 2666 deletions

View File

@@ -34,7 +34,7 @@ jobs:
node-version: ${{ inputs.node_version }}
- name: Install dependencies
if: steps.front-node_modules.outputs.cache-hit != 'true'
run: cd src/frontend/ && yarn install --frozen-lockfile
run: cd src/frontend/ && AUTO_INSTALL_EXTRA_DEPS=true yarn install --frozen-lockfile
- name: Cache install frontend
if: steps.front-node_modules.outputs.cache-hit != 'true'
uses: actions/cache@v4

View File

@@ -158,6 +158,7 @@ services:
Y_PROVIDER_URL: "ws://localhost:4444"
MEDIA_URL: "http://localhost:8083"
SW_DEACTIVATED: "true"
AUTO_INSTALL_EXTRA_DEPS: "true"
image: impress:frontend-development
ports:
- "3000:3000"

View File

@@ -1,13 +1,16 @@
FROM node:20-alpine AS frontend-deps
ARG AUTO_INSTALL_EXTRA_DEPS=false
WORKDIR /home/frontend/
COPY ./src/frontend/package.json ./package.json
COPY ./src/frontend/bin/conditional-install.js ./bin/conditional-install.js
COPY ./src/frontend/yarn.lock ./yarn.lock
COPY ./src/frontend/apps/impress/package.json ./apps/impress/package.json
COPY ./src/frontend/packages/eslint-config-impress/package.json ./packages/eslint-config-impress/package.json
RUN yarn install --frozen-lockfile
RUN AUTO_INSTALL_EXTRA_DEPS=${AUTO_INSTALL_EXTRA_DEPS} yarn install --frozen-lockfile
COPY .dockerignore ./.dockerignore
COPY ./src/frontend/.prettierrc.js ./.prettierrc.js

View File

@@ -19,8 +19,6 @@
"@blocknote/core": "0.23.2-hotfix.0",
"@blocknote/mantine": "0.23.2-hotfix.0",
"@blocknote/react": "0.23.2-hotfix.0",
"@blocknote/xl-docx-exporter": "0.23.2-hotfix.0",
"@blocknote/xl-pdf-exporter": "0.23.2-hotfix.0",
"@fontsource/material-icons": "5.2.5",
"@gouvfr-lasuite/integration": "1.0.2",
"@gouvfr-lasuite/ui-kit": "0.1.3",
@@ -79,5 +77,9 @@
"typescript": "*",
"webpack": "5.98.0",
"workbox-webpack-plugin": "7.1.0"
},
"extraDependencies": {
"@blocknote/xl-docx-exporter": "0.23.2-hotfix.0",
"@blocknote/xl-pdf-exporter": "0.23.2-hotfix.0"
}
}

View File

@@ -0,0 +1,139 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const readline = require('readline');
// Check if AUTO_INSTALL_EXTRA_DEPS is explicitly set to false
if (process.env.AUTO_INSTALL_EXTRA_DEPS === 'false') {
console.log('AUTO_INSTALL_EXTRA_DEPS is set to false, skipping script execution');
process.exit(0);
}
// Get the workspace root directory
const workspaceRoot = process.cwd();
// Check if AUTO_INSTALL_EXTRA_DEPS environment variable is set to bypass prompts
const autoMode = process.env.AUTO_INSTALL_EXTRA_DEPS === 'true';
// Create readline interface for user prompts
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// Function to prompt user for confirmation
function promptUser(question) {
return new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer.toLowerCase().startsWith('y'));
});
});
}
// Function to find all package.json files in the apps directory
function findPackageJsonFiles(directory) {
const appsDir = path.join(workspaceRoot, directory);
// Check if the directory exists
if (!fs.existsSync(appsDir)) {
console.log(`Directory ${directory} does not exist, skipping...`);
return [];
}
const packageJsonFiles = [];
// Read all items in the directory
const items = fs.readdirSync(appsDir);
for (const item of items) {
const itemPath = path.join(appsDir, item);
const stat = fs.statSync(itemPath);
if (stat.isDirectory()) {
// Check if this directory has a package.json
const packageJsonPath = path.join(itemPath, 'package.json');
if (fs.existsSync(packageJsonPath)) {
packageJsonFiles.push(packageJsonPath);
// Skip searching in subdirectories once a package.json is found
continue;
}
// Only search subdirectories if no package.json was found in this directory
packageJsonFiles.push(...findPackageJsonFiles(path.join(directory, item)));
}
}
return packageJsonFiles;
}
// Find all package.json files in the apps directory
const packageJsonFiles = findPackageJsonFiles('apps');
if (packageJsonFiles.length === 0) {
console.log('No package.json files found in the apps directory');
process.exit(0);
}
console.log(`Found ${packageJsonFiles.length} package.json files in the apps directory`);
// Process each package.json file
async function processPackageJsonFiles() {
for (const packageJsonPath of packageJsonFiles) {
try {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
// Check if extraDependencies exists
if (packageJson.extraDependencies && Object.keys(packageJson.extraDependencies).length > 0) {
console.log(`Found extraDependencies in ${packageJsonPath}, installing...`);
// Process each dependency individually
for (const [pkg, version] of Object.entries(packageJson.extraDependencies)) {
const dependencyToInstall = `${pkg}@${version}`;
// Prompt user if not in auto mode
let shouldInstall = autoMode;
if (!autoMode) {
shouldInstall = await promptUser(`
Do you want to install ${dependencyToInstall}? (y/n):
Note that these packages are dual-licensed by Blocknotejs
under AGPL-3.0 or a proprietary license. If you choose
to install them, please ensure you fulfill your licensing
obligations with respect to BlockNoteJS
`);
}
if (shouldInstall) {
// Install the dependency using npm install
try {
console.log(`Installing: ${dependencyToInstall}`);
execSync(`npm install --no-save --ignore-scripts --no-audit ${dependencyToInstall}`, { stdio: 'inherit' });
console.log(`Extra dependency ${dependencyToInstall} installed successfully`);
} catch (error) {
console.error(`Failed to install extra dependency ${dependencyToInstall}:`, error.message);
// Continue with other dependencies even if one fails
}
} else {
console.log(`Skipping installation of ${dependencyToInstall}`);
}
}
} else {
console.log(`No extraDependencies found in ${packageJsonPath}, skipping installation`);
}
} catch (error) {
console.error(`Error reading or parsing ${packageJsonPath}:`, error.message);
// Continue with other package.json files even if one fails
}
}
console.log('Finished processing all package.json files');
rl.close();
}
// Run the async function
processPackageJsonFiles().catch(error => {
console.error('An error occurred:', error);
rl.close();
process.exit(1);
});

View File

@@ -25,7 +25,8 @@
"i18n:deploy": "yarn I18N run format-deploy && yarn APP_IMPRESS prettier",
"i18n:test": "yarn I18N run test",
"test": "yarn server:test && yarn app:test",
"server:test": "yarn COLLABORATION_SERVER run test"
"server:test": "yarn COLLABORATION_SERVER run test",
"postinstall": "node ./bin/conditional-install.js"
},
"resolutions": {
"@types/node": "22.13.9",

File diff suppressed because it is too large Load Diff