[monorepo] add integration for GitHub Code Spaces (#32722)

* [web] serve site manifest as application/json

* [monorepo] bail out early from dev-env-setup when inside devcontainer

* [monorepo] move utils for waiting for services into shared bash script

* [monorepo] move domain related env-vars into dedicated .env file

The .env file will be overridden in GitHub Code Spaces.

* [monorepo] remove unused initial_mongo_setup Make target

It was previously used for the mongo replica set setup.
That setup is a docker init script now.

* [monorepo] add flag for silencing docker build output

* [monorepo] add integration for GitHub Code Spaces

* [web] make prettier happy

* [web] restore no use-credentials for manifest outside code spaces

* [monorepo] run make install as part of post-start again

GitOrigin-RevId: 8c2cc3b969b7e3e8f9c5dac778192c9412cbf4df
This commit is contained in:
Jakob Ackermann
2026-04-10 12:08:59 +02:00
committed by Copybot
parent 24b16c1304
commit b893ba36e2
8 changed files with 126 additions and 32 deletions

View File

@@ -0,0 +1,92 @@
import { promiseMapWithLimit } from '@overleaf/promise-utils'
import Settings from '@overleaf/settings'
import { waitForDb, db } from '../app/src/infrastructure/mongodb.mjs'
import GracefulShutdown from '../app/src/infrastructure/GracefulShutdown.mjs'
import UserRegistrationHandler from '../app/src/Features/User/UserRegistrationHandler.mjs'
import minimist from 'minimist'
import {
createProjectWithOldHistoryId,
provisionSplitTests,
} from './e2e_test_setup.mjs'
const { email: USER_EMAIL, password: PASSWORD } = minimist(
process.argv.slice(2),
{ string: ['email', 'password'] }
)
/**
* @param {string} email
* @return {Promise<string>}
*/
async function createUser(email) {
let userId
try {
const user = await UserRegistrationHandler.promises.registerNewUser({
email,
password: PASSWORD,
})
userId = user._id
} catch (err) {
if (err.message.includes('EmailAlreadyRegistered')) {
userId = err.info.userId
} else {
throw err
}
}
const features = email.startsWith('free')
? Settings.defaultFeatures
: Settings.features.professional
const isAdmin = email === USER_EMAIL || email === 'admin@overleaf.com'
let adminRoles = []
if (isAdmin) {
adminRoles = ['engineering']
}
await db.users.updateOne(
{ _id: userId },
{
$set: {
// Set admin flag.
isAdmin,
adminRoles,
// Override features.
features,
featuresOverrides: [{ features }],
// disable AI features, does not work with custom GH Code Spaces domain.
'aiFeatures.enabled': false,
},
}
)
return userId.toString()
}
/**
* @param {string} email
* @return {Promise<void>}
*/
async function provisionUser(email) {
const userId = await createUser(email)
await createProjectWithOldHistoryId(userId)
}
async function provisionUsers() {
const emails = [
USER_EMAIL,
'admin@overleaf.com',
'free@overleaf.com',
'premium@overleaf.com',
]
await promiseMapWithLimit(5, emails, provisionUser)
}
async function main() {
if (process.env.NODE_ENV !== 'development') {
throw new Error('only available in dev-env')
}
await waitForDb()
await Promise.all([provisionUsers(), provisionSplitTests()])
}
if (import.meta.main) {
await main()
await GracefulShutdown.gracefulShutdown()
}

View File

@@ -85,7 +85,7 @@ async function deleteUser(email) {
await UserDeleter.promises.expireDeletedUser(user._id)
}
async function createProjectWithOldHistoryId(userId) {
export async function createProjectWithOldHistoryId(userId) {
const projectName = 'old history id'
const historyId = parseInt(
await HistoryManager.promises.initializeProject(),
@@ -137,7 +137,7 @@ async function purgeNewUsers() {
)
}
async function provisionSplitTests() {
export async function provisionSplitTests() {
const backup = Path.join(
MONOREPO,
'backup',
@@ -213,12 +213,7 @@ async function main() {
await Promise.all([purgeNewUsers(), provisionUsers(), provisionSplitTests()])
}
await main()
await GracefulShutdown.gracefulShutdown(
{
close(cb) {
cb()
},
},
'SIGTERM'
)
if (import.meta.main) {
await main()
await GracefulShutdown.gracefulShutdown()
}