Skip to content

Commit

Permalink
Feat.
Browse files Browse the repository at this point in the history
  • Loading branch information
yoland68 committed Jun 14, 2024
1 parent 95c79a6 commit a12843b
Show file tree
Hide file tree
Showing 8 changed files with 537 additions and 128 deletions.
111 changes: 48 additions & 63 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,34 @@ inputs:
description: "Operating System. macos, linux, windows."
required: true
default: ""
python_version:
description: "Python Version. Will be used in the conda install command for pytorch. 3.9 or 3.10. Only valid when os is linux or windows."
required: false
default: "3.9"
cuda_version:
description: "CUDA Version. Will be used in the conda install command for pytorch. 11.8 or 12.1. Only valid when os is linux or windows."
required: false
default: "12.1"
torch_version:
description: "Pytorch Version. Will be used in the conda install command for pytorch. 1.10.0 or 1.11.0. Only valid when os is linux or windows."
required: false
default: 'stable'
models-json:
description: 'JSON string containing models and their download URLs. The models will be downloaded into the exact directory relative to /ComfyUI/models/. eg { "model_name": { url: "https://example.com/model.pth", "directory": "checkpoints" } }'
required: false
workflow_filenames:
description: "List of workflows to run. Seperate by comma. eg.
'workflow1,workflow2'. The list of workflow is listed in workflows/"
required: true
workflow_name:
description: "Name of the workflow to run. This is used to identify the workflow in the logs."
required: true
workflow_json_path:
description: "Path (relative to the root of the Github repo) of the Workflow
JSON to run. Must be API format JSON."
comfyui_flags:
description: "Flags to pass to the comfyui application. eg. --force-fp16"
required: false
default: "workflow.json"
default: ''
# Not yet supported
workflow_raw_json:
description: "Workflow's raw json file"
required: false
default: ''
timeout:
description: "Timeout for the workflow (in seconds)"
required: false
Expand Down Expand Up @@ -76,6 +89,7 @@ runs:
miniconda-version: latest
activate-environment: comfyui
auto-activate-base: false
python-version: ${{ inputs.python_version }}

- name: '[Unix-Mac-Only] Install Pytorch nightly'
if: ${{ inputs.os == 'macos' }}
Expand Down Expand Up @@ -127,13 +141,13 @@ runs:
if: ${{ inputs.os != 'windows' }}
shell: bash -el {0}
run: |
python main.py --quick-test-for-ci
comfy launch -- --quick-test-for-ci
- name: '[Unix] Run Python application'
if: ${{ inputs.os != 'windows' }}
shell: bash -el {0}
run: |
python3 main.py --force-fp16 > application.log 2>&1 &
comfy launch -- --force-fp16 > application.log 2>&1 &
- name: '[Unix] Check if the server is running'
if: ${{ inputs.os != 'windows' }}
Expand All @@ -155,43 +169,12 @@ runs:
echo "Script output: "
echo "$PROMPT_ID"
- name: '[Unix] Get start time'
id: unix_start_time
shell: bash
if: ${{ inputs.os != 'windows' }}
run: |
echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: '[Unix] Check Prompt Status and Get Output Files'
if: ${{ inputs.os != 'windows' }}
shell: bash -el {0}
run: |
cd ${{ github.action_path }}
echo "Prompt ID: ${{ steps.unix_queue_prompt.outputs.prompt_id }}"
python3 check_prompt_status.py ${{ steps.unix_queue_prompt.outputs.prompt_id }} http://localhost:8188/history ${{ inputs.timeout }}
- name: '[Unix] Get end time Unix'
id: unix_end_time
shell: bash
if: ${{ inputs.os != 'windows' }}
run: |
echo "end_time=$(date +%s)" >> $GITHUB_OUTPUT
- name: '[Unix] Auth to GCS'
uses: "google-github-actions/auth@v2"
if: ${{ inputs.os != 'windows' }}
with:
credentials_json: "${{ inputs.google_credentials }}"

- name: '[Unix] Upload Output Files to GCS'
if: ${{ success() && inputs.os != 'windows' }}
id: unix_upload-output-files
uses: google-github-actions/upload-cloud-storage@v2
with:
path: ${{ github.workspace }}/output
destination: ${{ inputs.gcs_bucket_name }}/output-files/${{ github.job }}-${{ inputs.os }}-${{ inputs.workflow_name }}-run${{ github.run_id }}
glob: "${{ inputs.output_prefix }}*"

- name: '[Unix] Upload log file to GCS'
if: ${{ ( success() || failure() ) && inputs.os != 'windows' }}
id: unix_upload-log-files
Expand Down Expand Up @@ -258,20 +241,21 @@ runs:
start_time: $start_time,
end_time: $end_time
}')
echo "$payload"
response_code=$(curl -o "${{ github.workspace }}/application.log" \
-s -w "%{http_code}" \
-X POST "${{inputs.api_endpoint}}" \
-H "Content-Type: application/json" \
-d "$payload")
if [[ $response_code -ne 200 ]]; then
echo "API request failed with status code $response_code and response body"
cat "${{ github.workspace }}/application.log"
exit 1
fi
- name: '[Unix] Upload Output Files'
uses: actions/upload-artifact@v4
Expand All @@ -297,34 +281,35 @@ runs:
run: conda deactivate && conda env remove --name comfyui && conda clean -all


#####################################################################################
## Windows Steps (F**k powershell) ##
## ##
## _.-;;-._ _ ##
## '-..-'| || | | | ##
## '-..-'|_.-;;-._| | |===( ) ////// ##
## '-..-'| || | |_| ||| | o o| ##
## '-..-'|_.-''-._| ||| ( c ) ____ ##
## ||| \= / || \_ ##
## |||||| || | ##
## |||||| ...||__/|-" ##
## |||||| __|________|__ ##
## ||| |______________| ##
## ||| || || || || ##
## ||| || || || || ##
## -------------------------------------|||-------------||-||------||-||------- ##
## |__> || || || || ##
## ##
## ##
#####################################################################################
#####################################################################################
## Windows Steps (F**k powershell) ##
## ##
## _.-;;-._ _ ##
## '-..-'| || | | | ##
## '-..-'|_.-;;-._| | |===( ) ////// ##
## '-..-'| || | |_| ||| | o o| ##
## '-..-'|_.-''-._| ||| ( c ) ____ ##
## ||| \= / || \_ ##
## |||||| || | ##
## |||||| ...||__/|-" ##
## |||||| __|________|__ ##
## ||| |______________| ##
## ||| || || || || ##
## ||| || || || || ##
## -------------------------------------|||-------------||-||------||-||------- ##
## |__> || || || || ##
## ##
## ##
#####################################################################################

- name: '[Win] Setup Conda'
uses: conda-incubator/[email protected]
if: ${{ inputs.os == 'windows' }}
with:
auto-update-conda: true
miniconda-version: latest
activate-environment: comfyui
python-version: ${{ inputs.python_version }}
continue-on-error: true

- name: '[Win-Only] Install Pytorch'
Expand Down
43 changes: 0 additions & 43 deletions check_prompt_status.py
Original file line number Diff line number Diff line change
@@ -1,43 +0,0 @@
import requests
import time
import sys

def get_status(prompt_id, url):
response = requests.get(f"{url}/{prompt_id}")
if response.status_code == 200:
return response.json()
return None

def is_completed(status_response, prompt_id):
# Check if the expected fields exist in the response
return (
status_response and
prompt_id in status_response and
'status' in status_response[prompt_id] and
status_response[prompt_id]['status'].get('completed', False)
)

def main(prompt_id, server_url, timeout):
start_time = time.time()
while True:
status_response = get_status(prompt_id, server_url)
if is_completed(status_response, prompt_id):
print("Prompt completed.")
break

if time.time() - start_time > timeout:
print("Timeout reached without completion.")
sys.exit(1)

time.sleep(10) # Check every 10 seconds

if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python check_prompt_status.py <prompt_id> <server_url> <timeout>")
sys.exit(1)

prompt_id_arg = sys.argv[1]
server_url_arg = sys.argv[2]
timeout_arg = int(sys.argv[3])

main(prompt_id_arg, server_url_arg, timeout_arg)
134 changes: 112 additions & 22 deletions queue_prompt.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,127 @@
import argparse
import json
import subprocess
import datetime

import requests
import argparse

from firebase_admin import storage

def read_json_file(file_path):
with open(file_path, 'r') as file:
with open(file_path, 'r', encoding='utf-8') as file:
return json.load(file)

def post_json_to_server(json_data, url):
return requests.post(url, json=json_data)

def main(json_file_path, server_url):
# Read JSON file
json_contents = read_json_file(json_file_path)
# Print the json_contents
#print(json.dumps(json_contents, indent=4))
# Construct the new JSON object
data_to_send = {"prompt": json_contents}

# Post the JSON to the server
response = post_json_to_server(data_to_send, server_url)
#print("Status Code:" + str(response.status_code))
#print("Response:" + response.text)
response_json = response.json()
if ('prompt_id' not in response_json):
print("Error: prompt_id not found in response.")
print(response_json)
def get_status(prompt_id, url):
response = requests.get(f"{url}/{prompt_id}")
if response.status_code == 200:
return response.json()
return None

def is_completed(status_response, prompt_id):
# Check if the expected fields exist in the response
return (
status_response
and prompt_id in status_response
and 'status' in status_response[prompt_id]
and status_response[prompt_id]['status'].get('completed', False)
)

#TODO: add support for different file type
def upload_img_from_filename(bucket_name: str, gs_path: str, file_path: str, public: bool = True, content_type="image/png"):
bucket = storage.bucket(bucket_name)
blob = bucket.blob(gs_path)
blob.upload_from_filename(file_path, content_type=content_type)
if public:
blob.make_public()


def send_payload_to_api(args, output_files_gcs_paths,
start_time, end_time):

# Create the payload as a dictionary
payload = {
"repo": args.repo,
"run_id": args.run_id,
"os": args.os,
"cuda_version": args.cuda_version,
"output_files_gcs_paths": output_files_gcs_paths,
"commit_hash": args.commit_hash,
"commit_time": args.commit_time,
"commit_message": args.commit_message,
"branch_name": args.branch_name,
"bucket_name": args.bucket_name,
"workflow_name": args.workflow_name,
"start_time": start_time,
"end_time": end_time
}

# Convert payload dictionary to a JSON string
payload_json = json.dumps(payload)

# Send POST request
headers = {'Content-Type': 'application/json'}
response = requests.post(args.api_endpoint, headers=headers, data=payload_json)

# Write response to application.log
log_file_path = "./application.log"
with open(log_file_path, 'w', encoding='utf-8') as log_file:
log_file.write("\n##### Comfy CI Post Response #####\n")
log_file.write(response.text)

# Check the response code
if response.status_code != 200:
print(f"API request failed with status code {response.status_code} and response body")
print(response.text)
exit(1)
else:
print(response_json['prompt_id'])
print("API request successful")

return response.status_code

def main(args):
# Split the workflow file names using ","
workflow_files = args.comfy_workflow_names.split(',')

counter = 1

for file_name in workflow_files:
# Construct the file path
file_path = f"workflows/{file_name}"
start_time = int(datetime.datetime.now().timestamp())
subprocess.run(
["comfycli", "run", "--workflow", file_path],
check=True,
)
end_time = int(datetime.datetime.now().timestamp())

#TODO: add support for multiple file outputs
gs_path = f"output-files/{args.github_action_workflow_name}-{args.os}-{args.comfy_workflow_name}-run-${args.run_id}"
upload_img_from_filename(args.bucket_name, gs_path, f"{args.workspace_path}/output/{args.output_file_prefix}_{counter:05}_.png", public=True)

send_payload_to_api(args, gs_path, start_time, end_time)
counter += 1


if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Send a JSON file contents to a server as a prompt.')
parser.add_argument('json_file_path', type=str, help='Path to the JSON file to send.')
parser.add_argument('--server-url', type=str, default='http://localhost:8188/prompt', help='URL of the server to send the JSON to.')
parser.add_argument('--comfy-workflow-names', type=str, help='List of comfy workflow names.')
parser.add_argument('--github-action-workflow-name', type=str, help='Github action workflow name.')
parser.add_argument('--os', type=str, help='Operating system.')
parser.add_argument('--run-id', type=str, help='Github Run ID.')
parser.add_argument('--repo', type=str, help='Github repo.')
parser.add_argument('--cuda-version', type=str, help='CUDA version.')
parser.add_argument('--commit-hash', type=str, help='Commit hash.')
parser.add_argument('--commit-time', type=str, help='Commit time.')
parser.add_argument('--commit-message', type=str, help='Commit message.')
parser.add_argument('--branch-name', type=str, help='Branch name.')
parser.add_argument('--workflow-file-names', type=str, help='Workflow file names.')
parser.add_argument('--gsc-bucket-name', type=str, help='Name of the GCS bucket to store the output files in.')
parser.add_argument('--workspace-path', type=str, help='Workspace (ComfyUI repo) path, likely ${HOME}/action_runners/_work/ComfyUI/ComfyUI/.')
parser.add_argument('--action-path', type=str, help='Action path., likely ${HOME}/action_runners/_work/comfy-action/.')
parser.add_argument('--output-file-prefix', type=str, help='Output file prefix.')

args = parser.parse_args()
main(args.json_file_path, args.server_url)
main(args)
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
requests
comfy-cli
firebase_admin
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit a12843b

Please sign in to comment.