@gogitcms/editor 0.33.0 → 0.34.1

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/app/src/App.tsx CHANGED
@@ -1904,16 +1904,24 @@ function CmsView({
1904
1904
  // the moment someone is about to publish another project's work is the moment
1905
1905
  // to say so — not a banner they scrolled past on the way here.
1906
1906
  const totalChanges = changeSummary.reduce((n, s) => n + s.count, 0);
1907
+ // Why pressing Apply opens a change request rather than merging, when it
1908
+ // does. Stated before they commit, so nobody expects a merge that will not
1909
+ // happen. A protected target comes first: it holds for whoever presses the
1910
+ // button, and the server escalates on it regardless of project access, so
1911
+ // the partial-access wording would be the wrong explanation of the same
1912
+ // outcome. Absent for an ordinary direct merge.
1913
+ const applyTargetBranch = repo.branches.find((b) => b.id === targetBranchId);
1914
+ const applyReviewReason = applyTargetBranch?.protected
1915
+ ? `${applyTargetBranch.name} is protected, so this opens a change request for review.`
1916
+ : branch.requiresChangeRequest
1917
+ ? `You can't publish every project here, so this opens a change request for review.`
1918
+ : undefined;
1907
1919
  const applySummaryText = changeSummary.length
1908
1920
  ? `${totalChanges} change${totalChanges === 1 ? "" : "s"} across ` +
1909
1921
  `${changeSummary.length} ${changeSummary.length === 1 ? "collection" : "collections"}` +
1910
1922
  (branchSpansProjects
1911
1923
  ? ` in ${changedProjects.length} projects — ${changedProjects.map(projectLabelFor).join(", ")}. ` +
1912
- (branch.requiresChangeRequest
1913
- // They cannot publish all of it, so say what pressing Apply actually
1914
- // does rather than letting them expect a merge that will not happen.
1915
- ? `You can't publish every project here, so this opens a change request for review.`
1916
- : `Merging publishes all of them.`)
1924
+ (applyReviewReason ?? `Merging publishes all of them.`)
1917
1925
  : "")
1918
1926
  : undefined;
1919
1927
 
@@ -2228,6 +2236,7 @@ function CmsView({
2228
2236
  canApplyChanges={changeSummary.length > 0 && !hasUnresolvedConflicts}
2229
2237
  applyOpen={applyOpen}
2230
2238
  applySummary={applySummaryText}
2239
+ applyReviewReason={applyReviewReason}
2231
2240
  applyProgress={applyProgress}
2232
2241
  applyConflictCount={conflicts.filter((c) => !c.resolution).length}
2233
2242
  onConfirmApply={onConfirmApply}
@@ -146,3 +146,51 @@ test("failure surfaces the error and a dismiss button", () => {
146
146
  expect(screen.getByTestId("merge-error")).toHaveTextContent("main moved while merging");
147
147
  expect(screen.getByTestId("apply-dismiss")).toBeInTheDocument();
148
148
  });
149
+
150
+ test("with a review reason the confirmation step promises a review, not a merge commit", () => {
151
+ render(
152
+ wrap(
153
+ <ApplyChangesModal
154
+ {...branches}
155
+ reviewReason="main is protected, so this opens a change request for review."
156
+ onConfirm={() => {}}
157
+ onClose={() => {}}
158
+ />,
159
+ ),
160
+ );
161
+
162
+ const confirm = screen.getByTestId("apply-confirm");
163
+ // The run will stop before the push, so the copy must not describe one.
164
+ expect(confirm).not.toHaveTextContent("pushes a merge commit");
165
+ expect(confirm).not.toHaveTextContent("is then deleted");
166
+ expect(screen.getByTestId("apply-confirm-review")).toHaveTextContent("opens a pull request for review");
167
+ expect(confirm).toHaveTextContent("Nothing is pushed to origin/main until it is approved");
168
+ expect(confirm).toHaveTextContent("main is protected");
169
+ });
170
+
171
+ test("needs_review without code conflicts explains the review instead of listing files", () => {
172
+ const onCreateChangeRequest = jest.fn();
173
+ render(
174
+ wrap(
175
+ <ApplyChangesModal
176
+ {...branches}
177
+ reviewReason="main is protected, so this opens a change request for review."
178
+ progress={{ ...base, status: "needs_review", step: "merging", escalationFiles: [] }}
179
+ onCreateChangeRequest={onCreateChangeRequest}
180
+ onClose={() => {}}
181
+ />,
182
+ ),
183
+ );
184
+
185
+ const panel = screen.getByTestId("merge-needs-review");
186
+ // No conflict: it is not called one, and no empty file list is drawn.
187
+ expect(panel).not.toHaveTextContent("Code conflicts need a developer");
188
+ expect(panel).toHaveTextContent("This merge needs a review");
189
+ expect(screen.getByTestId("merge-needs-review-reason")).toHaveTextContent("nothing was pushed to main");
190
+ expect(screen.getByTestId("merge-needs-review-reason")).toHaveTextContent("main is protected");
191
+
192
+ // The same escalation flow: an explanation, then a change request.
193
+ fireEvent.change(screen.getByTestId("change-request-explanation"), { target: { value: "Spring copy" } });
194
+ fireEvent.click(screen.getByTestId("create-change-request-submit"));
195
+ expect(onCreateChangeRequest).toHaveBeenCalledWith("Spring copy");
196
+ });
@@ -35,6 +35,15 @@ export type ApplyChangesModalProps = {
35
35
  progress?: MergeProgress;
36
36
  /** A short summary of what will be merged, shown on the confirmation step. */
37
37
  summary?: string;
38
+ /**
39
+ * Why this apply cannot push to the target directly and opens a change
40
+ * request instead — the target is protected, or the caller cannot publish
41
+ * every project on the branch. When set, the confirmation step describes the
42
+ * review that will happen rather than a merge commit that will not, and the
43
+ * needs_review panel gives this reason when there are no code conflicts to
44
+ * list. Absent for an ordinary direct merge.
45
+ */
46
+ reviewReason?: string;
38
47
  /** Number of unresolved conflicts, shown on the conflicted state. */
39
48
  conflictCount?: number;
40
49
  /**
@@ -99,7 +108,7 @@ function StepRow({ label, state }: { label: string; state: "done" | "active" | "
99
108
  * (spinner → green check), then shows a success panel with the commit + stats,
100
109
  * or an error. Read-only: it reflects a run the host drives via a subscription.
101
110
  */
102
- export function ApplyChangesModal({ sourceBranch, targetBranch, progress, summary, conflictCount, changeRequestOpen, changeRequestUrl, creatingChangeRequest, onConfirm, onReviewConflicts, onCreateChangeRequest, onViewChangeRequest, onClose, onDone }: ApplyChangesModalProps) {
111
+ export function ApplyChangesModal({ sourceBranch, targetBranch, progress, summary, reviewReason, conflictCount, changeRequestOpen, changeRequestUrl, creatingChangeRequest, onConfirm, onReviewConflicts, onCreateChangeRequest, onViewChangeRequest, onClose, onDone }: ApplyChangesModalProps) {
103
112
  const t = useTheme();
104
113
  const done = onDone ?? onClose;
105
114
  const [explanation, setExplanation] = useState("");
@@ -121,6 +130,13 @@ export function ApplyChangesModal({ sourceBranch, targetBranch, progress, summar
121
130
  const status = progress?.status;
122
131
  const conflicted = status === "conflicted";
123
132
  const needsReview = status === "needs_review";
133
+ // A needs_review run either lists code files a developer must resolve, or
134
+ // lists nothing because the merge was clean and simply may not be pushed by
135
+ // this run (a protected target, or partial project access). The two need
136
+ // different words: the second is not a conflict and naming it one would send
137
+ // the editor looking for files that are not there.
138
+ const escalationFiles = progress?.escalationFiles ?? [];
139
+ const codeConflicts = escalationFiles.length > 0;
124
140
  const terminal = status === "succeeded" || status === "failed" || conflicted || needsReview;
125
141
  const current = progress ? stepOrder[progress.step] : 0;
126
142
 
@@ -201,30 +217,63 @@ export function ApplyChangesModal({ sourceBranch, targetBranch, progress, summar
201
217
  // Confirmation step: spell out the irreversible, outward-facing effects
202
218
  // before anything is pushed to the remote.
203
219
  <View testID="apply-confirm" style={{ gap: t.space(3) }}>
204
- <Text variant="body" color="secondary">
205
- {`This merges ${sourceBranch} into ${targetBranch}, commits the result, and pushes a merge commit to origin/${targetBranch}.`}
206
- </Text>
207
- <Text variant="body" color="secondary">
208
- {`${sourceBranch} is then deleted. This can’t be undone.`}
209
- </Text>
220
+ {reviewReason ? (
221
+ // The run will stop before the push, so promising a merge commit
222
+ // on origin here would be describing something that will not
223
+ // happen. Say what will: a review, and nothing on the target
224
+ // until it is approved.
225
+ <>
226
+ <Text variant="body" color="secondary" testID="apply-confirm-review">
227
+ {`This merges ${sourceBranch} into ${targetBranch} in the CMS and opens a pull request for review. Nothing is pushed to origin/${targetBranch} until it is approved.`}
228
+ </Text>
229
+ <Text variant="body" color="secondary">{reviewReason}</Text>
230
+ </>
231
+ ) : (
232
+ <>
233
+ <Text variant="body" color="secondary">
234
+ {`This merges ${sourceBranch} into ${targetBranch}, commits the result, and pushes a merge commit to origin/${targetBranch}.`}
235
+ </Text>
236
+ <Text variant="body" color="secondary">
237
+ {`${sourceBranch} is then deleted. This can’t be undone.`}
238
+ </Text>
239
+ </>
240
+ )}
210
241
  {summary ? <Text variant="monoSm" color="tertiary">{summary}</Text> : null}
211
242
  </View>
212
243
  ) : needsReview ? (
213
- // Content merged, but code/config files conflict. A content editor can't
214
- // resolve thesecollect an explanation and escalate to a developer.
244
+ // Content merged, but the run may not push it. Either code/config
245
+ // files conflicta content editor can't resolve these or the
246
+ // target can't take a direct push from this run. Both collect an
247
+ // explanation and escalate through a pull request.
215
248
  <View testID="merge-needs-review" style={{ gap: t.space(3) }}>
216
- <View style={{ flexDirection: "row", alignItems: "center", gap: t.space(2) }}>
217
- <Icon name="gitPullRequest" size={16} color={t.color.diffDelFg} />
218
- <Text variant="body" weight="medium">Code conflicts need a developer</Text>
219
- </View>
220
- <Text variant="body" color="secondary">
221
- {`The content merged cleanly, but these files changed on both ${sourceBranch} and ${targetBranch} and need a developer to resolve:`}
222
- </Text>
223
- <View style={{ gap: 2, padding: t.space(2), borderRadius: t.radius.md, backgroundColor: t.color.diffDelBg }}>
224
- {(progress?.escalationFiles ?? []).map((f) => (
225
- <Text key={f} variant="monoSm" color={t.color.diffDelFg} testID={`escalation-file-${f}`}>{f}</Text>
226
- ))}
227
- </View>
249
+ {codeConflicts ? (
250
+ <>
251
+ <View style={{ flexDirection: "row", alignItems: "center", gap: t.space(2) }}>
252
+ <Icon name="gitPullRequest" size={16} color={t.color.diffDelFg} />
253
+ <Text variant="body" weight="medium">Code conflicts need a developer</Text>
254
+ </View>
255
+ <Text variant="body" color="secondary">
256
+ {`The content merged cleanly, but these files changed on both ${sourceBranch} and ${targetBranch} and need a developer to resolve:`}
257
+ </Text>
258
+ <View style={{ gap: 2, padding: t.space(2), borderRadius: t.radius.md, backgroundColor: t.color.diffDelBg }}>
259
+ {escalationFiles.map((f) => (
260
+ <Text key={f} variant="monoSm" color={t.color.diffDelFg} testID={`escalation-file-${f}`}>{f}</Text>
261
+ ))}
262
+ </View>
263
+ </>
264
+ ) : (
265
+ <>
266
+ <View style={{ flexDirection: "row", alignItems: "center", gap: t.space(2) }}>
267
+ <Icon name="gitPullRequest" size={16} color={t.color.textSecondary} />
268
+ <Text variant="body" weight="medium">This merge needs a review</Text>
269
+ </View>
270
+ <Text variant="body" color="secondary" testID="merge-needs-review-reason">
271
+ {`The content merged cleanly and nothing was pushed to ${targetBranch}. ` +
272
+ (reviewReason ?? `Someone with the authority to publish it has to approve it.`) +
273
+ ` Opening a change request creates a pull request for that review.`}
274
+ </Text>
275
+ </>
276
+ )}
228
277
  <Input
229
278
  value={explanation}
230
279
  onChangeText={setExplanation}
@@ -483,6 +483,9 @@ export type ContentBrowserProps = {
483
483
  canApplyChanges?: boolean;
484
484
  applyOpen?: boolean;
485
485
  applySummary?: string;
486
+ // Why applying opens a change request instead of merging (see
487
+ // ApplyChangesModal.reviewReason). Absent for a direct merge.
488
+ applyReviewReason?: string;
486
489
  applyProgress?: MergeProgress;
487
490
  applyConflictCount?: number;
488
491
  onConfirmApply?: () => void;
@@ -3767,6 +3770,7 @@ function ApplyModalOverlay(props: ContentBrowserProps) {
3767
3770
  sourceBranch={props.workspace.branch}
3768
3771
  targetBranch={props.targetBranch ?? props.defaultBranch ?? ""}
3769
3772
  summary={props.applySummary}
3773
+ reviewReason={props.applyReviewReason}
3770
3774
  progress={props.applyProgress}
3771
3775
  conflictCount={props.applyConflictCount}
3772
3776
  changeRequestOpen={props.changeRequestOpen}
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@gogitcms/editor",
3
- "version": "0.33.0",
3
+ "version": "0.34.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@gogitcms/editor",
9
- "version": "0.33.0",
9
+ "version": "0.34.1",
10
10
  "dependencies": {
11
11
  "@apollo/client": "^3.11.8",
12
12
  "@handlewithcare/react-prosemirror": "^3.2.7",
@@ -47,11 +47,11 @@
47
47
  "node": ">=20"
48
48
  },
49
49
  "optionalDependencies": {
50
- "@gogitcms/gitcms-local-darwin-arm64": "0.33.0",
51
- "@gogitcms/gitcms-local-darwin-x64": "0.33.0",
52
- "@gogitcms/gitcms-local-linux-arm64": "0.33.0",
53
- "@gogitcms/gitcms-local-linux-x64": "0.33.0",
54
- "@gogitcms/gitcms-local-win32-x64": "0.33.0"
50
+ "@gogitcms/gitcms-local-darwin-arm64": "0.34.1",
51
+ "@gogitcms/gitcms-local-darwin-x64": "0.34.1",
52
+ "@gogitcms/gitcms-local-linux-arm64": "0.34.1",
53
+ "@gogitcms/gitcms-local-linux-x64": "0.34.1",
54
+ "@gogitcms/gitcms-local-win32-x64": "0.34.1"
55
55
  }
56
56
  },
57
57
  "node_modules/@apollo/client": {
@@ -751,9 +751,9 @@
751
751
  }
752
752
  },
753
753
  "node_modules/@gogitcms/gitcms-local-darwin-arm64": {
754
- "version": "0.33.0",
755
- "resolved": "https://registry.npmjs.org/@gogitcms/gitcms-local-darwin-arm64/-/gitcms-local-darwin-arm64-0.33.0.tgz",
756
- "integrity": "sha512-LHNaFak+lrHWPK159o+O+tFZ04v17jUUFAe4l38c4Rn4YvIRbOZABgolXGkOe3+Tv9cqlwU/NpQzFyBEY6zAeg==",
754
+ "version": "0.34.1",
755
+ "resolved": "https://registry.npmjs.org/@gogitcms/gitcms-local-darwin-arm64/-/gitcms-local-darwin-arm64-0.34.1.tgz",
756
+ "integrity": "sha512-zphjPnMkZeuK5q8vQO3pJEbyH5ADAqhLwzLXxUlbrYfX9ivMZaq5pg/JxDhpccYr0bIAQJahRnjIcQuxwaPqvw==",
757
757
  "cpu": [
758
758
  "arm64"
759
759
  ],
@@ -764,9 +764,9 @@
764
764
  ]
765
765
  },
766
766
  "node_modules/@gogitcms/gitcms-local-darwin-x64": {
767
- "version": "0.33.0",
768
- "resolved": "https://registry.npmjs.org/@gogitcms/gitcms-local-darwin-x64/-/gitcms-local-darwin-x64-0.33.0.tgz",
769
- "integrity": "sha512-oUNSUJeP+kp1/Hht1oxP15H6Tix3pV4ZYPfajHXqo3sl5nWBtjuJfVoIdHxNQPgichho6+KKD19hY0fjsWOKtA==",
767
+ "version": "0.34.1",
768
+ "resolved": "https://registry.npmjs.org/@gogitcms/gitcms-local-darwin-x64/-/gitcms-local-darwin-x64-0.34.1.tgz",
769
+ "integrity": "sha512-E+Ll9Vo2YliKCPuVPzZrz/FJrVc6vabdBiBMSHYjbgREJ8fR38mmzftEmzebNG7KGzV1JoOEEk05x7xYprtXuA==",
770
770
  "cpu": [
771
771
  "x64"
772
772
  ],
@@ -777,9 +777,9 @@
777
777
  ]
778
778
  },
779
779
  "node_modules/@gogitcms/gitcms-local-linux-x64": {
780
- "version": "0.33.0",
781
- "resolved": "https://registry.npmjs.org/@gogitcms/gitcms-local-linux-x64/-/gitcms-local-linux-x64-0.33.0.tgz",
782
- "integrity": "sha512-D6L5EmaS98P697xaJp6BGfZoX95F6f7WH3idxa0GYnMu1zn0uTsrk5yTyPwpIAmNfs7ikXfqH4d33iWf4uP6Xw==",
780
+ "version": "0.34.1",
781
+ "resolved": "https://registry.npmjs.org/@gogitcms/gitcms-local-linux-x64/-/gitcms-local-linux-x64-0.34.1.tgz",
782
+ "integrity": "sha512-jGZmM79kjoQl7iuSudZlEoc2oA/Hy3ie5SU47BfDF89/3g5hHQpIKf8RcEy2oLGYaO1ghEGy1ekQ+4Cc9KHKdw==",
783
783
  "cpu": [
784
784
  "x64"
785
785
  ],
@@ -790,9 +790,9 @@
790
790
  ]
791
791
  },
792
792
  "node_modules/@gogitcms/gitcms-local-win32-x64": {
793
- "version": "0.33.0",
794
- "resolved": "https://registry.npmjs.org/@gogitcms/gitcms-local-win32-x64/-/gitcms-local-win32-x64-0.33.0.tgz",
795
- "integrity": "sha512-4PEuN3bE8yEbdRAt3Tiakl0cg7cU3XbQFJN0OPwhawXDkWi55sDzlmgqfEeg9DoCgOhu1VLcxMUwcJRDVqL5DA==",
793
+ "version": "0.34.1",
794
+ "resolved": "https://registry.npmjs.org/@gogitcms/gitcms-local-win32-x64/-/gitcms-local-win32-x64-0.34.1.tgz",
795
+ "integrity": "sha512-VfJVJCZJgjU3TLfYCvwjfZFp9TDDMjtBtBAI4RMJevXyexxBsa9D1/0np8UaMS/kXGNqJPZZfFZxvnh5ABlprg==",
796
796
  "cpu": [
797
797
  "x64"
798
798
  ],
@@ -978,9 +978,9 @@
978
978
  }
979
979
  },
980
980
  "node_modules/@posthog/types": {
981
- "version": "1.407.2",
982
- "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.407.2.tgz",
983
- "integrity": "sha512-F/x/qlZSGiianCEZlZ9XRYHUYffH07Zv+1oOllxAw15ifObagxSZzc2IneZE01eFqPl2OmoNuY86ER5Uyvy8Zw==",
981
+ "version": "1.408.0",
982
+ "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.408.0.tgz",
983
+ "integrity": "sha512-xMY8QrRKsTYu4A4Of7XF/xEt0HcnZOWwnICm3do7tGKA0lTlnnr4YW0AHQN1Uv2P3BDqf5GQoqh91MKE+dgdcA==",
984
984
  "license": "MIT"
985
985
  },
986
986
  "node_modules/@react-native/asset-utils": {
@@ -4058,14 +4058,14 @@
4058
4058
  "license": "MIT"
4059
4059
  },
4060
4060
  "node_modules/posthog-js": {
4061
- "version": "1.424.1",
4062
- "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.424.1.tgz",
4063
- "integrity": "sha512-5jJ3PWPDbJ255gcIgANRGt83npqb6hNwgmDt67PmaNMW3ZRTo+arlooEJaSC+poAAJHgsAU4VL0NJ9i/rvmd5g==",
4061
+ "version": "1.425.0",
4062
+ "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.425.0.tgz",
4063
+ "integrity": "sha512-HwbWI1PPFsVQjuFSmRoZioruw2DuIinH37AV9xu8iT2dUIgeBJYOGSv7VQbPSGtH/DeyxCv04UnMAdNKeBSJWg==",
4064
4064
  "license": "(Apache-2.0 AND MIT)",
4065
4065
  "dependencies": {
4066
4066
  "@posthog/browser-common": "^0.7.1",
4067
4067
  "@posthog/core": "^1.50.2",
4068
- "@posthog/types": "^1.407.1",
4068
+ "@posthog/types": "^1.408.0",
4069
4069
  "core-js": "^3.49.0",
4070
4070
  "dompurify": "^3.4.13",
4071
4071
  "fflate": "^0.4.8",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gogitcms/editor",
3
- "version": "0.33.0",
3
+ "version": "0.34.1",
4
4
  "description": "Terminal UI for setting up and running the Go·Git CMS content editor",
5
5
  "type": "module",
6
6
  "bin": {
@@ -65,10 +65,10 @@
65
65
  "@gogitcms/frontend": "workspace:*"
66
66
  },
67
67
  "optionalDependencies": {
68
- "@gogitcms/gitcms-local-darwin-arm64": "0.33.0",
69
- "@gogitcms/gitcms-local-darwin-x64": "0.33.0",
70
- "@gogitcms/gitcms-local-linux-arm64": "0.33.0",
71
- "@gogitcms/gitcms-local-linux-x64": "0.33.0",
72
- "@gogitcms/gitcms-local-win32-x64": "0.33.0"
68
+ "@gogitcms/gitcms-local-darwin-arm64": "0.34.1",
69
+ "@gogitcms/gitcms-local-darwin-x64": "0.34.1",
70
+ "@gogitcms/gitcms-local-linux-arm64": "0.34.1",
71
+ "@gogitcms/gitcms-local-linux-x64": "0.34.1",
72
+ "@gogitcms/gitcms-local-win32-x64": "0.34.1"
73
73
  }
74
74
  }