chore: SW-1779 migrate Zephyr scripts to internal ts-lib-zephyr-nodejs#161
chore: SW-1779 migrate Zephyr scripts to internal ts-lib-zephyr-nodejs#161owilliams-tetrascience wants to merge 6 commits into
Conversation
Replace the repo's bespoke Zephyr Scale integration (hand-rolled fetch, auth, timeouts, pagination, error handling) with the shared internal ts-lib-zephyr-nodejs library, per SW-1779 (tech debt). - report-zephyr-results.ts / sync-storybook-zephyr.ts now route all Zephyr HTTP through the library's ZephyrClient. Repo-specific logic (JUnit parsing, story parsing/write-back, testCaseId schema, cycle find-or-create, folder mapping, reuse selection) stays local. - Reporter now embeds each story's screenshot as a base64 <img> into every step of the execution (buildStepsPayload + putExecutionSteps), replacing the previous S3-upload + URL-in-comment path. - Screenshot transport: delete upload-test-screenshots.ts; ship test-results/screenshots/** in the storybook-junit-results artifact; drop the S3 upload + AWS OIDC steps from storybook-tests.yml. Remove the now-unused @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner devDependencies. - The library is published only to the internal registry, so it is kept out of package.json/yarn.lock (preserving the public-registry invariant) and installed as a leaf tarball inside the two Zephyr workflows. Requires JFROG_ARTIFACTORY_NPM_VIRTUAL_URL and JFROG_ARTIFACTORY_READ_NPM_AUTH secrets. - Docs updated in AGENTS.md and CONTRIBUTING.md. Refs: SW-1779 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR migrates the repository’s Zephyr Scale CI tooling from bespoke fetch-based HTTP calls (and S3-hosted screenshot links) to the shared internal ts-lib-zephyr-nodejs (ZephyrClient + helpers), while keeping repo-specific Storybook/JUnit parsing and story write-back logic in place.
Changes:
- Refactored Zephyr provisioning + reporting scripts to use
ZephyrClientand helper utilities, reducing custom HTTP/auth/pagination logic. - Replaced S3 screenshot upload/linking with embedding per-story screenshots directly into Zephyr execution steps (base64
<img>). - Updated GitHub Actions workflows to package/extract the internal Zephyr library from JFrog at runtime and to ship screenshots in the test-results artifact; removed AWS OIDC + S3 upload path and related dependencies.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Removes/updates AWS SDK lock entries consistent with dropping S3 screenshot upload deps. |
| scripts/zephyr/sync-storybook-zephyr.ts | Routes Zephyr API calls through ZephyrClient and uses library folder/steps helpers. |
| scripts/zephyr/report-zephyr-results.ts | Reports executions via ZephyrClient and embeds screenshots into each execution step. |
| scripts/upload-test-screenshots.ts | Deleted (S3 upload + screenshot URL mapping no longer used). |
| package.json | Drops S3-related AWS SDK devDependencies. |
| CONTRIBUTING.md | Documents the new internal-library-based Zephyr integration at a high level. |
| AGENTS.md | Adds detailed guidance for CI/local installation strategy for ts-lib-zephyr-nodejs and the new screenshot flow. |
| .github/workflows/zephyr-sync-storybook.yml | Adds JFrog leaf-tarball install step for ts-lib-zephyr-nodejs before running sync. |
| .github/workflows/zephyr-report-results.yml | Adds JFrog leaf-tarball install step for ts-lib-zephyr-nodejs before reporting. |
| .github/workflows/storybook-tests.yml | Removes AWS OIDC/S3 upload and uploads screenshots in the JUnit artifact instead. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Resolve CodeQL actions/untrusted-checkout (critical): the workflow_run- triggered report job is privileged (ZEPHYR_TOKEN + JFrog secrets) and checks out workflow_run.head_sha. Require the triggering run to come from this repository so the checked-out ref is always trusted, never untrusted fork code. Mirrors the same-repo guard already used in zephyr-sync-storybook.yml. Refs: SW-1779 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- JFrog npmrc auth: normalize the registry host (strip trailing slash) and always emit `//<host>/:_auth=...` so authentication works whether or not the secret URL ends with a slash. - Root-folder detection in sync-storybook-zephyr now matches `parentId === null` instead of a falsy check, so a folder id of 0 is never mistaken for a root folder. Refs: SW-1779 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The unit vitest project includes scripts/**/*.test.ts, and those tests import the Zephyr scripts. With a static import of the JFrog-only ts-lib-zephyr-nodejs (which is not installed in the publish/CI jobs, only in the two Zephyr workflows), Vite's import-analysis failed to resolve it at test-collection time and broke the publish/build jobs. Load the library through a runtime dynamic import whose specifier is held in a variable (so bundlers/Vitest can't statically resolve it) and keep only type-only imports at module scope. The pure parsing/cache helpers the unit tests exercise never reach the client code path, so the suite now loads without the package installed; the library is loaded only when a real Zephyr run reaches the reporting/provisioning code. Also drop the `#!/usr/bin/env tsx` shebangs (the scripts run via `npx tsx`, not directly) — Vite prepended its client import onto the shebang line, causing a parse error once resolution succeeded. Refs: SW-1779 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JFROG_ARTIFACTORY_NPM_VIRTUAL_URL is an environment-scoped secret (artifactory-prod), so it does not resolve in the Zephyr jobs (which do not bind that environment). The virtual registry URL is infra info, not a credential, so hardcode it and keep only the org-level JFROG_ARTIFACTORY_READ_NPM_AUTH token as a secret. Refs: SW-1779 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the duplicated inline JFrog leaf-tarball install steps with the new tetrascience/ts-ci-cd-lib/install-jfrog-npm-package composite action. Pinned to the action's PR branch (@install-jfrog-npm-package-action) so it can be exercised before ts-ci-cd-lib#29 merges; switch to @main once that lands. Refs: SW-1779 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Coverage Report
File CoverageNo changed files found. |
| # (its only transitive need, ts-morph, is already installed). | ||
| # NOTE: pinned to the action's PR branch for testing; switch to @main once | ||
| # tetrascience/ts-ci-cd-lib#29 merges. | ||
| - name: Install ts-lib-zephyr-nodejs (JFrog) |
There was a problem hiding this comment.
Were there blockers to publishing ts-lib-zephyr-nodejs publicly? It seems like all of this wiring + the other PR would be unnecessary if that is possible, and less code is almost always better
There was a problem hiding this comment.
Good question. ts-lib-zephyr-nodejs is intentionally private: it's an internal test-infra lib that hardcodes TetraScience Zephyr/JFrog specifics and has no external audience, and its own repo (license: UNLICENSED, no publishConfig) publishes only to JFrog — so publishing it to public npm would be a decision (and maintenance/security commitment) owned by that repo's team, out of scope for this migration. The wiring here is the standard pattern other TS repos already use to consume it (data-app repos, ts-service-web), so #161 stays consistent with them rather than forking a new distribution model. If the zephyr-lib team does decide to publish it publicly later, this collapses to a normal package.json dep and we can drop the JFrog step + #29 — worth raising with them, but not a blocker for this PR.
🤖 Addressed by Claude Code
There was a problem hiding this comment.
Digging into this a bit — I don't think ts-lib-zephyr-nodejs is actually intentionally private. It's private because that's the org default, but there's no deliberate decision behind it: src/ has zero TetraScience/JFrog-specific code (the only TS references are in the README/CI/CODEOWNERS), and it's a generic Zephyr Scale Cloud reporter/provisioner — clear external audience. The UNLICENSED + JFrog-only publishing are cookiecutter leftovers, not a distribution choice.
And it's our team's library, so this is our call to make. Since we're now consuming it from a public repo, I'd rather publish it to public npm than carry the JFrog leaf-tarball wiring here — that also lets us drop this step and ts-ci-cd-lib#29 entirely (the "less code" win).
Could you fold that into the integration work? It's roughly:
license: UNLICENSED→Apache-2.0+ committedLICENSEfile (matches@tetrascience-npm/tetrascience-react-ui)- rename to
@tetrascience-npm/ts-lib-zephyr-nodejs+publishConfig: { access: "public", registry: "https://registry.npmjs.org" } - point
publish.ymlat public npm (mirror thedata-apps-clienttrusted-publishing setup; first version needs a manual bootstrap) - add the
npmScopesblock to.yarnrc.ymlto avoid the JFrog__archiveUrllockfile pinning
Then #161 collapses to a normal package.json dep.
There was a problem hiding this comment.
Fair — I overstated "intentionally private"; you're right that it's org-default + cookiecutter leftovers, the src/ is generic, and it's your team's call. Public npm is the cleaner end state and collapses #161 to a normal dep + drops #29. 👍
One thing to sequence so we don't fragment: a few repos already depend on the unscoped ts-lib-zephyr-nodejs from JFrog (ts-service-web, a couple of data-app repos, ts-desktop-apps). Renaming to @tetrascience-npm/ts-lib-zephyr-nodejs means those keep resolving the old name from JFrog until they migrate — so it's a coordinated cutover, and the library has to be published publicly first (the first-version manual bootstrap) before #161 can depend on it.
Confirming scope/ordering with @OWilliams now. If we green-light it, the plan is: (1) library PR — Apache-2.0 + LICENSE, scope rename, publishConfig public, publish.yml → public trusted-publishing (mirror data-apps-client) + bootstrap v-first; (2) rework #161 to a plain package.json dep + .yarnrc.yml npmScopes, reverting the JFrog step and the dynamic-import shim; (3) close #29. I'll hold this thread open until we lock the direction.
🤖 Addressed by Claude Code
There was a problem hiding this comment.
Library PR is up: tetrascience/ts-lib-zephyr-nodejs#14 — renames to @tetrascience-npm/ts-lib-zephyr-nodejs, Apache-2.0 + LICENSE, publishConfig public, and publish.yml → public npm via OIDC trusted publishing (mirrors this repo's publish-public.yml). yarn check + all 368 tests pass.
Sequencing from here: (1) merge #14 + the one-time bootstrap (register the repo as a trusted publisher on npmjs, ensure a PROD env, mint the first public version — called out in that PR); (2) once a version is live on public npm, I'll flip this PR to a plain package.json dep (reverting the JFrog step + dynamic-import shim) and close ts-ci-cd-lib#29. Keeping #161 on JFrog until then so it stays green/mergeable if needed.
🤖 Addressed by Claude Code
Summary
Tech-debt migration (SW-1779): replaces the repo's bespoke Zephyr Scale integration — ~1,350 lines of hand-rolled
fetch, auth, timeouts, pagination, and error handling across the Zephyr scripts — with the shared internalts-lib-zephyr-nodejslibrary (ZephyrClient+ helpers). Net −310 lines.This repo is unlike the reference migration (a Playwright/private repo): Storybook stories run as Vitest browser tests, opt in via
parameters.zephyr.testCaseId(kept — 108 stories untouched), and reporting stays JUnit-XML-based. So the approach is incremental adoption — the thin wrapper scripts keep all repo-specific logic and only the HTTP layer moves to the library.What changed
scripts/zephyr/report-zephyr-results.ts/sync-storybook-zephyr.ts— all Zephyr HTTP now routes through the library'sZephyrClient. Repo-specific logic stays local: JUnit parsing, story parsing/write-back, thetestCaseIdschema, per-branch cycle find-or-create (SW-R33for main), folder mapping, and reusable-test-case selection.<img>into every step of the execution (buildStepsPayload+putExecutionSteps), replacing the old S3-upload + URL-in-comment path.scripts/upload-test-screenshots.ts;storybook-tests.ymlnow shipstest-results/screenshots/**in thestorybook-junit-resultsartifact and the S3 upload + AWS OIDC steps (andid-tokenpermission) are removed. Dropped the now-unused@aws-sdk/client-s3and@aws-sdk/s3-request-presignerdevDependencies.package.json/yarn.lockto preserve this repo's public-registry invariant (external contributors keep installing from npmjs). It is installed as a leaf tarball inside the two Zephyr workflows (npm pack+ extract — deliberately notnpm install --no-save, which corrupts the Yarnnode_modules).AGENTS.mdandCONTRIBUTING.md(referenced as an internal library, no links).Reviewer notes / before merge
JFROG_ARTIFACTORY_NPM_VIRTUAL_URLandJFROG_ARTIFACTORY_READ_NPM_AUTH. The CI auth line is written as npm_auth(base64 identity) — please confirm the secret's format matches, or the pack step needs a one-line_authTokentweak.yarn typecheck, fullyarn lint, all 70 zephyr unit tests, and an offlinebuildStepsPayloadcheck (base64<img>on every step). An end-to-end run against a real Zephyr cycle (per-step screenshots visible in the UI) should be done via thezephyr_synclabel + a Storybook Tests run.src/(published library) code changed — this is dev-tooling only, so no version bump is expected.🤖 Generated with Claude Code