import { storeJson } from '../fileModels/storeJson' import { i18n } from '../i18n' import { sdk } from '../sdk' import { dataDir, NACHO_DEFAULT_IGNORE_NAMES, NACHO_DEFAULT_WORKSHOP_PDF_LINK_TEXT, NACHO_DIR, } from '../utils' const { InputSpec, Value } = sdk const IGNORE_NAMES_PATH = `${NACHO_DIR}/ignore_names.txt` const mainMount = sdk.Mounts.of().mountVolume({ volumeId: 'main', subpath: null, mountpoint: dataDir, readonly: false, }) // Reads /data/nacho/ignore_names.txt off the main volume via a temp // subcontainer. Returns null if the file is missing/empty/unreadable so the // prefill falls back to the package default. async function readIgnoreNamesFile(effects: any): Promise { try { const res = await sdk.SubContainer.withTemp( effects, { imageId: 'spaces' }, mainMount, 'nacho-read-ignore-names', (subc) => subc.exec( [ 'sh', '-c', `if [ -f ${IGNORE_NAMES_PATH} ]; then cat ${IGNORE_NAMES_PATH}; fi`, ], { user: 'root' }, ), ) if (res.exitCode !== 0) return null const value = (res.stdout ?? '').toString().trim() return value.length > 0 ? value : null } catch { return null } } const inputSpec = InputSpec.of({ ignoreNames: Value.text({ name: i18n('Ignore Names'), description: i18n( 'Comma-separated list of names for the nacho UI to ignore (EXPO_PUBLIC_IGNORE_NAMES).', ), warning: null, footnote: null, default: NACHO_DEFAULT_IGNORE_NAMES, required: true, masked: false, placeholder: 'fold,swifty', minLength: 0, maxLength: null, }), workshopPdfLinkText: Value.text({ name: i18n('Workshop PDF Link Text'), description: i18n( 'Display text for the workshop PDF link in the nacho UI (EXPO_PUBLIC_WORKSHOP_PDF_LINK_TEXT). Leave empty to suppress the label.', ), warning: null, footnote: null, default: NACHO_DEFAULT_WORKSHOP_PDF_LINK_TEXT, required: false, masked: false, placeholder: 'Workshop PDF', minLength: 0, maxLength: null, }), }) export const configureNacho = sdk.Action.withInput( // id 'configure-nacho', // metadata async ({ effects }) => ({ name: i18n('Configure Nacho'), description: i18n( 'Set the user-tunable nacho settings. The Ignore Names list is written to /data/nacho/ignore_names.txt (read by nacho at runtime). The Workshop PDF link text is set via EXPO_PUBLIC_WORKSHOP_PDF_LINK_TEXT. Saving restarts the service. To upload a new Workshop PDF, use the separate "Upload Support PDF" action.', ), warning: null, allowedStatuses: 'any', group: null, visibility: 'enabled', }), // input inputSpec, // prefill — current values. Ignore Names is sourced from the file on disk // (the source of truth nacho actually reads), with the default as a fallback. async ({ effects }) => { const [store, fileValue] = await Promise.all([ storeJson.read().once(), readIgnoreNamesFile(effects), ]) return { ignoreNames: fileValue ?? NACHO_DEFAULT_IGNORE_NAMES, workshopPdfLinkText: store?.nachoWorkshopPdfLinkText ?? NACHO_DEFAULT_WORKSHOP_PDF_LINK_TEXT, } }, // run async ({ effects, input }) => { // Persist the workshop PDF link text in the store (so it stays an // EXPO_PUBLIC_* env var) and write the ignore names to its file on the // main volume. mkdir -p first in case /data/nacho doesn't yet exist // (e.g., the user runs this action before nacho-setup has executed). await storeJson.merge(effects, { nachoWorkshopPdfLinkText: input.workshopPdfLinkText ?? '', }) const ignoreNames = input.ignoreNames const res = await sdk.SubContainer.withTemp( effects, { imageId: 'spaces' }, mainMount, 'nacho-write-ignore-names', (subc) => subc.exec( [ 'sh', '-c', `set -eu; mkdir -p ${NACHO_DIR}; cat > ${IGNORE_NAMES_PATH} && chmod 644 ${IGNORE_NAMES_PATH}`, ], { input: ignoreNames, user: 'root' }, ), ) if (res.exitCode !== 0) { return { version: '1', title: i18n('Failure'), message: i18n('Could not write ignore_names.txt: ${error}', { error: ((res.stderr ?? '').toString() || `exit ${res.exitCode}`).slice( 0, 300, ), }), result: null, } } return { version: '1', title: i18n('Success'), message: i18n( 'Nacho configuration saved. Ignore Names written to /data/nacho/ignore_names.txt; the service is restarting to apply the new values.', ), result: null, } }, )