Skip to content

Implement ship target list#557

Closed
jsdavid278-cyber wants to merge 3 commits into
profullstack:masterfrom
jsdavid278-cyber:patch-2
Closed

Implement ship target list#557
jsdavid278-cyber wants to merge 3 commits into
profullstack:masterfrom
jsdavid278-cyber:patch-2

Conversation

@jsdavid278-cyber
Copy link
Copy Markdown
Contributor

No description provided.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 2, 2026

Greptile Summary

This PR replaces the stub target list action with a real implementation that reads and displays targets from sh1pt.config.ts. It introduces a text-based TypeScript config parser and exports readTargetSummary for testing. The three bugs flagged in the previous review round are all addressed, with matching regression tests added.

  • readObjectBody now strips comments before its regex search, preventing comment-embedded targets: patterns from being matched first.
  • readStringProperty uses delimiter-specific character classes ([^'] vs [^"]) to avoid cross-delimiter truncation.
  • isEscaped counts the full run of consecutive backslashes, so even-numbered backslash sequences no longer prevent a closing quote from being recognized.

Confidence Score: 4/5

Safe to merge after addressing the property-regex false-match issue, which can silently return a wrong enabled value when a use string contains a substring resembling enabled: false.

The three previously reported parser bugs are properly fixed and covered by new regression tests. One remaining gap: readBooleanProperty and readStringProperty scan the raw per-entry body without neutralising adjacent string-literal contents, so a use value containing enabled: false would be matched before the real enabled field, returning a wrong boolean silently.

packages/cli/src/commands/ship.ts — specifically the readStringProperty and readBooleanProperty functions and their interaction with adjacent string-valued properties in the same object body.

Important Files Changed

Filename Overview
packages/cli/src/commands/ship.ts Adds readTargetSummary and text-based config parser. Fixes three previously flagged parser bugs; one remaining gap where readBooleanProperty can match inside adjacent string literal values.
packages/cli/src/commands/ship.test.ts Adds three targeted regression tests covering the comment-stripping fix, cross-delimiter quote fix, and even-backslash-run fix.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[ship target list] --> B[readTargetSummary]
    B --> C{File exists?}
    C -- No --> D[throw Error]
    C -- Yes --> E[readFileSync source]
    E --> F[readObjectBody]
    F --> G[stripComments]
    G --> H{targets found?}
    H -- No --> I[return empty array]
    H -- Yes --> J[findMatchingBrace]
    J --> K[readTopLevelObjectEntries]
    K --> L[stripComments + keyRe loop]
    L --> N[findMatchingBrace per entry]
    N --> O[readStringProperty]
    N --> P[readBooleanProperty]
    O --> Q[TargetSummary array]
    P --> Q
    Q --> R{json flag?}
    R -- Yes --> S[JSON output]
    R -- No --> T[Colored terminal output]
Loading

Reviews (2): Last reviewed commit: "Add target parser regression tests" | Re-trigger Greptile

Comment thread packages/cli/src/commands/ship.ts
Comment thread packages/cli/src/commands/ship.ts
Comment thread packages/cli/src/commands/ship.ts Outdated
Strip comments before locating targets and handle mixed quotes plus escaped backslashes.
Covers comments, mixed quote delimiters, and escaped backslash parsing.
@jsdavid278-cyber
Copy link
Copy Markdown
Contributor Author

Addressed the parser review findings in the latest commits:

  • strip comments before locating the targets object
  • handle string values containing the opposite quote delimiter
  • correctly treat even runs of backslashes before a closing quote
  • added regression coverage for those cases

Local verification:

pnpm exec vitest run packages/cli/src/commands/ship.test.ts -> 7 tests passed
pnpm --filter @profullstack/sh1pt typecheck -> passed

Comment on lines +292 to +300
function readStringProperty(source: string, key: string): string | undefined {
const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(?:'([^']*)'|"([^"]*)")`).exec(source);
return match?.[1] ?? match?.[2];
}

function readBooleanProperty(source: string, key: string): boolean | undefined {
const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(true|false)`).exec(source);
return match?.[1] === undefined ? undefined : match[1] === 'true';
}
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.

P1 readBooleanProperty runs its regex against the raw per-entry body, which still contains string values from sibling properties. If a target's use value happens to embed a token matching enabled\s*:\s*(true|false) — e.g. use: "my enabled: false adapter" — the regex matches that substring first and returns the wrong boolean, silently overriding whatever the real enabled: field says. The same exposure exists for readStringProperty matching an enabled: pattern inside a longer use value. Stripping string-literal contents before running the property regexes would close this gap.

Suggested change
function readStringProperty(source: string, key: string): string | undefined {
const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(?:'([^']*)'|"([^"]*)")`).exec(source);
return match?.[1] ?? match?.[2];
}
function readBooleanProperty(source: string, key: string): boolean | undefined {
const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(true|false)`).exec(source);
return match?.[1] === undefined ? undefined : match[1] === 'true';
}
function readStringProperty(source: string, key: string): string | undefined {
const stripped = stripStringContents(source);
const mStripped = new RegExp(`${escapeRegExp(key)}\\s*:`).exec(stripped);
if (!mStripped) return undefined;
const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(?:'([^']*)'|"([^"]*)")`).exec(source);
return match?.[1] ?? match?.[2];
}
function readBooleanProperty(source: string, key: string): boolean | undefined {
const stripped = stripStringContents(source);
const mStripped = new RegExp(`${escapeRegExp(key)}\\s*:`).exec(stripped);
if (!mStripped) return undefined;
const match = new RegExp(`${escapeRegExp(key)}\\s*:\\s*(true|false)`).exec(
source.slice(mStripped.index),
);
return match?.[1] === undefined ? undefined : match[1] === 'true';
}
function stripStringContents(source: string): string {
let result = '';
let quote: '"' | "'" | undefined;
for (let i = 0; i < source.length; i += 1) {
const ch = source[i];
if (quote) {
result += ch === quote && !isEscaped(source, i) ? ((quote = undefined), ch) : ' ';
} else {
if (ch === '"' || ch === "'") quote = ch as '"' | "'";
result += ch;
}
}
return result;
}

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 4, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

1 similar comment
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 4, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

@ralyodio ralyodio closed this Jun 4, 2026
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