Skip to content

Prepare 0.3.0 release: land SDK migration on master + release automation#22

Merged
jochen-testingbot merged 3 commits into
masterfrom
prepare-release
Jul 18, 2026
Merged

Prepare 0.3.0 release: land SDK migration on master + release automation#22
jochen-testingbot merged 3 commits into
masterfrom
prepare-release

Conversation

@jochen-testingbot

@jochen-testingbot jochen-testingbot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Why

The Phase 3.2 SDK migration (#21) merged into the phase4-ci-publishing branch (its stacked base), not master — so master currently still has the old VSS.SDK/AMD/jQuery tab. This PR lands that work on master and adds release automation.

What changed

  • Bump 0.2.70.3.0 (single source of truth; stamped into the manifest + both task.jsons). Marks the Phases 1–5 modernization release.
  • release.yml (new): on a v* tag or manual dispatch, builds the .vsix and attaches it to a draft GitHub Release. Does not publish to the Marketplace.
  • publish.yml: changed to workflow_dispatch-only, so tagging a release never auto-publishes to Azure DevOps — marketplace publishing is now a deliberate manual action.

Also brings in the Phase 3.2 tab migration already reviewed in #21 (azure-devops-extension-sdk v4 + azure-devops-extension-api v5, TypeScript, self-contained bundle).

Verified

npm run lint, npm run typecheck, npm test (8 passing) and npm run package all green → testingbot.testingbot-tasks-0.3.0.vsix.

Reminder: the tab is browser code I can't run outside a live Azure DevOps org — smoke-test the results tab with the 0.3.0 vsix before publishing to the Marketplace (see #21).

Summary by CodeRabbit

  • New Features
    • Modernized the TestingBot results tab with improved Azure DevOps SDK integration.
    • Added paginated build results, test-session links, loading states, and clearer error handling.
    • Added secure dialog loading that only permits valid TestingBot HTTPS addresses.
    • Updated extension packaging for the new tab and dialog experience.
  • Bug Fixes
    • Improved reliability when loading build results and opening individual test sessions.
  • Release
    • Added automated draft GitHub Releases for version tags, including packaged extension files.

jochen-testingbot and others added 3 commits July 18, 2026 11:15
Ports the build-results tab off the deprecated vss-web-extension-sdk (VSS.SDK.js
+ AMD module loader) to the supported azure-devops-extension-sdk v4 +
azure-devops-extension-api v5. TypeScript, plain DOM, self-contained bundle. No
React (not needed). The /mini viewer and behaviour are preserved.

- info.ts / dialog.ts (was info.js / dialog.js): SDK.init/ready; build context
  via BuildPageDataService.getBuildPageData(); attachment listing via
  BuildRestClient.getAttachments + download from the attachment _links.self.href
  with SDK.getAccessToken(); TestingBot API via ServiceEndpointRestClient
  .executeServiceEndpointRequest; embed dialog via
  IHostPageLayoutService.openCustomDialog (config passed as { url }, read by the
  dialog through SDK.getConfiguration()).
- Drop jQuery for plain document.createElement, and Buffer for btoa; the 180-line
  inline MD5 is replaced by the blueimp-md5 package. Keeps the Phase 3 pagination
  (stable page size, count=0 guard) and error-state fixes.
- HTML now loads the webpack bundle directly (<script src="scripts/info.js">);
  VSS.SDK.js and the AMD loader are gone.
- webpack bundles the SDK/API/md5 in (no AMD externals, no buffer polyfill);
  @babel/preset-typescript compiles the tab. babel only strips types, so
  `npm run typecheck` (tb-build-info/tsconfig.json) type-checks it; wired into
  `npm run package` and the CI build workflow.
- Manifest: embed-dialog uri drops ?url={{url}} (config now via the dialog API);
  the lib/ (VSS.SDK.js) entry is removed from files.
- eslint: tab files are now .ts (browser globals, typescript-eslint).

Verified statically: npm run typecheck (tab) clean, npm run lint clean,
npm test 8 passing, npm run package builds a vsix whose tab bundle is
self-contained (66KB, no AMD/define, no VSS.SDK.js).

RUNTIME VALIDATION REQUIRED (cannot be exercised outside a live Azure DevOps org):
attachment download via _links.self.href + access token; getBuildPageData()
replacing the old onBuildChanged (one-shot read on load); openCustomDialog
rendering embedDialog.html and passing { url }. Smoke-test the tab in a real org
before release.
Phase 3.2: migrate the results tab to azure-devops-extension-sdk
The Phase 3.2 SDK migration (#21) merged into the phase4-ci-publishing branch,
not master, so master was missing it. This branches off that branch to bring it
to master, and adds release automation.

- Bump version 0.2.7 -> 0.3.0 (single source of truth; stamped into the manifest
  and both task.json files). Marks the Phases 1-5 modernization release.
- Add .github/workflows/release.yml: on a v* tag (or manual dispatch) it builds
  the .vsix and attaches it to a *draft* GitHub Release. It does NOT publish to
  the Marketplace.
- Make publish.yml a deliberate manual action (workflow_dispatch only) instead of
  firing on tags, so tagging a release never auto-publishes to Azure DevOps.

Contains the Phase 3.2 tab migration (already reviewed in #21): the results tab
on azure-devops-extension-sdk v4 + azure-devops-extension-api v5.

Verified: lint, typecheck, tests (8 passing) and `npm run package` all green;
produces testingbot.testingbot-tasks-0.3.0.vsix.
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The results tab and dialog were migrated from AMD JavaScript to self-contained TypeScript bundles. Extension packaging, linting, documentation, and dependency versions were updated, alongside CI typechecking and tag-based draft release automation.

Changes

Results Tab Modernization

Layer / File(s) Summary
TypeScript build and lint pipeline
package.json, tb-build-info/tsconfig.json, webpack.config.js, eslint.config.mjs, CLAUDE.md
TypeScript compilation, strict checking, self-contained webpack bundling, and ESLint coverage were added or updated for the results-tab scripts.
Results tab data and rendering flow
tb-build-info/scripts/info.ts, tb-build-info/infoTab.html
The tab initializes the SDK, loads build attachments and paged job data, renders results and pagination, and opens session dialogs.
Dialog flow and extension wiring
tb-build-info/scripts/dialog.ts, tb-build-info/scripts/dialog.js, tb-build-info/embedDialog.html, vss-extension.json
The dialog validates TestingBot URLs and renders an iframe or error message; manifest and HTML wiring now target the bundled scripts.
Modernization status records
MODERNIZATION.md
Documentation records the completed TypeScript SDK migration and self-contained plain-DOM bundle.
Estimated code review effort: 4 (Complex) | ~45 minutes

Release Automation

Layer / File(s) Summary
CI typechecking checkpoint
.github/workflows/build.yml
The build job runs npm run typecheck between linting and tests.
Publication trigger configuration
.github/workflows/publish.yml
Publication is documented and configured as a manual workflow_dispatch action rather than a version-tag push trigger.
Draft release packaging
.github/workflows/release.yml
Version tags or manual runs install dependencies, package the extension, and create a draft GitHub Release with the VSIX attached.
Package version alignment
package.json, tb-main/package.json, tb-stop-tunnel/package.json
Package versions were updated to 0.3.0.
Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BuildWorkflow
  participant TypeScriptCompiler
  participant TestSuite
  participant ReleaseWorkflow
  participant GitHubRelease
  BuildWorkflow->>TypeScriptCompiler: run npm run typecheck
  TypeScriptCompiler-->>BuildWorkflow: typecheck result
  BuildWorkflow->>TestSuite: run tests
  ReleaseWorkflow->>ReleaseWorkflow: install dependencies and package VSIX
  ReleaseWorkflow->>GitHubRelease: create draft release with Packages/*.vsix
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: 0.3.0 release prep, SDK migration, and release automation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@jochen-testingbot
jochen-testingbot merged commit 6524964 into master Jul 18, 2026
3 of 4 checks passed

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/publish.yml (1)

16-18: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Synchronize the remaining publication documentation.

README.md:70-72 and MODERNIZATION.md:26-30 still instruct maintainers that pushing a v* tag publishes to the Marketplace, but this workflow is now manual-only. Update those references to describe workflow_dispatch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/publish.yml around lines 16 - 18, Update the publication
instructions in README.md and MODERNIZATION.md to state that Marketplace
publishing is triggered manually via workflow_dispatch, replacing references to
pushing a v* tag while preserving the surrounding documentation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 14-19: Add workflow-level concurrency configuration in release.yml
keyed by the workflow name and ref, and set cancel-in-progress to false. Place
it alongside the top-level permissions/jobs configuration so release runs for
the same ref serialize without canceling earlier runs.
- Line 21: Update the release workflow action references for actions/checkout,
actions/setup-node, and softprops/action-gh-release from version tags to
reviewed, full-length commit SHA pins. Preserve each action’s current version
and configuration while ensuring all release actions are immutable.
- Around line 9-12: Add a required tag input to the workflow_dispatch
configuration, then use that input as the release tag_name and checkout ref for
manual runs. Keep push-triggered releases using their existing tag ref, while
ensuring manually triggered builds and releases target the explicitly provided
v* tag.

In `@tb-build-info/scripts/info.ts`:
- Around line 223-241: Handle startup failures in both entry points: in
tb-build-info/scripts/info.ts, move SDK.init() and SDK.ready() into main()’s
try/catch, and ensure the main() invocation is awaited or has a terminal
rejection handler; in tb-build-info/scripts/dialog.ts, likewise handle the
fired-and-forgotten main() invocation and await SDK.notifyLoadSucceeded() within
its flow. Preserve the existing success and error behavior.

---

Outside diff comments:
In @.github/workflows/publish.yml:
- Around line 16-18: Update the publication instructions in README.md and
MODERNIZATION.md to state that Marketplace publishing is triggered manually via
workflow_dispatch, replacing references to pushing a v* tag while preserving the
surrounding documentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bdc68a4c-c3e9-481e-a60d-36c09732a4f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9679ae6 and 542c8f8.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • .github/workflows/build.yml
  • .github/workflows/publish.yml
  • .github/workflows/release.yml
  • CLAUDE.md
  • MODERNIZATION.md
  • eslint.config.mjs
  • package.json
  • tb-build-info/embedDialog.html
  • tb-build-info/infoTab.html
  • tb-build-info/scripts/dialog.js
  • tb-build-info/scripts/dialog.ts
  • tb-build-info/scripts/info.js
  • tb-build-info/scripts/info.ts
  • tb-build-info/tsconfig.json
  • tb-main/package.json
  • tb-stop-tunnel/package.json
  • vss-extension.json
  • webpack.config.js
💤 Files with no reviewable changes (2)
  • tb-build-info/scripts/dialog.js
  • tb-build-info/scripts/info.js

Comment on lines +9 to +12
on:
push:
tags: [ 'v*' ]
workflow_dispatch:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the workflow file and any nearby release-related files.
git ls-files .github/workflows/release.yml .github/workflows || true

echo '--- release.yml ---'
cat -n .github/workflows/release.yml

echo '--- search for gh-release usage / tag_name / workflow_dispatch inputs ---'
rg -n "softprops/action-gh-release|tag_name|workflow_dispatch|inputs:|release" .github/workflows -S

Repository: testingbot/vso-testingbot-plugin

Length of output: 2506


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact workflow structure if the first pass is inconclusive.
python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/release.yml')
print(p.read_text())
PY

Repository: testingbot/vso-testingbot-plugin

Length of output: 1148


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the release workflow with line numbers and surrounding context for the gh-release step.
sed -n '1,220p' .github/workflows/release.yml

Repository: testingbot/vso-testingbot-plugin

Length of output: 1147


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check how workflow_dispatch behaves in GitHub Actions docs locally unavailable;
# instead inspect whether the workflow explicitly binds a ref for release creation.
rg -n "ref:|tag_name:|softprops/action-gh-release|workflow_dispatch" .github/workflows/release.yml -n -A4 -B4

Repository: testingbot/vso-testingbot-plugin

Length of output: 850


Provide an explicit tag for manual releases.
workflow_dispatch falls back to the triggering ref, so a manual run from a branch can create a release for that branch instead of a v* tag. Add a required dispatch input and pass it to tag_name (and checkout that ref for the build) so manual releases target the intended tag.

🧰 Tools
🪛 YAMLlint (1.37.1)

[warning] 9-9: truthy value should be one of [false, true]

(truthy)


[error] 11-11: too many spaces inside brackets

(brackets)


[error] 11-11: too many spaces inside brackets

(brackets)

🪛 zizmor (1.26.1)

[warning] 9-12: insufficient job-level concurrency limits (concurrency-limits): workflow is missing concurrency setting

(concurrency-limits)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 9 - 12, Add a required tag input
to the workflow_dispatch configuration, then use that input as the release
tag_name and checkout ref for manual runs. Keep push-triggered releases using
their existing tag ref, while ensuring manually triggered builds and releases
target the explicitly provided v* tag.

Comment on lines +14 to +19
permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Serialize release runs by ref.

Add a workflow-level concurrency group keyed by the workflow and ref, with cancel-in-progress: false, so repeated manual runs or tag retries cannot race while creating or updating the same draft release.

🧰 Tools
🪛 zizmor (1.26.1)

[error] 15-15: overly broad permissions (excessive-permissions): contents: write is overly broad at the workflow level

(excessive-permissions)


[warning] 15-15: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment

(undocumented-permissions)


[info] 18-18: workflow or action definition without a name (anonymous-definition): this job

(anonymous-definition)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 14 - 19, Add workflow-level
concurrency configuration in release.yml keyed by the workflow name and ref, and
set cancel-in-progress to false. Place it alongside the top-level
permissions/jobs configuration so release runs for the same ref serialize
without canceling earlier runs.

Source: Linters/SAST tools

release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,120p' .github/workflows/release.yml | cat -n

Repository: testingbot/vso-testingbot-plugin

Length of output: 1434


Pin the release workflow actions to commit SHAs
actions/checkout@v4, actions/setup-node@v4, and softprops/action-gh-release@v2 are tag refs, not immutable pins. Because this workflow has contents: write, pin them to reviewed full-length commit SHAs before shipping release artifacts.

🧰 Tools
🪛 zizmor (1.26.1)

[error] 21-21: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml at line 21, Update the release workflow action
references for actions/checkout, actions/setup-node, and
softprops/action-gh-release from version tags to reviewed, full-length commit
SHA pins. Preserve each action’s current version and configuration while
ensuring all release actions are immutable.

Source: Linters/SAST tools

Comment on lines +223 to +241
async function main(): Promise<void> {
await SDK.init({ loaded: false });
await SDK.ready();

buildClient = getClient(BuildRestClient);
endpointClient = getClient(ServiceEndpointRestClient);
projectId = SDK.getWebContext().project.id;

try {
await load();
} catch (err) {
console.error('error', err);
renderMessage('Could not load TestingBot results.');
} finally {
SDK.notifyLoadSucceeded();
}
}

main();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== File list ==\n'
git ls-files 'tb-build-info/scripts/*'

printf '\n== info.ts around main ==\n'
nl -ba tb-build-info/scripts/info.ts | sed -n '190,260p'

printf '\n== dialog.ts around main ==\n'
nl -ba tb-build-info/scripts/dialog.ts | sed -n '1,120p'

printf '\n== Search for SDK lifecycle calls ==\n'
rg -n "notifyLoadSucceeded|notifyLoadFailed|main\(\)|SDK\.init|SDK\.ready|Promise\.catch|catch\(" tb-build-info/scripts

Repository: testingbot/vso-testingbot-plugin

Length of output: 316


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in [Path('tb-build-info/scripts/info.ts'), Path('tb-build-info/scripts/dialog.ts')]:
    print(f"\n== {p} ==")
    text = p.read_text()
    for i, line in enumerate(text.splitlines(), 1):
        if 'main();' in line or 'notifyLoadSucceeded' in line or 'notifyLoadFailed' in line or 'SDK.init' in line or 'SDK.ready' in line:
            start = max(1, i-5)
            end = min(len(text.splitlines()), i+5)
            for j in range(start, end+1):
                print(f"{j:4d}: {text.splitlines()[j-1]}")
            print()
PY

Repository: testingbot/vso-testingbot-plugin

Length of output: 2331


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in [Path('tb-build-info/scripts/info.ts'), Path('tb-build-info/scripts/dialog.ts')]:
    print(f"\n== {p} ==")
    lines = p.read_text().splitlines()
    for needle in ['async function main', 'SDK.init', 'SDK.ready', 'notifyLoadSucceeded', 'notifyLoadFailed', 'main();']:
        hits = [i+1 for i,l in enumerate(lines) if needle in l]
        for i in hits:
            start = max(1, i-5)
            end = min(len(lines), i+5)
            print(f"-- context around line {i} ({needle}) --")
            for j in range(start, end+1):
                print(f"{j:4d}: {lines[j-1]}")
            print()
PY

Repository: testingbot/vso-testingbot-plugin

Length of output: 3530


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== package manifests mentioning azure-devops-extension-sdk ==\n'
rg -n '"azure-devops-extension-sdk"|azure-devops-extension-sdk' --glob 'package*.json' --glob '!**/node_modules/**'

printf '\n== repo references to notifyLoadSucceeded ==\n'
rg -n "notifyLoadSucceeded" --glob '!**/node_modules/**'

printf '\n== tsconfig / typings hints for tb-build-info ==\n'
rg -n '"types"|typeRoots|azure-devops-extension-sdk' tb-build-info package*.json tsconfig*.json --glob '!**/node_modules/**'

Repository: testingbot/vso-testingbot-plugin

Length of output: 360


🌐 Web query:

azure-devops-extension-sdk notifyLoadSucceeded return type

💡 Result:

The return type of the notifyLoadSucceeded method in the azure-devops-extension-sdk is a Promise [1][2]. This method is used to notify the Azure DevOps host that the extension has successfully finished loading and that its UI is ready to be displayed, which is particularly useful when the SDK is initialized with { loaded: false } [1][2][3].

Citations:


Handle the startup promises in both entry points.

  • tb-build-info/scripts/info.ts: SDK.init()/SDK.ready() happen outside the try, and main() is never awaited or caught, so startup failures can surface as unhandled rejections.
  • tb-build-info/scripts/dialog.ts: main() is also fired-and-forgotten, and SDK.notifyLoadSucceeded() returns a promise here as well, so it should be awaited or caught.
📍 Affects 2 files
  • tb-build-info/scripts/info.ts#L223-L241 (this comment)
  • tb-build-info/scripts/dialog.ts#L34-L37
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tb-build-info/scripts/info.ts` around lines 223 - 241, Handle startup
failures in both entry points: in tb-build-info/scripts/info.ts, move SDK.init()
and SDK.ready() into main()’s try/catch, and ensure the main() invocation is
awaited or has a terminal rejection handler; in tb-build-info/scripts/dialog.ts,
likewise handle the fired-and-forgotten main() invocation and await
SDK.notifyLoadSucceeded() within its flow. Preserve the existing success and
error behavior.

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