-
Notifications
You must be signed in to change notification settings - Fork 338
Expand file tree
/
Copy pathgit-clean.mjs
More file actions
108 lines (94 loc) · 3.06 KB
/
git-clean.mjs
File metadata and controls
108 lines (94 loc) · 3.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* @file Clean workspace of all temporary files and directories.
* Calls `git clean -xdf` with some exclusions, and also `bazel clean --expunge`, to clean Bazel build artifacts.
*/
const EXCLUSIONS = [
'.idea',
'.jj',
'.bazelrc.local',
'app/gui/',
'app/electron-client/playwright/.auth/user.json',
]
import { spawn } from 'node:child_process'
import { once } from 'node:events'
import fs from 'node:fs/promises'
import process from 'node:process'
const VERBOSE = process.argv.includes('--verbose') || process.argv.includes('-v')
const CLEAN_BAZEL = process.argv.includes('--clean-bazel')
async function runCommand(command, args) {
const child = spawn(command, args, { stdio: 'pipe' })
let stdout = ''
let stderr = ''
child.stdout.on('data', (data) => (stdout += data))
child.stderr.on('data', (data) => (stderr += data))
if (VERBOSE) {
console.log(`Running command: ${command} ${args.join(' ')}`)
}
const [code] = await once(child, 'close')
if (code !== 0) {
console.error(`Failed to run command ${command}: ${stderr}`)
process.exit(1)
}
if (VERBOSE) {
console.log('STDOUT:')
console.log(stdout)
console.log('STDERR:')
console.log(stderr)
}
}
async function runGitClean() {
return runCommand('git', ['clean', '-xdf', ...EXCLUSIONS.flatMap((e) => ['--exclude', e])])
}
async function runBazelClean() {
let executable = 'bazel'
return runCommand(executable, ['clean', '--expunge_async'])
}
async function removeIfExists(path) {
try {
await fs.rm(path, { recursive: true, force: true })
if (VERBOSE) {
console.log(`Removed: ${path}`)
}
} catch (err) {
if (err.code !== 'ENOENT') {
console.error(`Failed to remove ${path}: ${err.message}`)
throw err
}
// If error is ENOENT, entry does not exist, ignore
}
}
async function cleanJunctions() {
// On Windows, `pnpm` uses junctions as symbolic links for in-workspace dependencies.
// Unfortunately, Git for Windows treats those as hard links. That then leads to
// `git clean` recursing into those linked directories, happily deleting sources of
// whole linked packages or failing on files that were already deleted. Manually
// deleting junction directories before running clean prevents this from happening.
let junctions = [
'bazel-enso',
'bazel-out',
'bazel-bin',
'node_modules',
'app/common/node_modules',
'app/gui/node_modules',
'app/electron-client/node_modules',
'app/lang-markdown/node_modules',
'app/lezer-markdown/node_modules',
'app/project-manager-shim/node_modules',
'app/rust-ffi/node_modules',
'app/table-expression/node_modules',
'app/ydoc-server/node_modules',
'app/ydoc-server-nodejs/node_modules',
'app/ydoc-server-polyglot/node_modules',
'app/ydoc-shared/node_modules',
'lib/js/runner/node_modules',
'tools/simple-library-server/node_modules',
]
await Promise.all(junctions.map((junction) => removeIfExists(junction)))
}
if (CLEAN_BAZEL) {
await runBazelClean()
}
if (process.platform === 'win32') {
await cleanJunctions()
}
await runGitClean()