@bongos/core 1.17.3 → 1.18.3
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/.bongos-core.json +212 -332
- package/.devcontainer/healthcheck.sh +0 -0
- package/.devcontainer/init-firewall.sh +0 -0
- package/.devcontainer/post-create.sh +0 -0
- package/GOVERNANCE.md +1 -1
- package/README.md +26 -22
- package/bin/bongos.js +0 -0
- package/config/branding.neutral.json +5 -1
- package/docker-entrypoint.sh +0 -0
- package/docs/adr/0142-project-context-is-prose-only.md +41 -0
- package/docs/adr/0143-onboarding-repo-picker-oauth-scope.md +35 -0
- package/docs/adr/README.md +6 -0
- package/docs/api/openapi.json +1630 -632
- package/docs/api-reference.md +50 -15
- package/docs/file-map.md +0 -18
- package/docs/onboarding/diagrams/05-versioning.mmd +4 -6
- package/docs/onboarding/diagrams/assertions.json +5 -9
- package/docs/recipes/packaging-the-core.md +13 -0
- package/docs/recipes/upgrading-the-core.md +5 -2
- package/migrations/039_newcomer_friendly_column.sql +5 -0
- package/migrations/102_remove_box_first_provision_approval.sql +4 -0
- package/migrations/core_192_github_oauth_tokens.sql +33 -0
- package/modules/hall-ui/public/builders.js +37 -0
- package/modules/hall-ui/public/goals-page.js +98 -176
- package/modules/hall-ui/public/goals.css +183 -0
- package/modules/hall-ui/public/goals.html +35 -261
- package/modules/hall-ui/public/goals.js +7 -2
- package/modules/hall-ui/public/hall-widgets.js +12 -4
- package/modules/hall-ui/public/my-projects.html +78 -0
- package/modules/hall-ui/public/my-projects.js +142 -0
- package/modules/hall-ui/public/scouting.html +45 -0
- package/modules/hall-ui/public/scouting.js +73 -0
- package/modules/hall-ui/public/settings.css +355 -0
- package/modules/hall-ui/public/settings.html +86 -414
- package/modules/hall-ui/public/settings.js +96 -35
- package/modules/hall-ui/public/style.css +31 -0
- package/modules/hall-ui/public/watch.css +184 -0
- package/modules/hall-ui/public/watch.html +111 -285
- package/modules/hall-ui/public/watch.js +59 -21
- package/modules/status-ui/public/explore.html +54 -0
- package/modules/status-ui/public/explore.js +67 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/create.html +292 -19
- package/public-landing/index.html +11 -76
- package/scripts/cron/style_guide_audit.sh +0 -0
- package/scripts/gds/api.js +0 -0
- package/scripts/gds/architect-audit.js +0 -0
- package/scripts/gds/audit-authorship.sh +0 -0
- package/scripts/gds/audit-replay.sh +0 -0
- package/scripts/gds/box.js +0 -0
- package/scripts/gds/capture.js +0 -0
- package/scripts/gds/cli-lib.js +11 -5
- package/scripts/gds/conductor-dispatch.js +0 -0
- package/scripts/gds/coreB-carve-proof.sh +0 -0
- package/scripts/gds/db-backup-nightly.sh +0 -0
- package/scripts/gds/db-backup-offsite.sh +0 -0
- package/scripts/gds/doctor.js +0 -0
- package/scripts/gds/grade-smoke.js +0 -0
- package/scripts/gds/hooks/pre-push.js +0 -0
- package/scripts/gds/install-git-hooks.js +0 -0
- package/scripts/gds/lint-migrations.js +102 -0
- package/scripts/gds/login.js +42 -10
- package/scripts/gds/main-audit.js +0 -0
- package/scripts/gds/mirror-redact.js +5 -0
- package/scripts/gds/oauth-secret.js +0 -0
- package/scripts/gds/onboarding-analyze.js +0 -0
- package/scripts/gds/onboarding-ship.js +0 -0
- package/scripts/gds/package-core.js +263 -4
- package/scripts/gds/paste-token.js +0 -0
- package/scripts/gds/portability-smoke-art.sh +0 -0
- package/scripts/gds/portability-smoke-memory.sh +0 -0
- package/scripts/gds/provision.js +270 -11
- package/scripts/gds/publish-manifest.js +15 -0
- package/scripts/gds/push-main.js +0 -0
- package/scripts/gds/render-ideas.js +0 -0
- package/scripts/gds/run-unit-tests.js +38 -14
- package/scripts/gds/security-baseline-check.sh +0 -0
- package/scripts/gds/session.js +0 -0
- package/scripts/gds/setup.js +41 -0
- package/scripts/gds/ship.js +126 -32
- package/scripts/gds/smoke-batch-claim.sh +0 -0
- package/scripts/gds/smoke-batch-release.sh +0 -0
- package/scripts/gds/smoke-box.sh +0 -0
- package/scripts/gds/smoke-claim-consistency.sh +0 -0
- package/scripts/gds/smoke-dependencies.sh +0 -0
- package/scripts/gds/smoke-write.sh +0 -0
- package/scripts/gds/upgrade.js +147 -5
- package/scripts/hall-preview/server.js +18 -1
- package/scripts/migrate.sh +10 -0
- package/scripts/migrate_cost_ledger.sh +0 -0
- package/scripts/render-diagrams.sh +0 -0
- package/scripts/security/dep-audit.js +0 -0
- package/scripts/setup-builder.js +0 -0
- package/scripts/setup-builder.sh +0 -0
- package/scripts/snapshots/maybe-render-on-deploy.sh +0 -0
- package/src/bongos/auth.js +362 -5
- package/src/bongos/db.js +66 -0
- package/src/bongos/github-repos.js +93 -0
- package/src/bongos/routes/auth.js +93 -11
- package/src/bongos/routes/github.js +90 -0
- package/src/bongos/routes/instance.js +15 -3
- package/src/bongos/routes.js +2 -0
- package/src/branding.js +17 -2
- package/src/instance-config.js +16 -0
- package/src/module-api.js +48 -1
- package/tests/branding.mjs +29 -23
- package/tests/github_repos_helpers.mjs +91 -0
- package/tests/github_token_store.mjs +121 -0
- package/tests/idp_device_exchange.mjs +105 -0
- package/tests/idp_federation.mjs +100 -0
- package/tests/migrations_forward_safe.mjs +86 -0
- package/tests/module_api.mjs +1 -1
- package/tests/package_core.mjs +246 -1
- package/tests/provision.mjs +226 -0
- package/tests/pruned_lockfile_integrity.mjs +79 -0
- package/tests/ship_ci_deploy.mjs +60 -0
- package/tests/upgrade.mjs +254 -5
- package/.gitignore +0 -91
- package/scripts/hall-preview/fixtures/access-requests.json +0 -1
- package/scripts/hall-preview/fixtures/achievements.json +0 -76
- package/scripts/hall-preview/fixtures/analytics__builder.json +0 -251
- package/scripts/hall-preview/fixtures/audit-log.json +0 -1
- package/scripts/hall-preview/fixtures/boxes__cost-ledger.json +0 -1
- package/scripts/hall-preview/fixtures/builders__active-claims.json +0 -1
- package/scripts/hall-preview/fixtures/builders__directory.json +0 -81
- package/scripts/hall-preview/fixtures/builders__roster.json +0 -1
- package/scripts/hall-preview/fixtures/goals.json +0 -502
- package/scripts/hall-preview/fixtures/goals__25.json +0 -96
- package/scripts/hall-preview/fixtures/goals__26.json +0 -67
- package/scripts/hall-preview/fixtures/goals__3.json +0 -103
- package/scripts/hall-preview/fixtures/goals__35.json +0 -49
- package/scripts/hall-preview/fixtures/goals__conflicts.json +0 -154
- package/scripts/hall-preview/fixtures/goals__detail.json +0 -96
- package/scripts/hall-preview/fixtures/goals__inbox.json +0 -5
- package/scripts/hall-preview/fixtures/goals__requests.json +0 -3
- package/scripts/hall-preview/fixtures/grades__by-builder.json +0 -1
- package/scripts/hall-preview/fixtures/inbox.json +0 -330
- package/scripts/hall-preview/fixtures/instance.json +0 -124
- package/scripts/hall-preview/fixtures/leaderboard.json +0 -184
- package/scripts/hall-preview/fixtures/me.json +0 -476
- package/scripts/hall-preview/fixtures/me__recent-ships.json +0 -304
- package/scripts/hall-preview/fixtures/override-requests.json +0 -1
- package/scripts/hall-preview/fixtures/provisioning__fleet.json +0 -1
- package/scripts/hall-preview/fixtures/provisioning__fleet__cost-ledger.json +0 -1
- package/scripts/hall-preview/fixtures/provisioning__instances.json +0 -1
- package/scripts/hall-preview/fixtures/public__analytics__ecosystem.json +0 -516
- package/scripts/hall-preview/fixtures/public__bounty-table.json +0 -37
- package/scripts/hall-preview/fixtures/public__cost-summary.json +0 -742
- package/scripts/hall-preview/fixtures/public__estimation-drift.json +0 -41
- package/scripts/hall-preview/fixtures/public__grades.json +0 -50
- package/scripts/hall-preview/fixtures/public__progress.json +0 -124
- package/scripts/hall-preview/fixtures/public__recent-shipped.json +0 -426
- package/scripts/hall-preview/fixtures/public__repo-health.json +0 -160
- package/scripts/hall-preview/fixtures/public__repo-info.json +0 -5
- package/scripts/hall-preview/fixtures/security__docs.json +0 -1
- package/scripts/hall-preview/fixtures/security__reports.json +0 -1
- package/scripts/hall-preview/fixtures/sessions__search.json +0 -1
- package/scripts/hall-preview/fixtures/tasks.json +0 -2420
- package/scripts/hall-preview/fixtures/tasks__claimable.json +0 -2555
- package/scripts/hall-preview/fixtures/tasks__detail.json +0 -111
- package/scripts/hall-preview/fixtures/tasks__peer-votes__feed.json +0 -330
- package/scripts/hall-preview/fixtures/versions.json +0 -89
|
@@ -0,0 +1,54 @@
|
|
|
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>Explore — featured projects</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* Self-contained so the showcase renders correctly whether or not the host
|
|
9
|
+
injects theme tokens. Warm Cloud Bongos defaults; overridable by the pack. */
|
|
10
|
+
:root {
|
|
11
|
+
--bg: #fff7ed; --card: #ffffff; --ink: #2a1f17; --ink-soft: #6b5d52;
|
|
12
|
+
--rule: #fed7aa; --accent: #ea580c;
|
|
13
|
+
}
|
|
14
|
+
* { box-sizing: border-box; }
|
|
15
|
+
body {
|
|
16
|
+
margin: 0; padding: 2.5rem 1.25rem 4rem;
|
|
17
|
+
background: var(--bg); color: var(--ink);
|
|
18
|
+
font-family: "Quicksand", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
19
|
+
line-height: 1.5;
|
|
20
|
+
}
|
|
21
|
+
.wrap { max-width: 960px; margin: 0 auto; }
|
|
22
|
+
header { text-align: center; margin-bottom: 2.5rem; }
|
|
23
|
+
h1 { font-size: 2rem; margin: 0 0 .35rem; letter-spacing: .01em; }
|
|
24
|
+
.sub { color: var(--ink-soft); margin: 0; }
|
|
25
|
+
.grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
|
|
26
|
+
.card {
|
|
27
|
+
background: var(--card); border: 1px solid var(--rule); border-radius: 14px;
|
|
28
|
+
padding: 1.1rem 1.2rem; text-decoration: none; color: inherit;
|
|
29
|
+
display: flex; flex-direction: column; gap: .4rem; transition: transform .12s ease, box-shadow .12s ease;
|
|
30
|
+
}
|
|
31
|
+
.card:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(234,88,12,.12); }
|
|
32
|
+
.card h2 { font-size: 1.15rem; margin: 0; }
|
|
33
|
+
.card p { margin: 0; color: var(--ink-soft); font-size: .95rem; }
|
|
34
|
+
.card .origin { color: var(--accent); font-size: .82rem; word-break: break-all; margin-top: auto; }
|
|
35
|
+
.card .art { width: 100%; height: 120px; object-fit: cover; border-radius: 10px; margin-bottom: .3rem; }
|
|
36
|
+
.empty, .err { text-align: center; color: var(--ink-soft); padding: 3rem 1rem; }
|
|
37
|
+
@media (prefers-color-scheme: dark) {
|
|
38
|
+
:root { --bg: #1a1410; --card: #241c16; --ink: #f5ece3; --ink-soft: #b9a998; --rule: #4a3728; }
|
|
39
|
+
}
|
|
40
|
+
</style>
|
|
41
|
+
</head>
|
|
42
|
+
<body>
|
|
43
|
+
<div class="wrap">
|
|
44
|
+
<header>
|
|
45
|
+
<h1>Explore</h1>
|
|
46
|
+
<p class="sub">Featured projects built on the platform.</p>
|
|
47
|
+
</header>
|
|
48
|
+
<div id="grid" class="grid" hidden></div>
|
|
49
|
+
<div id="empty" class="empty" hidden>No featured projects yet — check back soon.</div>
|
|
50
|
+
<div id="err" class="err" hidden>Couldn't load projects right now.</div>
|
|
51
|
+
</div>
|
|
52
|
+
<script src="/explore.js"></script>
|
|
53
|
+
</body>
|
|
54
|
+
</html>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// explore.js — the public Explore showcase (ADR 0141 §5, task #1002110). Fetches the
|
|
2
|
+
// hub's featured projects and renders cards. On a NON-hub instance (platform-identity
|
|
3
|
+
// off) the endpoint 404s / returns nothing, so the page shows a graceful empty state.
|
|
4
|
+
//
|
|
5
|
+
// Project fields come from the network, so every value is set via textContent /
|
|
6
|
+
// attribute assignment — never innerHTML — so a project name/tagline can't inject markup.
|
|
7
|
+
(function () {
|
|
8
|
+
'use strict';
|
|
9
|
+
var grid = document.getElementById('grid');
|
|
10
|
+
var empty = document.getElementById('empty');
|
|
11
|
+
var err = document.getElementById('err');
|
|
12
|
+
|
|
13
|
+
function show(el) { if (el) el.hidden = false; }
|
|
14
|
+
|
|
15
|
+
// Only http(s) origins are linkified — never javascript:/data: etc.
|
|
16
|
+
function safeHref(origin) {
|
|
17
|
+
try {
|
|
18
|
+
var u = new URL(String(origin));
|
|
19
|
+
return (u.protocol === 'https:' || u.protocol === 'http:') ? u.toString() : null;
|
|
20
|
+
} catch (_) { return null; }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function card(p) {
|
|
24
|
+
var href = safeHref(p.origin);
|
|
25
|
+
var el = document.createElement(href ? 'a' : 'div');
|
|
26
|
+
el.className = 'card';
|
|
27
|
+
if (href) { el.href = href; el.target = '_blank'; el.rel = 'noopener noreferrer'; }
|
|
28
|
+
|
|
29
|
+
if (p.art_url && safeHref(p.art_url)) {
|
|
30
|
+
var img = document.createElement('img');
|
|
31
|
+
img.className = 'art';
|
|
32
|
+
img.src = p.art_url;
|
|
33
|
+
img.alt = '';
|
|
34
|
+
img.loading = 'lazy';
|
|
35
|
+
el.appendChild(img);
|
|
36
|
+
}
|
|
37
|
+
var h = document.createElement('h2');
|
|
38
|
+
h.textContent = p.name || p.origin || 'Untitled project';
|
|
39
|
+
el.appendChild(h);
|
|
40
|
+
if (p.tagline) {
|
|
41
|
+
var tag = document.createElement('p');
|
|
42
|
+
tag.textContent = p.tagline;
|
|
43
|
+
el.appendChild(tag);
|
|
44
|
+
}
|
|
45
|
+
if (p.origin) {
|
|
46
|
+
var o = document.createElement('span');
|
|
47
|
+
o.className = 'origin';
|
|
48
|
+
o.textContent = String(p.origin).replace(/^https?:\/\//, '');
|
|
49
|
+
el.appendChild(o);
|
|
50
|
+
}
|
|
51
|
+
return el;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fetch('/api/gds/projects/featured', { headers: { Accept: 'application/json' } })
|
|
55
|
+
.then(function (r) { if (!r.ok) throw new Error('http ' + r.status); return r.json(); })
|
|
56
|
+
.then(function (data) {
|
|
57
|
+
var projects = (data && Array.isArray(data.projects)) ? data.projects : [];
|
|
58
|
+
if (projects.length === 0) { show(empty); return; }
|
|
59
|
+
projects.forEach(function (p) { grid.appendChild(card(p)); });
|
|
60
|
+
show(grid);
|
|
61
|
+
})
|
|
62
|
+
.catch(function () {
|
|
63
|
+
// A missing endpoint (non-hub instance) is not an error to the visitor — show
|
|
64
|
+
// the empty state rather than a scary failure.
|
|
65
|
+
show(empty);
|
|
66
|
+
});
|
|
67
|
+
}());
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.18.3",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.3",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
|
@@ -29,6 +29,13 @@
|
|
|
29
29
|
--font-mono:"Fira Code",ui-monospace,SFMono-Regular,Menlo,monospace;
|
|
30
30
|
}
|
|
31
31
|
*{box-sizing:border-box;margin:0;padding:0;}
|
|
32
|
+
/* The `hidden` attribute must always win. Several blocks below set an explicit
|
|
33
|
+
`display` via a class (.field→block, .row2→grid); an author rule like that
|
|
34
|
+
silently defeats the user-agent [hidden] rule, so `el.hidden = true` fails to
|
|
35
|
+
hide them. The Home step (task 1002228) toggles el.hidden to switch between the
|
|
36
|
+
adopt-picker / fresh-name / manual sub-panels — without this they ALL render at
|
|
37
|
+
once. Make hidden authoritative for the whole page. */
|
|
38
|
+
[hidden]{display:none !important;}
|
|
32
39
|
html{scroll-behavior:smooth;}
|
|
33
40
|
body{
|
|
34
41
|
font-family:var(--font-body); color:var(--ink);
|
|
@@ -182,6 +189,25 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
182
189
|
}
|
|
183
190
|
.reassure.on{display:block;}
|
|
184
191
|
|
|
192
|
+
/* one-click deep link to GitHub's own "New repository" page with the name
|
|
193
|
+
pre-filled (greenfield only). No permissions — the visitor creates the empty
|
|
194
|
+
repo in GitHub's own UI; we just fill in the blanks. */
|
|
195
|
+
.ghbtn{
|
|
196
|
+
display:inline-flex;align-items:center;gap:9px;text-decoration:none;cursor:pointer;
|
|
197
|
+
background:#24292f;color:#fff;border:none;border-radius:999px;
|
|
198
|
+
font-family:var(--font-body);font-weight:700;font-size:15px;padding:12px 22px;
|
|
199
|
+
box-shadow:0 12px 26px -12px rgba(36,41,47,0.55);
|
|
200
|
+
transition:transform .12s ease, background .12s ease;
|
|
201
|
+
}
|
|
202
|
+
.ghbtn:hover{background:#1b1f24;transform:translateY(-1px);}
|
|
203
|
+
.ghbtn:active{transform:translateY(0) scale(.98);}
|
|
204
|
+
.ghbtn .gh{width:18px;height:18px;fill:currentColor;}
|
|
205
|
+
.ghcreate{display:none;margin-bottom:18px;}
|
|
206
|
+
.ghcreate.on{display:block;}
|
|
207
|
+
.ghcreate-note{font-size:13px;font-weight:500;color:var(--ink-faint);line-height:1.5;margin-top:9px;max-width:52ch;}
|
|
208
|
+
.ghcreate-note strong{color:var(--ink-soft);}
|
|
209
|
+
.steps .ghbtn{font-size:14px;padding:9px 16px;margin-top:10px;}
|
|
210
|
+
|
|
185
211
|
/* buttons */
|
|
186
212
|
.nav{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:26px;}
|
|
187
213
|
.btn{
|
|
@@ -308,6 +334,16 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
308
334
|
@media (prefers-reduced-motion:reduce){
|
|
309
335
|
*,*::before,*::after{animation:none !important;transition:none !important;}
|
|
310
336
|
}
|
|
337
|
+
/* GitHub repo picker (task 1002099) */
|
|
338
|
+
.linklike{background:none;border:0;padding:0;margin-left:12px;color:var(--accent-deep);font:inherit;font-size:13px;cursor:pointer;text-decoration:underline;}
|
|
339
|
+
#ghConnect .btn{margin-top:4px;}
|
|
340
|
+
.ghlist{margin-top:8px;max-height:220px;overflow-y:auto;border:1px solid var(--line,#e7d8c4);border-radius:10px;background:var(--bg-deep,#fff);}
|
|
341
|
+
.ghlist:empty{display:none;}
|
|
342
|
+
.ghrow{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:none;border:0;border-bottom:1px solid rgba(42,31,23,0.06);padding:9px 12px;font:inherit;font-size:13.5px;color:var(--ink,#2a1f17);cursor:pointer;}
|
|
343
|
+
.ghrow:last-child{border-bottom:0;}
|
|
344
|
+
.ghrow:hover{background:#fff9f2;}
|
|
345
|
+
.ghrow[aria-selected=true]{background:#fff3e6;box-shadow:inset 3px 0 0 var(--accent,#f97316);font-weight:600;}
|
|
346
|
+
.ghrow .priv{margin-left:auto;font-size:11px;font-weight:600;color:var(--ink-faint,#9b8a78);background:rgba(42,31,23,0.06);border-radius:5px;padding:1px 6px;}
|
|
311
347
|
</style>
|
|
312
348
|
</head>
|
|
313
349
|
<body>
|
|
@@ -386,7 +422,31 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
386
422
|
</button>
|
|
387
423
|
</div>
|
|
388
424
|
<div class="reassure" id="adoptNote">Adopting never overwrites your files: you get a conflict pre-flight first, nothing is written until you accept, and your existing backlog (issues, TODOs) is imported as real tasks.</div>
|
|
389
|
-
|
|
425
|
+
|
|
426
|
+
<!-- GitHub-integrated repo picker (adopt) + auto-create (fresh) — task 1002099.
|
|
427
|
+
Progressive enhancement: when the signed-in user grants repo access we let
|
|
428
|
+
them PICK a repo (adopt) or NAME one we auto-create (fresh). Without that
|
|
429
|
+
access — not granted yet, or the server can't store the token — we fall back
|
|
430
|
+
to the manual owner/repo inputs below (#manualHome), which stay authoritative. -->
|
|
431
|
+
<div id="ghConnect" class="reassure">
|
|
432
|
+
<p id="ghConnectMsg"> </p>
|
|
433
|
+
<button type="button" class="btn" id="ghConnectBtn">Connect GitHub</button>
|
|
434
|
+
<button type="button" class="linklike" id="ghManualToggle">or enter it manually</button>
|
|
435
|
+
</div>
|
|
436
|
+
<div id="ghBusy" class="hint" hidden>Reading your GitHub repositories…</div>
|
|
437
|
+
<div id="ghAdopt" class="field" hidden>
|
|
438
|
+
<span>Pick one of your repositories</span>
|
|
439
|
+
<input type="text" id="ghSearch" placeholder="Search your repos…" autocomplete="off">
|
|
440
|
+
<div id="ghList" class="ghlist" role="listbox" aria-label="Your repositories"></div>
|
|
441
|
+
<div class="hint" id="ghAdoptHint"> </div>
|
|
442
|
+
</div>
|
|
443
|
+
<div id="ghFresh" class="field" hidden>
|
|
444
|
+
<span>New repository name</span>
|
|
445
|
+
<input type="text" id="ghNewName" placeholder="mercury" autocomplete="off" maxlength="100">
|
|
446
|
+
<div class="hint" id="ghFreshHint">We’ll create an empty repo at <code id="ghFreshPreview" style="font-family:var(--font-mono)">github.com/you/mercury</code>.</div>
|
|
447
|
+
</div>
|
|
448
|
+
|
|
449
|
+
<div class="row2" id="manualHome">
|
|
390
450
|
<label class="field"><span>GitHub owner <small>(user or org)</small></span>
|
|
391
451
|
<input type="text" id="f-owner" placeholder="acme" autocomplete="off" maxlength="60">
|
|
392
452
|
</label>
|
|
@@ -394,6 +454,13 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
394
454
|
<input type="text" id="f-repo" placeholder="mercury" autocomplete="off" maxlength="120">
|
|
395
455
|
</label>
|
|
396
456
|
</div>
|
|
457
|
+
<div class="ghcreate" id="ghCreate">
|
|
458
|
+
<a class="ghbtn" id="ghCreateBtn" href="https://github.com/new" target="_blank" rel="noopener">
|
|
459
|
+
<svg class="gh" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
|
|
460
|
+
Create this repo on GitHub
|
|
461
|
+
</a>
|
|
462
|
+
<p class="ghcreate-note">Opens GitHub with the name already filled in — just click <strong>Create repository</strong> and leave it empty, then come back and press Next. Already made it? Just press Next.</p>
|
|
463
|
+
</div>
|
|
397
464
|
<div class="hint" id="h-home"> </div>
|
|
398
465
|
<div class="nav">
|
|
399
466
|
<button type="button" class="btn ghost" data-back="1">← Back</button>
|
|
@@ -470,7 +537,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
470
537
|
/* ── state ── */
|
|
471
538
|
var DRAFT_KEY = 'cb-create-draft';
|
|
472
539
|
var CREATED_KEY = 'cb-created-instance';
|
|
473
|
-
var state = { step: 0, name: '', mode: 'greenfield', owner: '', repo: '', slug: '', slugTouched: false, domain: '' };
|
|
540
|
+
var state = { step: 0, name: '', mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '' };
|
|
474
541
|
try { Object.assign(state, JSON.parse(sessionStorage.getItem(DRAFT_KEY) || '{}')); } catch (e) {}
|
|
475
542
|
var me = null;
|
|
476
543
|
|
|
@@ -483,6 +550,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
483
550
|
if (handedName && handedName.trim()) {
|
|
484
551
|
state.name = handedName.trim().slice(0, 80);
|
|
485
552
|
state.slug = deriveSlug(state.name); state.slugTouched = false; // (hoisted declaration)
|
|
553
|
+
state.repo = deriveRepo(state.name); state.repoTouched = false; // auto-repo from the name
|
|
486
554
|
state.step = state.step >= 1 && state.step <= 4 ? state.step : 1;
|
|
487
555
|
try { sessionStorage.setItem(DRAFT_KEY, JSON.stringify(state)); } catch (e) {}
|
|
488
556
|
history.replaceState(null, '', location.pathname);
|
|
@@ -507,14 +575,19 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
507
575
|
var v = String(s || '');
|
|
508
576
|
return v.length >= 2 && v.length <= 40 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(v) && v.indexOf('--') === -1;
|
|
509
577
|
}
|
|
510
|
-
function
|
|
578
|
+
function slugify(name, maxLen) {
|
|
511
579
|
return String(name || '').toLowerCase()
|
|
512
580
|
.replace(/['’]/g, '')
|
|
513
581
|
.replace(/[^a-z0-9]+/g, '-')
|
|
514
582
|
.replace(/-+/g, '-')
|
|
515
583
|
.replace(/^-|-$/g, '')
|
|
516
|
-
.slice(0,
|
|
584
|
+
.slice(0, maxLen);
|
|
517
585
|
}
|
|
586
|
+
function deriveSlug(name) { return slugify(name, 40); }
|
|
587
|
+
/* Repo-name default from the project name — the same slug-safe transform (GitHub
|
|
588
|
+
repo names accept [A-Za-z0-9._-], so a lowercase-dashed name is always valid and
|
|
589
|
+
matches the placeholder). Wider cap than the handle; the visitor can still edit. */
|
|
590
|
+
function deriveRepo(name) { return slugify(name, 100); }
|
|
518
591
|
function looksLikeDomain(d) {
|
|
519
592
|
return /^(?!-)[a-z0-9-]{1,63}(?<!-)(\.(?!-)[a-z0-9-]{1,63}(?<!-))+$/i.test(String(d || ''));
|
|
520
593
|
}
|
|
@@ -524,6 +597,20 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
524
597
|
var origin = 'https://' + domain;
|
|
525
598
|
return { homepageUrl: origin, callbackUrl: origin + '/api/gds/auth/web/callback' };
|
|
526
599
|
}
|
|
600
|
+
/* GitHub's own "New repository" page, pre-filled with the owner + name. No auth or
|
|
601
|
+
permissions on our side — the visitor creates the empty repo in GitHub's UI, which
|
|
602
|
+
is exactly what the scaffold then clones. `owner` selects the account/org; omitting
|
|
603
|
+
a blank param just leaves that field empty on GitHub. */
|
|
604
|
+
function githubNewRepoUrl(owner, repo) {
|
|
605
|
+
var u = new URL('https://github.com/new');
|
|
606
|
+
if (repo) u.searchParams.set('name', repo);
|
|
607
|
+
if (owner) u.searchParams.set('owner', owner);
|
|
608
|
+
return u.toString();
|
|
609
|
+
}
|
|
610
|
+
function updateGhCreateHref() {
|
|
611
|
+
var a = $('ghCreateBtn');
|
|
612
|
+
if (a) a.href = githubNewRepoUrl(state.owner, state.repo);
|
|
613
|
+
}
|
|
527
614
|
function esc(s) {
|
|
528
615
|
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
|
529
616
|
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
|
@@ -549,6 +636,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
549
636
|
saveDraft();
|
|
550
637
|
var h = $(panels[step]).querySelector('h1');
|
|
551
638
|
if (h) { h.setAttribute('tabindex', '-1'); h.focus({ preventScroll: false }); }
|
|
639
|
+
if (step === 2) { syncHome(); ensureRepoAccess(); }
|
|
552
640
|
if (step === 4) renderReview();
|
|
553
641
|
}
|
|
554
642
|
|
|
@@ -559,12 +647,25 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
559
647
|
$('f-slug').value = state.slug;
|
|
560
648
|
$('f-domain').value = state.domain;
|
|
561
649
|
|
|
650
|
+
/* Auto-fill the repo name from the project name until the visitor edits it
|
|
651
|
+
directly (mirrors the handle's auto-derive). The owner is filled once we know
|
|
652
|
+
who's signed in — see the /me boot below. */
|
|
653
|
+
if (!state.repoTouched && state.name && !state.repo) {
|
|
654
|
+
state.repo = deriveRepo(state.name);
|
|
655
|
+
$('f-repo').value = state.repo;
|
|
656
|
+
}
|
|
657
|
+
|
|
562
658
|
$('f-name').addEventListener('input', function () {
|
|
563
659
|
state.name = this.value;
|
|
564
660
|
if (!state.slugTouched) {
|
|
565
661
|
state.slug = deriveSlug(state.name);
|
|
566
662
|
$('f-slug').value = state.slug;
|
|
567
663
|
}
|
|
664
|
+
if (!state.repoTouched) {
|
|
665
|
+
state.repo = deriveRepo(state.name);
|
|
666
|
+
$('f-repo').value = state.repo;
|
|
667
|
+
updateGhCreateHref();
|
|
668
|
+
}
|
|
568
669
|
var h = $('h-name');
|
|
569
670
|
if (state.slug) { h.innerHTML = 'its handle on the platform: <code>' + esc(state.slug) + '</code>'; h.className = 'hint'; }
|
|
570
671
|
else { h.innerHTML = ' '; }
|
|
@@ -577,14 +678,115 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
577
678
|
$('c-green').setAttribute('aria-pressed', String(mode === 'greenfield'));
|
|
578
679
|
$('c-adopt').setAttribute('aria-pressed', String(mode === 'adopt'));
|
|
579
680
|
$('adoptNote').classList.toggle('on', mode === 'adopt');
|
|
681
|
+
// The "create the repo on GitHub" button only makes sense when starting fresh —
|
|
682
|
+
// an adopted repo already exists.
|
|
683
|
+
$('ghCreate').classList.toggle('on', mode === 'greenfield');
|
|
684
|
+
updateGhCreateHref();
|
|
580
685
|
saveDraft();
|
|
686
|
+
syncHome();
|
|
581
687
|
}
|
|
582
688
|
$('c-green').addEventListener('click', function () { setMode('greenfield'); });
|
|
583
689
|
$('c-adopt').addEventListener('click', function () { setMode('adopt'); });
|
|
584
690
|
setMode(state.mode);
|
|
585
691
|
|
|
586
|
-
|
|
587
|
-
|
|
692
|
+
/* ── GitHub-integrated repo picker (adopt) + auto-create (fresh) — task 1002099 / ADR 0143.
|
|
693
|
+
Progressive enhancement: probe GET /github/repos once we reach step 2.
|
|
694
|
+
200 → we hold repo access → show the picker (adopt) / name field (fresh);
|
|
695
|
+
403 → offer "Connect GitHub" (a ?repos=1 re-sign-in);
|
|
696
|
+
503 (server can't store the token) or any error → the manual inputs.
|
|
697
|
+
#manualHome (the classic owner/repo fields) is the always-safe fallback. ── */
|
|
698
|
+
var repoAccess = null; // null=unknown/loading · true · 'need-connect' · 'unavailable' · 'manual'
|
|
699
|
+
var repoList = []; // the user's repos (from the probe) — feeds the picker + the fresh-name clash hint
|
|
700
|
+
var ghProbed = false;
|
|
701
|
+
|
|
702
|
+
function isManualHome() { return repoAccess === 'manual' || repoAccess === 'unavailable'; }
|
|
703
|
+
function validRepoName(s) { return /^[A-Za-z0-9._-]{1,100}$/.test(String(s || '')); }
|
|
704
|
+
function myLogin() { return (me && me.github_login) || 'you'; }
|
|
705
|
+
|
|
706
|
+
function connectRepos(e) {
|
|
707
|
+
if (e) e.preventDefault();
|
|
708
|
+
// Same-tab navigation so the sessionStorage draft round-trips back to step 2.
|
|
709
|
+
window.location.assign(API + '/auth/web/start?return=/create&repos=1');
|
|
710
|
+
}
|
|
711
|
+
$('ghConnectBtn').addEventListener('click', connectRepos);
|
|
712
|
+
$('ghManualToggle').addEventListener('click', function () { repoAccess = 'manual'; syncHome(); $('f-owner').focus(); });
|
|
713
|
+
|
|
714
|
+
function syncHome() {
|
|
715
|
+
var need = repoAccess === 'need-connect';
|
|
716
|
+
var ok = repoAccess === true;
|
|
717
|
+
var adopt = state.mode === 'adopt';
|
|
718
|
+
$('ghBusy').hidden = !!repoAccess; // busy only while still probing (null)
|
|
719
|
+
$('ghConnect').classList.toggle('on', need); // .reassure box: shown via .on (not hidden — .reassure sets display:none)
|
|
720
|
+
$('ghAdopt').hidden = !(ok && adopt);
|
|
721
|
+
$('ghFresh').hidden = !(ok && !adopt);
|
|
722
|
+
$('manualHome').hidden = !isManualHome();
|
|
723
|
+
if (need) {
|
|
724
|
+
$('ghConnectMsg').textContent = adopt
|
|
725
|
+
? 'Connect your GitHub account and pick the repository to adopt.'
|
|
726
|
+
: 'Connect your GitHub account and we’ll create the empty repo for you.';
|
|
727
|
+
}
|
|
728
|
+
if (ok && adopt) renderRepoList();
|
|
729
|
+
if (ok && !adopt) syncFresh();
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function syncFresh() {
|
|
733
|
+
// Fresh + access: the repo is created under the signed-in account. Seed owner for
|
|
734
|
+
// the review/target now; the create response confirms the real full_name.
|
|
735
|
+
state.owner = myLogin();
|
|
736
|
+
$('ghNewName').value = state.repo || '';
|
|
737
|
+
updateFreshPreview();
|
|
738
|
+
}
|
|
739
|
+
function updateFreshPreview() {
|
|
740
|
+
var hint = $('ghFreshHint');
|
|
741
|
+
var clash = state.repo && repoList.some(function (r) { return String(r.name).toLowerCase() === state.repo.toLowerCase(); });
|
|
742
|
+
if (state.repo && !validRepoName(state.repo)) {
|
|
743
|
+
hint.className = 'hint err'; hint.textContent = 'Use letters, numbers, and . _ - (1–100 chars).';
|
|
744
|
+
} else if (clash) {
|
|
745
|
+
hint.className = 'hint err'; hint.innerHTML = 'You already have a repo named <strong>' + esc(state.repo) + '</strong> — pick another name, or adopt it instead.';
|
|
746
|
+
} else {
|
|
747
|
+
hint.className = 'hint'; hint.innerHTML = 'We’ll create an empty repo at <code style="font-family:var(--font-mono)">github.com/' + esc(myLogin()) + '/' + esc(state.repo || 'name') + '</code>.';
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
$('ghNewName').addEventListener('input', function () { state.repo = this.value.trim(); updateFreshPreview(); saveDraft(); });
|
|
751
|
+
|
|
752
|
+
function renderRepoList() {
|
|
753
|
+
var q = ($('ghSearch').value || '').trim().toLowerCase();
|
|
754
|
+
var rows = repoList.filter(function (r) { return !q || String(r.full_name).toLowerCase().indexOf(q) >= 0; }).slice(0, 100);
|
|
755
|
+
var sel = state.owner && state.repo ? (state.owner + '/' + state.repo) : '';
|
|
756
|
+
$('ghList').innerHTML = rows.length
|
|
757
|
+
? rows.map(function (r) {
|
|
758
|
+
return '<button type="button" class="ghrow" role="option" data-full="' + esc(r.full_name) + '" aria-selected="' + (r.full_name === sel) + '">' +
|
|
759
|
+
'<span>' + esc(r.full_name) + '</span>' + (r.private ? '<span class="priv">Private</span>' : '') + '</button>';
|
|
760
|
+
}).join('')
|
|
761
|
+
: '<div class="hint" style="padding:10px 12px">No repositories match “' + esc(q) + '”.</div>';
|
|
762
|
+
}
|
|
763
|
+
$('ghSearch').addEventListener('input', renderRepoList);
|
|
764
|
+
$('ghList').addEventListener('click', function (ev) {
|
|
765
|
+
var row = ev.target.closest('.ghrow'); if (!row) return;
|
|
766
|
+
var full = row.getAttribute('data-full'); var slash = full.indexOf('/');
|
|
767
|
+
state.owner = full.slice(0, slash); state.repo = full.slice(slash + 1);
|
|
768
|
+
saveDraft();
|
|
769
|
+
Array.prototype.forEach.call($('ghList').querySelectorAll('.ghrow'), function (b) { b.setAttribute('aria-selected', String(b === row)); });
|
|
770
|
+
var h = $('ghAdoptHint'); h.className = 'hint ok'; h.innerHTML = 'Adopting <strong>' + esc(full) + '</strong>.';
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
function ensureRepoAccess() {
|
|
774
|
+
if (ghProbed && repoAccess !== null) return; // already known this session
|
|
775
|
+
if (isManualHome()) return; // user chose manual — don't nag
|
|
776
|
+
repoAccess = null; syncHome(); // show "reading your repos…"
|
|
777
|
+
fetch(API + '/github/repos', { credentials: 'same-origin' })
|
|
778
|
+
.then(function (res) {
|
|
779
|
+
ghProbed = true;
|
|
780
|
+
if (res.status === 200) return res.json().then(function (d) { repoList = (d && d.repos) || []; repoAccess = true; });
|
|
781
|
+
if (res.status === 403 || res.status === 401) { repoAccess = 'need-connect'; return; }
|
|
782
|
+
repoAccess = 'unavailable'; return; // 503 or anything else → manual fallback
|
|
783
|
+
})
|
|
784
|
+
.catch(function () { ghProbed = true; repoAccess = 'unavailable'; })
|
|
785
|
+
.then(syncHome);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
$('f-owner').addEventListener('input', function () { state.owner = this.value.trim(); state.ownerTouched = true; updateGhCreateHref(); saveDraft(); });
|
|
789
|
+
$('f-repo').addEventListener('input', function () { state.repo = this.value.trim(); state.repoTouched = true; updateGhCreateHref(); saveDraft(); });
|
|
588
790
|
|
|
589
791
|
$('f-slug').addEventListener('input', function () {
|
|
590
792
|
state.slugTouched = true;
|
|
@@ -612,7 +814,17 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
612
814
|
return true;
|
|
613
815
|
}
|
|
614
816
|
if (step === 2) {
|
|
615
|
-
|
|
817
|
+
var homeErr = function (msg) { $('h-home').textContent = msg; $('h-home').className = 'hint err'; return false; };
|
|
818
|
+
if (repoAccess === 'need-connect') return homeErr('Connect GitHub to pick or create your repository — or choose “enter it manually”.');
|
|
819
|
+
if (repoAccess === true && state.mode !== 'adopt') {
|
|
820
|
+
// Fresh with access: just the new repo NAME (owner is the signed-in account).
|
|
821
|
+
if (!validRepoName(state.repo)) return homeErr('Name the new repository (letters, numbers, and . _ -).');
|
|
822
|
+
} else {
|
|
823
|
+
// Adopt (picker or manual) and the manual fallback: need a concrete owner + repo.
|
|
824
|
+
if (!state.owner || !state.repo) {
|
|
825
|
+
return homeErr(state.mode === 'adopt' && repoAccess === true ? 'Pick one of your repositories.' : 'Both owner and repository name are needed.');
|
|
826
|
+
}
|
|
827
|
+
}
|
|
616
828
|
$('h-home').innerHTML = ' '; $('h-home').className = 'hint';
|
|
617
829
|
return true;
|
|
618
830
|
}
|
|
@@ -662,20 +874,58 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
662
874
|
return 'Couldn’t file the request: ' + (err && err.message ? err.message : 'something went sideways. Try again.');
|
|
663
875
|
}
|
|
664
876
|
|
|
877
|
+
/* Fresh + repo access: auto-create the empty GitHub repo NOW (the commit moment),
|
|
878
|
+
then hand its real full_name to provisioning. No-op for adopt / manual / no-access.
|
|
879
|
+
Idempotent across a retry — once created we reuse the stashed full_name so a second
|
|
880
|
+
click doesn't trip GitHub's 422 "name already exists". Errors set err.gotoHome so
|
|
881
|
+
the caller bounces the user back to the Home step to fix the name / reconnect. */
|
|
882
|
+
function ensureFreshRepo() {
|
|
883
|
+
if (state.mode === 'adopt' || repoAccess !== true) return Promise.resolve();
|
|
884
|
+
if (state.repoAutoCreated && state.owner && state.repo) return Promise.resolve();
|
|
885
|
+
return fetch(API + '/github/repos', {
|
|
886
|
+
method: 'POST', credentials: 'same-origin',
|
|
887
|
+
headers: { 'Content-Type': 'application/json' },
|
|
888
|
+
body: JSON.stringify({ name: state.repo })
|
|
889
|
+
}).then(function (res) {
|
|
890
|
+
return res.json().catch(function () { return {}; }).then(function (data) {
|
|
891
|
+
if (res.status === 401 || res.status === 403) {
|
|
892
|
+
repoAccess = 'need-connect';
|
|
893
|
+
var e0 = new Error('reconnect'); e0.gotoHome = true;
|
|
894
|
+
e0.homeMsg = 'Your GitHub authorization expired — reconnect on the Home step to create the repo.';
|
|
895
|
+
throw e0;
|
|
896
|
+
}
|
|
897
|
+
if (!res.ok) {
|
|
898
|
+
var e = new Error((data.error && (data.error.message || data.error.code)) || ('HTTP ' + res.status));
|
|
899
|
+
e.data = data;
|
|
900
|
+
if (data.error && data.error.code === 'name_taken') { e.gotoHome = true; e.homeMsg = data.error.message; }
|
|
901
|
+
throw e;
|
|
902
|
+
}
|
|
903
|
+
var full = data.repo && data.repo.full_name;
|
|
904
|
+
if (full && full.indexOf('/') > 0) {
|
|
905
|
+
state.owner = full.slice(0, full.indexOf('/'));
|
|
906
|
+
state.repo = full.slice(full.indexOf('/') + 1);
|
|
907
|
+
}
|
|
908
|
+
state.repoAutoCreated = true; saveDraft();
|
|
909
|
+
});
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
|
|
665
913
|
$('doCreate').addEventListener('click', function () {
|
|
666
914
|
var btn = this;
|
|
667
915
|
var alertBox = $('createAlert');
|
|
668
916
|
alertBox.classList.remove('on');
|
|
669
917
|
btn.disabled = true; btn.textContent = 'Raising it into the sky…';
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
918
|
+
ensureFreshRepo().then(function () {
|
|
919
|
+
return fetch(API + '/provisioning/instances', {
|
|
920
|
+
method: 'POST', credentials: 'same-origin',
|
|
921
|
+
headers: { 'Content-Type': 'application/json' },
|
|
922
|
+
body: JSON.stringify({
|
|
923
|
+
slug: state.slug,
|
|
924
|
+
hosting_shape: 'standalone',
|
|
925
|
+
domain: state.domain || undefined,
|
|
926
|
+
target_ref: state.owner + '/' + state.repo
|
|
927
|
+
})
|
|
928
|
+
});
|
|
679
929
|
}).then(function (res) {
|
|
680
930
|
if (res.status === 401) { window.location.assign(API + '/auth/web/start?return=/create'); throw new Error('signing in'); }
|
|
681
931
|
return res.json().catch(function () { return {}; }).then(function (data) {
|
|
@@ -698,9 +948,17 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
698
948
|
startProgress(data.instance && data.instance.id);
|
|
699
949
|
}).catch(function (err) {
|
|
700
950
|
if (String(err && err.message) === 'signing in') return;
|
|
951
|
+
btn.disabled = false; btn.textContent = 'Create my project';
|
|
952
|
+
if (err && err.gotoHome) {
|
|
953
|
+
// A repo-creation problem (name taken / reconnect) — bounce back to the Home
|
|
954
|
+
// step with the reason, so the user fixes it where the repo is chosen.
|
|
955
|
+
show(2);
|
|
956
|
+
$('h-home').textContent = err.homeMsg || 'Please revisit the repository step.';
|
|
957
|
+
$('h-home').className = 'hint err';
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
701
960
|
alertBox.textContent = friendlyCreateError(err);
|
|
702
961
|
alertBox.classList.add('on');
|
|
703
|
-
btn.disabled = false; btn.textContent = 'Create my project';
|
|
704
962
|
});
|
|
705
963
|
});
|
|
706
964
|
|
|
@@ -719,9 +977,14 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
719
977
|
|
|
720
978
|
var steps = [];
|
|
721
979
|
var repoLabel = state.owner && state.repo ? '<strong>' + esc(state.owner + '/' + state.repo) + '</strong>' : 'your new repository';
|
|
980
|
+
var ghNewUrl = githubNewRepoUrl(state.owner, state.repo);
|
|
981
|
+
var ghIcon = '<svg class="gh" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>';
|
|
722
982
|
steps.push(adopt
|
|
723
983
|
? '<li data-onboard-step="github-repo"><b>Layer onto your existing repo</b><p>In your repo run <code style="font-family:var(--font-mono)">bongos init --adopt</code>. It shows a conflict pre-flight and writes <em>nothing</em> until you accept — your files and history stay yours, and your existing backlog is imported. Commit and push.</p></li>'
|
|
724
|
-
:
|
|
984
|
+
: (state.repoAutoCreated
|
|
985
|
+
? '<li data-onboard-step="github-repo"><b>Your empty repo is ready</b><p>We created ' + repoLabel + ' on GitHub for you — empty and waiting. The scaffold lands there when the build runs; nothing for you to do here.</p></li>'
|
|
986
|
+
: '<li data-onboard-step="github-repo"><b>Create the empty GitHub repo</b><p>Do this first — the build needs the repo to exist. Click below (GitHub opens with the name already filled in), press <strong>Create repository</strong>, and leave it completely empty. Seeing “repository not found” above? That just means the repo isn’t created yet — make it here.</p>' +
|
|
987
|
+
'<a class="ghbtn" href="' + esc(ghNewUrl) + '" target="_blank" rel="noopener">' + ghIcon + 'Create ' + repoLabel + ' on GitHub</a></li>'));
|
|
725
988
|
if (!state.domain) {
|
|
726
989
|
steps.push('<li><b>Add a domain you own</b><p>Sign-in on your new instance needs a real domain. When you have one, come back here or ask your hall — the address gets wired in minutes.</p></li>');
|
|
727
990
|
}
|
|
@@ -829,8 +1092,10 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
829
1092
|
again.addEventListener('click', function () {
|
|
830
1093
|
stopPolling();
|
|
831
1094
|
try { sessionStorage.removeItem(CREATED_KEY); sessionStorage.removeItem(DRAFT_KEY); } catch (e) {}
|
|
832
|
-
state = { step: 1, name: '', mode: 'greenfield', owner: '', repo: '', slug: '', slugTouched: false, domain: '' };
|
|
1095
|
+
state = { step: 1, name: '', mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '' };
|
|
833
1096
|
$('f-name').value = ''; $('f-owner').value = ''; $('f-repo').value = ''; $('f-slug').value = ''; $('f-domain').value = '';
|
|
1097
|
+
// Re-seed the owner from the signed-in account for the next project.
|
|
1098
|
+
if (me && me.github_login) { state.owner = me.github_login; $('f-owner').value = state.owner; }
|
|
834
1099
|
$('h-name').innerHTML = ' ';
|
|
835
1100
|
setMode('greenfield');
|
|
836
1101
|
show(1);
|
|
@@ -884,6 +1149,14 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
884
1149
|
.then(function (data) {
|
|
885
1150
|
me = data.builder || data.me || data;
|
|
886
1151
|
renderWho();
|
|
1152
|
+
// Default the GitHub owner to the signed-in account until the visitor edits
|
|
1153
|
+
// it — one less field to fill for the common "my own account" case.
|
|
1154
|
+
if (!state.ownerTouched && !state.owner && me && me.github_login) {
|
|
1155
|
+
state.owner = me.github_login;
|
|
1156
|
+
$('f-owner').value = state.owner;
|
|
1157
|
+
updateGhCreateHref();
|
|
1158
|
+
saveDraft();
|
|
1159
|
+
}
|
|
887
1160
|
// returning after a create? resume watching that instance instead of a blank wizard
|
|
888
1161
|
var created = null;
|
|
889
1162
|
try { created = JSON.parse(sessionStorage.getItem(CREATED_KEY) || 'null'); } catch (e) {}
|