Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
- name: Lint
run: npm run lint

- name: Type-check tab
run: npm run typecheck

- name: Test
run: npm test

Expand Down
26 changes: 14 additions & 12 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,25 @@ service endpoint. The tab never sees raw credentials outside that attachment.
`package.json` has no `dependencies`, so nothing is installed into
`dist/tb-stop-tunnel`.

- **Web/tab code** (`tb-build-info/scripts/*.js`) runs in the browser inside Azure
DevOps and is bundled by webpack (`webpack.config.js`) with AMD output and
`@babel/preset-env`. The two entry points (`info`, `dialog`) emit to
`dist/tb-build-info/scripts/`. The host-provided SDK modules (`TFS/*`, `VSS/*`,
`react`) are declared as webpack **externals** (`externalsType: 'amd'`) — they
are resolved by the VSS module loader at runtime, not bundled. Add any new
host-provided module to the `externals` list in `webpack.config.js`, or the
bundle will try to inline it. `info.js` still uses `Buffer`, so the config
provides a `buffer` polyfill (removed when the tab is rewritten — see
`MODERNIZATION.md` Phase 3).
- **Web/tab code** (`tb-build-info/scripts/*.ts`) runs in the browser inside Azure
DevOps. It is TypeScript using `azure-devops-extension-sdk` (v4) +
`azure-devops-extension-api` (v5) — `SDK.init`/`SDK.ready`/`SDK.getService`,
`BuildRestClient` for attachments, `ServiceEndpointRestClient` for the data
source, `IHostPageLayoutService.openCustomDialog` for the embed dialog. webpack
(`webpack.config.js`) bundles it **self-contained** (the SDK/API and `md5` are
bundled in, not external) via `@babel/preset-env` + `@babel/preset-typescript`;
the two entry points (`info`, `dialog`) emit to `dist/tb-build-info/scripts/`.
`infoTab.html` / `embedDialog.html` load those bundles directly with a plain
`<script>` — there is no `VSS.SDK.js` and no AMD loader anymore. babel only
strips types, so `npm run typecheck` (`tb-build-info/tsconfig.json`) type-checks
the tab separately.

## webpack pipeline specifics

`webpack.config.js` does the bundle plus a `copy-webpack-plugin` pass that copies
the static files into `dist/`: `images`, `overview.md`, `vss-extension.json`, the
`tb-main` / `tb-stop-tunnel` folders, everything in `tb-build-info` **except**
`scripts/**` (those are bundled, not copied), and `VSS.SDK.js` into `dist/lib/`.
`tb-main` / `tb-stop-tunnel` folders, and everything in `tb-build-info` **except**
`scripts/**` (those are bundled, not copied).

`bin/upload_all.js` is a helper that uploads the `tb-*` build task definitions
directly to a collection via tfx-cli (separate from packaging the extension).
Expand Down
9 changes: 5 additions & 4 deletions MODERNIZATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ _Date: 2026-07-17. Applies to repo `vso-testingbot-plugin` (marketplace: `testin
> secret leaves the browser (finding #1) — **blocked**: the TestingBot public
> API exposes no endpoint that returns the `md5(key:secret:session_id)` share
> hash, so keeping the embedded `/mini` viewer requires the secret in the
> browser until TestingBot adds such an endpoint; (b) the `vss-web-extension-sdk`
> → `azure-devops-extension-sdk` / React port (large, and the old SDK keeps
> working, so deferred to its own PR).
> browser until TestingBot adds such an endpoint. (b) The SDK port is now
> **DONE (PR #21):** the tab is TypeScript on `azure-devops-extension-sdk` v4 +
> `azure-devops-extension-api` v5 (plain DOM, no jQuery/Buffer), bundled
> self-contained; `VSS.SDK.js` is gone. (React/`azure-devops-ui` was not needed.)
> - **Phase 4 — MOSTLY DONE (PR #20):** GitHub Actions build workflow (lint +
> tests + package on every PR), ESLint flat config wired up (`npm run lint`),
> CodeQL workflow fixed (v3 actions), dead Jenkinsfile removed, README usage
Expand Down Expand Up @@ -110,7 +111,7 @@ Goal: a results tab with no secrets in the browser, no CDN scripts, and a suppor
| Step | Effort | Detail / rationale |
|------|--------|--------------------|
| 3.1 Delete the unpkg script tag | **S** | `infoTab.html:5` — remove entirely; `window.TextDecoder` is native (its sole use is `info.js:303`). Do this immediately; it does not depend on the SDK migration. |
| 3.2 Port from `vss-web-extension-sdk` 1.106 to `azure-devops-extension-sdk` | **L** | Use sdk **v4.x** for now (api 5.275.0's peerDependency is `^2 \|\| ^3 \|\| ^4`, not yet v5) + `azure-devops-extension-api` 5.275.0 with subpath imports (`azure-devops-extension-api/Build`), optionally `azure-devops-ui` 2.276.0 (hard peer dep: React 16.8.x, not 18). Get build context via `SDK.getService(BuildServiceIds.BuildPageDataService).getBuildPageData()` — `SDK.getConfiguration()` is unreliable for `ms.vss-build-web.build-results-tab`. The old VSS.SDK keeps working meanwhile ("will continue to work indefinitely" per Microsoft), so this can trail Phases 1-2 without a flag day. |
| 3.2 Port from `vss-web-extension-sdk` 1.106 to `azure-devops-extension-sdk` | **L** | _(DONE — PR #21: sdk v4.2.0 + api 5.275.0, TypeScript, plain DOM, self-contained bundle; `BuildRestClient.getAttachments`, `BuildPageDataService`, `IHostPageLayoutService.openCustomDialog`. No React.)_ Use sdk **v4.x** for now (api 5.275.0's peerDependency is `^2 \|\| ^3 \|\| ^4`, not yet v5) + `azure-devops-extension-api` 5.275.0 with subpath imports (`azure-devops-extension-api/Build`), optionally `azure-devops-ui` 2.276.0 (hard peer dep: React 16.8.x, not 18). Get build context via `SDK.getService(BuildServiceIds.BuildPageDataService).getBuildPageData()` — `SDK.getConfiguration()` is unreliable for `ms.vss-build-web.build-results-tab`. The old VSS.SDK keeps working meanwhile ("will continue to work indefinitely" per Microsoft), so this can trail Phases 1-2 without a flag day. |
| 3.3 Remove all credential use from the browser | **M** | Drop the client-built Basic header (`info.js:200-203`) — `executeServiceEndpointRequest` proxies auth server-side via the endpoint's stored credential. Read only non-secret metadata from the (fixed) attachment. For per-test share links (`info.js:246`, md5(key:secret:session_id) — TestingBot's own scheme), add a service-endpoint data source or a TestingBot API endpoint that returns share URLs/hashes server-side; until then, degrade to linking testingbot.com directly rather than shipping the secret. Delete the 180-line inline MD5 implementation. |
| 3.4 Fix pagination + error UX | **S** | Fixed page size from the offset-0 response (`meta` envelope: `{data, meta:{offset,count,total}}`, count max 500); guard zero counts; render error states instead of eternal "LOADING"; try/catch the pagination click handler. |
| 3.5 Validate `embedDialog` URL | **S** | `new URL(params.url)` must be `https:` + host `testingbot.com`, else render nothing (`dialog.js:16-20`). |
Expand Down
39 changes: 11 additions & 28 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,13 @@ export default tseslint.config(
'tb-*/index.js',
'tb-*/index.js.map',
'tb-*/tests/**/*.js',
'**/*.js.map',
// Third-party runtime, not ours to lint.
'lib/**'
'**/*.js.map'
]
},

// TypeScript task sources + their tests.
// TypeScript agent tasks (run on the build agent, Node) + their tests.
{
files: ['tb-*/**/*.ts'],
files: ['tb-main/**/*.ts', 'tb-stop-tunnel/**/*.ts'],
extends: [js.configs.recommended, ...tseslint.configs.recommended],
languageOptions: {
globals: { ...globals.node, ...globals.mocha }
Expand All @@ -33,37 +31,22 @@ export default tseslint.config(
}
},

// Node build scripts.
// TypeScript results-tab scripts (run in the browser via the extension SDK).
{
files: ['scripts/**/*.js', 'webpack.config.js'],
extends: [js.configs.recommended],
files: ['tb-build-info/**/*.ts'],
extends: [js.configs.recommended, ...tseslint.configs.recommended],
languageOptions: {
globals: globals.node,
sourceType: 'commonjs'
globals: globals.browser
}
},

// Legacy browser tab scripts (slated for the SDK rewrite). Lint for real
// errors only; the vendored MD5 implementation trips a lot of style rules.
// Node build scripts.
{
files: ['tb-build-info/scripts/**/*.js'],
files: ['scripts/**/*.js', 'webpack.config.js'],
extends: [js.configs.recommended],
languageOptions: {
sourceType: 'commonjs',
globals: {
...globals.browser,
VSS: 'readonly',
$: 'readonly',
jQuery: 'readonly',
Buffer: 'readonly'
}
},
rules: {
'no-unused-vars': 'off',
'no-redeclare': 'off',
'no-inner-declarations': 'off',
'no-empty': 'off',
'no-func-assign': 'off'
globals: globals.node,
sourceType: 'commonjs'
}
}
);
Loading