diff --git a/.changeset/hungry-ants-decide.md b/.changeset/hungry-ants-decide.md new file mode 100644 index 0000000..a378eb8 --- /dev/null +++ b/.changeset/hungry-ants-decide.md @@ -0,0 +1,5 @@ +--- +"kopflos": patch +--- + +Added `kopflos deploy` command diff --git a/.changeset/orange-years-wait.md b/.changeset/orange-years-wait.md new file mode 100644 index 0000000..b09310c --- /dev/null +++ b/.changeset/orange-years-wait.md @@ -0,0 +1,5 @@ +--- +"@kopflos-cms/plugin-deploy-resources": patch +--- + +Export function to deploy programmatically diff --git a/example/docker-compose.yaml b/example/docker-compose.yaml index 4aa8bba..2bd8a09 100644 --- a/example/docker-compose.yaml +++ b/example/docker-compose.yaml @@ -1,7 +1,7 @@ version: '2' services: oxigraph: - image: ghcr.io/oxigraph/oxigraph:0.4.1 + image: ghcr.io/oxigraph/oxigraph:0.4.2 user: root command: serve --location /data --bind 0.0.0.0:7878 ports: diff --git a/example/package.json b/example/package.json index 2d42385..7b6ac6c 100644 --- a/example/package.json +++ b/example/package.json @@ -6,6 +6,7 @@ "scripts": { "build": "kopflos build", "start": "kopflos serve --mode development --trust-proxy", + "deploy": "kopflos deploy", "prestart:prod": "npm run build", "start:prod": "kopflos serve --trust-proxy" }, diff --git a/package-lock.json b/package-lock.json index ca9439a..980f37c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,13 +35,13 @@ } }, "example": { - "version": "0.0.1-beta.0", + "version": "0.0.1-beta.2", "dependencies": { - "@kopflos-cms/serve-file": "0.1.0-beta.0", - "@kopflos-cms/vite": "0.0.1-beta.0", - "@kopflos-labs/handlebars": "0.1.0-beta.0", - "@kopflos-labs/html-template": "0.1.0-beta.0", - "@kopflos-labs/lit": "0.1.0-beta.0", + "@kopflos-cms/serve-file": "0.1.0-beta.1", + "@kopflos-cms/vite": "0.0.1-beta.1", + "@kopflos-labs/handlebars": "0.1.0-beta.1", + "@kopflos-labs/html-template": "0.1.0-beta.1", + "@kopflos-labs/lit": "0.1.0-beta.1", "@openlayers-elements/core": "^0.3.0", "@openlayers-elements/maps": "^0.3.0", "@shoelace-style/shoelace": "^2.17.1", @@ -49,7 +49,7 @@ "compression": "^1.7.4", "cors": "^2.8.5", "express": "^5.0.1", - "kopflos": "0.1.0-beta.0", + "kopflos": "0.1.0-beta.2", "lit-element": "^4.1.1" }, "devDependencies": { @@ -58,14 +58,14 @@ }, "labs/handlebars": { "name": "@kopflos-labs/handlebars", - "version": "0.1.0-beta.0", + "version": "0.1.0-beta.1", "license": "MIT", "dependencies": { "@zazuko/env": "^2.3.0", "@zazuko/prefixes": "^2.2.0", "clownface-shacl-path": "^2.4.0", "handlebars": "^4.7.8", - "sparql-path-parser": "^0.1.0-beta.0" + "sparql-path-parser": "^0.1.0-beta.1" }, "devDependencies": { "chai": "^5.1.1", @@ -74,17 +74,17 @@ }, "labs/html-template": { "name": "@kopflos-labs/html-template", - "version": "0.1.0-beta.0", + "version": "0.1.0-beta.1", "license": "MIT", "dependencies": { - "@kopflos-cms/logger": "^0.1.0-beta.0", + "@kopflos-cms/logger": "^0.1.0-beta.1", "@zazuko/env": "^2.3.0", "@zazuko/prefixes": "^2.2.0", "cheerio": "^1.0.0", "htmlparser2": "^9.1.0" }, "devDependencies": { - "@kopflos-cms/core": "^0.3.0-beta.9", + "@kopflos-cms/core": "^0.3.0-beta.10", "@types/chai-html": "^3.0.0", "@types/sinon": "^17.0.3", "@zazuko/env-node": "^2.1.4", @@ -129,7 +129,7 @@ }, "labs/lit": { "name": "@kopflos-labs/lit", - "version": "0.1.0-beta.0", + "version": "0.1.0-beta.1", "dependencies": { "@lit-labs/ssr": "^3.2.2", "@lit-labs/ssr-client": "^1.1.7", @@ -18406,12 +18406,12 @@ }, "packages/cli": { "name": "kopflos", - "version": "0.1.0-beta.0", + "version": "0.1.0-beta.2", "license": "MIT", "dependencies": { - "@kopflos-cms/express": "^0.1.0-beta.5", - "@kopflos-cms/logger": "^0.1.0-beta.0", - "@kopflos-cms/plugin-deploy-resources": "^0.1.0-beta.0", + "@kopflos-cms/express": "^0.1.0-beta.6", + "@kopflos-cms/logger": "^0.1.0-beta.1", + "@kopflos-cms/plugin-deploy-resources": "^0.1.0-beta.1", "commander": "^12.0.0", "cosmiconfig": "^9.0.0", "express": "^5.0.1", @@ -18426,10 +18426,10 @@ }, "packages/core": { "name": "@kopflos-cms/core", - "version": "0.3.0-beta.9", + "version": "0.3.0-beta.10", "license": "MIT", "dependencies": { - "@kopflos-cms/logger": "^0.1.0-beta.0", + "@kopflos-cms/logger": "^0.1.0-beta.1", "@rdfjs/types": "^1.1.0", "@tpluscode/sparql-builder": "^3.0.0", "@types/clownface": "^2.0.8", @@ -18476,11 +18476,11 @@ }, "packages/express": { "name": "@kopflos-cms/express", - "version": "0.1.0-beta.5", + "version": "0.1.0-beta.6", "license": "MIT", "dependencies": { - "@kopflos-cms/core": "^0.3.0-beta.9", - "@kopflos-cms/logger": "^0.1.0-beta.0", + "@kopflos-cms/core": "^0.3.0-beta.10", + "@kopflos-cms/logger": "^0.1.0-beta.1", "@rdfjs/express-handler": "^2.0.2", "@zazuko/env-node": "^2.1.3", "absolute-url": "^2.0.0", @@ -18522,23 +18522,23 @@ }, "packages/logger": { "name": "@kopflos-cms/logger", - "version": "0.1.0-beta.0", + "version": "0.1.0-beta.1", "dependencies": { "anylogger": "^1.0.11" } }, "packages/plugin-deploy-resources": { "name": "@kopflos-cms/plugin-deploy-resources", - "version": "0.1.0-beta.0", + "version": "0.1.0-beta.1", "license": "MIT", "dependencies": { "@hydrofoil/resource-store": "^0.2.2", "@hydrofoil/talos-core": "^0.2", - "@kopflos-cms/logger": "^0.1.0-beta.0", + "@kopflos-cms/logger": "^0.1.0-beta.1", "anylogger": "^1.0.11" }, "devDependencies": { - "@kopflos-cms/core": "^0.3.0-beta.9", + "@kopflos-cms/core": "^0.3.0-beta.10", "@zazuko/env-node": "^2.1.3", "chai": "^5.1.1", "mocha-chai-rdf": "^0.1.4" @@ -18546,7 +18546,7 @@ }, "packages/serve-file": { "name": "@kopflos-cms/serve-file", - "version": "0.1.0-beta.0", + "version": "0.1.0-beta.1", "dependencies": { "mime": "^4.0.4" }, @@ -18569,7 +18569,7 @@ } }, "packages/sparql-path-parser": { - "version": "0.1.0-beta.0", + "version": "0.1.0-beta.1", "dependencies": { "@tpluscode/rdf-ns-builders": "^4", "@types/sparqljs": "^3.1.11", @@ -18597,10 +18597,10 @@ }, "packages/vite": { "name": "@kopflos-cms/vite", - "version": "0.0.1-beta.0", + "version": "0.0.1-beta.1", "license": "MIT", "dependencies": { - "@kopflos-cms/logger": "^0.1.0-beta.0", + "@kopflos-cms/logger": "^0.1.0-beta.1", "express": "^5.0.1", "glob": "^11.0.0", "onetime": "^7.0.0", diff --git a/packages/cli/index.ts b/packages/cli/index.ts index 2f0611e..9da0891 100755 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -1,23 +1,12 @@ import 'ulog' -import express from 'express' import { program } from 'commander' -import kopflos from '@kopflos-cms/express' -import log from '@kopflos-cms/logger' -import { loadPlugins } from '@kopflos-cms/core/plugins.js' // eslint-disable-line import/no-unresolved -import { loadConfig } from './lib/config.js' import { variable } from './lib/options.js' +import deploy from './lib/command/deploy.js' +import build from './lib/command/build.js' +import serve from './lib/command/serve.js' program.name('kopflos') -interface ServeArgs { - mode?: 'development' | 'production' | unknown - config?: string - port?: number - host?: string - trustProxy?: boolean - variable: Record -} - program.command('serve') .description('Start the server') .option('-m, --mode ', 'Mode to run in (default: "production")') @@ -26,65 +15,14 @@ program.command('serve') .option('-h, --host ', 'Host to bind to (default: "0.0.0.0")') .addOption(variable) .option('--trust-proxy [proxy]', 'Trust the X-Forwarded-Host header') - .action(async ({ mode: _mode = 'production', config, port = 1429, host = '0.0.0.0', trustProxy, variable }: ServeArgs) => { - let mode: 'development' | 'production' - if (_mode !== 'development' && _mode !== 'production') { - log.warn('Invalid mode, defaulting to "production"') - mode = 'production' - } else { - mode = _mode - } - - const loadedConfig = await loadConfig({ - path: config, - }) - - const finalOptions = { - port, - host, - mode, - ...loadedConfig, - variables: { - ...loadedConfig.variables, - ...variable, - }, - } - - const app = express() - - if (trustProxy) { - app.set('trust proxy', trustProxy) - } - - const { instance, middleware } = await kopflos(finalOptions) - app.use(middleware) - - await instance.start() - - app.listen(port, host, () => { - log.info(`Server running on ${port}. API URL: ${finalOptions.baseIri}`) - }) - }) - -interface BuildArgs { - config?: string -} + .action(serve) program.command('build') .option('-c, --config ', 'Path to config file') - .action(async ({ config: configPath }: BuildArgs) => { - const config = await loadConfig({ - path: configPath, - }) - const plugins = await loadPlugins(config.plugins) + .action(build) - log.info('Running build actions...') - const buildActions = plugins.map(plugin => plugin.build?.()) - if (buildActions.length === 0) { - return log.warn('No plugins with build actions found') - } else { - await Promise.all(buildActions) - } - }) +program.command('deploy') + .option('-c, --config ', 'Path to config file') + .action(deploy) program.parse(process.argv) diff --git a/packages/cli/lib/command/build.ts b/packages/cli/lib/command/build.ts new file mode 100644 index 0000000..dd8f0e5 --- /dev/null +++ b/packages/cli/lib/command/build.ts @@ -0,0 +1,22 @@ +import log from '@kopflos-cms/logger' +import { loadPlugins } from '@kopflos-cms/core/plugins.js' // eslint-disable-line import/no-unresolved +import { loadConfig } from '../config.js' + +interface BuildArgs { + config?: string +} + +export default async function (args: BuildArgs) { + const config = await loadConfig({ + path: args.config, + }) + const plugins = await loadPlugins(config.plugins) + + log.info('Running build actions...') + const buildActions = plugins.map(plugin => plugin.build?.()) + if (buildActions.length === 0) { + return log.warn('No plugins with build actions found') + } else { + await Promise.all(buildActions) + } +} diff --git a/packages/cli/lib/command/deploy.ts b/packages/cli/lib/command/deploy.ts new file mode 100644 index 0000000..b6d3d37 --- /dev/null +++ b/packages/cli/lib/command/deploy.ts @@ -0,0 +1,28 @@ +import log from '@kopflos-cms/logger' +import { deploy } from '@kopflos-cms/plugin-deploy-resources' +import { createEnv } from '@kopflos-cms/core/env.js' // eslint-disable-line import/no-unresolved +import { loadConfig } from '../config.js' + +interface DeployArgs { + config?: string +} + +export default async function (args: DeployArgs) { + const config = await loadConfig({ + path: args.config, + }) + + const autoDeployPluginConfig = config.plugins?.['@kopflos-cms/plugin-deploy-resources'] + + if (!autoDeployPluginConfig) { + log.error("'@kopflos-cms/plugin-deploy-resources' not found in plugin configuration") + return process.exit(1) + } + + if (!autoDeployPluginConfig.paths?.length) { + log.warn('No resource paths specified. Nothing to deploy') + return process.exit(1) + } + + return deploy(autoDeployPluginConfig.paths, createEnv(config)) +} diff --git a/packages/cli/lib/command/serve.ts b/packages/cli/lib/command/serve.ts new file mode 100644 index 0000000..80149f0 --- /dev/null +++ b/packages/cli/lib/command/serve.ts @@ -0,0 +1,60 @@ +import log from '@kopflos-cms/logger' +import express from 'express' +import kopflos from '@kopflos-cms/express' +import { loadConfig } from '../config.js' + +interface ServeArgs { + mode?: 'development' | 'production' | unknown + config?: string + port?: number + host?: string + trustProxy?: boolean + variable: Record +} + +export default async function ({ + mode: _mode = 'production', + config, + port = 1429, + host = '0.0.0.0', + trustProxy, + variable, +}: ServeArgs) { + let mode: 'development' | 'production' + if (_mode !== 'development' && _mode !== 'production') { + log.warn('Invalid mode, defaulting to "production"') + mode = 'production' + } else { + mode = _mode + } + + const loadedConfig = await loadConfig({ + path: config, + }) + + const finalOptions = { + port, + host, + mode, + ...loadedConfig, + variables: { + ...loadedConfig.variables, + ...variable, + }, + } + + const app = express() + + if (trustProxy) { + app.set('trust proxy', trustProxy) + } + + const { instance, middleware } = await kopflos(finalOptions) + app.use(middleware) + + await instance.start() + + app.listen(port, host, () => { + log.info(`Server running on ${port}. API URL: ${finalOptions.baseIri}`) + }) +} diff --git a/packages/plugin-deploy-resources/index.ts b/packages/plugin-deploy-resources/index.ts index f7083f7..b0d1b76 100644 --- a/packages/plugin-deploy-resources/index.ts +++ b/packages/plugin-deploy-resources/index.ts @@ -1,4 +1,4 @@ -import type { Kopflos, KopflosPlugin } from '@kopflos-cms/core' +import type { Kopflos, KopflosEnvironment } from '@kopflos-cms/core' import { bootstrap } from '@hydrofoil/talos-core/bootstrap.js' import { fromDirectories } from '@hydrofoil/talos-core' import { ResourcePerGraphStore } from '@hydrofoil/resource-store' @@ -17,9 +17,16 @@ declare module '@kopflos-cms/core' { const log = createLogger('deploy-resources') -export default function kopflosPlugin({ paths = [], enabled = true }: Options = {}): Required> { +export async function deploy(paths: string[], env: KopflosEnvironment) { + await bootstrap({ + dataset: await fromDirectories(paths, env.kopflos.config.baseIri), + store: new ResourcePerGraphStore(env.sparql.default.stream, env), + }) +} + +export default function kopflosPlugin({ paths = [], enabled = true }: Options = {}) { return { - async onStart({ env }: Kopflos) { + onStart({ env }: Kopflos) { if (!enabled) { log.info('Auto deploy disabled. Skipping deployment') return @@ -32,10 +39,7 @@ export default function kopflosPlugin({ paths = [], enabled = true }: Options = log.info(`Auto deploy enabled. Deploying from: ${paths}`) - await bootstrap({ - dataset: await fromDirectories(paths, env.kopflos.config.baseIri), - store: new ResourcePerGraphStore(env.sparql.default.stream, env), - }) + return deploy(paths, env) }, } }