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
6 changes: 4 additions & 2 deletions .github/workflows/docker-build-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,8 @@ jobs:
build-args: |
MAIN_VERSION=${{ needs.determine-main-version.outputs.version }}
CORE_VERSION=${{ needs.determine-main-version.outputs.version }}
file: ./docker/build_and_push_with_extras.Dockerfile
file: ./docker/build_and_push.Dockerfile
target: full-bundles
tags: ${{ steps.tags.outputs.docker_tags }}
platforms: linux/${{ matrix.arch }}
provenance: false
Expand All @@ -810,7 +811,8 @@ jobs:
build-args: |
MAIN_VERSION=${{ needs.determine-main-version.outputs.version }}
CORE_VERSION=${{ needs.determine-main-version.outputs.version }}
file: ./docker/build_and_push_with_extras.Dockerfile
file: ./docker/build_and_push.Dockerfile
target: full-bundles
tags: ${{ steps.tags.outputs.ghcr_tags }}
platforms: linux/${{ matrix.arch }}
provenance: false
Expand Down
283 changes: 283 additions & 0 deletions .github/workflows/release-inventory-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
name: Release inventory gate

on:
workflow_call:
inputs:
ref:
description: Git ref containing the release contract and Dockerfiles
required: true
type: string
main-version:
description: Version to embed in application images
required: true
type: string
core-version:
description: Version to embed in core application images
required: true
type: string
sdk-artifact-name:
required: false
type: string
default: ""
lfx-artifact-name:
required: false
type: string
default: ""
base-artifact-name:
required: false
type: string
default: ""
core-artifact-name:
required: false
type: string
default: ""
main-artifact-name:
required: false
type: string
default: ""
bundles-artifact-name:
required: false
type: string
default: ""

env:
DOCKER_BUILDKIT: "1"

jobs:
python-inventory:
name: Python ${{ matrix.python-version }} / ${{ matrix.profile }}
if: ${{ inputs.main-artifact-name != '' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
profile: [python-default, python-full]
steps:
- name: Check out release contract
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}
Comment on lines +57 to +60

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Set persist-credentials: false on both checkouts. Both actions/checkout steps persist the GITHUB_TOKEN in .git/config, and later steps run downloaded wheels and docker build, so the token is reachable by executed code; disable persistence since neither step pushes.

  • .github/workflows/release-inventory-gate.yml#L57-L60: add persist-credentials: false under the with: of the python-inventory checkout.
  • .github/workflows/release-inventory-gate.yml#L196-L199: add persist-credentials: false under the with: of the image-inventory checkout.
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 57-60: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

📍 Affects 1 file
  • .github/workflows/release-inventory-gate.yml#L57-L60 (this comment)
  • .github/workflows/release-inventory-gate.yml#L196-L199
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-inventory-gate.yml around lines 57 - 60, Set
persist-credentials to false in the with configuration of both actions/checkout
steps: the python-inventory checkout at
.github/workflows/release-inventory-gate.yml lines 57-60 and the image-inventory
checkout at lines 196-199. Neither checkout pushes changes, so disable token
persistence for both sites.

Source: Linters/SAST tools


- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: false

- name: Download SDK wheel
if: ${{ inputs.sdk-artifact-name != '' }}
uses: actions/download-artifact@v7
with:
name: ${{ inputs.sdk-artifact-name }}
path: sdk-dist

- name: Download LFX wheel
if: ${{ inputs.lfx-artifact-name != '' }}
uses: actions/download-artifact@v7
with:
name: ${{ inputs.lfx-artifact-name }}
path: lfx-dist

- name: Download base wheel
if: ${{ inputs.base-artifact-name != '' }}
uses: actions/download-artifact@v7
with:
name: ${{ inputs.base-artifact-name }}
path: base-dist

- name: Download core wheel
if: ${{ inputs.core-artifact-name != '' }}
uses: actions/download-artifact@v7
with:
name: ${{ inputs.core-artifact-name }}
path: core-dist

- name: Download main wheel
if: ${{ inputs.main-artifact-name != '' }}
uses: actions/download-artifact@v7
with:
name: ${{ inputs.main-artifact-name }}
path: main-dist

- name: Download bundle wheels
if: ${{ inputs.bundles-artifact-name != '' }}
uses: actions/download-artifact@v7
with:
name: ${{ inputs.bundles-artifact-name }}
path: bundles-dist

- name: Install release shape from wheels
run: |
set -euo pipefail
shopt -s nullglob
uv venv inventory-env --seed --python "${{ matrix.python-version }}"

MAIN_WHEELS=(main-dist/langflow-*.whl)
[ ${#MAIN_WHEELS[@]} -eq 1 ]

LOCAL_WHEELS=()
for wheel_dir in sdk-dist lfx-dist base-dist core-dist; do
if [ -d "$wheel_dir" ]; then
while IFS= read -r wheel; do
LOCAL_WHEELS+=("$wheel")
done < <(find "$wheel_dir" -maxdepth 1 -name '*.whl' -type f | sort)
fi
done
mkdir -p bundles-dist

MAIN_TARGET="${MAIN_WHEELS[0]}"
if [ "${{ matrix.profile }}" = "python-full" ]; then
MAIN_TARGET="langflow[bundles] @ file://${PWD}/${MAIN_WHEELS[0]}"
fi

INSTALL_TARGETS=(
"${LOCAL_WHEELS[@]}"
"$MAIN_TARGET"
)

uv pip install --python inventory-env/bin/python \
--prerelease=if-necessary-or-explicit \
--find-links bundles-dist \
"${INSTALL_TARGETS[@]}"
uv pip check --python inventory-env/bin/python

- name: Validate distribution and component inventory
run: |
inventory-env/bin/python scripts/ci/check_release_inventory.py \
--contract scripts/ci/release_inventory_contract.json \
--profile "${{ matrix.profile }}" \
--output "inventory-reports/${{ matrix.profile }}-py${{ matrix.python-version }}.json"

- name: Verify application startup and health
timeout-minutes: 5
run: |
set -euo pipefail
inventory-env/bin/python -m langflow run --host 127.0.0.1 --port 7860 --backend-only &
server_pid=$!
trap 'kill "$server_pid" >/dev/null 2>&1 || true' EXIT
for _ in $(seq 1 60); do
if curl -fsS http://127.0.0.1:7860/health_check >/dev/null; then
exit 0
fi
if ! kill -0 "$server_pid" 2>/dev/null; then
echo "Langflow exited before becoming healthy"
exit 1
fi
sleep 2
done
echo "Langflow did not become healthy"
exit 1

- name: Upload Python inventory report
if: always()
uses: actions/upload-artifact@v6
with:
name: inventory-${{ matrix.profile }}-py${{ matrix.python-version }}
path: inventory-reports/*.json
if-no-files-found: warn

image-inventory:
name: Images / ${{ matrix.arch }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: Langflow-runner
- arch: arm64
runner: [self-hosted, linux, ARM64, langflow-ai-arm64-40gb-ephemeral]
runs-on: ${{ matrix.runner }}
steps:
- name: Check out release contract
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}

- name: Download bundle wheels for downstream-image test
if: ${{ inputs.bundles-artifact-name != '' }}
uses: actions/download-artifact@v7
with:
name: ${{ inputs.bundles-artifact-name }}
path: bundles-dist

- name: Prepare optional bundle-wheel directory
run: mkdir -p bundles-dist

- name: Build core, default, and full application images
run: |
set -euo pipefail
common_args=(
--build-arg "MAIN_VERSION=${{ inputs.main-version }}"
--build-arg "CORE_VERSION=${{ inputs.core-version }}"
-f docker/build_and_push.Dockerfile
)
docker build "${common_args[@]}" --target core -t "langflow-inventory:core-${{ matrix.arch }}" .
docker build "${common_args[@]}" --target full -t "langflow-inventory:default-${{ matrix.arch }}" .
docker build "${common_args[@]}" --target full-bundles -t "langflow-inventory:full-${{ matrix.arch }}" .
Comment on lines +212 to +221

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the workflow and inspect the relevant section with line numbers.
file=".github/workflows/release-inventory-gate.yml"
wc -l "$file"
sed -n '180,240p' "$file" | cat -n

Repository: langflow-ai/langflow

Length of output: 2985


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find how these inputs are defined and used elsewhere in the workflow.
rg -n "main-version|core-version|persist-credentials|docker build|build-arg" .github/workflows/release-inventory-gate.yml

Repository: langflow-ai/langflow

Length of output: 803


Avoid interpolating workflow inputs directly in run.

${{ inputs.main-version }} and ${{ inputs.core-version }} are expanded into the script before the shell runs, so pass them through env: and reference shell variables instead.

🧰 Tools
🪛 zizmor (1.26.1)

[error] 215-215: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[error] 216-216: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-inventory-gate.yml around lines 212 - 221, Update
the workflow step’s docker build arguments to receive the main and core versions
through an env block, then reference the corresponding shell variables in
common_args instead of interpolating inputs directly in run. Preserve the
existing build targets and image tags.

Source: Linters/SAST tools


- name: Validate image inventory
run: |
set -euo pipefail
mkdir -p inventory-reports
for profile in core default full; do
docker run --rm --entrypoint python \
-v "$PWD/scripts/ci:/inventory:ro" \
-v "$PWD/inventory-reports:/reports" \
"langflow-inventory:${profile}-${{ matrix.arch }}" \
/inventory/check_release_inventory.py \
--contract /inventory/release_inventory_contract.json \
--profile "image-${profile}" \
--output "/reports/image-${profile}-${{ matrix.arch }}.json"
done

- name: Verify image startup and health
timeout-minutes: 15
run: |
set -euo pipefail
credential="inventory-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${RANDOM}"
for profile in core default full; do
image="langflow-inventory:${profile}-${{ matrix.arch }}"
container_id=$(docker run -d -e "LANGFLOW_SUPERUSER_PASSWORD=$credential" "$image")
healthy=false
for _ in $(seq 1 60); do
if docker exec "$container_id" curl -fsS http://127.0.0.1:7860/health_check >/dev/null; then
healthy=true
break
fi
if [ "$(docker inspect -f '{{.State.Running}}' "$container_id" 2>/dev/null)" != "true" ]; then
break
fi
sleep 2
done
if [ "$healthy" != "true" ]; then
docker logs "$container_id" || true
docker rm -f "$container_id" || true
echo "$profile image did not become healthy"
exit 1
fi
docker rm -f "$container_id"
done

- name: Verify downstream extension of the core image
run: |
set -euo pipefail
docker build \
--build-arg "CORE_IMAGE=langflow-inventory:core-${{ matrix.arch }}" \
-f scripts/ci/core_extension.Dockerfile \
-t "langflow-inventory:core-extended-${{ matrix.arch }}" .
docker run --rm --entrypoint python \
"langflow-inventory:core-extended-${{ matrix.arch }}" \
-c 'from importlib.metadata import version; print(version("lfx-openai"))'

- name: Upload image inventory reports
if: always()
uses: actions/upload-artifact@v6
with:
name: inventory-images-${{ matrix.arch }}
path: inventory-reports/*.json
if-no-files-found: warn
Loading
Loading