Get started / Samples: packages and cloud targets exp Edit on GitHubMarkdown

Distributing the Cookwala samples

Status (2026-10-06): published on PyPI, npm, Maven Central, NuGet, Homebrew and GHCR (tag samples-v0.3.0), with the single file and the .deb on GitHub Releases. Chocolatey is submitted and in moderation; RPM, pacman (AUR), apk, conda-forge and Snap have manifests ready but nothing submitted yet. JFrog Artifactory is a bring-your-own-instance channel — publish.sh pushes to whatever instance you point it at; there is no public one. Version: [VERSION](VERSION) (python packaging/build.py --check-versions fails CI if a manifest disagrees).

One payload, many channels #

The Python package is standard library only, so one file, cookwala-samples-<v>.pyz (a zipapp), runs on any Python 3.9+ on any OS. The OS package managers ship that file plus a one-line launcher; language registries ship their native port.

python samples/packaging/build.py builds the pyz, the wheel and sdist, the .deb, and renders every manifest below into samples/build/manifests/ with the version, URLs and sha256 filled in. Assets are expected at https://github.com/amado2k5/cookwala/releases/download/samples-v<v>/; pass --base-url to point them at another host, such as an Artifactory generic repository.

Channels #

ChannelInstall (once published)Source in this repoPublished byNeeds
PyPI (pip, pipx, uv)pip install cookwala-samplespython/pyproject.tomlCI on tagPyPI trusted publisher for this workflow
npmnpm i -g @cookwala/samples · npx @cookwala/samples demojs/package.jsonCI on tagNPM_TOKEN, the @cookwala npm scope
Maven Central (Maven, Gradle, sbt, Leiningen)ai.cookwala:cookwala-samples:0.3.0java/pom.xml, java/build.gradle.ktsCI on tagMAVEN_CENTRAL_USERNAME/PASSWORD, MAVEN_GPG_PRIVATE_KEY/PASSPHRASE, the ai.cookwala namespace
GitHub Packages (Maven)as above, with the GitHub Packages repositoryjava/build.gradle.ktsCI on tagnothing extra (GITHUB_TOKEN)
NuGetdotnet add package Cookwala.Samples · dotnet tool install -g Cookwala.Samples.Tooldotnet/Directory.Build.props, dotnet/src/*/*.csprojCI on taga nuget.org trusted-publishing policy (owner amado2k5, repo cookwala, workflow samples.yml, environment release, pattern Cookwala.*); the package owner name is read from the NUGET_USER repository variable (default amado2026); or a NUGET_API_KEY secret
Homebrew (macOS, Linux)brew install amado2k5/cookwala/cookwala-samplespackaging/homebrew/cookwala-samples.rb.inCI on tag, to the tap repositorya tap repo amado2k5/homebrew-cookwala, HOMEBREW_TAP_TOKEN
Chocolatey (Windows)choco install cookwala-samplespackaging/chocolatey/CI on tag (Windows runner)CHOCO_API_KEY; community moderation before it is public
Scoop (Windows)scoop bucket add cookwala https://github.com/amado2k5/scoop-cookwala · scoop install cookwala-samplespackaging/scoop/cookwala-samples.json.inExcavator updates version/hash on each samples-v* releaselive — bucket published, see packaging/scoop/SUBMIT.md
apt (Debian, Ubuntu)sudo apt install ./cookwala-samples_0.3.0_all.deb, or from an apt repositorybuilt by packaging/build.pyGitHub release asset; Artifactory Debian repoan apt repository (Artifactory, Cloudsmith, a PPA) for apt install cookwala-samples
RPM (dnf, yum, zypper)sudo dnf install cookwala-samplespackaging/rpm/cookwala-samples.spec.inby hand: rpmbuild -ba, Fedora COPR or openSUSE OBSa COPR or OBS project
pacman (Arch, AUR)yay -S cookwala-samplespackaging/arch/PKGBUILD.inby hand: push the rendered PKGBUILD to the AURan AUR account
apk (Alpine)apk add cookwala-samplespackaging/alpine/APKBUILD.inby hand: aports merge requestan aports maintainer
conda-forge (conda, mamba, pixi)conda install -c conda-forge cookwala-samplespackaging/conda/meta.yaml.in (from the PyPI sdist)by hand: staged-recipes pull requestthe PyPI release first
Snapsudo snap install cookwala-samples --edgepackaging/snap/snapcraft.yamlby hand: snapcraft uploada Snap Store account
OCI image (GHCR, Docker Hub, Quay, ECR, ACR)docker run --rm -p 8080:8080 ghcr.io/amado2k5/cookwala-samplespackaging/docker/ContainerfileCI on tag (GHCR)nothing extra (GITHUB_TOKEN)
Helm (Kubernetes)helm install samples packaging/helm/cookwala-samplespackaging/helm/cookwala-samples/from the repository; an OCI chart push is one command (helm push)a chart registry, if wanted
JFrog Artifactorythe native client of each type, pointed at your Artifactorypackaging/artifactory/publish.shCI on tag, or by handARTIFACTORY_URL, ARTIFACTORY_USER, ARTIFACTORY_TOKEN (+ ARTIFACTORY_DOCKER_REGISTRY)
GitHub releasedownload the pyz, deb, wheel, sdist and SHA256SUMS.github/workflows/samples.ymlCI on tagnothing extra
Azure Functionsdeploycloud/azure-functions/CI on demand (samples-cloud.yml, target=azure), or by hand (az, func)an Azure subscription + cloud-test environment secrets
AWS Lambdadeploycloud/aws-lambda/ (SAM)CI on demand (samples-cloud.yml, target=aws), or by hand (sam deploy)an AWS account + cloud-test environment secret
Google Cloud Run functionsdeploycloud/gcp-functions/CI on demand (samples-cloud.yml, target=gcp), or by hand (gcloud)a GCP project + cloud-test environment secrets
OpenShift, OpenShift Serverlessdeploycloud/openshift/by hand (oc)a cluster

Not provided, with the reason: winget needs a native Windows installer (exe or msi) and the samples ship a Python zipapp; Windows users have Chocolatey, Scoop, pip and npm. Go modules and crates.io have no sample port yet (the SDKs exist in sdk/go and sdk/rust).

Artifactory #

packaging/artifactory/publish.sh pushes each package type to a local repository of the matching type, with each ecosystem's own client: twine (PyPI), npm, Maven (-Partifactory) or Gradle, dotnet nuget push, Debian (with deb.distribution, deb.component, deb.architecture properties), RPM, Docker, and a generic repository for the pyz. Create the repositories first (default keys cookwala-<type>-local, overridable by environment variables listed in the script); then point remote or virtual repositories at them as usual.

Releasing #

  1. Bump samples/VERSION and every manifest (python samples/packaging/build.py --check-versions lists them); add a line to the Python, npm, Maven and NuGet changelogs if you keep them.
  2. Regenerate the bundle if the vocabularies, limits or example recipes changed: python samples/tools/build_bundle.py.
  3. Merge, then tag: git tag samples-v0.3.0 && git push origin samples-v0.3.0.
  4. CI tests every port, builds every package, creates the GitHub release, and publishes to each registry whose secret is set. The other channels take the rendered manifests from the release's samples-packages artifact.

Verified in this repository (2026-10-05) #

Installed the way a user installs, from local stand-ins for each registry #

packaging/install-test.sh repeats all of this, and CI runs it on every change (install job). No registry is contacted: npm gets a Verdaccio registry, pip a PEP 503 index, Maven and Gradle a file repository, NuGet a folder feed, apt a repository made with apt-ftparchive, Homebrew a local tap and a local copy of the release asset.

ChannelWhat ranWhere
npmnpx @cookwala/samples demo; npm i -g @cookwala/samples; import { demo } from '@cookwala/samples' in a new projectempty npm home and cache
PyPIpip install cookwala-samples from the wheel and from the sdist; pipx install; uvx cookwala-samplesnew virtual environments
single filepython3 cookwala-samples-0.3.0.pyz demoany Python 3.9+
Mavena new project depending on ai.cookwala:cookwala-samples:0.3.0, compiled and run; java -jar on the artifactempty local repository
Gradlea new project with implementation("ai.cookwala:cookwala-samples:0.3.0"), gradle runempty Gradle home
NuGetdotnet add package Cookwala.Samples in a new console app, dotnet run; dotnet tool install -g Cookwala.Samples.Tool, then cookwala-samplesempty NuGet cache and tool home
aptapt-get install cookwala-samples from an apt repository (pulls in python3), run, apt-get remove leaves nothingDebian 12 container
RPMrpmbuild -bb on the rendered spec (its %check runs the pyz), dnf install, run, dnf removeFedora 41 container
pacmanmakepkg (checks the sha256 and runs check()), pacman -U, run, pacman -RArch Linux container
apkabuild -r (sha256, check(), signed), apk add, run, apk delAlpine 3.20 container
Homebrewbrew install from a tap (installs [email protected]), run, brew test passesHomebrew's Linux container
condaconda build on the rendered recipe from the sdist (its tests run), conda create, runMiniforge container
Chocolateychoco pack makes the .nupkg; the install and uninstall scripts run under PowerShell, download the asset, check its sha256, create and remove the command; Chocolatey's three helper functions are stood inPowerShell on Linux; a real choco install needs Windows
Scoopscoop install from the live bucket on windows-latest, then cookwala-samples demo — daily + on packaging changes.github/workflows/samples-channel-scoop.yml
OCI imageruns the demo; serves HTTP as an arbitrary uid on a read-only root filesystemDocker
AWS Lambdathe handler answers inside AWS's own public.ecr.aws/lambda/python:3.12 image (runtime interface emulator)Docker
Azure Functionsfunc start with Azure Functions Core Tools 4 serves /api/health, /api/v1/samples/plan and the demolocal Functions host
Google Cloud functionsfunctions-framework --target samples serves every endpointlocal Functions Framework

The first manual pass found two real bugs, now fixed and covered by tests: the JavaScript and Java run commands exited 0 when a job did not complete (Python and C# exited 1), and an unknown --fault kind was ignored instead of being a usage error. The four CLIs now give the same exit codes and the same reports.

Tests and builds #

WhatHow
Python: 33 tests, including equality with the reference dry run for every example recipe and device, JSON Schema validation of every request, status, log and incident, the demo against the reference hub over HTTP, and every service endpoint with the network disabledpython -m unittest discover -s samples/python/tests
JavaScript: 42 tests (Node 18 and 22); Markdown and CSV demo reports byte-identical to Pythonnpm test in samples/js
Java: tests under both Maven and Gradle (Java 17 and 21); demo identical to Python; sources and javadoc jars for Maven Centralmvn -B package, gradle build in samples/java
.NET: 55 tests; demo identical to Pythondotnet test in samples/dotnet
pyz, wheel and sdist build; twine check passespackaging/build.py
the Helm chart lints and renders a Deployment, Service, Ingress and RouteHelm 3.16
the Helm chart installs into a throwaway kind cluster and the Knative service answers over Kourier, both serving /health and the demo report.github/workflows/samples-cloud.yml kind job (kind, Knative Serving 1.23)
the Azure Functions deploy runs on a real subscription: Bicep stack, zip publish, /health + /plan + demo report over the public URL, then the resource group is deleted.github/workflows/samples-cloud.yml azure job (workflow_dispatch, cloud-test environment)

Not run here #

The OpenShift template (needs a real cluster; Helm and Knative on a throwaway kind cluster are covered by the kind job). Everything else above runs in CI: snap install (samples-channel-snap.yml), choco install and scoop install on windows-latest (samples-channel-chocolatey.yml, samples-channel-scoop.yml), and all three cloud deploys — Azure Functions, AWS Lambda and Google Cloud Run functions — are proven against real accounts; see below.

Proven against real accounts: Azure Functions, AWS Lambda, Google Cloud #

Run 37443213472 (2026-10-06, samples-cloud.yml dispatched with target=azure behind the protected cloud-test environment):

  • OIDC login via azure/login — a Microsoft Entra app registration (cookwala-ci) with a federated credential scoped to environment:cloud-test; no stored client secret.
  • az group create + az deployment group create deployed samples/cloud/azure-functions/main.bicep (storage account, Y1 consumption plan, Application Insights, Function App) to eastus — overridable via the AZURE_REGION variable, since new subscriptions reject some regions (RequestDisallowedByAzure / "not accepting new customers").
  • Zip-deployed samples/cloud/azure-functions/ with a remote build.
  • Called the real endpoints: GET /api/health returned {"ok": true, ...}, POST /api/v1/samples/plan ranked the devices, and GET /api/v1/samples/demo?format=markdown produced the report with 3 completed.
  • if: always() teardown deleted the resource group.

What the job actually did, from the run log:

text
$ az group create -n cw-samples-ci-37443213472 -l eastus --tags ttl=1d purpose=samples-ci
$ az deployment group create -g cw-samples-ci-37443213472 -f samples/cloud/azure-functions/main.bicep -p appName=cwci37443213472
$ az functionapp deployment source config-zip -g cw-samples-ci-37443213472 -n cwci37443213472 --src /tmp/app.zip --build-remote true
$ curl "https://cwci37443213472.azurewebsites.net/api/health?code=<key>"
{"ok": true, "service": "cookwala-samples", "version": "0.3.0", "core": "0.2.0"}
$ curl "https://cwci37443213472.azurewebsites.net/api/v1/samples/demo?format=markdown&code=<key>"
# Cookwala samples demo (offline, simulated kitchen) ... 3 completed ...
azure function demo verified
$ az group delete --yes --no-wait -n cw-samples-ci-37443213472

Required secrets in the cloud-test environment: AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_SUBSCRIPTION_ID. One setup detail worth noting: because this repository was renamed, GitHub emits the OIDC subject with numeric entity ids (repo:amado2k5@20147989/cookwala@1403544608:environment:cloud-test) — the federated credential's subject must match that form, not repo:amado2k5/cookwala:....

Run 37448439405 (2026-10-06, target=aws):

  • OIDC role assumption via configure-aws-credentials — an IAM OIDC provider for token.actions.githubusercontent.com plus a cookwala-ci role whose trust policy matches repo:amado2k5*/cookwala*:environment:cloud-test; no stored access keys.
  • sam build + sam deploy created the stack cw-samples-ci-37448439405 in eu-west-1: the Lambda function, its execution role, and an HTTP API.
  • Called the public HttpApiUrl: /health returned {"ok": true, ...} and /v1/samples/demo?format=markdown produced the report with 3 completed.
  • if: always() teardown ran sam delete.

Two findings this run exposed, both fixed in the same PR: the SAM template's AuthType: NONE needed an explicit AWS::Lambda::Permission for lambda:InvokeFunctionUrl, and anonymous Function-URL calls are blocked on brand-new AWS accounts regardless — so the workflow proves the deploy through the API Gateway HttpApiUrl output instead.

The blocked path and the working path, verified by hand against a real stack:

text
$ sam deploy --stack-name cw-debug --region eu-west-1 --parameter-overrides FunctionUrlAuth=NONE
Successfully created/updated stack - cw-debug
$ curl https://4yaogxj73phvz24wu7w3p6t5uu0hhhfh.lambda-url.eu-west-1.on.aws/health
{"Message":"Forbidden ..."}            # 403 even with AuthType NONE + public policy (new-account block)
$ curl https://eh00djbagh.execute-api.eu-west-1.amazonaws.com/health
{"ok": true, "service": "cookwala-samples", "version": "0.3.0", "core": "0.2.0"}
$ curl "https://eh00djbagh.execute-api.eu-west-1.amazonaws.com/v1/samples/demo?format=markdown"
# Cookwala samples demo (offline, simulated kitchen) ... 3 completed ...
$ sam delete --no-prompts --stack-name cw-debug    # Deleted successfully

Required secret in the cloud-test environment: AWS_ROLE_TO_ASSUME (the role ARN).

Run 37450278019 (2026-10-06, target=gcp):

  • OIDC via google-github-actions/auth — a workload identity pool + provider on project cookwala-ci-5604 impersonating service account github-ci@, conditioned on the numeric repository_id (1403544608), so the repo-rename subject quirk cannot apply.
  • gcloud functions deploy --gen2 created cookwala-samples-ci-37450278019 in europe-west1 (Cloud Run function, --allow-unauthenticated).
  • Called the public .run.app URL: /health returned {"ok": true, ...} and /v1/samples/demo?format=markdown produced the report with 3 completed.
  • if: always() teardown deleted the function.

What the job actually did, from the run log:

text
$ gcloud functions deploy cookwala-samples-ci-37450278019 --gen2 --runtime python312 \
    --region europe-west1 --source samples/cloud/gcp-functions --entry-point samples \
    --trigger-http --allow-unauthenticated --project cookwala-ci-5604
serviceConfig:
  uri: https://cookwala-samples-ci-37450278019-wokqyuqweq-ew.a.run.app
$ curl https://cookwala-samples-ci-37450278019-wokqyuqweq-ew.a.run.app/health
{"ok": true, "service": "cookwala-samples", "version": "0.3.0", "core": "0.2.0"}
$ curl "https://cookwala-samples-ci-37450278019-wokqyuqweq-ew.a.run.app/v1/samples/demo?format=markdown"
# Cookwala samples demo (offline, simulated kitchen) ... 3 completed ...
gcp function demo verified
$ gcloud functions delete cookwala-samples-ci-37450278019 --gen2 --region europe-west1

Required secrets in the cloud-test environment: GCP_PROJECT, GCP_SERVICE_ACCOUNT, GCP_WORKLOAD_IDENTITY_PROVIDER. Gen2 functions need an open billing account on the project — a fresh billing account's free trial plus the always-free 2M-invocation tier keeps CI cost at ~$0.