mirror of
https://github.com/morten-olsen/mini-loader.git
synced 2026-02-08 01:36:26 +01:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a08f9e1c91 | ||
|
|
e0c41d9220 | ||
|
|
028b65587e | ||
|
|
7436b3439c |
17
.github/workflows/release.yml
vendored
17
.github/workflows/release.yml
vendored
@@ -71,12 +71,24 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Retrieve version
|
||||
run: |
|
||||
echo "TAG_NAME=$(git describe --tag --abbrev=0) >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
@@ -84,11 +96,16 @@ jobs:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
latest
|
||||
${{ steps.version.outputs.TAG_NAME }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
@@ -27,6 +27,10 @@ COPY --from=builder /app/out/full/ .
|
||||
RUN pnpm turbo run build --filter=@morten-olsen/mini-loader-server
|
||||
|
||||
FROM base AS runner
|
||||
ENV \
|
||||
NODE_ENV=production \
|
||||
DATA_DIR=/data \
|
||||
CACHE_DIR=/cache
|
||||
RUN apk add --no-cache jq curl
|
||||
WORKDIR /app
|
||||
|
||||
@@ -39,7 +43,7 @@ RUN chmod +x /entrypoint.sh
|
||||
|
||||
COPY --from=installer /app .
|
||||
EXPOSE 4500
|
||||
VOLUME /app/data
|
||||
VOLUME /data
|
||||
|
||||
HEALTHCHECK \
|
||||
--interval=10s \
|
||||
|
||||
@@ -7,6 +7,8 @@ GID=${GID:-1001}
|
||||
addgroup --system --gid ${GID} nodejs && \
|
||||
adduser --system --uid ${UID} -G nodejs miniloader && \
|
||||
|
||||
mkdir -p /app/data
|
||||
chown -R miniloader:nodejs /app/data
|
||||
mkdir -p ${DATA_DIR}
|
||||
mkdir -p ${CACHE_DIR}
|
||||
chown -R miniloader:nodejs ${DATA_DIR}
|
||||
chown -R miniloader:nodejs ${CACHE_DIR}
|
||||
su miniloader -s /bin/sh -c "$CMD"
|
||||
@@ -29,4 +29,4 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/morten-olsen/mini-loader"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import 'source-map-support/register.js';
|
||||
import '../dist/esm/index.js';
|
||||
import '../dist/esm/src/index.js';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "@morten-olsen/mini-loader-cli",
|
||||
"version": "1.0.0",
|
||||
"main": "./dist/esm/index.js",
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"main": "./dist/esm/src/index.js",
|
||||
"types": "./dist/esm/src/index.d.ts",
|
||||
"license": "GPL-3.0",
|
||||
"bin": {
|
||||
"mini-loader": "./bin/index.mjs"
|
||||
@@ -16,11 +16,12 @@
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/esm/index.js"
|
||||
"import": "./dist/esm/src/index.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@morten-olsen/mini-loader-runner": "workspace:^",
|
||||
"@morten-olsen/mini-loader-server": "workspace:^",
|
||||
"@rollup/plugin-auto-install": "^3.0.5",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
@@ -40,7 +41,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@morten-olsen/mini-loader-configs": "workspace:^",
|
||||
"@morten-olsen/mini-loader-server": "workspace:^",
|
||||
"@types/inquirer": "^9.0.7",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import { run as runLoad } from '@morten-olsen/mini-loader-runner';
|
||||
import { bundle } from '../../bundler/bundler.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { readSecrets } from './local.utils.js';
|
||||
import { Config } from '../../config/config.js';
|
||||
|
||||
const run = new Command('run');
|
||||
|
||||
@@ -12,6 +13,7 @@ run
|
||||
.argument('script')
|
||||
.action(async (script) => {
|
||||
const location = resolve(script);
|
||||
const config = new Config();
|
||||
const { autoInstall } = run.opts();
|
||||
const secrets = await readSecrets();
|
||||
|
||||
@@ -21,6 +23,7 @@ run
|
||||
const { promise, emitter } = await runLoad({
|
||||
script: code,
|
||||
secrets,
|
||||
cacheLocation: config.cacheLocation,
|
||||
});
|
||||
emitter.addListener('message', (message) => {
|
||||
switch (message.type) {
|
||||
|
||||
@@ -7,12 +7,13 @@ type ConfigValues = {
|
||||
context?: string;
|
||||
};
|
||||
|
||||
const paths = envPaths('mini-loader');
|
||||
|
||||
class Config {
|
||||
#location: string;
|
||||
#config?: ConfigValues;
|
||||
|
||||
constructor() {
|
||||
const paths = envPaths('mini-loader');
|
||||
this.#location = join(paths.config, 'config.json');
|
||||
if (existsSync(this.#location)) {
|
||||
this.#config = JSON.parse(readFileSync(this.#location, 'utf-8'));
|
||||
@@ -23,6 +24,10 @@ class Config {
|
||||
return this.#config?.context || 'default';
|
||||
}
|
||||
|
||||
public get cacheLocation() {
|
||||
return join(paths.cache, this.context);
|
||||
}
|
||||
|
||||
public setContext = (context: string) => {
|
||||
this.#config = {
|
||||
...(this.#config || {}),
|
||||
|
||||
@@ -5,10 +5,11 @@ type RunOptions = {
|
||||
script: string;
|
||||
input?: Buffer | string;
|
||||
secrets?: Record<string, string>;
|
||||
cacheLocation: string;
|
||||
};
|
||||
|
||||
const run = async ({ script, input, secrets }: RunOptions) => {
|
||||
const info = await setup({ script, input, secrets });
|
||||
const run = async ({ script, input, secrets, cacheLocation }: RunOptions) => {
|
||||
const info = await setup({ script, input, secrets, cacheLocation });
|
||||
|
||||
const worker = new Worker(info.scriptLocation, {
|
||||
stdin: false,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { join } from 'path';
|
||||
import os from 'os';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { chmod, mkdir, rm, writeFile } from 'fs/promises';
|
||||
import { createServer } from 'net';
|
||||
@@ -9,6 +8,7 @@ type SetupOptions = {
|
||||
input?: Buffer | string;
|
||||
script: string;
|
||||
secrets?: Record<string, string>;
|
||||
cacheLocation: string;
|
||||
};
|
||||
|
||||
type RunEvents = {
|
||||
@@ -20,7 +20,7 @@ type RunEvents = {
|
||||
const setup = async (options: SetupOptions) => {
|
||||
const { input, script, secrets } = options;
|
||||
const emitter = new EventEmitter<RunEvents>();
|
||||
const dataDir = join(os.tmpdir(), 'mini-loader', nanoid());
|
||||
const dataDir = join(options.cacheLocation, nanoid());
|
||||
|
||||
await mkdir(dataDir, { recursive: true });
|
||||
await chmod(dataDir, 0o700);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import 'source-map-support/register.js';
|
||||
import '../dist/esm/index.js';
|
||||
import '../dist/esm/src/index.js';
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"name": "@morten-olsen/mini-loader-server",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0",
|
||||
"main": "./dist/esm/index.js",
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"main": "./dist/esm/src/index.js",
|
||||
"types": "./dist/esm/src/index.d.ts",
|
||||
"bin": {
|
||||
"mini-loader-server": "./bin/index.mjs"
|
||||
},
|
||||
@@ -16,7 +16,7 @@
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/esm/index.js"
|
||||
"import": "./dist/esm/src/index.js"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -32,6 +32,7 @@
|
||||
"@trpc/server": "^10.45.0",
|
||||
"commander": "^11.1.0",
|
||||
"cron": "^3.1.6",
|
||||
"env-paths": "^3.0.0",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"fastify": "^4.25.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
|
||||
@@ -20,10 +20,10 @@ class Auth {
|
||||
|
||||
#setup = async () => {
|
||||
const { config } = this.#options;
|
||||
const secretLocation = resolve(config.files.location, 'secret');
|
||||
const secretLocation = resolve(config.files.data, 'secret');
|
||||
let secret = '';
|
||||
await mkdir(config.files.data, { recursive: true });
|
||||
if (!existsSync(secretLocation)) {
|
||||
await mkdir(config.files.location, { recursive: true });
|
||||
secret = nanoid();
|
||||
await writeFile(secretLocation, secret);
|
||||
} else {
|
||||
|
||||
@@ -3,7 +3,8 @@ import { Knex } from 'knex';
|
||||
type Config = {
|
||||
database: Omit<Knex.Config, 'migrations'>;
|
||||
files: {
|
||||
location: string;
|
||||
data: string;
|
||||
cache: string;
|
||||
};
|
||||
auth?: {
|
||||
oidc?: {
|
||||
|
||||
@@ -62,7 +62,7 @@ class LoadRepo extends EventEmitter<LoadRepoEvents> {
|
||||
const db = await database.instance;
|
||||
const id = options.id || nanoid();
|
||||
const script = createHash('sha256').update(options.script).digest('hex');
|
||||
const scriptDir = resolve(this.#options.config.files.location, 'scripts');
|
||||
const scriptDir = resolve(this.#options.config.files.data, 'scripts');
|
||||
await mkdir(scriptDir, { recursive: true });
|
||||
await writeFile(resolve(scriptDir, `${script}.js`), options.script);
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class RunnerInstance extends EventEmitter<RunnerInstanceEvents> {
|
||||
const { runs, secrets } = repos;
|
||||
try {
|
||||
const { script: scriptHash, input } = await runs.getById(id);
|
||||
const scriptLocation = resolve(config.files.location, 'scripts', `${scriptHash}.js`);
|
||||
const scriptLocation = resolve(config.files.data, 'scripts', `${scriptHash}.js`);
|
||||
const script = await readFile(scriptLocation, 'utf-8');
|
||||
const allSecrets = await secrets.getAll();
|
||||
await runs.started(id);
|
||||
@@ -67,6 +67,7 @@ class RunnerInstance extends EventEmitter<RunnerInstanceEvents> {
|
||||
script,
|
||||
secrets: allSecrets,
|
||||
input,
|
||||
cacheLocation: config.files.cache,
|
||||
});
|
||||
this.#run = current;
|
||||
const { promise, emitter } = current;
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { resolve } from 'path';
|
||||
import envPaths from 'env-paths';
|
||||
import { Database } from '../database/database.js';
|
||||
import { Repos } from '../repos/repos.js';
|
||||
import { Runner } from '../runner/runner.js';
|
||||
import { Config } from '../config/config.js';
|
||||
import { Auth } from '../auth/auth.js';
|
||||
import { resolve } from 'path';
|
||||
import { Scheduler } from '../scheduler/scheduler.js';
|
||||
|
||||
const paths = envPaths('mini-loader-server');
|
||||
|
||||
class Runtime {
|
||||
#repos: Repos;
|
||||
#runner: Runner;
|
||||
@@ -41,12 +44,13 @@ class Runtime {
|
||||
database: {
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: resolve(process.cwd(), 'data', 'database.sqlite'),
|
||||
filename: resolve(paths.data, 'database.sqlite'),
|
||||
},
|
||||
useNullAsDefault: true,
|
||||
},
|
||||
files: {
|
||||
location: resolve(process.cwd(), 'data', 'files'),
|
||||
data: process.env.DATA_DIR || resolve(paths.data, 'data', 'files'),
|
||||
cache: process.env.CACHE_DIR || resolve(paths.cache, 'data', 'cache'),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import pkg from '../../package.json';
|
||||
import { fastifyTRPCPlugin, FastifyTRPCPluginOptions } from '@trpc/server/adapters/fastify';
|
||||
import fastify from 'fastify';
|
||||
import { RootRouter, rootRouter } from '../router/router.js';
|
||||
@@ -13,9 +14,6 @@ const createServer = async (runtime: Runtime) => {
|
||||
level: 'warn',
|
||||
},
|
||||
});
|
||||
server.get('/', async () => {
|
||||
return { hello: 'world' };
|
||||
});
|
||||
|
||||
server.get('/health', async (req) => {
|
||||
let authorized = false;
|
||||
@@ -27,7 +25,7 @@ const createServer = async (runtime: Runtime) => {
|
||||
authorized = true;
|
||||
}
|
||||
} catch (error) {}
|
||||
return { authorized, status: 'ok' };
|
||||
return { authorized, status: 'ok', version: pkg.version };
|
||||
});
|
||||
|
||||
server.register(fastifyTRPCPlugin, {
|
||||
|
||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -39,6 +39,9 @@ importers:
|
||||
'@morten-olsen/mini-loader-runner':
|
||||
specifier: workspace:^
|
||||
version: link:../runner
|
||||
'@morten-olsen/mini-loader-server':
|
||||
specifier: workspace:^
|
||||
version: link:../server
|
||||
'@rollup/plugin-auto-install':
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5(rollup@4.9.4)
|
||||
@@ -91,9 +94,6 @@ importers:
|
||||
'@morten-olsen/mini-loader-configs':
|
||||
specifier: workspace:^
|
||||
version: link:../configs
|
||||
'@morten-olsen/mini-loader-server':
|
||||
specifier: workspace:^
|
||||
version: link:../server
|
||||
'@types/inquirer':
|
||||
specifier: ^9.0.7
|
||||
version: 9.0.7
|
||||
@@ -176,6 +176,9 @@ importers:
|
||||
cron:
|
||||
specifier: ^3.1.6
|
||||
version: 3.1.6
|
||||
env-paths:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
eventemitter3:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
|
||||
Reference in New Issue
Block a user