@runeditrun/rer 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +157 -2
- package/bin/rer.mjs +3 -0
- package/cli/README.md +243 -0
- package/cli/adapter.mjs +10 -0
- package/cli/ai-local.mjs +65 -0
- package/cli/archive-path.mjs +52 -0
- package/cli/archive-vite.mjs +55 -0
- package/cli/archive.mjs +8 -0
- package/cli/auth-copy.mjs +80 -0
- package/cli/bounded.mjs +70 -0
- package/cli/cache-doctor.mjs +63 -0
- package/cli/container-transfer.mjs +35 -0
- package/cli/container.mjs +62 -0
- package/cli/contract.mjs +25 -0
- package/cli/dependencies.mjs +45 -0
- package/cli/dependency-approval.mjs +33 -0
- package/cli/dependency-doctor.mjs +34 -0
- package/cli/dependency-input.mjs +43 -0
- package/cli/deploy-config.mjs +118 -0
- package/cli/deploy.mjs +37 -0
- package/cli/dev-identity.mjs +80 -0
- package/cli/errors.mjs +14 -0
- package/cli/export.mjs +42 -0
- package/cli/font-cache.mjs +29 -0
- package/cli/hosted-output.mjs +110 -0
- package/cli/independent.mjs +29 -0
- package/cli/inspect.mjs +174 -0
- package/cli/jsonc.mjs +111 -0
- package/cli/lifecycle.mjs +148 -0
- package/cli/local.mjs +100 -0
- package/cli/main.mjs +57 -0
- package/cli/managed-server.mjs +143 -0
- package/cli/node-runtime-cache.mjs +68 -0
- package/cli/node-runtime-discovery.mjs +56 -0
- package/cli/node-runtime-download.mjs +59 -0
- package/cli/node-runtime.mjs +50 -0
- package/cli/output.mjs +22 -0
- package/cli/parse.mjs +77 -0
- package/cli/port.mjs +52 -0
- package/cli/portability-worker.mjs +56 -0
- package/cli/portability.mjs +66 -0
- package/cli/preview-cache.mjs +99 -0
- package/cli/project.mjs +48 -0
- package/cli/release.mjs +24 -0
- package/cli/runtime-front-door.mjs +46 -0
- package/cli/scheduled.mjs +81 -0
- package/cli/server-activity.mjs +12 -0
- package/cli/server-proxy.mjs +93 -0
- package/cli/server-registry.mjs +182 -0
- package/cli/server-resources.mjs +22 -0
- package/cli/settings.mjs +15 -0
- package/cli/setup-answers.mjs +29 -0
- package/cli/setup-input.mjs +113 -0
- package/cli/shared.mjs +7 -0
- package/cli/transfers.mjs +33 -0
- package/cli/version.mjs +36 -0
- package/composition/git-source.mjs +111 -0
- package/edition-authoring/files.mjs +81 -0
- package/edition-authoring/manifest.mjs +65 -0
- package/global/README.md +158 -0
- package/global/checkout.mjs +72 -0
- package/global/cli/command.mjs +13 -0
- package/global/cli/install.mjs +75 -0
- package/global/cli/layout.mjs +45 -0
- package/global/cli/status.mjs +54 -0
- package/global/cli/uninstall.mjs +36 -0
- package/global/delegate.mjs +13 -0
- package/global/launch/capacity.mjs +72 -0
- package/global/launch/dev.mjs +117 -0
- package/global/launch/facts.mjs +94 -0
- package/global/launch/failure.mjs +5 -0
- package/global/launch/local-defaults.mjs +4 -0
- package/global/launch/prepare.mjs +63 -0
- package/global/launch/render.mjs +44 -0
- package/global/launch/state.mjs +106 -0
- package/global/launch/steps.mjs +90 -0
- package/global/launch/stop.mjs +89 -0
- package/global/launch/worker.mjs +62 -0
- package/global/list.mjs +59 -0
- package/global/main.mjs +53 -0
- package/global/package-info.mjs +38 -0
- package/global/registry.mjs +24 -0
- package/global/rer.mjs +29 -0
- package/global/update/archive.mjs +89 -0
- package/global/update/command.mjs +90 -0
- package/global/update/latest.mjs +6 -0
- package/global/update/notice.mjs +58 -0
- package/global/update/registry.mjs +47 -0
- package/global/version.mjs +29 -0
- package/install.sh +144 -0
- package/modules/ai/LICENSE +21 -0
- package/modules/ai/README.md +90 -0
- package/modules/ai/client.mjs +71 -0
- package/modules/ai/config.mjs +39 -0
- package/modules/ai/dev/index.d.mts +6 -0
- package/modules/ai/dev/index.mjs +64 -0
- package/modules/ai/dev/outbox.mjs +33 -0
- package/modules/ai/errors.mjs +28 -0
- package/modules/ai/fake/index.d.mts +3 -0
- package/modules/ai/fake/index.mjs +82 -0
- package/modules/ai/index.d.mts +10 -0
- package/modules/ai/index.mjs +3 -0
- package/modules/ai/package.json +6 -0
- package/modules/container/README.md +156 -0
- package/modules/container/adapters/d1.mjs +34 -0
- package/modules/container/adapters/egress.mjs +40 -0
- package/modules/container/adapters/jobs.mjs +47 -0
- package/modules/container/adapters/objects.mjs +84 -0
- package/modules/container/adapters/retained.mjs +14 -0
- package/modules/container/adapters/sqlite-error.mjs +7 -0
- package/modules/container/adapters/sqlite-worker.mjs +26 -0
- package/modules/container/assets.mjs +14 -0
- package/modules/container/bindings.mjs +1 -0
- package/modules/container/boundary.mjs +38 -0
- package/modules/container/build.mjs +65 -0
- package/modules/container/deploy-input.mjs +17 -0
- package/modules/container/entrypoint.sh +5 -0
- package/modules/container/host.mjs +43 -0
- package/modules/container/ingress.mjs +23 -0
- package/modules/container/lock.mjs +11 -0
- package/modules/container/migrations.mjs +16 -0
- package/modules/container/node-engine.mjs +39 -0
- package/modules/container/portability-schema.mjs +14 -0
- package/modules/container/remote/deploy.py +76 -0
- package/modules/container/remote/files.py +275 -0
- package/modules/container/remote/resources.py +157 -0
- package/modules/container/runner.mjs +10 -0
- package/modules/container/ssh-plan.mjs +44 -0
- package/modules/container/ssh.mjs +63 -0
- package/modules/container/transfer.mjs +27 -0
- package/modules/container/worker-engine.mjs +32 -0
- package/modules/dependencies/README.md +211 -0
- package/modules/dependencies/approval.mjs +51 -0
- package/modules/dependencies/auth.mjs +40 -0
- package/modules/dependencies/authority.mjs +14 -0
- package/modules/dependencies/crypto.mjs +13 -0
- package/modules/dependencies/index.d.mts +23 -0
- package/modules/dependencies/index.mjs +113 -0
- package/modules/dependencies/logout.mjs +42 -0
- package/modules/dependencies/native.mjs +9 -0
- package/modules/dependencies/protocol.mjs +23 -0
- package/modules/dependencies/schema/approval-attempt.sql +3 -0
- package/modules/dependencies/schema/dependencies.sql +15 -0
- package/modules/dependencies/store.mjs +19 -0
- package/modules/deploy-kit/README.md +308 -0
- package/modules/deploy-kit/artifact.mjs +47 -0
- package/modules/deploy-kit/build-modules.mjs +21 -0
- package/modules/deploy-kit/check.mjs +13 -0
- package/modules/deploy-kit/cli.mjs +50 -0
- package/modules/deploy-kit/container-config.mjs +8 -0
- package/modules/deploy-kit/default-stage.mjs +14 -0
- package/modules/deploy-kit/deploy.mjs +80 -0
- package/modules/deploy-kit/health-request.mjs +86 -0
- package/modules/deploy-kit/health.mjs +99 -0
- package/modules/deploy-kit/index.mjs +12 -0
- package/modules/deploy-kit/local-only.mjs +10 -0
- package/modules/deploy-kit/migrations.mjs +48 -0
- package/modules/deploy-kit/owner-cli.mjs +63 -0
- package/modules/deploy-kit/owner.mjs +297 -0
- package/modules/deploy-kit/prepare.mjs +62 -0
- package/modules/deploy-kit/provider.mjs +99 -0
- package/modules/deploy-kit/queue-consumer.mjs +10 -0
- package/modules/deploy-kit/receipt.mjs +16 -0
- package/modules/deploy-kit/render.mjs +47 -0
- package/modules/deploy-kit/resources.mjs +106 -0
- package/modules/deploy-kit/service-grants.mjs +25 -0
- package/modules/deploy-kit/sql-safety.mjs +94 -0
- package/modules/deploy-kit/sql-tokens.mjs +49 -0
- package/modules/deploy-kit/teardown.mjs +100 -0
- package/modules/deploy-kit/templates.mjs +31 -0
- package/modules/deploy-kit/validation.mjs +58 -0
- package/modules/deploy-kit/worker.mjs +105 -0
- package/modules/erasure/CONTRACT.md +90 -0
- package/modules/erasure/README.md +221 -0
- package/modules/erasure/audit.mjs +23 -0
- package/modules/erasure/blobs.mjs +45 -0
- package/modules/erasure/cascade.mjs +91 -0
- package/modules/erasure/completion.mjs +62 -0
- package/modules/erasure/declaration.mjs +51 -0
- package/modules/erasure/fence.mjs +19 -0
- package/modules/erasure/index.d.mts +62 -0
- package/modules/erasure/index.mjs +85 -0
- package/modules/erasure/order.mjs +56 -0
- package/modules/erasure/plan.mjs +121 -0
- package/modules/erasure/rate.mjs +57 -0
- package/modules/erasure/receipt.mjs +32 -0
- package/modules/erasure/root.mjs +21 -0
- package/modules/erasure/schema/erasure-rates.sql +5 -0
- package/modules/erasure/schema/erasure.sql +27 -0
- package/modules/erasure/scope.mjs +29 -0
- package/modules/erasure/sql.mjs +23 -0
- package/modules/erasure/verification.mjs +69 -0
- package/modules/integrations/core/CONTRACT.md +50 -0
- package/modules/integrations/core/README.md +27 -0
- package/modules/integrations/core/crypto.ts +19 -0
- package/modules/integrations/core/database.ts +21 -0
- package/modules/integrations/core/delivery.ts +97 -0
- package/modules/integrations/core/destination.ts +13 -0
- package/modules/integrations/core/index.ts +7 -0
- package/modules/integrations/core/node-transport.ts +72 -0
- package/modules/integrations/core/public-address.ts +57 -0
- package/modules/integrations/core/repository.ts +74 -0
- package/modules/integrations/core/schema.sql +36 -0
- package/modules/integrations/core/sqlite.ts +20 -0
- package/modules/integrations/core/timestamped-body-signature.ts +38 -0
- package/modules/integrations/core/tokens.ts +71 -0
- package/modules/integrations/core/types.ts +52 -0
- package/modules/integrations/core/upgrade-token-usage-context.sql +5 -0
- package/modules/integrations/core/webhook.ts +73 -0
- package/modules/job-queue/README.md +173 -0
- package/modules/job-queue/cli.mjs +64 -0
- package/modules/job-queue/command.mjs +64 -0
- package/modules/job-queue/config.mjs +48 -0
- package/modules/job-queue/identity.mjs +48 -0
- package/modules/job-queue/index.mjs +135 -0
- package/modules/job-queue/membership.mjs +30 -0
- package/modules/job-queue/module.json +10 -0
- package/modules/job-queue/policy.mjs +29 -0
- package/modules/job-queue/processes.mjs +80 -0
- package/modules/job-queue/resources.mjs +37 -0
- package/modules/job-queue/storage.mjs +64 -0
- package/modules/job-queue/supervisor.mjs +30 -0
- package/modules/job-queue/waiting.mjs +16 -0
- package/modules/mail/LICENSE +21 -0
- package/modules/mail/README.md +336 -0
- package/modules/mail/doctor.mjs +81 -0
- package/modules/mail/index.mjs +8 -0
- package/modules/mail/live-proof/README.md +113 -0
- package/modules/mail/live-proof/run.mjs +55 -0
- package/modules/mail/package.json +19 -0
- package/modules/mail/pnpm-lock.yaml +537 -0
- package/modules/mail/src/adapters/cloudflare/README.md +84 -0
- package/modules/mail/src/adapters/cloudflare/headers.mjs +63 -0
- package/modules/mail/src/adapters/cloudflare/index.mjs +47 -0
- package/modules/mail/src/adapters/cloudflare/response.mjs +99 -0
- package/modules/mail/src/adapters/messaging/README.md +92 -0
- package/modules/mail/src/adapters/messaging/contract.mjs +66 -0
- package/modules/mail/src/adapters/messaging/event-schema.mjs +28 -0
- package/modules/mail/src/adapters/messaging/index.mjs +61 -0
- package/modules/mail/src/adapters/messaging/jws.mjs +68 -0
- package/modules/mail/src/adapters/messaging/request.mjs +52 -0
- package/modules/mail/src/adapters/messaging/serialize.mjs +21 -0
- package/modules/mail/src/adapters/resend/README.md +120 -0
- package/modules/mail/src/adapters/resend/index.mjs +22 -0
- package/modules/mail/src/adapters/resend/response.mjs +54 -0
- package/modules/mail/src/adapters/resend/serialize.mjs +46 -0
- package/modules/mail/src/adapters/resend/size.mjs +99 -0
- package/modules/mail/src/adapters/sendgrid/README.md +75 -0
- package/modules/mail/src/adapters/sendgrid/index.mjs +35 -0
- package/modules/mail/src/adapters/sendgrid/serialize.mjs +104 -0
- package/modules/mail/src/core/errors.mjs +9 -0
- package/modules/mail/src/core/transport.mjs +33 -0
- package/modules/mail/src/core/validation.mjs +172 -0
- package/modules/mail/src/development/index.d.mts +37 -0
- package/modules/mail/src/development/index.mjs +35 -0
- package/modules/mail/src/development/outbox.mjs +78 -0
- package/modules/mail/src/development/schema.sql +9 -0
- package/modules/mail/src/development/viewer-html.mjs +42 -0
- package/modules/mail/src/development/viewer.mjs +65 -0
- package/modules/mail/src/doctor/README.md +107 -0
- package/modules/mail/src/doctor/common.mjs +64 -0
- package/modules/mail/src/doctor/dns.mjs +102 -0
- package/modules/mail/src/doctor/index.mjs +28 -0
- package/modules/mail/src/doctor/probe.mjs +66 -0
- package/modules/mail/src/doctor/receive.mjs +81 -0
- package/modules/mail/src/doctor/records.mjs +45 -0
- package/modules/mail/src/events/README.md +268 -0
- package/modules/mail/src/events/d1-schema.sql +25 -0
- package/modules/mail/src/events/d1-storage.mjs +76 -0
- package/modules/mail/src/events/index.mjs +3 -0
- package/modules/mail/src/events/intake.mjs +201 -0
- package/modules/mail/src/events/storage.mjs +52 -0
- package/modules/mail/src/events/verify.mjs +92 -0
- package/modules/mail/src/inbound/README.md +137 -0
- package/modules/mail/src/inbound/bytes.mjs +91 -0
- package/modules/mail/src/inbound/headers.mjs +143 -0
- package/modules/mail/src/inbound/index.mjs +29 -0
- package/modules/mail/src/inbound/mime.mjs +36 -0
- package/modules/mail/src/inbound/multipart.mjs +27 -0
- package/modules/mail/src/inbound/resend-attachment.mjs +43 -0
- package/modules/mail/src/inbound/resend.mjs +54 -0
- package/modules/mail/src/inbound/sendgrid.mjs +60 -0
- package/modules/mail/src/sender.mjs +33 -0
- package/modules/mail/src/sendgrid-email/index.d.mts +33 -0
- package/modules/mail/src/sendgrid-email/index.mjs +86 -0
- package/modules/mail/src/unsubscribe/index.mjs +142 -0
- package/modules/operator-auth/README.md +599 -0
- package/modules/operator-auth/accounts/adapter.mjs +41 -0
- package/modules/operator-auth/accounts/admin.mjs +159 -0
- package/modules/operator-auth/accounts/credentials.mjs +27 -0
- package/modules/operator-auth/accounts/editor-owner.mjs +13 -0
- package/modules/operator-auth/accounts/index.d.mts +9 -0
- package/modules/operator-auth/accounts/index.mjs +9 -0
- package/modules/operator-auth/accounts/native.d.mts +38 -0
- package/modules/operator-auth/accounts/native.mjs +171 -0
- package/modules/operator-auth/accounts/oa.mjs +90 -0
- package/modules/operator-auth/accounts/provider-control.mjs +50 -0
- package/modules/operator-auth/accounts/provider-pin.mjs +14 -0
- package/modules/operator-auth/accounts/roles.mjs +26 -0
- package/modules/operator-auth/accounts/schema/dependency-setup.sql +17 -0
- package/modules/operator-auth/accounts/schema/dev-sessions.sql +22 -0
- package/modules/operator-auth/accounts/schema/domain-rules-step-up.sql +20 -0
- package/modules/operator-auth/accounts/schema/invitation-cancellation.sql +17 -0
- package/modules/operator-auth/accounts/schema/operator-auth.sql +142 -0
- package/modules/operator-auth/accounts/schema/provider-control.sql +30 -0
- package/modules/operator-auth/accounts/schema/provider-pins.sql +6 -0
- package/modules/operator-auth/accounts/settings-page.mjs +4 -0
- package/modules/operator-auth/accounts/store.mjs +246 -0
- package/modules/operator-auth/branding.mjs +66 -0
- package/modules/operator-auth/config.mjs +35 -0
- package/modules/operator-auth/conformance/README.md +81 -0
- package/modules/operator-auth/conformance/index.d.mts +43 -0
- package/modules/operator-auth/conformance/index.mjs +300 -0
- package/modules/operator-auth/conformance/presentation.mjs +57 -0
- package/modules/operator-auth/crypto.mjs +17 -0
- package/modules/operator-auth/dev/badge.mjs +62 -0
- package/modules/operator-auth/dev/index.d.mts +14 -0
- package/modules/operator-auth/dev/index.mjs +137 -0
- package/modules/operator-auth/dev/personas.mjs +17 -0
- package/modules/operator-auth/dev/sign-in-as.d.mts +3 -0
- package/modules/operator-auth/dev/sign-in-as.mjs +25 -0
- package/modules/operator-auth/dev-admission.d.mts +4 -0
- package/modules/operator-auth/dev-admission.mjs +10 -0
- package/modules/operator-auth/dev-gate.mjs +86 -0
- package/modules/operator-auth/docs/README.md +78 -0
- package/modules/operator-auth/docs/native-accounts.md +313 -0
- package/modules/operator-auth/docs/tanstack-start.md +218 -0
- package/modules/operator-auth/docs/vite-spa.md +144 -0
- package/modules/operator-auth/errors.mjs +9 -0
- package/modules/operator-auth/http.mjs +42 -0
- package/modules/operator-auth/identity/admission.mjs +105 -0
- package/modules/operator-auth/identity/after-confirm.mjs +18 -0
- package/modules/operator-auth/identity/email-link/confirmation.mjs +14 -0
- package/modules/operator-auth/identity/email-link/index.d.mts +48 -0
- package/modules/operator-auth/identity/email-link/index.mjs +128 -0
- package/modules/operator-auth/identity/email-link/message.mjs +16 -0
- package/modules/operator-auth/identity/index.d.mts +7 -0
- package/modules/operator-auth/identity/index.mjs +5 -0
- package/modules/operator-auth/identity/oidc/README.md +171 -0
- package/modules/operator-auth/identity/oidc/client-auth.mjs +16 -0
- package/modules/operator-auth/identity/oidc/config.mjs +102 -0
- package/modules/operator-auth/identity/oidc/crypto.mjs +29 -0
- package/modules/operator-auth/identity/oidc/errors.mjs +5 -0
- package/modules/operator-auth/identity/oidc/index.d.mts +33 -0
- package/modules/operator-auth/identity/oidc/index.mjs +287 -0
- package/modules/operator-auth/identity/oidc/jwt.mjs +102 -0
- package/modules/operator-auth/identity/oidc/network.mjs +93 -0
- package/modules/operator-auth/identity/oidc/presets.mjs +26 -0
- package/modules/operator-auth/identity/oidc/schema/oidc.sql +50 -0
- package/modules/operator-auth/identity/oidc/store.mjs +68 -0
- package/modules/operator-auth/identity/principal.mjs +28 -0
- package/modules/operator-auth/index.d.mts +223 -0
- package/modules/operator-auth/index.mjs +11 -0
- package/modules/operator-auth/input.mjs +55 -0
- package/modules/operator-auth/local-cli.mjs +16 -0
- package/modules/operator-auth/page.mjs +34 -0
- package/modules/operator-auth/policy.mjs +20 -0
- package/modules/operator-auth/routes.mjs +91 -0
- package/modules/operator-auth/service.mjs +152 -0
- package/modules/operator-auth/signin/index.mjs +67 -0
- package/modules/owner-claim/README.md +137 -0
- package/modules/owner-claim/idp.mjs +40 -0
- package/modules/owner-claim/index.d.mts +27 -0
- package/modules/owner-claim/index.mjs +39 -0
- package/modules/owner-claim/page.mjs +11 -0
- package/modules/owner-claim/schema/idp.sql +4 -0
- package/modules/owner-claim/schema/owner-claim.sql +18 -0
- package/modules/owner-claim/transaction.mjs +87 -0
- package/modules/owner-claim/verify.mjs +80 -0
- package/modules/portability/README.md +444 -0
- package/modules/portability/bridge.mjs +102 -0
- package/modules/portability/catalogue.mjs +118 -0
- package/modules/portability/cli-options.mjs +38 -0
- package/modules/portability/cli.mjs +56 -0
- package/modules/portability/copies.mjs +68 -0
- package/modules/portability/dev-data.mjs +32 -0
- package/modules/portability/diff.mjs +33 -0
- package/modules/portability/export.mjs +129 -0
- package/modules/portability/files.mjs +65 -0
- package/modules/portability/format.mjs +122 -0
- package/modules/portability/generated.mjs +60 -0
- package/modules/portability/hash.mjs +79 -0
- package/modules/portability/http.mjs +42 -0
- package/modules/portability/import.mjs +185 -0
- package/modules/portability/index.d.mts +58 -0
- package/modules/portability/index.mjs +22 -0
- package/modules/portability/journal.mjs +49 -0
- package/modules/portability/legacy-files.mjs +43 -0
- package/modules/portability/legacy-safety.mjs +27 -0
- package/modules/portability/lines.mjs +36 -0
- package/modules/portability/migration-history.mjs +18 -0
- package/modules/portability/migration-statements.mjs +23 -0
- package/modules/portability/payloads.mjs +52 -0
- package/modules/portability/provider-data.mjs +25 -0
- package/modules/portability/restore-safety.mjs +30 -0
- package/modules/portability/schema/portability.sql +25 -0
- package/modules/portability/storage.mjs +49 -0
- package/modules/release/README.md +212 -0
- package/modules/release/catalogue.mjs +79 -0
- package/modules/release/export.mjs +51 -0
- package/modules/release/produce.mjs +136 -0
- package/modules/release/public-child-transport-state.cjs +29 -0
- package/modules/release/public-child.mjs +71 -0
- package/modules/release/public-git.mjs +21 -0
- package/modules/release/publication-scan.mjs +22 -0
- package/modules/release/publish-handoff.mjs +37 -0
- package/modules/release/source.mjs +71 -0
- package/modules/release/test-summary.mjs +50 -0
- package/onboarding/README.md +163 -0
- package/onboarding/activity.mjs +31 -0
- package/onboarding/adapter-auth.mjs +88 -0
- package/onboarding/adapter-hosting.mjs +35 -0
- package/onboarding/adapter-labels.mjs +13 -0
- package/onboarding/adapter-settings.mjs +129 -0
- package/onboarding/adapter.mjs +238 -0
- package/onboarding/agent-handoff.mjs +9 -0
- package/onboarding/app-processes.mjs +21 -0
- package/onboarding/archive-output.d.mts +3 -0
- package/onboarding/archive-output.mjs +54 -0
- package/onboarding/command-custody.mjs +24 -0
- package/onboarding/command-recovery.mjs +8 -0
- package/onboarding/commands.mjs +166 -0
- package/onboarding/dependency-approval.mjs +36 -0
- package/onboarding/dev-identity.mjs +23 -0
- package/onboarding/email.mjs +24 -0
- package/onboarding/failures.mjs +42 -0
- package/onboarding/input-limits.mjs +7 -0
- package/onboarding/installer-inputs.mjs +24 -0
- package/onboarding/launcher.mjs +34 -0
- package/onboarding/local/build-files.mjs +64 -0
- package/onboarding/local/build.mjs +49 -0
- package/onboarding/local/environment.mjs +30 -0
- package/onboarding/local/files.mjs +35 -0
- package/onboarding/local/operation.mjs +166 -0
- package/onboarding/local/owner.mjs +34 -0
- package/onboarding/local/process-table.mjs +114 -0
- package/onboarding/local/recovery.mjs +79 -0
- package/onboarding/local/state.mjs +31 -0
- package/onboarding/metadata.mjs +7 -0
- package/onboarding/operator-install.mjs +45 -0
- package/onboarding/operator-installer.mjs +39 -0
- package/onboarding/operator-prerequisites.mjs +27 -0
- package/onboarding/package.json +6 -0
- package/onboarding/prefill.mjs +16 -0
- package/onboarding/private-files.mjs +97 -0
- package/onboarding/private.mjs +35 -0
- package/onboarding/process-cleanup.mjs +134 -0
- package/onboarding/process-diagnostics.mjs +91 -0
- package/onboarding/process-scope.mjs +118 -0
- package/onboarding/process-table.mjs +118 -0
- package/onboarding/process-tree.mjs +36 -0
- package/onboarding/production-modules.mjs +22 -0
- package/onboarding/production-options.mjs +40 -0
- package/onboarding/production-plan.mjs +8 -0
- package/onboarding/production-runtime.mjs +30 -0
- package/onboarding/project-vite.mjs +14 -0
- package/onboarding/providers.mjs +289 -0
- package/onboarding/queue.mjs +53 -0
- package/onboarding/queued-entry.mjs +34 -0
- package/onboarding/readiness.mjs +59 -0
- package/onboarding/redaction-jwt.mjs +32 -0
- package/onboarding/redaction.mjs +298 -0
- package/onboarding/schema/SETUP.schema.json +514 -0
- package/onboarding/schema/dependencies.mjs +35 -0
- package/onboarding/schema/json-schema.mjs +202 -0
- package/onboarding/schema/setup-validation.mjs +325 -0
- package/onboarding/secrets.mjs +21 -0
- package/onboarding/server.mjs +188 -0
- package/onboarding/setup-manifest.mjs +116 -0
- package/onboarding/state.mjs +46 -0
- package/onboarding/theme-files.mjs +37 -0
- package/onboarding/theme.mjs +118 -0
- package/onboarding/ui/activity.js +98 -0
- package/onboarding/ui/address-client.mjs +33 -0
- package/onboarding/ui/app.js +377 -0
- package/onboarding/ui/index.html +1 -0
- package/onboarding/ui/model-core.mjs +127 -0
- package/onboarding/ui/model.mjs +5 -0
- package/onboarding/ui/style.css +63 -0
- package/onboarding/ui/theme.css +37 -0
- package/onboarding/ui/workspace-art.js +30 -0
- package/onboarding/wrangler-auth.mjs +43 -0
- package/operations/README.md +246 -0
- package/operations/operations.mjs +168 -0
- package/operations/project-operations.mjs +157 -0
- package/package.json +11 -4
- package/scripts/publish-check/README.md +72 -0
- package/scripts/publish-check/checksum_maps.py +52 -0
- package/scripts/publish-check/cli.py +34 -0
- package/scripts/publish-check/command_arguments.py +51 -0
- package/scripts/publish-check/command_shell_quotes.py +41 -0
- package/scripts/publish-check/command_source_quotes.py +120 -0
- package/scripts/publish-check/content_identities.py +250 -0
- package/scripts/publish-check/copy_identities.py +41 -0
- package/scripts/publish-check/der.py +61 -0
- package/scripts/publish-check/destination.py +117 -0
- package/scripts/publish-check/env_values.py +74 -0
- package/scripts/publish-check/font_licence.py +23 -0
- package/scripts/publish-check/git_history.py +115 -0
- package/scripts/publish-check/github_refs.py +217 -0
- package/scripts/publish-check/gitleaks.py +72 -0
- package/scripts/publish-check/gitleaks.toml +40 -0
- package/scripts/publish-check/history_scan.py +176 -0
- package/scripts/publish-check/hit_location.py +18 -0
- package/scripts/publish-check/hygiene_config.py +42 -0
- package/scripts/publish-check/hygiene_text.py +11 -0
- package/scripts/publish-check/identity-recipe.mjs +54 -0
- package/scripts/publish-check/json_identity.py +79 -0
- package/scripts/publish-check/license-exceptions.json +11 -0
- package/scripts/publish-check/licenses.py +49 -0
- package/scripts/publish-check/policy.py +18 -0
- package/scripts/publish-check/project_identity.py +89 -0
- package/scripts/publish-check/provenance.py +91 -0
- package/scripts/publish-check/provenance_pins.py +33 -0
- package/scripts/publish-check/public_base.py +63 -0
- package/scripts/publish-check/public_hygiene.py +80 -0
- package/scripts/publish-check/publication-config.json +12 -0
- package/scripts/publish-check/publication_config.py +34 -0
- package/scripts/publish-check/report.py +52 -0
- package/scripts/publish-check/repository_commands.py +75 -0
- package/scripts/publish-check/repository_intent.py +52 -0
- package/scripts/publish-check/reviewed.py +41 -0
- package/scripts/publish-check/source_hashes.mjs +6 -0
- package/scripts/publish-check/tree_scan.py +88 -0
- package/scripts/publish-check/trust_bundle.py +87 -0
- package/scripts/publish-check.mjs +58 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { git } from './source.mjs';
|
|
5
|
+
import { sha256 } from './catalogue.mjs';
|
|
6
|
+
|
|
7
|
+
// Read the checked Git blobs directly. export-ignore/export-subst attributes
|
|
8
|
+
// cannot silently remove or change bytes after publication inspection.
|
|
9
|
+
export function sourceTree(source, tree) {
|
|
10
|
+
mkdirSync(tree);
|
|
11
|
+
for (const [path, entry] of source.entries) {
|
|
12
|
+
// Git control paths can alias .git on case-insensitive filesystems. Never
|
|
13
|
+
// materialize source-supplied configuration before initializing an export.
|
|
14
|
+
if (path.split('/').some(part => part.normalize('NFC').replace(/\p{Cf}/gu, '').toLowerCase().replace(/[ .]+$/, '') === '.git'))
|
|
15
|
+
throw Error('Public export cannot contain Git control paths');
|
|
16
|
+
if (!['100644', '100755'].includes(entry.mode) || entry.type !== 'blob') throw Error('Public export requires ordinary committed files');
|
|
17
|
+
const target = resolve(tree, path);
|
|
18
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
19
|
+
writeFileSync(target, source.bytes(path), { flag: 'wx', mode: entry.mode === '100755' ? 0o755 : 0o644 });
|
|
20
|
+
}
|
|
21
|
+
return tree;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function releaseMessage(source) {
|
|
25
|
+
const notes = source.release.publicReleaseNotes;
|
|
26
|
+
if (notes !== undefined && (typeof notes !== 'string' || !notes.trim())) throw Error('Public release notes must be nonempty text');
|
|
27
|
+
return source.tag + (notes ? `\n\n${notes.trim()}` : '');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function annotate(repo, source, commit, name = source.tag) {
|
|
31
|
+
git(repo, '-c', 'user.name=Run Edit Run', '-c', 'user.email=releases@runeditrun.invalid', '-c', 'core.hooksPath=/dev/null',
|
|
32
|
+
'tag', '-a', name, commit, '-m', releaseMessage(source));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function cleanHistory(source, temporary, tagged = false) {
|
|
36
|
+
const tree = sourceTree(source, resolve(temporary, 'clean-source'));
|
|
37
|
+
git(tree, 'init', '-q', '-b', 'main');
|
|
38
|
+
git(tree, 'add', '-f', '--all');
|
|
39
|
+
git(tree, '-c', 'user.name=Run Edit Run', '-c', 'user.email=releases@runeditrun.invalid', '-c', 'core.hooksPath=/dev/null', 'commit', '-qm', releaseMessage(source));
|
|
40
|
+
const commit = git(tree, 'rev-parse', 'HEAD').toString().trim();
|
|
41
|
+
if (git(tree, 'rev-parse', 'HEAD^{tree}').toString().trim() !== source.tree) throw Error('Clean export changed source bytes');
|
|
42
|
+
if (tagged) annotate(tree, source, commit);
|
|
43
|
+
return { repo: tree, commit };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function publicExport(source, temporary, output) {
|
|
47
|
+
const tree = sourceTree(source, resolve(temporary, 'public-source'));
|
|
48
|
+
const archive = resolve(output, 'public-source.tar');
|
|
49
|
+
execFileSync('tar', ['-cf', archive, '-C', tree, '.'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
50
|
+
return { path: 'public-source.tar', sha256: sha256(readFileSync(archive)), format: 'tar', revision: source.commit };
|
|
51
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync, cpSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { inspectSource, newOutput, git, command } from './source.mjs';
|
|
5
|
+
import { catalogue, writeCatalogue, sha256, json } from './catalogue.mjs';
|
|
6
|
+
import { publicExport, cleanHistory, annotate, releaseMessage } from './export.mjs';
|
|
7
|
+
import { publicationScan, validatePublicationDestination } from './publication-scan.mjs';
|
|
8
|
+
import { publicDestination, requirePublicDestination, remotePublicHead, publicChild, assertPublicTree } from './public-child.mjs';
|
|
9
|
+
import { writePublishHandoff } from './publish-handoff.mjs';
|
|
10
|
+
|
|
11
|
+
export async function release({ repo, ref = 'HEAD', output, project, repository, dryRun = false, tag = false, cleanHistory: clean = false, envFile, onto }) {
|
|
12
|
+
if (!/^[\w.-]+\/[\w.-]+$/.test(project ?? '')) throw Error('--project must be publisher/name');
|
|
13
|
+
if (!/^https:\/\/github\.com\/[\w.-]+\/[\w.-]+(?:\.git)?$/i.test(repository ?? '')) throw Error('--repository must be a credential-free GitHub repository URL');
|
|
14
|
+
if (onto !== undefined && clean) throw Error('--onto and --clean-history are mutually exclusive');
|
|
15
|
+
const preview = dryRun || !tag;
|
|
16
|
+
const source = inspectSource(repo, ref);
|
|
17
|
+
validatePublicationDestination(source, project, repository);
|
|
18
|
+
releaseMessage(source);
|
|
19
|
+
if (typeof tag === 'string' && tag !== source.tag) throw Error('Requested tag must match the committed release version');
|
|
20
|
+
const existing = git(source.repo, 'tag', '--list', source.tag).toString().trim();
|
|
21
|
+
if (existing && tag && !dryRun) throw Error('Release tag already exists; choose a new committed version');
|
|
22
|
+
// Validate metadata before installs or output writes.
|
|
23
|
+
catalogue(source, project, repository, { verify: { status: 'PENDING' }, publishCheck: { status: 'PENDING' } }, preview);
|
|
24
|
+
const destination = onto !== undefined ? publicDestination(onto, repository, source.repo) : null;
|
|
25
|
+
let publicParent = null;
|
|
26
|
+
if (destination) {
|
|
27
|
+
requirePublicDestination(destination);
|
|
28
|
+
publicParent = remotePublicHead(source.repo, destination, source.tag);
|
|
29
|
+
}
|
|
30
|
+
output = newOutput(source.repo, output);
|
|
31
|
+
const temporary = mkdtempSync(resolve(tmpdir(), 'rer-release-'));
|
|
32
|
+
let receipt;
|
|
33
|
+
try {
|
|
34
|
+
const checkout = resolve(temporary, 'seed');
|
|
35
|
+
git(source.repo, 'clone', '--no-hardlinks', '--no-checkout', '--', source.repo, checkout);
|
|
36
|
+
git(checkout, 'checkout', '--detach', source.commit);
|
|
37
|
+
// Honour the seed's committed install policy. Base's native-build settings
|
|
38
|
+
// are not interchangeable with pnpm 8's locked onlyBuiltDependencies.
|
|
39
|
+
const install = await command('pnpm', ['install', '--frozen-lockfile'], checkout);
|
|
40
|
+
const verify = install.status === 'PASS' ? await command('pnpm', ['run', 'verify'], checkout) : { status: 'FAIL', reason: 'frozen-install-failed', tests: 0, passed: 0, failed: 0 };
|
|
41
|
+
// Release-only scale and capacity cases stay outside routine CI. The
|
|
42
|
+
// committed seed script is an additional gate in previews and releases.
|
|
43
|
+
if (verify.status === 'PASS' && JSON.parse(source.bytes('package.json')).scripts['verify:release']) {
|
|
44
|
+
verify.release = await command('pnpm', ['run', 'verify:release'], checkout);
|
|
45
|
+
if (verify.release.status !== 'PASS') {
|
|
46
|
+
verify.status = 'FAIL'; verify.exitCode = verify.release.exitCode;
|
|
47
|
+
verify.reason = 'release-verification-failed';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (git(checkout, 'status', '--porcelain', '--untracked-files=no').length) {
|
|
51
|
+
verify.status = 'FAIL'; verify.reason = 'verify-changed-tracked-source';
|
|
52
|
+
}
|
|
53
|
+
// Explicit clean-history publication scans every byte and ref of the actual
|
|
54
|
+
// parentless export. Ordinary releases retain the private full-history gate.
|
|
55
|
+
const exported = destination ? publicChild(source, temporary, destination, publicParent, Boolean(tag))
|
|
56
|
+
: clean ? cleanHistory(source, temporary, Boolean(tag)) : null;
|
|
57
|
+
const scanRepo = exported?.repo ?? source.repo;
|
|
58
|
+
const scanRef = exported?.commit ?? source.commit;
|
|
59
|
+
let report = publicationScan(scanRepo, scanRef, envFile, project, repository, destination?.spec);
|
|
60
|
+
if (!exported && tag && report.status === 'PASS') {
|
|
61
|
+
// Stage the exact final annotation before any source tag or distributable
|
|
62
|
+
// artifact exists, in a fresh clone of the selected commit: verification
|
|
63
|
+
// left build output in its checkout, which is not release source. Source
|
|
64
|
+
// history was scanned above, including refs a checkout clone leaves out.
|
|
65
|
+
const candidate = resolve(temporary, 'annotation');
|
|
66
|
+
git(source.repo, 'clone', '--no-hardlinks', '--no-checkout', '--', source.repo, candidate);
|
|
67
|
+
git(candidate, 'checkout', '--detach', source.commit);
|
|
68
|
+
const staged = git(candidate, 'tag', '--list', source.tag).toString().trim();
|
|
69
|
+
annotate(candidate, source, source.commit, staged ? `${source.tag}-publication-candidate` : source.tag);
|
|
70
|
+
const annotations = publicationScan(candidate, source.commit, envFile, project, repository);
|
|
71
|
+
report.annotationInspection = annotations;
|
|
72
|
+
if (annotations.status !== 'PASS') {
|
|
73
|
+
report.status = annotations.status;
|
|
74
|
+
report.complete = annotations.complete;
|
|
75
|
+
report.guarantees.historyScanned = annotations.complete;
|
|
76
|
+
report.findings.push(...annotations.findings);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (destination) {
|
|
80
|
+
assertPublicTree(exported.repo, exported.commit, source.tree);
|
|
81
|
+
if (report.status === 'PASS' && (report.complete !== true || report.guarantees?.historyScanned !== true ||
|
|
82
|
+
report.publicBase?.commit !== publicParent || report.source?.commit !== exported.commit)) {
|
|
83
|
+
report.status = 'INCOMPLETE';
|
|
84
|
+
report.complete = false;
|
|
85
|
+
report.guarantees ??= {};
|
|
86
|
+
report.guarantees.historyScanned = false;
|
|
87
|
+
}
|
|
88
|
+
// A successful scan cannot authorize a handoff against a different tip.
|
|
89
|
+
requirePublicDestination(destination);
|
|
90
|
+
if (remotePublicHead(exported.repo, destination, source.tag) !== publicParent) throw Error('Public destination changed during release');
|
|
91
|
+
}
|
|
92
|
+
writeFileSync(resolve(output, 'publish-check.json'), json(report), { flag: 'wx' });
|
|
93
|
+
const gates = { verify, publishCheck: { status: report.status, findings: report.findings.length,
|
|
94
|
+
reportSha256: sha256(json(report)) } };
|
|
95
|
+
const passed = verify.status === 'PASS' && report.status === 'PASS';
|
|
96
|
+
receipt = { schemaVersion: 1, status: passed ? (preview ? 'DRY_RUN' : 'RELEASED') : 'BLOCKED',
|
|
97
|
+
dryRun, tagRequested: Boolean(tag), source: { commit: source.commit, tree: source.tree }, tag: { name: source.tag, created: false, pushed: false }, gates,
|
|
98
|
+
publicationHistory: destination ? 'public-child' : clean ? 'parentless-export' : 'source-history',
|
|
99
|
+
...(destination ? { publicParent, publicDestination: { repository: destination.url, branch: destination.branch } } : {}),
|
|
100
|
+
publicExport: null, siteRelease: null };
|
|
101
|
+
// A blocked preview may prepare the site metadata/docs for local review.
|
|
102
|
+
// Its provenance explicitly records blocked gates; it is never a release.
|
|
103
|
+
const publicSource = exported ? { ...source, commit: exported.commit, publicTagStaged: Boolean(destination && tag) } : source;
|
|
104
|
+
if (passed || preview) receipt.siteRelease = writeCatalogue(publicSource, output, catalogue(publicSource, project, repository, gates, preview));
|
|
105
|
+
if (passed) {
|
|
106
|
+
if (tag) {
|
|
107
|
+
receipt.publicExport = publicExport(source, temporary, output);
|
|
108
|
+
if (exported) {
|
|
109
|
+
cpSync(exported.repo, resolve(output, 'public-git'), { recursive: true, errorOnExist: true });
|
|
110
|
+
Object.assign(receipt.publicExport, { gitPath: 'public-git', commit: exported.commit, tree: source.tree,
|
|
111
|
+
...(destination ? { publicParent, tag: source.tag, tagObject: exported.tagObject, revision: exported.commit } : {}) });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Verification must not have changed the original or selected source.
|
|
115
|
+
if (git(source.repo, 'status', '--porcelain', '--untracked-files=all').length) throw Error('Source changed during release');
|
|
116
|
+
if (tag && !dryRun) {
|
|
117
|
+
// Public-child writes every artifact before the last guard/source tag,
|
|
118
|
+
// so a failed write or changed remote cannot leave a private tag.
|
|
119
|
+
if (!destination) annotate(source.repo, source, source.commit);
|
|
120
|
+
receipt.tag.created = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (passed && tag && destination) writePublishHandoff(output, destination, receipt);
|
|
124
|
+
writeFileSync(resolve(output, 'release-receipt.json'), json(receipt), { flag: 'wx' });
|
|
125
|
+
if (passed && destination) {
|
|
126
|
+
requirePublicDestination(destination);
|
|
127
|
+
if (remotePublicHead(exported.repo, destination, source.tag) !== publicParent) throw Error('Public destination changed before handoff');
|
|
128
|
+
if (tag && !dryRun) annotate(source.repo, source, source.commit);
|
|
129
|
+
}
|
|
130
|
+
return receipt;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
// No incomplete directory should be mistaken for a completed export.
|
|
133
|
+
rmSync(output, { recursive: true, force: true });
|
|
134
|
+
throw error;
|
|
135
|
+
} finally { rmSync(temporary, { recursive: true, force: true }); }
|
|
136
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Shared serialization for the fixture's Git and smart HTTP transport doubles.
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const { randomUUID } = require('node:crypto');
|
|
4
|
+
|
|
5
|
+
exports.withTransportState = function (path, update, operation) {
|
|
6
|
+
const lock = path + '.lock', deadline = Date.now() + 10000;
|
|
7
|
+
const pause = new Int32Array(new SharedArrayBuffer(4));
|
|
8
|
+
for (;;) {
|
|
9
|
+
try { fs.mkdirSync(lock); break; }
|
|
10
|
+
catch (error) {
|
|
11
|
+
if (error.code !== 'EEXIST') throw error;
|
|
12
|
+
if (Date.now() >= deadline) throw new Error('Timed out waiting for fixture transport state lock');
|
|
13
|
+
Atomics.wait(pause, 0, 0, 10);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
let temporary;
|
|
17
|
+
try {
|
|
18
|
+
const state = fs.existsSync(path) ? JSON.parse(fs.readFileSync(path)) : { heads: 0, fetches: 0 };
|
|
19
|
+
update(state);
|
|
20
|
+
temporary = path + '.' + process.pid + '.' + randomUUID() + '.tmp';
|
|
21
|
+
const fd = fs.openSync(temporary, 'wx');
|
|
22
|
+
try { fs.writeFileSync(fd, JSON.stringify(state)); } finally { fs.closeSync(fd); }
|
|
23
|
+
fs.renameSync(temporary, path);
|
|
24
|
+
temporary = undefined;
|
|
25
|
+
return operation(state);
|
|
26
|
+
} finally {
|
|
27
|
+
try { if (temporary) fs.unlinkSync(temporary); } finally { fs.rmdirSync(lock); }
|
|
28
|
+
}
|
|
29
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { sourceTree, releaseMessage } from './export.mjs';
|
|
4
|
+
import { publicGit, publicOid } from './public-git.mjs';
|
|
5
|
+
|
|
6
|
+
const repositoryUrl = /^https:\/\/github\.com\/([A-Za-z0-9][A-Za-z0-9_.-]*)\/([A-Za-z0-9][A-Za-z0-9_.-]*)$/i;
|
|
7
|
+
const identity = url => url.match(repositoryUrl)?.slice(1).join('/').toLowerCase().replace(/\.git$/, '');
|
|
8
|
+
|
|
9
|
+
export function publicDestination(onto, repository, repo) {
|
|
10
|
+
if (typeof onto !== 'string' || onto.split('#').length !== 2) throw Error('--onto requires an explicit public repository branch');
|
|
11
|
+
const [url, branch] = onto.split('#');
|
|
12
|
+
if (!identity(url) || identity(url) !== identity(repository)) throw Error('--onto must match the declared publication repository');
|
|
13
|
+
if (!branch || branch === 'HEAD' || branch.startsWith('-') || branch.startsWith('refs/')) throw Error('--onto requires a branch name');
|
|
14
|
+
publicGit(repo, ['check-ref-format', `refs/heads/${branch}`]);
|
|
15
|
+
return { url, branch, spec: onto, identity: identity(url) };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function requirePublicDestination(destination) {
|
|
19
|
+
// Reuse the scanner's strict HTTP/status/duplicate-key validation. Pending
|
|
20
|
+
// destinations are deliberately not accepted by this release mode.
|
|
21
|
+
const script = 'import sys; sys.path.insert(0, sys.argv[1]); from github_refs import GitHubResolver; sys.exit(0 if GitHubResolver().resolve(sys.argv[2]) == "public" else 1)';
|
|
22
|
+
try {
|
|
23
|
+
execFileSync('python3', ['-B', '-c', script, resolve(import.meta.dirname, '../../scripts/publish-check'), destination.identity],
|
|
24
|
+
{ stdio: ['ignore', 'pipe', 'pipe'] });
|
|
25
|
+
} catch { throw Error('Public destination must exist and have confirmed public visibility'); }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function remotePublicHead(repo, destination, tag) {
|
|
29
|
+
const headRef = `refs/heads/${destination.branch}`, tagRef = `refs/tags/${tag}`;
|
|
30
|
+
const lines = publicGit(repo, ['--git-dir=/dev/null', 'ls-remote', '--refs', '--', destination.url, headRef, tagRef]).toString().trim().split('\n').filter(Boolean);
|
|
31
|
+
if (lines.length !== 1) throw Error('Public destination must have one unambiguous branch and no release tag');
|
|
32
|
+
const fields = lines[0].split('\t');
|
|
33
|
+
if (fields.length !== 2 || fields[1] !== headRef || !/^[0-9a-f]{40}$/.test(fields[0])) throw Error('Public destination branch is missing or ambiguous');
|
|
34
|
+
return fields[0];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function assertPublicTree(repo, commit, sourceTreeId) {
|
|
38
|
+
if (publicOid(repo, 'rev-parse', `${commit}^{tree}`) !== sourceTreeId) throw Error('Public child changed the selected source tree');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function publicChild(source, temporary, destination, parent, tagged) {
|
|
42
|
+
const repo = sourceTree(source, resolve(temporary, 'public-child'));
|
|
43
|
+
publicGit(repo, ['init', '-q', '--template=', '-b', destination.branch]);
|
|
44
|
+
publicGit(repo, ['fetch', '--quiet', '--no-tags', '--no-recurse-submodules', '--no-write-fetch-head', '--',
|
|
45
|
+
destination.url, `refs/heads/${destination.branch}:refs/public-release/parent`]);
|
|
46
|
+
if (publicOid(repo, 'rev-parse', 'refs/public-release/parent') !== parent ||
|
|
47
|
+
publicOid(repo, 'cat-file', '-t', parent) !== 'commit' ||
|
|
48
|
+
publicOid(repo, 'rev-parse', '--is-shallow-repository') !== 'false') throw Error('Public parent changed or is not a complete commit');
|
|
49
|
+
// Store only selected source blobs; never copy or fetch private Git objects.
|
|
50
|
+
// Index plumbing avoids clean filters and all export/working-tree attributes.
|
|
51
|
+
const records = [];
|
|
52
|
+
for (const [path, entry] of source.entries) {
|
|
53
|
+
const oid = publicGit(repo, ['hash-object', '-w', '--stdin', '--no-filters'], source.bytes(path)).toString().trim();
|
|
54
|
+
if (oid !== entry.oid) throw Error('Public source blob identity changed');
|
|
55
|
+
records.push(`${entry.mode} ${oid}\t${path}\0`);
|
|
56
|
+
}
|
|
57
|
+
publicGit(repo, ['update-index', '-z', '--index-info'], Buffer.from(records.join('')));
|
|
58
|
+
const tree = publicOid(repo, 'write-tree');
|
|
59
|
+
if (tree !== source.tree) throw Error('Public child changed the selected source tree');
|
|
60
|
+
const commit = publicGit(repo, ['commit-tree', tree, '-p', parent, '-F', '-'], Buffer.from(releaseMessage(source) + '\n')).toString().trim();
|
|
61
|
+
publicGit(repo, ['update-ref', `refs/heads/${destination.branch}`, commit]);
|
|
62
|
+
publicGit(repo, ['update-ref', '-d', 'refs/public-release/parent']);
|
|
63
|
+
assertPublicTree(repo, commit, source.tree);
|
|
64
|
+
if (publicOid(repo, 'rev-list', '--parents', '-n', '1', commit) !== `${commit} ${parent}`) throw Error('Public child must have exactly the pinned public parent');
|
|
65
|
+
let tagObject = null;
|
|
66
|
+
if (tagged) {
|
|
67
|
+
publicGit(repo, ['tag', '-a', '--cleanup=verbatim', source.tag, commit, '-F', '-'], Buffer.from(releaseMessage(source) + '\n'));
|
|
68
|
+
tagObject = publicOid(repo, 'rev-parse', `refs/tags/${source.tag}`);
|
|
69
|
+
}
|
|
70
|
+
return { repo, commit, parent, tagObject };
|
|
71
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
// Public construction never inherits alternate object stores, templates, filters,
|
|
4
|
+
// replacement ancestry, hooks, signing, credential URL rewrites or global config.
|
|
5
|
+
export function publicGit(repo, args, input) {
|
|
6
|
+
const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_')));
|
|
7
|
+
Object.assign(env, { GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null',
|
|
8
|
+
GIT_NO_REPLACE_OBJECTS: '1', GIT_NO_LAZY_FETCH: '1', GIT_GRAFT_FILE: '/dev/null', GIT_TERMINAL_PROMPT: '0' });
|
|
9
|
+
try {
|
|
10
|
+
return execFileSync('git', ['--no-replace-objects', '-C', repo,
|
|
11
|
+
'-c', 'core.hooksPath=/dev/null', '-c', 'core.useReplaceRefs=false',
|
|
12
|
+
'-c', 'core.attributesFile=/dev/null', '-c', 'core.autocrlf=false',
|
|
13
|
+
'-c', 'commit.gpgSign=false', '-c', 'tag.gpgSign=false',
|
|
14
|
+
'-c', 'user.name=Run Edit Run', '-c', 'user.email=releases@runeditrun.invalid',
|
|
15
|
+
'-c', 'protocol.ext.allow=never', '-c', 'protocol.fd.allow=never',
|
|
16
|
+
'-c', 'gc.auto=0', '-c', 'maintenance.auto=false', ...args],
|
|
17
|
+
{ env, input, stdio: ['pipe', 'pipe', 'pipe'], maxBuffer: 128 * 1024 * 1024 });
|
|
18
|
+
} catch { throw Error('Public Git operation failed'); }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const publicOid = (repo, ...args) => publicGit(repo, args).toString().trim();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export function validatePublicationDestination(source, project, repository) {
|
|
5
|
+
try {
|
|
6
|
+
execFileSync('python3', ['-B', resolve(import.meta.dirname, '../../scripts/publish-check/destination.py'),
|
|
7
|
+
'--repo', source.repo, '--ref', source.commit, '--project', project, '--repository', repository],
|
|
8
|
+
{ stdio: ['ignore', 'pipe', 'pipe'] });
|
|
9
|
+
} catch { throw Error('Publication destination must agree with the committed edition and project identities'); }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function publicationScan(repo, ref, envFile, project, repository, publicBase) {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(execFileSync(process.execPath,
|
|
15
|
+
[resolve(import.meta.dirname, '../../scripts/publish-check.mjs'), '--repo', repo, '--ref', ref,
|
|
16
|
+
...(publicBase ? ['--public-base', publicBase] : []), ...(project != null ? ['--project', project] : []), ...(repository != null ? ['--repository', repository] : []), ...(envFile ? ['--env-file', envFile] : [])],
|
|
17
|
+
{ stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 128 * 1024 * 1024 }));
|
|
18
|
+
} catch (error) {
|
|
19
|
+
try { return JSON.parse(error.stdout); }
|
|
20
|
+
catch { throw Error('Publication inspection did not produce a report'); }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { sha256, json } from './catalogue.mjs';
|
|
4
|
+
|
|
5
|
+
const quote = value => "'" + String(value).replaceAll("'", "'\\''") + "'";
|
|
6
|
+
// A test command must not mask a failed command substitution with matching output.
|
|
7
|
+
const requireOutput = (command, expected) => `release_output=$(${command}) || exit 1\ntest "$release_output" = ${expected}`;
|
|
8
|
+
|
|
9
|
+
export function writePublishHandoff(output, destination, receipt) {
|
|
10
|
+
const { commit, tree, tagObject } = receipt.publicExport;
|
|
11
|
+
const parent = receipt.publicParent;
|
|
12
|
+
const tag = receipt.tag.name;
|
|
13
|
+
// Embedded identities and receipt digest bind the commands to this artifact,
|
|
14
|
+
// including when pasted from an unrelated working directory.
|
|
15
|
+
const script = [
|
|
16
|
+
'set -eu',
|
|
17
|
+
`artifact=${quote(output)}`,
|
|
18
|
+
'cd -- "$artifact/public-git"',
|
|
19
|
+
requireOutput(`node -e 'const fs=require("node:fs"),c=require("node:crypto"); process.stdout.write(c.createHash("sha256").update(fs.readFileSync(process.argv[1])).digest("hex"))' "$artifact/release-receipt.json"`, quote(sha256(json(receipt)))),
|
|
20
|
+
requireOutput(`node -e 'const fs=require("node:fs"),c=require("node:crypto"); process.stdout.write(c.createHash("sha256").update(fs.readFileSync(process.argv[1])).digest("hex"))' "$artifact/publish-check.json"`, quote(receipt.gates.publishCheck.reportSha256)),
|
|
21
|
+
requireOutput(`node -e 'const fs=require("node:fs"),c=require("node:crypto"); process.stdout.write(c.createHash("sha256").update(fs.readFileSync(process.argv[1])).digest("hex"))' "$artifact/public-git/.git/config"`, quote(sha256(readFileSync(resolve(output, 'public-git/.git/config'))))),
|
|
22
|
+
'# Discard inherited Git overrides while keeping authentication in the environment.',
|
|
23
|
+
'release_git() {',
|
|
24
|
+
` node -e ${quote('const cp=require("node:child_process"); const env=Object.fromEntries(Object.entries(process.env).filter(([k])=>!k.startsWith("GIT_") && k!=="GH_DEBUG" && k!=="DEBUG")); Object.assign(env,{GIT_CONFIG_NOSYSTEM:"1",GIT_CONFIG_GLOBAL:"/dev/null",GIT_NO_REPLACE_OBJECTS:"1",GIT_GRAFT_FILE:"/dev/null",GIT_TERMINAL_PROMPT:"0"}); const result=cp.spawnSync("git",["--no-replace-objects","-c","core.hooksPath=/dev/null","-c","core.useReplaceRefs=false","-c","push.followTags=false","-c","credential.helper=","-c","credential.helper=!gh auth git-credential",...process.argv.slice(1)],{env,stdio:"inherit"}); process.exit(result.status??1);')} -- "$@"`,
|
|
25
|
+
'}',
|
|
26
|
+
requireOutput('release_git rev-parse --show-toplevel', '"$artifact/public-git"'),
|
|
27
|
+
requireOutput('release_git rev-parse HEAD', quote(commit)),
|
|
28
|
+
requireOutput("release_git rev-parse 'HEAD^{tree}'", quote(tree)),
|
|
29
|
+
requireOutput('release_git rev-list --parents -n 1 HEAD', quote(`${commit} ${parent}`)),
|
|
30
|
+
requireOutput(`release_git cat-file -t ${quote(`refs/tags/${tag}`)}`, 'tag'),
|
|
31
|
+
requireOutput(`release_git rev-parse ${quote(`refs/tags/${tag}`)}`, quote(tagObject)),
|
|
32
|
+
requireOutput(`release_git rev-parse ${quote(`refs/tags/${tag}^{commit}`)}`, quote(commit)),
|
|
33
|
+
requireOutput(`release_git ls-remote --refs -- ${quote(destination.url)} ${quote(`refs/heads/${destination.branch}`)} ${quote(`refs/tags/${tag}`)}`, quote(`${parent}\trefs/heads/${destination.branch}`)),
|
|
34
|
+
`release_git push --atomic --no-follow-tags -- ${quote(destination.url)} ${quote(`${commit}:refs/heads/${destination.branch}`)} ${quote(`${tagObject}:refs/tags/${tag}`)}`,
|
|
35
|
+
].join('\n');
|
|
36
|
+
writeFileSync(resolve(output, 'PUBLISH.md'), `# Publish the reviewed public child\n\nThe producer has not pushed anything. Review the receipt and publication report before running these commands. They publish exactly the public child and its annotated tag to the declared destination with one ordinary atomic push. A moved branch or existing tag requires a fresh release artifact; do not force the push. Authentication uses your existing GitHub CLI credentials.\n\n\`\`\`sh\n${script}\n\`\`\`\n`, { flag: 'wx' });
|
|
37
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { mkdtemp,rm } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { shared } from '../../cli/shared.mjs';
|
|
4
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
5
|
+
import { realpathSync, existsSync, lstatSync, mkdirSync } from 'node:fs';
|
|
6
|
+
import { resolve, dirname, relative, isAbsolute } from 'node:path';
|
|
7
|
+
import { testSummary } from './test-summary.mjs';
|
|
8
|
+
|
|
9
|
+
export function git(repo, ...args) {
|
|
10
|
+
const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_')));
|
|
11
|
+
Object.assign(env, { GIT_NO_REPLACE_OBJECTS: '1', GIT_NO_LAZY_FETCH: '1', GIT_TERMINAL_PROMPT: '0',
|
|
12
|
+
GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null', GIT_GRAFT_FILE: '/dev/null' });
|
|
13
|
+
try {
|
|
14
|
+
return execFileSync('git', ['--no-replace-objects', '-c', 'core.useReplaceRefs=false', '-c', 'core.hooksPath=/dev/null', '-C', repo, ...args],
|
|
15
|
+
{ env, stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 128 * 1024 * 1024 });
|
|
16
|
+
} catch { throw Error('Git source operation failed'); }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function inspectSource(repo, ref) {
|
|
20
|
+
repo = realpathSync(repo);
|
|
21
|
+
if (git(repo, 'status', '--porcelain', '--untracked-files=all').length) throw Error('Release requires a clean source checkout');
|
|
22
|
+
const commit = git(repo, 'rev-parse', '--verify', '--end-of-options', `${ref}^{commit}`).toString().trim();
|
|
23
|
+
const tree = git(repo, 'rev-parse', `${commit}^{tree}`).toString().trim();
|
|
24
|
+
const records = git(repo, 'ls-tree', '-r', '-z', commit).toString().split('\0').filter(Boolean);
|
|
25
|
+
const entries = new Map(records.map(record => {
|
|
26
|
+
const separator = record.indexOf('\t');
|
|
27
|
+
const header = record.slice(0, separator), path = record.slice(separator + 1);
|
|
28
|
+
const [mode, type, oid] = header.split(' ');
|
|
29
|
+
if (!path || path.split('/').some(part => ['', '.', '..', '.git'].includes(part)) || path.includes('\\')) throw Error('Unsafe Git tree path');
|
|
30
|
+
return [path, { mode, type, oid }];
|
|
31
|
+
}));
|
|
32
|
+
const bytes = path => {
|
|
33
|
+
const entry = entries.get(path);
|
|
34
|
+
if (!entry || entry.type !== 'blob' || !['100644', '100755'].includes(entry.mode)) throw Error('Release input must be a committed ordinary file');
|
|
35
|
+
return git(repo, 'cat-file', 'blob', entry.oid);
|
|
36
|
+
};
|
|
37
|
+
const seed = JSON.parse(bytes('seed.json'));
|
|
38
|
+
const pkg = JSON.parse(bytes('package.json'));
|
|
39
|
+
const release = JSON.parse(bytes('RELEASE.json'));
|
|
40
|
+
if (!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?$/.test(seed.version) ||
|
|
41
|
+
pkg.version !== seed.version || release.version !== seed.version || !pkg.scripts?.verify) throw Error('Seed, package and release versions must agree, and verify must exist');
|
|
42
|
+
const timestamp = git(repo, 'log', '-1', '--format=%cI', commit).toString().trim();
|
|
43
|
+
return { repo, commit, tree, entries, bytes, seed, release, timestamp, tag: `v${seed.version}` };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function newOutput(repo, output) {
|
|
47
|
+
output = resolve(output);
|
|
48
|
+
for (let ancestor = output; ; ancestor = dirname(ancestor)) {
|
|
49
|
+
if (existsSync(ancestor) && lstatSync(ancestor).isSymbolicLink()) throw Error('Output cannot traverse symlinks');
|
|
50
|
+
if (ancestor === dirname(ancestor)) break;
|
|
51
|
+
}
|
|
52
|
+
if (existsSync(output)) throw Error('Output must be a new directory');
|
|
53
|
+
const rel = relative(repo, output);
|
|
54
|
+
if (!rel || (!isAbsolute(rel) && !rel.startsWith('..' + '/'))) throw Error('Output must be outside the source checkout');
|
|
55
|
+
mkdirSync(output, { recursive: true });
|
|
56
|
+
return output;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Keep child diagnostics private: arbitrary verify scripts can print credential
|
|
60
|
+
// values. Only exit status and numeric test summaries leave this boundary.
|
|
61
|
+
export async function command(command, args, cwd) {
|
|
62
|
+
const {createCommands}=await shared('commands.mjs');
|
|
63
|
+
const directory=await mkdtemp(resolve(tmpdir(),'rer-release-'));
|
|
64
|
+
const store=await createCommands(directory,{logs:[]},{terminal:()=>{},queue:{verb:'release'}});
|
|
65
|
+
const summary=testSummary(),env={...process.env,FORCE_COLOR:'0'};delete env.NODE_TEST_CONTEXT;
|
|
66
|
+
try {
|
|
67
|
+
await store.run('Release verification',command,args,{cwd,env,onChild:child=>child.stdout.on('data',bytes=>summary.write(bytes))});
|
|
68
|
+
return {status:'PASS',exitCode:0,...summary.finish()};
|
|
69
|
+
}catch(error){return {status:'FAIL',exitCode:error.childExitCode??null,...summary.finish()};}
|
|
70
|
+
finally{await store.closeCommands();await rm(directory,{recursive:true,force:true});}
|
|
71
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
2
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
3
|
+
|
|
4
|
+
const nodeFields = { tests: 'tests', pass: 'passed', fail: 'failed', skipped: 'skipped', cancelled: 'cancelled', todo: 'todo' };
|
|
5
|
+
const playwrightFields = { passed: 'passed', failed: 'failed', skipped: 'skipped', flaky: 'flaky', interrupted: 'interrupted', 'did not run': 'notRun' };
|
|
6
|
+
|
|
7
|
+
// Retain only bounded, complete lines and expose only numeric counters. These
|
|
8
|
+
// are reporter summaries, not individual test titles, retries or diagnostics.
|
|
9
|
+
export function testSummary() {
|
|
10
|
+
const decoder = new StringDecoder('utf8');
|
|
11
|
+
const counts = { tests: 0, passed: 0, failed: 0, skipped: 0, flaky: 0 };
|
|
12
|
+
let pending = '', oversized = false;
|
|
13
|
+
|
|
14
|
+
function line(raw) {
|
|
15
|
+
const text = stripVTControlCharacters(raw).replace(/\r$/, '');
|
|
16
|
+
const node = /^(?:ℹ|#)[ \t]+(tests|pass|fail|skipped|cancelled|todo)[ \t]+(\d+)[ \t]*$/.exec(text);
|
|
17
|
+
const playwright = /^ {2}(\d+) (passed \(\d+(?:\.\d+)?(?:ms|s|m|h|d)\)|failed|skipped|flaky|interrupted|did not run)[ \t]*$/.exec(text);
|
|
18
|
+
if (!node && !playwright) return;
|
|
19
|
+
const field = node ? nodeFields[node[1]] : playwrightFields[playwright[2].startsWith('passed ') ? 'passed' : playwright[2]];
|
|
20
|
+
const value = Number(node ? node[2] : playwright[1]);
|
|
21
|
+
if (!Number.isSafeInteger(value) || !Number.isSafeInteger((counts[field] ?? 0) + value) ||
|
|
22
|
+
(playwright && !Number.isSafeInteger(counts.tests + value))) return;
|
|
23
|
+
if (value || field in counts) counts[field] = (counts[field] ?? 0) + value;
|
|
24
|
+
// Node supplies its total explicitly; Playwright's outcome buckets are disjoint.
|
|
25
|
+
if (playwright) counts.tests += value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function consume(text) {
|
|
29
|
+
for (const part of text.split(/(?<=\n)/)) {
|
|
30
|
+
if (!oversized) {
|
|
31
|
+
if (pending.length + part.length > 8192) { pending = ''; oversized = true; }
|
|
32
|
+
else pending += part;
|
|
33
|
+
}
|
|
34
|
+
if (part.endsWith('\n')) {
|
|
35
|
+
if (!oversized) line(pending.slice(0, -1));
|
|
36
|
+
pending = ''; oversized = false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
write(bytes) { consume(decoder.write(bytes)); },
|
|
43
|
+
finish() {
|
|
44
|
+
consume(decoder.end());
|
|
45
|
+
if (!oversized && pending) line(pending);
|
|
46
|
+
pending = ''; oversized = false;
|
|
47
|
+
return { ...counts };
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|