187 lines
6.9 KiB
Python
187 lines
6.9 KiB
Python
# Copyright (C) 2025 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import sys
|
|
import os
|
|
import glob
|
|
import time
|
|
|
|
from golden_manager import GoldenManager, ACCESS_TYPE_SSH, ACCESS_TYPE_TOKEN
|
|
from image_diff import same_image
|
|
|
|
from utils import execute, ArgParseImpl
|
|
from utils import prompt_helper, PROMPT_YES, PROMPT_NO
|
|
|
|
def line_prompt(prompt, validator=lambda a:True):
|
|
while True:
|
|
res = input(f'{prompt} => ').strip()
|
|
if validator(res):
|
|
return res
|
|
return None
|
|
|
|
CONFIG_NEW_SRC_DIR = 'goldens_dir'
|
|
CONFIG_GOLDENS_BRANCH = 'goldens_branch'
|
|
CONFIG_GOLDENS_UPDATES = 'goldens_updates'
|
|
CONFIG_GOLDENS_DELETES = 'goldens_deletes'
|
|
CONFIG_PUSH_TO_REMOTE = 'push-to-remote'
|
|
CONFIG_COMMIT_MSG = 'commit_msg'
|
|
|
|
def _get_current_branch():
|
|
code, res = execute('git branch --show-current')
|
|
return res.strip()
|
|
|
|
def _file_as_str(fpath):
|
|
with open(fpath, 'r') as f:
|
|
return f.read()
|
|
|
|
def _do_update(golden_manager, config):
|
|
deletes = config[CONFIG_GOLDENS_DELETES]
|
|
updates = config[CONFIG_GOLDENS_UPDATES]
|
|
if len(deletes) == 0 and len(updates) == 0:
|
|
print('Nothing to update. Exiting...')
|
|
exit(0)
|
|
|
|
branch = config[CONFIG_GOLDENS_BRANCH]
|
|
src_dir = config[CONFIG_NEW_SRC_DIR]
|
|
push_to_remote = config[CONFIG_PUSH_TO_REMOTE]
|
|
commit_msg = config[CONFIG_COMMIT_MSG]
|
|
golden_manager.source_from(src_dir, commit_msg, branch,
|
|
updates=updates,
|
|
deletes=deletes,
|
|
push_to_remote=push_to_remote)
|
|
|
|
def _get_deletes_updates(update_dir, golden_dir):
|
|
ret_delete = []
|
|
ret_update = []
|
|
for ext in ['tif', 'json']:
|
|
base = set(glob.glob(f'./**/*.{ext}', root_dir=golden_dir, recursive=True))
|
|
new = set(glob.glob(f'./**/*.{ext}', root_dir=update_dir, recursive=True))
|
|
|
|
delete = list(base - new)
|
|
update = list(new - base)
|
|
|
|
for fpath in base.intersection(new):
|
|
base_fpath = os.path.join(golden_dir, fpath)
|
|
new_fpath = os.path.join(update_dir, fpath)
|
|
if (ext == 'tif' and not same_image(new_fpath, base_fpath)) or \
|
|
(ext == 'json' and _file_as_str(new_fpath) != _file_as_str(base_fpath)):
|
|
update.append(fpath)
|
|
|
|
ret_update += update
|
|
ret_delete += delete
|
|
|
|
return ret_delete, ret_update
|
|
|
|
# Ask a bunch of questions to gather the configuration for the update
|
|
def _interactive_mode(base_golden_dir):
|
|
config = {}
|
|
cur_branch = _get_current_branch()
|
|
if prompt_helper(
|
|
f'Generate the new goldens from your local ' \
|
|
f'Filament branch? (branch={cur_branch})') == PROMPT_YES:
|
|
code, res = execute('bash ./test/renderdiff/generate.sh',
|
|
capture_output=False)
|
|
if code != 0:
|
|
print('Failed to generate new goldens')
|
|
exit(1)
|
|
# Note that this matches RENDER_OUTPUT_DIR in preamble.sh
|
|
config[CONFIG_NEW_SRC_DIR] = os.path.join(os.getcwd(), './out/renderdiff/renders')
|
|
else:
|
|
def validator(src_dir):
|
|
if not os.path.exists(src_dir):
|
|
print(f'Cannot find directory {src_dir}. Please try again.')
|
|
return False
|
|
return True
|
|
|
|
config[CONFIG_NEW_SRC_DIR] = line_prompt(
|
|
'Please provide path of directory containing new goldens',
|
|
validator)
|
|
|
|
if prompt_helper(f'Update new goldens to branch={cur_branch}? '
|
|
'(Note that this refers to a branch in the goldens repo, not the Filament repo.)'
|
|
) == PROMPT_YES:
|
|
config[CONFIG_GOLDENS_BRANCH] = cur_branch
|
|
else:
|
|
config[CONFIG_GOLDENS_BRANCH] = line_prompt('Please provide new branch name for update')
|
|
|
|
if prompt_helper(f'Provide a commit message?') == PROMPT_YES:
|
|
config[CONFIG_COMMIT_MSG] = line_prompt('Message:')
|
|
else:
|
|
config[CONFIG_COMMIT_MSG] = f'Update {time.time()} from filament ({cur_branch})'
|
|
|
|
new_golden_dir = config[CONFIG_NEW_SRC_DIR]
|
|
deletes, updates = _get_deletes_updates(new_golden_dir, base_golden_dir)
|
|
if len(deletes) + len(updates) != 0:
|
|
prompt = 'The following files will be changed:\n' + \
|
|
'\n'.join([f' {fname} [delete]' for fname in deletes]) + \
|
|
'\n'.join([f' {fname} [update]' for fname in updates]) + \
|
|
'\nIs that ok?'
|
|
if prompt_helper(prompt) == PROMPT_YES:
|
|
config[CONFIG_GOLDENS_DELETES] = deletes
|
|
config[CONFIG_GOLDENS_UPDATES] = updates
|
|
else:
|
|
# We cannot proceed if user answered no.
|
|
exit(1)
|
|
else:
|
|
config[CONFIG_GOLDENS_DELETES] = []
|
|
config[CONFIG_GOLDENS_UPDATES] = []
|
|
|
|
config[CONFIG_PUSH_TO_REMOTE] = \
|
|
prompt_helper(f'Commit golden repo changes to remote?') == PROMPT_YES
|
|
return config
|
|
|
|
if __name__ == "__main__":
|
|
parser = ArgParseImpl()
|
|
parser.add_argument('--branch', help='Branch of the golden repo to write to')
|
|
parser.add_argument('--golden-repo-token', help='Access token for the golden repo')
|
|
parser.add_argument('--push-to-remote', action="store_true", help='Access token for the golden repo')
|
|
|
|
# write-to-branch mode
|
|
parser.add_argument('--source', help='Directory containing the new goldens')
|
|
parser.add_argument('--commit-msg', help='Message for the commit to the golden repo')
|
|
|
|
# merge-to-main mode (used in postsubmit)
|
|
parser.add_argument('--merge-to-main', action="store_true", help='Merge to main the given branch')
|
|
parser.add_argument('--filament-tag', help='Tag to append to the commit message on merge')
|
|
|
|
args, _ = parser.parse_known_args(sys.argv[1:])
|
|
config = {}
|
|
golden_manager = GoldenManager(
|
|
os.getcwd(),
|
|
access_type=ACCESS_TYPE_SSH if not args.golden_repo_token else ACCESS_TYPE_TOKEN,
|
|
access_token=args.golden_repo_token
|
|
)
|
|
base_golden_dir = golden_manager.directory()
|
|
|
|
# This is the write-to-branch mode
|
|
if args.branch and args.source and args.commit_msg:
|
|
assert os.path.exists(args.source), f'{args.source} (--source) directory not found'
|
|
deletes, updates = _get_deletes_updates(args.source, base_golden_dir)
|
|
config = {
|
|
CONFIG_PUSH_TO_REMOTE: args.push_to_remote,
|
|
CONFIG_GOLDENS_BRANCH: args.branch,
|
|
CONFIG_NEW_SRC_DIR: args.source,
|
|
CONFIG_GOLDENS_UPDATES: updates,
|
|
CONFIG_GOLDENS_DELETES: deletes,
|
|
CONFIG_COMMIT_MSG: args.commit_msg,
|
|
}
|
|
_do_update(golden_manager, config)
|
|
# This is the merge-to-main mode
|
|
elif args.branch and args.merge_to_main and args.filament_tag:
|
|
golden_manager.merge_to_main(branch=args.branch, tag=args.filament_tag, push_to_remote=True)
|
|
# Else, we're in interactive mode of write-to-branch (for local execution).
|
|
else:
|
|
config = _interactive_mode(base_golden_dir)
|
|
_do_update(golden_manager, config)
|