mirror of
https://github.com/TautvydasDerzinskas/Thingport.git
synced 2026-10-19 05:10:01 -05:00
fix(extension): use template for Edge certification notes
This commit is contained in:
1 parent
84b4f5bd14
commit
30be8c4349
4 files changed
+81
-8
No files matched your search
@@ -147,10 +147,13 @@ jobs:
|
||||
with:
|
||||
name: thingport-grab-store-zips
|
||||
path: extension/dist/zips
|
||||
# Edge wants complete certification notes with every submission; see the notes file.
|
||||
- name: Upload and submit for certification
|
||||
run: |
|
||||
npx tsx scripts/publish-edge.ts dist/zips/thingport-grab-edge.zip --notes \
|
||||
"Automated update to version ${{ needs.release.outputs.version }}. Changes: https://github.com/TautvydasDerzinskas/Thingport/releases/tag/${{ needs.release.outputs.tag }} -- testing instructions are unchanged from the previous submission; documentation: https://thingport.net/docs/grab/"
|
||||
env:
|
||||
RELEASE_VERSION: ${{ needs.release.outputs.version }}
|
||||
RELEASE_TAG: ${{ needs.release.outputs.tag }}
|
||||
EDGE_REVIEW_TEST_SERVER: ${{ secrets.EDGE_REVIEW_TEST_SERVER }}
|
||||
run: npx tsx scripts/publish-edge.ts dist/zips/thingport-grab-edge.zip --notes-file scripts/edge-certification-notes.txt
|
||||
|
||||
# Its own job so it can be re-run alone. Same AMO credentials as the per-merge unlisted signing
|
||||
# in extension-release.yml; see extension/CONTRIBUTING.md.
|
||||
|
||||
@@ -262,6 +262,18 @@ previous version is still in certification, the job fails with `InProgressSubmis
|
||||
Microsoft has finished, open the failed run and use **Re-run failed jobs**. (The release itself,
|
||||
tag and changelog included, is already done at that point and isn't repeated.)
|
||||
|
||||
Edge wants complete certification notes (under 2,000 characters) with every submission, and may fail
|
||||
one that only points at an earlier one. The job sends
|
||||
[scripts/edge-certification-notes.txt](scripts/edge-certification-notes.txt) with the version and
|
||||
the release link filled in; edit that file when testing steps or permissions change. The script
|
||||
refuses to upload if the filled-in notes are too long.
|
||||
|
||||
Reviewers need a Thingport server to test against. If the optional repo secret
|
||||
`EDGE_REVIEW_TEST_SERVER` is set, its text goes into the notes (e.g. "Use the test server
|
||||
https://… with email … and password …."). Without it, the notes tell the reviewer how to start a
|
||||
server of their own with `install.sh`. Keep reviewer login details in that secret only: this repo
|
||||
is public.
|
||||
|
||||
To test the script against Edge by hand, with the credentials below in your environment:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
Thingport Grab {{version}}. Changes in this version: {{changes}}
|
||||
|
||||
WHAT IT DOES
|
||||
Imports 3D-printing models from MakerWorld, Printables and Thingiverse into the user's own self-hosted Thingport server (open source: https://github.com/TautvydasDerzinskas/Thingport). It has no servers of its own and sends nothing to the developer or any third party.
|
||||
|
||||
HOW TO TEST
|
||||
1. A Thingport server is required. {{testServer}}
|
||||
2. Click the toolbar icon, enter the server address, email and password, and connect. Allow the access request for that address.
|
||||
3. Open a model page, e.g. https://www.printables.com/model/3161-3d-benchy or https://makerworld.com/en/models/3080401, click the Thingport button at the bottom right of the page and choose Import. The model appears in the Thingport web app and under Recent imports in the toolbar popup.
|
||||
4. MakerWorld downloads need a MakerWorld login in the same browser; Printables and Thingiverse work without one.
|
||||
|
||||
PERMISSIONS
|
||||
- storage: the server address, sign-in and recent imports.
|
||||
- cookies: reads the MakerWorld session cookie, on makerworld.com only, which MakerWorld requires for downloads.
|
||||
- downloads: reads the link from a download MakerWorld's page starts, then cancels it.
|
||||
- host access: the three model sites, and the user's own server address (requested at setup).
|
||||
|
||||
The code isn't minified or obfuscated. Documentation: https://thingport.net/docs/grab/ Privacy policy: https://github.com/TautvydasDerzinskas/Thingport/blob/main/extension/PRIVACY.md
|
||||
@@ -5,7 +5,12 @@
|
||||
// Microsoft anywhere from hours to days -- this only gets the update into the queue.
|
||||
//
|
||||
// EDGE_PRODUCT_ID=... EDGE_CLIENT_ID=... EDGE_API_KEY=... \
|
||||
// tsx scripts/publish-edge.ts dist/zips/thingport-grab-edge.zip [--notes "..."]
|
||||
// tsx scripts/publish-edge.ts dist/zips/thingport-grab-edge.zip [--notes-file <path> | --notes "..."]
|
||||
//
|
||||
// Edge requires complete certification notes (under 2,000 characters) with every submission, and
|
||||
// may fail one that only points at an earlier one. --notes-file fills a template's {{version}},
|
||||
// {{changes}} and {{testServer}} from RELEASE_VERSION, RELEASE_TAG and EDGE_REVIEW_TEST_SERVER (a
|
||||
// secret: a reviewer account on a test server); see edge-certification-notes.txt.
|
||||
//
|
||||
// Run by .github/workflows/extension-release.yml when package.json's version hasn't been
|
||||
// published to Edge yet (see extension/CONTRIBUTING.md for setting up the credentials).
|
||||
@@ -16,6 +21,14 @@ import { readFile } from "node:fs/promises";
|
||||
const API_BASE = process.env.EDGE_API_BASE || "https://api.addons.microsoftedge.microsoft.com";
|
||||
const POLL_INTERVAL_MS = 5000;
|
||||
const POLL_TIMEOUT_MS = 10 * 60 * 1000;
|
||||
const NOTES_MAX_CHARS = 2000;
|
||||
const REPO_URL = "https://github.com/TautvydasDerzinskas/Thingport";
|
||||
// Without a reviewer account, the reviewer runs a server of their own.
|
||||
const SELF_HOSTED_TEST_SERVER =
|
||||
"No shared test server is provided. Start one with Docker in about two minutes (Linux or macOS) by " +
|
||||
"running: curl -fsSL https://thingport.net/install.sh | WEB_PORT=8080 sh\nThen open " +
|
||||
"http://localhost:8080 and register; the first account becomes the admin. Use http://localhost:8080 " +
|
||||
"as the server address in step 2.";
|
||||
|
||||
type Operation = {
|
||||
id?: string;
|
||||
@@ -45,6 +58,26 @@ function argValue(flag: string): string | undefined {
|
||||
return index >= 0 ? process.argv[index + 1] : undefined;
|
||||
}
|
||||
|
||||
/** The template with its placeholders filled; refuses notes Edge would reject. */
|
||||
async function notesFromTemplate(path: string): Promise<string> {
|
||||
const version = process.env.RELEASE_VERSION?.trim() || "update";
|
||||
const tag = process.env.RELEASE_TAG?.trim();
|
||||
const values: Record<string, string> = {
|
||||
version,
|
||||
changes: tag ? `${REPO_URL}/releases/tag/${tag}` : `${REPO_URL}/blob/main/extension/CHANGELOG.md`,
|
||||
testServer: process.env.EDGE_REVIEW_TEST_SERVER?.trim() || SELF_HOSTED_TEST_SERVER,
|
||||
};
|
||||
const notes = (await readFile(path, "utf8"))
|
||||
.replace(/\{\{(\w+)\}\}/g, (match, key: string) => values[key] ?? match)
|
||||
.trim();
|
||||
const unfilled = notes.match(/\{\{\w+\}\}/);
|
||||
if (unfilled) throw new Error(`${path}: unknown placeholder ${unfilled[0]}`);
|
||||
if (notes.length >= NOTES_MAX_CHARS) {
|
||||
throw new Error(`Certification notes are ${notes.length} characters; Edge allows under ${NOTES_MAX_CHARS}.`);
|
||||
}
|
||||
return notes;
|
||||
}
|
||||
|
||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
/** A 202 whose Location header carries the operation to poll (documented as the bare ID; a
|
||||
@@ -85,8 +118,17 @@ async function waitFor(url: string, auth: Record<string, string>, what: string):
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const zipPath = process.argv.slice(2).find((arg, i, all) => !arg.startsWith("--") && all[i - 1] !== "--notes");
|
||||
if (!zipPath) throw new Error("Usage: tsx scripts/publish-edge.ts <package.zip> [--notes <text>]");
|
||||
const zipPath = process.argv
|
||||
.slice(2)
|
||||
.find((arg, i, all) => !arg.startsWith("--") && all[i - 1] !== "--notes" && all[i - 1] !== "--notes-file");
|
||||
if (!zipPath) {
|
||||
throw new Error("Usage: tsx scripts/publish-edge.ts <package.zip> [--notes-file <path> | --notes <text>]");
|
||||
}
|
||||
// Checked before the upload, so a too-long note doesn't leave a half-done draft behind.
|
||||
const notesFile = argValue("--notes-file");
|
||||
const notes = notesFile
|
||||
? await notesFromTemplate(notesFile)
|
||||
: (argValue("--notes") ?? "Automated update. Testing instructions are unchanged from the previous submission.");
|
||||
const productId = requireEnv("EDGE_PRODUCT_ID");
|
||||
const auth = { Authorization: `ApiKey ${requireEnv("EDGE_API_KEY")}`, "X-ClientID": requireEnv("EDGE_CLIENT_ID") };
|
||||
const productUrl = `${API_BASE}/v1/products/${productId}`;
|
||||
@@ -101,8 +143,6 @@ async function main(): Promise<void> {
|
||||
const uploaded = await waitFor(`${productUrl}/submissions/draft/package/operations/${uploadId}`, auth, "Upload");
|
||||
console.log(`Upload processed: ${uploaded.message ?? "ok"}`);
|
||||
|
||||
const notes =
|
||||
argValue("--notes") ?? "Automated update. Testing instructions are unchanged from the previous submission.";
|
||||
const publishId = await startOperation(
|
||||
`${productUrl}/submissions`,
|
||||
{ method: "POST", headers: { ...auth, "Content-Type": "application/json" }, body: JSON.stringify({ notes }) },
|
||||
|
||||
Reference in new issue
Block a user