From 7004529fad9c18ba5eee64dbc8ca10e7547f6ebf Mon Sep 17 00:00:00 2001 From: Hexagon Date: Mon, 22 Apr 2024 19:19:03 +0200 Subject: [PATCH] Update docs. Fix error when running without configuration. Fix watcher keeping process alive after terminating. --- README.md | 17 ++++---- deno.json | 2 +- docs/src/_data.json | 2 +- docs/src/changelog.md | 7 +++ .../src/examples/basic-webinterface/README.md | 5 +-- docs/src/examples/plugins/README.md | 4 +- docs/src/examples/splunk/README.md | 2 +- docs/src/examples/splunk/pup.jsonc | 2 +- docs/src/examples/telemetry/README.md | 4 +- .../telemetry/task-with-telemetry-1.ts | 2 +- .../telemetry/task-with-telemetry-2.ts | 5 ++- docs/src/faq.md | 3 +- docs/src/index.md | 31 ++++++++----- docs/src/installation.md | 2 +- docs/src/usage/configuration.md | 27 ++++++++++++ docs/src/usage/index.md | 26 +++++++++-- docs/src/usage/library.md | 3 +- docs/src/usage/service.md | 4 +- lib/cli/main.ts | 43 ++++++++++--------- lib/core/watcher.ts | 5 ++- 20 files changed, 130 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index a93b13d..732af00 100644 --- a/README.md +++ b/README.md @@ -35,28 +35,29 @@ deno run -Ar jsr:@pup/pup@1.0.0-rc.28 setup --channel prerelease This command downloads the latest version of Pup and installs it on your system. The `--channel prerelease` option is included as there is no stable version of Pup yet. Read more abour release channels [here](https://hexagon.github.io/pup/installation.html#release-channels). -### Configuration +### Configuration and Usage -Pup revolves around instance (ecosystem) configuration files, each process belongs to an instance defined by a `pup.json`. This file can either be created manually, or by the command line helpers. +Pup revolves around instance configuration files, where each managed process belongs to an instance defined by a `pup.json`. This file can either be created manually, or by the command line helpers +used below: 1. To create a simple instances running a single process: `pup init --id "my-server" --autostart --cmd "deno run -A server.ts"` - If you intend to create multiple pup instances on the same server, you can pass an instance name through `--name my-instance-name` + If you intend to create multiple pup instances on the same server, you can pass an instance name through `--name my-instance-name`. This name will also be used as the system service name. -2. (Optional) In case you have an additional task to execute, such as a cleanup script, you can make use of `pup append`. The following example shows how to add an extra task that use the cron start +2. _(Optional)_ In case you have an additional task to execute, such as a cleanup script, you can make use of `pup append`. The following example shows how to add an extra task that use the cron start policy: `pup append --id "my-task" --cmd "deno run -A task.ts" --cron "0 0 * * * *"` -3. (Optional) Test your instance (exit by pressing CTRL+C): - - `pup run` +3. _(Optional)_ Test your instance by running it foreground using `pup run` (exit by pressing CTRL+C): 4. To make your instance run at boot, enable it using `pup enable-service`. - `pup enable-service` + Will by default use the instance name for service name, which defaults to `pup`. You can override by passing `-n my-custom-name`. + +5. To stream the logs from a running instance, use the command `pup monitor`. To show historic logs, use `pup logs`. Will by default use the instance name for service name, which defaults to `pup`. You can override by passing `-n my-custom-name`. diff --git a/deno.json b/deno.json index b6e654f..04c357f 100644 --- a/deno.json +++ b/deno.json @@ -33,7 +33,7 @@ "tasks": { "update-deps": "deno run --allow-read=. --allow-net=jsr.io,registry.npmjs.org jsr:@check/deps", - "check": "deno fmt --check && deno lint && deno check --unstable-kv pup.ts && deno test --allow-read --allow-write --allow-env --allow-net --allow-sys --allow-run --unstable-kv --coverage=cov_profile && echo \"Generating coverage\" && deno coverage cov_profile --exclude=pup/test --lcov --output=cov_profile.lcov", + "check": "deno fmt --check && deno lint && deno check --unstable-kv --unstable-net pup.ts && deno test --allow-read --allow-write --allow-env --allow-net --allow-sys --allow-run --unstable-kv --unstable-net --coverage=cov_profile && echo \"Generating coverage\" && deno coverage cov_profile --exclude=pup/test --lcov --output=cov_profile.lcov", "check-coverage": "deno task check && genhtml cov_profile.lcov --output-directory cov_profile/html && lcov --list cov_profile.lcov && deno run --allow-net --allow-read https://deno.land/std/http/file_server.ts cov_profile/html", "build-schema": "deno run --allow-write --allow-read --allow-env=XDG_DATA_HOME,HOME tools/build-schema.ts && deno fmt", "build-versions": "deno run --allow-read --allow-write --allow-env tools/release.ts && deno fmt", diff --git a/docs/src/_data.json b/docs/src/_data.json index b58c3f3..fd72366 100644 --- a/docs/src/_data.json +++ b/docs/src/_data.json @@ -6,7 +6,7 @@ "description": "Universal Process Manager" }, "substitute": { - "$PUP_VERSION": "1.0.0-rc.10" + "$PUP_VERSION": "1.0.0-rc.28" }, "top_links": [ { diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 61f5df1..2f6f35d 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -9,6 +9,13 @@ nav_order: 13 All notable changes to this project will be documented in this section. +## [1.0.0-rc.29] - Unreleased + +- fix(docs): Update docs to include the latest features. +- fix(core): Fix regression which caused an error when running without configuration. +- fix(watcher): Fix an issue where the watcher prevented the main process to exit on termination. +- fix(cli): Fix pup init + ## [1.0.0-rc.28] - 2023-04-22 - fix(core): Generate secret before starting main process. diff --git a/docs/src/examples/basic-webinterface/README.md b/docs/src/examples/basic-webinterface/README.md index 5cc41ab..d505bc5 100644 --- a/docs/src/examples/basic-webinterface/README.md +++ b/docs/src/examples/basic-webinterface/README.md @@ -27,7 +27,7 @@ To activate the web interface plugin, set up the `plugins:`-section of your `pup "processes": [/* ... */], "plugins": [ { - "url": "jsr:@pup/pup@$VERSION/plugins/web-interface", + "url": "jsr:@pup/pup@$PUP_VERSION/plugins/web-interface", "options": { "port": 5000 } @@ -36,9 +36,6 @@ To activate the web interface plugin, set up the `plugins:`-section of your `pup } ``` -If running pup using the normal release channels `stable` or `prerelease`, `$VERSION` will be replaced by the version of the currently running pup instance. If running a `canary` version, or custom -intallation, you **can not** use the `$VERSION` variable, and should give an absolute url. - ## Running the example `cd` to `/docs/src/examples/basic-webinterface` directory. diff --git a/docs/src/examples/plugins/README.md b/docs/src/examples/plugins/README.md index ee2af29..98f0113 100644 --- a/docs/src/examples/plugins/README.md +++ b/docs/src/examples/plugins/README.md @@ -14,7 +14,7 @@ To create a custom plugin, developers should extend the `PluginImplementation` c provides various hooks and events to listen for. ```typescript -import { LogEvent, PluginApi, PluginConfiguration, PluginImplementation, PluginMetadata } from "jsr:@pup/pup@$VERSION/mod.ts" +import { LogEvent, PluginApi, PluginConfiguration, PluginImplementation, PluginMetadata } from "jsr:@pup/pup@PUP_$VERSION/mod.ts" export class PupPlugin extends PluginImplementation { constructor(pup: PluginApi, config: PluginConfiguration) { @@ -98,7 +98,7 @@ To sum it up, and create a custom plugin that intercepts the logger through hook this example, the plugin will print all available log data when the log signal is received. ```typescript -import { LogEvent, PluginApi, PluginConfiguration, PluginImplementation } from "jsr:@pup/pup@$VERSION/mod.ts" +import { LogEvent, PluginApi, PluginConfiguration, PluginImplementation } from "jsr:@pup/pup@PUP_$VERSION/mod.ts" export class PupPlugin extends PluginImplementation { constructor(pup: PluginApi, config: PluginConfiguration) { diff --git a/docs/src/examples/splunk/README.md b/docs/src/examples/splunk/README.md index 77b1235..964a05c 100644 --- a/docs/src/examples/splunk/README.md +++ b/docs/src/examples/splunk/README.md @@ -12,7 +12,7 @@ The example at [/docs/src/examples/splunk](https://github.com/Hexagon/pup/tree/m output to Splunk using the splunk-hec plugin. > **Note:** If you're connecting to a Splunk HEC server with a bad certificate, such as during testing, you'll need to start pup manually with the `--unsafely-ignore-certificate-errors` flag. The full -> command for this would be `deno run -Ar --unsafely-ignore-certificate-errors jsr:@pup/pup@$VERSION run` { .note } +> command for this would be `deno run -Ar --unsafely-ignore-certificate-errors jsr:@pup/pup@$PUP_VERSION run` { .note } ## Files diff --git a/docs/src/examples/splunk/pup.jsonc b/docs/src/examples/splunk/pup.jsonc index 061205d..fefc788 100644 --- a/docs/src/examples/splunk/pup.jsonc +++ b/docs/src/examples/splunk/pup.jsonc @@ -14,7 +14,7 @@ "plugins": [ /* Enable splunk HEC plugin */ { - /* Use a full url to the plugin, example: jsr:@pup/pup@$VERSION/plugins/splunk-hec */ + /* Use a full url to the plugin, example: jsr:@pup/pup@$PUP_VERSION/plugins/splunk-hec */ "url": "../../plugins/splunk-hec/mod.ts", "options": { /* diff --git a/docs/src/examples/telemetry/README.md b/docs/src/examples/telemetry/README.md index f82d5c8..4c338a0 100644 --- a/docs/src/examples/telemetry/README.md +++ b/docs/src/examples/telemetry/README.md @@ -18,7 +18,7 @@ This example demonstrates the telemetry feature of pup, which ... The simplest use case, where you only want to monitor your client metrics is used like this: ```ts -import { PupTelemetry } from "jsr:@pup/pup@$VERSION/telemetry" +import { PupTelemetry } from "jsr:@pup/pup@$PUP_VERSION/telemetry" new PupTelemetry() @@ -32,7 +32,7 @@ telemetry (including main process) using `status` on the cli. ```ts // PupTelemetry is a singleton, so it can be imported one or many times in your application -import { PupTelemetry } from "jsr:@pup/pup@$VERSION/telemetry" // Pin this to a specific version of pup +import { PupTelemetry } from "jsr:@pup/pup@$PUP_VERSION/telemetry" // Pin this to a specific version of pup const telemetry = new PupTelemetry() // One part of your application ... diff --git a/docs/src/examples/telemetry/task-with-telemetry-1.ts b/docs/src/examples/telemetry/task-with-telemetry-1.ts index 4b4db31..34fed5f 100644 --- a/docs/src/examples/telemetry/task-with-telemetry-1.ts +++ b/docs/src/examples/telemetry/task-with-telemetry-1.ts @@ -1,6 +1,6 @@ // See docs/examples/telemetry/README.md for full documentation on telemetry, including using the IPC // - Pin this to the latest version of pup, or include in import map -import { PupTelemetry } from "jsr:@pup/pup@1.0.0-rc.28/telemetry" +import { PupTelemetry } from "jsr:@pup/pup@$PUP_VERSION/telemetry" const telemetry = new PupTelemetry(1) // The task diff --git a/docs/src/examples/telemetry/task-with-telemetry-2.ts b/docs/src/examples/telemetry/task-with-telemetry-2.ts index 48e38b2..e4c6f89 100644 --- a/docs/src/examples/telemetry/task-with-telemetry-2.ts +++ b/docs/src/examples/telemetry/task-with-telemetry-2.ts @@ -1,13 +1,14 @@ // See docs/examples/telemetry/README.md for full documentation on telemetry, including using the IPC // - Pin this to the latest version of pup, or include in import map -import { PupTelemetry } from "jsr:@pup/pup@1.0.0-rc.28/telemetry" +import { PupTelemetry } from "jsr:@pup/pup@$PUP_VERSION/telemetry" const telemetry = new PupTelemetry(1) // The task console.log("Process running") // Receive data -telemetry.on("message", (data) => { +// deno-lint-ignore no-explicit-any +telemetry.on("message", (data: any) => { console.log(`task-2 received: ${data}`) }) diff --git a/docs/src/faq.md b/docs/src/faq.md index 2a590bd..902a03c 100644 --- a/docs/src/faq.md +++ b/docs/src/faq.md @@ -23,8 +23,7 @@ names and values represent their corresponding values. **Q: Can I run multiple instances simultaneously?** -A: Yes, you can run multiple instances simultaneously. However, it is essential to ensure that each instance has a separate configuration file and that they do not conflict with each other in terms of -process IDs or shared resources. +A: Yes, you can run multiple instances simultaneously. By default, each instance will use a separate port for the internal Rest API used when issueing cli commands. **Q: Is there a limit to the number of processes Pup can manage?** diff --git a/docs/src/index.md b/docs/src/index.md index c384c35..4f502ad 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -31,41 +31,50 @@ Pup is centered on a single configuration file, `pup.json`, which manages all as To install Pup, open your terminal and execute the following command: ```bash -deno run -Ar jsr:@pup/pup@1.0.0-rc.28 setup --channel prerelease +deno run -Ar jsr:@pup/pup@$PUP_VERSION setup --channel prerelease ``` This command downloads the latest version of Pup and installs it on your system. The `--channel prerelease` option is included as there is no stable version of Pup yet. Read more abour release channels [here](https://hexagon.github.io/pup/installation.html#release-channels). -### Configuration +### Configuration and Usage -Pup revolves around instances configuration files, each process belongs to an instances defined by a `pup.json`. This file can either be created manually, or by the command line helpers. +Pup revolves around instance configuration files, where each managed process belongs to an instance defined by a `pup.json`. This file can either be created manually, or by the command line helpers +used below: 1. To create a simple instances running a single process: `pup init --id "my-server" --autostart --cmd "deno run -A server.ts"` - If you intend to create multiple pup instances on the same server, you can pass an instance name through `--name my-instance-name` + If you intend to create multiple pup instances on the same server, you can pass an instance name through `--name my-instance-name`. This name will also be used as the system service name. -2. (Optional) In case you have an additional task to execute, such as a cleanup script, you can make use of `pup append`. The following example shows how to add an extra task that use the cron start +2. _(Optional)_ In case you have an additional task to execute, such as a cleanup script, you can make use of `pup append`. The following example shows how to add an extra task that use the cron start policy: `pup append --id "my-task" --cmd "deno run -A task.ts" --cron "0 0 * * * *"` -3. (Optional) Test your instance (exit by pressing CTRL+C): - - `pup run` +3. _(Optional)_ Test your instance by running it foreground using `pup run` (exit by pressing CTRL+C): 4. To make your instance run at boot, enable it using `pup enable-service`. - `pup enable-service` + Will by default use the instance name for service name, which defaults to `pup`. You can override by passing `-n my-custom-name`. + +5. To stream the logs from a running instance, use the command `pup monitor`. To show historic logs, use `pup logs`. Will by default use the instance name for service name, which defaults to `pup`. You can override by passing `-n my-custom-name`. #### Single command example -It is possible to use pup to keep a process alive temporary, without a `pup.json` or system service. +It is possible to use pup without a `pup.json` or system service. + +**Keeping a process alive** -To achieve this, use `pup run` with `--cmd` and a start policy +To keep a process alive temporary, use `pup run` with `--cmd` and a start policy `pup run --autostart --cmd "deno run server.ts"` + +**Restarting a process on filesystem changes** + +To restart if any file changes withing the current directory, add `--watch `: + +`pup run --autostart --cmd "deno run server.ts" --watch .` diff --git a/docs/src/installation.md b/docs/src/installation.md index 3e500de..e2a4d11 100644 --- a/docs/src/installation.md +++ b/docs/src/installation.md @@ -20,7 +20,7 @@ Before proceeding with the installation, ensure that you have the following inst To install Pup, open your terminal and execute the following command: ```bash -deno run -Ar jsr:@pup/pup@1.0.0-rc.28 setup --channel prerelease +deno run -Ar jsr:@pup/pup@$PUP_VERSION setup --channel prerelease ``` This command downloads the latest version of Pup and installs it on your system. diff --git a/docs/src/usage/configuration.md b/docs/src/usage/configuration.md index 99d14e4..a3f1033 100644 --- a/docs/src/usage/configuration.md +++ b/docs/src/usage/configuration.md @@ -195,6 +195,33 @@ To activate plugins, add your plugins to the configuration using this pattern: } ``` +### API + +The API configuration object is optional, and is used to customize the Rest API used to interact with Pup. Here's a breakdown of the available properties: + +| Option | Type | Description | Default | +| ---------- | ------ | ------------------------------------------------------ | ----------- | +| `hostname` | string | (Optional) The hostname the API server should bind to. | "127.0.0.1" | +| `port` | number | (Optional) The port the API server should listen on. | Random port | +| `revoked` | array | (Optional) A list of revoked API consumers. | [] | + +By default, the API is only exposed to localhost, and will listen on a random port. + +If you want to use the API remotely, you can supply a specific port (one per instance) and set hostname to `0.0.0.0`. It is highly recommended to use a proxy such as nginx to expose the API to the +internet. + +**Example, listening on port 9000 of all interfaces:** + +```json +{ + "api": { + "hostname": "0.0.0.0", + "port": 9000, + "revoked": ["my-consumer", "my-other-consumer"] + } +} +``` + ## Validating the Configuration To ensure your configuration is valid, just run `pup run` (or `pup run --config custom/path/to/config.json`). If using pup as a library, you can use the `validateConfiguration()` function provided by diff --git a/docs/src/usage/index.md b/docs/src/usage/index.md index 29d9421..5caaf39 100644 --- a/docs/src/usage/index.md +++ b/docs/src/usage/index.md @@ -21,9 +21,9 @@ General flags are used to control the basic behavior of Pup and can be combined - `-v, version`: Show the current version of Pup. - `upgrade `: Upgrade pup to the latest, or specified, version. -## Running pup with an Existing Configuration File +## Running in foreground -You can run Pup with an existing configuration file by using the `run` command, optionally followed by the `--config` option: +To test your configuration, you can run Pup in the foreground using the `run` command, optionally followed by the `--config` option: ```bash # This will use pup.json or pup.jsonc in current directory @@ -33,13 +33,31 @@ pup run pup run --config path/to/config-file ``` +## Running as a service + +You can run Pup in as a service by using the `enable-service` command, optionally followed by the `--config` option: + +```bash +# This will install a system service and run pup in the background +pup enable-service + +# or +pup enable-service --config path/to/config-file +``` + ## Viewing Logs -Pup enables you to inspect its internally stored logs through the `logs` command. This command provides several options to help filter the logs and customize the output: +Pup enables you to inspect its internally stored logs through the `logs` command, or live stream the logs using the `monitor` command. Both options supports arguments to help filter the logs and +customize the output: + +### Arguments to both `logs` and `monitor` -- `-n`: (optional) Defines the number of log entries to display. - `--id `: (optional) Allows filtering of logs based on the process ID. - `--severity `: (optional) Enables filtering logs based on the severity level. The acceptable severity levels include error, warning, info, and log. + +### ´logs´ only + +- `-n`: (optional) Defines the number of log entries to display. - `--start `: (optional) Allows you to display logs that were generated after a specified timestamp. The timestamp should be in the ISO8601 format. - `--end `: (optional) Lets you display logs generated before a particular timestamp. The timestamp should be in the ISO8601 format. diff --git a/docs/src/usage/library.md b/docs/src/usage/library.md index f3992b4..37aa0f5 100644 --- a/docs/src/usage/library.md +++ b/docs/src/usage/library.md @@ -11,7 +11,8 @@ nav_order: 5 Pup can be integrated into your application. Simply import it from your preferred location. We recommend [jsr.io/@pup/pup](https://jsr.io/@pup/pup). Here's how you can set up your main script: ```ts -import { Configuration, Pup } from "jsr:@pup/pup@$VERSION/mod.ts" +// Change $PUP_VERSION to the latest available version +import { Configuration, Pup } from "jsr:@pup/pup@$PUP_VERSION/mod.ts" const configuration: Configuration = { "logger": { diff --git a/docs/src/usage/service.md b/docs/src/usage/service.md index b26a151..4ba07c2 100644 --- a/docs/src/usage/service.md +++ b/docs/src/usage/service.md @@ -94,7 +94,7 @@ RUN mkdir /app COPY . /app/ # Install pup - Pin this url to a specific version in production -RUN ["deno","install","-Afrn","pup", "jsr:@pup/pup@1.0.0-rc.28"] +RUN ["deno","install","-Afrn","pup", "jsr:@pup/pup@$PUP_VERSION"] # Go! ENTRYPOINT ["sh", "-c", "cd /app && pup run"] @@ -155,8 +155,6 @@ Make sure to replace `/home/user/.deno/bin/deno` with the actual path of your de You should also replace `/path/to/your/pup.json` with the actual path. -Finally you should add a version specifier to `jsr:@pup/pup@$VERSION/pup.ts`, like `jsr:@pup/pup@$PUP_VERSION/pup.ts`, Find the latest version at . - Note that systemd always expects full paths. Also note that you will need to use full paths to executables in pup.json when running using systemd, alternatively you can use the `path` configuration key in each process to add the paths needed, like: diff --git a/lib/cli/main.ts b/lib/cli/main.ts index 76761d5..fadb110 100644 --- a/lib/cli/main.ts +++ b/lib/cli/main.ts @@ -121,7 +121,7 @@ async function main() { if (useConfigFile) { const configFileCwd = toResolvedAbsolutePath(checkedArgs?.get("cwd") || cwd()) configFile = await findConfigFile(configFileCwd, useConfigFile, checkedArgs?.get("config")) - } + } // Exit if a configuration file is expected, but not found if (useConfigFile && !configFile) { console.error("Configuration file not found.") @@ -153,18 +153,18 @@ async function main() { * * Generate a new configuration file and exit */ - if (baseArgument === "init") { - // Default new configuration file to pup.json - const fallbackedConfigFile = configFile ?? "pup.json" - if (await exists(fallbackedConfigFile)) { - console.error(`Configuration file '${fallbackedConfigFile}' already exists, exiting.`) - exit(1) - } else { - await createConfigurationFile(fallbackedConfigFile, checkedArgs!, cmd!) - console.log(`Configuration file '${fallbackedConfigFile}' created`) - exit(0) - } - } + if (baseArgument === "init") { + // Default new configuration file to pup.json + const fallbackedConfigFile = configFile ?? "pup.json" + if (await exists(fallbackedConfigFile)) { + console.error(`Configuration file '${fallbackedConfigFile}' already exists, exiting.`) + exit(1) + } else { + await createConfigurationFile(fallbackedConfigFile, checkedArgs!, cmd!) + console.log(`Configuration file '${fallbackedConfigFile}' created`) + exit(0) + } + } // Read or generate configuration let configuration: Configuration if (configFile) { @@ -190,13 +190,16 @@ async function main() { } // Prepare API port let port = configuration.api?.port - const portFile = `${await toTempPath(configFile as string)}/.main.port` - const portFileObj = new Prop(portFile) - if (!port) { - try { - const filePort = await portFileObj.load() - port = parseInt(filePort) - } catch (_e) { /* That's ok, there is no running instance. */ } + if (useConfigFile) { + port = configuration.api?.port + const portFile = `${await toTempPath(configFile as string)}/.main.port` + const portFileObj = new Prop(portFile) + if (!port) { + try { + const filePort = await portFileObj.load() + port = parseInt(filePort) + } catch (_e) { /* That's ok, there is no running instance. */ } + } } // Prepare secret file diff --git a/lib/core/watcher.ts b/lib/core/watcher.ts index 42aa049..dceb20c 100644 --- a/lib/core/watcher.ts +++ b/lib/core/watcher.ts @@ -35,6 +35,7 @@ export class Watcher implements AsyncIterable { private skip?: RegExp[] = [] private config: WatcherConfig private stopWatching = false + private watcher?: Deno.FsWatcher constructor(config: WatcherConfig = {}) { this.config = config @@ -116,7 +117,8 @@ export class Watcher implements AsyncIterable { } const run = async () => { - for await (const event of Deno.watchFs(this.paths)) { + this.watcher = Deno.watchFs(this.paths) + for await (const event of this.watcher) { if (this.stopWatching) { break // Exit the loop if stopWatching is true } @@ -142,5 +144,6 @@ export class Watcher implements AsyncIterable { public stop() { this.stopWatching = true + this.watcher?.close() } }