looply-cli 0.5.13 → 0.5.17

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.
Files changed (44) hide show
  1. package/dist/.src-hash +1 -1
  2. package/dist/bin.d.ts.map +1 -1
  3. package/dist/bin.js +8 -0
  4. package/dist/bin.js.map +1 -1
  5. package/dist/config.d.ts +19 -1
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +29 -0
  8. package/dist/config.js.map +1 -1
  9. package/dist/daemon-cmd.d.ts.map +1 -1
  10. package/dist/daemon-cmd.js +18 -0
  11. package/dist/daemon-cmd.js.map +1 -1
  12. package/dist/heroku-api.d.ts +87 -0
  13. package/dist/heroku-api.d.ts.map +1 -0
  14. package/dist/heroku-api.js +178 -0
  15. package/dist/heroku-api.js.map +1 -0
  16. package/dist/preview-cmd.d.ts +51 -0
  17. package/dist/preview-cmd.d.ts.map +1 -0
  18. package/dist/preview-cmd.js +194 -0
  19. package/dist/preview-cmd.js.map +1 -0
  20. package/dist/preview-environments.d.ts +173 -0
  21. package/dist/preview-environments.d.ts.map +1 -0
  22. package/dist/preview-environments.js +482 -0
  23. package/dist/preview-environments.js.map +1 -0
  24. package/dist/preview-github.d.ts +63 -0
  25. package/dist/preview-github.d.ts.map +1 -0
  26. package/dist/preview-github.js +130 -0
  27. package/dist/preview-github.js.map +1 -0
  28. package/dist/preview-settings-cache.d.ts +33 -0
  29. package/dist/preview-settings-cache.d.ts.map +1 -0
  30. package/dist/preview-settings-cache.js +108 -0
  31. package/dist/preview-settings-cache.js.map +1 -0
  32. package/dist/preview-wiring.d.ts +35 -0
  33. package/dist/preview-wiring.d.ts.map +1 -0
  34. package/dist/preview-wiring.js +81 -0
  35. package/dist/preview-wiring.js.map +1 -0
  36. package/dist/run-spend-report.d.ts +55 -0
  37. package/dist/run-spend-report.d.ts.map +1 -0
  38. package/dist/run-spend-report.js +84 -0
  39. package/dist/run-spend-report.js.map +1 -0
  40. package/dist/server-client.d.ts +22 -0
  41. package/dist/server-client.d.ts.map +1 -1
  42. package/dist/server-client.js +26 -0
  43. package/dist/server-client.js.map +1 -1
  44. package/package.json +2 -2
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HerokuError = exports.HEROKU_ALLOWED = exports.HEROKU_API = void 0;
4
+ exports.herokuCallRefusal = herokuCallRefusal;
5
+ exports.makeHerokuApi = makeHerokuApi;
6
+ exports.netrcPassword = netrcPassword;
7
+ exports.herokuToken = herokuToken;
8
+ const node_fs_1 = require("node:fs");
9
+ const node_os_1 = require("node:os");
10
+ const node_path_1 = require("node:path");
11
+ /**
12
+ * THE SLICE OF HEROKU A PREVIEW NEEDS: a pipeline's review apps — read them,
13
+ * ask for one, remove one — and nothing else.
14
+ *
15
+ * ─────────────────────────────────────────────────────────────────────────────
16
+ * 🔴 NEVER A PRODUCTION APP. Several of the owner's apps deploy production from
17
+ * Heroku (looply-api, autoryn). This client can only reach the routes in
18
+ * `HEROKU_ALLOWED` below, checked on every call before anything is sent:
19
+ *
20
+ * GET /pipelines/:nameOrId resolve the configured pipeline
21
+ * GET /pipelines/:id/review-apps list its review apps
22
+ * GET /review-apps/:id one review app
23
+ * POST /review-apps ask Heroku for one (only when the setting says `create`)
24
+ * DELETE /review-apps/:id remove one — a REVIEW app, by its review-app id
25
+ * GET /apps/:id the web address of an app THIS CLIENT just read
26
+ * as a review app, and no other
27
+ *
28
+ * There is no route here that can scale, configure, release to, restart or
29
+ * delete an app as such: `DELETE /review-apps/:id` is Heroku's own review-app
30
+ * teardown, and it refuses an id that is not a review app.
31
+ *
32
+ * 🔴 THE MACHINE'S OWN HEROKU LOGIN, AND NOTHING NEW. `HEROKU_API_KEY` when set,
33
+ * else the token the Heroku CLI stored in the person's netrc (`_netrc` on
34
+ * Windows) for api.heroku.com. Never stored, printed or sent anywhere but
35
+ * api.heroku.com. None → null, and the caller says so in words.
36
+ */
37
+ exports.HEROKU_API = 'https://api.heroku.com';
38
+ const ID = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}';
39
+ const NAME_OR_ID = `(?:[a-z][a-z0-9-]{1,99}|${ID})`;
40
+ exports.HEROKU_ALLOWED = [
41
+ { method: 'GET', path: new RegExp(`^/pipelines/${NAME_OR_ID}$`) },
42
+ { method: 'GET', path: new RegExp(`^/pipelines/${ID}/review-apps$`) },
43
+ { method: 'GET', path: new RegExp(`^/review-apps/${ID}$`) },
44
+ { method: 'POST', path: /^\/review-apps$/ },
45
+ { method: 'DELETE', path: new RegExp(`^/review-apps/${ID}$`) },
46
+ { method: 'GET', path: new RegExp(`^/apps/${ID}$`) },
47
+ ];
48
+ /** Null when Heroku may be asked this; otherwise why not. */
49
+ function herokuCallRefusal(method, path) {
50
+ const ok = exports.HEROKU_ALLOWED.some((r) => r.method === method && r.path.test(path));
51
+ return ok ? null : `${method} ${path} is not a review-app call; Looply's Heroku client only reads and manages review apps`;
52
+ }
53
+ class HerokuError extends Error {
54
+ status;
55
+ constructor(message, status) {
56
+ super(message);
57
+ this.status = status;
58
+ this.name = 'HerokuError';
59
+ }
60
+ }
61
+ exports.HerokuError = HerokuError;
62
+ function makeHerokuApi(deps) {
63
+ const doFetch = deps.fetch ?? ((url, init) => fetch(url, init));
64
+ /** App ids this client read as review apps — the only apps `webUrl` may look at. */
65
+ const reviewAppIds = new Set();
66
+ async function call(method, path, body, okMissing = false) {
67
+ const refused = herokuCallRefusal(method, path);
68
+ if (refused !== null)
69
+ throw new HerokuError(`refused before anything was sent: ${refused}`, 0);
70
+ const res = await doFetch(`${exports.HEROKU_API}${path}`, {
71
+ method,
72
+ headers: {
73
+ authorization: `Bearer ${deps.token}`,
74
+ accept: 'application/vnd.heroku+json; version=3',
75
+ 'user-agent': 'looply-cli',
76
+ ...(body === undefined ? {} : { 'content-type': 'application/json' }),
77
+ },
78
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
79
+ });
80
+ const text = await res.text();
81
+ if (okMissing && (res.status === 404 || res.status === 403))
82
+ return null;
83
+ if (res.status < 200 || res.status >= 300) {
84
+ let message = text.slice(0, 200);
85
+ try {
86
+ const j = JSON.parse(text);
87
+ if (typeof j.message === 'string')
88
+ message = j.message.slice(0, 200);
89
+ }
90
+ catch {
91
+ /* not JSON */
92
+ }
93
+ throw new HerokuError(`Heroku answered ${res.status} to ${method} ${path.replace(/\/[0-9a-f-]{36}/g, '/…')}: ${message}`, res.status);
94
+ }
95
+ return (text === '' ? null : JSON.parse(text));
96
+ }
97
+ const toReviewApp = (r) => {
98
+ const appId = typeof r['app']?.['id'] === 'string' ? r['app']['id'] : null;
99
+ if (appId !== null)
100
+ reviewAppIds.add(appId);
101
+ const status = String(r['status'] ?? 'pending');
102
+ return {
103
+ id: String(r['id']),
104
+ appId,
105
+ branch: String(r['branch'] ?? ''),
106
+ prNumber: typeof r['pr_number'] === 'number' ? r['pr_number'] : null,
107
+ status: ['pending', 'creating', 'created', 'deleting', 'deleted', 'errored'].includes(status) ? status : 'pending',
108
+ message: typeof r['message'] === 'string' && r['message'] !== '' ? r['message'].slice(0, 200) : null,
109
+ };
110
+ };
111
+ return {
112
+ async pipelineId(nameOrId) {
113
+ const p = await call('GET', `/pipelines/${nameOrId}`, undefined, true);
114
+ return p !== null && typeof p['id'] === 'string' ? p['id'] : null;
115
+ },
116
+ async reviewApps(pipelineId) {
117
+ const list = await call('GET', `/pipelines/${pipelineId}/review-apps`);
118
+ return (list ?? []).map(toReviewApp);
119
+ },
120
+ async webUrl(appId) {
121
+ if (!reviewAppIds.has(appId))
122
+ return null; // never an app this client did not read as a review app
123
+ const a = await call('GET', `/apps/${appId}`, undefined, true);
124
+ return a !== null && typeof a['web_url'] === 'string' ? a['web_url'] : null;
125
+ },
126
+ async createReviewApp(input) {
127
+ const r = await call('POST', '/review-apps', {
128
+ pipeline: input.pipelineId,
129
+ branch: input.branch,
130
+ pr_number: input.prNumber,
131
+ source_blob: { url: input.sourceUrl, version: input.sha },
132
+ });
133
+ return toReviewApp(r ?? {});
134
+ },
135
+ async deleteReviewApp(id) {
136
+ await call('DELETE', `/review-apps/${id}`, undefined, true);
137
+ },
138
+ };
139
+ }
140
+ /* ─────────────────────────────────────────────────────────── credentials ── */
141
+ /** The api.heroku.com password in a netrc file's text, or null. */
142
+ function netrcPassword(text, host = 'api.heroku.com') {
143
+ const tokens = text.split(/\s+/).filter((t) => t !== '');
144
+ for (let i = 0; i < tokens.length; i++) {
145
+ if (tokens[i] !== 'machine' || tokens[i + 1] !== host)
146
+ continue;
147
+ for (let j = i + 2; j < tokens.length && tokens[j] !== 'machine'; j++) {
148
+ if (tokens[j] === 'password' && tokens[j + 1] !== undefined)
149
+ return tokens[j + 1];
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+ /** A Heroku token for this machine, and where it came from; null when there is none. */
155
+ function herokuToken(deps = {}) {
156
+ const env = deps.env ?? process.env;
157
+ const fromEnv = env['HEROKU_API_KEY']?.trim();
158
+ if (fromEnv)
159
+ return { token: fromEnv, from: 'env' };
160
+ const home = deps.home ?? (0, node_os_1.homedir)();
161
+ const read = deps.read ??
162
+ ((p) => {
163
+ try {
164
+ return (0, node_fs_1.readFileSync)(p, 'utf8');
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ });
170
+ for (const name of ['_netrc', '.netrc']) {
171
+ const text = read((0, node_path_1.join)(home, name));
172
+ const t = text === null ? null : netrcPassword(text);
173
+ if (t !== null && /^[A-Za-z0-9_.:-]{20,}$/.test(t))
174
+ return { token: t, from: 'netrc' };
175
+ }
176
+ return null;
177
+ }
178
+ //# sourceMappingURL=heroku-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heroku-api.js","sourceRoot":"","sources":["../src/heroku-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { type PreviewSettings } from '@looply/core';
2
+ import { type HerokuApi } from './heroku-api';
3
+ import { type PreviewStore } from './preview-environments';
4
+ import { type GitHubPreviewReader } from './preview-github';
5
+ /**
6
+ * `looply preview [--dry-run] [--provider <p>] [--site <s>] [--pipeline <p>] [--limit <n>]`
7
+ *
8
+ * Without `--dry-run`: the provider in force (and where it came from) and every
9
+ * preview this machine recorded, one line each.
10
+ *
11
+ * With `--dry-run`: READS ONLY. For the newest open pull requests on origin it
12
+ * says what the provider in force (or `--provider …`, to try one before choosing
13
+ * it) would show on the ticket — from what Netlify or Heroku already reported to
14
+ * GitHub, and from Heroku's API when this machine is signed in to Heroku. It
15
+ * also says when a provider ALREADY builds previews for this repository that the
16
+ * setting is not using ("Netlify already builds deploy previews here").
17
+ *
18
+ * 🔴 A DRY RUN NEVER RUNS THE PREVIEW COMMAND, NEVER ASKS HEROKU FOR AN APP and
19
+ * never removes one, and records nothing on the server: it costs nothing.
20
+ */
21
+ export interface PreviewCmdDeps {
22
+ settingsInForce(): {
23
+ settings: PreviewSettings | null;
24
+ from: string;
25
+ };
26
+ declared: {
27
+ command?: string;
28
+ teardown?: string;
29
+ };
30
+ store: PreviewStore;
31
+ /** owner/repo of origin on github.com, or null with why. */
32
+ origin(): Promise<{
33
+ owner: string;
34
+ repo: string;
35
+ } | {
36
+ because: string;
37
+ }>;
38
+ reader(owner: string, repo: string): Promise<GitHubPreviewReader | null>;
39
+ heroku(): HerokuApi | null;
40
+ out(line: string): void;
41
+ }
42
+ export declare function parsePreviewArgs(argv: readonly string[]): {
43
+ dryRun: boolean;
44
+ settings: PreviewSettings | null;
45
+ limit: number;
46
+ } | {
47
+ error: string;
48
+ };
49
+ export declare function previewCommand(argv: readonly string[], deps: PreviewCmdDeps): Promise<number>;
50
+ export declare function nodePreviewDeps(cwd: string, out: (line: string) => void): PreviewCmdDeps;
51
+ //# sourceMappingURL=preview-cmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-cmd.d.ts","sourceRoot":"","sources":["../src/preview-cmd.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4D,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAK9G,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAwD,KAAK,mBAAmB,EAAsB,MAAM,kBAAkB,CAAC;AAGtI;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,cAAc;IAC7B,eAAe,IAAI;QAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,QAAQ,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,KAAK,EAAE,YAAY,CAAC;IACpB,4DAA4D;IAC5D,MAAM,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IACzE,MAAM,IAAI,SAAS,GAAG,IAAI,CAAC;IAC3B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CA8BlJ;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmFnG;AAUD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,cAAc,CA8BxF"}
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parsePreviewArgs = parsePreviewArgs;
4
+ exports.previewCommand = previewCommand;
5
+ exports.nodePreviewDeps = nodePreviewDeps;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const core_1 = require("@looply/core");
9
+ const config_1 = require("./config");
10
+ const delivery_rules_1 = require("./delivery-rules");
11
+ const git_driver_1 = require("./git-driver");
12
+ const github_api_1 = require("./github-api");
13
+ const heroku_api_1 = require("./heroku-api");
14
+ const preview_environments_1 = require("./preview-environments");
15
+ const preview_github_1 = require("./preview-github");
16
+ const preview_settings_cache_1 = require("./preview-settings-cache");
17
+ function parsePreviewArgs(argv) {
18
+ let dryRun = false;
19
+ let limit = 5;
20
+ const shape = {};
21
+ for (let i = 0; i < argv.length; i++) {
22
+ const a = argv[i];
23
+ const val = () => {
24
+ const v = argv[i + 1];
25
+ if (v === undefined || v.startsWith('--'))
26
+ return null;
27
+ i++;
28
+ return v;
29
+ };
30
+ if (a === '--dry-run')
31
+ dryRun = true;
32
+ else if (a === '--provider' || a === '--site' || a === '--pipeline') {
33
+ const v = val();
34
+ if (v === null)
35
+ return { error: `${a} needs a value` };
36
+ shape[a.slice(2)] = v;
37
+ }
38
+ else if (a === '--create')
39
+ shape['create'] = true;
40
+ else if (a === '--limit') {
41
+ const v = Number(val());
42
+ if (!Number.isInteger(v) || v < 1 || v > 30)
43
+ return { error: '--limit must be a whole number from 1 to 30' };
44
+ limit = v;
45
+ }
46
+ else
47
+ return { error: `unknown argument: ${a}` };
48
+ }
49
+ if (Object.keys(shape).length === 0)
50
+ return { dryRun, settings: null, limit };
51
+ if (!('provider' in shape))
52
+ return { error: '--site, --pipeline and --create need --provider' };
53
+ const v = (0, core_1.validatePreviewSettings)(shape);
54
+ if (!v.ok)
55
+ return { error: `--${v.key === '<root>' ? 'provider' : v.key}: ${v.because}` };
56
+ if (!dryRun)
57
+ return { error: '--provider only tries a provider with --dry-run; choose it for real in AutoRyn or preview.provider in .looply/config.yaml' };
58
+ return { dryRun, settings: v.settings, limit };
59
+ }
60
+ async function previewCommand(argv, deps) {
61
+ const args = parsePreviewArgs(argv);
62
+ if ('error' in args) {
63
+ deps.out(`looply preview: ${args.error}`);
64
+ return 64;
65
+ }
66
+ const inForce = deps.settingsInForce();
67
+ const settings = args.settings ?? inForce.settings;
68
+ deps.out(`Preview provider: ${settings === null ? 'none' : settings.provider} — ${(0, core_1.describePreview)(settings)}${args.settings ? ' (tried with --provider)' : ` (from ${inForce.from})`}`);
69
+ if (settings?.provider === 'command') {
70
+ deps.out(` command : ${deps.declared.command ?? '(none declared — preview.command in .looply/config.yaml)'}`);
71
+ deps.out(` teardown: ${deps.declared.teardown ?? '(none declared)'}`);
72
+ }
73
+ if (!args.dryRun) {
74
+ const all = deps.store.all().sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
75
+ if (all.length === 0)
76
+ deps.out('No previews recorded on this machine yet.');
77
+ for (const p of all) {
78
+ deps.out(` #${p.pullNumber} ${p.goalId} ${p.provider} ${p.state}${p.url ? ` ${p.url}` : ''}${p.detail ? ` (${p.detail})` : ''}`);
79
+ }
80
+ return 0;
81
+ }
82
+ deps.out('Dry run: reads only. No command runs, nothing is created or removed, nothing is recorded.');
83
+ const origin = await deps.origin();
84
+ if ('because' in origin) {
85
+ deps.out(` ${origin.because}`);
86
+ return 1;
87
+ }
88
+ const reader = await deps.reader(origin.owner, origin.repo);
89
+ if (reader === null) {
90
+ deps.out(' this machine has no GitHub credentials Looply can use (gh is not signed in, and git\'s credential helper gave none)');
91
+ return 1;
92
+ }
93
+ const pulls = await reader.openPulls(args.limit);
94
+ deps.out(`${origin.owner}/${origin.repo}: ${pulls.length} open pull request${pulls.length === 1 ? '' : 's'}`);
95
+ const heroku = settings?.provider === 'heroku' ? deps.heroku() : null;
96
+ if (settings?.provider === 'heroku')
97
+ deps.out(` Heroku login on this machine: ${heroku === null ? 'none (reading GitHub deployments only)' : 'yes (reading review apps through the Heroku API)'}`);
98
+ let herokuPid;
99
+ const alsoSeen = new Set();
100
+ for (const p of pulls) {
101
+ let signals = [];
102
+ try {
103
+ signals = await reader.signals(p.headSha);
104
+ }
105
+ catch (e) {
106
+ deps.out(` #${p.number} ${p.branch}: could not read its statuses (${e.message})`);
107
+ continue;
108
+ }
109
+ const netlify = (0, preview_github_1.netlifySignal)(signals);
110
+ if (netlify !== null && settings?.provider !== 'netlify')
111
+ alsoSeen.add(`Netlify (${netlify.name})`);
112
+ let line;
113
+ if (settings === null)
114
+ line = 'no preview (provider none)';
115
+ else if (settings.provider === 'netlify') {
116
+ const s = (0, preview_github_1.netlifySignal)(signals, settings.site);
117
+ line = s === null ? 'nothing from Netlify yet → building, then failed after 20 minutes if it never appears' : describeSignal(s);
118
+ }
119
+ else if (settings.provider === 'heroku') {
120
+ if (heroku !== null) {
121
+ if (herokuPid === undefined)
122
+ herokuPid = await heroku.pipelineId(settings.pipeline).catch(() => null);
123
+ if (herokuPid === null)
124
+ line = `this machine's Heroku login cannot see the pipeline ${settings.pipeline} → failed`;
125
+ else {
126
+ const app = (await heroku.reviewApps(herokuPid)).find((a) => a.prNumber === p.number && a.status !== 'deleted');
127
+ line = app
128
+ ? `review app ${app.status}${app.status === 'created' && app.appId ? ` → live ${(0, core_1.safePreviewUrl)(await heroku.webUrl(app.appId)) ?? '(no https address)'}` : ''}`
129
+ : settings.create
130
+ ? 'no review app → Looply WOULD ask Heroku for one (a new paid app; not done in a dry run)'
131
+ : 'no review app → waiting for the pipeline\'s automatic review apps';
132
+ }
133
+ }
134
+ else {
135
+ const s = (0, preview_github_1.herokuSignal)(signals, settings.pipeline, p.number);
136
+ line = s === null ? 'no review-app deployment on GitHub yet' : describeSignal(s);
137
+ }
138
+ }
139
+ else {
140
+ line = deps.declared.command
141
+ ? `would run the preview command with LOOPLY_PULL_REQUEST_NUMBER=${p.number} LOOPLY_PREVIEW_BRANCH=${p.branch} LOOPLY_HEAD_SHA=${p.headSha.slice(0, 12)} (not run in a dry run)`
142
+ : 'failed: no preview.command is declared in .looply/config.yaml';
143
+ }
144
+ deps.out(` #${p.number} ${p.branch} (${p.headSha.slice(0, 12)}): ${line}`);
145
+ }
146
+ if (alsoSeen.size > 0) {
147
+ deps.out(`Already building previews for this repository: ${[...alsoSeen].join(', ')}. Choose it as the preview provider to show its link on tickets.`);
148
+ }
149
+ return 0;
150
+ }
151
+ function describeSignal(s) {
152
+ const url = (0, core_1.safePreviewUrl)(s.url);
153
+ if (s.state === 'live')
154
+ return url === null ? `${s.name} reports success without an https address → failed` : `live ${url} (${s.source} ${s.name})`;
155
+ if (s.state === 'failed')
156
+ return `failed (${s.source} ${s.name}${s.description ? `: ${s.description}` : ''})`;
157
+ if (s.state === 'inactive')
158
+ return `removed (${s.source} ${s.name} is inactive)`;
159
+ return `building (${s.source} ${s.name})`;
160
+ }
161
+ function nodePreviewDeps(cwd, out) {
162
+ const stateDir = (0, git_driver_1.projectStateDir)(cwd);
163
+ let config = null;
164
+ try {
165
+ config = (0, config_1.parseConfig)((0, node_fs_1.readFileSync)((0, node_path_1.join)(cwd, '.looply', 'config.yaml'), 'utf8'));
166
+ }
167
+ catch {
168
+ config = null;
169
+ }
170
+ const exec = (0, git_driver_1.nodeGitExec)(cwd);
171
+ return {
172
+ settingsInForce: () => (0, preview_settings_cache_1.resolvePreview)(config?.preview?.settings, stateDir),
173
+ declared: { ...(config?.preview?.command ? { command: config.preview.command } : {}), ...(config?.preview?.teardown ? { teardown: config.preview.teardown } : {}) },
174
+ store: (0, preview_environments_1.filePreviewStore)(stateDir),
175
+ async origin() {
176
+ const r = await exec(['remote', 'get-url', delivery_rules_1.DELIVERY_REMOTE]);
177
+ const url = r.stdout.trim();
178
+ if (r.code !== 0 || url === '')
179
+ return { because: 'this repository has no origin remote' };
180
+ const gh = (0, delivery_rules_1.githubRepoOf)(url);
181
+ return gh === null ? { because: `origin (${(0, delivery_rules_1.redactUrl)(url)}) is not on github.com` } : gh;
182
+ },
183
+ async reader(owner, repo) {
184
+ const t = await (0, github_api_1.githubToken)();
185
+ return t === null ? null : (0, preview_github_1.makeGitHubPreviewReader)({ owner, repo, token: t.token });
186
+ },
187
+ heroku() {
188
+ const t = (0, heroku_api_1.herokuToken)();
189
+ return t === null ? null : (0, heroku_api_1.makeHerokuApi)({ token: t.token });
190
+ },
191
+ out,
192
+ };
193
+ }
194
+ //# sourceMappingURL=preview-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-cmd.js","sourceRoot":"","sources":["../src/preview-cmd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,173 @@
1
+ import { type PreviewReport, type PreviewSettings, type PreviewState } from '@looply/core';
2
+ import type { DeliveryStore } from './branch-delivery';
3
+ import type { GitHubApi } from './github-api';
4
+ import type { HerokuApi } from './heroku-api';
5
+ import { type GitHubPreviewReader } from './preview-github';
6
+ /**
7
+ * A DELIVERED PULL REQUEST BECOMES A PREVIEW SITE A PERSON CAN OPEN.
8
+ *
9
+ * ─────────────────────────────────────────────────────────────────────────────
10
+ * After `branch-delivery.ts` opened a goal's pull request, one pass every few
11
+ * minutes (beside `advanceDeliveries`) looks at each delivered pull request:
12
+ *
13
+ * open → the project's preview provider is asked where its preview stands
14
+ * (building / live with its address / failed), and that is recorded on
15
+ * the goal, where AutoRyn shows "Test it here →". A new commit on the
16
+ * pull request is a rebuild.
17
+ * closed → merged or not, the preview is torn down by the provider's own
18
+ * mechanism and recorded as removed.
19
+ *
20
+ * Providers (core's `preview-environment.ts` has the settings):
21
+ *
22
+ * netlify — READ ONLY. Netlify builds deploy previews itself; its commit status
23
+ * `netlify/<site>/deploy-preview` on the head names the address.
24
+ * Netlify keeps old deploy previews; closing is recorded as removed
25
+ * with that said in words.
26
+ * heroku — Heroku's review apps on the named pipeline: read through Heroku's
27
+ * API (this machine's own Heroku login) or, without one, the GitHub
28
+ * deployment Heroku reports. Created by Looply only when the setting
29
+ * says `create` (a new paid app); removed through
30
+ * `DELETE /review-apps/:id` — `heroku-api.ts` can reach no other kind of app.
31
+ * command — the project's OWN `.looply/config.yaml` `preview.command`, run
32
+ * hidden in the project folder; the last https address it prints is
33
+ * the preview. `preview.teardown` runs when the pull request closes.
34
+ *
35
+ * 🔴 NO SECRETS LEAVE THE MACHINE, AND NONE ARE LOGGED. What is recorded is a
36
+ * state, a cleaned address (scheme + host + path) and a sentence Looply wrote —
37
+ * never a command's output. The local log of a preview command
38
+ * (`run-logs/preview.log`) has credentials in URLs, `token=…`-style pairs and
39
+ * bearer tokens masked before it is written.
40
+ *
41
+ * Nothing here decides anything about the goal. It runs after the goal was met
42
+ * and its pull request opened; a failure here is a failed preview, said in
43
+ * words, and never throws.
44
+ */
45
+ export declare const PREVIEW_ENVIRONMENTS_FILE = "preview-environments.json";
46
+ export interface PreviewEntry {
47
+ goalId: string;
48
+ provider: Exclude<PreviewSettings['provider'], 'none'>;
49
+ state: PreviewState;
50
+ url: string | null;
51
+ detail: string | null;
52
+ owner: string;
53
+ repo: string;
54
+ pullNumber: number;
55
+ pullRequestUrl: string | null;
56
+ branch: string;
57
+ /** The head commit this state is about. */
58
+ sha: string | null;
59
+ /** When Looply started waiting for this commit's preview (the "never appeared" clock). */
60
+ startedAt: string;
61
+ updatedAt: string;
62
+ /** Heroku: the review app Looply read or asked for. */
63
+ reviewAppId?: string | null;
64
+ /** Failed teardowns so far. */
65
+ teardownAttempts?: number;
66
+ }
67
+ export interface PreviewStore {
68
+ get(goalId: string): PreviewEntry | null;
69
+ all(): PreviewEntry[];
70
+ put(entry: PreviewEntry): void;
71
+ }
72
+ export declare function filePreviewStore(stateDir: string): PreviewStore;
73
+ export declare function memoryPreviewStore(): PreviewStore;
74
+ /** The pull request a preview is for. */
75
+ export interface PreviewTarget {
76
+ readonly goalId: string;
77
+ readonly owner: string;
78
+ readonly repo: string;
79
+ readonly pullNumber: number;
80
+ readonly pullRequestUrl: string | null;
81
+ readonly branch: string;
82
+ readonly headSha: string;
83
+ }
84
+ export interface Observation {
85
+ readonly state: PreviewState;
86
+ readonly url: string | null;
87
+ readonly detail: string | null;
88
+ readonly reviewAppId?: string | null;
89
+ }
90
+ export interface PreviewDriver {
91
+ readonly provider: PreviewEntry['provider'];
92
+ /**
93
+ * Where this pull request's preview of `headSha` stands — starting it where the
94
+ * provider is Looply's to start. Null: the provider has said nothing yet.
95
+ */
96
+ observe(t: PreviewTarget, prior: PreviewEntry | null): Promise<Observation | null>;
97
+ /** Tear it down after the pull request closed. `removed: false` is retried next pass. */
98
+ teardown(t: PreviewTarget, prior: PreviewEntry): Promise<{
99
+ removed: boolean;
100
+ detail: string;
101
+ }>;
102
+ /** What a person should check when the provider never reported a preview. */
103
+ readonly neverAppeared: string;
104
+ }
105
+ /** How long a provider is given to show any sign of a preview before it reads as failed. */
106
+ export declare const PREVIEW_APPEAR_GRACE_MS: number;
107
+ /** Failed teardowns before Looply stops trying and says to remove it by hand. */
108
+ export declare const MAX_TEARDOWN_ATTEMPTS = 5;
109
+ /** Netlify deploy previews: read, never made, never deleted by Looply. */
110
+ export declare function netlifyDriver(settings: {
111
+ site?: string;
112
+ }, reader: GitHubPreviewReader): PreviewDriver;
113
+ /** Heroku review apps on one pipeline. */
114
+ export declare function herokuDriver(settings: {
115
+ pipeline: string;
116
+ create?: boolean;
117
+ }, reader: GitHubPreviewReader, heroku: HerokuApi | null): PreviewDriver;
118
+ export interface CommandOutcome {
119
+ readonly code: number;
120
+ readonly stdout: string;
121
+ }
122
+ export type PreviewCommandRunner = (command: string, env: Record<string, string>, timeoutMs: number) => Promise<CommandOutcome>;
123
+ /** How long a preview command may run before it is stopped and the preview reads as failed. */
124
+ export declare const PREVIEW_COMMAND_TIMEOUT_MS: number;
125
+ /** Preview commands building at once on this machine, per project. */
126
+ export declare const MAX_PREVIEW_JOBS = 2;
127
+ /**
128
+ * The address a preview command printed: a `LOOPLY_PREVIEW_URL=https://…` line
129
+ * when there is one, else the last https address in its output; cleaned by
130
+ * core's `safePreviewUrl`. Null when it printed none.
131
+ */
132
+ export declare function previewUrlFromOutput(stdout: string): string | null;
133
+ /**
134
+ * The project's own preview command. The build runs in the background — a
135
+ * 30-minute build must not hold up the five-minute pass — and its result is
136
+ * picked up by the next pass. A daemon restart mid-build starts it again.
137
+ */
138
+ export declare function commandDriver(decl: {
139
+ command?: string;
140
+ teardown?: string;
141
+ }, run: PreviewCommandRunner): PreviewDriver;
142
+ /**
143
+ * Mask what looks like a credential before a line is written to a local log:
144
+ * user:password in URLs, `token=…` / `api_key: …` / `password=…` pairs,
145
+ * `Bearer …`, and the token shapes of GitHub, Heroku and Netlify.
146
+ */
147
+ export declare function redactSecrets(text: string): string;
148
+ /**
149
+ * Run a declared preview step in the project folder, hidden, bounded, its output
150
+ * appended (secrets masked) to `.looply/state/run-logs/preview.log`.
151
+ */
152
+ export declare function makePreviewCommandRunner(projectDir: string, stateDir: string): PreviewCommandRunner;
153
+ export interface PreviewDeps {
154
+ /** The provider in force now (config.yaml `preview.provider`, else the remote setting); null: none. */
155
+ settings(): PreviewSettings | null;
156
+ /** A driver for a provider: the setting in force for a new preview, or the one an existing preview was made with. */
157
+ driver(provider: PreviewEntry['provider']): PreviewDriver | null;
158
+ deliveries: Pick<DeliveryStore, 'all'>;
159
+ store: PreviewStore;
160
+ /** A pull-request client for owner/repo, or null without credentials. */
161
+ github(owner: string, repo: string): Promise<GitHubApi | null>;
162
+ record(goalId: string, report: PreviewReport): Promise<void>;
163
+ now(): number;
164
+ log(line: string): void;
165
+ }
166
+ /**
167
+ * One pass over every delivered pull request: start or read its preview while it
168
+ * is open, tear it down once it closed. Each change is recorded on the goal.
169
+ * Never throws.
170
+ */
171
+ export declare function advancePreviews(deps: PreviewDeps): Promise<void>;
172
+ export declare function providerName(p: PreviewEntry['provider']): string;
173
+ //# sourceMappingURL=preview-environments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-environments.d.ts","sourceRoot":"","sources":["../src/preview-environments.ts"],"names":[],"mappings":"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"}