Skip to content

Geko [minor] Added support for SPM-only projects - #133

Open
dmnre wants to merge 2 commits into
mainfrom
spm-only-projects
Open

dmnre wants to merge 2 commits into
mainfrom
spm-only-projects

Conversation

@dmnre

@dmnre dmnre commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds support for SPM-only projects — repositories that contain a standalone Package.swift at the root and are managed purely through Swift Package Manager, without an Xcode project or the Geko/ dependency directory layout.

Previously Geko only resolved Swift Package Manager dependencies that lived under the Geko/Package.swift path. This change lets a plain Swift package (with its own Package.swift, Package.resolved, and .build directory) be recognised and used as the project's dependency source.

Motivation and Context

This change enables Geko to treat a root-level Package.swift as the project's dependency manifest, so SPM-only projects get the same dependency resolution, graph generation, and fetch/update support as existing projects, while preserving full backwards compatibility for the existing Geko/Package.swift layout.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@dmnre
dmnre requested a review from a team September 20, 2026 17:15
// swiftlint:disable:next force_try
try! RelativePath(validating: "\(Constants.gekoDirectoryName)/Package.swift")
return traverseAndLocate(at: locatingPath, appending: subPath)
let rootDirectory = locatingPath

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn’t we lose traverseAndLocate here for locating the root? Or was the recursion removed intentionally for some reason?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the expected precedence here?

How should Geko behave in a repository that contains both a publishable root Package.swift and Geko/Package.swift?

More broadly, do we want to support the case where a repository has both:

  • a regular/publishable Swift package at the root, and
  • a separate Geko/Package.swift used for Geko configuration?

If so, I think we should define the expected resolution logic explicitly. Which package should take precedence, and how should Geko distinguish between them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn’t we lose traverseAndLocate here for locating the root? Or was the recursion removed intentionally for some reason?

I added the rootDirectory resolution using a call to rootDirectoryLocator.locate(from:).

How should Geko behave in a repository that contains both a publishable root Package.swift and Geko/Package.swift?

Using two Package.swift files complicates implementation and worsens the user experience, as having two Package.swift files can confuse the user about which one is more prioritized, and overall the less prioritized one would essentially just sit there unused.

a separate Geko/Package.swift used for Geko configuration?

The Package.swift at the root can also be used for Geko configuration.

In general, I didn't find any compelling reasons to have two Package.swift files, so I added a generation error when both a root Package.swift and a Geko/Package.swift are present.

)
}
var newDependenciesPaths = Set<AbsolutePath>()
for (path, project) in zip(paths, projects) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic is a bit risky because we’re using zip with two collections whose iteration order isn’t guaranteed.

projects is created from Array(paths), but then we zip it with paths again. On top of that, this is a compactMap, so PackageInfoMapper.map may return nil and shift all subsequent path/project pairs.

As far as I understand, zip here should operate on arrays with a stable matching order. Could we preserve the path together with the mapped project instead, for example by returning (path, project) directly from the concurrent mapping?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I've fixed it.

Set(manifestFilesLocator.locateManifests(at: path).map(\.0))
}

public func validateHasProjectOrWorkspaceManifest(at path: AbsolutePath) throws {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe its time to rename?

Hint: validateHasGeneratableManifest

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants