Files
Benjamin Diedrichsen 6ecb2c366f
Publish snapshot / snapshot (push) Successful in 1m2s
[refactor] moving cubes into own package"
[fix] default parameter run records parameters in session for replay[fix] remove default parameters for several cubes
2026-07-28 12:18:10 +02:00

338 lines
7.8 KiB
Markdown

# Nopy Session Format
Nopy supports two session file formats: **JSON** and **MJS** (ES Module JavaScript).
## Supported Formats
### JSON Format (`.session.json`)
Traditional JSON format for session files:
```json
{
"version": "1.0.0",
"timestamp": "2025-10-15T00:00:00.000Z",
"cubes": [
{
"key": "runtime:nodevm",
"variables": {
"VERSION": "22",
"USER": "myuser"
}
}
],
"hosts": ["@ssh/myhost.local"],
"auth": {
"method": "password",
"username": "admin"
},
"env": {}
}
```
**Limitations:**
- No comments allowed (pure JSON)
- Cannot use dynamic values or computation
- No code reuse or imports
### MJS Format (`.session.mjs`) - **Recommended**
JavaScript module format with full ES Module support:
```javascript
// Nopy Session Configuration
// Comments are fully supported!
// You can import values from other files
import { commonHosts } from './common-config.mjs';
// You can use dynamic values
const timestamp = new Date().toISOString();
const nodeVersion = process.env.NODE_VERSION || "22";
export default {
version: "1.0.0",
timestamp,
cubes: [
// Inline comments for each cube
{
key: "runtime:nodevm",
variables: {
VERSION: nodeVersion, // Dynamic value
USER: "myuser",
ALIAS: "nodelts",
GLOBAL_PACKAGES: "pm2 yarn"
}
},
// Add more cubes...
],
hosts: commonHosts, // Imported from another file
auth: {
method: "password",
username: "admin"
},
env: {
NODE_ENV: process.env.NODE_ENV || "production"
}
};
```
**Advantages:**
-**Comments** - Document your configuration inline
-**Dynamic values** - Use environment variables, compute values
-**Code reuse** - Import common configurations from other files
-**Parameterization** - Easily parameterize sessions from external tools
-**Type safety** - Use JSDoc or TypeScript for validation
-**Computation** - Calculate values, filter arrays, etc.
## Advanced MJS Examples
### Using Environment Variables
```javascript
export default {
version: "1.0.0",
timestamp: new Date().toISOString(),
cubes: [
{
key: "typestack-install",
variables: {
REPO: process.env.GIT_REPO || "git@github.com:org/repo.git",
USER: process.env.DEPLOY_USER || "admin",
APP: process.env.APP_NAME || "myapp",
ENV: process.env.NODE_ENV || "production"
}
}
],
hosts: [process.env.TARGET_HOST || "@ssh/localhost"],
auth: {
method: "password",
username: process.env.SSH_USER || "admin"
}
};
```
### Conditional Cube Inclusion
```javascript
const isDevelopment = process.env.NODE_ENV === 'development';
export default {
version: "1.0.0",
timestamp: new Date().toISOString(),
cubes: [
{
key: "runtime:docker",
variables: { DISTRO: "debian" }
},
// Only include in development
...(isDevelopment ? [{
key: "debug-tools",
variables: { INSTALL_GDB: true }
}] : [])
],
hosts: ["@ssh/myhost.local"],
auth: { method: "ssh" }
};
```
### Importing Common Configuration
**common-config.mjs:**
```javascript
export const productionHosts = [
"@ssh/prod-server-1.local",
"@ssh/prod-server-2.local"
];
export const stagingHosts = [
"@ssh/staging.local"
];
export const commonCubes = [
{
key: "apt:essentials",
variables: { UPDATE: true }
},
{
key: "runtime:docker",
variables: { DISTRO: "debian" }
}
];
```
**my-session.session.mjs:**
```javascript
import { productionHosts, commonCubes } from './common-config.mjs';
export default {
version: "1.0.0",
timestamp: new Date().toISOString(),
cubes: [
...commonCubes, // Include common cubes
{
key: "typestack-install",
variables: {
REPO: "git@github.com:myorg/myapp.git",
USER: "appuser",
APP: "myapp"
}
}
],
hosts: productionHosts, // Use imported hosts
auth: {
method: "password",
username: "admin"
}
};
```
### Programmatic Generation
You can even generate sessions programmatically from other tools:
**generate-session.mjs:**
```javascript
import fs from 'fs';
function generateSession(config) {
const cubes = config.services.map(service => ({
key: "typestack-install",
variables: {
REPO: service.repo,
USER: config.user,
APP: service.name,
ENV: config.environment
}
}));
const session = {
version: "1.0.0",
timestamp: new Date().toISOString(),
cubes,
hosts: config.hosts,
auth: {
method: "password",
username: config.user
}
};
const content = `export default ${JSON.stringify(session, null, 2)};`;
fs.writeFileSync('generated.session.mjs', content);
}
// Generate from external configuration
generateSession({
user: "deploy",
environment: "production",
services: [
{ name: "api", repo: "git@github.com:org/api.git" },
{ name: "web", repo: "git@github.com:org/web.git" }
],
hosts: ["@ssh/prod.local"]
});
```
## Loading Sessions
Both formats are loaded the same way:
```javascript
import { loadSession } from '@bitsquare/nopy';
// Load JSON
const jsonSession = await loadSession('./my-session.session.json');
// Load MJS
const mjsSession = await loadSession('./my-session.session.mjs');
```
The file extension determines which loader to use.
## Migration from JSON to MJS
To convert an existing JSON session to MJS:
1. Rename the file from `.session.json` to `.session.mjs`
2. Add `export default` before the configuration object
3. Remove quotes from property keys (optional)
4. Add comments and dynamic values as needed
**Before (JSON):**
```json
{
"version": "1.0.0",
"cubes": [...]
}
```
**After (MJS):**
```javascript
export default {
version: "1.0.0",
cubes: [...]
};
```
## Best Practices
1. **Use MJS for new sessions** - Take advantage of comments and flexibility
2. **Document your cubes** - Add comments explaining what each cube does
3. **Use environment variables** - Make sessions reusable across environments
4. **Extract common config** - Share configuration across multiple sessions
5. **Version control** - Both formats work well with git, but a recorded session holds every value its run used; read one before committing it
6. **Validate at runtime** - The loader validates the structure regardless of format
7. **Leave secrets out** - Declare them in the manifest instead, and let the replay ask
## Session Schema
Both formats must export/contain an object with this structure:
```typescript
interface NopySession {
version: string; // Session format version
timestamp: string; // ISO timestamp
cubes: CubeSession[]; // Array of cube configurations
hosts: string[]; // Target hosts
auth: AuthSession; // Authentication configuration
env?: Record<string, any>; // Global environment variables
}
interface CubeSession {
key: string; // Cube identifier
variables: Record<string, any>; // Cube-specific variables
}
interface AuthSession {
method: 'ssh-key' | 'password' | 'ssh';
username?: string;
}
```
A session nopy *writes* holds, per cube, every value that cube ran with — what
was typed, what came from `.nopyrc.json`, what a dependency supplied, and what
fell through to the schema's `.default()`. Two things are deliberately absent and
are asked for again on replay: the SSH password, and any key the cube's manifest
listed under `secrets`.
A session you write by hand is under no such obligation — `variables` may hold as
few keys as you like, and anything missing resolves the usual way. Note that a
key declared a secret is prompted for whether or not the session carries a value:
writing one in only pre-fills the prompt, it does not skip it. The variable form
shows what it is editing, so a secret you put in a session file appears on screen
as well as on disk.