Skip to content

feat(connections): create a connection from a project folder (#1959)#1960

Merged
datlechin merged 7 commits into
mainfrom
feat/open-project-folder
Jul 25, 2026
Merged

feat(connections): create a connection from a project folder (#1959)#1960
datlechin merged 7 commits into
mainfrom
feat/open-project-folder

Conversation

@datlechin

@datlechin datlechin commented Jul 25, 2026

Copy link
Copy Markdown
Member

Closes #1959.

Point TablePro at a project folder and it reads the database settings the project already has, instead of you copying them out of a .env by hand.

File > Open Project Folder... or the welcome-screen menu opens a folder chooser. TablePro scans the folder, lists what it found (destination, then type + file + key), and on Continue opens the connection form pre-filled. Nothing is saved or connected until you save it.

Approach

This reuses the pipeline Import from URL already runs on:

scanner -> ParsedConnectionURL -> WindowOpener.openConnectionFormFromURL
        -> PendingNewConnectionImport -> ConnectionFormCoordinator.applyParsed

Everything from ParsedConnectionURL onward is untouched code that already handles every database type, including Mongo multi-host, Redis DB index, Oracle service names and SSH. ConnectionURLParser needed no changes.

Two shapes were considered and rejected:

  • A new ForeignAppImporter. Its contract is "is competitor app X installed, read its store" (appBundleIdentifier, installedAppURL(), readsPasswordsFromKeychain), none of which a folder has. ForeignAppImporterRegistryTests also hard-codes count == 6.
  • Routing through ConnectionExportService.performImport. That exists for N-way batch import with duplicate resolution and post-import Keychain restore. This flow produces one pre-filled form, and going through that pipeline would write to storage before the user has seen the connection form.

Files read

File What it reads
.env family DATABASE_URL, MONGODB_URI, REDIS_URL, TURSO_DATABASE_URL and similar; Laravel DB_*; Postgres/MySQL/MariaDB/Mongo/SQL Server container variables; PG*
wp-config.php DB_NAME, DB_USER, DB_PASSWORD, DB_HOST
prisma/schema.prisma datasource provider and url, including env("...")
config/database.yml Rails development section, following anchors and << merge keys, resolving ENV["..."]
docker-compose.yml, compose.yaml Database services, using the published host port
application.properties, application.yml spring.datasource.*
appsettings.json ConnectionStrings entries

Template files (.env.example, .env.sample, .env.template, .env.dist) are skipped. .envrc is a shell script, so it is never read or run. A project using more than one engine gets a row per engine; pooled and direct URLs for the same engine collapse to the direct one.

Adds Yams 5.4.0 for the YAML formats.

The dotenv dialect

There is no dotenv spec and the real libraries disagree. Running 105 cases through motdotla, python-dotenv, phpdotenv, ruby-dotenv, godotenv, Node --env-file and Bun, they disagree on 57 of them.

The parser targets the intersection plus a documented superset, and the choice is pinned by tests: inline # requires preceding whitespace (so p#ssw0rd survives), double quotes expand \n \r \t \\ \" \' and leave unknown escapes intact, single quotes are fully literal, ${VAR}/$VAR/${VAR:-default} resolve same-file then process env, last duplicate wins, one malformed line does not abort the file.

Unresolvable indirection is reported rather than guessed, in both .env (${{Postgres.DATABASE_URL}}) and Compose (${DB_PASSWORD} with nothing to resolve it).

Security

The scanner reads files from repos the user may have just cloned, so containment is the design, not a checklist.

  • git init && ln -s /etc/passwd .env && git add -A stores a mode-120000 blob that a plain git clone materializes. Files are filtered on isRegularFile && !isSymbolicLink before any read, and symlinked directories are never descended. Both cases have regression tests using real symlinks.
  • Path comparison uses .standardizingPath only. resolvingSymlinksInPath() and realpath(3) rewrite /private/tmp and /tmp in opposite directions, and one returns its input unchanged for a nonexistent path, so a naive containment check passes vacuously.
  • Absolute deny-list (~/.ssh, ~/.aws, ~/.gnupg, ~/.docker, Keychains, /etc) enforced even if that folder is picked directly.
  • Depth 4, 1 MB per file, 20k entry ceiling, and a directory skip list. .gitignore is deliberately not honored, since it ignores exactly the files this feature targets.
  • $(...) is blocked in the lexer rather than merely unwired, because ruby-dotenv genuinely runs it through a subshell.
  • No PasswordSource is ever synthesized from scanned content, since PasswordSource.command runs /bin/bash -c.
  • No scanned value reaches OSLog at any level. Only counts and error kinds are logged.

Reads stay inside the chosen folder with one deliberate exception: WordPress permits wp-config.php one level above the site, so that single filename is probed in the parent and labelled ../wp-config.php in the UI. This is documented.

A connection whose source file or host is named for production starts at Safe Mode Alert rather than Silent, reusing the existing SafeModeLevel machinery. The marker match is token-based, so production_orders counts and products.example.com does not.

UI

The findings list follows Apple's documented row anatomy rather than a label/value grid: leading type icon, destination as the title (middle-truncated, file paths shown relative to the project root), Type · file · key as the subtitle, and trailing SF Symbols for status with .help() plus .accessibilityLabel. .listStyle(.bordered) is the macOS-only dialog list style. Full sentences for the selected row go in a detail strip under the list, which is HIG's prescribed escape hatch for content that does not fit a row.

Passwords are never rendered. A row shows a key symbol and the value goes straight to the Keychain on save, per HIG's "Never prepopulate a password field".

Also in this PR

Not part of #1959, but a consequence of it or found while testing it:

  • Welcome pane regrouped. Adding a fourth button made the stack incoherent, so the three "add from an existing source" actions moved into an Add from Existing pull-down beside Create Connection. HIG's Pull-down buttons page describes this case directly: "An Add button could present a menu that lets people specify the item they want to add ... without requiring additional buttons in your interface." Try Sample Database moved to the connection list, which already offers it when empty.
  • Import from URL... added to the File menu. It previously existed nowhere in the menu bar, reachable only from the type-chooser sheet footer.
  • Dead File-menu items fixed. Import from Other App..., Open Project Folder..., Import Connections... and Export Connections... posted to AppCommands subjects only WelcomeViewModel observes, so with the welcome window hidden they silently did nothing.
  • The + tooltip no longer hard-codes ⌘N. New Connection resolves through the user-remappable keyboard map, so the tooltip could lie; it now derives from BoundKey.displayString. The ⌘N / ⌘, hint chips were removed for the same reason plus lack of any HIG basis for key equivalents outside menus.
  • License activation sheet now opens. It was built and onAppear fired, but AppKit threw NSInternalInconsistencyException from -[NSRemoteView containingWindowWillOrderOnScreen:] while ordering the sheet window, so it never became visible; afterwards the state stuck and further clicks were no-ops. Cause was forcing focus into a text field during sheet presentation, which spins up AutoFill's remote view mid-ordering.
  • Same latent bug fixed in the two MCP token sheets. GeneralPaneView was left alone, since it is a Form in a window rather than a sheet.

Note for future readers: the assertion above is thrown inside Apple's ViewBridge from an AppleInternal build path and may be a beta-OS issue. Removing the forced focus avoids provoking it; please do not restore those .defaultFocus calls without rechecking.

Tests

~100 tests, all passing, plus the existing ConnectionURLParserTests still green.

Bugs caught by tests and fixed in source rather than worked around:

  • Compose interpolation ran on raw text, so Symfony's ${POSTGRES_PASSWORD:-!ChangeMe!} substituted !ChangeMe! into YAML where ! starts a tag, and the file failed to parse. Docker does interpolation post-parse; now so does this.
  • Dedup missed equivalent connections because ConnectionURLParser returns port == nil for a default port, so .env and appsettings.json describing the same database did not match. Identity now resolves through type.defaultPort.
  • The URL normalizer's last-@ userinfo split was applied to file-based URLs too, so sqlite:///Users/me/@scope/db.sqlite became sqlite://%2FUsers%2Fme%2F@scope/db.sqlite and pointed at a nonexistent file. It now skips any URL whose remainder begins with /, which also protects Postgres's unix-socket form.

NSOpenPanel is not scriptable in CI, so there is no UI automation for the picker itself, consistent with the existing Import from Other App flow. Every piece of logic behind it is covered at unit and integration level.

Notes

  • swiftlint --strict clean on all touched files.
  • TablePro/Resources/Localizable.xcstrings is deliberately not in this branch. It already had unrelated pending extractions in the working tree, and Xcode regenerates it on build.
  • New docs page at docs/features/project-folder-import.mdx, in the Workflow group, cross-linked from the connection URL page.

@mintlify

mintlify Bot commented Jul 25, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jul 25, 2026, 2:34 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27e9e7d6f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +39 to +40
if directory == rootPath, rootDocument == nil || file.match.tier == .nearCertain {
rootDocument = document

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Resolve configs from a deterministic environment file

When a project contains multiple root dotenv files, every .nearCertain file overwrites rootDocument; because the walker sorts paths, a common .env plus .env.production.local setup resolves Prisma, Rails, and Compose references using the production file simply because it sorts later. This can silently prefill production credentials, while the resulting candidate is attributed to the consuming config and may not trigger the production-path heuristic. Select the environment according to each format's precedence rather than allowing the last near-certain file to win.

Useful? React with 👍 / 👎.

Comment on lines +38 to +40
parsed.database.lowercased(),
parsed.username.lowercased(),
].joined(separator: "\u{1F}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve database-specific fields in deduplication

When two candidates share type, host, port, database, and username but differ in a database-specific selector, this identity collapses distinct connections. For example, redis://host/0 and redis://host/1 differ only in redisDatabase, and Oracle services are stored in oracleServiceName while database is empty, so only one candidate survives. Include these fields, along with Mongo's multiHost, in the identity.

Useful? React with 👍 / 👎.

Comment on lines +80 to +81
if let candidate {
return candidate

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Collect every distinct URL from a dotenv file

When one dotenv file defines more than one relational database, such as a PostgreSQL DATABASE_URL and a separate MYSQL_URL, firstURLCandidate returns after the first valid key and the second connection is never shown. The same early-return behavior also hides multiple Mongo or Redis URLs. Collect all valid keyed candidates and let the existing identity-based deduplication remove aliases that describe the same connection.

Useful? React with 👍 / 👎.

Comment on lines +192 to +193
while let open = remainder.range(of: "${") {
result += remainder[remainder.startIndex..<open.lowerBound]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Expand unbraced Compose variables

When Compose uses unbraced interpolation such as POSTGRES_PASSWORD=$DB_PASSWORD or a published port containing $DB_PORT, this loop never sees the variable because it searches only for ${. The scanner consequently imports the literal password $DB_PASSWORD or fails to recognize the published port, producing a connection that does not match the service. Implement $VAR interpolation and Compose's $$ escaping in addition to the braced form.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01a979e6f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +131 to +133
let parent = rootURL.standardizedFileURL.deletingLastPathComponent()
let candidateURL = parent.appendingPathComponent("wp-config.php")
let path = candidateURL.standardizedFileURL.path

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep WordPress scanning inside the selected folder

When the selected folder has a sibling wp-config.php in its parent, this constructs and reads that file even though the user granted the scan only for rootURL. This violates the documented containment boundary and can load credentials from outside the chosen project; remove the parent lookup or require the user to select that parent explicitly.

Useful? React with 👍 / 👎.

Comment on lines +149 to +151
let prefix = variables["MARIADB_PASSWORD"] != nil || variables["MARIADB_DATABASE"] != nil
? "MARIADB"
: "MYSQL"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recognize MariaDB credentials from the service type

When a MariaDB Compose service defines only common variables such as MARIADB_ROOT_PASSWORD or MARIADB_USER, this condition selects the MYSQL prefix despite type == .mariadb. The resulting candidate silently loses the configured password/user and opens a connection form that cannot authenticate; select MARIADB from the database type or recognize every supported MariaDB variable.

Useful? React with 👍 / 👎.

Comment on lines +83 to +90
let sqlServerHints = ["trusted_connection", "encrypt", "trustservercertificate", "integrated security"]
if sqlServerHints.contains(where: { keywords[$0] != nil }) {
return .mssql
}
if keywords["uid"] != nil || keywords["pwd"] != nil {
return .mysql
}
return nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept standard SQL Server connection strings using Database

When an appsettings entry uses the common SqlClient form Server=db;Database=app;User Id=sa;Password=..., none of these provider checks match because it uses neither Initial Catalog nor one of the listed SQL Server hints, and User Id/Password also do not satisfy the MySQL uid/pwd check. The connection is therefore omitted entirely even though all required SQL Server fields were parsed.

Useful? React with 👍 / 👎.

Comment on lines +23 to +25
guard let adapter = YamlMappingSupport.string(selected.mapping["adapter"]),
let type = adapterType(adapter) else {
return []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor URL-based Rails database configurations

When a Rails environment is configured solely with url: <%= ENV["DATABASE_URL"] %>, which Rails supports, the selected mapping has no adapter and this guard discards it before resolving the URL. Such projects produce no Rails candidate even when the referenced value is available in the indexed dotenv file; derive the database type and fields from url when present.

Useful? React with 👍 / 👎.

@mintlify

mintlify Bot commented Jul 25, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟡 Building Jul 25, 2026, 2:33 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45126ca585

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

Button(String(localized: "Open Project Folder...")) {
AppCommands.shared.openProjectFolder.send(())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Route the menu action independently of the Welcome window

When a connection window is open, TabRouter.closeWelcomeWindows() has already closed the only view whose WelcomeViewModel subscribes to this subject and owns the project-scan sheet. Consequently, File > Open Project Folder... either has no live subscriber or updates sheet state belonging to a closed Welcome window, so the advertised menu route cannot display its results outside the Welcome screen. Route this command through an application-level coordinator or reopen the Welcome window before starting the flow.

Useful? React with 👍 / 👎.

Comment on lines +46 to +49
if lowercased.hasPrefix("jdbc:"),
!lowercased.hasPrefix("jdbc:sqlserver:"),
!lowercased.hasPrefix("jdbc:oracle:") {
value = String(value.dropFirst("jdbc:".count))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Parse SQL Server and Oracle JDBC URL forms

When a Spring datasource uses standard database-specific JDBC syntax, these exclusions leave it in a form the downstream generic URL parser cannot handle: jdbc:sqlserver://localhost:1433;databaseName=app fails because the semicolon properties make the synthesized URLComponents authority invalid, while common Oracle forms such as jdbc:oracle:thin:@//host:1521/service fail the subsequent :// guard entirely. Such Spring projects silently produce no candidate; normalize or parse these JDBC formats explicitly.

Useful? React with 👍 / 👎.

Comment on lines +65 to +67
let separators: Set<Character> = ["=", ":"]
guard let index = line.firstIndex(where: { separators.contains($0) }) else {
return nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept whitespace-separated Java properties

When a valid Java properties file uses whitespace as the key/value separator, for example spring.datasource.url jdbc:postgresql://localhost/app, this search finds neither = nor : and drops the assignment. Since whitespace separation is part of the .properties format, the scanner then misses the datasource URL—or separately loses its username and password—even though Spring accepts the file.

Useful? React with 👍 / 👎.


static func parseKeywords(_ connectionString: String) -> [String: String] {
var keywords: [String: String] = [:]
for segment in connectionString.split(separator: ";") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor quoting when splitting appsettings connection strings

When an ADO.NET connection-string value contains a semicolon inside quotes, such as Password="pa;ss", splitting blindly at every semicolon records the password as "pa and discards the remainder. The candidate is still presented as having a password but cannot authenticate, so the keyword parser needs to recognize quoted or escaped delimiters.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e459b601e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +62 to +63
if let turso = firstURLCandidate(context, keys: tursoURLKeys) {
candidates.append(turso)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Merge the Turso auth token into the candidate

When a dotenv file uses the standard TURSO_DATABASE_URL=libsql://... plus TURSO_AUTH_TOKEN=... pair, this path parses only the URL and leaves parsedURL.password empty. The connection form uses that password field for libSQL's API token, so the resulting Turso connection is prefilled without its credentials and fails authentication; merge TURSO_AUTH_TOKEN into the parsed candidate.

Useful? React with 👍 / 👎.

let database = databaseKind(for: image) else {
return nil
}
let variables = environmentVariables(service["environment"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Load service credentials declared through env_file

When a Compose database service puts POSTGRES_*, MYSQL_*, or similar variables in env_file, this reads only the inline environment block. The candidate therefore gets blank or default credentials even though Compose supplies the referenced file at runtime; a separately scanned dotenv candidate also cannot reliably compensate because it lacks the service's published-port mapping. Resolve and merge the service's env_file entries before applying credentials.

Useful? React with 👍 / 👎.

Comment on lines +76 to +77
if name.contains("mongo") {
return ServiceDatabase(type: .mongodb, defaultPort: 27_017)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude non-database images containing database names

When a Compose project includes a common sidecar such as mongo-express, this substring check classifies the admin UI as a MongoDB server and emits a bogus 127.0.0.1:27017 candidate. If the real Mongo service has credentials, its identity differs and deduplication retains both entries, leaving users able to select an unusable connection; match known database image repositories rather than any image containing mongo.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c3e17659f8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +34 to +37
let isProduction = ScannedProductionHeuristic.isProduction(
relativePath: sourceRelativePath,
host: parsedURL.host,
database: parsedURL.database

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Include the selected environment in production detection

When a Rails database.yml selects a production: section whose host and database lack a production marker (for example, db.example.com/app), this check ignores the sourceKey value production and leaves safeModeLevel unset. TransientConnectionFactory.build then defaults the imported connection to Silent rather than Alert, removing the intended production protection; include the selected environment/source key in the heuristic.

Useful? React with 👍 / 👎.

Comment on lines +63 to +64
if let nested = firstNestedDatabase(mapping) {
return ("\(name).\(nested.key)", nested.mapping)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Return every nested Rails database

When a Rails environment defines multiple databases such as development.primary and development.animals, this immediately returns the first nested mapping in alphabetical order, so every other connection is omitted and the chosen one may not even be primary. Extract a candidate for each nested mapping with an adapter instead of reducing the environment to one entry.

Useful? React with 👍 / 👎.

Comment on lines +30 to +33
private static func lastUserInfoSeparator(in remainder: String) -> String.Index? {
let boundary = remainder.firstIndex { $0 == "?" || $0 == "#" } ?? remainder.endIndex
let searchable = remainder[remainder.startIndex..<boundary]
return searchable.lastIndex(of: "@")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop treating path at-signs as credentials

When a credential-free URL contains a legal @ in its database/path, such as postgresql://localhost/my@db, this searches beyond the authority and treats that path character as the user-info separator. Normalization rewrites the value to postgresql://localhost%2Fmy@db, which is then parsed as host db with username localhost/my; limit user-info detection to the authority portion.

Useful? React with 👍 / 👎.

Comment on lines +39 to +40
let trimmed = entry.value.trimmingCharacters(in: .whitespaces)
return trimmed.isEmpty ? nil : trimmed

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve whitespace inside quoted dotenv values

When a quoted dotenv credential intentionally begins or ends with whitespace, this trims characters that the quoted syntax preserves; for example, DB_PASSWORD=" secret " is imported as secret, causing authentication to fail. Unquoted values are already trimmed by readUnquoted, so document lookup should retain the decoded value of quoted entries.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80ccaf8667

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +158 to +161
case .mongodb:
fields.username = variables["MONGO_INITDB_ROOT_USERNAME"] ?? ""
fields.password = variables["MONGO_INITDB_ROOT_PASSWORD"] ?? ""
fields.database = variables["MONGO_INITDB_DATABASE"] ?? ""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Set the Mongo root authentication database to admin

When a Compose service supplies MONGO_INITDB_ROOT_USERNAME/MONGO_INITDB_ROOT_PASSWORD together with MONGO_INITDB_DATABASE=app, the official image creates the root user in admin, but this candidate sets database to app without setting authSource. MongoDBConnection consequently uses the nonempty application database as the effective authentication source, so the imported connection fails authentication; set authSource to admin for these root credentials.

Useful? React with 👍 / 👎.

Comment on lines +49 to +51
func environment(near url: URL) -> DotenvDocument? {
let directory = url.deletingLastPathComponent().standardizedFileURL.path
return documentsByDirectory[directory] ?? rootDocument

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Search ancestor directories for the nearest dotenv file

When the selected folder is a monorepo and a nested application has apps/api/.env beside apps/api/config, a config such as apps/api/config/database.yml cannot resolve its ENV[...] fields: this lookup checks only the config file's exact directory and then jumps to the scan root, skipping apps/api/.env. That can produce a candidate with default host and blank credentials even though the relevant dotenv file was scanned; walk upward to the project root and use the nearest applicable document.

Useful? React with 👍 / 👎.

Comment on lines +32 to +39
let candidate = ScannedConnectionCandidate(
parsedURL: merged,
sourceRelativePath: relativePath,
sourceKey: "spring.datasource.url",
kind: .springYaml,
tier: .configFile
)
return [candidate]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Return candidates from every Spring YAML document

When application.yml contains multiple --- documents with datasource URLs for different Spring profiles, this returns immediately after parsing the first document. Every later profile connection is silently omitted from the findings, even though loadDocuments loaded it; accumulate each parseable candidate and return after the loop instead.

Useful? React with 👍 / 👎.

@datlechin
datlechin merged commit b830044 into main Jul 25, 2026
3 checks passed
@datlechin
datlechin deleted the feat/open-project-folder branch July 25, 2026 23:36
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.

Open DB by selecting a project directory

1 participant