@observo-ai/cli 0.1.0

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 ADDED
@@ -0,0 +1,14 @@
1
+ # @observo-ai/cli
2
+
3
+ npm wrapper for the [Observo CLI](https://github.com/observo-ai/observo-cli).
4
+
5
+ ```bash
6
+ npm install -g @observo-ai/cli
7
+ observo --version
8
+ ```
9
+
10
+ The postinstall script downloads the platform-matched binary from the
11
+ matching GitHub Release. Supported: macOS (Intel + Apple Silicon),
12
+ Linux (amd64 + arm64), Windows (amd64 + arm64).
13
+
14
+ Source + docs: <https://github.com/observo-ai/observo-cli>
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@observo-ai/cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for pushing CI test runs, coverage, and live test status to Observo",
5
+ "homepage": "https://github.com/observo-ai/observo-cli",
6
+ "bugs": {
7
+ "url": "https://github.com/observo-ai/observo-cli/issues"
8
+ },
9
+ "license": "Apache-2.0",
10
+ "author": "Observo",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/observo-ai/observo-cli.git"
14
+ },
15
+ "bin": {
16
+ "observo": "./bin/observo"
17
+ },
18
+ "scripts": {
19
+ "postinstall": "node scripts/install.js"
20
+ },
21
+ "files": [
22
+ "bin/",
23
+ "scripts/",
24
+ "README.md"
25
+ ],
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "keywords": [
30
+ "observo",
31
+ "ci",
32
+ "test-pipeline",
33
+ "coverage",
34
+ "junit",
35
+ "lcov",
36
+ "playwright"
37
+ ]
38
+ }
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+ // postinstall: download the platform-matched observo binary from the
3
+ // matching GitHub Release and drop it at bin/observo so `npx observo`
4
+ // and the package.json `bin` field resolve.
5
+ //
6
+ // Version comes from package.json (set to the tag value by release.yml
7
+ // before publish), so user gets the exact binary built for that release.
8
+ //
9
+ // Failure modes are explicit: if the platform isn't supported or the
10
+ // download fails, the postinstall exits non-zero so npm surfaces the
11
+ // install error rather than leaving a broken bin behind.
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('node:fs');
16
+ const path = require('node:path');
17
+ const https = require('node:https');
18
+ const os = require('node:os');
19
+ const { execSync } = require('node:child_process');
20
+ const zlib = require('node:zlib');
21
+
22
+ const pkg = require('../package.json');
23
+ const VERSION = pkg.version;
24
+ const BINARY_NAME = process.platform === 'win32' ? 'observo.exe' : 'observo';
25
+ const BIN_DIR = path.join(__dirname, '..', 'bin');
26
+ const BIN_PATH = path.join(BIN_DIR, BINARY_NAME);
27
+
28
+ // Platform → GoReleaser archive name mapping. Must match the
29
+ // `name_template` in .goreleaser.yaml: observo_<version>_<os>_<arch>.<ext>
30
+ function archiveName() {
31
+ const platformMap = {
32
+ darwin: 'Darwin',
33
+ linux: 'Linux',
34
+ win32: 'Windows',
35
+ };
36
+ const archMap = {
37
+ x64: 'x86_64',
38
+ arm64: 'arm64',
39
+ };
40
+ const goos = platformMap[process.platform];
41
+ const goarch = archMap[process.arch];
42
+ if (!goos || !goarch) {
43
+ throw new Error(
44
+ `unsupported platform: ${process.platform}/${process.arch}\n` +
45
+ 'Supported: darwin/amd64, darwin/arm64, linux/amd64, linux/arm64, windows/amd64, windows/arm64.'
46
+ );
47
+ }
48
+ const ext = process.platform === 'win32' ? 'zip' : 'tar.gz';
49
+ return `observo_${VERSION}_${goos}_${goarch}.${ext}`;
50
+ }
51
+
52
+ function downloadURL() {
53
+ return `https://github.com/observo-ai/observo-cli/releases/download/v${VERSION}/${archiveName()}`;
54
+ }
55
+
56
+ function followRedirects(url, depth = 0) {
57
+ if (depth > 5) {
58
+ return Promise.reject(new Error('too many redirects'));
59
+ }
60
+ return new Promise((resolve, reject) => {
61
+ const req = https.get(url, (res) => {
62
+ if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307 || res.statusCode === 308) {
63
+ res.resume();
64
+ return resolve(followRedirects(res.headers.location, depth + 1));
65
+ }
66
+ if (res.statusCode !== 200) {
67
+ return reject(new Error(`download failed: HTTP ${res.statusCode} for ${url}`));
68
+ }
69
+ const chunks = [];
70
+ res.on('data', (c) => chunks.push(c));
71
+ res.on('end', () => resolve(Buffer.concat(chunks)));
72
+ res.on('error', reject);
73
+ });
74
+ req.on('error', reject);
75
+ });
76
+ }
77
+
78
+ async function extractTarGz(buf, dest) {
79
+ // Avoid pulling tar/extract npm dep — use the system `tar` which exists
80
+ // on macOS, Linux, and Windows 10+ (1803+). Pipe via stdin.
81
+ const tmp = path.join(os.tmpdir(), `observo-${Date.now()}.tar.gz`);
82
+ fs.writeFileSync(tmp, buf);
83
+ try {
84
+ execSync(`tar -xzf "${tmp}" -C "${dest}" "${BINARY_NAME}"`, { stdio: 'inherit' });
85
+ } finally {
86
+ fs.unlinkSync(tmp);
87
+ }
88
+ }
89
+
90
+ function extractZip(buf, dest) {
91
+ // Windows: use built-in tar (also supports zip on 1803+).
92
+ const tmp = path.join(os.tmpdir(), `observo-${Date.now()}.zip`);
93
+ fs.writeFileSync(tmp, buf);
94
+ try {
95
+ execSync(`tar -xf "${tmp}" -C "${dest}" "${BINARY_NAME}"`, { stdio: 'inherit' });
96
+ } finally {
97
+ fs.unlinkSync(tmp);
98
+ }
99
+ }
100
+
101
+ async function main() {
102
+ fs.mkdirSync(BIN_DIR, { recursive: true });
103
+
104
+ const url = downloadURL();
105
+ process.stdout.write(`observo: downloading ${url}\n`);
106
+
107
+ const buf = await followRedirects(url);
108
+
109
+ if (url.endsWith('.zip')) {
110
+ extractZip(buf, BIN_DIR);
111
+ } else {
112
+ await extractTarGz(buf, BIN_DIR);
113
+ }
114
+
115
+ if (!fs.existsSync(BIN_PATH)) {
116
+ throw new Error(`extracted archive did not contain ${BINARY_NAME}`);
117
+ }
118
+
119
+ if (process.platform !== 'win32') {
120
+ fs.chmodSync(BIN_PATH, 0o755);
121
+ }
122
+
123
+ // Verify the binary runs and matches the requested version. Catches
124
+ // GoReleaser version-skew bugs at install time, not first invocation.
125
+ try {
126
+ const out = execSync(`"${BIN_PATH}" --version`, { encoding: 'utf8' });
127
+ process.stdout.write(`observo: installed ${out.trim().split('\n')[0]}\n`);
128
+ } catch (err) {
129
+ throw new Error(`observo binary installed but failed to run: ${err.message}`);
130
+ }
131
+ }
132
+
133
+ main().catch((err) => {
134
+ process.stderr.write(`observo: install failed: ${err.message}\n`);
135
+ process.exit(1);
136
+ });