laneyard 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/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/src/cli/add.js +81 -0
- package/dist/src/cli/detect.js +70 -0
- package/dist/src/config/load.js +46 -0
- package/dist/src/config/resolve.js +29 -0
- package/dist/src/config/schema.js +55 -0
- package/dist/src/config/store.js +71 -0
- package/dist/src/db/cache.js +22 -0
- package/dist/src/db/open.js +12 -0
- package/dist/src/db/runs.js +97 -0
- package/dist/src/db/schema.sql +45 -0
- package/dist/src/git/workspace.js +106 -0
- package/dist/src/heuristics/error-summary.js +43 -0
- package/dist/src/logs/store.js +66 -0
- package/dist/src/main.js +124 -0
- package/dist/src/runner/artifacts.js +59 -0
- package/dist/src/runner/live-steps.js +38 -0
- package/dist/src/runner/orchestrate.js +140 -0
- package/dist/src/runner/pty.js +83 -0
- package/dist/src/runner/report.js +86 -0
- package/dist/src/server/app.js +84 -0
- package/dist/src/server/auth.js +81 -0
- package/dist/src/server/routes/projects.js +35 -0
- package/dist/src/server/routes/runs.js +100 -0
- package/dist/src/server/ws.js +66 -0
- package/dist/src/sidecar/bridge.js +44 -0
- package/dist/src/sidecar/lanes.js +40 -0
- package/dist/src/sidecar/ruby-env.js +63 -0
- package/dist/tests/cli/add.test.js +64 -0
- package/dist/tests/cli/detect.test.js +63 -0
- package/dist/tests/config/load.test.js +68 -0
- package/dist/tests/config/resolve.test.js +44 -0
- package/dist/tests/config/store.test.js +58 -0
- package/dist/tests/db/runs.test.js +54 -0
- package/dist/tests/e2e/full-thread.test.js +65 -0
- package/dist/tests/fixtures/repos.js +30 -0
- package/dist/tests/git/workspace.test.js +66 -0
- package/dist/tests/heuristics/error-summary.test.js +31 -0
- package/dist/tests/logs/store.test.js +38 -0
- package/dist/tests/main.test.js +27 -0
- package/dist/tests/ruby/introspect.test.js +59 -0
- package/dist/tests/runner/artifacts.test.js +49 -0
- package/dist/tests/runner/live-steps.test.js +35 -0
- package/dist/tests/runner/orchestrate.test.js +124 -0
- package/dist/tests/runner/pty.test.js +53 -0
- package/dist/tests/runner/report.test.js +91 -0
- package/dist/tests/server/api.test.js +132 -0
- package/dist/tests/server/auth.test.js +53 -0
- package/dist/tests/server/ws.test.js +43 -0
- package/dist/tests/sidecar/lanes.test.js +52 -0
- package/dist/tests/sidecar/ruby-env.test.js +25 -0
- package/dist/tests/smoke.test.js +7 -0
- package/dist/web/assets/index-583x0xuo.css +1 -0
- package/dist/web/assets/index-BEpABKPS.js +61 -0
- package/dist/web/index.html +13 -0
- package/package.json +70 -0
- package/ruby/introspect.rb +87 -0
- package/scripts/fix-node-pty-permissions.mjs +26 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>laneyard</title>
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BEpABKPS.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-583x0xuo.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "laneyard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"laneyard": "dist/src/main.js"
|
|
7
|
+
},
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "tsx watch src/main.ts",
|
|
13
|
+
"dev:web": "vite --config web/vite.config.ts",
|
|
14
|
+
"build:web": "vite build --config web/vite.config.ts",
|
|
15
|
+
"build": "tsc -p tsconfig.json && cp src/db/schema.sql dist/src/db/ && npm run build:web",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p web/tsconfig.json --noEmit",
|
|
19
|
+
"postinstall": "node scripts/fix-node-pty-permissions.mjs",
|
|
20
|
+
"prepare": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@fastify/cookie": "^11.0.0",
|
|
24
|
+
"@fastify/static": "^8.0.0",
|
|
25
|
+
"@fastify/websocket": "^11.0.0",
|
|
26
|
+
"better-sqlite3": "^11.7.0",
|
|
27
|
+
"fastify": "^5.2.0",
|
|
28
|
+
"node-pty": "^1.0.0",
|
|
29
|
+
"react": "^19.2.7",
|
|
30
|
+
"react-dom": "^19.2.7",
|
|
31
|
+
"react-router-dom": "^7.18.1",
|
|
32
|
+
"tinyglobby": "^0.2.10",
|
|
33
|
+
"yaml": "^2.7.0",
|
|
34
|
+
"zod": "^3.24.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
38
|
+
"@types/node": "^22.10.0",
|
|
39
|
+
"@types/react": "^19.2.17",
|
|
40
|
+
"@types/react-dom": "^19.2.3",
|
|
41
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
42
|
+
"tsx": "^4.19.0",
|
|
43
|
+
"typescript": "^5.7.0",
|
|
44
|
+
"vite": "^5.4.21",
|
|
45
|
+
"vitest": "^2.1.0"
|
|
46
|
+
},
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"description": "A self-hosted web UI for fastlane: trigger lanes from your browser, watch builds stream live, keep your artifacts and your signing keys.",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/martinfrouin/laneyard.git"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist",
|
|
55
|
+
"ruby",
|
|
56
|
+
"scripts",
|
|
57
|
+
"README.md",
|
|
58
|
+
"LICENSE"
|
|
59
|
+
],
|
|
60
|
+
"keywords": [
|
|
61
|
+
"fastlane",
|
|
62
|
+
"ci",
|
|
63
|
+
"self-hosted",
|
|
64
|
+
"ios",
|
|
65
|
+
"android",
|
|
66
|
+
"mobile",
|
|
67
|
+
"build-server",
|
|
68
|
+
"testflight"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Laneyard's introspection sidecar.
|
|
5
|
+
#
|
|
6
|
+
# Launched in a project's folder — ideally via `bundle exec` — it's the only
|
|
7
|
+
# component that knows fastlane. It never writes anything: it reads and returns
|
|
8
|
+
# JSON on standard output.
|
|
9
|
+
#
|
|
10
|
+
# ruby introspect.rb lanes --fastlane-dir fastlane
|
|
11
|
+
# ruby introspect.rb actions --fastlane-dir fastlane
|
|
12
|
+
# ruby introspect.rb parse --fastlane-dir fastlane
|
|
13
|
+
#
|
|
14
|
+
# The output contract is constant: { "ok": true, ... } or { "ok": false, "error": "..." }.
|
|
15
|
+
# An error is a valid response, never a trace on stderr.
|
|
16
|
+
|
|
17
|
+
require "json"
|
|
18
|
+
|
|
19
|
+
# See below: the real standard output is set aside right from the start so
|
|
20
|
+
# that nothing but our JSON can slip into it.
|
|
21
|
+
REAL_STDOUT = $stdout.dup
|
|
22
|
+
|
|
23
|
+
def respond(payload)
|
|
24
|
+
REAL_STDOUT.puts JSON.generate(payload)
|
|
25
|
+
REAL_STDOUT.flush
|
|
26
|
+
exit 0
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def fail_with(message)
|
|
30
|
+
respond({ ok: false, error: message.to_s })
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
command = ARGV[0]
|
|
34
|
+
dir_index = ARGV.index("--fastlane-dir")
|
|
35
|
+
fastlane_dir = dir_index ? ARGV[dir_index + 1] : "fastlane"
|
|
36
|
+
fastfile_path = File.join(Dir.pwd, fastlane_dir, "Fastfile")
|
|
37
|
+
|
|
38
|
+
fail_with("Fastfile not found: #{fastfile_path}") unless File.exist?(fastfile_path)
|
|
39
|
+
|
|
40
|
+
# fastlane readily writes to standard output — plugin warnings, deprecation
|
|
41
|
+
# messages, update banner. Just one of these messages would corrupt the JSON
|
|
42
|
+
# the caller expects. Everything therefore goes to standard error, and only
|
|
43
|
+
# `respond` writes to the real output.
|
|
44
|
+
$stdout = $stderr
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
require "fastlane"
|
|
48
|
+
rescue LoadError => e
|
|
49
|
+
fail_with("fastlane is not available in this Ruby environment (#{e.message})")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def collect_lanes(fastfile_path)
|
|
53
|
+
ff = Fastlane::FastFile.new(fastfile_path)
|
|
54
|
+
lanes = []
|
|
55
|
+
ff.runner.lanes.each do |platform, platform_lanes|
|
|
56
|
+
platform_lanes.each do |name, lane|
|
|
57
|
+
lanes << {
|
|
58
|
+
name: name.to_s,
|
|
59
|
+
platform: platform&.to_s,
|
|
60
|
+
description: Array(lane.description).join(" ").strip,
|
|
61
|
+
private: lane.is_private
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
lanes
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
case command
|
|
69
|
+
when "lanes"
|
|
70
|
+
begin
|
|
71
|
+
lanes = collect_lanes(fastfile_path)
|
|
72
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
73
|
+
# A Fastfile is arbitrary Ruby: loading it can raise anything at all,
|
|
74
|
+
# including syntax errors that don't descend from StandardError.
|
|
75
|
+
#
|
|
76
|
+
# Careful: the success case's `respond` must stay OUTSIDE this
|
|
77
|
+
# begin/rescue. `respond` ends with `exit 0`, which raises SystemExit —
|
|
78
|
+
# a subclass of Exception, so caught by this `rescue Exception` if the
|
|
79
|
+
# call is made inside. The symptom is a second JSON blob (the "exit"
|
|
80
|
+
# error) written right after the first, which corrupts the output on
|
|
81
|
+
# the caller's side.
|
|
82
|
+
fail_with("Could not load the Fastfile: #{e.message}")
|
|
83
|
+
end
|
|
84
|
+
respond({ ok: true, lanes: lanes })
|
|
85
|
+
else
|
|
86
|
+
fail_with("Unknown command: #{command.inspect}")
|
|
87
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// npm dépose parfois le spawn-helper de node-pty sans droit d'exécution, ce qui
|
|
3
|
+
// fait échouer tout lancement de processus avec un message opaque. On répare au
|
|
4
|
+
// lieu de laisser chacun le découvrir.
|
|
5
|
+
import { chmod, readdir, stat } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
|
|
8
|
+
const root = "node_modules/node-pty/prebuilds";
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
for (const dir of await readdir(root)) {
|
|
12
|
+
const helper = join(root, dir, "spawn-helper");
|
|
13
|
+
try {
|
|
14
|
+
const info = await stat(helper);
|
|
15
|
+
// 0o111 : au moins un bit d'exécution.
|
|
16
|
+
if ((info.mode & 0o111) === 0) {
|
|
17
|
+
await chmod(helper, 0o755);
|
|
18
|
+
console.log(`node-pty : droit d'exécution rendu à ${helper}`);
|
|
19
|
+
}
|
|
20
|
+
} catch {
|
|
21
|
+
// Pas de helper dans ce dossier : rien à faire.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// node-pty absent ou sans prebuilds : l'installation n'a pas à échouer pour autant.
|
|
26
|
+
}
|