当前位置: 首页 > news >正文

【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)流水线,具体到我们这个场景,流水线大概长这样:

  1. 触发:当你向GitHub仓库的特定分支(比如maindev)推送代码,或者发起一个拉取请求(Pull Request)时,自动触发工作流。
  2. 构建环境:GitHub Actions会启动一个全新的虚拟服务器(Runner),在上面安装好所有依赖,比如Python、ComfyUI、以及你的自定义节点。
  3. 运行测试:在这个环境中,启动ComfyUI服务,然后通过其API或者脚本,加载你预先设计好的、用于测试的工作流(.json.png文件),并执行图像生成任务。
  4. 验证结果:将生成的图片与事先保存好的“预期正确”的图片(通常叫golden imagebaseline)进行比对。比对可以是简单的像素对比,也可以是更复杂的感知哈希(pHash)对比,允许微小的差异。
  5. 反馈结果:根据比对结果,决定这个工作流运行是成功还是失败。结果会直接显示在GitHub的提交记录或Pull Request页面上,一目了然。

整个过程完全自动化,无需人工干预。接下来,我们就一步步实现它。

2. 在项目中配置GitHub Actions工作流

首先,你需要在你的Qwen-Image-Edit-F2P项目仓库中,创建GitHub Actions的配置文件。

  1. 在你的项目根目录下,创建如下文件夹和文件:

    .github/ └── workflows/ └── ci-comfyui-test.yml

    这个ci-comfyui-test.yml文件就是我们工作流的“剧本”。

  2. 打开这个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.pytests/目录下的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框架和Pillowimagehash库来编写测试。

首先,确保你的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时,测试肯定会失败,因为还没有基线图片。你需要:

  1. 手动运行一次测试脚本,确保生成的图片是正确的。
  2. 将这些正确的图片复制到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.md

5. 运行与调试

将以上所有文件准备好并推送到GitHub仓库后,GitHub Actions就会自动运行。

  1. 查看运行状态:在你的GitHub仓库页面,点击“Actions”标签页,就能看到工作流的运行历史和实时日志。
  2. 调试失败:如果工作流失败了,仔细查看日志。常见问题包括:
    • 依赖安装失败:检查requirements.txt和系统包。
    • ComfyUI启动失败:检查端口是否被占用,或者模型文件缺失导致启动错误。可以通过在CI步骤中增加curl http://127.0.0.1:8188来检查服务是否健康。
    • API调用失败:检查工作流JSON格式是否正确,节点ID在API格式中是否存在。
    • 图片比对失败:检查基线图片是否存在,或者调整tolerance阈值。
  3. 优化速度:CI时间就是金钱(对于私有仓库)。可以考虑使用actions/cache来缓存Python依赖包和下载好的模型文件,避免每次从头下载安装。

整体走下来,这套自动化测试流程一开始搭建需要花点时间,但一旦跑通,它带来的收益是巨大的。每次提交代码,你都能立刻知道核心的图像生成功能是否被意外破坏,这为团队协作和项目迭代提供了坚实的质量保障。尤其是对于像图像生成这种输出结果直观但验证复杂的功能,自动化测试能节省大量手动回归测试的时间。

你可以根据自己项目的实际情况,对这个流程进行裁剪和增强,比如增加更多不同参数组合的测试工作流,或者将测试结果以注释的形式自动添加到Pull Request中。希望这个指南能帮你把Qwen-Image-Edit-F2P项目的开发流程变得更高效、更可靠。

获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

http://www.jsqmd.com/news/717727/

相关文章:

  • CLion效率翻倍:一键生成含参数名的函数注释(实时模板+Doxygen全攻略)
  • Wan2.2-I2V-A14B惊艳案例:动态光影变化+景深过渡自然的海边视频生成
  • 从Spring Boot到飞腾+麒麟OS:Java AI推理引擎国产化部署 checklist(含等保2.0三级认证配置模板)
  • 2025-2026年西奥多电话查询:使用前需核实资质与了解服务范围 - 品牌推荐
  • 前端最佳实践:从代码规范到团队协作
  • 终极指南:一键解锁网易云音乐NCM加密文件,轻松实现格式转换自由
  • 为什么 AI 编排层要选 FastAPI 而不是 Django?深度解析 + 适合场景
  • Altium Designer新手必看:保姆级Gerber文件生成与检查全流程(附CAM350/华秋DFM避坑指南)
  • **发散创新:基于角色与策略的动态权限控制系统设计与实现**在现代企业级应用中,权限管理已不再是简单的“用户
  • Navicat Cloud进阶篇:怎样高效细粒度设置项目成员权限_云端技巧
  • 2025-2026年天和电话查询:选购麻将机前请核实资质与使用须知 - 品牌推荐
  • AI写论文攻略在此!4款AI论文生成工具,开启高效论文写作!
  • 告别向日葵收费:用ChmlFrp+Windows RDP打造你的私有远程办公环境(2024最新配置)
  • 从DALL-E 2到Stable Diffusion:深入聊聊‘无分类器引导’技术是如何让AI画画更听话的
  • YOLO目标检测算法与mAP评估指标详解(附示例)
  • 让AI做PPT?职场人士必备PPT制作skill:html-ppt-skill
  • 【限时解密】头部AIGC平台内部AI沙箱架构图流出(脱敏版):如何用轻量级Kata容器实现毫秒级冷启+零信任设备访问控制
  • 从一次线上故障复盘说起:我是如何用阿里云SLB+ECS+OSS架构,差点搞垮自己网站的
  • GANs技术解析:从原理到实战应用
  • Java 25 虚拟线程与结构化并发:构建高效并发应用
  • 量子最优控制在热态制备中的高效实现
  • Redis如何防止热点Key过期引发缓存击穿
  • 2025-2026年天和电话查询:选购麻将机前需了解产品特性与维护事项 - 品牌推荐
  • Yageo国巨01005系列号阻原厂原装一级代理分钟经销商
  • 反熵共同体——OpenClaw的宇宙热力学本体论(第十七篇)
  • 电机IF启动
  • 告别刷写失败!手把手教你用CANoe/CANalyzer调试UDS 37服务(RequestTransferExit)
  • Qwen3.5-2B模型精调实战:使用自定义数据集训练行业专属模型
  • Wan2.2-I2V-A14B环境部署详解:Windows系统下CUDA与模型服务配置
  • 终极网页时光机:用Wayback Machine扩展一键回溯互联网记忆