refactor: simplify Dockerfile and begin using unified base image (#1247)

The unified based image provides all hardware acceleration methods, so
users will not have install specific hardware variants.

Hardware-specific images will be published for a short time in order to
provide warnings to existing users to migrate their install over to the
non-suffixed version.

Thank you to @jasongdove for his hard work on the base image!
This commit is contained in:
Christian Benincasa
2025-06-24 07:37:59 -04:00
committed by GitHub
parent c358dfc60d
commit b14f6386a3
13 changed files with 236 additions and 180 deletions

View File

@@ -43,25 +43,25 @@ jobs:
max-parallel: 2
matrix:
builds:
- base_tag: '7.0'
- base_tag: '7.1.1'
platform: linux/amd64
suffix:
target: linux-x64
os: ubuntu-latest
- base_tag: 7.0-nvidia
- base_tag: 7.1.1-nvidia
platform: linux/amd64
suffix: -nvidia
target: linux-x64
os: ubuntu-latest
- base_tag: 7.0-vaapi
- base_tag: 7.1.1-vaapi
platform: linux/amd64
suffix: -vaapi
target: linux-x64
os: ubuntu-latest
- base_tag: 7.0-arm64
- base_tag: 7.1.1-arm64
platform: linux/arm64
suffix: -arm64
target: linux-arm64

View File

@@ -38,7 +38,8 @@ RUN corepack enable && corepack enable pnpm
RUN pnpm --version
RUN ln -s /usr/local/bin/ffmpeg /usr/bin/ffmpeg
RUN ln -s /usr/local/bin/ffprobe /usr/bin/ffprobe
ENTRYPOINT [ "/tunarr/tunarr" ]
RUN curl -sfS https://dotenvx.sh | sh
ENTRYPOINT [ "dotenvx", "run", "--", "/tunarr/tunarr" ]
CMD [ "server" ]
# Add Tunarr sources
@@ -57,7 +58,18 @@ COPY CHANGELOG.md CHANGELOG.md
FROM ffmpeg-base AS dev
EXPOSE 5173
WORKDIR /tunarr
COPY . .
COPY ./server server
COPY ./web web
COPY ./shared shared
COPY ./types types
COPY ./scripts scripts
COPY README.md README.md
COPY package.json package.json
COPY pnpm-lock.yaml pnpm-lock.yaml
COPY pnpm-workspace.yaml pnpm-workspace.yaml
COPY turbo.json turbo.json
RUN pnpm install --frozen-lockfile
ENTRYPOINT [ "pnpm" ]
CMD [ "turbo", "dev" ]
@@ -68,44 +80,32 @@ ARG NODE_ENVIRONMENT
ENV NODE_ENV=${NODE_ENVIRONMENT:-production}
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
### Begin server build ###
FROM sources AS build-server
# Install deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
FROM sources AS build-full-stack
ARG exec_target=linux-x64
ARG is_edge_build
ARG tunarr_build
ARG exec_target=linux-x64
# Build common modules
RUN <<EOF
touch server/.env
echo TUNARR_BUILD=${tunarr_build} >> server/.env
echo TUNARR_EDGE_BUILD=${is_edge_build} >> server/.env
cat server/.env
touch .env
echo TUNARR_BUILD="${tunarr_build}" >> .env
echo TUNARR_EDGE_BUILD=${is_edge_build} >> .env
echo TUNARR_BUILD_BASE_TAG=${base_image_tag} >> .env
cat .env
cp .env server/.env
EOF
# Build and bundle
RUN echo "Building target: ${exec_target}"
RUN pnpm turbo --filter=@tunarr/server make-bin -- --target ${exec_target} --no-include-version
### End server build ###
### Begin server web ###
FROM sources AS build-web
# Install deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
# Build common modules
RUN pnpm turbo --filter=@tunarr/web bundle
FROM sources AS build-full-stack
ARG exec_target=linux-x64
# Install deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
# Bundle web in a separate task
RUN NODE_OPTIONS=--max-old-space-size=32768 pnpm turbo bundle --filter=@tunarr/web
RUN echo "Building target: ${exec_target}"
RUN pnpm turbo --filter=@tunarr/server make-bin -- --target ${exec_target} --no-include-version
RUN pnpm turbo make-bin -- --target ${exec_target} --no-include-version
### Full stack ###
FROM ffmpeg-base AS full-stack
WORKDIR /tunarr
ARG exec_target=linux-x64
COPY --from=build-full-stack /tunarr/.env /tunarr/.env
COPY --from=build-full-stack /tunarr/server/bin /tunarr/server/bin
# Create a symlink to the executable in /tunarr. This simplifies things for the
# user, such as volume mapping their legacy DBs, while not interrupting the

136
pnpm-lock.yaml generated
View File

@@ -92,6 +92,9 @@ importers:
server:
dependencies:
'@dotenvx/dotenvx':
specifier: ^1.45.1
version: 1.45.1
'@fastify/cors':
specifier: ^10.0.1
version: 10.0.1
@@ -277,8 +280,8 @@ importers:
specifier: ^17.0.29
version: 17.0.29
'@yao-pkg/pkg':
specifier: ^6.3.0
version: 6.3.0
specifier: ^6.5.1
version: 6.5.1
copyfiles:
specifier: ^2.2.0
version: 2.2.0
@@ -288,9 +291,6 @@ importers:
del-cli:
specifier: ^3.0.0
version: 3.0.0
dotenv:
specifier: ^16.4.5
version: 16.4.5
dotenv-cli:
specifier: ^7.4.1
version: 7.4.1
@@ -1010,9 +1010,19 @@ packages:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
'@dotenvx/dotenvx@1.45.1':
resolution: {integrity: sha512-wKHPD+/NMMJVBPg3i98uD9jsURDy+Ck6RQRiWf39TlOAzC+Ge1FkmDk3sgeljYZxA3qF6E7SJmvRqC70XQuuVA==}
hasBin: true
'@drizzle-team/brocli@0.10.2':
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
'@ecies/ciphers@0.2.3':
resolution: {integrity: sha512-tapn6XhOueMwht3E2UzY0ZZjYokdaw9XtL9kEyjhQ/Fb9vL9xTFbOaI+fV0AWvTpYu4BNloC6getKW6NtSg4mA==}
engines: {bun: '>=1', deno: '>=2', node: '>=16'}
peerDependencies:
'@noble/ciphers': ^1.0.0
'@emotion/babel-plugin@11.13.5':
resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==}
@@ -1974,6 +1984,18 @@ packages:
peerDependencies:
react: ^17.0.0 || ^18.0.0 || ^19.0.0
'@noble/ciphers@1.3.0':
resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==}
engines: {node: ^14.21.3 || >=16}
'@noble/curves@1.9.2':
resolution: {integrity: sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==}
engines: {node: ^14.21.3 || >=16}
'@noble/hashes@1.8.0':
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -2968,12 +2990,12 @@ packages:
'@vitest/utils@3.0.3':
resolution: {integrity: sha512-f+s8CvyzPtMFY1eZKkIHGhPsQgYo5qCm6O8KZoim9qm1/jT64qBgGpO5tHscNH6BzRHM+edLNOP+3vO8+8pE/A==}
'@yao-pkg/pkg-fetch@3.5.19':
resolution: {integrity: sha512-EEURrS1Q5sSSAwaQ4zD0wZsquDiG8CroY3SCi7jYBoM0NG3eRA239mW6YMUYPNWUK4zCMhuK3bzFT5aIZP/rDg==}
'@yao-pkg/pkg-fetch@3.5.23':
resolution: {integrity: sha512-rn45sqVQSkcJNSBdTnYze3n+kyub4CN8aiWYlPgA9yp9FZeEF+BlpL68kSIm3HaVuANniF+7RBMH5DkC4zlHZA==}
hasBin: true
'@yao-pkg/pkg@6.3.0':
resolution: {integrity: sha512-adD3EkqXk+WxZvfMMIJ756EBYKBYr0/6BOBzYb/NrKPKRozwXwe8SKyqr9ntyuPRD+jDX04huDRM52ptee3UsQ==}
'@yao-pkg/pkg@6.5.1':
resolution: {integrity: sha512-z6XlySYfnqfm1AfVlBN8A3yeAQniIwL7TKQfDCGsswYSVYLt2snbRefQYsfQQ3pw5lVXrZdLqgTjzaqID9IkWA==}
engines: {node: '>=18.0.0'}
hasBin: true
@@ -3965,6 +3987,10 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
eciesjs@0.4.15:
resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==}
engines: {bun: '>=1', deno: '>=2', node: '>=16'}
electron-to-chromium@1.4.615:
resolution: {integrity: sha512-/bKPPcgZVUziECqDc+0HkT87+0zhaWSZHNXqF8FLd2lQcptpmUFwoCSWjCdOng9Gdq+afKArPdEg/0ZW461Eng==}
@@ -4360,8 +4386,8 @@ packages:
fastq@1.17.1:
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
fdir@6.4.3:
resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
fdir@6.4.6:
resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
@@ -5059,6 +5085,10 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
isexe@3.1.1:
resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
engines: {node: '>=16'}
iterator.prototype@1.1.4:
resolution: {integrity: sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==}
engines: {node: '>= 0.4'}
@@ -5739,6 +5769,10 @@ packages:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
object-treeify@1.1.33:
resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==}
engines: {node: '>= 10'}
object.assign@4.1.5:
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
engines: {node: '>= 0.4'}
@@ -6864,8 +6898,8 @@ packages:
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
tinyglobby@0.2.10:
resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
tinyglobby@0.2.14:
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
engines: {node: '>=12.0.0'}
tinypool@0.8.4:
@@ -7519,6 +7553,11 @@ packages:
engines: {node: '>= 8'}
hasBin: true
which@4.0.0:
resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==}
engines: {node: ^16.13.0 || >=18.0.0}
hasBin: true
why-is-node-running@2.2.2:
resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
engines: {node: '>=8'}
@@ -7734,7 +7773,7 @@ snapshots:
'@babel/generator@7.24.7':
dependencies:
'@babel/types': 7.26.7
'@babel/types': 7.27.1
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
jsesc: 2.5.2
@@ -7841,15 +7880,13 @@ snapshots:
'@babel/helper-string-parser@7.25.9': {}
'@babel/helper-string-parser@7.27.1':
optional: true
'@babel/helper-string-parser@7.27.1': {}
'@babel/helper-validator-identifier@7.24.7': {}
'@babel/helper-validator-identifier@7.25.9': {}
'@babel/helper-validator-identifier@7.27.1':
optional: true
'@babel/helper-validator-identifier@7.27.1': {}
'@babel/helper-validator-option@7.24.7': {}
@@ -7878,7 +7915,6 @@ snapshots:
'@babel/parser@7.27.2':
dependencies:
'@babel/types': 7.27.1
optional: true
'@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.7)':
dependencies:
@@ -7975,7 +8011,6 @@ snapshots:
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
optional: true
'@changesets/apply-release-plan@7.0.4':
dependencies:
@@ -8251,8 +8286,24 @@ snapshots:
'@jridgewell/trace-mapping': 0.3.9
optional: true
'@dotenvx/dotenvx@1.45.1':
dependencies:
commander: 11.1.0
dotenv: 16.4.5
eciesjs: 0.4.15
execa: 5.1.1
fdir: 6.4.6(picomatch@4.0.2)
ignore: 5.3.1
object-treeify: 1.1.33
picomatch: 4.0.2
which: 4.0.0
'@drizzle-team/brocli@0.10.2': {}
'@ecies/ciphers@0.2.3(@noble/ciphers@1.3.0)':
dependencies:
'@noble/ciphers': 1.3.0
'@emotion/babel-plugin@11.13.5':
dependencies:
'@babel/helper-module-imports': 7.24.7
@@ -9019,6 +9070,14 @@ snapshots:
transitivePeerDependencies:
- '@types/react'
'@noble/ciphers@1.3.0': {}
'@noble/curves@1.9.2':
dependencies:
'@noble/hashes': 1.8.0
'@noble/hashes@1.8.0': {}
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -10145,7 +10204,7 @@ snapshots:
loupe: 3.1.2
tinyrainbow: 2.0.0
'@yao-pkg/pkg-fetch@3.5.19':
'@yao-pkg/pkg-fetch@3.5.23':
dependencies:
https-proxy-agent: 5.0.1
node-fetch: 2.7.0
@@ -10158,12 +10217,12 @@ snapshots:
- encoding
- supports-color
'@yao-pkg/pkg@6.3.0':
'@yao-pkg/pkg@6.5.1':
dependencies:
'@babel/generator': 7.24.7
'@babel/parser': 7.26.7
'@babel/types': 7.26.7
'@yao-pkg/pkg-fetch': 3.5.19
'@babel/parser': 7.27.2
'@babel/types': 7.27.1
'@yao-pkg/pkg-fetch': 3.5.23
into-stream: 6.0.0
minimist: 1.2.8
multistream: 4.1.0
@@ -10173,7 +10232,7 @@ snapshots:
resolve: 1.22.10
stream-meter: 1.0.4
tar: 7.4.3
tinyglobby: 0.2.10
tinyglobby: 0.2.14
unzipper: 0.12.3
transitivePeerDependencies:
- encoding
@@ -11132,6 +11191,13 @@ snapshots:
eastasianwidth@0.2.0: {}
eciesjs@0.4.15:
dependencies:
'@ecies/ciphers': 0.2.3(@noble/ciphers@1.3.0)
'@noble/ciphers': 1.3.0
'@noble/curves': 1.9.2
'@noble/hashes': 1.8.0
electron-to-chromium@1.4.615: {}
emoji-regex@10.3.0: {}
@@ -11785,7 +11851,7 @@ snapshots:
dependencies:
reusify: 1.0.4
fdir@6.4.3(picomatch@4.0.2):
fdir@6.4.6(picomatch@4.0.2):
optionalDependencies:
picomatch: 4.0.2
@@ -12068,7 +12134,7 @@ snapshots:
dir-glob: 3.0.1
fast-glob: 3.3.3
glob: 7.2.3
ignore: 5.2.4
ignore: 5.3.1
merge2: 1.4.1
slash: 3.0.0
@@ -12490,6 +12556,8 @@ snapshots:
isexe@2.0.0: {}
isexe@3.1.1: {}
iterator.prototype@1.1.4:
dependencies:
define-data-property: 1.1.4
@@ -13309,6 +13377,8 @@ snapshots:
object-keys@1.1.1: {}
object-treeify@1.1.33: {}
object.assign@4.1.5:
dependencies:
call-bind: 1.0.7
@@ -14591,9 +14661,9 @@ snapshots:
tinyexec@0.3.2: {}
tinyglobby@0.2.10:
tinyglobby@0.2.14:
dependencies:
fdir: 6.4.3(picomatch@4.0.2)
fdir: 6.4.6(picomatch@4.0.2)
picomatch: 4.0.2
tinypool@0.8.4: {}
@@ -14983,7 +15053,7 @@ snapshots:
dependencies:
browserslist: 4.22.2
escalade: 3.1.1
picocolors: 1.0.1
picocolors: 1.1.1
uri-js@4.4.1:
dependencies:
@@ -15370,6 +15440,10 @@ snapshots:
dependencies:
isexe: 2.0.0
which@4.0.0:
dependencies:
isexe: 3.1.1
why-is-node-running@2.2.2:
dependencies:
siginfo: 2.0.0

View File

@@ -12,7 +12,7 @@
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 tsc -p tsconfig.build.json",
"build-dev": "cross-env NODE_ENV=development tsc -p tsconfig.build.json --noEmit --watch",
"typecheck": "tsc -p tsconfig.build.json --noEmit",
"bundle": "dotenv -- tsx scripts/bundle-old.ts",
"bundle": "dotenv -- tsx scripts/bundle.ts",
"make-bin": "dotenv -- tsx scripts/make-bin.ts",
"clean": "rimraf --glob ./build/ ./dist/ ./src/generated/web-imports.ts ./src/generated/web-imports.js",
"debug": "dotenv -e .env.development -- tsx watch --trace-warnings --tsconfig ./tsconfig.build.json --ignore 'src/streams' --inspect-wait ./src",
@@ -25,6 +25,7 @@
"test:watch": "vitest --watch"
},
"dependencies": {
"@dotenvx/dotenvx": "^1.45.1",
"@fastify/cors": "^10.0.1",
"@fastify/error": "^4.1.0",
"@fastify/multipart": "^9.0.1",
@@ -88,11 +89,10 @@
"@types/unzip-stream": "^0.3.4",
"@types/uuid": "^9.0.6",
"@types/yargs": "^17.0.29",
"@yao-pkg/pkg": "^6.3.0",
"@yao-pkg/pkg": "^6.5.1",
"copyfiles": "^2.2.0",
"cross-env": "^7.0.3",
"del-cli": "^3.0.0",
"dotenv": "^16.4.5",
"dotenv-cli": "^7.4.1",
"drizzle-kit": "^0.30.4",
"esbuild-plugin-pino": "^2.2.1",

View File

@@ -1,94 +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_BUILD': `"${process.env.TUNARR_BUILD}"`,
'process.env.TUNARR_EDGE_BUILD': `"${isEdgeBuild}"`,
'import.meta.url': '__import_meta_url',
},
});
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);

View File

@@ -1,7 +1,14 @@
import Bun from 'bun';
import { bunPluginPino } from 'bun-plugin-pino';
import '@dotenvx/dotenvx/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 { format } from 'node:util';
import { rimraf } from 'rimraf';
import { nativeNodeModulesPlugin } from '../esbuild/native-node-module.ts';
import { nodeProtocolPlugin } from '../esbuild/node-protocol.ts';
const DIST_DIR = 'dist';
@@ -17,23 +24,37 @@ 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';
console.log('Bundling app...');
process.env.NODE_ENV = 'production';
await Bun.build({
entrypoints: ['src/index.ts'],
outdir: DIST_DIR,
target: 'bun',
naming: '[dir]/bundle.[ext]',
minify: {
whitespace: true,
identifiers: false,
syntax: true,
const result = await esbuild.build({
entryPoints: {
bundle: 'src/index.ts',
},
sourcemap: 'linked',
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',
@@ -45,14 +66,40 @@ await Bun.build({
'mariadb',
'libsql',
],
mainFields: ['module', 'main'],
plugins: [
bunPluginPino({
nativeNodeModulesPlugin(),
nodeProtocolPlugin(),
// copy({
// resolveFrom: 'cwd',
// assets: {
// from: ['node_modules/@fastify/swagger-ui/static/*'],
// to: ['build/static'],
// },
// }),
esbuildPluginPino({
transports: ['pino-pretty', 'pino-roll'],
logging: 'quiet',
}),
],
keepNames: true, // This is to ensure that Entity class names remain the same
metafile: true,
define: {
'process.env.NODE_ENV': '"production"',
'process.env.TUNARR_BUILD': `"${process.env.TUNARR_BUILD}"`,
'process.env.TUNARR_EDGE_BUILD': `"${isEdgeBuild}"`,
'import.meta.url': '__import_meta_url',
},
});
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);

View File

@@ -159,7 +159,7 @@ for (const arch of args.target) {
'-c',
'pkg.config.json',
'-t',
`node22.13.1-${arch}`,
`node22.15.1-${arch}`,
`${dir.path}/dist/bundle.cjs`,
// Look into whether we want this sometimes...
'--no-bytecode',

View File

@@ -58,7 +58,9 @@ export async function bootstrapTunarr(
) {
const hasTunarrDb = await fileExists(opts.databaseDirectory);
if (!hasTunarrDb) {
RootLogger.info(`Existing database at ${opts.databaseDirectory} not found`);
RootLogger.info(
`Existing database at ${opts.databaseDirectory} not found, creating it.`,
);
await fs.mkdir(opts.databaseDirectory, { recursive: true });
await migrateFromPreAlphaDefaultDb(opts.databaseDirectory);
}

View File

@@ -224,7 +224,7 @@ class Connection {
results?.forEach((it) => {
if (it.status === 'Success') {
this.logger.info(
this.logger.debug(
`migration "${it.migrationName}" was executed successfully`,
);
} else if (it.status === 'Error') {

View File

@@ -1,3 +1,6 @@
import dotenv from '@dotenvx/dotenvx';
dotenv.config({ debug: true });
import { bootstrapTunarr } from '@/bootstrap.js';
import { setGlobalOptions } from '@/globals.js';
import {

View File

@@ -8,7 +8,9 @@ import { KEYS } from '../types/inject.ts';
import { Maybe } from '../types/util.ts';
import { cacheGetOrSet } from '../util/cache.ts';
import { ChildProcessHelper } from '../util/ChildProcessHelper.ts';
import { isLinux, isNonEmptyString } from '../util/index.ts';
import { isDocker } from '../util/containerUtil.ts';
import { fileExists } from '../util/fsUtil.ts';
import { isLinux, isMac, isNonEmptyString, isWindows } from '../util/index.ts';
import type { Logger } from '../util/logging/LoggerFactory.ts';
@injectable()
@@ -49,6 +51,24 @@ export class SystemDevicesService {
SystemDevicesService.DEVICES_KEY,
async () => {
const devices: string[] = [];
if (isWindows() || isMac()) {
return [];
}
if (!(await fileExists('/dev/dri'))) {
if (isDocker()) {
this.logger.warn(
'Could not find /dev/dri directory. Did you passed this in as a device when starting your container?',
);
} else {
this.logger.error(
'Unexpected state. Found no /dev/dri on Linux machine.',
);
}
return [];
}
for (const device of await readdir('/dev/dri')) {
if (device.startsWith('card') || device.startsWith('render')) {
devices.push(path.join('/dev/dri', device));

View File

@@ -447,7 +447,7 @@ export const isTest = currentEnv() === 'test';
export const isEdgeBuild = getBooleanEnvVar(
TUNARR_ENV_VARS.IS_EDGE_BUILD_ENV_VAR,
);
export const tunarrBuild = process.env[TUNARR_ENV_VARS.BUILD_ENV_VAR];
export const tunarrBuild = process.env.TUNARR_BUILD;
export const zipWithIndex = <T>(
seq: readonly T[],
@@ -505,6 +505,10 @@ export function isLinux() {
return process.platform === 'linux';
}
export function isMac() {
return process.platform === 'darwin';
}
export function isWindows() {
return process.platform === 'win32';
}

View File

@@ -13,7 +13,7 @@
"outputs": ["temp/metadata.json"]
},
"bundle": {
"inputs": ["./scripts/bundle-old.ts", "./src/**"],
"inputs": ["./scripts/bundle.ts", "./src/**"],
"dependsOn": ["^build", "^bundle"],
"outputs": ["dist/**"]
},