canopycms 0.0.68-int.98 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2131 -0
- package/README.md +47 -63
- package/dist/ai/generate.d.ts +5 -0
- package/dist/ai/generate.js +244 -115
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/json-to-markdown.d.ts +21 -1
- package/dist/ai/json-to-markdown.js +120 -57
- package/dist/ai/references.d.ts +72 -0
- package/dist/ai/references.js +223 -0
- package/dist/ai/resolve-branch.js +9 -2
- package/dist/ai/to-plain-text.d.ts +17 -8
- package/dist/ai/to-plain-text.js +18 -15
- package/dist/ai/types.d.ts +84 -3
- package/dist/api/admin-branch-health.d.ts +24 -1
- package/dist/api/admin-branch-health.js +52 -20
- package/dist/api/admin.d.ts +23 -5
- package/dist/api/admin.js +36 -1
- package/dist/api/assets.js +88 -78
- package/dist/api/branch-create-window.d.ts +12 -0
- package/dist/api/branch-create-window.js +13 -0
- package/dist/api/branch-merge.d.ts +1 -1
- package/dist/api/branch-merge.js +1 -1
- package/dist/api/branch-review.d.ts +3 -3
- package/dist/api/branch-review.js +16 -7
- package/dist/api/branch-status.d.ts +16 -6
- package/dist/api/branch-status.js +57 -12
- package/dist/api/branch-withdraw.d.ts +2 -2
- package/dist/api/branch-withdraw.js +8 -3
- package/dist/api/branch.d.ts +25 -12
- package/dist/api/branch.js +164 -103
- package/dist/api/client.d.ts +78 -58
- package/dist/api/client.js +150 -117
- package/dist/api/comments.d.ts +12 -3
- package/dist/api/comments.js +118 -9
- package/dist/api/content.d.ts +9 -9
- package/dist/api/content.js +131 -22
- package/dist/api/entries-constants.d.ts +14 -3
- package/dist/api/entries-constants.js +14 -3
- package/dist/api/entries.d.ts +40 -2
- package/dist/api/entries.js +102 -6
- package/dist/api/github-sync.d.ts +19 -0
- package/dist/api/github-sync.js +50 -3
- package/dist/api/groups.d.ts +4 -4
- package/dist/api/groups.js +2 -1
- package/dist/api/guards.js +23 -3
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +1 -0
- package/dist/api/permissions.d.ts +21 -2
- package/dist/api/permissions.js +50 -3
- package/dist/api/request-deadline.d.ts +59 -0
- package/dist/api/request-deadline.js +73 -0
- package/dist/api/resolve-references.js +8 -7
- package/dist/api/route-builder.d.ts +14 -0
- package/dist/api/route-builder.js +14 -1
- package/dist/api/schema.d.ts +11 -9
- package/dist/api/schema.js +2 -0
- package/dist/api/settings-helpers.d.ts +5 -1
- package/dist/api/settings-helpers.js +10 -2
- package/dist/api/types.d.ts +18 -0
- package/dist/api/users-constants.d.ts +6 -0
- package/dist/api/users-constants.js +6 -0
- package/dist/assets/asset-url.d.ts +28 -16
- package/dist/assets/asset-url.js +51 -25
- package/dist/assets/finalize.js +3 -0
- package/dist/assets/index.d.ts +1 -1
- package/dist/assets/materialize.d.ts +153 -0
- package/dist/assets/materialize.js +402 -0
- package/dist/assets/store-local.d.ts +19 -5
- package/dist/assets/store-local.js +69 -10
- package/dist/assets/store-s3.d.ts +38 -7
- package/dist/assets/store-s3.js +128 -22
- package/dist/assets/transform-directives.d.ts +52 -13
- package/dist/assets/transform-directives.js +87 -27
- package/dist/assets/transform.d.ts +3 -3
- package/dist/assets/transform.js +13 -8
- package/dist/assets/types.d.ts +31 -4
- package/dist/auth/cache.d.ts +1 -1
- package/dist/auth/cache.js +1 -1
- package/dist/auth/caching-auth-plugin.d.ts +2 -0
- package/dist/auth/caching-auth-plugin.js +12 -2
- package/dist/auth/file-based-auth-cache.d.ts +8 -0
- package/dist/auth/file-based-auth-cache.js +18 -8
- package/dist/auth/person-name.d.ts +5 -0
- package/dist/auth/person-name.js +8 -0
- package/dist/auth/plugin.d.ts +10 -1
- package/dist/auth/types.d.ts +2 -0
- package/dist/auth/user-metadata-lookup.d.ts +21 -0
- package/dist/auth/user-metadata-lookup.js +116 -0
- package/dist/authorization/branch.d.ts +1 -1
- package/dist/authorization/branch.js +12 -13
- package/dist/authorization/groups/schema.d.ts +4 -4
- package/dist/authorization/helpers.d.ts +8 -0
- package/dist/authorization/helpers.js +8 -0
- package/dist/authorization/index.d.ts +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/protected-branch.d.ts +5 -4
- package/dist/authorization/protected-branch.js +7 -5
- package/dist/branch-health.d.ts +35 -8
- package/dist/branch-health.js +54 -18
- package/dist/branch-metadata-error.d.ts +27 -0
- package/dist/branch-metadata-error.js +23 -0
- package/dist/branch-metadata-file.d.ts +205 -24
- package/dist/branch-metadata-file.js +126 -25
- package/dist/branch-metadata.d.ts +45 -6
- package/dist/branch-metadata.js +142 -50
- package/dist/branch-provisioning.d.ts +167 -0
- package/dist/branch-provisioning.js +501 -0
- package/dist/branch-registry.d.ts +8 -0
- package/dist/branch-registry.js +61 -9
- package/dist/branch-schema-cache.d.ts +29 -12
- package/dist/branch-schema-cache.js +64 -21
- package/dist/branch-sparse.d.ts +23 -0
- package/dist/branch-sparse.js +118 -0
- package/dist/branch-workspace.d.ts +42 -3
- package/dist/branch-workspace.js +239 -100
- package/dist/build/asset-refs.d.ts +94 -0
- package/dist/build/asset-refs.js +267 -0
- package/dist/build/generate-ai-content.js +5 -0
- package/dist/build/inline-flight.d.ts +37 -0
- package/dist/build/inline-flight.js +142 -0
- package/dist/cli/asset-refs.d.ts +52 -0
- package/dist/cli/asset-refs.js +194 -0
- package/dist/cli/cli.d.ts +15 -1
- package/dist/cli/cli.js +8543 -3193
- package/dist/cli/configured-asset-store.d.ts +12 -0
- package/dist/cli/configured-asset-store.js +46 -0
- package/dist/cli/generate-ai-content.js +4244 -1072
- package/dist/cli/init.js +217 -126
- package/dist/cli/project-detect.d.ts +2 -0
- package/dist/cli/project-detect.js +2 -0
- package/dist/cli/template-files/Dockerfile.cms.template +13 -5
- package/dist/cli/template-files/cms-stack.ts.template +16 -13
- package/dist/client.d.ts +1 -2
- package/dist/client.js +1 -2
- package/dist/comment-store.d.ts +25 -0
- package/dist/comment-store.js +43 -8
- package/dist/config/flatten.js +2 -0
- package/dist/config/helpers.js +2 -2
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schemas/config.d.ts +12 -29
- package/dist/config/schemas/config.js +9 -12
- package/dist/config/schemas/field.d.ts +3 -0
- package/dist/config/schemas/field.js +58 -4
- package/dist/config/schemas/media.d.ts +4 -13
- package/dist/config/schemas/media.js +5 -8
- package/dist/config/schemas/url.d.ts +2 -20
- package/dist/config/schemas/url.js +2 -25
- package/dist/config/types.d.ts +116 -22
- package/dist/config/types.js +3 -9
- package/dist/config/validation.d.ts +25 -4
- package/dist/config/validation.js +116 -3
- package/dist/content-id-index.js +2 -2
- package/dist/content-index-generation.d.ts +1 -1
- package/dist/content-index-generation.js +1 -1
- package/dist/content-listing.d.ts +10 -1
- package/dist/content-listing.js +22 -5
- package/dist/content-reader.d.ts +9 -0
- package/dist/content-reader.js +16 -11
- package/dist/content-store.d.ts +75 -12
- package/dist/content-store.js +249 -86
- package/dist/content-tree.js +2 -2
- package/dist/context.js +5 -2
- package/dist/dangling-reference-log.d.ts +17 -0
- package/dist/dangling-reference-log.js +29 -0
- package/dist/dev-content-watcher.d.ts +1 -1
- package/dist/dev-content-watcher.js +1 -1
- package/dist/editor/BranchManager.d.ts +23 -7
- package/dist/editor/BranchManager.js +119 -54
- package/dist/editor/CanopyEditor.d.ts +1 -1
- package/dist/editor/CanopyEditor.js +1 -2
- package/dist/editor/CommentsPanel.d.ts +14 -0
- package/dist/editor/CommentsPanel.js +68 -43
- package/dist/editor/Editor.d.ts +11 -5
- package/dist/editor/Editor.js +891 -218
- package/dist/editor/EditorAuthGate.d.ts +7 -1
- package/dist/editor/EditorAuthGate.js +41 -9
- package/dist/editor/EntryNavigator.d.ts +22 -0
- package/dist/editor/EntryNavigator.js +353 -105
- package/dist/editor/FormHeader.d.ts +25 -0
- package/dist/editor/FormHeader.js +82 -0
- package/dist/editor/FormRenderer.d.ts +27 -3
- package/dist/editor/FormRenderer.js +171 -76
- package/dist/editor/PreviewFrame.d.ts +40 -0
- package/dist/editor/PreviewFrame.js +215 -0
- package/dist/editor/PreviewPane.d.ts +47 -0
- package/dist/editor/PreviewPane.js +147 -0
- package/dist/editor/PreviewToolbarTools.d.ts +22 -0
- package/dist/editor/PreviewToolbarTools.js +73 -0
- package/dist/editor/admin/SystemHealthPanel.d.ts +2 -1
- package/dist/editor/admin/SystemHealthPanel.js +163 -33
- package/dist/editor/admin/useSystemHealth.d.ts +18 -0
- package/dist/editor/admin/useSystemHealth.js +70 -1
- package/dist/editor/branch-status.d.ts +17 -0
- package/dist/editor/branch-status.js +29 -0
- package/dist/editor/canopy-path.d.ts +9 -6
- package/dist/editor/canopy-path.js +8 -11
- package/dist/editor/client-reference-resolver.d.ts +43 -16
- package/dist/editor/client-reference-resolver.js +155 -64
- package/dist/editor/comments/BranchComments.d.ts +1 -1
- package/dist/editor/comments/BranchComments.js +2 -2
- package/dist/editor/comments/CloseRefusedNotice.d.ts +4 -0
- package/dist/editor/comments/CloseRefusedNotice.js +7 -0
- package/dist/editor/comments/CommentThreadsPopover.d.ts +47 -0
- package/dist/editor/comments/CommentThreadsPopover.js +274 -0
- package/dist/editor/comments/CommentWriteAlert.d.ts +11 -0
- package/dist/editor/comments/CommentWriteAlert.js +13 -0
- package/dist/editor/comments/EntryComments.d.ts +10 -4
- package/dist/editor/comments/EntryComments.js +14 -17
- package/dist/editor/comments/FieldWrapper.d.ts +18 -4
- package/dist/editor/comments/FieldWrapper.js +62 -28
- package/dist/editor/comments/InlineCommentThread.d.ts +9 -2
- package/dist/editor/comments/InlineCommentThread.js +11 -47
- package/dist/editor/comments/ThreadCarousel.d.ts +1 -1
- package/dist/editor/comments/ThreadCarousel.js +18 -21
- package/dist/editor/comments/ThreadReplyControls.d.ts +28 -0
- package/dist/editor/comments/ThreadReplyControls.js +92 -0
- package/dist/editor/comments/field-comment-context.d.ts +14 -0
- package/dist/editor/comments/field-comment-context.js +14 -0
- package/dist/editor/comments/form-comments.d.ts +48 -0
- package/dist/editor/comments/form-comments.js +178 -0
- package/dist/editor/comments/item-anchor-copy.d.ts +6 -0
- package/dist/editor/comments/item-anchor-copy.js +6 -0
- package/dist/editor/comments/move-focus-if-unmoved.d.ts +6 -0
- package/dist/editor/comments/move-focus-if-unmoved.js +11 -0
- package/dist/editor/comments/popover-placement.d.ts +18 -0
- package/dist/editor/comments/popover-placement.js +58 -0
- package/dist/editor/comments/use-tracked-writes.d.ts +16 -0
- package/dist/editor/comments/use-tracked-writes.js +53 -0
- package/dist/editor/components/BranchAccessList.d.ts +15 -0
- package/dist/editor/components/BranchAccessList.js +52 -0
- package/dist/editor/components/BranchLockBanner.d.ts +22 -0
- package/dist/editor/components/BranchLockBanner.js +39 -0
- package/dist/editor/components/BranchPill.d.ts +32 -0
- package/dist/editor/components/BranchPill.js +33 -0
- package/dist/editor/components/BranchesDrawer.d.ts +11 -0
- package/dist/editor/components/BranchesDrawer.js +5 -0
- package/dist/editor/components/ConfirmDeleteModal.d.ts +6 -1
- package/dist/editor/components/ConfirmDeleteModal.js +3 -2
- package/dist/editor/components/DestructiveMenuItem.d.ts +7 -0
- package/dist/editor/components/DestructiveMenuItem.js +8 -0
- package/dist/editor/components/EditorCrashScreen.d.ts +17 -0
- package/dist/editor/components/EditorCrashScreen.js +22 -0
- package/dist/editor/components/EditorErrorBoundary.d.ts +38 -0
- package/dist/editor/components/EditorErrorBoundary.js +40 -0
- package/dist/editor/components/EditorHeader.d.ts +56 -106
- package/dist/editor/components/EditorHeader.js +130 -131
- package/dist/editor/components/EditorRail.d.ts +49 -0
- package/dist/editor/components/EditorRail.js +65 -0
- package/dist/editor/components/EntryBreadcrumb.d.ts +33 -0
- package/dist/editor/components/EntryBreadcrumb.js +56 -0
- package/dist/editor/components/EntryCreateModal.d.ts +12 -7
- package/dist/editor/components/EntryCreateModal.js +51 -57
- package/dist/editor/components/NoEditPermissionNotice.d.ts +6 -5
- package/dist/editor/components/NoEditPermissionNotice.js +5 -4
- package/dist/editor/components/QuickOpenPalette.d.ts +22 -0
- package/dist/editor/components/QuickOpenPalette.js +66 -0
- package/dist/editor/components/ReadOnlyDraftNotice.d.ts +11 -0
- package/dist/editor/components/ReadOnlyDraftNotice.js +12 -0
- package/dist/editor/components/ReasonedMenuItem.d.ts +15 -0
- package/dist/editor/components/ReasonedMenuItem.js +15 -0
- package/dist/editor/components/ReferencedByList.d.ts +11 -0
- package/dist/editor/components/ReferencedByList.js +28 -0
- package/dist/editor/components/RelativeTime.d.ts +10 -0
- package/dist/editor/components/RelativeTime.js +10 -0
- package/dist/editor/components/RenameEntryModal.d.ts +9 -1
- package/dist/editor/components/RenameEntryModal.js +19 -31
- package/dist/editor/components/RetryToastMessage.d.ts +9 -0
- package/dist/editor/components/RetryToastMessage.js +6 -0
- package/dist/editor/components/SaveAllResult.d.ts +25 -0
- package/dist/editor/components/SaveAllResult.js +86 -0
- package/dist/editor/components/SaveSplitButton.d.ts +30 -0
- package/dist/editor/components/SaveSplitButton.js +108 -0
- package/dist/editor/components/StagedChangesDrawer.d.ts +39 -0
- package/dist/editor/components/StagedChangesDrawer.js +92 -0
- package/dist/editor/components/UnavailableEntryNotice.d.ts +6 -0
- package/dist/editor/components/UnavailableEntryNotice.js +9 -0
- package/dist/editor/components/UnavailableTypeMessage.d.ts +6 -0
- package/dist/editor/components/UnavailableTypeMessage.js +9 -0
- package/dist/editor/components/UndoToastMessage.d.ts +18 -0
- package/dist/editor/components/UndoToastMessage.js +10 -0
- package/dist/editor/components/UserBadge.d.ts +2 -5
- package/dist/editor/components/UserBadge.js +62 -33
- package/dist/editor/components/ValidationSummary.d.ts +14 -0
- package/dist/editor/components/ValidationSummary.js +24 -0
- package/dist/editor/components/index.d.ts +2 -2
- package/dist/editor/components/index.js +2 -2
- package/dist/editor/context/ApiClientContext.d.ts +8 -2
- package/dist/editor/context/ApiClientContext.js +19 -8
- package/dist/editor/context/AssetContext.d.ts +13 -35
- package/dist/editor/context/AssetContext.js +12 -31
- package/dist/editor/context/EditorStateContext.d.ts +4 -4
- package/dist/editor/context/EditorStateContext.js +4 -4
- package/dist/editor/context/SWRProvider.d.ts +3 -3
- package/dist/editor/context/SiteMdxAllowContext.d.ts +4 -0
- package/dist/editor/context/SiteMdxAllowContext.js +7 -0
- package/dist/editor/context/index.d.ts +2 -1
- package/dist/editor/context/index.js +2 -1
- package/dist/editor/contrast.d.ts +19 -0
- package/dist/editor/contrast.js +41 -0
- package/dist/editor/copy.d.ts +18 -0
- package/dist/editor/copy.js +18 -0
- package/dist/editor/editor-config.js +16 -0
- package/dist/editor/editor-utils.d.ts +34 -8
- package/dist/editor/editor-utils.js +42 -20
- package/dist/editor/entry-search.d.ts +20 -0
- package/dist/editor/entry-search.js +34 -0
- package/dist/editor/field-breadcrumb.d.ts +12 -0
- package/dist/editor/field-breadcrumb.js +85 -0
- package/dist/editor/field-props.d.ts +86 -0
- package/dist/editor/field-props.js +27 -0
- package/dist/editor/field-target.d.ts +30 -0
- package/dist/editor/field-target.js +77 -0
- package/dist/editor/fields/BlockField.d.ts +12 -0
- package/dist/editor/fields/BlockField.js +89 -73
- package/dist/editor/fields/CodeField.d.ts +2 -0
- package/dist/editor/fields/CodeField.js +6 -2
- package/dist/editor/fields/DateTimeField.d.ts +4 -29
- package/dist/editor/fields/DateTimeField.js +79 -41
- package/dist/editor/fields/ExpandAllButton.d.ts +6 -0
- package/dist/editor/fields/ExpandAllButton.js +4 -0
- package/dist/editor/fields/FieldCrashFallback.d.ts +17 -0
- package/dist/editor/fields/FieldCrashFallback.js +30 -0
- package/dist/editor/fields/FieldLabel.d.ts +37 -0
- package/dist/editor/fields/FieldLabel.js +19 -0
- package/dist/editor/fields/ImageField.d.ts +4 -0
- package/dist/editor/fields/ImageField.js +29 -14
- package/dist/editor/fields/InlineGroupField.js +4 -3
- package/dist/editor/fields/ItemList.d.ts +42 -0
- package/dist/editor/fields/ItemList.js +130 -0
- package/dist/editor/fields/MarkdownField.d.ts +32 -0
- package/dist/editor/fields/MarkdownField.js +223 -46
- package/dist/editor/fields/MdxImageDialog.js +1 -1
- package/dist/editor/fields/NumberField.d.ts +2 -0
- package/dist/editor/fields/NumberField.js +8 -5
- package/dist/editor/fields/NumberListField.d.ts +2 -0
- package/dist/editor/fields/NumberListField.js +19 -16
- package/dist/editor/fields/ObjectField.d.ts +2 -7
- package/dist/editor/fields/ObjectField.js +15 -5
- package/dist/editor/fields/ObjectListField.d.ts +29 -0
- package/dist/editor/fields/ObjectListField.js +57 -0
- package/dist/editor/fields/ReferenceField.d.ts +2 -0
- package/dist/editor/fields/ReferenceField.js +34 -9
- package/dist/editor/fields/SelectField.d.ts +2 -0
- package/dist/editor/fields/SelectField.js +11 -2
- package/dist/editor/fields/StringListField.d.ts +4 -1
- package/dist/editor/fields/StringListField.js +22 -8
- package/dist/editor/fields/TextField.d.ts +2 -0
- package/dist/editor/fields/TextField.js +6 -2
- package/dist/editor/fields/ToggleField.d.ts +2 -0
- package/dist/editor/fields/ToggleField.js +8 -3
- package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +1 -1
- package/dist/editor/fields/entry-link/InsertEntryLink.js +1 -1
- package/dist/editor/fields/form-expansion.d.ts +9 -0
- package/dist/editor/fields/form-expansion.js +2 -0
- package/dist/editor/fields/item-expansion.d.ts +15 -0
- package/dist/editor/fields/item-expansion.js +60 -0
- package/dist/editor/fields/list-item-keys.d.ts +8 -0
- package/dist/editor/fields/list-item-keys.js +50 -0
- package/dist/editor/fields/list-item-title.d.ts +20 -0
- package/dist/editor/fields/list-item-title.js +85 -0
- package/dist/editor/fields/markdown-export-options.d.ts +2 -0
- package/dist/editor/fields/markdown-export-options.js +8 -0
- package/dist/editor/fields/markdown-fidelity-visitors.d.ts +11 -0
- package/dist/editor/fields/markdown-fidelity-visitors.js +160 -0
- package/dist/editor/fields/mdx-jsx-support.d.ts +5 -1
- package/dist/editor/fields/mdx-jsx-support.js +157 -69
- package/dist/editor/fields/rich-text-failures.d.ts +5 -0
- package/dist/editor/fields/rich-text-failures.js +26 -0
- package/dist/editor/fields/toolbar-wrap.d.ts +7 -0
- package/dist/editor/fields/toolbar-wrap.js +35 -0
- package/dist/editor/form-outline.d.ts +13 -0
- package/dist/editor/form-outline.js +70 -0
- package/dist/editor/group-manager/GroupCard.d.ts +4 -1
- package/dist/editor/group-manager/GroupCard.js +12 -3
- package/dist/editor/group-manager/GroupForm.js +1 -1
- package/dist/editor/group-manager/InternalGroupsTab.d.ts +2 -1
- package/dist/editor/group-manager/InternalGroupsTab.js +4 -4
- package/dist/editor/group-manager/MemberList.d.ts +2 -1
- package/dist/editor/group-manager/MemberList.js +15 -4
- package/dist/editor/group-manager/hooks/useGroupState.d.ts +5 -0
- package/dist/editor/group-manager/hooks/useGroupState.js +30 -7
- package/dist/editor/group-manager/index.js +34 -5
- package/dist/editor/group-manager/types.d.ts +5 -2
- package/dist/editor/hooks/create-branch-request.d.ts +20 -0
- package/dist/editor/hooks/create-branch-request.js +54 -0
- package/dist/editor/hooks/save-all.d.ts +68 -0
- package/dist/editor/hooks/save-all.js +61 -0
- package/dist/editor/hooks/useBranchActions.d.ts +11 -1
- package/dist/editor/hooks/useBranchActions.js +42 -19
- package/dist/editor/hooks/useBranchManager.d.ts +21 -0
- package/dist/editor/hooks/useBranchManager.js +321 -125
- package/dist/editor/hooks/useBranchesData.d.ts +8 -4
- package/dist/editor/hooks/useBranchesData.js +9 -5
- package/dist/editor/hooks/useCommentSystem.d.ts +14 -2
- package/dist/editor/hooks/useCommentSystem.js +79 -55
- package/dist/editor/hooks/useCommentsData.d.ts +11 -7
- package/dist/editor/hooks/useCommentsData.js +20 -19
- package/dist/editor/hooks/useDraftManager.d.ts +77 -3
- package/dist/editor/hooks/useDraftManager.js +887 -94
- package/dist/editor/hooks/useEditorLayout.d.ts +53 -7
- package/dist/editor/hooks/useEditorLayout.js +98 -20
- package/dist/editor/hooks/useEntriesData.d.ts +2 -1
- package/dist/editor/hooks/useEntriesData.js +18 -4
- package/dist/editor/hooks/useEntryManager.d.ts +41 -2
- package/dist/editor/hooks/useEntryManager.js +227 -41
- package/dist/editor/hooks/useGroupManager.js +13 -16
- package/dist/editor/hooks/usePermissionManager.js +14 -6
- package/dist/editor/hooks/usePreviewMarks.d.ts +21 -0
- package/dist/editor/hooks/usePreviewMarks.js +33 -0
- package/dist/editor/hooks/useReferenceResolution.d.ts +10 -3
- package/dist/editor/hooks/useReferenceResolution.js +34 -177
- package/dist/editor/hooks/useSchemaManager.d.ts +25 -6
- package/dist/editor/hooks/useSchemaManager.js +97 -39
- package/dist/editor/hooks/useUserMetadata.d.ts +14 -4
- package/dist/editor/hooks/useUserMetadata.js +76 -47
- package/dist/editor/hooks/user-metadata-batcher.d.ts +8 -0
- package/dist/editor/hooks/user-metadata-batcher.js +33 -0
- package/dist/editor/media/AssetCard.d.ts +6 -7
- package/dist/editor/media/AssetCard.js +24 -31
- package/dist/editor/media/AssetListRow.d.ts +16 -0
- package/dist/editor/media/AssetListRow.js +41 -0
- package/dist/editor/media/CropStep.js +6 -1
- package/dist/editor/media/MediaLibrary.d.ts +4 -3
- package/dist/editor/media/MediaLibrary.js +11 -4
- package/dist/editor/media/MediaLibraryBody.d.ts +3 -3
- package/dist/editor/media/MediaLibraryBody.js +195 -80
- package/dist/editor/media/MediaLibrarySkeleton.d.ts +6 -0
- package/dist/editor/media/MediaLibrarySkeleton.js +14 -0
- package/dist/editor/media/crop-math.d.ts +3 -4
- package/dist/editor/media/crop-math.js +11 -22
- package/dist/editor/media/editor-image-src.d.ts +8 -0
- package/dist/editor/media/editor-image-src.js +10 -0
- package/dist/editor/media/format-bytes.d.ts +2 -0
- package/dist/editor/media/format-bytes.js +23 -0
- package/dist/editor/media/media-library-prefs.d.ts +23 -0
- package/dist/editor/media/media-library-prefs.js +49 -0
- package/dist/editor/media/media-library-view.d.ts +34 -0
- package/dist/editor/media/media-library-view.js +40 -0
- package/dist/editor/media/useAssetLibrary.d.ts +30 -0
- package/dist/editor/media/useAssetLibrary.js +126 -0
- package/dist/editor/media/useStableAssetOrder.d.ts +11 -0
- package/dist/editor/media/useStableAssetOrder.js +37 -0
- package/dist/editor/media/useThumbnailFailure.d.ts +6 -0
- package/dist/editor/media/useThumbnailFailure.js +16 -0
- package/dist/editor/permission-manager/GroupSelector.js +2 -2
- package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -1
- package/dist/editor/permission-manager/PermissionEditor.js +15 -4
- package/dist/editor/permission-manager/PermissionLevelBadge.js +1 -1
- package/dist/editor/permission-manager/PermissionTree.d.ts +1 -1
- package/dist/editor/permission-manager/UserSelector.d.ts +1 -1
- package/dist/editor/permission-manager/UserSelector.js +3 -3
- package/dist/editor/permission-manager/index.js +12 -3
- package/dist/editor/permission-manager/types.d.ts +5 -2
- package/dist/editor/platform.d.ts +3 -0
- package/dist/editor/platform.js +9 -0
- package/dist/editor/preview-asset-base.d.ts +9 -0
- package/dist/editor/preview-asset-base.js +17 -0
- package/dist/editor/preview-bridge.d.ts +51 -27
- package/dist/editor/preview-bridge.js +103 -144
- package/dist/editor/preview-marks.d.ts +15 -0
- package/dist/editor/preview-marks.js +74 -0
- package/dist/editor/preview-path.d.ts +12 -0
- package/dist/editor/preview-path.js +45 -1
- package/dist/editor/preview-scale.d.ts +27 -0
- package/dist/editor/preview-scale.js +34 -0
- package/dist/editor/raw-asset-base.d.ts +5 -0
- package/dist/editor/raw-asset-base.js +14 -0
- package/dist/editor/relative-time.d.ts +4 -4
- package/dist/editor/relative-time.js +16 -8
- package/dist/editor/schema-editor/CollectionEditor.d.ts +20 -3
- package/dist/editor/schema-editor/CollectionEditor.js +361 -62
- package/dist/editor/schema-editor/EntryTypeEditor.d.ts +5 -1
- package/dist/editor/schema-editor/EntryTypeEditor.js +11 -6
- package/dist/editor/schema-editor/collection-draft.d.ts +22 -0
- package/dist/editor/schema-editor/collection-draft.js +40 -0
- package/dist/editor/schema-editor/index.d.ts +1 -1
- package/dist/editor/schema-label.d.ts +10 -0
- package/dist/editor/schema-label.js +17 -0
- package/dist/editor/settings-conflict.d.ts +5 -0
- package/dist/editor/settings-conflict.js +8 -0
- package/dist/editor/shell/ActivityRail.d.ts +37 -0
- package/dist/editor/shell/ActivityRail.js +91 -0
- package/dist/editor/shell/EditorShell.d.ts +17 -0
- package/dist/editor/shell/EditorShell.js +31 -0
- package/dist/editor/shell/PaneLayout.d.ts +28 -0
- package/dist/editor/shell/PaneLayout.js +116 -0
- package/dist/editor/shell/SidePanelHost.d.ts +35 -0
- package/dist/editor/shell/SidePanelHost.js +184 -0
- package/dist/editor/shell/TopBar.d.ts +12 -0
- package/dist/editor/shell/TopBar.js +14 -0
- package/dist/editor/shell/host-chrome.d.ts +16 -0
- package/dist/editor/shell/host-chrome.js +69 -0
- package/dist/editor/slug.d.ts +16 -0
- package/dist/editor/slug.js +43 -0
- package/dist/editor/theme.d.ts +2 -1
- package/dist/editor/theme.js +314 -13
- package/dist/editor/unanswered.d.ts +17 -0
- package/dist/editor/unanswered.js +26 -0
- package/dist/editor/unavailable-entry-type.d.ts +20 -0
- package/dist/editor/unavailable-entry-type.js +28 -0
- package/dist/editor/undo-removal.d.ts +57 -0
- package/dist/editor/undo-removal.js +163 -0
- package/dist/editor/user-display.d.ts +4 -0
- package/dist/editor/user-display.js +5 -0
- package/dist/editor/utils/confirm-modal.d.ts +8 -0
- package/dist/editor/utils/confirm-modal.js +12 -0
- package/dist/editor/utils/editor-errors.d.ts +19 -0
- package/dist/editor/utils/editor-errors.js +32 -0
- package/dist/editor/utils/focus-return.d.ts +56 -0
- package/dist/editor/utils/focus-return.js +106 -0
- package/dist/entry-link-resolver.d.ts +11 -1
- package/dist/entry-link-resolver.js +65 -5
- package/dist/entry-schema-registry.d.ts +3 -3
- package/dist/entry-schema-registry.js +7 -4
- package/dist/entry-schema.d.ts +49 -3
- package/dist/entry-schema.js +18 -0
- package/dist/git-manager.d.ts +172 -10
- package/dist/git-manager.js +449 -107
- package/dist/github-service.d.ts +39 -2
- package/dist/github-service.js +101 -7
- package/dist/http/handler.js +74 -9
- package/dist/http/types.d.ts +2 -0
- package/dist/http/worker-not-ready.d.ts +9 -4
- package/dist/http/worker-not-ready.js +59 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/item-keys.d.ts +46 -0
- package/dist/item-keys.js +235 -0
- package/dist/operating-mode/editor-mode-check.d.ts +15 -0
- package/dist/operating-mode/editor-mode-check.js +24 -0
- package/dist/operating-mode/mode-env.d.ts +13 -14
- package/dist/operating-mode/mode-env.js +13 -14
- package/dist/operating-mode/types.d.ts +2 -2
- package/dist/paths/branch.d.ts +0 -1
- package/dist/paths/branch.js +1 -8
- package/dist/paths/index.d.ts +1 -1
- package/dist/paths/index.js +1 -1
- package/dist/paths/validation.js +0 -2
- package/dist/preview.d.ts +8 -0
- package/dist/preview.js +7 -0
- package/dist/reference-resolver.js +1 -0
- package/dist/resolve-canopy-user.d.ts +1 -1
- package/dist/resolve-canopy-user.js +1 -1
- package/dist/schema/meta-loader.d.ts +18 -8
- package/dist/schema/meta-loader.js +42 -9
- package/dist/schema/registry-fingerprint.d.ts +14 -0
- package/dist/schema/registry-fingerprint.js +63 -0
- package/dist/schema/resolver.d.ts +7 -4
- package/dist/schema/resolver.js +7 -6
- package/dist/schema/schema-store.d.ts +7 -10
- package/dist/schema/schema-store.js +13 -11
- package/dist/schema/schema-unavailable-error.d.ts +11 -0
- package/dist/schema/schema-unavailable-error.js +15 -0
- package/dist/schema/types.d.ts +22 -0
- package/dist/schema-registry-record.d.ts +35 -0
- package/dist/schema-registry-record.js +134 -0
- package/dist/server.d.ts +79 -9
- package/dist/server.js +62 -8
- package/dist/services.d.ts +33 -5
- package/dist/services.js +155 -37
- package/dist/settings-workspace.d.ts +23 -3
- package/dist/settings-workspace.js +159 -45
- package/dist/stamp-on-publish.d.ts +36 -0
- package/dist/stamp-on-publish.js +71 -0
- package/dist/static/index.d.ts +58 -5
- package/dist/static/index.js +133 -3
- package/dist/static/seo.d.ts +5 -5
- package/dist/static/seo.js +5 -5
- package/dist/submission-attribution.d.ts +8 -0
- package/dist/submission-attribution.js +22 -0
- package/dist/system-users.d.ts +12 -0
- package/dist/system-users.js +12 -0
- package/dist/task-queue/cms-task-queue.d.ts +1 -1
- package/dist/task-queue/cms-task-queue.js +1 -1
- package/dist/task-queue/index.d.ts +1 -1
- package/dist/task-queue/index.js +1 -1
- package/dist/task-queue/task-queue.d.ts +6 -0
- package/dist/task-queue/task-queue.js +29 -0
- package/dist/task-queue/worker-status.d.ts +35 -9
- package/dist/task-queue/worker-status.js +84 -17
- package/dist/types.d.ts +90 -9
- package/dist/url-collision.js +3 -3
- package/dist/url-exclusivity-fixtures.d.ts +8 -1
- package/dist/url-exclusivity-fixtures.js +11 -1
- package/dist/user.js +2 -1
- package/dist/utils/atomic-write.js +2 -1
- package/dist/utils/base-branch.d.ts +13 -0
- package/dist/utils/base-branch.js +23 -0
- package/dist/utils/content-serialize.d.ts +15 -7
- package/dist/utils/content-serialize.js +353 -51
- package/dist/utils/content-write-lock.d.ts +6 -1
- package/dist/utils/content-write-lock.js +21 -6
- package/dist/utils/debug.d.ts +1 -1
- package/dist/utils/debug.js +5 -3
- package/dist/utils/error.js +13 -2
- package/dist/utils/escape-text.d.ts +7 -0
- package/dist/utils/escape-text.js +27 -0
- package/dist/utils/fs.d.ts +12 -4
- package/dist/utils/fs.js +18 -5
- package/dist/utils/git.d.ts +21 -1
- package/dist/utils/git.js +63 -8
- package/dist/utils/json-source-splice.d.ts +14 -0
- package/dist/utils/json-source-splice.js +120 -0
- package/dist/utils/markdown-body-splice.d.ts +39 -0
- package/dist/utils/markdown-body-splice.js +421 -0
- package/dist/utils/occ-json-write.js +18 -12
- package/dist/utils/provision-log.d.ts +25 -0
- package/dist/utils/provision-log.js +49 -0
- package/dist/utils/provisioning-lock.d.ts +32 -1
- package/dist/utils/provisioning-lock.js +78 -32
- package/dist/utils/sanitize-href.d.ts +9 -23
- package/dist/utils/sanitize-href.js +14 -28
- package/dist/utils/title-field.d.ts +10 -0
- package/dist/utils/title-field.js +47 -0
- package/dist/utils/yaml-source-splice.d.ts +58 -0
- package/dist/utils/yaml-source-splice.js +515 -0
- package/dist/validation/block-structural-keys.d.ts +1 -1
- package/dist/validation/block-structural-keys.js +1 -1
- package/dist/validation/deletion-checker.d.ts +21 -29
- package/dist/validation/deletion-checker.js +41 -117
- package/dist/validation/entry-validator.d.ts +6 -10
- package/dist/validation/entry-validator.js +19 -26
- package/dist/validation/field-traversal.d.ts +22 -0
- package/dist/validation/field-traversal.js +39 -8
- package/dist/validation/markdown-safety.d.ts +68 -0
- package/dist/validation/markdown-safety.js +596 -0
- package/dist/validation/mdx-allowlist.d.ts +55 -0
- package/dist/validation/mdx-allowlist.js +183 -0
- package/dist/validation/reference-validator.d.ts +2 -0
- package/dist/validation/reference-validator.js +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/branch-policy.d.ts +124 -0
- package/dist/worker/branch-policy.js +243 -0
- package/dist/worker/bundle-header.d.ts +117 -0
- package/dist/worker/bundle-header.js +253 -0
- package/dist/worker/cms-worker.d.ts +236 -142
- package/dist/worker/cms-worker.js +772 -285
- package/dist/worker/gateway-egress.d.ts +43 -0
- package/dist/worker/gateway-egress.js +209 -0
- package/dist/worker/gateway-instance.d.ts +42 -0
- package/dist/worker/gateway-instance.js +129 -0
- package/dist/worker/git-process.d.ts +81 -0
- package/dist/worker/git-process.js +234 -0
- package/dist/worker/git-sync.d.ts +34 -17
- package/dist/worker/git-sync.js +394 -131
- package/dist/worker/github-auth.d.ts +9 -9
- package/dist/worker/github-auth.js +13 -15
- package/dist/worker/github-gateway-http.d.ts +62 -0
- package/dist/worker/github-gateway-http.js +977 -0
- package/dist/worker/github-gateway-wire.d.ts +590 -0
- package/dist/worker/github-gateway-wire.js +488 -0
- package/dist/worker/github-gateway.d.ts +238 -0
- package/dist/worker/github-gateway.js +726 -0
- package/dist/worker/github-mirror-upkeep.d.ts +75 -0
- package/dist/worker/github-mirror-upkeep.js +266 -0
- package/dist/worker/github-mirror.d.ts +364 -0
- package/dist/worker/github-mirror.js +1030 -0
- package/dist/worker/github-transfer.d.ts +56 -0
- package/dist/worker/github-transfer.js +444 -0
- package/dist/worker/history-rewrite.d.ts +4 -5
- package/dist/worker/history-rewrite.js +12 -17
- package/dist/worker/provisioned-workspace.d.ts +5 -5
- package/dist/worker/provisioned-workspace.js +5 -5
- package/dist/worker/rebase.d.ts +1 -1
- package/dist/worker/rebase.js +58 -54
- package/dist/worker/remote-git-maintenance.d.ts +3 -0
- package/dist/worker/remote-git-maintenance.js +12 -0
- package/dist/worker/schema-gate.d.ts +49 -0
- package/dist/worker/schema-gate.js +226 -0
- package/dist/worker/self-checks.d.ts +107 -0
- package/dist/worker/self-checks.js +358 -0
- package/dist/worker/shared-repo-git.d.ts +96 -0
- package/dist/worker/shared-repo-git.js +459 -0
- package/dist/worker/sparse-cone.d.ts +20 -0
- package/dist/worker/sparse-cone.js +136 -0
- package/dist/worker/stale-mirror.d.ts +82 -0
- package/dist/worker/stale-mirror.js +130 -0
- package/dist/worker/task-runner.d.ts +5 -3
- package/dist/worker/task-runner.js +287 -186
- package/dist/worker/worker-context.d.ts +36 -37
- package/package.json +35 -7
- package/dist/editor/EditorPanes.d.ts +0 -10
- package/dist/editor/EditorPanes.js +0 -98
- package/dist/editor/components/EditorFooter.d.ts +0 -11
- package/dist/editor/components/EditorFooter.js +0 -9
- package/dist/editor/components/EditorSidebar.d.ts +0 -67
- package/dist/editor/components/EditorSidebar.js +0 -22
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { BranchMetadataFileManager, getBranchMetadataFileManager } from '../branch-metadata.js';
|
|
2
|
+
import { completeTask, dequeueTask, failTask, recoverOrphanedTasks, releaseTask, retryTask, } from '../task-queue/cms-task-queue.js';
|
|
3
|
+
import { isNoCommitsBetweenError, isRefAlreadyGoneError } from '../github-service.js';
|
|
4
|
+
import { BranchMetadataCorruptError, BranchMetadataFileManager, getBranchMetadataFileManager, } from '../branch-metadata.js';
|
|
6
5
|
import { sanitizeBranchName, RESERVED_SETTINGS_BRANCH_PREFIX } from '../paths/branch-name.js';
|
|
7
|
-
import { gitNetworkChildEnv } from '../git-manager.js';
|
|
8
6
|
import { getErrorMessage, redactCredentials } from '../utils/error.js';
|
|
9
|
-
import { isNonFastForwardRejection,
|
|
7
|
+
import { isNonFastForwardRejection, workflowPushRefusalFile } from '../utils/git.js';
|
|
8
|
+
import { GitHubPushError, describeMove } from './github-gateway.js';
|
|
9
|
+
import { GatewayRefusalError } from './branch-policy.js';
|
|
10
|
+
import { GatewayRequestRefusedError, GitHubGatewayStoppingError, GitHubGatewayUnreachableError, } from './github-gateway-wire.js';
|
|
11
|
+
import { RefusedPushError, assertPlainBranchName } from './github-mirror.js';
|
|
12
|
+
import { pushToGitHub } from './github-transfer.js';
|
|
10
13
|
import { clearHistoryRewrittenMarker, readPublishedSha } from './history-rewrite.js';
|
|
11
14
|
import { writeWorkerStatus } from '../task-queue/worker-status.js';
|
|
12
|
-
import { workerLog, workerLogError } from './log.js';
|
|
15
|
+
import { workerLog, workerLogError, workerLogWarn } from './log.js';
|
|
16
|
+
import { assertSharedRepoConfig } from './shared-repo-git.js';
|
|
13
17
|
/**
|
|
14
18
|
* An error inherent to the task itself (malformed payload, unknown action):
|
|
15
19
|
* retrying can never succeed, so the task should fail fast instead of
|
|
@@ -17,6 +21,19 @@ import { workerLog, workerLogError } from './log.js';
|
|
|
17
21
|
*/
|
|
18
22
|
export class PermanentTaskError extends Error {
|
|
19
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* The attempt was cut off by a draining `stop()` reaching its deadline, not by
|
|
26
|
+
* anything wrong with the task: it goes back to pending with no retry spent.
|
|
27
|
+
*/
|
|
28
|
+
class TaskAbortedForShutdownError extends Error {
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* GitHub refused a submit's PR because the pushed branch has no commits its base lacks: the API's
|
|
32
|
+
* own check found changes against an older base, or could not run. The failure handler returns
|
|
33
|
+
* the branch to editing if it is still in that submit, since nothing is under review.
|
|
34
|
+
*/
|
|
35
|
+
class NothingToSubmitTaskError extends PermanentTaskError {
|
|
36
|
+
}
|
|
20
37
|
/**
|
|
21
38
|
* Classify a task failure as permanent (fail fast) or transient (retry).
|
|
22
39
|
*
|
|
@@ -35,12 +52,17 @@ export class PermanentTaskError extends Error {
|
|
|
35
52
|
*
|
|
36
53
|
* Permanent — retrying the identical request cannot succeed:
|
|
37
54
|
* - PermanentTaskError (malformed payload, unknown action);
|
|
55
|
+
* - GatewayRefusalError: the GitHub gateway's policy refused it (worker/branch-policy.ts);
|
|
56
|
+
* - GatewayRequestRefusedError: the gateway's socket refused it for its shape or size;
|
|
38
57
|
* - other HTTP 4xx (401/404/422): the request itself is bad;
|
|
39
58
|
* - plain HTTP 403 with no rate-limit signal: a real permission denial.
|
|
40
59
|
*/
|
|
41
60
|
export function isPermanentTaskFailure(err) {
|
|
42
|
-
if (err instanceof PermanentTaskError
|
|
61
|
+
if (err instanceof PermanentTaskError ||
|
|
62
|
+
err instanceof GatewayRefusalError ||
|
|
63
|
+
err instanceof GatewayRequestRefusedError) {
|
|
43
64
|
return true;
|
|
65
|
+
}
|
|
44
66
|
const status = getHttpStatus(err);
|
|
45
67
|
if (status === null)
|
|
46
68
|
return false;
|
|
@@ -151,7 +173,10 @@ export async function processTaskQueue(ctx) {
|
|
|
151
173
|
}
|
|
152
174
|
let processed = 0;
|
|
153
175
|
let task;
|
|
176
|
+
// Checked before every claim: a draining worker finishes the task it holds
|
|
177
|
+
// and claims no other.
|
|
154
178
|
while (processed < ctx.maxTasksPerCycle &&
|
|
179
|
+
!ctx.isDraining() &&
|
|
155
180
|
(task = await dequeueTask(ctx.taskDir, ctx.log)) !== null) {
|
|
156
181
|
try {
|
|
157
182
|
const result = await executeTaskWithTimeout(ctx, task);
|
|
@@ -159,12 +184,27 @@ export async function processTaskQueue(ctx) {
|
|
|
159
184
|
await updateBranchMetadata(ctx, task, result);
|
|
160
185
|
}
|
|
161
186
|
catch (err) {
|
|
187
|
+
// A stop, not the task, is why these failed, so the task goes back to pending with no retry
|
|
188
|
+
// spent, as after a drain's abort: the gateway draining, or out of reach while this worker
|
|
189
|
+
// drains (its host going away).
|
|
190
|
+
const gatewayStopping = err instanceof GitHubGatewayStoppingError;
|
|
191
|
+
const gatewayGone = ctx.isDraining() && err instanceof GitHubGatewayUnreachableError;
|
|
192
|
+
if (err instanceof TaskAbortedForShutdownError || gatewayStopping || gatewayGone) {
|
|
193
|
+
// It runs again from the start, exactly as a retry after a timed-out attempt does.
|
|
194
|
+
await releaseTask(ctx.taskDir, task.id, ctx.log);
|
|
195
|
+
workerLogWarn(gatewayStopping
|
|
196
|
+
? `The GitHub gateway is stopping; released task ${task.id} (${task.action}) to pending`
|
|
197
|
+
: gatewayGone
|
|
198
|
+
? `The GitHub gateway is out of reach while the worker drains; released task ${task.id} (${task.action}) to pending for the next worker`
|
|
199
|
+
: `Drain deadline hit, aborted task ${task.id} (${task.action}); released to pending for the next worker`);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
162
202
|
const message = getErrorMessage(err);
|
|
163
203
|
workerLogError(`Task ${task.id} (${task.action}) failed:`, message);
|
|
164
204
|
// [REDACT] task.error is persisted (pending/failed task JSON) and served
|
|
165
|
-
// to the browser by the admin panel's Tasks tab
|
|
166
|
-
// message
|
|
167
|
-
//
|
|
205
|
+
// to the browser by the admin panel's Tasks tab, so a credential in any
|
|
206
|
+
// failure's message must not reach it. Console output above stays raw
|
|
207
|
+
// (journald/CloudWatch is trusted).
|
|
168
208
|
const persistedMessage = redactCredentials(message);
|
|
169
209
|
// DEP-L1: only transient failures are worth retrying; a permanent one
|
|
170
210
|
// would burn the retry budget on an identical doomed request.
|
|
@@ -177,21 +217,13 @@ export async function processTaskQueue(ctx) {
|
|
|
177
217
|
}
|
|
178
218
|
else {
|
|
179
219
|
await failTask(ctx.taskDir, task.id, persistedMessage, ctx.log);
|
|
180
|
-
await updateBranchMetadataOnFailure(ctx, task, persistedMessage
|
|
220
|
+
await updateBranchMetadataOnFailure(ctx, task, persistedMessage, {
|
|
221
|
+
unlock: err instanceof NothingToSubmitTaskError,
|
|
222
|
+
});
|
|
181
223
|
workerLogError(permanent
|
|
182
224
|
? ' Permanently failed (non-retryable error)'
|
|
183
225
|
: ` Permanently failed after ${maxRetries} retries`);
|
|
184
226
|
}
|
|
185
|
-
// The credential may have rotated, and the next retry resolves
|
|
186
|
-
// buildGitHubUrl() afresh to pick a new token up. Without this a push
|
|
187
|
-
// meeting a revoked token spends its whole retry budget (5s/10s/20s
|
|
188
|
-
// backoff) waiting on the git-sync loop's refresh up to 5 minutes away,
|
|
189
|
-
// and fails permanently while the working token sits in the secret store.
|
|
190
|
-
//
|
|
191
|
-
// Ungated, and AFTER the outcome is recorded rather than before: the task
|
|
192
|
-
// is safely in pending/ or failed/ while a network read runs, and that
|
|
193
|
-
// read is bounded and never throws. See CmsWorker.refreshGitHubCredential.
|
|
194
|
-
await ctx.refreshGitHubCredential();
|
|
195
227
|
}
|
|
196
228
|
processed++;
|
|
197
229
|
}
|
|
@@ -206,31 +238,38 @@ export async function processTaskQueue(ctx) {
|
|
|
206
238
|
}
|
|
207
239
|
}
|
|
208
240
|
/**
|
|
209
|
-
* Execute a task bounded by taskTimeoutMs (DEP-H1)
|
|
210
|
-
*
|
|
211
|
-
* -
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
241
|
+
* Execute a task bounded by taskTimeoutMs (DEP-H1) and by the worker's
|
|
242
|
+
* shutdown signal. Two layers:
|
|
243
|
+
* - an AbortSignal cancels Octokit HTTP calls and kills the push's git process
|
|
244
|
+
* (pushBranchToGitHub passes it to simple-git's `abort`);
|
|
245
|
+
* - a Promise.race rejects when either fires, so work that cannot observe the
|
|
246
|
+
* signal (a hung credential resolution in the gateway's push) still ends
|
|
247
|
+
* the attempt and the worker moves on instead of stalling forever.
|
|
215
248
|
*
|
|
216
|
-
* A raced-out resolution is not
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
* and are not bounded by taskTimeoutMs at all. pushBranchToGitHub additionally
|
|
220
|
-
* kills stalled git processes via simple-git's block timeout.
|
|
249
|
+
* A raced-out credential resolution is not cancelled. On the App path the mint
|
|
250
|
+
* is separately bounded by gitTokenMintTimeoutMs, and git-sync.ts's two
|
|
251
|
+
* resolutions run on the sync loop and are not bounded by taskTimeoutMs at all.
|
|
221
252
|
*/
|
|
222
253
|
async function executeTaskWithTimeout(ctx, task) {
|
|
223
254
|
const controller = new AbortController();
|
|
224
255
|
const timer = setTimeout(() => controller.abort(), ctx.taskTimeoutMs);
|
|
256
|
+
const shutdown = ctx.shutdownSignal();
|
|
257
|
+
const signal = AbortSignal.any([controller.signal, shutdown]);
|
|
225
258
|
try {
|
|
226
|
-
const work = ctx.executeTask(task,
|
|
227
|
-
// If the
|
|
259
|
+
const work = ctx.executeTask(task, signal);
|
|
260
|
+
// If the abort wins the race, the losing promise must not surface an
|
|
228
261
|
// unhandled rejection when it eventually settles.
|
|
229
262
|
work.catch(() => { });
|
|
230
|
-
const
|
|
231
|
-
|
|
263
|
+
const aborted = new Promise((_, reject) => {
|
|
264
|
+
const onAbort = () => reject(shutdown.aborted
|
|
265
|
+
? new TaskAbortedForShutdownError(`Task aborted: the worker is shutting down`)
|
|
266
|
+
: new Error(`Task timed out after ${ctx.taskTimeoutMs}ms`));
|
|
267
|
+
if (signal.aborted)
|
|
268
|
+
onAbort();
|
|
269
|
+
else
|
|
270
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
232
271
|
});
|
|
233
|
-
return await Promise.race([work,
|
|
272
|
+
return await Promise.race([work, aborted]);
|
|
234
273
|
}
|
|
235
274
|
finally {
|
|
236
275
|
clearTimeout(timer);
|
|
@@ -241,36 +280,29 @@ export async function executeTask(ctx, task, signal) {
|
|
|
241
280
|
switch (action) {
|
|
242
281
|
case 'push-branch': {
|
|
243
282
|
const branch = requireString(payload, 'branch');
|
|
244
|
-
await ctx.pushBranchToGitHub(branch);
|
|
283
|
+
await ctx.pushBranchToGitHub(branch, signal);
|
|
245
284
|
return { pushed: true };
|
|
246
285
|
}
|
|
247
286
|
case 'push-and-create-pr': {
|
|
248
287
|
const branch = requireString(payload, 'branch');
|
|
249
|
-
await ctx.pushBranchToGitHub(branch);
|
|
250
|
-
const pr = await ctx.
|
|
251
|
-
owner: ctx.githubOwner,
|
|
252
|
-
repo: ctx.githubRepo,
|
|
288
|
+
await ctx.pushBranchToGitHub(branch, signal);
|
|
289
|
+
const pr = await ctx.github().createPullRequest({
|
|
253
290
|
head: branch,
|
|
254
291
|
base: optionalString(payload, 'baseBranch', ctx.baseBranch),
|
|
255
292
|
title: optionalString(payload, 'title', `Submit ${branch}`),
|
|
256
293
|
body: optionalString(payload, 'body', ''),
|
|
257
|
-
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
return { prUrl: pr.data.html_url, prNumber: pr.data.number };
|
|
294
|
+
}, signal);
|
|
295
|
+
workerLog(`Created PR #${pr.number} for ${branch}`);
|
|
296
|
+
return { prUrl: pr.url, prNumber: pr.number };
|
|
261
297
|
}
|
|
262
298
|
case 'push-and-update-pr': {
|
|
263
299
|
const branch = requireString(payload, 'branch');
|
|
264
300
|
const prNumber = requireNumber(payload, 'pullRequestNumber');
|
|
265
|
-
await ctx.pushBranchToGitHub(branch);
|
|
266
|
-
await ctx.
|
|
267
|
-
owner: ctx.githubOwner,
|
|
268
|
-
repo: ctx.githubRepo,
|
|
269
|
-
pull_number: prNumber,
|
|
301
|
+
await ctx.pushBranchToGitHub(branch, signal);
|
|
302
|
+
await ctx.github().updatePullRequest(prNumber, {
|
|
270
303
|
title: optionalString(payload, 'title', `Submit ${branch}`),
|
|
271
304
|
body: optionalString(payload, 'body', ''),
|
|
272
|
-
|
|
273
|
-
});
|
|
305
|
+
}, signal);
|
|
274
306
|
workerLog(`Updated PR #${prNumber} for ${branch}`);
|
|
275
307
|
return { prNumber };
|
|
276
308
|
}
|
|
@@ -294,24 +326,29 @@ export async function executeTask(ctx, task, signal) {
|
|
|
294
326
|
// The settings branch is an orphan with no history in common with the
|
|
295
327
|
// base, so GitHub 422s a PR for it: push it and stop.
|
|
296
328
|
if (isSettingsBranch(branch)) {
|
|
297
|
-
await ctx.pushBranchToGitHub(branch);
|
|
298
|
-
workerLog(`
|
|
329
|
+
await ctx.pushBranchToGitHub(branch, signal);
|
|
330
|
+
workerLog(`No PR for ${branch}: settings branches never get one`);
|
|
299
331
|
return { pushed: true };
|
|
300
332
|
}
|
|
301
|
-
await ctx.pushBranchToGitHub(branch);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
333
|
+
await ctx.pushBranchToGitHub(branch, signal);
|
|
334
|
+
let result;
|
|
335
|
+
try {
|
|
336
|
+
result = await ctx.github().createOrUpdatePullRequest({
|
|
337
|
+
head: branch,
|
|
338
|
+
base,
|
|
339
|
+
title: optionalString(payload, 'title', `Submit ${branch}`),
|
|
340
|
+
body: optionalString(payload, 'body', ''),
|
|
341
|
+
// Content submits (api/github-sync.ts) set both.
|
|
342
|
+
markReadyIfDraft: payload.markReadyIfDraft === true,
|
|
343
|
+
mergeSectionIntoBody: payload.mergeSectionIntoBody === true,
|
|
344
|
+
}, signal);
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
if (!isNoCommitsBetweenError(err))
|
|
348
|
+
throw err;
|
|
349
|
+
throw new NothingToSubmitTaskError(`Nothing was submitted: "${branch}" has no changes compared with "${base}", so GitHub ` +
|
|
350
|
+
'opened no pull request.');
|
|
351
|
+
}
|
|
315
352
|
workerLog(result.created
|
|
316
353
|
? `Created PR #${result.number} for ${branch}`
|
|
317
354
|
: `Updated existing PR #${result.number} for ${branch}`);
|
|
@@ -319,50 +356,83 @@ export async function executeTask(ctx, task, signal) {
|
|
|
319
356
|
}
|
|
320
357
|
case 'convert-to-draft': {
|
|
321
358
|
const draftPrNumber = requireNumber(payload, 'pullRequestNumber');
|
|
322
|
-
|
|
323
|
-
// Use the GraphQL API via Octokit.
|
|
324
|
-
const { data: pr } = await ctx.octokit().pulls.get({
|
|
325
|
-
owner: ctx.githubOwner,
|
|
326
|
-
repo: ctx.githubRepo,
|
|
327
|
-
pull_number: draftPrNumber,
|
|
328
|
-
request: { signal },
|
|
329
|
-
});
|
|
330
|
-
await ctx
|
|
331
|
-
.octokit()
|
|
332
|
-
.graphql(`mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, { id: pr.node_id, request: { signal } });
|
|
359
|
+
await ctx.github().convertPullRequestToDraft(draftPrNumber, signal);
|
|
333
360
|
workerLog(`Converted PR #${draftPrNumber} to draft`);
|
|
334
361
|
return { prNumber: draftPrNumber, draft: true };
|
|
335
362
|
}
|
|
336
363
|
case 'close-pr': {
|
|
337
364
|
const closePrNumber = requireNumber(payload, 'pullRequestNumber');
|
|
338
|
-
await ctx.
|
|
339
|
-
owner: ctx.githubOwner,
|
|
340
|
-
repo: ctx.githubRepo,
|
|
341
|
-
pull_number: closePrNumber,
|
|
342
|
-
state: 'closed',
|
|
343
|
-
request: { signal },
|
|
344
|
-
});
|
|
365
|
+
await ctx.github().updatePullRequest(closePrNumber, { state: 'closed' }, signal);
|
|
345
366
|
return { closed: true };
|
|
346
367
|
}
|
|
347
368
|
case 'delete-remote-branch': {
|
|
348
369
|
const branch = requireString(payload, 'branch');
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
370
|
+
// Defense-in-depth behind the delete handler's own refusal.
|
|
371
|
+
if (sanitizeBranchName(branch) === ctx.sanitizedBaseBranch || isSettingsBranch(branch)) {
|
|
372
|
+
throw new PermanentTaskError(`Refusing to delete "${branch}" on GitHub: the base and settings branches are never deleted`);
|
|
373
|
+
}
|
|
374
|
+
// A branch that reused the name after this task was queued (a requeued task can run long
|
|
375
|
+
// after) owns the GitHub branch once it recorded a different PR or GitHub push. Without
|
|
376
|
+
// either, the ref is taken to be the deleted branch's. Corrupt metadata, which includes a
|
|
377
|
+
// file with no branch record, keeps the GitHub branch; other read errors retry.
|
|
378
|
+
const deletedPr = typeof payload.pullRequestNumber === 'number' ? payload.pullRequestNumber : undefined;
|
|
379
|
+
const deletedPushedAt = typeof payload.pushedToGitHubAt === 'string' ? payload.pushedToGitHubAt : undefined;
|
|
380
|
+
let live = null;
|
|
381
|
+
let unreadable = false;
|
|
382
|
+
try {
|
|
383
|
+
live = await BranchMetadataFileManager.loadOnly(ctx.branchWorkspacePath(branch));
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
if (!(err instanceof BranchMetadataCorruptError))
|
|
387
|
+
throw err;
|
|
388
|
+
unreadable = true;
|
|
389
|
+
}
|
|
390
|
+
if (unreadable) {
|
|
391
|
+
workerLog(`Not deleting GitHub branch ${branch}: the branch now under that name is unreadable`);
|
|
392
|
+
return { deleted: false, skipped: 'metadata-unreadable' };
|
|
393
|
+
}
|
|
394
|
+
const livePr = live?.branch.pullRequestNumber;
|
|
395
|
+
if (livePr !== undefined && livePr !== deletedPr) {
|
|
396
|
+
workerLog(`Not deleting GitHub branch ${branch}: a newer branch's PR #${livePr} uses it`);
|
|
397
|
+
return { deleted: false, skipped: 'name-reused' };
|
|
398
|
+
}
|
|
399
|
+
const livePushedAt = live?.branch.pushedToGitHubAt;
|
|
400
|
+
if (livePushedAt !== undefined && livePushedAt !== deletedPushedAt) {
|
|
401
|
+
workerLog(`Not deleting GitHub branch ${branch}: a newer branch pushed under that name`);
|
|
402
|
+
return { deleted: false, skipped: 'name-reused' };
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
await ctx.github().deleteBranch(branch, signal);
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
// Already gone is the outcome this task wants; failing it would only park it in failed/.
|
|
409
|
+
if (!isRefAlreadyGoneError(err))
|
|
410
|
+
throw err;
|
|
411
|
+
workerLog(`GitHub branch ${branch} was already deleted`);
|
|
412
|
+
return { deleted: false, alreadyGone: true };
|
|
413
|
+
}
|
|
414
|
+
workerLog(`Deleted GitHub branch ${branch}`);
|
|
355
415
|
return { deleted: true };
|
|
356
416
|
}
|
|
357
417
|
default:
|
|
358
418
|
throw new PermanentTaskError(`Unknown task action: ${action}`);
|
|
359
419
|
}
|
|
360
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* The branch whose metadata records a task's outcome, or null when none does. A
|
|
423
|
+
* delete-remote-branch task names a branch already deleted here, so a workspace under that
|
|
424
|
+
* name is leftover or a newer branch's that reused the name.
|
|
425
|
+
*/
|
|
426
|
+
function metadataBranchOf(task) {
|
|
427
|
+
if (task.action === 'delete-remote-branch')
|
|
428
|
+
return null;
|
|
429
|
+
return typeof task.payload.branch === 'string' ? task.payload.branch : null;
|
|
430
|
+
}
|
|
361
431
|
/**
|
|
362
432
|
* Update branch metadata after successful task completion.
|
|
363
433
|
*/
|
|
364
434
|
export async function updateBranchMetadata(ctx, task, result) {
|
|
365
|
-
const branch =
|
|
435
|
+
const branch = metadataBranchOf(task);
|
|
366
436
|
if (!branch)
|
|
367
437
|
return;
|
|
368
438
|
const branchPath = ctx.branchWorkspacePath(branch);
|
|
@@ -409,9 +479,14 @@ export async function updateBranchMetadata(ctx, task, result) {
|
|
|
409
479
|
* Update branch metadata after permanent task failure. `error` is already
|
|
410
480
|
* redacted by the caller (see [REDACT] in processTaskQueue) and is recorded as
|
|
411
481
|
* syncFailureReason so the editor can show WHY, not just that it failed.
|
|
482
|
+
*
|
|
483
|
+
* `unlock` also returns the branch to 'editing', but only while it is still in
|
|
484
|
+
* the submit this task carries (its `submittedAt`), 'submitted' with no PR: a
|
|
485
|
+
* withdraw, a newer submit, or a review decision that landed meanwhile is left
|
|
486
|
+
* alone.
|
|
412
487
|
*/
|
|
413
|
-
async function updateBranchMetadataOnFailure(ctx, task, error) {
|
|
414
|
-
const branch =
|
|
488
|
+
async function updateBranchMetadataOnFailure(ctx, task, error, options) {
|
|
489
|
+
const branch = metadataBranchOf(task);
|
|
415
490
|
if (!branch)
|
|
416
491
|
return;
|
|
417
492
|
const branchPath = ctx.branchWorkspacePath(branch);
|
|
@@ -423,9 +498,21 @@ async function updateBranchMetadataOnFailure(ctx, task, error) {
|
|
|
423
498
|
}
|
|
424
499
|
try {
|
|
425
500
|
const meta = getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
501
|
+
const failed = { name: branch, syncStatus: 'sync-failed', syncFailureReason: error };
|
|
502
|
+
const submittedAt = task.payload.submittedAt;
|
|
503
|
+
const unlocked = options.unlock &&
|
|
504
|
+
typeof submittedAt === 'string' &&
|
|
505
|
+
(await meta.saveIf({
|
|
506
|
+
branch: {
|
|
507
|
+
...failed,
|
|
508
|
+
status: 'editing',
|
|
509
|
+
syncFailureReason: `${error} The branch is unlocked for editing; save a change, then submit again.`,
|
|
510
|
+
},
|
|
511
|
+
}, (existing) => existing?.branch.status === 'submitted' &&
|
|
512
|
+
existing.branch.pullRequestNumber === undefined &&
|
|
513
|
+
existing.branch.submittedAt === submittedAt)) !== null;
|
|
514
|
+
if (!unlocked)
|
|
515
|
+
await meta.save({ branch: failed });
|
|
429
516
|
}
|
|
430
517
|
catch (err) {
|
|
431
518
|
workerLogError(`Failed to update failure metadata for ${branch}:`, err instanceof Error ? err.message : err);
|
|
@@ -448,37 +535,28 @@ function throwIfWorkflowRefusal(branch, message) {
|
|
|
448
535
|
`retrying will not help until a developer with permission to change workflow files ` +
|
|
449
536
|
`resolves it on GitHub.`);
|
|
450
537
|
}
|
|
451
|
-
export async function pushBranchToGitHub(ctx, branch) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
// genuinely diverged branch would be retried instead of raising
|
|
474
|
-
// PermanentTaskError;
|
|
475
|
-
// - readPublishedSha below captures remote.git's tip BEFORE the push, and
|
|
476
|
-
// that value decides whether the [SYNC-H1] marker is cleared at the end.
|
|
477
|
-
// An awaited resolution in between widens the window in which the tip can
|
|
478
|
-
// move underneath that decision.
|
|
479
|
-
//
|
|
480
|
-
// It also means all three pushes provably carry the same credential.
|
|
481
|
-
const githubUrl = await ctx.buildGitHubUrl();
|
|
538
|
+
export async function pushBranchToGitHub(ctx, branch, signal) {
|
|
539
|
+
// The credential reaches git only in the GitHub gateway's state directory, its mirror or a
|
|
540
|
+
// quarantine there (worker/github-mirror.ts); remote.git's config is Lambda-writable, and the
|
|
541
|
+
// bundle built here runs under the pins. The push sends exactly `outgoingSha`, the commit remote.git held when it
|
|
542
|
+
// was read, so what the marker logic at the end compares is what GitHub received.
|
|
543
|
+
await assertSharedRepoConfig(ctx.remoteGitPath, 'bare');
|
|
544
|
+
// Before `branch` reaches any git: `readPublishedSha` below would read a `<rev>:<path>` form as a
|
|
545
|
+
// tree lookup in remote.git. A refusal is permanent; a failure to run git is retried.
|
|
546
|
+
try {
|
|
547
|
+
await assertPlainBranchName(branch);
|
|
548
|
+
}
|
|
549
|
+
catch (err) {
|
|
550
|
+
if (err instanceof RefusedPushError)
|
|
551
|
+
throw new PermanentTaskError(err.message);
|
|
552
|
+
throw err;
|
|
553
|
+
}
|
|
554
|
+
// Defense-in-depth behind the gateway's policy, which protects a base detected rather than
|
|
555
|
+
// configured only when it is GitHub's default branch (BranchPolicy).
|
|
556
|
+
if (sanitizeBranchName(branch) === ctx.sanitizedBaseBranch) {
|
|
557
|
+
throw new PermanentTaskError(`Refusing to push "${branch}" to GitHub: it is the base branch, which the CanopyCMS worker ` +
|
|
558
|
+
`never pushes. Nothing in CanopyCMS queues such a push; find out what wrote this task.`);
|
|
559
|
+
}
|
|
482
560
|
// [SYNC-H1] If the rebase loop rewrote this branch's already-published
|
|
483
561
|
// history, GitHub still holds the commit it replaced, so an ordinary
|
|
484
562
|
// push is non-fast-forward forever. Push under a lease keyed to exactly
|
|
@@ -487,61 +565,41 @@ export async function pushBranchToGitHub(ctx, branch) {
|
|
|
487
565
|
const branchPath = ctx.branchWorkspacePath(branch);
|
|
488
566
|
const metaFile = await BranchMetadataFileManager.loadOnly(branchPath).catch(() => null);
|
|
489
567
|
const marker = metaFile?.branch.historyRewrittenFrom;
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
await git.raw([
|
|
495
|
-
'push',
|
|
496
|
-
`--force-with-lease=${branch}:${marker}`,
|
|
497
|
-
'--end-of-options',
|
|
498
|
-
githubUrl,
|
|
499
|
-
`${branch}:${branch}`,
|
|
500
|
-
]);
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
// Pass URL directly to avoid persisting the token in remote.git/config
|
|
504
|
-
await git.push(githubUrl, branch);
|
|
568
|
+
const readSha = async () => {
|
|
569
|
+
const sha = await readPublishedSha(ctx, branch);
|
|
570
|
+
if (sha === null) {
|
|
571
|
+
throw new Error(`Branch "${branch}" is not in remote.git, so there is nothing to push`);
|
|
505
572
|
}
|
|
573
|
+
return sha;
|
|
574
|
+
};
|
|
575
|
+
// The commit GitHub was sent: the transfer reads it again if the branch moves under it.
|
|
576
|
+
let outgoingSha;
|
|
577
|
+
let outcome;
|
|
578
|
+
try {
|
|
579
|
+
;
|
|
580
|
+
({ sha: outgoingSha, outcome } = await pushToGitHub(ctx, {
|
|
581
|
+
branch,
|
|
582
|
+
lease: marker,
|
|
583
|
+
prerequisiteBranches: [branch, ctx.baseBranch],
|
|
584
|
+
}, readSha, signal));
|
|
506
585
|
}
|
|
507
586
|
catch (err) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
//
|
|
515
|
-
// fast-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
// (git evaluates the lease only when it actually has an update to apply,
|
|
523
|
-
// so an up-to-date ref with a stale lease prints "Everything up-to-date",
|
|
524
|
-
// exits 0, and is absorbed above without reaching here.)
|
|
525
|
-
if (marker && isStaleLeaseRejection(message)) {
|
|
526
|
-
try {
|
|
527
|
-
await git.push(githubUrl, branch);
|
|
528
|
-
}
|
|
529
|
-
catch (retryErr) {
|
|
530
|
-
const retryMessage = getErrorMessage(retryErr);
|
|
531
|
-
throwIfWorkflowRefusal(branch, retryMessage);
|
|
532
|
-
if (isNonFastForwardRejection(retryMessage)) {
|
|
533
|
-
throw new PermanentTaskError(`Push rejected for branch "${branch}": GitHub's tip is neither the commit this ` +
|
|
534
|
-
`deployment last published nor an ancestor of what it is pushing, so the branch ` +
|
|
535
|
-
`has genuinely diverged and nothing was overwritten. Something else moved it on ` +
|
|
536
|
-
`GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`);
|
|
537
|
-
}
|
|
538
|
-
throw retryErr;
|
|
587
|
+
// A retry can never make the branch not protected.
|
|
588
|
+
if (err instanceof RefusedPushError)
|
|
589
|
+
throw new PermanentTaskError(err.message);
|
|
590
|
+
if (!(err instanceof GitHubPushError))
|
|
591
|
+
throw err;
|
|
592
|
+
throwIfWorkflowRefusal(branch, err.message);
|
|
593
|
+
// A refused lease was already retried plain, and that retry rejected too: only a
|
|
594
|
+
// non-fast-forward then means the branch has genuinely diverged.
|
|
595
|
+
if (err.kind === 'rejected-after-stale-lease') {
|
|
596
|
+
if (isNonFastForwardRejection(err.message)) {
|
|
597
|
+
throw new PermanentTaskError(`Push rejected for branch "${branch}": GitHub's tip is neither the commit this ` +
|
|
598
|
+
`deployment last published nor an ancestor of what it is pushing, so the branch ` +
|
|
599
|
+
`has genuinely diverged and nothing was overwritten. Something else moved it on ` +
|
|
600
|
+
`GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`);
|
|
539
601
|
}
|
|
540
|
-
|
|
541
|
-
// has moved past the rewritten commit and the marker is spent.
|
|
542
|
-
await clearHistoryRewrittenMarker(ctx, branchPath, branch);
|
|
543
|
-
workerLog(`Pushed ${branch} to GitHub (GitHub had already moved past the rewritten commit)`);
|
|
544
|
-
return;
|
|
602
|
+
throw new Error(err.message);
|
|
545
603
|
}
|
|
546
604
|
// An ordinary non-fast-forward rejection: GitHub has commits this
|
|
547
605
|
// deployment never published, so retrying the identical push can never
|
|
@@ -549,19 +607,62 @@ export async function pushBranchToGitHub(ctx, branch) {
|
|
|
549
607
|
// it fails fast instead of burning the retry budget. Deliberately does NOT
|
|
550
608
|
// advise renaming the branch: one reaching this point usually has an open
|
|
551
609
|
// PR, which renaming would orphan.
|
|
552
|
-
if (isNonFastForwardRejection(message)) {
|
|
610
|
+
if (isNonFastForwardRejection(err.message)) {
|
|
553
611
|
throw new PermanentTaskError(`Push rejected for branch "${branch}": GitHub's tip is not what this deployment last ` +
|
|
554
612
|
`published, so the branch has diverged and needs reconciling. Something else moved it ` +
|
|
555
613
|
`on GitHub -- a direct push, or another CanopyCMS deployment sharing this repository.`);
|
|
556
614
|
}
|
|
557
|
-
throw err;
|
|
615
|
+
throw new Error(err.message);
|
|
616
|
+
}
|
|
617
|
+
if (outcome.pastStaleLease) {
|
|
618
|
+
// The lease was refused, so GitHub is provably not at the marker: it
|
|
619
|
+
// has moved past the rewritten commit and the marker is spent.
|
|
620
|
+
await clearHistoryRewrittenMarker(ctx, branchPath, branch);
|
|
621
|
+
await recordPushedToGitHub(ctx, branchPath, branch);
|
|
622
|
+
logPushOutcome(ctx, branch, outcome, 'GitHub had already moved past the rewritten commit');
|
|
623
|
+
return;
|
|
558
624
|
}
|
|
559
625
|
// Clear the marker only once GitHub is confirmed to hold something other
|
|
560
626
|
// than the commit we rewrote. A push that sent nothing new (remote.git
|
|
561
627
|
// still at the marker because its own publish has not landed yet) must
|
|
562
628
|
// leave the marker set -- it is the sole trigger for the self-heal pass.
|
|
563
|
-
if (marker && outgoingSha
|
|
629
|
+
if (marker && outgoingSha !== marker) {
|
|
564
630
|
await clearHistoryRewrittenMarker(ctx, branchPath, branch);
|
|
565
631
|
}
|
|
566
|
-
|
|
632
|
+
await recordPushedToGitHub(ctx, branchPath, branch);
|
|
633
|
+
logPushOutcome(ctx, branch, outcome);
|
|
634
|
+
}
|
|
635
|
+
/** An unmoved ref is routine (a task re-run after a crash), so it is logged at debug level. */
|
|
636
|
+
function logPushOutcome(ctx, branch, outcome, note) {
|
|
637
|
+
const suffix = note === undefined ? '' : `; ${note}`;
|
|
638
|
+
if (outcome.moved) {
|
|
639
|
+
workerLog(`Pushed ${branch} to GitHub (${describeMove(outcome)}${suffix})`);
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
ctx.log.debug(`${branch} already up to date on GitHub${suffix}`);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Stamp `pushedToGitHubAt`, branch delete's proof that the GitHub branch is this one. Recorded
|
|
647
|
+
* when the push lands, whatever the PR call after it does. Best-effort: a branch deleted
|
|
648
|
+
* meanwhile has no metadata to stamp, and a failed stamp only leaves a later delete to skip
|
|
649
|
+
* GitHub.
|
|
650
|
+
*/
|
|
651
|
+
async function recordPushedToGitHub(ctx, branchPath, branch) {
|
|
652
|
+
if (isSettingsBranch(branch))
|
|
653
|
+
return;
|
|
654
|
+
try {
|
|
655
|
+
await fs.stat(branchPath);
|
|
656
|
+
}
|
|
657
|
+
catch {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
try {
|
|
661
|
+
await getBranchMetadataFileManager(branchPath, ctx.contentBranchesPath).save({
|
|
662
|
+
branch: { name: branch, pushedToGitHubAt: new Date().toISOString() },
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
catch (err) {
|
|
666
|
+
workerLogError(`Failed to record the GitHub push for ${branch}:`, getErrorMessage(err));
|
|
667
|
+
}
|
|
567
668
|
}
|