Files
cypress/packages/server/lib/saved_state.ts
Cacie Prins e463fdbc61 fix: Redirect spammy electron stderr to a debug sink (#32188)
* wip: system test to reproduce

* system test for alsa stderr

* split cypress from 3rd party stderr at parent process to electron child

* rm garbage warning regexes

* fix newline behavior when parsing internal stderr

* migrate left over console errors

* clean up system test name

* fix typed import

* extract stderr splitting to separate pkg so runner can use @packages/error

* rm new err log from packherd-quire

* handle backpressure

* docs

* some unit tests & coverage for stderr-filtering

* unit tests

* no longer test regexp specific output in spawn unit tests

* filter enabled debug namespaces rather than just cypress namespacesc

* revise stream splitting et al

* try to fix v8 snapshot build??

* fix console.log assertion

* add missing eslint config

* rm unused spies

* fix regexp for optional leading wsp and ansi on debug entries

* update unit tests because sinon

* lint

* colon..

* build stderr-filtering before checking if binary exists

* adds TagStream transform stream, fixes stderr from child proc config

* add build-prod script for stderr-filtering

* changelog

* properly handle backpressure in prefixed content transform stream

* use standard tsconfig?

* better tsconfig

* Add pkgStderrFiltering to monorepoPaths

* add \"files\" manifest

* pipe all stderr to stderr when CYPRESS_INTERNAL_DEBUG_ELECTRON is enabled

* rm explicit build of stderr-filtering in check-if-binary-exists step

* ensure all dependencies of scripts/ are built before scripts are executed in the check-if-binary-exists command

* fix dev version ref

* swap logic

* add stdin piping

* fix exec name on the run-on-dependencies command to be more useful

* use correct env

* rm obsolete type refs

* simplify stderr-filtering public iface, pipe cy-in-cy stderr through filtering tx

* bust cache

* fix mocks

* fix v8-snapshot

* move stderrfiltering to dev pkg in cli

* skip integrity check in ci, if they are out of date things should fail anyway

* copypasta over a portion of stderr-filtering to cli, since cli cannot import @packages

* Delete issues.md

* rm special filtering for cy in cy

* rm too narrow rules file

---------

Co-authored-by: Jennifer Shehane <shehane.jennifer@gmail.com>
Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
Co-authored-by: Bill Glesias <bglesias@gmail.com>
2025-08-19 17:05:53 -04:00

128 lines
3.1 KiB
TypeScript

import _ from 'lodash'
import path from 'path'
import Debug from 'debug'
import Bluebird from 'bluebird'
import appData from './util/app_data'
import cwd from './cwd'
import FileUtil from './util/file'
import { fs } from './util/fs'
import { AllowedState, allowedKeys } from '@packages/types'
import { globalPubSub } from '@packages/data-context'
import { logError } from '@packages/stderr-filtering'
const debug = Debug('cypress:server:saved_state')
const stateFiles: Record<string, typeof FileUtil> = {}
export const formStatePath = (projectRoot?: string) => {
return Bluebird.try(() => {
debug('making saved state from %s', cwd())
if (projectRoot) {
debug('for project path %s', projectRoot)
return projectRoot
}
debug('missing project path, looking for project here')
let cypressConfigPath = cwd('cypress.config.js')
return fs.pathExistsAsync(cypressConfigPath)
.then((found) => {
if (found) {
debug('found cypress file %s', cypressConfigPath)
projectRoot = cwd()
return
}
cypressConfigPath = cwd('cypress.config.ts')
return fs.pathExistsAsync(cypressConfigPath)
})
.then((found) => {
if (found) {
debug('found cypress file %s', cypressConfigPath)
projectRoot = cwd()
}
return projectRoot
})
}).then((projectRoot) => {
const fileName = 'state.json'
if (projectRoot) {
debug(`state path for project ${projectRoot}`)
return path.join(appData.toHashName(projectRoot), fileName)
}
debug('state path for global mode')
return path.join('__global__', fileName)
})
}
const normalizeAndAllowSet = (set, key, value) => {
const valueObject = (() => {
if (_.isString(key)) {
const tmp = {}
tmp[key] = value
return tmp
}
return key
})()
const invalidKeys = _.filter(_.keys(valueObject), (key) => {
return !_.includes(allowedKeys, key)
})
if (invalidKeys.length) {
logError(`WARNING: attempted to save state for non-allowed key(s): ${invalidKeys.join(', ')}. All keys must be allowed in server/lib/saved_state.ts`)
}
return set(_.pick(valueObject, allowedKeys))
}
interface SavedStateAPI {
get: () => Bluebird<AllowedState>
set: (stateToSet: AllowedState) => Bluebird<void>
}
export const create = (projectRoot?: string, isTextTerminal: boolean = false): Bluebird<SavedStateAPI> => {
if (isTextTerminal) {
debug('noop saved state')
return Bluebird.resolve(FileUtil.noopFile)
}
return formStatePath(projectRoot)
.then((statePath: string) => {
const fullStatePath = appData.projectsPath(statePath)
debug('full state path %s', fullStatePath)
if (stateFiles[fullStatePath]) {
return stateFiles[fullStatePath]
}
debug('making new state file around %s', fullStatePath)
const stateFile = new FileUtil({
path: fullStatePath,
})
globalPubSub.on('test:cleanup', () => {
stateFile.__resetForTest()
})
stateFile.set = _.wrap(stateFile.set.bind(stateFile), normalizeAndAllowSet)
stateFiles[fullStatePath] = stateFile
return stateFile as SavedStateAPI
})
}