Lightning to use sync v2 formats#4722
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4722 +/- ##
==========================================
- Coverage 89.93% 89.85% -0.09%
==========================================
Files 444 444
Lines 21979 22110 +131
==========================================
+ Hits 19767 19866 +99
- Misses 2212 2244 +32 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Broaden the GitHub-sync repo+branch guard from "no ancestor may share" to "no project in the same tree may share" (root, sandboxes, siblings, cousins all share one (repo, branch) namespace), and move the enforcement from a check-then-insert pattern in Elixir to a Postgres unique index. Two concurrent transactions in the same project family can no longer both succeed at READ COMMITTED. - Add Lightning.Projects.root_id/1 walking parent_id to the top. - Migration adds project_repo_connections.root_project_id (NOT NULL after backfill) and a unique index on (root_project_id, repo, branch). - ProjectRepoConnection sets root_project_id on insert, declares unique_constraint(:branch, name: ...), and exposes tree_unique_violation?/1 so the boundary can translate the constraint violation back to :branch_used_in_project_tree. - VersionControl.create_github_connection drops its in-memory pre-flight check and relies on the unique index for race-safety. - Tests cover sibling/cousin collisions, the renamed error, and a direct INSERT race that's only resolvable by the index. Co-authored-by: Claude <noreply@anthropic.com>
Security Review ✅
|
|
@josephjclark , @midigofrank , sorry... dug deeper and this needs a few more tweaks. will be ready for review shortly. |
|
High level QA notes for this branch:
I've run a smoke test and it looks great - the format looks right and I can run a workflow yaml through the latest CLI version EDIT: QA looks good! Just one issue executing from the project, see below |
|
@taylordowns2000 I notice an issue here where when viewing workflow yaml, the credential is not included. It's fine when exporting the project. But this affects main too! So I'm going to raise it out as a bug, No action needed here. |
This is super interesting @josephjclark - the reason is likely that credentials are not included in templates and "view" is the precursor to "publish as template". How should we handle this? (a) Leave it as is |
|
@taylordowns2000 I'll take that convo over to #4731 |
|
@taylordowns2000 one change I'm going to recommend on both sides: let's add something like The CLI currently writes a This helps tools like the CLI and maybe later the provisioner understand and anticipate the structure they are about to parse. The practical upshot of this is that the CLI is erroring if you do Now, we should probably align that version with the portability spec v4. And we should probably considering semvering it (at least major and minor, maybe not patch) So I recommend:
This will also make it much easier to coordinate the next schema change we want to make. BTW I've made a separate issue for the "should portability spec be a JSON schema" thing. Still want to defer that for that though |
Description
Closes #4718.
Switches Lightning to the v2 (portability spec) YAML format for the four user-facing surfaces below. The wire shape is now fully aligned with the OpenFn portability spec:
@openfn/kit/packages/lexicon/portability.d.ts.1. Canvas → "View workflow as code"
CodeViewPanelserializes the live workflow state to v2 YAML viaassets/js/yaml/format.ts(delegates toassets/js/yaml/v2.ts).1a. Export button
Same code path — the Download button writes the v2 YAML the panel renders.
1b. Publish as template button
TemplatePublishPanelwrites new templates in v2. Existing v1 templates remain readable on the Pick Template path (see 2a).2. Create new → "Import code as workflow"
YAMLImportPaneluses the format-aware façade: detects v1 vs v2 from pasted/uploaded YAML and dispatches to the matching parser. Empty-editor placeholder shows the v2 shape so new users land on the new format.2a. Pick template
TemplatePanelruns every template through the same façade. v1 templates load (lenient v1 parser), v2 templates parse strictly. A test asserts that the v1 and v2 versions of the same scenario produce structurally equivalent workflow state.3. Settings → "Export project" button
Projects.export_project/2callsLightning.Workflows.YamlFormat.serialize_project/2, which emits v2 YAML. The old v1 emitter (Lightning.ExportUtils) is removed.4. GitHub sync — block sandbox/ancestor branch collisions
A sandbox project can no longer link to the same
(repo, branch)as any of its ancestors:Projects.ancestor_ids/1walks theparent_idchain via a recursive CTE.ProjectRepoConnection.changeset/2validates against ancestor branches and surfaces an inline error in the Save form.VersionControl.create_github_connection/2re-checks the same condition transactionally so a concurrent insert can't race past the validation.Spec coverage
Fully implemented from the spec:
ProjectSpecid(required)ProjectSpecname?,description?ProjectSpecworkflows: WorkflowSpec[]ProjectSpeccredentials?: Credential[]ProjectSpeccollections?: string[]WorkflowSpecid?,name?idis the hyphenated nameWorkflowSpecsteps: Array<Job | Trigger>Stepid?,name?,next?StepEdgeboolean | string | ConditionalStepEdgeConditionalStepEdgecondition?(JS body),label?,disabled?Triggerenabled?Credentialname,owner(both required)Jobadaptors?: string[]Jobexpression?Jobconfiguration?: object | string<email>|<name>strings)Edge condition mapping
Lightning's internal
condition_typeenum ↔ speccondition(a JS expression body):condition:alwaysnext: <target>shortcut):on_job_success'!state.errors':on_job_failure'!!state.errors':js_expression<user JS body>(literal block when multi-line)Parser also accepts the spec's shortcuts:
next: { foo: true }→:alwaysnext: { foo: false }→:js_expressionbody"false"(Lightning has no:never)next: { foo: "<js>" }→:js_expressionbody verbatimLightning extensions (documented)
The portability spec marks
Triggerfields explicitly TODO (// TODO a trigger supports many more keys which the spec must support). Until the spec author fills these in, we keep two trigger-only extensions:type:discriminator (webhook/cron/kafka)openfn:block carrying cron expression, cron cursor reference, webhook reply mode, and Kafka configThese are commented as Lightning extensions in both
v2.exandv2.ts.What does NOT work yet (out of scope for this PR)
@openfn/cliintegration) is intentionally deferred — there is no Phase 5 import bridge in this branch.WorkflowSpec.globals,WorkflowSpec.start,WorkflowSpec.options,Step.previous,Job.configurationas object. Lightning has no internal equivalents today.test/integration/cli_deploy_test.exsis broken by the export cutover (it compares against a v1 fixture). Tagged:integrationand disabled by default; will be updated when the @openfn/cli suite is next exercised.Validation steps
id:,name:,steps:array, nojobs:/edges:, jobs useadaptors: [...], expression bodies underexpression: |.id:,workflows:is a sequence (- id: …),credentials:is a sequence of{name, owner},collections:is a sequence of names.!state.errors/!!state.errors.repo:main, create a sandbox of it, attempt to connect the sandbox torepo:main→ inline error, Save button disabled. Switch the sandbox torepo:dev→ Save enabled.AI Usage
Please disclose whether you've used AI anywhere in this PR (it's cool, we just
want to know!):
You can read more details in our
Responsible AI Policy
Pre-submission checklist
/reviewwith Claude Code)
(e.g.,
:owner,:admin,:editor,:viewer)