【ComfyUI】Qwen-Image-Edit-F2P 持续集成:使用GitHub Actions自动化测试工作流
ComfyUI Qwen-Image-Edit-F2P 持续集成:用GitHub Actions搭建自动化测试工作流
你是不是也遇到过这种情况?自己写了个ComfyUI的自定义节点,或者调整了一个复杂的工作流,每次改完代码,都得手动打开ComfyUI,加载节点,运行一遍测试流程,看看图片生成得对不对。一次两次还行,改得频繁了,或者团队里好几个人一起开发,这种手动测试就太折腾人了,还容易出错。
今天咱们就来聊聊,怎么给像Qwen-Image-Edit-F2P这样的ComfyUI项目,搭一个“自动化质检员”。这个质检员就是GitHub Actions。简单来说,就是每当你把代码推送到GitHub仓库,它就会自动帮你做这几件事:拉起一个干净的测试环境、部署好ComfyUI和你的节点、运行你预设好的测试任务、最后把生成的图片和你期望的结果对比一下,告诉你这次改动有没有“翻车”。
这招特别适合开源项目维护,或者团队协作开发,能大大提升代码更新的质量和信心。下面,我就手把手带你走一遍搭建流程。
1. 准备工作:理解我们要做什么
在动手写代码之前,咱们先得把整个流程想明白。我们的目标是实现一个持续集成(CI)流水线,具体到我们这个场景,流水线大概长这样:
- 触发:当你向GitHub仓库的特定分支(比如
main或dev)推送代码,或者发起一个拉取请求(Pull Request)时,自动触发工作流。 - 构建环境:GitHub Actions会启动一个全新的虚拟服务器(Runner),在上面安装好所有依赖,比如Python、ComfyUI、以及你的自定义节点。
- 运行测试:在这个环境中,启动ComfyUI服务,然后通过其API或者脚本,加载你预先设计好的、用于测试的工作流(
.json或.png文件),并执行图像生成任务。 - 验证结果:将生成的图片与事先保存好的“预期正确”的图片(通常叫
golden image或baseline)进行比对。比对可以是简单的像素对比,也可以是更复杂的感知哈希(pHash)对比,允许微小的差异。 - 反馈结果:根据比对结果,决定这个工作流运行是成功还是失败。结果会直接显示在GitHub的提交记录或Pull Request页面上,一目了然。
整个过程完全自动化,无需人工干预。接下来,我们就一步步实现它。
2. 在项目中配置GitHub Actions工作流
首先,你需要在你的Qwen-Image-Edit-F2P项目仓库中,创建GitHub Actions的配置文件。
在你的项目根目录下,创建如下文件夹和文件:
.github/ └── workflows/ └── ci-comfyui-test.yml这个
ci-comfyui-test.yml文件就是我们工作流的“剧本”。打开这个YAML文件,我们开始编写。下面是一个相对完整、可以直接修改使用的模板:
name: ComfyUI CI - Test Image Generation on: push: branches: [ main, dev ] pull_request: branches: [ main ] jobs: test-comfyui-workflow: runs-on: ubuntu-latest # 使用最新的Ubuntu系统作为运行环境 steps: # 1. 检出代码 - name: Checkout repository uses: actions/checkout@v4 # 2. 设置Python环境 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.10' # 根据ComfyUI要求指定Python版本 # 3. 安装系统依赖(例如,某些Python包可能需要系统库) - name: Install system dependencies run: | sudo apt-get update sudo apt-get install -y libgl1-mesa-glx libglib2.0-0 wget # 4. 安装Python依赖 - name: Install Python dependencies run: | pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu # 示例用CPU版本,可按需改CUDA pip install -r requirements.txt # 安装你项目自身的依赖 # 如果项目是ComfyUI自定义节点,可能需要安装ComfyUI本体 # 这里假设你的项目是ComfyUI的一个自定义节点仓库,需要克隆ComfyUI git clone https://github.com/comfyanonymous/ComfyUI.git cd ComfyUI pip install -r requirements.txt cd .. # 5. 部署自定义节点/工作流 - name: Deploy custom node/workflow run: | # 将你的自定义节点文件复制到ComfyUI的节点目录 # 假设你的节点代码在 `custom_nodes/` 目录下 cp -r custom_nodes/ ComfyUI/custom_nodes/ # 将你的测试工作流文件复制到指定位置 mkdir -p ComfyUI/test_workflows cp tests/test_workflow_api.json ComfyUI/test_workflows/ # 6. 下载模型(如果需要,这里以Qwen为例,实际需根据你的测试需求调整) - name: Download required models (Optional) run: | mkdir -p ComfyUI/models/checkpoints # 这里可以放置下载模型的命令,例如使用wget或huggingface-cli # 注意:模型可能很大,会显著增加CI时间。可以考虑使用小型测试模型,或将模型缓存。 # wget -O ComfyUI/models/checkpoints/qwen_test_model.safetensors https://example.com/model.safetensors continue-on-error: true # 模型下载可能失败或耗时,允许继续 # 7. 启动ComfyUI后台服务 - name: Start ComfyUI server run: | cd ComfyUI python main.py --listen 127.0.0.1 --port 8188 & sleep 30 # 等待服务启动完成 env: # 设置环境变量,例如禁用GPU使用CPU,避免CI环境无GPU报错 CUDA_VISIBLE_DEVICES: "" # 8. 运行测试脚本 - name: Run test workflow via API run: | cd ComfyUI python test_workflow_runner.py # 这是你需要自己编写的测试脚本 # 9. 验证输出结果 (示例:使用pytest进行图像比对) - name: Validate generated images run: | cd ComfyUI python -m pytest tests/ -v这个配置文件定义了一个名为test-comfyui-workflow的任务(job),它会在Ubuntu系统上按顺序执行一系列步骤(steps)。
关键步骤解释:
- 步骤4:我们克隆了官方的ComfyUI仓库并安装其依赖。如果你的项目本身就是完整的ComfyUI fork,这一步可以简化。
- 步骤5:这是关键,将你的自定义节点代码和测试工作流文件放到正确的位置。
- 步骤6:下载模型是可选的。在CI中下载大模型通常不现实,建议准备一个极小的、专门用于测试的模型文件,或者利用Git LFS存储在仓库内。
- 步骤7:在后台启动ComfyUI服务,并等待一段时间确保其完全启动。
CUDA_VISIBLE_DEVICES: “”是为了在无GPU的环境下强制使用CPU。 - 步骤8 & 9:执行测试脚本并验证结果。这里引用了两个你需要自己创建的文件:
test_workflow_runner.py和tests/目录下的Pytest测试用例。
3. 编写测试脚本与验证逻辑
现在我们来创建上面提到的两个核心文件。
3.1 测试工作流运行脚本 (test_workflow_runner.py)
这个脚本负责通过ComfyUI的API来触发工作流执行。ComfyUI提供了WebSocket和HTTP API两种方式,这里我们用更简单的HTTP API。
#!/usr/bin/env python3 import json import requests import time import sys import os def run_workflow(workflow_file, output_dir): """ 通过ComfyUI API运行指定的工作流文件 """ server_address = "http://127.0.0.1:8188" # 1. 加载工作流定义 with open(workflow_file, 'r', encoding='utf-8') as f: workflow_data = json.load(f) # API端点:执行工作流 prompt = workflow_data # 如果需要,可以在这里动态修改prompt中的某些节点参数,例如种子(seed) # prompt["6"]["inputs"]["seed"] = 123456 # 2. 提交任务 submit_url = f"{server_address}/prompt" print(f"Submitting workflow to {submit_url}") try: response = requests.post(submit_url, json={"prompt": prompt}) response.raise_for_status() data = response.json() prompt_id = data['prompt_id'] print(f"Prompt ID: {prompt_id}") except requests.exceptions.RequestException as e: print(f"Failed to submit prompt: {e}") sys.exit(1) # 3. 轮询查询任务历史,获取生成的图片信息 history_url = f"{server_address}/history" max_attempts = 60 # 最多等待60秒 for i in range(max_attempts): time.sleep(1) try: history_response = requests.get(history_url) history = history_response.json() if prompt_id in history: result = history[prompt_id] outputs = result.get('outputs', {}) for node_id, node_output in outputs.items(): images = node_output.get('images', []) for img_info in images: # 4. 下载生成的图片 filename = img_info['filename'] subfolder = img_info.get('subfolder', '') download_url = f"{server_address}/view?filename={filename}&subfolder={subfolder}&type=output" img_response = requests.get(download_url) # 确保输出目录存在 os.makedirs(output_dir, exist_ok=True) local_path = os.path.join(output_dir, filename) with open(local_path, 'wb') as f: f.write(img_response.content) print(f"Image saved to: {local_path}") return # 成功获取输出,退出函数 except requests.exceptions.RequestException: continue # 请求失败,继续重试 print(f"Error: Workflow execution timed out after {max_attempts} seconds.") sys.exit(1) if __name__ == "__main__": # 指定测试工作流文件和输出目录 workflow_path = "./test_workflows/test_workflow_api.json" output_path = "./test_output" if not os.path.exists(workflow_path): print(f"Error: Workflow file not found at {workflow_path}") sys.exit(1) run_workflow(workflow_path, output_path) print("Workflow execution completed.")这个脚本做了几件事:读取工作流JSON文件、通过API提交任务、不断检查任务是否完成、最后下载生成的图片到本地目录。
3.2 图像比对测试用例 (tests/test_image_diff.py)
接下来,我们需要验证生成的图片是否正确。我们可以使用pytest框架和Pillow、imagehash库来编写测试。
首先,确保你的requirements.txt包含测试依赖:
pytest Pillow imagehash然后创建测试文件:
import os import pytest from PIL import Image import imagehash import sys def test_image_generation_baseline(): """对比生成的图片与基线图片是否相似""" # 配置路径 generated_img_dir = "../test_output" # 脚本生成的图片目录 baseline_img_dir = "./baseline_images" # 预先保存的正确结果目录 # 获取生成的图片列表 generated_images = [f for f in os.listdir(generated_img_dir) if f.lower().endswith(('png', 'jpg', 'jpeg'))] if not generated_images: pytest.fail("No generated images found for comparison.") for img_name in generated_images: gen_path = os.path.join(generated_img_dir, img_name) baseline_path = os.path.join(baseline_img_dir, img_name) # 检查基线图片是否存在 if not os.path.exists(baseline_path): # 如果是第一次运行,可以将生成的图片复制为基线 # 这里我们直接报错,提醒需要建立基线 pytest.fail(f"Baseline image not found for {img_name}. Please manually verify the generated image and copy it to {baseline_img_dir} as the new baseline.") # 打开图片 try: gen_img = Image.open(gen_path) baseline_img = Image.open(baseline_path) except Exception as e: pytest.fail(f"Failed to open images for {img_name}: {e}") # 计算感知哈希(pHash),允许图像有微小差异(如压缩噪声) gen_hash = imagehash.phash(gen_img) baseline_hash = imagehash.phash(baseline_img) # 计算哈希值之间的汉明距离 hamming_distance = gen_hash - baseline_hash # 设定一个容差阈值。通常,pHash距离<=5可以认为是“相似”的。 # 这个阈值需要根据你的测试内容调整。对于确定性输出(固定种子),可以设为0。 tolerance = 10 assert hamming_distance <= tolerance, f"Image {img_name} differs from baseline. Hash distance: {hamming_distance} (threshold: {tolerance})" print(f"All {len(generated_images)} generated images passed baseline comparison.")这个测试用例会遍历test_output目录下的所有图片,与tests/baseline_images/目录下同名的基线图片进行比对。它使用感知哈希(pHash)算法,能容忍一些无关紧要的像素变化(比如压缩造成的细微差异),但能捕捉到有意义的图像内容变化。
如何建立基线?第一次运行CI时,测试肯定会失败,因为还没有基线图片。你需要:
- 手动运行一次测试脚本,确保生成的图片是正确的。
- 将这些正确的图片复制到
tests/baseline_images/目录下,提交到仓库。 从此以后,CI就会用这些图片作为标准来比对后续的生成结果。
4. 创建测试工作流与基线
4.1 设计测试工作流 (test_workflow_api.json)
你需要在ComfyUI中设计一个或多个用于测试的工作流。这个工作流应该:
- 覆盖核心功能:包含你想要测试的Qwen-Image-Edit-F2P节点的主要操作。
- 尽量简单快速:避免使用高分辨率、多步迭代等耗时的操作,以缩短CI时间。
- 保持确定性:为随机数生成器(如KSampler的种子)设置固定值,确保每次输入相同,输出也相同,这样比对才有意义。
在ComfyUI中设计好工作流后,点击菜单栏的“Save (API Format)”将其保存为JSON文件,例如test_workflow_api.json,并放在项目指定的位置(如tests/目录下)。
4.2 组织项目目录
最终,你的项目目录结构可能看起来像这样:
your-qwen-image-edit-repo/ ├── .github/ │ └── workflows/ │ └── ci-comfyui-test.yml ├── custom_nodes/ │ └── Qwen-Image-Edit-F2P/ # 你的自定义节点代码 ├── tests/ │ ├── __init__.py │ ├── test_workflow_api.json # 测试用的工作流文件 │ ├── baseline_images/ # 存放基线图片 │ │ └── test_output_001.png │ └── test_image_diff.py # 图像比对测试 ├── test_workflow_runner.py # API测试运行脚本 ├── requirements.txt # 项目依赖 └── README.md5. 运行与调试
将以上所有文件准备好并推送到GitHub仓库后,GitHub Actions就会自动运行。
- 查看运行状态:在你的GitHub仓库页面,点击“Actions”标签页,就能看到工作流的运行历史和实时日志。
- 调试失败:如果工作流失败了,仔细查看日志。常见问题包括:
- 依赖安装失败:检查
requirements.txt和系统包。 - ComfyUI启动失败:检查端口是否被占用,或者模型文件缺失导致启动错误。可以通过在CI步骤中增加
curl http://127.0.0.1:8188来检查服务是否健康。 - API调用失败:检查工作流JSON格式是否正确,节点ID在API格式中是否存在。
- 图片比对失败:检查基线图片是否存在,或者调整
tolerance阈值。
- 依赖安装失败:检查
- 优化速度:CI时间就是金钱(对于私有仓库)。可以考虑使用
actions/cache来缓存Python依赖包和下载好的模型文件,避免每次从头下载安装。
整体走下来,这套自动化测试流程一开始搭建需要花点时间,但一旦跑通,它带来的收益是巨大的。每次提交代码,你都能立刻知道核心的图像生成功能是否被意外破坏,这为团队协作和项目迭代提供了坚实的质量保障。尤其是对于像图像生成这种输出结果直观但验证复杂的功能,自动化测试能节省大量手动回归测试的时间。
你可以根据自己项目的实际情况,对这个流程进行裁剪和增强,比如增加更多不同参数组合的测试工作流,或者将测试结果以注释的形式自动添加到Pull Request中。希望这个指南能帮你把Qwen-Image-Edit-F2P项目的开发流程变得更高效、更可靠。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
