Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
fa24935
chore: auto-bake note keys and regenerate backend locales/en.json [sk…
github-actions[bot] Oct 20, 2026
20ef058
fix(telemetry): 修复 Windows 进程探测导致的写入器启动失败和进程误终止
lien0219 Oct 20, 2026
1ca29a6
Merge branch 'develop' of https://github.com/lien0219/openxflow into …
lien0219 Oct 20, 2026
c0053fd
Merge remote-tracking branch 'origin/main' into develop
lien0219 Oct 20, 2026
a81550b
fix(ci): repair PR checks and add OCI preview packaging
lien0219 Oct 20, 2026
1de5e5f
fix(ci): stabilize Python 3.10 tests and refresh generated assets
lien0219 Oct 21, 2026
eeae5a5
fix(ci): skip upstream nightly gate in forks
lien0219 Oct 21, 2026
5d78005
fix(ci): correct Trivy action version tag in release workflow
lien0219 Oct 21, 2026
7680679
fix(ci): pin resolvable Trivy action for PR and release scans
lien0219 Oct 22, 2026
48afb90
test(ci): guard both Trivy scan steps against stale actions
lien0219 Oct 22, 2026
d30ffa7
style(ci): apply Ruff formatting to Trivy release test
lien0219 Oct 22, 2026
8e06322
Merge pull request #55 from lien0219/fix/openxflow-trivy-action
lien0219 Oct 23, 2026
819a65e
fix(ci): convert OCI archives to per-arch Docker scan inputs for Trivy
lien0219 Oct 23, 2026
7af42a7
test(ci): require both OCI image architectures to pass Trivy
lien0219 Oct 23, 2026
97af6f6
fix(ci): align Trivy scan policy checks with per-arch scan steps
lien0219 Oct 23, 2026
1584f78
Merge pull request #56 from lien0219/fix/openxflow-trivy-oci-scanning
lien0219 Oct 23, 2026
9b211cc
fix(security): raise Pillow Python 3.11+ vulnerability floors
lien0219 Oct 25, 2026
757b0cd
fix(security): raise runtime dependency vulnerability floors
lien0219 Oct 25, 2026
214d09b
fix(ci): retain machine-readable Trivy evidence even when security ga…
lien0219 Oct 25, 2026
20f8e70
test(ci): assert Trivy evidence persists and security scan stays bloc…
lien0219 Oct 25, 2026
e21b544
fix(security): align lfx Pillow constraints with backend across Pytho…
lien0219 Oct 25, 2026
6c1c731
fix(security): harden production image dependencies
lien0219 Oct 25, 2026
5049e51
chore(security): record amd64 image manifest
lien0219 Oct 25, 2026
8d56476
fix(ci): synchronize affected bundle releases and component index
lien0219 Oct 25, 2026
72a8726
fix(security): scope IBM SDK Trivy false positive with expiry and reg…
lien0219 Oct 25, 2026
e69548a
fix(ci): use timezone-aware UTC date in Trivy expiry regression test
lien0219 Oct 25, 2026
d13cde9
test(ci): make listener backoff regression resilient to async persist…
lien0219 Oct 25, 2026
4bcd56a
Merge pull request #57 from lien0219/fix/openxflow-image-security
lien0219 Oct 26, 2026
9893a32
fix(security): narrowly cover IBM SDK compiled sample in Trivy secret…
lien0219 Oct 26, 2026
20069fd
fix(ci): validate Buildx OCI layout indirection and use reviewed Triv…
lien0219 Oct 26, 2026
77a1c92
fix(ci): expose HTTP port on all runtime image targets and apply Ruff…
lien0219 Oct 26, 2026
b81d908
fix(ci): supply ephemeral admin credentials for authenticated image s…
lien0219 Oct 26, 2026
0c2250f
fix(ci): make ARM64 smoke readiness diagnosable and Ruff compliant
lien0219 Oct 26, 2026
1fd269d
fix(ci): eliminate offline ARM64 smoke overhead and lint churn
lien0219 Oct 27, 2026
d54cdec
fix(ci): remove unused smoke_image import flagged by Ruff F401 and au…
lien0219 Oct 27, 2026
832ba3a
test(ci): stabilize trigger heartbeat ownership regression under para…
lien0219 Oct 27, 2026
e15ede5
test(ci): isolate ingestion lock exception regression from shared SQLite
lien0219 Oct 27, 2026
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
30 changes: 25 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,20 @@ jobs:
runs-on: ubuntu-latest
outputs:
should-proceed: ${{ steps.check-pypi.outputs.success }}
status: ${{ steps.check-pypi.outputs.status }}
steps:
- name: Check PyPI package update
id: check-pypi
run: |
# Nightly publishing only runs in the canonical Langflow repository. Do not
# block fork PRs on the global PyPI package's daily upload schedule.
if [[ "${{ github.repository }}" != "langflow-ai/langflow" ]]; then
echo "Nightly PyPI check is not applicable to ${{ github.repository }}"
echo "success=true" >> $GITHUB_OUTPUT
echo "status=not-applicable" >> $GITHUB_OUTPUT
exit 0
fi

# Get today's date in ISO format for comparison
TODAY=$(date -u +"%Y-%m-%d")
echo "Today's date: $TODAY"
Expand All @@ -118,6 +128,7 @@ jobs:
if [ "$HTTP_STATUS" -ne 200 ]; then
echo "Error: PyPI API returned HTTP status $HTTP_STATUS"
echo "success=false" >> $GITHUB_OUTPUT
echo "status=unavailable" >> $GITHUB_OUTPUT
exit 0
fi

Expand All @@ -127,6 +138,7 @@ jobs:
echo "Response preview:"
head -n 10 response.json
echo "success=false" >> $GITHUB_OUTPUT
echo "status=unavailable" >> $GITHUB_OUTPUT
exit 0
fi

Expand All @@ -136,6 +148,7 @@ jobs:
if [ -z "$LATEST_VERSION" ]; then
echo "Could not find a nightly (.devN) release"
echo "success=false" >> $GITHUB_OUTPUT
echo "status=unavailable" >> $GITHUB_OUTPUT
exit 0
fi

Expand All @@ -145,6 +158,7 @@ jobs:
if [ -z "$RELEASE_DATE" ]; then
echo "Could not extract release date"
echo "success=false" >> $GITHUB_OUTPUT
echo "status=unavailable" >> $GITHUB_OUTPUT
exit 0
fi

Expand All @@ -155,9 +169,11 @@ jobs:
if [[ "$RELEASE_DATE" == "$TODAY" ]]; then
echo "Package was updated today"
echo "success=true" >> $GITHUB_OUTPUT
echo "status=healthy" >> $GITHUB_OUTPUT
else
echo "Package was not updated today"
echo "success=false" >> $GITHUB_OUTPUT
echo "status=stale" >> $GITHUB_OUTPUT
fi

# Clean up
Expand Down Expand Up @@ -394,7 +410,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v7
with:
ref: ${{ inputs.ref || github.ref }}
ref: ${{ inputs.ref || github.event.pull_request.head.sha || github.sha }}

# SeaweedFS's S3 gateway: MinIO no longer publishes pullable images. A service
# container cannot pass it the "server" command, so start it here. With no S3
Expand Down Expand Up @@ -557,7 +573,7 @@ jobs:
echo "=== CI Status Summary ==="
echo "Should run tests: ${{ needs.set-ci-condition.outputs.should-run-tests }}"
echo "Should run CI: ${{ needs.set-ci-condition.outputs.should-run-ci }}"
echo "Nightly build status: ${{ needs.check-nightly-status.outputs.should-proceed }}"
echo "Nightly build status: ${{ needs.check-nightly-status.outputs.status }}"
echo "Event type: ${{ github.event_name }}"
echo "Docs only changes: $DOCS_ONLY"
echo "Path filter decision: ${{ needs.path-filter.result }}"
Expand Down Expand Up @@ -620,9 +636,9 @@ jobs:
echo " 2. Re-run the workflow if needed"

elif [[ "${{ needs.check-nightly-status.outputs.should-proceed }}" != "true" && "${{ github.event_name }}" != "workflow_dispatch" && "$DOCS_ONLY" != "true" ]]; then
echo "🚫 CI BLOCKED: Nightly build is broken"
echo "🚫 CI BLOCKED: Nightly package is stale or unavailable"
echo ""
echo "The nightly PyPI package was not updated today, indicating the nightly build failed."
echo "The latest nightly PyPI package was not updated today or its status could not be verified."
echo ""
echo "🔧 Next steps:"
echo " 1. Work with the team to investigate and fix the nightly build"
Expand All @@ -637,7 +653,11 @@ jobs:
echo "🎉 Your changes are ready:"
echo " - All tests passed"
echo " - Code quality checks passed"
echo " - Nightly build is healthy"
if [[ "${{ needs.check-nightly-status.outputs.status }}" == "not-applicable" ]]; then
echo " - Nightly status check is not applicable to this repository"
else
echo " - Nightly build is healthy"
fi
fi

echo ""
Expand Down
216 changes: 209 additions & 7 deletions .github/workflows/openxflow-production-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: OpenXFlow Production Release

on:
pull_request:
types: [closed]
types: [opened, synchronize, reopened, closed]
branches: [main]

permissions:
Expand All @@ -13,13 +13,167 @@ env:
IMAGE_NAME: lien0219/openxflow
PRODUCTION_REPOSITORY: lien0219/openxflow

# Every merged PR gets its own run. Only the floating main promotion is
# serialized, so a slow build cannot discard an immutable source image.
# PR updates produce a verified artifact without registry publication. Every
# merged PR gets its own release run; only floating main promotion is serialized.
concurrency:
group: openxflow-production-${{ github.event.pull_request.merge_commit_sha || github.run_id }}
cancel-in-progress: false

jobs:
preview-package:
name: Build and verify PR package
if: >-
github.repository == 'lien0219/openxflow' &&
github.event_name == 'pull_request' &&
github.event.action != 'closed' &&
github.event.pull_request.base.ref == 'main' &&
github.event.pull_request.base.repo.full_name == 'lien0219/openxflow'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Check out PR head
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 1
persist-credentials: false

- name: Set up emulation and Buildx
uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3

- name: Materialize the PR image verification plan
id: preview-plan
shell: bash
run: |
set -Eeuo pipefail
source="$(git rev-parse HEAD)"
version="$(cat deploy/IMAGE_VERSION)"
created="$(git show -s --format=%cI "$source")"
python - "$source" "$version" "$created" > /tmp/openxflow-pr-plan.json <<'PY'
import json
import sys
print(json.dumps({"source": sys.argv[1], "version": sys.argv[2], "created": sys.argv[3]}))
PY
{
echo "source=$source"
echo "version=$version"
echo "created=$created"
} >> "$GITHUB_OUTPUT"

- name: Build the PR OCI package
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/build_and_push.Dockerfile
target: full
platforms: linux/amd64,linux/arm64
push: false
outputs: type=oci,dest=/tmp/openxflow-pr.oci.tar
build-args: |
OPENXFLOW_VERSION=${{ steps.preview-plan.outputs.version }}
OPENXFLOW_REVISION=${{ steps.preview-plan.outputs.source }}
OPENXFLOW_CREATED=${{ steps.preview-plan.outputs.created }}
OPENXFLOW_SOURCE=https://github.com/lien0219/openxflow
OPENXFLOW_URL=https://github.com/lien0219/openxflow
labels: |
org.opencontainers.image.title=OpenXFlow
org.opencontainers.image.description=OpenXFlow workflow automation platform
org.opencontainers.image.version=${{ steps.preview-plan.outputs.version }}
org.opencontainers.image.revision=${{ steps.preview-plan.outputs.source }}
org.opencontainers.image.created=${{ steps.preview-plan.outputs.created }}
org.opencontainers.image.source=https://github.com/lien0219/openxflow
org.opencontainers.image.url=https://github.com/lien0219/openxflow
cache-from: type=gha,scope=openxflow-pr-${{ github.event.pull_request.number }}
cache-to: type=gha,mode=max,scope=openxflow-pr-${{ github.event.pull_request.number }}
provenance: mode=max
sbom: true

# Trivy's --input accepts Docker save tarballs or an unpacked OCI layout,
# not a Buildx type=oci tar archive. Convert each platform separately so
# both architectures receive the same blocking vulnerability/secret scan.
- name: Prepare per-architecture Trivy scan inputs (PR)
shell: bash
run: |
set -Eeuo pipefail
sudo apt-get update -qq
sudo apt-get install -y -qq skopeo jq
for arch in amd64 arm64; do
output="/tmp/openxflow-pr-${arch}.docker.tar"
skopeo copy --override-os linux --override-arch "$arch" \
"oci-archive:/tmp/openxflow-pr.oci.tar" \
"docker-archive:${output}:openxflow-scan:${arch}"
actual="$(skopeo inspect "docker-archive:${output}" | jq -r '.Architecture // empty')"
if [ "$actual" != "$arch" ]; then
echo "::error::Expected $arch scan input, got ${actual:-missing}"
exit 1
fi
done

- name: Prepare Trivy evidence folder (PR)
shell: bash
run: mkdir -p /tmp/openxflow-trivy

- name: Scan the unpushed PR amd64 image
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
input: /tmp/openxflow-pr-amd64.docker.tar
format: json
output: /tmp/openxflow-trivy/pr-amd64.json
exit-code: "1"
ignore-unfixed: "false"
severity: CRITICAL,HIGH
scanners: vuln,secret
trivyignores: .trivyignore.yaml

- name: Scan the unpushed PR arm64 image
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
input: /tmp/openxflow-pr-arm64.docker.tar
format: json
output: /tmp/openxflow-trivy/pr-arm64.json
exit-code: "1"
ignore-unfixed: "false"
severity: CRITICAL,HIGH
scanners: vuln,secret
trivyignores: .trivyignore.yaml

- name: Verify PR package attestations and runtime probes
shell: bash
run: |
set -Eeuo pipefail
python scripts/ci/openxflow_verify_image.py \
--archive /tmp/openxflow-pr.oci.tar \
--plan /tmp/openxflow-pr-plan.json \
--output-dir /tmp/openxflow-pr-image-verification
Comment on lines +146 to +149

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Apply the SDK secret exception to the final verification scan

For the full image containing ibm_watsonx_ai/connections.py, the new preview job can pass both Trivy action steps but still fail here on the same SDK false positive: openxflow_verify_image.py runs its own secret scan with --ignorefile /dev/null, then rejects every reported finding. Consequently, the .trivyignore.yaml exception added in this commit never reaches this final gate, preventing upload of the verified PR package; the production verification invocation has the same mismatch. Pass the narrowly scoped exception into the verifier too. Trivy's documentation confirms that YAML exceptions require explicitly specifying their path with --ignorefile.

Useful? React with 👍 / 👎.


- name: Retain failed and successful Trivy PR reports
if: always()
uses: actions/upload-artifact@v4
with:
name: openxflow-pr-trivy-${{ github.event.pull_request.number }}-${{ steps.preview-plan.outputs.source }}
path: /tmp/openxflow-trivy/
if-no-files-found: ignore
retention-days: 14

- name: Retain PR image verification evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: openxflow-pr-image-verification-${{ github.event.pull_request.number }}-${{ steps.preview-plan.outputs.source }}
path: /tmp/openxflow-pr-image-verification
if-no-files-found: ignore
retention-days: 7

- name: Upload verified PR OCI package
uses: actions/upload-artifact@v4
with:
name: openxflow-pr-oci-${{ github.event.pull_request.number }}-${{ steps.preview-plan.outputs.source }}
path: /tmp/openxflow-pr.oci.tar
if-no-files-found: error
retention-days: 7

validate-merge:
name: Validate exact merged main source
if: >-
Expand Down Expand Up @@ -297,15 +451,63 @@ jobs:
'import importlib.util; assert importlib.util.find_spec("langflow"); assert importlib.util.find_spec("pgvector")'
docker run --rm --entrypoint langflow openxflow-runtime-probe:local --help >/dev/null

- name: Scan the unpushed OCI archive (or recovered retry)
uses: aquasecurity/trivy-action@0.28.0
# Trivy's --input accepts Docker save tarballs or an unpacked OCI layout,
# not a Buildx type=oci tar archive. Convert each platform separately so
# both architectures receive the same blocking vulnerability/secret scan.
- name: Prepare per-architecture Trivy scan inputs (production)
shell: bash
run: |
set -Eeuo pipefail
sudo apt-get update -qq
sudo apt-get install -y -qq skopeo jq
for arch in amd64 arm64; do
output="/tmp/openxflow-${arch}.docker.tar"
skopeo copy --override-os linux --override-arch "$arch" \
"oci-archive:/tmp/openxflow.oci.tar" \
"docker-archive:${output}:openxflow-scan:${arch}"
actual="$(skopeo inspect "docker-archive:${output}" | jq -r '.Architecture // empty')"
if [ "$actual" != "$arch" ]; then
echo "::error::Expected $arch scan input, got ${actual:-missing}"
exit 1
fi
done

- name: Prepare Trivy evidence folder (production)
shell: bash
run: mkdir -p /tmp/openxflow-trivy

- name: Scan the unpushed production amd64 image
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
input: /tmp/openxflow.oci.tar
format: table
input: /tmp/openxflow-amd64.docker.tar
format: json
output: /tmp/openxflow-trivy/production-amd64.json
exit-code: "1"
ignore-unfixed: "false"
severity: CRITICAL,HIGH
scanners: vuln,secret
trivyignores: .trivyignore.yaml

- name: Scan the unpushed production arm64 image
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
input: /tmp/openxflow-arm64.docker.tar
format: json
output: /tmp/openxflow-trivy/production-arm64.json
exit-code: "1"
ignore-unfixed: "false"
severity: CRITICAL,HIGH
scanners: vuln,secret
trivyignores: .trivyignore.yaml

- name: Retain production Trivy reports
if: always()
uses: actions/upload-artifact@v4
with:
name: openxflow-production-trivy-${{ github.run_id }}-${{ github.run_attempt }}
path: /tmp/openxflow-trivy/
if-no-files-found: ignore
retention-days: 14

- name: Verify OCI attestations, both architectures, and runtime probes
shell: bash
Expand Down
12 changes: 12 additions & 0 deletions .trivyignore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Exception for a third-party SDK's literal property-name example, not a credential.
# Review with IBM Watson SDK upgrades; expires automatically.
secrets:
- id: gcp-service-account
paths:
- "**/site-packages/ibm_watsonx_ai/connections.py"
- "**/site-packages/ibm_watsonx_ai/__pycache__/connections.cpython-314.pyc"
expired_at: 2026-12-31
statement: >-
IBM Watson SDK example property mapping includes the literal identifier
gcp_service_account; no service-account JSON private key is embedded.
Only this rule in this SDK source file and its deterministic CPython 3.14\n compiled bytecode are excluded; all other findings still block.
Loading
Loading