Skip to content

Commit dc632a8

Browse files
authored
Merge pull request #87 from homebysix/dev
1.19.0 merge to main
2 parents f2de47f + abb690c commit dc632a8

File tree

7 files changed

+137
-76
lines changed

7 files changed

+137
-76
lines changed

CHANGELOG.md

+19
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,25 @@ All notable changes to this project will be documented in this file. This projec
1414

1515
Nothing yet.
1616

17+
## [1.19.0] - 2025-01-16
18+
19+
### Added
20+
21+
- Added `--warn-on-missing-installer-items` flag that makes missing Munki install/uninstall items a warning instead of a failure. (#86, thanks to @haircut)
22+
- Apply the same checks to `uninstaller_item_location` that were previously applied to `installer_item_location`.
23+
- `check-autopkg-recipes` requires Munki recipe `pkginfo` dicts to contain at least `name` and `description`.
24+
- `check-autopkg-recipes` now validates that `uninstall_method` and `uninstall_script` are set appropriately in Munki recipes.
25+
26+
### Changed
27+
28+
- `check-autopkg-recipes` includes jamf-upload as an AutoPkg recipe type, and updated processors included in jamf/jamf-upload recipe convention.
29+
- `check-munki-pkgsinfo` requires a `version` key in addition to `name` and `description`.
30+
31+
### Fixed
32+
33+
- Bug fix in `check-munkiadmin-scripts` that prevented script names from processing correctly.
34+
- Bug fix in `check-munki-pkgsinfo` that prevented `--warn-on-duplicate-imports` flag from working correctly.
35+
1736
## [1.18.0] - 2025-01-04
1837

1938
### Added

README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ For any hook in this repo you wish to use, add the following to your pre-commit
1515

1616
```yaml
1717
- repo: https://github.com/homebysix/pre-commit-macadmin
18-
rev: v1.18.0
18+
rev: v1.19.0
1919
hooks:
2020
- id: check-plists
2121
# - id: ...
@@ -120,7 +120,7 @@ After adding a hook to your pre-commit config, it's not a bad idea to run `pre-c
120120
- Choose to just warn if icons referenced in pkginfo files are missing (this will allow pre-commit checks to pass if no other issues exist):
121121
`args: ['--warn-on-missing-icons]`
122122

123-
- Choose to just warn if installer items (`installer_item_location`) referenced in pkginfo files are missing (this will allow pre-commit checks to pass if no other issues exist):
123+
- Choose to just warn if installer/uninstaller items (`installer_item_location` or `uninstaller_item_location`) referenced in pkginfo files are missing (this will allow pre-commit checks to pass if no other issues exist):
124124
`args: ['--warn-on-missing-installer-items]`
125125

126126
- Choose to just warn if pkg/pkginfo files with __1 (or similar) suffixes are detected (this will allow pre-commit checks to pass if no other issues exist):
@@ -147,7 +147,7 @@ When combining arguments that take lists (for example: `--required-keys`, `--cat
147147

148148
```yaml
149149
- repo: https://github.com/homebysix/pre-commit-macadmin
150-
rev: v1.18.0
150+
rev: v1.19.0
151151
hooks:
152152
- id: check-munki-pkgsinfo
153153
args: ['--catalogs', 'testing', 'stable', '--']
@@ -157,7 +157,7 @@ But if you also use the `--categories` argument, you would move the trailing `--
157157

158158
```yaml
159159
- repo: https://github.com/homebysix/pre-commit-macadmin
160-
rev: v1.18.0
160+
rev: v1.19.0
161161
hooks:
162162
- id: check-munki-pkgsinfo
163163
args: ['--catalogs', 'testing', 'stable', '--categories', 'Design', 'Engineering', 'Web Browsers', '--']
@@ -169,7 +169,7 @@ If it looks better to your eye, feel free to use a multi-line list for long argu
169169

170170
```yaml
171171
- repo: https://github.com/homebysix/pre-commit-macadmin
172-
rev: v1.18.0
172+
rev: v1.19.0
173173
hooks:
174174
- id: check-munki-pkgsinfo
175175
args: [

pre_commit_hooks/check_autopkg_recipes.py

+50-24
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
validate_pkginfo_key_types,
1717
validate_required_keys,
1818
validate_restart_action_key,
19+
validate_uninstall_method,
1920
)
2021

2122

@@ -382,6 +383,34 @@ def validate_proc_type_conventions(process, filename):
382383

383384
# For each processor type, this is the list of processors that
384385
# we only expect to see in that type. List order is unimportant.
386+
# TODO: Simpler to check for `com.github.grahampugh.jamf-upload.processors/` prefix?
387+
jamf_upload_procs = [
388+
"com.github.grahampugh.jamf-upload.processors/JamfAccountUploader",
389+
"com.github.grahampugh.jamf-upload.processors/JamfCategoryUploader",
390+
"com.github.grahampugh.jamf-upload.processors/JamfClassicAPIObjectUploader",
391+
"com.github.grahampugh.jamf-upload.processors/JamfComputerGroupDeleter",
392+
"com.github.grahampugh.jamf-upload.processors/JamfComputerGroupUploader",
393+
"com.github.grahampugh.jamf-upload.processors/JamfComputerProfileUploader",
394+
"com.github.grahampugh.jamf-upload.processors/JamfDockItemUploader",
395+
"com.github.grahampugh.jamf-upload.processors/JamfExtensionAttributeUploader",
396+
"com.github.grahampugh.jamf-upload.processors/JamfIconUploader",
397+
"com.github.grahampugh.jamf-upload.processors/JamfMacAppUploader",
398+
"com.github.grahampugh.jamf-upload.processors/JamfMobileDeviceGroupUploader",
399+
"com.github.grahampugh.jamf-upload.processors/JamfMobileDeviceProfileUploader",
400+
"com.github.grahampugh.jamf-upload.processors/JamfPackageCleaner",
401+
"com.github.grahampugh.jamf-upload.processors/JamfPackageRecalculator",
402+
"com.github.grahampugh.jamf-upload.processors/JamfPackageUploader",
403+
"com.github.grahampugh.jamf-upload.processors/JamfPatchChecker",
404+
"com.github.grahampugh.jamf-upload.processors/JamfPatchUploader",
405+
"com.github.grahampugh.jamf-upload.processors/JamfPkgMetadataUploader",
406+
"com.github.grahampugh.jamf-upload.processors/JamfPolicyDeleter",
407+
"com.github.grahampugh.jamf-upload.processors/JamfPolicyLogFlusher",
408+
"com.github.grahampugh.jamf-upload.processors/JamfPolicyUploader",
409+
"com.github.grahampugh.jamf-upload.processors/JamfScriptUploader",
410+
"com.github.grahampugh.jamf-upload.processors/JamfSoftwareRestrictionUploader",
411+
"com.github.grahampugh.jamf-upload.processors/JamfUploaderSlacker",
412+
"com.github.grahampugh.jamf-upload.processors/JamfUploaderTeamsNotifier",
413+
]
385414
proc_type_conventions = {
386415
"download": [
387416
"SparkleUpdateInfoProvider",
@@ -405,30 +434,8 @@ def validate_proc_type_conventions(process, filename):
405434
# https://github.com/jssimporter/JSSImporter
406435
"jss": ["JSSImporter"],
407436
# https://github.com/grahampugh/jamf-upload
408-
"jamf": [
409-
"com.github.grahampugh.jamf-upload.processors/JamfAccountUploader",
410-
"com.github.grahampugh.jamf-upload.processors/JamfCategoryUploader",
411-
"com.github.grahampugh.jamf-upload.processors/JamfClassicAPIObjectUploader",
412-
"com.github.grahampugh.jamf-upload.processors/JamfComputerGroupUploader",
413-
"com.github.grahampugh.jamf-upload.processors/JamfComputerProfileUploader",
414-
"com.github.grahampugh.jamf-upload.processors/JamfDockItemUploader",
415-
"com.github.grahampugh.jamf-upload.processors/JamfExtensionAttributeUploader",
416-
"com.github.grahampugh.jamf-upload.processors/JamfIconUploader",
417-
"com.github.grahampugh.jamf-upload.processors/JamfMacAppUploader",
418-
"com.github.grahampugh.jamf-upload.processors/JamfMobileDeviceGroupUploader",
419-
"com.github.grahampugh.jamf-upload.processors/JamfMobileDeviceProfileUploader",
420-
"com.github.grahampugh.jamf-upload.processors/JamfPackageCleaner",
421-
"com.github.grahampugh.jamf-upload.processors/JamfPackageUploader",
422-
"com.github.grahampugh.jamf-upload.processors/JamfPatchChecker",
423-
"com.github.grahampugh.jamf-upload.processors/JamfPatchUploader",
424-
"com.github.grahampugh.jamf-upload.processors/JamfPolicyDeleter",
425-
"com.github.grahampugh.jamf-upload.processors/JamfPolicyLogFlusher",
426-
"com.github.grahampugh.jamf-upload.processors/JamfPolicyUploader",
427-
"com.github.grahampugh.jamf-upload.processors/JamfScriptUploader",
428-
"com.github.grahampugh.jamf-upload.processors/JamfSoftwareRestrictionUploader",
429-
"com.github.grahampugh.jamf-upload.processors/JamfUploaderSlacker",
430-
"com.github.grahampugh.jamf-upload.processors/JamfUploaderTeamsNotifier",
431-
],
437+
"jamf": jamf_upload_procs,
438+
"jamf-upload": jamf_upload_procs,
432439
# https://github.com/autopkg/filewave
433440
"filewave": [
434441
"com.github.autopkg.filewave.FWTool/FileWaveImporter",
@@ -574,6 +581,7 @@ def main(argv=None):
574581
# recipe_text = openfile.read()
575582

576583
# Top level keys that all AutoPkg recipes should contain.
584+
# TODO: Make required recipe keys configurable.
577585
required_keys = ["Identifier"]
578586
if not validate_required_keys(recipe, filename, required_keys):
579587
retval = 1
@@ -609,12 +617,30 @@ def main(argv=None):
609617
# If the Input key contains a pkginfo dict, make a best effort to validate its contents.
610618
input_key = recipe.get("Input", recipe.get("input", recipe.get("INPUT")))
611619
if input_key and "pkginfo" in input_key:
620+
621+
# Check for presence of required pkginfo keys.
622+
# TODO: Make required pkginfo keys within a recipe configurable.
623+
req_keys = ["name", "description"]
624+
if not validate_required_keys(input_key["pkginfo"], filename, req_keys):
625+
retval = 1
626+
627+
# Ensure pkginfo keys have expected types.
612628
if not validate_pkginfo_key_types(input_key["pkginfo"], filename):
613629
retval = 1
630+
631+
# Validate RestartAction key.
614632
if not validate_restart_action_key(input_key["pkginfo"], filename):
615633
retval = 1
634+
635+
# Validate uninstall method.
636+
if not validate_uninstall_method(input_key["pkginfo"], filename):
637+
retval = 1
638+
639+
# Check for deprecated pkginfo keys.
616640
if not detect_deprecated_keys(input_key["pkginfo"], filename):
617641
retval = 1
642+
643+
# Check for common mistakes in key names.
618644
if not detect_typoed_keys(input_key["pkginfo"], filename):
619645
retval = 1
620646

pre_commit_hooks/check_munki_pkgsinfo.py

+38-44
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
validate_required_keys,
1515
validate_restart_action_key,
1616
validate_shebangs,
17+
validate_uninstall_method,
1718
)
1819

1920

@@ -25,11 +26,12 @@ def build_argument_parser():
2526
)
2627
parser.add_argument("--categories", nargs="+", help="List of approved categories.")
2728
parser.add_argument("--catalogs", nargs="+", help="List of approved catalogs.")
29+
default_req_keys = ["description", "name", "version"]
2830
parser.add_argument(
2931
"--required-keys",
3032
nargs="+",
31-
default=["description", "name"],
32-
help="List of required top-level keys.",
33+
default=default_req_keys,
34+
help=f"List of required top-level keys. Defaults to: {default_req_keys}",
3335
)
3436
parser.add_argument(
3537
"--require-pkg-blocking-apps",
@@ -38,7 +40,7 @@ def build_argument_parser():
3840
)
3941
parser.add_argument("filenames", nargs="*", help="Filenames to check.")
4042
parser.add_argument(
41-
"--munki-repo", default=".", help="path to local munki repo defaults to '.'"
43+
"--munki-repo", default=".", help="path to local munki repo. Defaults to '.'"
4244
)
4345
parser.add_argument(
4446
"--warn-on-missing-icons",
@@ -122,11 +124,15 @@ def main(argv=None):
122124
if not validate_restart_action_key(pkginfo, filename):
123125
retval = 1
124126

127+
# Validate uninstall method.
128+
if not validate_uninstall_method(pkginfo, filename):
129+
retval = 1
130+
125131
# Check for deprecated pkginfo keys.
126132
if not detect_deprecated_keys(pkginfo, filename):
127133
retval = 1
128134

129-
# Check for common mistakes key names.
135+
# Check for common mistakes in key names.
130136
if not detect_typoed_keys(pkginfo, filename):
131137
retval = 1
132138

@@ -144,31 +150,6 @@ def main(argv=None):
144150
print(f'{filename}: catalog "{catalog}" is not in approved list')
145151
retval = 1
146152

147-
# Check for missing or case-conflicted installer items
148-
if not _check_case_sensitive_path(
149-
os.path.join(
150-
args.munki_repo, "pkgs", pkginfo.get("installer_item_location", "")
151-
)
152-
):
153-
msg = "installer item does not exist or path is not case sensitive"
154-
if args.warn_on_missing_installer_items:
155-
print(f"{filename}: WARNING: {msg}")
156-
else:
157-
print(f"{filename}: {msg}")
158-
retval = 1
159-
160-
# Check for pkg filenames showing signs of duplicate imports.
161-
if pkginfo.get("installer_item_location", "").endswith(tuple(dupe_suffixes)):
162-
installer_item_location = pkginfo["installer_item_location"]
163-
msg = (
164-
f"installer item '{installer_item_location}' may be a duplicate import"
165-
)
166-
if args.warn_on_missing_icons:
167-
print(f"{filename}: WARNING: {msg}")
168-
else:
169-
print(f"{filename}: {msg}")
170-
retval = 1
171-
172153
# Checking for the absence of blocking_applications for pkg installers.
173154
# If a pkg doesn't require blocking_applications, use empty "<array/>" in pkginfo.
174155
if args.require_pkg_blocking_apps and all(
@@ -184,6 +165,34 @@ def main(argv=None):
184165
)
185166
retval = 1
186167

168+
# Begin checks that apply to both installers and uninstallers
169+
for i_type in ("installer", "uninstaller"):
170+
171+
# Check for missing or case-conflicted installer or uninstaller items
172+
if not _check_case_sensitive_path(
173+
os.path.join(
174+
args.munki_repo, "pkgs", pkginfo.get(f"{i_type}_item_location", "")
175+
)
176+
):
177+
msg = f"{i_type} item does not exist or path is not case sensitive"
178+
if args.warn_on_missing_installer_items:
179+
print(f"{filename}: WARNING: {msg}")
180+
else:
181+
print(f"{filename}: {msg}")
182+
retval = 1
183+
184+
# Check for pkg filenames showing signs of duplicate imports.
185+
if pkginfo.get(f"{i_type}_item_location", "").endswith(
186+
tuple(dupe_suffixes)
187+
):
188+
item_loc = pkginfo[f"{i_type}_item_location"]
189+
msg = f"{i_type} item '{item_loc}' may be a duplicate import"
190+
if args.warn_on_duplicate_imports:
191+
print(f"{filename}: WARNING: {msg}")
192+
else:
193+
print(f"{filename}: {msg}")
194+
retval = 1
195+
187196
# Ensure an icon exists for the item.
188197
if not any(
189198
(
@@ -201,21 +210,6 @@ def main(argv=None):
201210
print(f"{filename}: {msg}")
202211
retval = 1
203212

204-
# Ensure uninstall method is set correctly if uninstall_script exists.
205-
uninst_method = pkginfo.get("uninstall_method")
206-
if "uninstall_script" in pkginfo and uninst_method != "uninstall_script":
207-
print(
208-
f"{filename}: has an uninstall script, but the uninstall "
209-
f'method is set to "{uninst_method}"'
210-
)
211-
retval = 1
212-
elif "uninstall_script" not in pkginfo and uninst_method == "uninstall_script":
213-
print(
214-
f"{filename}: uninstall_method is set to uninstall_script, "
215-
'but no uninstall script is present"'
216-
)
217-
retval = 1
218-
219213
# Ensure all pkginfo scripts have a proper shebang.
220214
script_types = (
221215
"installcheck_script",

pre_commit_hooks/check_munkiadmin_scripts.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ def main(argv=None):
3232
prefixes = ["manifest", "pkginfo", "repository"]
3333
actions = ["custom", "postopen", "postsave", "presave"]
3434
ma_script_prefixes = [f"{p}-{a}" for p in prefixes for a in actions]
35-
if not any(filename.startswith(prefix) for prefix in ma_script_prefixes):
35+
if not any(
36+
os.path.basename(filename).startswith(prefix)
37+
for prefix in ma_script_prefixes
38+
):
3639
print(f"{filename}: does not start with a valid MunkiAdmin script prefix")
3740
retval = 1
3841

pre_commit_hooks/util.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def detect_typoed_keys(input_dict, filename):
141141

142142

143143
def validate_restart_action_key(pkginfo, filename):
144-
"""Verifies that required_keys are present in pkginfo dictionary."""
144+
"""Verifies that the RestartAction key is set correctly."""
145145
passed = True
146146
allowed_values = (
147147
"RequireShutdown",
@@ -159,6 +159,25 @@ def validate_restart_action_key(pkginfo, filename):
159159
return passed
160160

161161

162+
def validate_uninstall_method(pkginfo, filename):
163+
"""Verifies that uninstall_method and uninstall_script is used appropriately."""
164+
passed = True
165+
uninst_method = pkginfo.get("uninstall_method")
166+
if pkginfo.get("uninstall_script") and uninst_method != "uninstall_script":
167+
print(
168+
f"{filename}: has an uninstall script, but the uninstall "
169+
f'method is set to "{uninst_method}"'
170+
)
171+
passed = False
172+
elif not pkginfo.get("uninstall_script") and uninst_method == "uninstall_script":
173+
print(
174+
f"{filename}: uninstall_method is set to uninstall_script, "
175+
'but no uninstall script is present"'
176+
)
177+
passed = False
178+
return passed
179+
180+
162181
def validate_pkginfo_key_types(pkginfo, filename):
163182
"""Validation of pkginfo key types.
164183

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
name="pre-commit-macadmin",
77
description="Pre-commit hooks for Mac admins, client engineers, and IT consultants.",
88
url="https://github.com/homebysix/pre-commit-macadmin",
9-
version="1.18.0",
9+
version="1.19.0",
1010
author="Elliot Jordan",
1111
author_email="[email protected]",
1212
packages=["pre_commit_hooks"],

0 commit comments

Comments
 (0)