Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .github/plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
{
"name": "azure-storage",
"description": "Azure Storage canvas — browse blobs and containers, bulk transfer with live progress, filter with aggregate statistics, mint and open SAS links, preview blobs, and plan account to account copies, all agent-drivable on one live surface.",
"version": "0.3.2",
"version": "0.3.3",
"source": "canvases/azure-storage"
},
{
Expand Down
2 changes: 1 addition & 1 deletion canvases/azure-storage/.github/plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "azure-storage",
"version": "0.3.2",
"version": "0.3.3",
"description": "Azure Storage canvas — browse blobs and containers, bulk transfer with live progress, filter with aggregate statistics, mint and open SAS links, preview blobs, and plan account to account copies, all agent-drivable on one live surface.",
"extensions": {
"com.github.copilot": {
Expand Down
12 changes: 6 additions & 6 deletions canvases/azure-storage/SHA256SUMS
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
3a262e33c0e21381bd1a4f51edb8ffaf6d823d3a1516e4ef510ca3aff95da9b0 .github/plugin/plugin.json
396925277edf6f794be74d0cd02f64226dbfa26abe6b2453a8fd0b3a44a58917 .github/plugin/plugin.json
a813071223c79d70046ffde6cd6de1d52eecf7b7b3ce174a3e1bcb2c2ebb283d THIRD_PARTY_NOTICES.txt
5fb6a9018aecd7aede2a8937f78d81867bf00c5550f30ab071d158b3852ccaca assets/preview.png
122366309ceaa49cae3a1fad71798502bb0587e31ad50e1746ad8af8e7608a77 checksums.json
45fcd18ae09de9b5542e7a113556482105da42419b1c603fcc69ace3be1a55b4 com.github.copilot/extensions/azure-storage/extension.mjs
602d715345e1d6a9416c37dbe5019c2ec21a044cdd7721d42075a6fa29a5e94a checksums.json
9f75b0541587faa2854f99718c5a028e13a4a2f7d2c6c46a0fd8f29bee548a2d com.github.copilot/extensions/azure-storage/extension.mjs
f7d102239138f46038f9fbbc576d6a34e2f03370f27c282dc7b59dbe470f791d com.github.copilot/extensions/azure-storage/sdk/blob.mjs
88d23b0a1a226385486aade505b086c7bf524ef0bd5b031bac4df0351e8a20f6 com.github.copilot/extensions/azure-storage/sdk/file.mjs
fa0526b9488b0e797bfa79044b632edb53483e9e2dc12fe672d093bfb9ace670 com.github.copilot/extensions/azure-storage/sdk/identity.mjs
6553c36dad97992722dd5d1320889ec09038847c31f2a04cf8756930f725c169 com.github.copilot/extensions/azure-storage/sdk/queue.mjs
cf4b43961f200d9d73f1158aa28b628863274d33e66f53457afa7f6a16562202 com.github.copilot/extensions/azure-storage/sdk/tables.mjs
5520dc6cc7535a61cae0fe0c5764746837e96ecc5a431b360deca6d2534d0f3f com.github.copilot/extensions/azure-storage/studio-package.json
be4affe6bd79ceb663389d03dc47fc1e55a8223d7075b370dc0f78853825faaf package.json
e534abc17ed846674eeb89b13b1bf1da5efa3aad6e35fbc0fa9454b12eec118b release.json
c0865cfca79c90f8ebf75e11d0cf9c61bda757b4f03160b89fd3ebb6622b7725 com.github.copilot/extensions/azure-storage/studio-package.json
1434b589e284ae951a0ca236c237b936288959b6013af84119c8b0691c686aab package.json
5a3b3801d5ace7180420f28c2d4658046ca30da52fe4062dadb27980bd91d80f release.json
27a2fce06a96abfd4a76405a78ff80b3be2cacf50f9dd4bde49649cfea84942f skills/azure-storage/SKILL.md
10 changes: 5 additions & 5 deletions canvases/azure-storage/checksums.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
".github/plugin/plugin.json": "3a262e33c0e21381bd1a4f51edb8ffaf6d823d3a1516e4ef510ca3aff95da9b0",
".github/plugin/plugin.json": "396925277edf6f794be74d0cd02f64226dbfa26abe6b2453a8fd0b3a44a58917",
"assets/preview.png": "5fb6a9018aecd7aede2a8937f78d81867bf00c5550f30ab071d158b3852ccaca",
"com.github.copilot/extensions/azure-storage/extension.mjs": "45fcd18ae09de9b5542e7a113556482105da42419b1c603fcc69ace3be1a55b4",
"com.github.copilot/extensions/azure-storage/extension.mjs": "9f75b0541587faa2854f99718c5a028e13a4a2f7d2c6c46a0fd8f29bee548a2d",
"com.github.copilot/extensions/azure-storage/sdk/blob.mjs": "f7d102239138f46038f9fbbc576d6a34e2f03370f27c282dc7b59dbe470f791d",
"com.github.copilot/extensions/azure-storage/sdk/file.mjs": "88d23b0a1a226385486aade505b086c7bf524ef0bd5b031bac4df0351e8a20f6",
"com.github.copilot/extensions/azure-storage/sdk/identity.mjs": "fa0526b9488b0e797bfa79044b632edb53483e9e2dc12fe672d093bfb9ace670",
"com.github.copilot/extensions/azure-storage/sdk/queue.mjs": "6553c36dad97992722dd5d1320889ec09038847c31f2a04cf8756930f725c169",
"com.github.copilot/extensions/azure-storage/sdk/tables.mjs": "cf4b43961f200d9d73f1158aa28b628863274d33e66f53457afa7f6a16562202",
"com.github.copilot/extensions/azure-storage/studio-package.json": "5520dc6cc7535a61cae0fe0c5764746837e96ecc5a431b360deca6d2534d0f3f",
"package.json": "be4affe6bd79ceb663389d03dc47fc1e55a8223d7075b370dc0f78853825faaf",
"release.json": "e534abc17ed846674eeb89b13b1bf1da5efa3aad6e35fbc0fa9454b12eec118b",
"com.github.copilot/extensions/azure-storage/studio-package.json": "c0865cfca79c90f8ebf75e11d0cf9c61bda757b4f03160b89fd3ebb6622b7725",
"package.json": "1434b589e284ae951a0ca236c237b936288959b6013af84119c8b0691c686aab",
"release.json": "5a3b3801d5ace7180420f28c2d4658046ca30da52fe4062dadb27980bd91d80f",
"skills/azure-storage/SKILL.md": "27a2fce06a96abfd4a76405a78ff80b3be2cacf50f9dd4bde49649cfea84942f",
"THIRD_PARTY_NOTICES.txt": "a813071223c79d70046ffde6cd6de1d52eecf7b7b3ce174a3e1bcb2c2ebb283d"
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "azure-storage",
"version": "0.3.2"
"version": "0.3.3"
}
2 changes: 1 addition & 1 deletion canvases/azure-storage/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "azure-storage",
"version": "0.3.2",
"version": "0.3.3",
"private": true,
"type": "module",
"main": "com.github.copilot/extensions/azure-storage/extension.mjs",
Expand Down
16 changes: 13 additions & 3 deletions canvases/azure-storage/release.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schemaVersion": 2,
"format": "plugin",
"name": "azure-storage",
"version": "0.3.2",
"version": "0.3.3",
"mutableDocumentation": true,
"publicationRepository": "microsoft/azure-dev-tools",
"plugin": {
Expand Down Expand Up @@ -49,8 +49,8 @@
},
"build": {
"esbuild": "0.25.12",
"lockSha256": "2e1e8312ac618fc822ed8ae6f6c836b0a8939417db3b291459d9bacb9f3abcf7",
"inputSha256": "96237f35544ee31a4069222b7c14e09354250668be0bcd9999e96cb63f558f5b"
"lockSha256": "58506251f648905700430510e8bb76f1151e345d4ab5bfeb2d49a429039b2469",
"inputSha256": "a232022935bd48209a2f787d73b5d5386b2be678eb084b4ec05d3aa63fd51438"
},
"dependencies": [
{
Expand Down Expand Up @@ -153,11 +153,21 @@
"version": "12.32.0",
"license": "MIT"
},
{
"name": "@cloud-foundation/azure-storage-core",
"version": "0.1.0",
"license": null
},
{
"name": "@cloud-foundation/studio-runtime",
"version": "0.1.0",
"license": null
},
{
"name": "@microsoft/canvas-toolkit",
"version": "0.1.0",
"license": "MIT"
},
{
"name": "@typespec/ts-http-runtime",
"version": "0.3.9",
Expand Down
10 changes: 10 additions & 0 deletions docs/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ The verifier compares all protected files on the default branch to those
tags, including additions and deletions, and pins each original tag to its
reviewed commit. Do not rewrite or move old receipts, tags, or versions.

Storage's canonical product identity is `azure-storage`, with its package under
`canvases/azure-storage`. The verifier uses that identity for its launcher,
extension, receipt and release tags; `azure-storage-canvas` is obsolete.
Current candidate tests derive versions from the marketplace; historical
immutable tag checks retain their original pins.
Current Hosted Skills, Resources Query and Cost Health versions may be released
independently at different production commits. Their release commits must still
descend from the original Cost Health baseline; the historical combined-release
tags retain their exact commit and ancestry checks.

A new candidate for any canvas uses the reviewed
`schemaVersion: 2`, `mutableDocumentation: true` contract: `release.json` and
`checksums.json` enumerate protected files only, and its `SHA256SUMS` covers
Expand Down
47 changes: 11 additions & 36 deletions scripts/verify-plugin-marketplace.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ const packages = {
extensionPath: "com.github.copilot/extensions/azure-sre-agent",
receipt: "canvases/azure-sre-agent/SHA256SUMS",
},
"azure-storage-canvas": {
path: "canvases/azure-storage-canvas",
"azure-storage": {
path: "canvases/azure-storage",
manifest: ".github/plugin/plugin.json",
skills: ["./skills/azure-storage-canvas/"],
extension: "azure-storage-canvas",
extensionPath: "com.github.copilot/extensions/azure-storage-canvas",
receipt: "canvases/azure-storage-canvas/SHA256SUMS",
skills: ["./skills/azure-storage/"],
extension: "azure-storage",
extensionPath: "com.github.copilot/extensions/azure-storage",
receipt: "canvases/azure-storage/SHA256SUMS",
},
"containers": {
path: "canvases/containers",
Expand Down Expand Up @@ -350,36 +350,11 @@ export function verifyMarketplace(manifest, { candidate = false } = {}) {
}
const costCommit = git("rev-parse", "azure-cost-health-check-v0-4-3-b355172^{commit}");
verifySubsequentReleaseCommit(commits[0], costCommit);
const pairedTags = pairedCanvasProducts.map((name) =>
releaseTagFor(name, marketplaceVersion(manifest, name), { allowMissing: candidate }));
if (pairedTags.some(Boolean) && !pairedTags.every(Boolean)) {
throw new Error("Paired canvas releases must both be candidates or both have immutable tags");
}
const pairedCommits = pairedTags.every(Boolean)
? pairedTags.map((tag) => git("rev-parse", `${tag}^{commit}`))
: [];
if (pairedCommits.length) {
const canvasCommits = pairedCommits;
if (new Set(canvasCommits).size !== 1) {
throw new Error("New canvas release tags must point to the same reviewed merge commit");
}
verifySubsequentReleaseCommit(costCommit, canvasCommits[0]);
}
const currentCostTag = releaseTagFor(
"azure-cost-health-check",
marketplaceVersion(manifest, "azure-cost-health-check"),
{ allowMissing: candidate },
);
if (currentCostTag) {
if (!pairedCommits.length) {
throw new Error("Cost Health cannot be published before the paired canvas releases");
}
verifySubsequentReleaseCommit(
pairedCommits[0],
git("rev-parse", `${currentCostTag}^{commit}`),
);
} else if (pairedCommits.length) {
verifySubsequentReleaseCommit(pairedCommits[0], "HEAD");
for (const name of [...pairedCanvasProducts, "azure-cost-health-check"]) {
const tag = releaseTagFor(name, marketplaceVersion(manifest, name),
{ allowMissing: candidate });
const commit = tag ? git("rev-parse", `${tag}^{commit}`) : git("rev-parse", "HEAD");
if (commit !== costCommit) verifySubsequentReleaseCommit(costCommit, commit);
}
}
return results;
Expand Down
11 changes: 2 additions & 9 deletions test/fixtures/marketplace.candidate.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,30 @@
"plugins": [
{
"name": "azure-functions-hosted-skills",
"version": "0.5.5",
"source": "canvases/azure-functions-hosted-skills"
},
{
"name": "azure-resources-query",
"version": "0.1.6",
"source": "canvases/azure-resources-query"
},
{
"name": "canvas-authoring",
"version": "0.1.2",
"source": "plugins/canvas-authoring"
},
{
"name": "azure-cost-health-check",
"version": "0.4.5",
"source": "canvases/azure-cost-health-check"
},
{
"name": "azure-sre-agent",
"version": "0.2.7",
"source": "canvases/azure-sre-agent"
},
{
"name": "azure-storage-canvas",
"version": "0.1.1",
"source": "canvases/azure-storage-canvas"
"name": "azure-storage",
"source": "canvases/azure-storage"
},
{
"name": "containers",
"version": "0.1.1",
"source": "canvases/containers"
}
]
Expand Down
24 changes: 14 additions & 10 deletions test/plugin-marketplace.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {

const fixture = JSON.parse(readFileSync(new URL("./fixtures/marketplace.candidate.json", import.meta.url)));
const catalog = JSON.parse(readFileSync(new URL("../.github/plugin/marketplace.json", import.meta.url)));
fixture.plugins = fixture.plugins.map((plugin) => ({ ...plugin, version: versionOf(plugin.name) }));
const root = fileURLToPath(new URL("../", import.meta.url));
const verifier = "scripts/verify-plugin-marketplace.mjs";

Expand Down Expand Up @@ -58,13 +59,13 @@ test("requires release tags for full verification, then checks every catalog plu
assert.ok(results[0].startsWith(`azure-functions-hosted-skills@${versionOf("azure-functions-hosted-skills")} azure-functions-hosted-skills-v`));
assert.ok(results[1].startsWith(`azure-resources-query@${versionOf("azure-resources-query")} azure-resources-query-v`));
assert.ok(results[2].startsWith(`canvas-authoring@${versionOf("canvas-authoring")} canvas-authoring-v`));
assert.match(results[3], /azure-cost-health-check@0\.4\.5 azure-cost-health-check-v0-4-5-/);
assert.match(results[4], /azure-sre-agent@0\.2\.7 azure-sre-agent-v0-2-7-/);
assert.ok(results[5].startsWith(`azure-storage-canvas@${versionOf("azure-storage-canvas")} azure-storage-canvas-v`));
assert.equal(results[6], "containers@0.1.1 (candidate; immutable tag pending)");
assert.ok(results[3].startsWith(`azure-cost-health-check@${versionOf("azure-cost-health-check")} azure-cost-health-check-v`));
assert.ok(results[4].startsWith(`azure-sre-agent@${versionOf("azure-sre-agent")} azure-sre-agent-v`));
assert.ok(results[5].startsWith(`azure-storage@${versionOf("azure-storage")} azure-storage-v`));
assert.ok(results[6].startsWith(`containers@${versionOf("containers")} containers-v`));
});

test("candidate validates every reviewed package and omits only missing immutable tags", () => {
test("candidate validates independently released products and omits only missing immutable tags", () => {
const candidate = modified((m) => { m.name = "azure-dev-tools"; });
const results = verifyMarketplace(candidate, { candidate: true });
assert.equal(results.length, candidate.plugins.length);
Expand All @@ -75,8 +76,8 @@ test("candidate validates every reviewed package and omits only missing immutabl
assert.equal(results[index], `${name}@${version} ${tags[0] ?? "(candidate; immutable tag pending)"}`);
}
assert.ok(results[2].startsWith(`canvas-authoring@${versionOf("canvas-authoring")} `));
assert.match(results[4], /azure-sre-agent@0\.2\.7 azure-sre-agent-v0-2-7-9da2e40/);
assert.equal(results[6], "containers@0.1.1 (candidate; immutable tag pending)");
assert.ok(results[4].startsWith(`azure-sre-agent@${versionOf("azure-sre-agent")} `));
assert.ok(results[6].startsWith(`containers@${versionOf("containers")} `));
});

test("candidate rejects corrupted protected payloads and checksum receipts", () => {
Expand Down Expand Up @@ -204,20 +205,22 @@ test("canvas-authoring uses versioned receipts without changing historical recei
assert.equal(git("show", `HEAD:${historical}`),
git("show", `canvas-authoring-v0-1-1-8af10f8:${historical}`));
assert.doesNotThrow(verify);
assert.throws(() => verify(false), /expected exactly one reviewed immutable release tag/);
const currentTag = git("tag", "-l", `canvas-authoring-v${product.version.replaceAll(".", "-")}-*`);
if (currentTag) assert.doesNotThrow(() => verify(false));
else assert.throws(() => verify(false), /expected exactly one reviewed immutable release tag/);

const receipt = `docs/canvas-authoring/${product.version}/SHA256SUMS`;
const original = readFileSync(join(clone, receipt), "utf8");
writeFileSync(join(clone, receipt), original.split("\n").filter((line) =>
!line.endsWith(" com.github.copilot/extensions/canvas-authoring/extension.mjs")).join("\n"));
git("add", "--", receipt);
git("commit", "--quiet", "-m", "Remove authoring runtime receipt");
assert.throws(verify, /checksum receipt must cover every protected plugin file exactly once/);
assert.throws(verify, /current checksum receipt differs|checksum receipt must cover every protected plugin file exactly once/);
writeFileSync(join(clone, receipt), original.replace(
/^[0-9a-f]{64}(?= plugin.json$)/m, "0".repeat(64)));
git("add", "--", receipt);
git("commit", "--quiet", "-m", "Corrupt authoring manifest receipt");
assert.throws(verify, /plugin file differs from checksum receipt: plugin.json/);
assert.throws(verify, /current checksum receipt differs|plugin file differs from checksum receipt: plugin.json/);

writeFileSync(join(clone, receipt), original);
const parts = product.version.split(".").map(Number);
Expand All @@ -236,6 +239,7 @@ test("canvas-authoring uses versioned receipts without changing historical recei
git("add", "--", manifestPath, "docs/canvas-authoring");
git("commit", "--quiet", "-m", "Prepare next authoring receipt");
assert.doesNotThrow(verify);
assert.throws(() => verify(false), /expected exactly one reviewed immutable release tag/);
} finally {
rmSync(checkout, { recursive: true, force: true });
}
Expand Down
14 changes: 8 additions & 6 deletions test/storage-release.test.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
import { copyFileSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
Expand All @@ -12,7 +12,7 @@ import {
verifyTagSource,
} from "../scripts/verify-plugin-marketplace.mjs";

const name = "azure-storage-canvas";
const name = "azure-storage";
const source = `canvases/${name}`;
const catalog = JSON.parse(readFileSync(
new URL("../.github/plugin/marketplace.json", import.meta.url)));
Expand All @@ -31,13 +31,13 @@ test("Storage accepts reviewed semantic versions without a fixed version gate",
});

test("Storage rejects obsolete identities and noncanonical package sources", () => {
for (const obsolete of ["azure-storage", "azure-storage-explorer"]) {
for (const obsolete of ["azure-storage-canvas", "azure-storage-explorer"]) {
assert.throws(() => verifyCurrentVersion(obsolete, "0.1.1"),
/expected a reviewed product/);
}
for (const wrongSource of [
"plugins/azure-storage-canvas",
"canvases/azure-storage-canvas/src",
`plugins/${name}`,
`${source}/src`,
`${source}/com.github.copilot/extensions/${name}`,
"canvases/azure-resources-query",
{ source: "github", repo: "microsoft/azure-dev-tools", path: source, ref: "main" },
Expand Down Expand Up @@ -81,7 +81,7 @@ test("the Storage candidate verifies its canonical plugin and protected receipt"
const plugin = catalog.plugins.find((entry) => entry.name === name);
assert.ok(plugin, "Storage must have a production marketplace entry");
assert.match(verifyPlugin(plugin, { candidate: true }),
/^azure-storage-canvas@\d+\.\d+\.\d+ /);
/^azure-storage@\d+\.\d+\.\d+ /);
});

test("Storage rejects changes to its protected runtime, launcher, notices, icon and receipt", () => {
Expand All @@ -101,6 +101,8 @@ test("Storage rejects changes to its protected runtime, launcher, notices, icon
};
try {
execFileSync("git", ["clone", "--quiet", "--local", "--no-hardlinks", root, clone]);
copyFileSync(join(root, "scripts/verify-plugin-marketplace.mjs"),
join(clone, "scripts/verify-plugin-marketplace.mjs"));
git("config", "user.name", "Storage release policy test");
git("config", "user.email", "release-policy@example.invalid");
assert.doesNotThrow(verify);
Expand Down