Skip to content

SCAL-338917 chore: resolve @version SDK placeholders automatically at release time - #676

Open
sastaachar wants to merge 1 commit into
mainfrom
chore/auto-resolve-version-placeholders
Open

sastaachar wants to merge 1 commit into
mainfrom
chore/auto-resolve-version-placeholders

Conversation

@sastaachar

@sastaachar sastaachar commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Jira: SCAL-338917

The problem

Every public member carries a @version tag naming the SDK version it is available from, and those comments are published to developers.thoughtspot.com. But the next published version isn't decided until bump-version-and-pr runs — so at merge time the author has no way to know it, and guesses.

The guesses are wrong. Six SDK versions cited in src/** were never published to npm:

Version Annotations
1.19.1 1 already live on the docs site, version doesn't exist
1.27.0 5 already live, wrong
1.30.2 1 already live, wrong
1.34.0 12 already live, wrong
1.53.0 31 see below
1.54.0 14 guessed two minors ahead

This got confirmed while the branch was being written: main carried 45 annotations promising 1.53.0 and 1.54.0 when v1.52.1 — a patch — was cut and published. All 45 now tell external developers a member is available from a version that does not exist.

The fix

Authors write a placeholder for the SDK half, and the real ThoughtSpot release for the other:

/**
 * @version SDK: <TBD> | ThoughtSpot Cloud: 26.10.0.cl
 */

Only the SDK half may be <TBD>. The ThoughtSpot release isn't derivable from this repo, so no automation can fill it in — the author has to state it.

One script, scripts/resolve-version-placeholders.js, wired in at three points:

  • resolve-versions — runs in bumpVersion.yml right after npm version and before docgen, so the typedoc JSON the docs site is built from carries real versions. src added to that workflow's git add, so the rewrite is reviewable in the chore: release PR.
  • lint-versions — new validate-version-tags job in pr-checks.yml. Fails if <TBD> appears after the |, or if a <TBD> annotation names no ThoughtSpot release at all — caught while the author is still around to fix it.
  • check-versions — added to prepublishOnly. A release can't ship an unresolved placeholder in its public API docs.

Since SDK and Cloud minors advance in lockstep (1.N26.(N-43).0.cl), the release step also warns when the two disagree. Warning, not failure — back-dating to an earlier release is sometimes deliberate.

Notes for review

  • Nothing existing changes. The missing-ThoughtSpot-release rule only fires on lines still carrying <TBD>, so the hundreds of legacy @version SDK: 1.19.0 annotations stay valid. Both gates pass clean on current main.
  • No src/** file is touched by this PR — it's tooling, workflows and docs only.
  • 30 unit tests, 99% line coverage, in their own jest.config.scripts.js so build tooling stays out of the shipped src/ coverage thresholds. npm test now runs both projects.
  • Full SDK suite on this branch: 47/48 suites pass. The one failure (ts-embed.spec.ts → "should trigger Navigate only after UpdateEmbedParams has settled") passes in isolation and is order-dependent/flaky, pre-existing, and unrelated — this PR changes no src file.

Follow-ups, deliberately not in this PR

  1. Convert the 45 wrong annotations on main (31× 1.53.0, 14× 1.54.0) to <TBD> so the next release self-corrects. Happy to add it here if you'd rather it land together.
  2. The 4 historical wrong versions are live on developers.thoughtspot.com and need a docs correction.
  3. Unrelated defect found while testing: jest.config.sdk.js testPathIgnorePatterns doesn't exclude /.claude/, so a local npm test also runs 242 specs from stale checkouts under .claude/worktrees/ — 232 suites, 981s, and phantom failures. CI is unaffected.

🤖 Generated with Claude Code

Contributors have to name the SDK version a member is available from, but the
next published version is not decided until the bump-version-and-pr workflow
runs, so today they guess. Six SDK versions cited in src/ were never published:
1.19.1, 1.27.0, 1.30.2 and 1.34.0 already shipped to the docs site that way, and
main currently carries 31 annotations for 1.53.0 and 14 for 1.54.0 — all of them
wrong the moment a release is cut as a patch instead of a minor.

Authors now write `@version SDK: <TBD> | ThoughtSpot Cloud: 26.10.0.cl` and the
release bump substitutes the real SDK version, before docgen regenerates the
typedoc JSON the docs site is built from, committed as part of the release PR.

Only the SDK half may be <TBD>. The ThoughtSpot release is not derivable from
this repo, so no automation can fill it in and the author must state it; the new
validate-version-tags PR check enforces that while the author is still around to
fix it. Because SDK and Cloud minors advance in lockstep (1.N -> 26.(N-43).0.cl),
the release step also warns when the two disagree — a warning, not a failure,
since back-dating to an earlier release is sometimes correct.

check-versions runs on prepublishOnly so a release can never ship an unresolved
placeholder in its public API docs.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@sastaachar
sastaachar requested a review from a team as a code owner September 17, 2026 06:59

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces a new version placeholder resolution script (resolve-version-placeholders.js) along with its test suite and Jest configuration, integrating a workflow to automatically resolve <TBD> SDK version placeholders in JSDoc tags while validating ThoughtSpot release versions. Feedback on the changes includes addressing a validation bypass for empty or whitespace-only ThoughtSpot segments, refactoring the resolution script to fail-fast before writing partial changes to disk, and correcting a British English spelling in the test suite to adhere to the repository's American English style guide.

Comment on lines +149 to +151
if (thoughtSpotSegments.length === 0) {
problems.push(PROBLEM.MISSING_THOUGHTSPOT);
}

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.

medium

If a developer writes @version SDK: <TBD> | (with an empty or whitespace-only ThoughtSpot segment), thoughtSpotSegments.length will be 1 (containing [' ']). This bypasses the thoughtSpotSegments.length === 0 check, meaning PROBLEM.MISSING_THOUGHTSPOT is not added. As a result, the PR check (--lint) will pass, and during release, it will resolve to @version SDK: 1.53.0 | , which is malformed and violates the style guide.

We should update the check to also flag empty or whitespace-only segments as missing.

Suggested change
if (thoughtSpotSegments.length === 0) {
problems.push(PROBLEM.MISSING_THOUGHTSPOT);
}
if (thoughtSpotSegments.length === 0 || thoughtSpotSegments.every(seg => !seg.trim())) {
problems.push(PROBLEM.MISSING_THOUGHTSPOT);
}
References
  1. Use @version SDK: X.Y.Z | ThoughtSpot Cloud: A.B.C.cl — no colon after @version itself. @version tag values should have a space after the pipe: SDK: 1.0.0 | ThoughtSpot: 8.0.0.cl. (link)

Comment on lines +246 to +280
function runResolve(results, sdkVersion, dryRun) {
const resolvable = results
.map((result) => ({
...result,
hits: result.hits.filter((hit) => hit.problems.includes(PROBLEM.UNRESOLVED_SDK)),
}))
.filter((result) => result.hits.length > 0);

const total = resolvable.reduce((count, result) => count + result.hits.length, 0);
if (total === 0) {
console.log('No <TBD> SDK version placeholders to resolve.');
} else {
console.log(`Resolving ${total} placeholder(s) → SDK: ${sdkVersion}${dryRun ? ' (dry run)' : ''}`);
resolvable.forEach(({ filePath, updated, hits }) => {
hits.forEach(({ line, after, warning }) => {
console.log(` ${path.relative(process.cwd(), filePath)}:${line} ${after}`);
if (warning) {
console.log(` ⚠ ${warning}`);
}
});
if (!dryRun) {
fs.writeFileSync(filePath, updated, 'utf8');
}
});
}

// A `<TBD>` the release cannot fill in must stop the release, not ship.
const unresolvable = reportProblems(results, [PROBLEM.TBD_THOUGHTSPOT]);
if (unresolvable > 0) {
console.error(
`✖ ${unresolvable} annotation(s) have a <TBD> ThoughtSpot release, which cannot be resolved automatically.`,
);
process.exit(1);
}
}

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.

medium

Currently, runResolve writes resolved placeholders to disk before checking for unresolvable TBD_THOUGHTSPOT errors. If there are any unresolvable errors, the script will still modify some files on disk before exiting with a non-zero code.

To ensure transactional safety and avoid leaving the repository in a partially modified/dirty state, we should check for any unresolvable errors and fail-fast before writing any changes to disk.

function runResolve(results, sdkVersion, dryRun) {
    // Check for unresolvable errors first to fail-fast and avoid dirty writes
    const unresolvable = results.flatMap(({ hits }) => hits)
        .filter((hit) => hit.problems.includes(PROBLEM.TBD_THOUGHTSPOT)).length;

    if (unresolvable > 0) {
        reportProblems(results, [PROBLEM.TBD_THOUGHTSPOT]);
        console.error(
            `✖ ${unresolvable} annotation(s) have a <TBD> ThoughtSpot release, which cannot be resolved automatically.`,
        );
        process.exit(1);
    }

    const resolvable = results
        .map((result) => ({
            ...result,
            hits: result.hits.filter((hit) => hit.problems.includes(PROBLEM.UNRESOLVED_SDK)),
        }))
        .filter((result) => result.hits.length > 0);

    const total = resolvable.reduce((count, result) => count + result.hits.length, 0);
    if (total === 0) {
        console.log('No <TBD> SDK version placeholders to resolve.');
    } else {
        console.log(`Resolving ${total} placeholder(s) → SDK: ${sdkVersion}${dryRun ? ' (dry run)' : ''}`);
        resolvable.forEach(({ filePath, updated, hits }) => {
            hits.forEach(({ line, after, warning }) => {
                console.log(`  ${path.relative(process.cwd(), filePath)}:${line}  ${after}`);
                if (warning) {
                    console.log(`      ⚠ ${warning}`);
                }
            });
            if (!dryRun) {
                fs.writeFileSync(filePath, updated, 'utf8');
            }
        });
    }
}

expect(parseArgs([])).toEqual({ check: false, lint: false, dryRun: false });
});

test('recognises each mode flag', () => {

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.

medium

The test description uses the British English spelling "recognises". According to the repository style guide (Rule 95), all written work must be in American English (en-US) format. Please use "recognizes" instead.

Suggested change
test('recognises each mode flag', () => {
test('recognizes each mode flag', () => {
References
  1. All written work must be in American English (en-US) format. Use American English spelling (e.g., 'color' not 'colour', 'behavior' not 'behaviour'). (link)

@pkg-pr-new

pkg-pr-new Bot commented Sep 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@thoughtspot/visual-embed-sdk@676

commit: 7b902c2

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.

1 participant