// pull-env.ts: copy a val's environment variables into its .env on this VM. // // EXPORT_TOKEN=... deno run -A pull-env.ts NAME... // // Reads the named variables through export-data.ts, which runs inside the // val on Val Town, and writes them to /.env (mode 600), replacing // lines for the same names. Prints names only, never values. const [base, dir, ...names] = Deno.args; const token = Deno.env.get("EXPORT_TOKEN"); if (!base || !dir || !names.length || !token) { console.error("usage: EXPORT_TOKEN=... pull-env.ts NAME..."); Deno.exit(2); } const u = new URL("/env", base); u.searchParams.set("names", names.join(",")); const res = await fetch(u, { headers: { Authorization: `Bearer ${token}` } }); if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`); const values = (await res.json()) as Record; const file = `${dir}/.env`; let lines: string[] = []; try { lines = (await Deno.readTextFile(file)).split("\n").filter((l) => l !== ""); } catch { /* new file */ } const todo: string[] = []; for (const name of names) { const v = values[name]; let line: string; // Both systemd (EnvironmentFile) and Deno (--env-file) read these forms // the same way: bare safe characters, or single quotes, taken literally. if (/^[A-Za-z0-9_\-.:\/+=@,]*$/.test(v)) line = `${name}=${v}`; else if (!/['\n\r]/.test(v)) line = `${name}='${v}'`; else { todo.push(name); continue; } lines = lines.filter((l) => !l.startsWith(`${name}=`)); lines.push(line); console.log(`${name}: set`); } await Deno.writeTextFile(file, lines.join("\n") + "\n", { mode: 0o600 }); await Deno.chmod(file, 0o600); if (todo.length) { console.log(`not written, the value has a quote or newline; set by hand: ${todo.join(", ")}`); Deno.exit(1); }