forked from EnigmaticaModpacks/Enigmatica2Expert
-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
312 additions
and
197 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import type { Options } from 'fast-glob' | ||
import type { InputFieldOptions } from 'terminal-kit/Terminal.js' | ||
|
||
import { relative } from 'node:path' | ||
import process from 'node:process' | ||
|
||
import boxen from 'boxen' | ||
import chalk from 'chalk' | ||
import fast_glob from 'fast-glob' | ||
import fse from 'fs-extra' | ||
import logUpdate from 'log-update' | ||
import terminal_kit from 'terminal-kit' | ||
|
||
import { | ||
end, | ||
write, | ||
} from '../lib/utils.js' | ||
|
||
const { rmSync } = fse | ||
|
||
const { terminal: term } = terminal_kit | ||
|
||
/** | ||
* Globs with default options `dot: true, onlyFiles: false` | ||
*/ | ||
export function globs(source: string | string[], options?: Options) { | ||
return fast_glob.sync(source, { dot: true, onlyFiles: false, ...options }) | ||
} | ||
|
||
export function getIgnoredFiles(ignored: any) { | ||
return globs( | ||
ignored._rules.filter((r: { negative: any }) => !r.negative).map((r: { pattern: any }) => r.pattern), | ||
{ ignore: ignored._rules.filter((r: { negative: any }) => r.negative).map((r: { pattern: any }) => r.pattern) } | ||
) | ||
} | ||
|
||
/** | ||
* @param {string | readonly string[]} fileArg list of globs of files to remove | ||
*/ | ||
export function removeFiles(fileArg: readonly string[] | string) { | ||
const files = [fileArg].flat(2) | ||
/** @type {string[]} */ | ||
const removed: string[] = [] | ||
files.forEach((file) => { | ||
try { | ||
rmSync(file, { recursive: true, maxRetries: 1 }) | ||
removed.push(file) | ||
} | ||
catch (error) { | ||
process.stdout.write(`\n${chalk.red(`Cannot remove: ${chalk.blue(file)}`)}\n`) | ||
} | ||
}) | ||
return `removed: ${removed.length}\n${ | ||
removed.map(s => chalk.gray(relative(process.cwd(), s))).join('\n') | ||
}` | ||
} | ||
|
||
export const style = { | ||
trace : chalk.hex('#7b4618'), | ||
info : chalk.hex('#915c27'), | ||
log : chalk.hex('#ad8042'), | ||
label : chalk.hex('#bfab67'), | ||
string: chalk.hex('#bfc882'), | ||
number: chalk.hex('#a4b75c'), | ||
status: chalk.hex('#647332'), | ||
chose : chalk.hex('#3e4c22'), | ||
end : chalk.hex('#2e401c'), | ||
} | ||
|
||
/** | ||
* Write task in log and execute it | ||
* @param s Name of the tast would be printed in Log | ||
* @param fn Function of task | ||
* @param cwd Optional working path where task is executed | ||
*/ | ||
export function doTask(s: string, fn: () => void, cwd?: string) { | ||
const oldCwd = process.cwd() | ||
if (cwd) process.chdir(cwd) | ||
write(style.label(s)) | ||
end(fn()) | ||
if (cwd) process.chdir(oldCwd) | ||
} | ||
|
||
/* | ||
█████╗ ██╗ ██╗████████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗ | ||
██╔══██╗██║ ██║╚══██╔══╝██╔═══██╗████╗ ████║██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║ | ||
███████║██║ ██║ ██║ ██║ ██║██╔████╔██║███████║ ██║ ██║██║ ██║██╔██╗ ██║ | ||
██╔══██║██║ ██║ ██║ ██║ ██║██║╚██╔╝██║██╔══██║ ██║ ██║██║ ██║██║╚██╗██║ | ||
██║ ██║╚██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║ | ||
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ | ||
*/ | ||
|
||
let STEP = 1 | ||
|
||
/** | ||
* Prompt user to write something and press ENTER or ESC | ||
* @param message message to show | ||
* @param options message to show | ||
* @returns inputted string or undefined | ||
*/ | ||
export async function enterString(message: string, options?: InputFieldOptions) { | ||
const msg = `[${STEP++}] ${message}` | ||
term(style.trace(msg.replace(/(ENTER|ESC)/g, style.info('$1')))) | ||
const result = await term.inputField({ | ||
cancelable: true, | ||
...(options ?? {}), | ||
}).promise | ||
term('\n') | ||
return result | ||
} | ||
|
||
/** | ||
* Prompt user to press ENTER or ESC | ||
* @param message message to show | ||
* @param condition repeat until true | ||
* @returns `true` if ENTER pressed, `false` otherwise | ||
*/ | ||
export async function pressEnterOrEsc(message: string, condition?: () => Promise<boolean>) { | ||
let oneTime = 0 | ||
while (condition ? !(await condition()) : !oneTime++) | ||
if ((await enterString(message)) === undefined) return false | ||
|
||
return true | ||
} | ||
|
||
export function getBoxForLabel(label: string) { | ||
logUpdate.done() | ||
return function updateBox(...args: any[]) { | ||
return logUpdate( | ||
boxen( | ||
args.map((v, i) => Object.values(style)[i](String(v))).join(' '), | ||
{ | ||
borderStyle: 'round', | ||
borderColor: '#22577a', | ||
width : 50, | ||
padding : { left: 1, right: 1 }, | ||
title : style.info(label), | ||
} | ||
) | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { join, parse } from 'node:path' | ||
import process from 'node:process' | ||
|
||
import fse from 'fs-extra' | ||
import git_describe from 'git-describe' | ||
import replace_in_file from 'replace-in-file' | ||
import Client from 'ssh2-sftp-client' | ||
|
||
import { | ||
end, | ||
loadJson, | ||
} from '../lib/utils.js' | ||
import { getBoxForLabel, globs, pressEnterOrEsc, style } from './build_utils' | ||
|
||
const { readFileSync, writeFileSync } = fse | ||
|
||
const { gitDescribeSync } = git_describe | ||
export async function manageSFTP( | ||
serverRemoveDirs: string[] = [ | ||
'bansoukou', | ||
'config', | ||
'mods', | ||
'patchouli_books', | ||
'resources', | ||
'schematics', | ||
'scripts', | ||
'structures', | ||
], | ||
serverSetupConfig: string = 'server/server-setup-config.yaml' | ||
) { | ||
const sftpConfigs = globs('secrets/sftp_servers/*/sftp.json').map((filename) => { | ||
const dir = parse(filename).dir | ||
return { | ||
dir, | ||
label : dir.split('/').pop(), | ||
config: loadJson(filename) as { [key: string]: string }, | ||
} | ||
}) | ||
|
||
const currentVersion = gitDescribeSync().tag | ||
|
||
const serverConfigTmp = '~tmp-server-setup-config.yaml' | ||
const confText = readFileSync(serverSetupConfig, 'utf8') | ||
.replace( | ||
/(additionalFiles:\s*)\n {4}\S.*$\n {4}\S.*$/m, | ||
`$1 | ||
- url: https://mediafilez.forgecdn.net/files/4886/383/mc2discord-forge-1.12.2-3.3.1.jar | ||
destination: mods/mc2discord-forge-1.12.2-3.3.1.jar` | ||
) | ||
// .replace( | ||
// /(localFiles:\s*)\n/m, | ||
// `$1 | ||
// - url: https://mediafilez.forgecdn.net/files/4886/383/mc2discord-forge-1.12.2-3.3.1.jar | ||
// destination: mods/mc2discord-forge-1.12.2-3.3.1.jar` | ||
// ) | ||
writeFileSync(serverConfigTmp, confText) | ||
|
||
for (const conf of sftpConfigs) { | ||
if (!(await pressEnterOrEsc( | ||
`To upload SFTP ${style.string(conf.label)} press ENTER. Press ESC to skip.` | ||
))) | ||
continue | ||
|
||
const sftp = new Client() | ||
const updateBox = getBoxForLabel(conf.label) | ||
|
||
updateBox('Establishing connection') | ||
try { | ||
await sftp.connect(conf.config) | ||
} | ||
catch (error) { | ||
end('Cant connect to SFTP') | ||
continue | ||
} | ||
|
||
updateBox('Removing folders') | ||
const stillToRemove = serverRemoveDirs.slice(0) | ||
await Promise.all( | ||
serverRemoveDirs.map(async (dir) => { | ||
try { | ||
if (!(await sftp.stat(dir)).isDirectory) return | ||
await sftp.rmdir(dir, true) | ||
stillToRemove.splice(stillToRemove.indexOf(dir), 1) | ||
updateBox('Still to remove:', stillToRemove.join(', ')) | ||
} | ||
catch (error) {} | ||
}) | ||
) | ||
|
||
updateBox(`Copy ${serverConfigTmp}`) | ||
await sftp.fastPut(serverConfigTmp, 'server-setup-config.yaml') | ||
|
||
updateBox('Change and copy server overrides') | ||
const title = `+ Server Started! +` | ||
const spaces = (' ').repeat(Math.max(1, (title.length - currentVersion.length) / 2) | 0) | ||
const replaceResult = replace_in_file.sync({ | ||
files : join(conf.dir, 'overrides/config/mc2discord.toml'), | ||
from : /(start\s*=\s*")[^"]+"/, | ||
to : `$1\`\`\`diff\\n${title}\\n${spaces}${currentVersion}\\n\`\`\`"`, | ||
countMatches: true, | ||
disableGlobs: true, | ||
}) | ||
|
||
updateBox('Remove', 'serverstarter.lock') | ||
sftp.delete('serverstarter.lock', true) | ||
|
||
if (replaceResult.length === 0) | ||
throw new Error('Nothing replaced! Code failure') | ||
|
||
let fileCounter = 0 | ||
sftp.on('upload', () => updateBox('Copy overrides', ++fileCounter)) | ||
await sftp.uploadDir(join(conf.dir, 'overrides'), './') | ||
|
||
await sftp.end() | ||
} | ||
} | ||
|
||
// Launch file | ||
if (import.meta.url === (await import('node:url')).pathToFileURL(process.argv[1]).href) { | ||
await manageSFTP() | ||
process.exit(0) | ||
} |
Oops, something went wrong.