Skip to content

Commit c3368fc

Browse files
committed
x
1 parent 1771c5b commit c3368fc

6 files changed

Lines changed: 132 additions & 105 deletions

File tree

USER_GUIDE.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,11 @@ Desktop keeps advanced Cloud workflows in the Cloud web app, including multiple
178178

179179
### GitHub-Linked Courses
180180

181-
If a course is linked to a GitHub repository, production deploys are managed by GitHub. Push to the repo to update Production.
181+
If a course is linked to a GitHub repository, deploys are managed by GitHub. Push to the repo to update the hosted course.
182182

183-
Desktop still supports:
184-
- preview-only deploys
185-
- Preview pointer changes
186-
- main preview link password/expiry management
183+
Desktop shows Cloud status for GitHub-linked courses, but deploys, Preview pointer changes, and preview-link changes stay in the GitHub/Cloud workflow.
187184

188-
Desktop disables Production pointer changes for GitHub-linked courses to avoid conflicting with the repository workflow.
185+
Desktop disables direct deploy and pointer-change actions for GitHub-linked courses to avoid conflicting with the repository workflow.
189186

190187
Cloud features should always be labeled optional in Desktop UI/docs.
191188

renderer/src/lib/cloud-status.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export function isGithubLinkedStatus(status) {
2+
if (!status) return false;
3+
4+
return (
5+
status.source?.type === 'github' ||
6+
status.source_type === 'github' ||
7+
status.sourceType === 'github' ||
8+
!!status.github_repo ||
9+
!!status.githubRepo ||
10+
!!status.source?.githubRepo ||
11+
status.source?.directProductionDeployAllowed === false
12+
);
13+
}

renderer/src/views/Dashboard.svelte

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import { popover } from '../actions/popover.js';
1515
import { getDisplayErrorMessage } from '../lib/errors.js';
1616
import { generatePreviewPassword } from '../lib/preview-password.js';
17+
import { isGithubLinkedStatus } from '../lib/cloud-status.js';
1718
1819
let { onCreateNew, onOpenProject, onCloseProject } = $props();
1920
@@ -184,7 +185,7 @@
184185
}
185186
}
186187
// Detect GitHub-linked courses from the status response
187-
const isGithub = status?.source?.type === 'github' || status?.source_type === 'github';
188+
const isGithub = isGithubLinkedStatus(status);
188189
const project = linkedProjects.find((candidate) => candidate.path === path);
189190
if (project && project.githubLinked !== isGithub) {
190191
updateProject(path, { githubLinked: isGithub || undefined });
@@ -699,12 +700,16 @@
699700
async function confirmRepairDeploy() {
700701
if (!staleBindingPrompt) return;
701702
const { project } = staleBindingPrompt;
703+
if (project.githubLinked) {
704+
await clearStaleBindingOnly();
705+
return;
706+
}
702707
staleBindingPrompt = null;
703708
await confirmDeploy(
704709
null,
705710
project,
706711
true,
707-
project.githubLinked ? { preview: true, promote: false } : { preview: getCloudPreviewState(project.path) === 'active' }
712+
{ preview: getCloudPreviewState(project.path) === 'active' }
708713
);
709714
}
710715
@@ -1141,10 +1146,10 @@
11411146
title="Linked Cloud Course Missing"
11421147
message={staleBindingPrompt.message || `"${staleBindingPrompt.project?.title || staleBindingPrompt.project?.name || 'This project'}" is still linked to a CourseCode Cloud course that no longer exists.`}
11431148
detail={staleBindingPrompt.project?.githubLinked
1144-
? 'You can clear the old local cloud link and update the preview deployment now, or clear the link only and leave this project in a not deployed state. Production deploys stay managed by GitHub.'
1149+
? 'You can clear the old local cloud link and leave this project in a not deployed state. GitHub-linked deploys stay managed by GitHub.'
11451150
: 'You can clear the old local cloud link and redeploy now, or clear the link only and leave this project in a not deployed state.'}
1146-
alternateLabel="Clear Link Only"
1147-
confirmLabel={staleBindingPrompt.project?.githubLinked ? 'Clear Link and Deploy Preview' : 'Clear Link and Redeploy'}
1151+
alternateLabel={staleBindingPrompt.project?.githubLinked ? undefined : 'Clear Link Only'}
1152+
confirmLabel={staleBindingPrompt.project?.githubLinked ? 'Clear Link' : 'Clear Link and Redeploy'}
11481153
cancelLabel="Not Now"
11491154
onconfirm={confirmRepairDeploy}
11501155
onalternate={clearStaleBindingOnly}
@@ -1171,7 +1176,7 @@
11711176
</svg>
11721177
<strong>GitHub-linked course</strong>
11731178
</div>
1174-
<p class="github-info-body">Production deploys are controlled by your GitHub repo. Push to deploy.</p>
1179+
<p class="github-info-body">Deploys are controlled by your GitHub repo. Push to deploy.</p>
11751180
<div class="preview-link-panel">
11761181
<div class="preview-link-header">
11771182
<span class="preview-link-title">Main Preview Link</span>
@@ -1181,44 +1186,22 @@
11811186
{#if cloudPreviewState === 'active'}
11821187
Stakeholders can open the cloud preview now.
11831188
{:else if cloudPreviewState === 'expired'}
1184-
The main preview link expired. Turn it back on before sharing the Preview pointer.
1189+
The main preview link expired.
11851190
{:else if cloudPreviewState === 'disabled'}
1186-
The main preview link is off. Turn it on to publish a preview URL.
1191+
The main preview link is off.
11871192
{:else}
1188-
No main preview link exists yet. Turn it on to create one.
1193+
No main preview link exists yet.
11891194
{/if}
11901195
</p>
11911196
<div class="preview-link-actions">
1192-
<button
1193-
class="preview-link-btn"
1194-
disabled={isPreviewLinkBusy(project.path)}
1195-
onclick={(e) => setPreviewLinkState(e, project, cloudPreviewState !== 'active', { autoSelectPreview: true })}
1196-
>
1197-
{#if isPreviewLinkBusy(project.path)}
1198-
<div class="btn-spinner"></div>
1199-
{:else if cloudPreviewState === 'active'}
1200-
Turn Off Preview
1201-
{:else}
1202-
Turn On Preview
1203-
{/if}
1204-
</button>
12051197
{#if hasActiveCloudPreview(project.path)}
12061198
<button class="preview-link-btn subtle" onclick={(e) => openCloudPreview(e, project)}>Open Link</button>
12071199
{/if}
12081200
</div>
1209-
{#if cloudPreviewState !== 'active' && previewLinkNeedsPassword(project.path)}
1210-
<PreviewPasswordControl
1211-
id="dashboard-github-preview-password"
1212-
bind:requirePassword={previewRequirePassword}
1213-
bind:password={previewPassword}
1214-
disabled={isPreviewLinkBusy(project.path)}
1215-
/>
1216-
{/if}
12171201
</div>
1218-
<p class="github-info-body">You can still update the <strong>Preview pointer</strong> directly:</p>
1202+
<p class="github-info-body">Preview deploys and pointer updates are managed by GitHub.</p>
12191203
<div class="deploy-popover-actions">
12201204
<button class="deploy-popover-cancel" onclick={() => { deployPopover = null; deployAnchorEl = null; }}>Dismiss</button>
1221-
<button class="deploy-popover-confirm" disabled={!canUpdatePreviewPointer(project.path) || isPreviewLinkBusy(project.path)} onclick={(e) => { deployPreview = true; confirmDeploy(e, project); }}>Deploy Preview</button>
12221205
</div>
12231206
{:else}
12241207
<div class="deploy-popover-field">

renderer/src/views/ProjectDetail.svelte

Lines changed: 57 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import PreviewPasswordControl from '../components/PreviewPasswordControl.svelte';
1818
import { getDisplayErrorMessage } from '../lib/errors.js';
1919
import { generatePreviewPassword } from '../lib/preview-password.js';
20+
import { isGithubLinkedStatus } from '../lib/cloud-status.js';
2021
2122
let { projectPath } = $props();
2223
@@ -283,7 +284,7 @@
283284
}
284285
cloudStatus = status;
285286
// Detect GitHub-linked courses from the status response
286-
const isGithub = status?.source?.type === 'github' || status?.source_type === 'github';
287+
const isGithub = isGithubLinkedStatus(status);
287288
if (project && project.githubLinked !== isGithub) {
288289
project = { ...project, githubLinked: isGithub || undefined };
289290
}
@@ -353,7 +354,7 @@
353354
}
354355
355356
function canPromoteToPreview(deployment) {
356-
return deployment.id !== cloudStatus?.preview_deployment_id;
357+
return !project?.githubLinked && deployment.id !== cloudStatus?.preview_deployment_id;
357358
}
358359
359360
function resetCloudLinkControls() {
@@ -843,10 +844,14 @@
843844
}
844845
845846
async function confirmRepairDeploy() {
847+
if (project?.githubLinked) {
848+
await clearStaleBindingOnly();
849+
return;
850+
}
846851
staleBindingPrompt = null;
847852
await deploy(
848853
true,
849-
project?.githubLinked ? { preview: true, promote: false } : { preview: getCloudPreviewState() === 'active' }
854+
{ preview: getCloudPreviewState() === 'active' }
850855
);
851856
resetDeployOptions();
852857
}
@@ -1155,36 +1160,40 @@
11551160
</div>
11561161
{/if}
11571162
1158-
<div class="cloud-link-controls">
1159-
<label class="deploy-toggle-label">
1160-
<input type="checkbox" bind:checked={cloudLinkPasswordEditing} />
1161-
<span>{cloudStatus?.previewLink?.hasPassword ? 'Change password settings' : 'Require password'}</span>
1162-
</label>
1163-
{#if cloudLinkPasswordEditing}
1164-
<PreviewPasswordControl
1165-
id="cloud-panel-preview-password"
1166-
bind:requirePassword={cloudLinkRequirePassword}
1167-
bind:password={cloudLinkPassword}
1168-
disabled={cloudActionBusy === 'preview-link'}
1169-
/>
1170-
{/if}
1171-
<label class="cloud-select-label" for="cloud-link-expiry">
1172-
<span>Expiry</span>
1173-
<select id="cloud-link-expiry" bind:value={cloudLinkExpiryAction} disabled={cloudActionBusy === 'preview-link'}>
1174-
<option value="keep">Keep current expiry</option>
1175-
<option value="extend7">Extend 7 days</option>
1176-
</select>
1177-
</label>
1178-
</div>
1163+
{#if project?.githubLinked}
1164+
<p class="cloud-panel-note">Preview deploys, pointer updates, and preview-link changes are managed by GitHub for this course.</p>
1165+
{:else}
1166+
<div class="cloud-link-controls">
1167+
<label class="deploy-toggle-label">
1168+
<input type="checkbox" bind:checked={cloudLinkPasswordEditing} />
1169+
<span>{cloudStatus?.previewLink?.hasPassword ? 'Change password settings' : 'Require password'}</span>
1170+
</label>
1171+
{#if cloudLinkPasswordEditing}
1172+
<PreviewPasswordControl
1173+
id="cloud-panel-preview-password"
1174+
bind:requirePassword={cloudLinkRequirePassword}
1175+
bind:password={cloudLinkPassword}
1176+
disabled={cloudActionBusy === 'preview-link'}
1177+
/>
1178+
{/if}
1179+
<label class="cloud-select-label" for="cloud-link-expiry">
1180+
<span>Expiry</span>
1181+
<select id="cloud-link-expiry" bind:value={cloudLinkExpiryAction} disabled={cloudActionBusy === 'preview-link'}>
1182+
<option value="keep">Keep current expiry</option>
1183+
<option value="extend7">Extend 7 days</option>
1184+
</select>
1185+
</label>
1186+
</div>
11791187
1180-
<div class="cloud-actions">
1181-
<button class="preview-link-btn" disabled={cloudActionBusy === 'preview-link'} onclick={saveCloudLinkSettings}>
1182-
{cloudActionBusy === 'preview-link' ? 'Saving…' : cloudStatus?.previewLink?.exists ? 'Save Preview Link' : 'Create Preview Link'}
1183-
</button>
1184-
{#if getCloudPreviewState() === 'active'}
1185-
<button class="preview-link-btn subtle" disabled={cloudActionBusy === 'preview-link'} onclick={disableCloudPreviewLink}>Turn Off</button>
1186-
{/if}
1187-
</div>
1188+
<div class="cloud-actions">
1189+
<button class="preview-link-btn" disabled={cloudActionBusy === 'preview-link'} onclick={saveCloudLinkSettings}>
1190+
{cloudActionBusy === 'preview-link' ? 'Saving…' : cloudStatus?.previewLink?.exists ? 'Save Preview Link' : 'Create Preview Link'}
1191+
</button>
1192+
{#if getCloudPreviewState() === 'active'}
1193+
<button class="preview-link-btn subtle" disabled={cloudActionBusy === 'preview-link'} onclick={disableCloudPreviewLink}>Turn Off</button>
1194+
{/if}
1195+
</div>
1196+
{/if}
11881197
</section>
11891198
11901199
<section class="cloud-manage-section">
@@ -1251,6 +1260,7 @@
12511260
<button
12521261
class="preview-link-btn subtle"
12531262
disabled={!canPromoteToPreview(deployment) || cloudActionBusy === `preview:${deployment.id}`}
1263+
title={project?.githubLinked ? 'Preview is managed by GitHub' : 'Set as Preview'}
12541264
onclick={() => promoteDeploymentPointer(deployment, 'preview')}
12551265
>
12561266
{cloudActionBusy === `preview:${deployment.id}` ? 'Setting…' : 'Set Preview'}
@@ -1277,7 +1287,7 @@
12771287
<div class="deploy-dialog-backdrop" role="presentation" onclick={() => deployPopoverOpen = false}>
12781288
{#if project?.githubLinked}
12791289
<div class="deploy-popover deploy-dialog-panel" role="dialog" aria-modal="true" aria-label="GitHub deploy info" tabindex="-1" onclick={(e) => e.stopPropagation()} onkeydown={(e) => e.stopPropagation()}>
1280-
<p class="deploy-toggle-tip">Production deploys are managed by your GitHub repository. Push to deploy.</p>
1290+
<p class="deploy-toggle-tip">Deploys are managed by your GitHub repository. Push to deploy.</p>
12811291
<div class="preview-link-panel">
12821292
<div class="preview-link-header">
12831293
<span class="preview-link-title">Main Preview Link</span>
@@ -1287,40 +1297,22 @@
12871297
{#if getCloudPreviewState() === 'active'}
12881298
Stakeholders can open the main preview now.
12891299
{:else if getCloudPreviewState() === 'expired'}
1290-
The main preview link expired. Turn it back on before sharing the Preview pointer.
1300+
The main preview link expired.
12911301
{:else if getCloudPreviewState() === 'disabled'}
1292-
The main preview link is off. Turn it on to publish a preview URL.
1302+
The main preview link is off.
12931303
{:else}
1294-
No main preview link exists yet. Turn it on to create one.
1304+
No main preview link exists yet.
12951305
{/if}
12961306
</p>
12971307
<div class="preview-link-actions">
1298-
<button class="preview-link-btn" disabled={isPreviewLinkBusy()} onclick={() => setPreviewLinkState(getCloudPreviewState() !== 'active', { autoSelectPreview: true })}>
1299-
{#if isPreviewLinkBusy()}
1300-
<div class="btn-spinner"></div>
1301-
{:else if getCloudPreviewState() === 'active'}
1302-
Turn Off Preview
1303-
{:else}
1304-
Turn On Preview
1305-
{/if}
1306-
</button>
13071308
{#if getCloudPreviewState() === 'active'}
13081309
<button class="preview-link-btn subtle" onclick={openCloudPreview}>Open Link</button>
13091310
{/if}
13101311
</div>
1311-
{#if getCloudPreviewState() !== 'active' && previewLinkNeedsPassword()}
1312-
<PreviewPasswordControl
1313-
id="project-github-preview-password"
1314-
bind:requirePassword={previewRequirePassword}
1315-
bind:password={previewPassword}
1316-
disabled={isPreviewLinkBusy()}
1317-
/>
1318-
{/if}
13191312
</div>
1320-
<p class="deploy-toggle-tip">You can still update the Preview pointer directly from Desktop.</p>
1313+
<p class="deploy-toggle-tip">Preview deploys and pointer updates are managed by GitHub.</p>
13211314
<div class="deploy-popover-actions">
13221315
<button class="deploy-popover-cancel" onclick={() => deployPopoverOpen = false}>Dismiss</button>
1323-
<button class="deploy-popover-confirm" disabled={!canUpdatePreviewPointer() || isPreviewLinkBusy()} onclick={() => deploy(false, { preview: true, promote: false })}>Deploy Preview</button>
13241316
</div>
13251317
</div>
13261318
{:else}
@@ -1529,10 +1521,10 @@
15291521
title="Linked Cloud Course Missing"
15301522
message={staleBindingPrompt.message || `"${project?.title || project?.name || 'This project'}" is still linked to a CourseCode Cloud course that no longer exists.`}
15311523
detail={project?.githubLinked
1532-
? 'You can clear the old local cloud link and update the preview deployment now, or clear the link only and leave this project in a not deployed state. Production deploys stay managed by GitHub.'
1524+
? 'You can clear the old local cloud link and leave this project in a not deployed state. GitHub-linked deploys stay managed by GitHub.'
15331525
: 'You can clear the old local cloud link and redeploy now, or clear the link only and leave this project in a not deployed state.'}
1534-
alternateLabel="Clear Link Only"
1535-
confirmLabel={project?.githubLinked ? 'Clear Link and Deploy Preview' : 'Clear Link and Redeploy'}
1526+
alternateLabel={project?.githubLinked ? undefined : 'Clear Link Only'}
1527+
confirmLabel={project?.githubLinked ? 'Clear Link' : 'Clear Link and Redeploy'}
15361528
cancelLabel="Not Now"
15371529
onconfirm={confirmRepairDeploy}
15381530
onalternate={clearStaleBindingOnly}
@@ -2159,6 +2151,13 @@
21592151
background: var(--bg-primary);
21602152
}
21612153
2154+
.cloud-panel-note {
2155+
margin: 12px 0 0;
2156+
color: var(--text-secondary);
2157+
font-size: 12px;
2158+
line-height: 1.45;
2159+
}
2160+
21622161
.cloud-label,
21632162
.cloud-select-label span,
21642163
.cloud-reason-field span {

0 commit comments

Comments
 (0)