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 .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ run = "mise run install && bun scripts/render-homebrew-formula.ts"

[tasks.render-chocolatey-package]
run = "mise run install && bun scripts/render-chocolatey-package.ts"

[tasks.render-winget-manifest]
run = "mise run install && bun scripts/render-winget-manifest.ts"
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Skillet installs, discovers, and updates `SKILL.md`-based skills across supporte
| Binary release | Download from GitHub Releases | Planned |
| Homebrew | `brew install skillet` | Configured |
| Chocolatey | `choco install skillet` | Configured |
| winget | `winget install skillet` | Planned |
| winget | `winget install skillet` | Configured |
| npm / npx | `npx skillet ...` | Planned |
| Docker | `docker run ... skillet ...` | Planned |
| Local dev | `mise run dev -- --help` | Available |
Expand Down Expand Up @@ -143,3 +143,4 @@ Distribution docs:

- Homebrew: `docs/distribution/homebrew.md`
- Chocolatey: `docs/distribution/chocolatey.md`
- winget: `docs/distribution/winget.md`
25 changes: 25 additions & 0 deletions docs/distribution/winget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# winget Distribution

Skillet ships a winget manifest set for Windows installs.

## Release Flow

1. Build Windows artifact and checksums.
2. Render `packaging/winget/<version>/` from `SHA256SUMS`.
3. Validate manifests with `winget validate`.
4. Submit manifests to [`microsoft/winget-pkgs`](https://github.com/microsoft/winget-pkgs).

## Commands

```bash
mise run build -- --targets=windows-x64
bun scripts/write-checksums.ts
mise run render-winget-manifest -- --version <version>
```

Validation and install checks (Windows):

```powershell
winget validate packaging/winget/<version>/echohello-dev.skillet.yaml
winget install echohello-dev.skillet
```
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"test": "vitest run",
"build": "bun scripts/build-release.ts",
"render:homebrew": "bun scripts/render-homebrew-formula.ts",
"render:choco": "bun scripts/render-chocolatey-package.ts"
"render:choco": "bun scripts/render-chocolatey-package.ts",
"render:winget": "bun scripts/render-winget-manifest.ts"
},
"dependencies": {
"cac": "^6.7.14",
Expand Down
13 changes: 13 additions & 0 deletions packaging/winget/0.0.0/echohello-dev.skillet.installer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
PackageIdentifier: echohello-dev.skillet
PackageVersion: 0.0.0
Installers:
- Architecture: x64
InstallerType: exe
InstallerUrl: https://github.com/echohello-dev/skillet/releases/download/v0.0.0/skillet-windows-x64.exe
InstallerSha256: 51CDDEFDE243F0F27E501CA420D5E1D1B9CAD548DC9884EA4052D2915AFEF179
AppsAndFeaturesEntries:
- DisplayName: skillet
Commands:
- skillet
ManifestType: installer
ManifestVersion: 1.6.0
17 changes: 17 additions & 0 deletions packaging/winget/0.0.0/echohello-dev.skillet.locale.en-US.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
PackageIdentifier: echohello-dev.skillet
PackageVersion: 0.0.0
PackageLocale: en-US
Publisher: echohello-dev
PublisherUrl: https://github.com/echohello-dev
PublisherSupportUrl: https://github.com/echohello-dev/skillet/issues
Author: echohello-dev
PackageName: skillet
PackageUrl: https://github.com/echohello-dev/skillet
License: MIT
ShortDescription: Portable CLI for managing agent skills.
Moniker: skillet
Tags:
- cli
- skills
ManifestType: defaultLocale
ManifestVersion: 1.6.0
5 changes: 5 additions & 0 deletions packaging/winget/0.0.0/echohello-dev.skillet.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PackageIdentifier: echohello-dev.skillet
PackageVersion: 0.0.0
DefaultLocale: en-US
ManifestType: version
ManifestVersion: 1.6.0
63 changes: 63 additions & 0 deletions scripts/render-winget-manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import fs from "node:fs";
import path from "node:path";
import { parseSha256Sums } from "../src/distribution/checksums";
import { renderWingetManifestFiles } from "../src/distribution/winget";

function main(): void {
const args = process.argv.slice(2);
const version = readArg(args, "--version") ?? readVersionFromPackageJson();
const checksumsPath = path.resolve(readArg(args, "--checksums") ?? "dist/SHA256SUMS");
const outputDir = path.resolve(readArg(args, "--output-dir") ?? `packaging/winget/${version}`);
const releaseUrlBase =
readArg(args, "--release-url-base") ??
`https://github.com/echohello-dev/skillet/releases/download/v${version}`;

if (!fs.existsSync(checksumsPath)) {
throw new Error(`Checksums file not found: ${checksumsPath}`);
}

const checksums = parseSha256Sums(fs.readFileSync(checksumsPath, "utf8"));
const files = renderWingetManifestFiles({ version, releaseUrlBase, checksumsByArtifact: checksums });

fs.mkdirSync(outputDir, { recursive: true });
fs.writeFileSync(path.join(outputDir, "echohello-dev.skillet.yaml"), files.versionManifest);
fs.writeFileSync(path.join(outputDir, "echohello-dev.skillet.installer.yaml"), files.installerManifest);
fs.writeFileSync(path.join(outputDir, "echohello-dev.skillet.locale.en-US.yaml"), files.localeManifest);
console.log(`Wrote winget manifests to ${outputDir}`);
}

function readArg(args: string[], name: string): string | undefined {
for (let i = 0; i < args.length; i += 1) {
const token = args[i];
if (token === name) {
const next = args[i + 1];
if (!next || next.startsWith("--")) {
throw new Error(`Missing value for ${name}`);
}
return next;
}

const prefix = `${name}=`;
if (token.startsWith(prefix)) {
const value = token.slice(prefix.length);
if (value.length === 0) {
throw new Error(`Missing value for ${name}`);
}
return value;
}
}

return undefined;
}

function readVersionFromPackageJson(): string {
const packageJsonPath = path.resolve("package.json");
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) as { version?: string };
if (!packageJson.version || packageJson.version.length === 0) {
throw new Error("package.json version is missing");
}

return packageJson.version;
}

main();
70 changes: 70 additions & 0 deletions src/distribution/winget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const WINDOWS_X64_ARTIFACT = "skillet-windows-x64.exe";
const PACKAGE_IDENTIFIER = "echohello-dev.skillet";

export type WingetManifestOptions = {
version: string;
releaseUrlBase: string;
checksumsByArtifact: Map<string, string>;
};

export type WingetManifestFiles = {
versionManifest: string;
installerManifest: string;
localeManifest: string;
};

export function renderWingetManifestFiles(options: WingetManifestOptions): WingetManifestFiles {
const releaseUrlBase = options.releaseUrlBase.replace(/\/+$/, "");
const installerUrl = `${releaseUrlBase}/${WINDOWS_X64_ARTIFACT}`;
const installerSha256 = requireChecksum(options.checksumsByArtifact, WINDOWS_X64_ARTIFACT).toUpperCase();

return {
versionManifest: `PackageIdentifier: ${PACKAGE_IDENTIFIER}
PackageVersion: ${options.version}
DefaultLocale: en-US
ManifestType: version
ManifestVersion: 1.6.0
`,
installerManifest: `PackageIdentifier: ${PACKAGE_IDENTIFIER}
PackageVersion: ${options.version}
Installers:
- Architecture: x64
InstallerType: exe
InstallerUrl: ${installerUrl}
InstallerSha256: ${installerSha256}
AppsAndFeaturesEntries:
- DisplayName: skillet
Commands:
- skillet
ManifestType: installer
ManifestVersion: 1.6.0
`,
localeManifest: `PackageIdentifier: ${PACKAGE_IDENTIFIER}
PackageVersion: ${options.version}
PackageLocale: en-US
Publisher: echohello-dev
PublisherUrl: https://github.com/echohello-dev
PublisherSupportUrl: https://github.com/echohello-dev/skillet/issues
Author: echohello-dev
PackageName: skillet
PackageUrl: https://github.com/echohello-dev/skillet
License: MIT
ShortDescription: Portable CLI for managing agent skills.
Moniker: skillet
Tags:
- cli
- skills
ManifestType: defaultLocale
ManifestVersion: 1.6.0
`,
};
}

function requireChecksum(checksumsByArtifact: Map<string, string>, artifactName: string): string {
const checksum = checksumsByArtifact.get(artifactName);
if (!checksum) {
throw new Error(`Missing checksum for artifact: ${artifactName}`);
}

return checksum;
}
24 changes: 24 additions & 0 deletions tests/distribution/winget.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, expect, it } from "vitest";
import { renderWingetManifestFiles } from "../../src/distribution/winget";

describe("renderWingetManifestFiles", () => {
it("renders winget manifest trio with checksum and installer URL", () => {
const files = renderWingetManifestFiles({
version: "1.2.3",
releaseUrlBase: "https://github.com/echohello-dev/skillet/releases/download/v1.2.3",
checksumsByArtifact: new Map([
["skillet-windows-x64.exe", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
]),
});

expect(files.versionManifest).toContain("PackageIdentifier: echohello-dev.skillet");
expect(files.versionManifest).toContain("PackageVersion: 1.2.3");
expect(files.installerManifest).toContain(
"InstallerUrl: https://github.com/echohello-dev/skillet/releases/download/v1.2.3/skillet-windows-x64.exe",
);
expect(files.installerManifest).toContain(
"InstallerSha256: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
);
expect(files.localeManifest).toContain("PackageName: skillet");
});
});