Feature Description
Add a new action-type input (article / kernel) to action.yaml so that mode: create and mode: update apply independently to both article deployment and custom kernel deployment. When action-type: kernel is set, the action builds a Docker image, pushes it to Google Artifact Registry (GCR), and registers it with Jupyter Enterprise Gateway (EG) — so instructors can ship a course-specific Python environment from the same action they already use to publish courses.
action-type: article (default — existing behaviour unchanged):
mode: create → create a new course or blog post
mode: update → update an existing course or blog post
action-type: kernel (new):
mode: create → build image + push to GCR + register new kernelspec on EG host + validate
mode: update → rebuild image + push new tag + patch existing kernelspec + validate
action-type defaults to article so every existing workflow continues working with zero changes.
Motivation
Kernels that power qBook course notebooks are currently deployed manually to the EG host — there is no CI pipeline, no version history in GCR, and no way for an instructor to self-serve a custom environment for their course.
This creates three problems:
No per-course kernel isolation. All courses share the kernels that were manually deployed. An instructor who needs a specific version of qiskit or pennylane cannot get a custom environment without server-level access.
No kernel availability check at publish time. The course publish step does not verify that kernels referenced in course.json are accessible to the instructor before the course goes live. Students discover a missing kernel mid-session, not at publish time.
No create vs update distinction for kernels. Creating a kernel for the first time (new kernelspec directory + EG restart) is a meaningfully different operation from updating one (patch image_name in existing kernel.json, no restart). There is currently no way to express this difference in the action.
Proposed Solution
Input structure
The new action-type input sits at the top level. Everything else flows from it:
action-type: article | kernel ← new top-level switch (default: article)
├── mode: create | update ← applies to both article and kernel
├── article-type: course | blog ← only used when action-type is article
└── kernel-name, gcr-*, eg-*, … ← only used when action-type is kernel
create vs update for kernels
| |
mode: create |
mode: update |
| Build & push image |
Full build, commit SHA tag + :latest |
Rebuild with new commit SHA tag + :latest |
| kernelspec on EG host |
Install new directory via jupyter-docker-spec install |
Patch image_name in existing kernel.json only (SSH Python) |
| EG restart |
Yes — new kernelspec must be discovered |
No — running sessions unaffected; new starts use new image |
| Validate + smoke test |
Yes |
Yes |
New example workflows
Two new files added to examples/ alongside the existing workflow.yml:
examples/kernel-create.yml — triggered manually (workflow_dispatch), used once when setting up a new kernel for the first time.
examples/kernel-update.yml — triggered automatically on push to main when kernel/** files change, used for ongoing kernel updates.
Implementation roadmap
The full action.yaml and script implementations are attached to this issue.
Alternatives Considered
Keep a single mode input with values like kernel-create / kernel-update — rejected. Combining two concepts into one string breaks the existing create/update mental model that users already know from the article workflow and makes the input harder to document.
Separate action.yaml file for kernel deployment — rejected. A second action would duplicate the shared setup steps (UV install, Python, secret validation, notifications) and require instructors to maintain two action references in their course repos.
Validate kernels at runtime in qBook rather than at publish time — rejected. Runtime checks mean a student discovers a missing kernel mid-session. Failing fast at publish time ensures instructors fix the problem before any student is affected.
Implicit action-type detection from which inputs are present — rejected. Explicit is better for CI configuration. action-type defaulting to article preserves full backward compatibility without guesswork.
Additional Context
kernel-display-name must exactly match kernelId in course.json and display_name in kernel.json on the EG host. This is the string qBook sends to EG when a student opens a notebook. The validate_kernel_availability.py step enforces this via the qBraid API before the step can succeed.
mode: update does not restart EG. Running kernel sessions hold a reference to the container started with the previous image. Only new sessions use the new image. This is intentional — it avoids interrupting active students during a kernel update.
Pre-pulling the image is handled inside the deploy scripts. docker pull runs on the EG host immediately after the kernelspec is written, so the first student kernel start after a deploy is never a cold pull from GCR.
The EG host VM needs roles/artifactregistry.reader on its attached GCP service account so it can docker pull from Artifact Registry without manual credential management.
See attached files for the full action.yaml implementation and new script source code.
Feature Description
Add a new
action-typeinput (article/kernel) toaction.yamlso thatmode: createandmode: updateapply independently to both article deployment and custom kernel deployment. Whenaction-type: kernelis set, the action builds a Docker image, pushes it to Google Artifact Registry (GCR), and registers it with Jupyter Enterprise Gateway (EG) — so instructors can ship a course-specific Python environment from the same action they already use to publish courses.action-type: article(default — existing behaviour unchanged):mode: create→ create a new course or blog postmode: update→ update an existing course or blog postaction-type: kernel(new):mode: create→ build image + push to GCR + register new kernelspec on EG host + validatemode: update→ rebuild image + push new tag + patch existing kernelspec + validateaction-typedefaults toarticleso every existing workflow continues working with zero changes.Motivation
Kernels that power qBook course notebooks are currently deployed manually to the EG host — there is no CI pipeline, no version history in GCR, and no way for an instructor to self-serve a custom environment for their course.
This creates three problems:
No per-course kernel isolation. All courses share the kernels that were manually deployed. An instructor who needs a specific version of
qiskitorpennylanecannot get a custom environment without server-level access.No kernel availability check at publish time. The course publish step does not verify that kernels referenced in
course.jsonare accessible to the instructor before the course goes live. Students discover a missing kernel mid-session, not at publish time.No
createvsupdatedistinction for kernels. Creating a kernel for the first time (new kernelspec directory + EG restart) is a meaningfully different operation from updating one (patchimage_namein existingkernel.json, no restart). There is currently no way to express this difference in the action.Proposed Solution
Input structure
The new
action-typeinput sits at the top level. Everything else flows from it:createvsupdatefor kernelsNew example workflows
Two new files added to
examples/alongside the existingworkflow.yml:examples/kernel-create.yml— triggered manually (workflow_dispatch), used once when setting up a new kernel for the first time.examples/kernel-update.yml— triggered automatically on push tomainwhenkernel/**files change, used for ongoing kernel updates.Implementation roadmap
action-typeinput + guard existing steps withif: inputs.action-type != 'kernel'validate_kernel_inputs.py+ Kernel Stage 1 stepbuild_push_kernel.py+ Kernel Stage 3register_kernelspec.py(create) +patch_kernelspec.py(update) + Kernel Stage 4a/4bvalidate_kernel_availability.py+smoke_test_kernel.py+ Kernel Stage 5/6emit-outputssteps + updateoutputs:block inaction.yamlexamples/kernel-create.ymlandexamples/kernel-update.ymlexamples/README.mdwith kernel mode quick start sectionThe full
action.yamland script implementations are attached to this issue.Alternatives Considered
Keep a single
modeinput with values likekernel-create/kernel-update— rejected. Combining two concepts into one string breaks the existingcreate/updatemental model that users already know from the article workflow and makes the input harder to document.Separate
action.yamlfile for kernel deployment — rejected. A second action would duplicate the shared setup steps (UV install, Python, secret validation, notifications) and require instructors to maintain two action references in their course repos.Validate kernels at runtime in qBook rather than at publish time — rejected. Runtime checks mean a student discovers a missing kernel mid-session. Failing fast at publish time ensures instructors fix the problem before any student is affected.
Implicit
action-typedetection from which inputs are present — rejected. Explicit is better for CI configuration.action-typedefaulting toarticlepreserves full backward compatibility without guesswork.Additional Context
kernel-display-namemust exactly matchkernelIdincourse.jsonanddisplay_nameinkernel.jsonon the EG host. This is the string qBook sends to EG when a student opens a notebook. Thevalidate_kernel_availability.pystep enforces this via the qBraid API before the step can succeed.mode: updatedoes not restart EG. Running kernel sessions hold a reference to the container started with the previous image. Only new sessions use the new image. This is intentional — it avoids interrupting active students during a kernel update.Pre-pulling the image is handled inside the deploy scripts.
docker pullruns on the EG host immediately after the kernelspec is written, so the first student kernel start after a deploy is never a cold pull from GCR.The EG host VM needs
roles/artifactregistry.readeron its attached GCP service account so it candocker pullfrom Artifact Registry without manual credential management.See attached files for the full
action.yamlimplementation and new script source code.