diff --git a/.github/workflows/publish-dockers.yaml b/.github/workflows/publish-dockers.yaml index 7ec3f2c..a5fd387 100644 --- a/.github/workflows/publish-dockers.yaml +++ b/.github/workflows/publish-dockers.yaml @@ -11,6 +11,11 @@ on: description: 'Optional comma-separated distributions to publish' required: false type: string + include_ui_image: + description: 'Set to false to skip publishing the UI Docker image' + required: false + default: true + type: boolean jobs: publish-cpu-distros: @@ -88,5 +93,6 @@ jobs: with: version: ${{ inputs.version }} distros: ${{ inputs.distros }} + include_ui_image: ${{ inputs.include_ui_image }} dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }} dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index ac592ef..a4cc460 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -10,6 +10,20 @@ release-0.1.x ← v0.1.0rc1, v0.1.0rc2, v0.1.0, v0.1.1rc1, v0.1.1, ... release-0.2.x ← v0.2.0rc1, v0.2.0, ... ``` +## Packages + +The release process handles multiple packages across different repositories: + +- **Python packages** (published to PyPI/test.pypi): + - `llama-stack-client` (from `llama-stack-client-python` repo) + - `llama-stack` (from `llama-stack` repo) + +- **npm packages** (published to npmjs.org): + - `llama-stack-client` (from `llama-stack-client-typescript` repo) + - `llama-stack-ui` (from `llama-stack` repo at `src/llama_stack_ui/`) + +All packages are versioned together and released synchronously. Note that the UI package is built and published as part of the `llama-stack` repository release process. + ## Workflows **Cut Release Candidate** (`cut-release-candidate.yaml`) diff --git a/actions/publish-dockers/action.yaml b/actions/publish-dockers/action.yaml index 9695916..2bcfbce 100644 --- a/actions/publish-dockers/action.yaml +++ b/actions/publish-dockers/action.yaml @@ -9,6 +9,11 @@ inputs: description: 'Optional comma-separated distributions to publish' required: false type: string + include_ui_image: + description: 'Set to false to skip publishing the llamastack/ui image' + required: false + default: 'true' + type: string dockerhub_username: description: 'DockerHub username' required: true @@ -39,6 +44,7 @@ runs: env: VERSION: ${{ inputs.version }} DISTROS: ${{ inputs.distros }} + INCLUDE_UI_IMAGE: ${{ inputs.include_ui_image }} run: | chmod +x ${{ github.action_path }}/main.sh ${{ github.action_path }}/main.sh diff --git a/actions/publish-dockers/main.sh b/actions/publish-dockers/main.sh index a33ebfa..6192840 100755 --- a/actions/publish-dockers/main.sh +++ b/actions/publish-dockers/main.sh @@ -5,9 +5,18 @@ if [ -z "$VERSION" ]; then exit 1 fi DISTROS=${DISTROS:-} +INCLUDE_UI_IMAGE=${INCLUDE_UI_IMAGE:-true} set -euo pipefail +is_truthy() { + case "$1" in + true | 1 | yes | on) return 0 ;; + false | 0 | no | off) return 1 ;; + *) return 1 ;; + esac +} + release_exists() { local source=$1 releases=$(curl -s https://${source}.org/pypi/llama-stack/json | jq -r '.releases | keys[]') @@ -110,6 +119,65 @@ EOF fi } +build_and_push_ui_docker_image() { + local version=$1 + + if ! command -v docker &>/dev/null; then + echo "docker CLI is required to publish llamastack/ui" >&2 + exit 1 + fi + + local tag_suffix + if [ "$PYPI_SOURCE" = "testpypi" ]; then + tag_suffix="test-${version}" + else + tag_suffix="${version}" + fi + + ( + set -euo pipefail + local tmp_dir + tmp_dir=$(mktemp -d) + trap 'rm -rf "$tmp_dir"' EXIT + + cat > "$tmp_dir/Containerfile" <<'EOF' +ARG UI_VERSION +FROM node:22.5.1-alpine + +ENV NODE_ENV=production + +# Install dumb-init for proper signal handling +RUN apk add --no-cache dumb-init + +# Create non-root user for security +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Install the UI package from npm +RUN npm install -g "llama-stack-ui@${UI_VERSION}" + +USER nextjs + +ENTRYPOINT ["dumb-init", "--"] +CMD ["llama-stack-ui"] +EOF + + docker build \ + --build-arg UI_VERSION="$version" \ + -t "llamastack/ui:${tag_suffix}" \ + -f "$tmp_dir/Containerfile" \ + "$tmp_dir" + ) + + if [ "$PYPI_SOURCE" = "testpypi" ]; then + docker push "llamastack/ui:${tag_suffix}" + else + docker tag "llamastack/ui:${tag_suffix}" "llamastack/ui:latest" + docker push "llamastack/ui:${tag_suffix}" + docker push "llamastack/ui:latest" + fi +} + if [ -z "$DISTROS" ]; then DISTROS=(starter meta-reference-gpu postgres-demo dell starter-gpu) else @@ -120,4 +188,10 @@ for distro in "${DISTROS[@]}"; do build_and_push_docker $distro done +if is_truthy "$INCLUDE_UI_IMAGE"; then + build_and_push_ui_docker_image "$VERSION" +else + echo "Skipping UI docker image publish (INCLUDE_UI_IMAGE=$INCLUDE_UI_IMAGE)" +fi + echo "Done" diff --git a/actions/release-final-package/main.sh b/actions/release-final-package/main.sh index dadb7f1..0e9bfa8 100755 --- a/actions/release-final-package/main.sh +++ b/actions/release-final-package/main.sh @@ -182,13 +182,10 @@ add_bump_version_commit() { perl -pi -e "s/llama-stack-client>=.*,/llama-stack-client>=$version\",/" pyproject.toml if [ "$repo" == "stack" ]; then - # Handle both old (llama_stack/ui) and new (src/llama_stack_ui) paths if [ -f "src/llama_stack_ui/package.json" ]; then UI_PATH="src/llama_stack_ui" - elif [ -f "llama_stack/ui/package.json" ]; then - UI_PATH="llama_stack/ui" else - echo "ERROR: Could not find llama_stack_ui/package.json" >&2 + echo "ERROR: Could not find src/llama_stack_ui/package.json" >&2 exit 1 fi @@ -203,6 +200,21 @@ add_bump_version_commit() { fi fi + # Update UI version for stack repo + if [ "$repo" == "stack" ]; then + if [ -f "src/llama_stack_ui/package.json" ]; then + UI_PATH="src/llama_stack_ui" + else + echo "ERROR: Could not find src/llama_stack_ui/package.json" >&2 + exit 1 + fi + + if [ -n "$UI_PATH" ]; then + perl -pi -e "s/\"version\": \".*\"/\"version\": \"$version\"/" "$UI_PATH/package.json" + (cd "$UI_PATH" && npm install && npm run build) + fi + fi + if is_truthy "$should_update_lockfiles"; then run_precommit_lockfile_update fi @@ -365,6 +377,21 @@ if ! is_truthy "$DRY_RUN"; then --skip-existing \ --non-interactive \ "dist/*.whl" "dist/*.tar.gz" + + if [ "$repo" == "stack" ] && [ -d "src/llama_stack_ui/dist" ]; then + echo "Uploading llama-stack-ui to npm" + cd src/llama_stack_ui/dist + + if npm view llama-stack-ui@$RELEASE_VERSION version &>/dev/null; then + echo "Version $RELEASE_VERSION already exists on npm for llama-stack-ui, skipping publish" + else + npx yarn publish --access public --tag $RELEASE_VERSION --registry https://registry.npmjs.org/ + fi + + npx yarn tag add llama-stack-ui@$RELEASE_VERSION latest || true + cd ../../.. + fi + cd .. done else diff --git a/actions/test-and-cut/main.sh b/actions/test-and-cut/main.sh index 0aedd6c..3e6acf1 100755 --- a/actions/test-and-cut/main.sh +++ b/actions/test-and-cut/main.sh @@ -175,27 +175,37 @@ build_packages() { fi fi - # TODO: this is dangerous use uvx toml-cli toml set project.version $VERSION instead of this - perl -pi -e "s/^version = .*$/version = \"$VERSION\"/" pyproject.toml + if [ "$repo" == "stack-client-typescript" ]; then + # TypeScript client package - update package.json version + perl -pi -e "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" package.json + npx yarn install + npx yarn build + else + # Python packages - update pyproject.toml version + # TODO: this is dangerous use uvx toml-cli toml set project.version $VERSION instead of this + perl -pi -e "s/^version = .*$/version = \"$VERSION\"/" pyproject.toml + + if ! is_truthy "$LLAMA_STACK_ONLY"; then + # this one is only applicable for llama-stack-client-python + if [ -f "src/llama_stack_client/_version.py" ]; then + perl -pi -e "s/__version__ = .*$/__version__ = \"$VERSION\"/" src/llama_stack_client/_version.py + fi - if ! is_truthy "$LLAMA_STACK_ONLY"; then - # this one is only applicable for llama-stack-client-python - if [ -f "src/llama_stack_client/_version.py" ]; then - perl -pi -e "s/__version__ = .*$/__version__ = \"$VERSION\"/" src/llama_stack_client/_version.py + # this is applicable for llama-stack repo but we should not do it when + # LLAMA_STACK_ONLY is true + perl -pi -e "s/llama-stack-client>=.*/llama-stack-client>=$VERSION\",/" pyproject.toml fi - if [ -f "package.json" ]; then + + # Build UI package + if [ "$repo" == "stack" ]; then + echo "Building llama-stack-ui npm package..." + cd src/llama_stack_ui perl -pi -e "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" package.json + npx yarn install + npx yarn build + cd ../.. fi - # this is applicable for llama-stack repo but we should not do it when - # LLAMA_STACK_ONLY is true - perl -pi -e "s/llama-stack-client>=.*/llama-stack-client>=$VERSION\",/" pyproject.toml - fi - - if [ "$repo" == "stack-client-typescript" ]; then - npx yarn install - npx yarn build - else uv build -q uv pip install dist/*.whl fi diff --git a/actions/upload-packages-and-tag/main.sh b/actions/upload-packages-and-tag/main.sh index cc71092..1c1662a 100755 --- a/actions/upload-packages-and-tag/main.sh +++ b/actions/upload-packages-and-tag/main.sh @@ -102,6 +102,17 @@ for repo in "${REPOS[@]}"; do PYPROJECT_VERSION=$(cat pyproject.toml | grep version) echo "version to build: $PYPROJECT_VERSION" + # Build UI package + if [ "$repo" == "stack" ]; then + if [ -d "src/llama_stack_ui" ]; then + echo "Building llama-stack-ui npm package..." + cd src/llama_stack_ui + npx yarn install + npx yarn build + cd ../.. + fi + fi + uv build -q uv pip install dist/*.whl fi @@ -121,6 +132,16 @@ for repo in "${REPOS[@]}"; do --repository-url https://test.pypi.org/legacy/ \ --skip-existing \ dist/*.whl dist/*.tar.gz + + # Publish UI npm package + if [ "$repo" == "stack" ]; then + if [ -d "src/llama_stack_ui/dist" ]; then + echo "Uploading llama-stack-ui to npm" + cd src/llama_stack_ui/dist + npx yarn publish --access public --tag rc-$VERSION --registry https://registry.npmjs.org/ + cd ../../.. + fi + fi fi cd ..