Skip to content

CLI: handle multiple local copies of the same project - #1575

Open
midigofrank wants to merge 4 commits into
mainfrom
frank/ofn-4589
Open

midigofrank wants to merge 4 commits into
mainfrom
frank/ofn-4589

Conversation

@midigofrank

@midigofrank midigofrank commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Short Description

Lets a workspace hold several local copies of the same project (same UUID/id under different aliases, or the same UUID on different hosts) without the CLI throwing MultipleMatchingProjectsError where the intent is clear.

Fixes #1119
Fixes #1541 (OFN-4589)

Implementation Details

Aliases come from state file names (main@app.openfn.org.yaml), so within a workspace they're the one identifier that's unique per copy. The fix uses them wherever lookups used to rely on UUID or id:

  • Matching: an exact alias match wins when every other match is a copy of the same project (same UUID). Real ambiguity still throws, and the error now lists alias@host and suggests using one.
  • openfn.yaml now records alias, so the CLI knows which copy is checked out (this resolves the TODOs in Workspace.ts and deploy.ts). It stays local: it's stripped from project metadata and never sent to Lightning.
  • getTrackedProject() prefers alias + host. pull and deploy with no project or target use it rather than the UUID.
  • Project paths are keyed by project instead of id, so copies no longer overwrite each other. merge now writes to the right file, and merge --base writes back to the base file. That only worked by luck before.
  • fetch <uuid> --endpoint X -o file skips the local workspace lookup, since it has everything it needs (OFN-4589). With -o, the output path is the target and the workspace isn't searched.

Behaviour is unchanged for an openfn.yaml with no alias (every existing workspace until its next checkout or pull): lookups fall back to UUID/id and throw on duplicates rather than guess.

QA Notes

  • Duplicate a state file in .projects/ (e.g. copy main@<host>.yaml to backup@<host>.yaml), then:
    • openfn project checkout backup works, and openfn.yaml gets alias: backup
    • openfn project pull (no argument) pulls into the checked-out copy
    • openfn project fetch <uuid> with no -o still errors, because it's unclear which file to write
  • openfn project fetch <uuid> --endpoint <url> -o out.yaml succeeds with duplicates present
  • Not covered by automated tests: the pull and deploy defaults. There are no pull tests in the CLI yet, so those are worth a manual check.

AI Usage

  • I have used Claude Code
  • I have used another model
  • I have not used AI

A workspace can hold several local copies of one project, each from its
own state file (main@app.openfn.org.yaml, backup@app.openfn.org.yaml).
They share a UUID and id, so anything keyed on those mixed them up.
Copies overwrote each other's entries in the path map, openfn.yaml had
no way to say which copy was checked out, and get() threw even when
given an exact alias.

matchProject now returns the exact alias match when all the other
matches share its UUID. Real ambiguity still throws, and the error
lists each clash as alias@host and suggests using one.

Workspace keeps project paths by Project rather than by id, so each
copy keeps its own file. getProjectPath() now takes a Project.
extractConfig writes the active project's alias to openfn.yaml, and
getTrackedProject() looks that alias and host up first. The alias is
left out of project.openfn so the metadata doesn't store it twice.

merge used getProjectPath(id) to find where to write. With --base it
only got the right file because the id-keyed map happened to point
there. It now writes back to the --base file directly.
…explicit

`openfn project fetch <uuid> --endpoint X -o file.yaml` threw
MultipleMatchingProjectsError if two local project files shared the
UUID. With a full UUID, an endpoint and an output path, the command has
everything it needs, so the local copies don't matter.

Skip the local workspace lookup in that case. Also stop searching the
workspace for an output target once -o is given, because the path is the
target. Duplicates still throw when the UUID, endpoint or output path is
missing.
When no project was given, pull and deploy fell back to the active
project's UUID. That throws if another local copy shares the UUID,
even though openfn.yaml says which one is checked out.

If openfn.yaml records an alias, pull now defaults to the tracked
project's alias@host and deploy uses the tracked project. Without an
alias, both fall back to the UUID as before.
@midigofrank midigofrank changed the title Cli: handle overlapping project files CLI: handle multiple local copies of the same project Oct 6, 2026
@midigofrank
midigofrank marked this pull request as ready for review October 6, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New Issues

Development

Successfully merging this pull request may close these issues.

Sync: when fetching a file from a path, CLI will moan about id conflicts CLI: handle overlapping state files

1 participant