mirror of
https://github.com/chrisbenincasa/tunarr.git
synced 2026-04-18 09:03:35 -04:00
fix: properly generate build-time constants
This commit is contained in:
@@ -11,3 +11,5 @@ catalog:
|
||||
random-js: 2.1.0
|
||||
typescript: 5.7.3
|
||||
zod: ^4.1.5
|
||||
|
||||
enablePrePostScripts: true
|
||||
|
||||
3
server/.gitignore
vendored
3
server/.gitignore
vendored
@@ -10,6 +10,5 @@ streams/
|
||||
bin/
|
||||
|
||||
src/generated/**
|
||||
!src/generated/web-imports.d.ts
|
||||
!src/generated/.gitkeep
|
||||
!src/generated/env.ts
|
||||
emby.ts
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
"make-bin": "dotenvx run -- tsx scripts/make-bin.ts",
|
||||
"clean": "rimraf --glob ./build/ ./dist/ ./bin/*",
|
||||
"debug": "cross-env NODE_ENV=development dotenvx run -f .env.development -- tsx watch --trace-warnings --tsconfig ./tsconfig.build.json --ignore 'src/streams' --inspect-wait ./src",
|
||||
"predev": "tsx scripts/generateEnvModule.ts",
|
||||
"dev": "cross-env NODE_ENV=development dotenvx run -f .env.development -- tsx watch --heap-snapshot-on-oom --trace-warnings --tsconfig ./tsconfig.build.json --ignore 'build' --ignore 'src/streams' --ignore 'src/**/*.test.ts' ./src/index.ts",
|
||||
"generate-env": "tsx scripts/generateEnvModule.ts",
|
||||
"generate-openapi": "tsx src/index.ts generate-openapi",
|
||||
"install-meilisearch": "tsx scripts/download-meilisearch.ts",
|
||||
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import 'dotenv/config';
|
||||
import esbuild from 'esbuild';
|
||||
import esbuildPluginPino from 'esbuild-plugin-pino';
|
||||
import fg from 'fast-glob';
|
||||
import fs from 'node:fs';
|
||||
import { basename } from 'node:path';
|
||||
import { rimraf } from 'rimraf';
|
||||
import { nativeNodeModulesPlugin } from '../esbuild/native-node-module.js';
|
||||
import { nodeProtocolPlugin } from '../esbuild/node-protocol.js';
|
||||
|
||||
const DIST_DIR = 'dist';
|
||||
|
||||
if (fs.existsSync(DIST_DIR)) {
|
||||
console.log('Deleting old build...');
|
||||
await rimraf(DIST_DIR);
|
||||
}
|
||||
|
||||
fs.mkdirSync(DIST_DIR);
|
||||
|
||||
console.log('Copying images...');
|
||||
fs.cpSync('src/resources/images', `${DIST_DIR}/resources/images`, {
|
||||
recursive: true,
|
||||
});
|
||||
|
||||
const isEdgeBuild = process.env.TUNARR_EDGE_BUILD === 'true';
|
||||
|
||||
console.log('Bundling app...');
|
||||
const result = await esbuild.build({
|
||||
entryPoints: {
|
||||
bundle: 'src/index.ts',
|
||||
},
|
||||
outExtension: {
|
||||
'.js': '.cjs',
|
||||
},
|
||||
bundle: true,
|
||||
minify: true,
|
||||
outdir: DIST_DIR,
|
||||
logLevel: 'info',
|
||||
format: 'cjs',
|
||||
platform: 'node',
|
||||
target: 'node22',
|
||||
inject: [
|
||||
'./esbuild/bundlerPathsOverrideShim.ts',
|
||||
'./esbuild/importMetaUrlShim.ts',
|
||||
],
|
||||
tsconfig: './tsconfig.build.json',
|
||||
external: [
|
||||
'mysql',
|
||||
'mysql2',
|
||||
'sqlite3',
|
||||
'pg',
|
||||
'tedious',
|
||||
'pg-query-stream',
|
||||
'oracledb',
|
||||
'mariadb',
|
||||
'libsql',
|
||||
],
|
||||
mainFields: ['module', 'main'],
|
||||
plugins: [
|
||||
nativeNodeModulesPlugin(),
|
||||
nodeProtocolPlugin(),
|
||||
// copy({
|
||||
// resolveFrom: 'cwd',
|
||||
// assets: {
|
||||
// from: ['node_modules/@fastify/swagger-ui/static/*'],
|
||||
// to: ['build/static'],
|
||||
// },
|
||||
// }),
|
||||
esbuildPluginPino({
|
||||
transports: ['pino-pretty', 'pino-roll'],
|
||||
}),
|
||||
],
|
||||
keepNames: true, // This is to ensure that Entity class names remain the same
|
||||
metafile: true,
|
||||
define: {
|
||||
'process.env.NODE_ENV': '"production"',
|
||||
'process.env.TUNARR_VERSION': `"${process.env.TUNARR_VERSION}"`,
|
||||
'process.env.TUNARR_BUILD': `"${process.env.TUNARR_BUILD}"`,
|
||||
'process.env.TUNARR_EDGE_BUILD': `"${isEdgeBuild}"`,
|
||||
'import.meta.url': '__import_meta_url',
|
||||
'import.meta.dirname': '__import_meta_dirname',
|
||||
},
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${DIST_DIR}/meta.json`, JSON.stringify(result.metafile));
|
||||
|
||||
fs.cpSync('package.json', `${DIST_DIR}/package.json`);
|
||||
|
||||
const nativeBindings = await fg('node_modules/better-sqlite3/**/*.node');
|
||||
for (const binding of nativeBindings) {
|
||||
console.log(`Copying ${binding} to out dir`);
|
||||
fs.cpSync(binding, `${DIST_DIR}/build/${basename(binding)}`);
|
||||
}
|
||||
|
||||
console.log('Done bundling!');
|
||||
process.exit(0);
|
||||
@@ -11,6 +11,7 @@ import { nodeProtocolPlugin } from '../esbuild/node-protocol.ts';
|
||||
|
||||
import { trimStart } from 'lodash-es';
|
||||
import { createRequire } from 'node:module';
|
||||
import { generateEnvModule } from './generateEnvModule.ts';
|
||||
const __require = createRequire(import.meta.url);
|
||||
const esbuildPluginPino = __require('esbuild-plugin-pino');
|
||||
|
||||
@@ -28,17 +29,25 @@ fs.cpSync('src/resources/images', `${DIST_DIR}/resources/images`, {
|
||||
recursive: true,
|
||||
});
|
||||
|
||||
const tunarrKeyVals: Record<string, string> = {};
|
||||
for (const [key, val] of Object.entries(process.env)) {
|
||||
if (key.startsWith('TUNARR_') && val) {
|
||||
tunarrKeyVals[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
console.debug(format('Building with Tunarr env: %O', tunarrKeyVals));
|
||||
|
||||
const isEdgeBuild = process.env.TUNARR_EDGE_BUILD === 'true';
|
||||
|
||||
// TODO: Do we want to hard-code any TUNARR_ prefixed environment variables at build time?
|
||||
await generateEnvModule([
|
||||
'NODE_ENV',
|
||||
'TUNARR_VERSION',
|
||||
'TUNARR_BUILD',
|
||||
'TUNARR_EDGE_BUILD',
|
||||
]);
|
||||
const define = {
|
||||
'process.env.NODE_ENV': '"production"',
|
||||
'process.env.TUNARR_VERSION': `"${trimStart(process.env.TUNARR_VERSION, 'v')}"`,
|
||||
'process.env.TUNARR_BUILD': `"${process.env.TUNARR_BUILD}"`,
|
||||
'process.env.TUNARR_EDGE_BUILD': `"${isEdgeBuild}"`,
|
||||
'import.meta.url': '__import_meta_url',
|
||||
};
|
||||
|
||||
console.debug(format('Building with Tunarr env: %O', define));
|
||||
|
||||
console.log('Bundling app...');
|
||||
const result = await esbuild.build({
|
||||
entryPoints: {
|
||||
@@ -87,13 +96,7 @@ const result = await esbuild.build({
|
||||
],
|
||||
keepNames: true, // This is to ensure that Entity class names remain the same
|
||||
metafile: true,
|
||||
define: {
|
||||
'process.env.NODE_ENV': '"production"',
|
||||
'process.env.TUNARR_VERSION': `"${trimStart(process.env.TUNARR_VERSION, 'v')}"`,
|
||||
'process.env.TUNARR_BUILD': `"${process.env.TUNARR_BUILD}"`,
|
||||
'process.env.TUNARR_EDGE_BUILD': `"${isEdgeBuild}"`,
|
||||
'import.meta.url': '__import_meta_url',
|
||||
},
|
||||
define,
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${DIST_DIR}/meta.json`, JSON.stringify(result.metafile));
|
||||
|
||||
31
server/scripts/generateEnvModule.ts
Normal file
31
server/scripts/generateEnvModule.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import fs from 'node:fs/promises';
|
||||
|
||||
export async function generateEnvModule(keysToInline: string[]) {
|
||||
const entries = keysToInline
|
||||
.map((key) => {
|
||||
const value = process.env[key];
|
||||
if (!!value) {
|
||||
return ` ${key}: ${JSON.stringify(value)},`;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join('\n');
|
||||
|
||||
const moduleContent = `
|
||||
// AUTO-GENERATED - DO NOT EDIT MANUALLY
|
||||
// Generated a build time by bundle.ts
|
||||
|
||||
export const BUILD_ENV: Record<string, string> = {
|
||||
${entries}
|
||||
} as const;
|
||||
|
||||
export const BUILD_ENV_KEYS = new Set(Object.keys(BUILD_ENV));
|
||||
`;
|
||||
|
||||
await fs.writeFile('./src/generated/env.ts', moduleContent);
|
||||
}
|
||||
|
||||
if (process.argv[1] === import.meta.filename) {
|
||||
await generateEnvModule([]);
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
isPodman,
|
||||
isRunningInContainer,
|
||||
} from '../util/containerUtil.ts';
|
||||
import { getEnvVar, TUNARR_ENV_VARS } from '../util/env.ts';
|
||||
import { streamFileBackwards } from '../util/fsUtil.ts';
|
||||
import { take } from '../util/streams.ts';
|
||||
|
||||
@@ -457,19 +458,20 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
},
|
||||
},
|
||||
async (_, res) => {
|
||||
const matching = seq.collect(
|
||||
Object.entries(process.env),
|
||||
([key, val]) => {
|
||||
if (key.startsWith('TUNARR_') && val) {
|
||||
return [key, val] as const;
|
||||
} else if ((key === 'NODE_ENV' || key === 'LOG_LEVEL') && val) {
|
||||
return [key, val] as const;
|
||||
}
|
||||
return;
|
||||
},
|
||||
);
|
||||
const matches = seq.collect(Object.values(TUNARR_ENV_VARS), (key) => {
|
||||
const value = getEnvVar(key);
|
||||
if (!value) return;
|
||||
return [key, value] as const;
|
||||
});
|
||||
const obj = Object.fromEntries(matches);
|
||||
if (process.env.NODE_ENV) {
|
||||
obj['NODE_ENV'] = process.env.NODE_ENV;
|
||||
}
|
||||
if (process.env.LOG_LEVEL) {
|
||||
obj['LOG_LEVEL'] = process.env.LOG_LEVEL;
|
||||
}
|
||||
|
||||
return res.send(Object.fromEntries(matching));
|
||||
return res.send(obj);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
0
server/src/generated/.gitkeep
Normal file
0
server/src/generated/.gitkeep
Normal file
12
server/src/generated/env.ts
Normal file
12
server/src/generated/env.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
// AUTO-GENERATED - DO NOT EDIT MANUALLY
|
||||
// Generated a build time by bundle.ts
|
||||
|
||||
export const BUILD_ENV: Record<string, string> = {
|
||||
NODE_ENV: "production",
|
||||
TUNARR_VERSION: "v1.1.4",
|
||||
TUNARR_BUILD: "ff21b76",
|
||||
TUNARR_EDGE_BUILD: "false",
|
||||
} as const;
|
||||
|
||||
export const BUILD_ENV_KEYS = new Set(Object.keys(BUILD_ENV));
|
||||
@@ -1,3 +1,4 @@
|
||||
import { BUILD_ENV, BUILD_ENV_KEYS } from '@/generated/env.js';
|
||||
import { TruthyQueryParam } from '../types/schemas.ts';
|
||||
import type { Nullable } from '../types/util.ts';
|
||||
import { isNonEmptyString, parseIntOrNull } from './index.ts';
|
||||
@@ -52,6 +53,9 @@ export const TUNARR_ENV_VARS = {
|
||||
type ValidEnvVar = (typeof TUNARR_ENV_VARS)[keyof typeof TUNARR_ENV_VARS];
|
||||
|
||||
export function getEnvVar(name: ValidEnvVar): Nullable<string> {
|
||||
if (BUILD_ENV_KEYS.has(name)) {
|
||||
return BUILD_ENV[name] ?? null;
|
||||
}
|
||||
const val = process.env[name];
|
||||
return isNonEmptyString(val) ? val : null;
|
||||
}
|
||||
|
||||
@@ -459,7 +459,7 @@ export function flipMap<K extends string | number, V extends string | number>(
|
||||
export const filename = (path: string) => fileURLToPath(path);
|
||||
|
||||
export const currentEnv = once(() => {
|
||||
const env = process.env['NODE_ENV'];
|
||||
const env = process.env.NODE_ENV;
|
||||
return env ?? 'production';
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"mikro-orm.prod.config.ts",
|
||||
"mikro-orm.base.config.ts"
|
||||
"./src/**/*",
|
||||
],
|
||||
"exclude": [
|
||||
"./dist/**/*",
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"./scripts/**/*.ts",
|
||||
"./src/**/*",
|
||||
"./scripts/**/*",
|
||||
],
|
||||
"exclude": [
|
||||
"./src/**/*.ignore.ts",
|
||||
|
||||
@@ -1,74 +1,45 @@
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"extends": [
|
||||
"//"
|
||||
],
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {},
|
||||
"build": {
|
||||
"dependsOn": ["^build", "generate-env"]
|
||||
},
|
||||
"typecheck": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
"dependsOn": ["^build", "generate-env"]
|
||||
},
|
||||
"bundle": {
|
||||
"inputs": [
|
||||
"./scripts/bundle.ts",
|
||||
"./src/**"
|
||||
],
|
||||
"dependsOn": [
|
||||
"^build",
|
||||
"@tunarr/web#bundle"
|
||||
],
|
||||
"outputs": [
|
||||
"dist/**"
|
||||
]
|
||||
"inputs": ["./scripts/bundle.ts", "./src/**"],
|
||||
"dependsOn": ["^build", "@tunarr/web#bundle"],
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"build-dev": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
"dependsOn": ["^build", "generate-env"]
|
||||
},
|
||||
"make-bin": {
|
||||
"cache": false,
|
||||
"dependsOn": [
|
||||
"@tunarr/web#bundle",
|
||||
"bundle"
|
||||
],
|
||||
"outputs": [
|
||||
"bin/**"
|
||||
]
|
||||
"dependsOn": ["@tunarr/web#bundle", "bundle"],
|
||||
"outputs": ["bin/**"]
|
||||
},
|
||||
"lint-staged": {},
|
||||
"lint": {
|
||||
"dependsOn": [
|
||||
"lint-staged"
|
||||
]
|
||||
"dependsOn": ["lint-staged"]
|
||||
},
|
||||
"install-meilisearch": {
|
||||
"inputs": [
|
||||
"./scripts/download-meilisearch.ts"
|
||||
],
|
||||
"dependsOn": [
|
||||
"@tunarr/shared#build"
|
||||
],
|
||||
"inputs": ["./scripts/download-meilisearch.ts"],
|
||||
"dependsOn": ["@tunarr/shared#build"],
|
||||
"cache": false
|
||||
},
|
||||
"dev": {
|
||||
"dependsOn": [
|
||||
"install-meilisearch",
|
||||
"@tunarr/shared#build"
|
||||
],
|
||||
"dependsOn": ["install-meilisearch", "@tunarr/shared#build"],
|
||||
"persistent": true,
|
||||
"cache": false,
|
||||
"interruptible": true
|
||||
},
|
||||
"generate-openapi": {
|
||||
"inputs": [
|
||||
"./src/**"
|
||||
],
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
}
|
||||
"inputs": ["./src/**"],
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"generate-env": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user