@webship.co/workspace-docker 0.0.0-stage → 1.0.0-alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -2
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/runtime.d.ts +12 -0
- package/dist/src/lib/runtime.js +64 -0
- package/dist/src/lib/runtime.js.map +1 -0
- package/features/docker.feature +8 -0
- package/package.json +49 -4
- package/scripts/host-install.sh +14 -0
package/README.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @webship.co/workspace-docker
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Docker Compose for the [Webship Workspace](https://github.com/webship/workspace) dashboard: the **DOCKER**
|
|
4
|
+
menu on every project folder that carries a compose file (compose.yaml, docker-compose.yml and their variants), with the runtime's verbs as terminal sessions in
|
|
5
|
+
the project, the up/down caption and the site link, and "Write a compose.yaml skeleton here" when a new folder is made.
|
|
6
|
+
A runtime of [`@webship.co/workspace-container`](https://github.com/webship/workspace-container).
|
|
7
|
+
|
|
8
|
+
A project is a Docker Compose project when it carries `compose.yaml`, `compose.yml`, `docker-compose.yml` or `docker-compose.yaml`. The name is the file's `name:` or the folder; the site link is the first published port of the first service that has one. The dashboard container runs docker against the host's socket (Docker-outside-of-Docker), so the verbs act on the same daemon the host uses.
|
|
9
|
+
|
|
10
|
+
## Use it
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cd ~/workspace
|
|
14
|
+
bash core/scripts/plugins.sh --require github:webship/workspace-docker
|
|
15
|
+
bash core/scripts/plugins.sh --activate docker
|
|
16
|
+
bash core/scripts/plugins.sh --install docker
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## The verbs
|
|
20
|
+
|
|
21
|
+
| Verb | Command |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| Up | `docker compose up -d` |
|
|
24
|
+
| Down | `docker compose down` |
|
|
25
|
+
| Restart | `docker compose restart` |
|
|
26
|
+
| Status | `docker compose ps` |
|
|
27
|
+
| Logs | `docker compose logs -f --tail=200` |
|
|
28
|
+
| Shell | `docker compose exec <first service> sh` |
|
|
29
|
+
| Pull | `docker compose pull` |
|
|
30
|
+
| Build | `docker compose build` |
|
|
31
|
+
| Down and remove volumes | `docker compose down -v` (data is lost: marked in red) |
|
|
32
|
+
|
|
33
|
+
Every verb opens a terminal session in the project folder, so what the CLI prints is what you see,
|
|
34
|
+
and a verb that asks a question can be answered.
|
|
35
|
+
|
|
36
|
+
The `workspacePlugin` block in `package.json` is the manifest the installer reads.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docker Compose for Webship Workspace.
|
|
3
|
+
*
|
|
4
|
+
* The DOCKER menu on every project folder that carries a compose file (compose.yaml, docker-compose.yml and their variants): the runtime's verbs as terminal
|
|
5
|
+
* sessions in the project, the up/down caption, the site link, and "Write a compose.yaml skeleton here" when a new folder
|
|
6
|
+
* is made. All of it is the container family's surface (@webship.co/workspace-container); this
|
|
7
|
+
* package is the Docker Compose runtime plus its host install.
|
|
8
|
+
*/
|
|
9
|
+
import type { PluginApi } from '@webship.co/workspace-plugin-api';
|
|
10
|
+
import { dockerRuntime } from './lib/runtime';
|
|
11
|
+
export { dockerRuntime };
|
|
12
|
+
export declare function register(api: PluginApi): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Docker Compose for Webship Workspace.
|
|
4
|
+
*
|
|
5
|
+
* The DOCKER menu on every project folder that carries a compose file (compose.yaml, docker-compose.yml and their variants): the runtime's verbs as terminal
|
|
6
|
+
* sessions in the project, the up/down caption, the site link, and "Write a compose.yaml skeleton here" when a new folder
|
|
7
|
+
* is made. All of it is the container family's surface (@webship.co/workspace-container); this
|
|
8
|
+
* package is the Docker Compose runtime plus its host install.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.dockerRuntime = void 0;
|
|
12
|
+
exports.register = register;
|
|
13
|
+
const workspace_container_1 = require("@webship.co/workspace-container");
|
|
14
|
+
const runtime_1 = require("./lib/runtime");
|
|
15
|
+
Object.defineProperty(exports, "dockerRuntime", { enumerable: true, get: function () { return runtime_1.dockerRuntime; } });
|
|
16
|
+
function register(api) {
|
|
17
|
+
(0, workspace_container_1.registerContainer)(api, runtime_1.dockerRuntime);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AASH,4BAEC;AARD,yEAAoE;AAEpE,2CAA8C;AAErC,8FAFA,uBAAa,OAEA;AAEtB,SAAgB,QAAQ,CAAC,GAAc;IACrC,IAAA,uCAAiB,EAAC,GAAG,EAAE,uBAAa,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docker Compose, as a container runtime: a project with a compose file.
|
|
3
|
+
*/
|
|
4
|
+
import type { ContainerRuntime } from '@webship.co/workspace-container';
|
|
5
|
+
export declare const COMPOSE_FILES: string[];
|
|
6
|
+
/** The compose project's name, first service and first published port, read without a YAML parser. */
|
|
7
|
+
export declare function readCompose(file: string, fallbackName: string): {
|
|
8
|
+
name: string;
|
|
9
|
+
service?: string;
|
|
10
|
+
port?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const dockerRuntime: ContainerRuntime;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Docker Compose, as a container runtime: a project with a compose file.
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.dockerRuntime = exports.COMPOSE_FILES = void 0;
|
|
10
|
+
exports.readCompose = readCompose;
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const workspace_container_1 = require("@webship.co/workspace-container");
|
|
14
|
+
exports.COMPOSE_FILES = ['compose.yaml', 'compose.yml', 'docker-compose.yml', 'docker-compose.yaml'];
|
|
15
|
+
/** The compose project's name, first service and first published port, read without a YAML parser. */
|
|
16
|
+
function readCompose(file, fallbackName) {
|
|
17
|
+
let text = '';
|
|
18
|
+
try {
|
|
19
|
+
text = fs_1.default.readFileSync(file, 'utf8');
|
|
20
|
+
}
|
|
21
|
+
catch (_) {
|
|
22
|
+
return { name: fallbackName };
|
|
23
|
+
}
|
|
24
|
+
const name = (text.match(/^name:\s*["']?([\w.-]+)/m) || [])[1] || fallbackName;
|
|
25
|
+
const services = text.indexOf('\nservices:');
|
|
26
|
+
const body = services >= 0 ? text.slice(services) : '';
|
|
27
|
+
const service = (body.match(/\n {2}([\w.-]+):/) || [])[1];
|
|
28
|
+
const port = (body.match(/^\s+-\s*["']?(?:[\d.]+:)?(\d+):\d+/m) || [])[1];
|
|
29
|
+
return { name, service, port };
|
|
30
|
+
}
|
|
31
|
+
exports.dockerRuntime = {
|
|
32
|
+
id: 'docker',
|
|
33
|
+
label: 'DOCKER',
|
|
34
|
+
title: 'Docker Compose',
|
|
35
|
+
cli: 'docker',
|
|
36
|
+
cliHint: 'https://docs.docker.com/engine/install/',
|
|
37
|
+
detect: (dir) => {
|
|
38
|
+
const p = (0, workspace_container_1.detectByFiles)(dir, exports.COMPOSE_FILES);
|
|
39
|
+
if (!p)
|
|
40
|
+
return null;
|
|
41
|
+
const c = readCompose(path_1.default.join(dir, p.file), path_1.default.basename(dir));
|
|
42
|
+
return { file: p.file, name: c.name, url: c.port ? `http://localhost:${c.port}` : undefined };
|
|
43
|
+
},
|
|
44
|
+
state: async (p, dir, lib) => {
|
|
45
|
+
const out = await lib.run('docker', ['compose', '-f', p.file, 'ps', '--status', 'running', '--format', '{{.Name}} {{.State}}'], dir, { timeoutMs: 15 * 1000 });
|
|
46
|
+
if (!out.ok)
|
|
47
|
+
return 'unknown';
|
|
48
|
+
return /running/.test(String(out.stdout)) ? 'up' : 'down';
|
|
49
|
+
},
|
|
50
|
+
verbs: {
|
|
51
|
+
start: { label: 'Up (docker compose up -d)', icon: 'play', kind: 'start', cmd: (p) => `docker compose -f ${p.file} up -d` },
|
|
52
|
+
stop: { label: 'Down', icon: 'ban', kind: 'stop', cmd: (p) => `docker compose -f ${p.file} down` },
|
|
53
|
+
restart: { label: 'Restart', icon: 'refresh', kind: 'restart', cmd: (p) => `docker compose -f ${p.file} restart` },
|
|
54
|
+
status: { label: 'Status (ps)', icon: 'info', kind: 'status', cmd: (p) => `docker compose -f ${p.file} ps` },
|
|
55
|
+
logs: { label: 'Logs (follow)', icon: 'file-text', kind: 'logs', cmd: (p) => `docker compose -f ${p.file} logs -f --tail=200` },
|
|
56
|
+
shell: { label: 'Shell in the first service', icon: 'terminal', kind: 'shell', cmd: (p) => `docker compose -f ${p.file} exec $(docker compose -f ${p.file} config --services | head -1) sh` },
|
|
57
|
+
pull: { label: 'Pull images', icon: 'cloud-download', kind: 'other', cmd: (p) => `docker compose -f ${p.file} pull` },
|
|
58
|
+
build: { label: 'Build', icon: 'cog', kind: 'other', cmd: (p) => `docker compose -f ${p.file} build` },
|
|
59
|
+
downv: { label: 'Down and remove volumes (data is lost)', icon: 'trash', kind: 'other', danger: true, cmd: (p) => `docker compose -f ${p.file} down -v` },
|
|
60
|
+
},
|
|
61
|
+
init: { label: 'Write a compose.yaml skeleton here', cmd: (name) => `[ -e compose.yaml ] || printf 'name: ${name}\\nservices:\\n web:\\n image: nginx:alpine\\n ports:\\n - "8080:80"\\n' > compose.yaml`, help: 'One nginx service on port 8080, to edit.' },
|
|
62
|
+
menuNote: 'The verbs run against the daemon the host uses (Docker-outside-of-Docker)',
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/lib/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Feature: The Docker Compose plugin on a project row
|
|
2
|
+
Once the plugin is activated and installed, every project folder that carries a compose file (compose.yaml, docker-compose.yml and their variants) carries
|
|
3
|
+
the DOCKER menu, and the Plugins page lists the plugin with its four steps.
|
|
4
|
+
|
|
5
|
+
Scenario: The plugin is listed on the Plugins page
|
|
6
|
+
Given I am on "/settings/plugins"
|
|
7
|
+
Then I should see "Docker Compose"
|
|
8
|
+
And I should see "@webship.co/workspace-docker"
|
package/package.json
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webship.co/workspace-docker",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0-alpha1",
|
|
4
|
+
"description": "Docker Compose for Webship Workspace: the DOCKER menu on every project with a compose file (up, down, restart, ps, logs, a shell in the first service, pull, build), the up/down caption, the published port as the site link. A runtime of @webship.co/workspace-container.",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/webship/workspace-docker.git"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/src",
|
|
14
|
+
"features",
|
|
15
|
+
"scripts",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"prepare": "npm run build",
|
|
21
|
+
"test": "npm run build && node --test dist/tests/*.test.js",
|
|
22
|
+
"lint": "tsc -p tsconfig.json --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"workspacePlugin": {
|
|
25
|
+
"name": "docker",
|
|
26
|
+
"label": "Docker Compose",
|
|
27
|
+
"typeActions": {
|
|
28
|
+
"docker": [
|
|
29
|
+
"sites",
|
|
30
|
+
"code"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"hostInstall": "scripts/host-install.sh"
|
|
35
|
+
},
|
|
36
|
+
"features": [
|
|
37
|
+
"features/docker.feature"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@webship.co/workspace-container": "^1.0.0-alpha1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^24.0.0",
|
|
45
|
+
"@webship.co/workspace-plugin-api": "^1.0.0-alpha1",
|
|
46
|
+
"typescript": "^5.9.0"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
## The host side of the Docker Compose plugin: the CLI on this machine. Run by core/scripts/plugins.sh
|
|
3
|
+
## --install docker, and by core/scripts/install.sh for every installed plugin that ships one.
|
|
4
|
+
## Idempotent: whatever is already there is reported, not redone; nothing is installed unasked.
|
|
5
|
+
set -u
|
|
6
|
+
BIN='';
|
|
7
|
+
for candidate in "$(command -v docker 2>/dev/null)" "${HOME}/.local/bin/docker" ; do
|
|
8
|
+
[ -n "${candidate}" ] && [ -x "${candidate}" ] && { BIN="${candidate}"; break; }
|
|
9
|
+
done
|
|
10
|
+
if [ -n "${BIN}" ] ; then
|
|
11
|
+
printf ' ✓ %-16s %s\n' "docker" "${BIN}" ;
|
|
12
|
+
else
|
|
13
|
+
printf ' ✗ %-16s %s\n' "docker" "missing — the DOCKER menu's verbs need it (docs.docker.com/engine/install)" ;
|
|
14
|
+
fi
|