Skip to content

Commit

Permalink
Merge pull request #2 from homebysix/autopkg-key-order
Browse files Browse the repository at this point in the history
If input is an AutoPkg recipe, adjust order of process keys in yaml output
  • Loading branch information
grahampugh authored Feb 3, 2020
2 parents 17d2b12 + f2e2ee1 commit 51032da
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 79 deletions.
2 changes: 1 addition & 1 deletion .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.4.0
hooks:
- id: check-added-large-files
args: ['--maxkb=100']
- id: check-ast
- id: check-byte-order-marker
- id: check-case-conflict
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: end-of-file-fixer
- id: fix-encoding-pragma
- id: mixed-line-ending
- id: trailing-whitespace
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ Usage: yaml-plist.py <input-file> [<output-file>]

**Notes:**

1. With `plistyamlplist.py`, if you do not specify an `output-file` value, the script determines if the `input-file` is
a plist or yaml file. If a plist file, the `input-file` name will be appended with `.yaml` for the output file. If a
1. With `plistyamlplist.py`, if you do not specify an `output-file` value, the script determines if the `input-file` is
a plist or yaml file. If a plist file, the `input-file` name will be appended with `.yaml` for the output file. If a
yaml file, the output file name will be the `input-file` name with `.yaml` removed.
2. With `plist_yaml.py`, if you do not specify an `output-file` value, the `input-file` name will be appended with
2. With `plist_yaml.py`, if you do not specify an `output-file` value, the `input-file` name will be appended with
`.yaml` for the output file.
3. With `yaml_plist.py`, if you do not specify an `output-file` value, and the `input-file` name ends with `.yaml`,
3. With `yaml_plist.py`, if you do not specify an `output-file` value, and the `input-file` name ends with `.yaml`,
the output file name will be the `input-file` name with `.yaml` removed.
4. With `plist_yaml.py`, you may have to first convert a binary plist to text format using `plutil`.

Expand Down Expand Up @@ -69,15 +69,15 @@ $ ./plistyamlplist.py ~/Downloads/com.something.plist.yaml

## YAML folder

If you have a folder named `YAML` in your path, and you do not supply a destination, the script will determine if a
If you have a folder named `YAML` in your path, and you do not supply a destination, the script will determine if a
corresponding folder exists in the path without 'YAML'. For example, consider the following file:

/Users/myuser/gitrepo/YAML/product/com.something.plist.yaml
If the folder `/Users/myuser/gitrepo/product` exists, the converted file will be created/overwritten at:

If the folder `/Users/myuser/gitrepo/product` exists, the converted file will be created/overwritten at:

/Users/myuser/gitrepo/product/com.something.plist

If there is no `YAML` folder in the path, the converted file will be placed in the same folder

# Credits
Expand Down
96 changes: 69 additions & 27 deletions plist_yaml.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
If this script is run directly, it takes an input file and an output file from
the command line. The input file must be in PLIST format.
The output file will be in YAML format:
"""If this script is run directly, it takes an input file and an output file
from the command line. The input file must be in PLIST format. The output file
will be in YAML format:
plist_yaml.py <input-file> <output-file>
Expand All @@ -12,50 +12,92 @@
"""

import sys
from collections import OrderedDict

try:
from plistlib import Data # Python 3
from plistlib import load as load_plist
except ImportError:
from plistlib import Data # Python 2
from plistlib import readPlist as load_plist

import yaml
from os import path
from plistlib import readPlist, Data


def normalize_types(input):
"""
This allows YAML and JSON to store Data fields as strings. However, this
operation is irreversible. Only use if read-only access to the plist is
required.
def represent_ordereddict(dumper, data):
value = []

for item_key, item_value in data.items():
node_key = dumper.represent_data(item_key)
node_value = dumper.represent_data(item_value)

value.append((node_key, node_value))

return yaml.nodes.MappingNode(u"tag:yaml.org,2002:map", value)


def normalize_types(input_data):
"""This allows YAML and JSON to store Data fields as strings.
However, this operation is irreversible. Only use if read-only
access to the plist is required.
"""
if isinstance(input, Data): return input.data
if isinstance(input, list):
if isinstance(input_data, Data):
return input_data.data
if isinstance(input_data, list):
retval = []
for child in input:
for child in input_data:
retval.append(normalize_types(child))
return retval
if isinstance(input, dict):
if isinstance(input_data, dict):
retval = {}
for key, child in input.iteritems():
retval[key] = normalize_types(child)
for key in input_data:
retval[key] = normalize_types(input_data[key])
return retval
return input
return input_data


def sort_autopkg_processes(recipe):
"""If input is an AutoPkg recipe, adjust the processor dictionaries such
that the Arguments key is ordered last.
This usually puts the Processor key first, which makes the process
list more human-readable.
"""
if "Process" in recipe:
process = recipe["Process"]
new_process = []
for processor in process:
if "Arguments" in processor:
processor = OrderedDict(processor)
processor.move_to_end("Arguments")
new_process.append(processor)
recipe["Process"] = new_process
return recipe


def convert(xml):
"""Do the conversion"""
return yaml.dump(xml, width=float('inf'), default_flow_style=False)
"""Do the conversion."""
yaml.add_representer(OrderedDict, represent_ordereddict)
return yaml.dump(xml, width=float("inf"), default_flow_style=False)


def plist_yaml(in_path, out_path):
"""Convert plist to yaml"""
in_file = open(in_path, 'r')
input = readPlist(in_file)
"""Convert plist to yaml."""
with open(in_path, "rb") as in_file:
input_data = load_plist(in_file)

normalized = normalize_types(input)
normalized = normalize_types(input_data)
if sys.version_info.major == 3 and in_path.endswith((".recipe", ".recipe.plist")):
normalized = sort_autopkg_processes(normalized)
output = convert(normalized)

out_file = open(out_path, 'w')
out_file = open(out_path, "w")
out_file.writelines(output)


def main():
"""Get the command line inputs if running this script directly"""
"""Get the command line inputs if running this script directly."""
if len(sys.argv) < 2:
print("Usage: plist_yaml.py <input-file> <output-file>")
sys.exit(1)
Expand All @@ -72,5 +114,5 @@ def main():
plist_yaml(in_path, out_path)


if __name__ == '__main__':
if __name__ == "__main__":
main()
60 changes: 34 additions & 26 deletions plistyamlplist.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
If this script is run directly, it takes an input file and an output file from
the command line. It detects if the input file is a YAML or a PLIST file,
"""If this script is run directly, it takes an input file and an output file
from the command line. It detects if the input file is a YAML or a PLIST file,
and converts to the other format:
plistyamlplist.py <input-file> <output-file>
Expand All @@ -13,27 +13,32 @@

import sys
from os import path

from plist_yaml import plist_yaml
from yaml_plist import yaml_plist


def usage():
"""print help"""
"""print help."""
print("Usage: plistyamlplist.py <input-file> <output-file>\n")
print("If <input-file> is a PLIST and <output-file> is omitted,\n"
"<input-file> is converted to <input-file>.yaml\n")
print("If <input-file> ends in .yaml or .yml and <output-file> is omitted,\n"
"<input-file>.yaml is converted to PLIST format with name <input-file>\n")
print(
"If <input-file> is a PLIST and <output-file> is omitted,\n"
"<input-file> is converted to <input-file>.yaml\n"
)
print(
"If <input-file> ends in .yaml or .yml and <output-file> is omitted,\n"
"<input-file>.yaml is converted to PLIST format with name <input-file>\n"
)


def check_if_plist(in_path):
"""rather than restrict by filename, check if the file is a plist by reading
the second line of the file for the PLIST declaration"""
"""rather than restrict by filename, check if the file is a plist by
reading the second line of the file for the PLIST declaration."""
with open(in_path) as fp:
for i, line in enumerate(fp):
if i == 1:
# print line
if line.find('PLIST 1.0') == -1:
if line.find("PLIST 1.0") == -1:
is_plist = False
else:
is_plist = True
Expand All @@ -43,26 +48,29 @@ def check_if_plist(in_path):


def check_for_yaml_folder(check_path):
"""Check folder hierarchy for a YAML folder. Output to same folder structure outwith YAML
folder if it exists,
e.g. /path/to/YAML/folder/subfolder/my.plist.yaml ==> /path/to/folder/subfolder/my.plist
Note there is no reverse option at this time"""
"""Check folder hierarchy for a YAML folder.
Output to same folder structure outwith YAML folder if it exists,
e.g. /path/to/YAML/folder/subfolder/my.plist.yaml ==>
/path/to/folder/subfolder/my.plist Note there is no reverse option
at this time
"""
check_abspath = path.abspath(check_path)
if 'YAML' in check_abspath:
print('YAML folder exists : {}'.format(check_abspath))
top_path, base_path = check_abspath.split('YAML/')
if "YAML" in check_abspath:
print("YAML folder exists : {}".format(check_abspath))
top_path, base_path = check_abspath.split("YAML/")
out_path = path.dirname(path.join(top_path, base_path))
if path.exists(out_path):
print('Path exists : {}'.format(out_path))
print("Path exists : {}".format(out_path))
return out_path
else:
print('Path does not exist : {}'.format(out_path))
print('Please create this folder and try again')
print("Path does not exist : {}".format(out_path))
print("Please create this folder and try again")
exit(1)


def main():
"""get the command line inputs if running this script directly"""
"""get the command line inputs if running this script directly."""
if len(sys.argv) < 2:
usage()
exit(1)
Expand All @@ -71,7 +79,7 @@ def main():
try:
sys.argv[2]
except IndexError:
if in_path.endswith('.yaml') or in_path.endswith('.yml'):
if in_path.endswith(".yaml") or in_path.endswith(".yml"):
out_dir = check_for_yaml_folder(in_path)
if out_dir:
filename, file_extension = path.splitext(path.basename(in_path))
Expand All @@ -90,7 +98,7 @@ def main():
out_path = sys.argv[2]

# auto-determine which direction the conversion should go
if in_path.endswith('.yaml'):
if in_path.endswith(".yaml"):
yaml_plist(in_path, out_path)
else:
if check_if_plist(in_path):
Expand All @@ -99,8 +107,8 @@ def main():
print("\nERROR: Input File is neither PLIST nor YAML format.\n")
usage()
exit(1)
print('Wrote to : {}'.format(out_path))
print("Wrote to : {}".format(out_path))


if __name__ == '__main__':
if __name__ == "__main__":
main()
35 changes: 18 additions & 17 deletions yaml_plist.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
If this script is run directly, it takes an input file and an output file from
the command lineThe input file must be in YAML format.
The output file will be in PLIST format:
"""If this script is run directly, it takes an input file and an output file
from the command lineThe input file must be in YAML format. The output file
will be in PLIST format:
yaml_plist.py <input-file> <output-file>
Expand All @@ -14,31 +14,32 @@
"""

import sys
import yaml
from os import path
from plistlib import writePlistToString

import yaml


def convert(yaml):
"""Do the conversion"""
lines = writePlistToString(yaml).splitlines()
lines.append('')
def convert(data):
"""Do the conversion."""
lines = writePlistToString(data).splitlines()
lines.append("")
return "\n".join(lines)


def yaml_plist(in_path, out_path):
"""Convert yaml to plist"""
in_file = open(in_path, 'r')
out_file = open(out_path, 'w')
"""Convert yaml to plist."""
in_file = open(in_path, "r")
out_file = open(out_path, "w")

input = yaml.safe_load(in_file)
output = convert(input)
input_data = yaml.safe_load(in_file)
output = convert(input_data)

out_file.writelines(output)


def main():
"""Get the command line inputs if running this script directly"""
"""Get the command line inputs if running this script directly."""
if len(sys.argv) < 2:
print("Usage: yaml_plist.py <input-file> <output-file>")
sys.exit(1)
Expand All @@ -47,7 +48,7 @@ def main():
try:
sys.argv[2]
except Exception as e:
if in_path.endswith('.yaml'):
if in_path.endswith(".yaml"):
filename, file_extension = path.splitext(in_path)
out_path = filename
else:
Expand All @@ -59,5 +60,5 @@ def main():
yaml_plist(in_path, out_path)


if __name__ == '__main__':
if __name__ == "__main__":
main()

0 comments on commit 51032da

Please sign in to comment.