Linters Hooks Pipelines

Automation to Save Your Bacon Elliot Jordan End-User Platform Security Nordstrom “I’m not really a software developer.

I just think I’m a software developer because I develop software.”

— Arjen van Bochoven ‣ Package sources ‣ Scripts and extension plist, , json, shell, python attributes ‣ AutoPkg recipes/ shell, python overrides ‣ MDM profiles plist, shell, python plist ‣ Munki repos ‣ Documentation plist, python, shell text, , reStructuredText Mac Software "Dev Ops" Admin Developer Reducing errors

Streamlining development

Automating tedious tasks Ground Rules

Protected "master" branch

Peer review

Remote Git hosting

Production code in Git

Code standards Linters Linters

Linters Linters

Linters Linters

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Terminal $ brew install shellcheck ==> Downloading https://homebrew.bintray.com/bottles/ shellcheck-0.6.0_1.mojave.bottle.tar.gz ==> Pouring shellcheck-0.6.0_1.mojave.bottle.tar.gz ! /usr/local/Cellar/shellcheck/0.6.0_1: 8 files, 7.2MB $ which shellcheck /usr/local/bin/shellcheck ⌘C $

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Linters Atom + Shellcheck

⌘V

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Click to learn more!

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Typo caught

Linters Atom + Shellcheck

Suggestions for improving resiliency

Linters Atom + Shellcheck

Deprecated syntax callouts

Linters Atom + Shellcheck

Generally accepted practices

Linters Atom + Shellcheck

Useless cat!

Photo: Byron Chin

Linters Atom + Shellcheck

Linters Atom + Shellcheck

Linters Shellcheck + zsh

‣ Shellcheck doesn't currently support zsh

However:

‣ That might change someday

‣ Bash scripts should work fine in Catalina

Linters Atom + Pylint

Linters Atom + Pylint

Linters Atom + Pylint

Linters Atom + Pylint

Linters Atom + Pylint

Linters Atom + Pylint

Linters Atom + Pylint

Linters Atom + Pylint

Linters Atom + Pylint

Linters Atom + Pylint

Linters Fine-Tuning Linters

E0611

Linters Fine-Tuning Linters

E0611

Linters Fine-Tuning Linters

# pylint: disable=E0611

Linters Fine-Tuning Linters

# pylint: disable=no-name-in-module

Terminal $ pylint example2.py No config file found, using default configuration ************* Module example2 C: 1, 0: Missing module docstring (missing-docstring) E: 3, 0: No name 'CFPreferencesCopyAppValue' in module 'CoreFoundation' (no-name-in-module) C: 6, 0: Constant name "munki_repo" doesn't conform to UPPER_CASE naming style (invalid-name)

------Your code has been rated at -13.33/10 (previous run: -13.33/10, +0.00)

$

Linters Fine-Tuning Linters

# pylint: disable=no-name-in-module

# pylint: enable=no-name-in-module

Linters Fine-Tuning Linters

# pylint: disable=E0611

Linters Fine-Tuning Linters

Linters Fine-Tuning Linters

# shellcheck disable=SC2115

Linters Fine-Tuning Linters

# shellcheck disable=SC2115

Linters Linter Limitations

‣ Installed per-app and per-Mac ‣ Not easily distributed across a team ‣ Suggestions are optional

Linters Autoformatters

‣ Python black, yapf, autopep8, isort ‣ Go gofmt ‣ Ruby rubocop

Linters Python Black

Linters Python Black

Linters Python Black

Linters Python Black

Linters Fine-Tuning Autoformatters

# fmt: off

# fmt: on

Linters When to Avoid Autoformatters

‣ Submitting a very small change to a repo ‣ Contributing to a new repo for the first time ‣ If the maintainers strongly prefer their own style

Linters Autoformatter Limitations

‣ You may not agree with the style choices (but you get used to it) ‣ Can be quite jarring to convert a project to use autoformatters for the first time (large diffs) ‣ Not easily distributable across team without help from other frameworks

Linters Hooks Hooks

git commit -a -m "My great commit"

fix the issue and try again pre-commit hook(s)

exit exit nonzero zero

commit fails commit succeeds

Linters Hooks Hooks

git commit -a -m "My great commit"

fix the issue and try again pre-commit hook(s)

exit exit nonzero zero

commit fails commit succeeds

Linters Hooks Hooks

pre-commit hook(s)

Linters Hooks Installing Pre-Commit

Terminal $ brew install pre-commit Updating Homebrew... ==> Downloading https://homebrew.bintray.com/bottles/pre-commit-1.17.0.moja ve.bottle.tar.gz ==> Pouring pre-commit-1.17.0.mojave.bottle.tar.gz ! /usr/local/Cellar/pre-commit/1.17.0: 703 files, 8.9MB $ which pre-commit /usr/local/bin/pre-commit $ pre-commit --version pre-commit 1.17.0 $

Linters Hooks Installing Pre-Commit

Terminal $ cd ~/path/to/git_repo $ touch .pre-commit-config.yaml $ open -a Atom .pre-commit-config.yaml $

Linters Hooks Installing Pre-Commit

Terminal

$ cd ~/path/to/git_repo .pre-commit-config.yaml $ touch .pre-commit-config.yaml repos:$ open -a Atom .pre-commit-config.yaml - repo:$ https://github.com/pre-commit/pre-commit-hooks rev: v2.2.3 hooks: - id: no-commit-to-branch

Linters Hooks Installing Pre-Commit

Terminal $ cd ~/path/to/git_repo $ touch .pre-commit-config.yaml $ open -a Atom .pre-commit-config.yaml $ pre-commit install pre-commit installed at .git/hooks/pre-commit $

Linters Hooks Installing Pre-Commit

Terminal $ git branch * master $ git commit -am "Add pre-commit config" Don't commit to branch...... Failed $ git checkout -b pre-commit A .pre-install-config.yaml Switched to a new branch 'pre-commit' $ git commit -am "Add pre-commit config" Don't commit to branch...... Passed [pre-commit 48f2745] Add pre-commit config 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .pre-install-config.yaml $

Linters Hooks Configuring Pre-Commit

.pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.2.3 hooks: - id: no-commit-to-branch

Linters Hooks Configuring Pre-Commit

.pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.2.3 hooks: - id: no-commit-to-branch - id: check-added-large-files args: [--maxkb=100] - id: check-merge-conflict

- repo: https://github.com/python/black rev: 19.3b0 hooks: - id: black

Linters Hooks Configuring Pre-Commit

.pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.2.3 hooks: - id: no-commit-to-branch P - id: check-added-large-files Text Editor Pre-Commit args: [--maxkb=100] - id: check-merge-conflict

- repo: https://github.com/python/black rev: 19.3b0 hooks: - id: black

Linters Hooks Configuring Pre-Commit

.pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/ - repo: https://github.com/ambv/black pre-commit-hooks rev: 19.3b0 rev: v2.2.1 hooks: hooks: - id: black - id: check-added-large-files args: [--maxkb=200] - repo: https://github.com/asottile/ - id: check-byte-order-marker blacken-docs - id: check-case-conflict rev: v0.5.0 - id: check-docstring-first hooks: - id: check-merge-conflict - id: blacken-docs - id: check-symlinks additional_dependencies: - id: check-yaml [black==19.3b0] - id: mixed-line-ending - id: no-commit-to-branch - id: trailing-whitespace args: [--markdown-linebreak-ext=md]

Linters Hooks Configuring Pre-Commit

.pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/ - repo: https://github.com/ambv/black pre-commit-hooks rev: 19.3b0 rev: v2.2.1 hooks: hooks: - id: black - id: check-added-large-files args: [--maxkb=200] - repo: https://github.com/asottile/ - id: check-byte-order-marker blacken-docs - id: check-case-conflict rev: v0.5.0 - id: check-docstring-first hooks: - id: check-merge-conflict - id: blacken-docs - id: check-symlinks additional_dependencies: - id: check-yaml [black==19.3b0] - id: mixed-line-ending - id: no-commit-to-branch - id: trailing-whitespace args: [--markdown-linebreak-ext=md]

Linters Hooks Updating Pre-Commit Hooks

Terminal $ pre-commit autoupdate Updating https://github.com/pre-commit/pre-commit-hooks...updating v2.2.0 -> v2.2.3. Updating https://github.com/python/black...[INFO] Initializing environment for https://github.com/python/black. updating stable -> 19.3b0. Updating https://github.com/asottile/blacken-docs...[INFO] Initializing environment for https://github.com/asottile/blacken-docs. updating v1.0.0 -> v1.1.0. $

Linters Hooks Configuring Pre-Commit

.pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/ - repo: https://github.com/ambv/black pre-commit-hooks rev: 19.3b0 rev: v2.2.1 hooks: hooks: - id: black - id: check-added-large-files args: [--maxkb=200] - repo: https://github.com/asottile/ - id: check-byte-order-marker blacken-docs - id: check-case-conflict rev: v0.5.0 - id: check-docstring-first hooks: - id: check-merge-conflict - id: blacken-docs - id: check-symlinks additional_dependencies: - id: check-yaml [black==19.3b0] - id: mixed-line-ending - id: no-commit-to-branch - id: trailing-whitespace args: [--markdown-linebreak-ext=md]

Linters Hooks Pre-Commit Hooks for Mac Admins

https://github.com/homebysix/pre-commit-macadmin

Linters Hooks Pre-Commit Hooks for Mac Admins

~/Developer/pkg-sources/.pre-commit-config.yaml repos: - repo: https://github.com/homebysix/pre-commit-macadmin rev: v1.3.0 hooks: - id: check-munkipkg-buildinfo - id: check-outset-scripts - id: check-plists

Linters Hooks Pre-Commit Hooks for Mac Admins

~/Developer/pkg-sources/.pre-commit-config.yaml repos: - repo: https://github.com/homebysix/pre-commit-macadmin rev: v1.3.0 hooks: - id: check-munkipkg-buildinfo - id: check-outset-scripts - id: check-plists

Linters Hooks Pre-Commit Hooks for Mac Admins

~/Developer/pkg-sources/.pre-commit-config.yaml repos: - repo: https://github.com/homebysix/pre-commit-macadmin rev: v1.3.0 hooks: - id: check-munkipkg-buildinfo - id: check-outset-scripts - id: check-plists

Linters Hooks Pre-Commit Hooks for Mac Admins

~/Developer/public-autopkg-recipes/.pre-commit-config.yaml repos: - repo: https://github.com/homebysix/pre-commit-macadmin rev: v1.3.0 hooks: - id: check-autopkg-recipes args: ['--recipe-prefix=com.github.yourusername.', '--strict'] - id: forbid-autopkg-overrides - id: forbid-autopkg-trust-info

Linters Hooks Pre-Commit Hooks for Mac Admins

~/Developer/public-autopkg-recipes/.pre-commit-config.yaml repos: - repo: https://github.com/homebysix/pre-commit-macadmin rev: v1.3.0 hooks: - id: check-autopkg-recipes args: ['--recipe-prefix=com.github.yourusername.', '--strict'] - id: forbid-autopkg-overrides - id: forbid-autopkg-trust-info

Linters Hooks Pre-Commit Hooks for Mac Admins

~/Developer/public-autopkg-recipes/.pre-commit-config.yaml repos: - repo: https://github.com/homebysix/pre-commit-macadmin rev: v1.3.0 hooks: - id: check-autopkg-recipes args: ['--recipe-prefix=com.github.yourusername.', '--strict'] - id: forbid-autopkg-overrides - id: forbid-autopkg-trust-info

Linters Hooks Pre-Commit Hooks for Mac Admins

~/Developer/company-autopkg-overrides/.pre-commit-config.yaml repos: - repo: https://github.com/homebysix/pre-commit-macadmin rev: v1.3.0 hooks: - id: check-autopkg-recipes args: ['--recipe-prefix=com.example.autopkg.', '--override-prefix=local.', '--strict'] - id: check-autopkg-recipe-list - id: check-plists

Linters Hooks Pre-Commit Hooks for Mac Admins

~/Developer/company-autopkg-overrides/.pre-commit-config.yaml repos: - repo: https://github.com/homebysix/pre-commit-macadmin rev: v1.3.0 hooks: - id: check-autopkg-recipes args: ['--recipe-prefix=com.example.autopkg.', '--override-prefix=local.', '--strict'] - id: check-autopkg-recipe-list - id: check-plists

Linters Hooks Pre-Commit Hooks for Mac Admins

~/Developer/company-munki-repo/.pre-commit-config.yaml repos: - repo: https://github.com/homebysix/pre-commit-macadmin rev: v1.3.0 hooks: - id: check-munki-pkgsinfo args: [ '--required-keys', 'category', 'description', 'developer', 'name', 'version', '--catalogs', 'stable', 'testing', '--categories', 'Communication', 'Printers', 'Productivity', 'Security' '--'] - id: check-munkiadmin-scripts - id: check-plists

Linters Hooks Collaborating with Pre-Commit

Git Remote

.pre-commit-config.yaml

brew install pre-commit brew install pre-commit git pull git pull git push pre-commit install pre-commit install

.pre-commit-config.yaml .pre-commit-config.yaml .pre-commit-config.yaml

Collaborators run pre-commit install once for each Git repo.

Linters Hooks Pre-Commit Hook Limitations

‣ Hooks are typically written for a general audience ‣ Updating repo rev is a minor recurring task ‣ Hooks can still be bypassed

‣ If contributor doesn't set up local repo with pre-commit install

‣ If contributor skips hooks using git commit --no-verify

‣ If contributor commits using GitLab/GitHub web UI

Linters Hooks Pipelines Pipelines

Git Remote

Linters Hooks Pipelines Pipelines

product build code syncing to inspection prod

"master" branch

Linters Hooks Pipelines Pipelines

code product syncing to inspection build prod

"master" branch peer review

• linting • MunkiPkg builds • autoformatters • sync repo to cloud hosting • makecatalogs • pre-commit hooks • import artifacts into Munki • code signing • org-specific test scripts

Continuous Integration Continuous Deployment

Linters Hooks Pipelines Pipelines

code product syncing to inspection build prod

"master" branch peer review

• linting • MunkiPkg builds • autoformatters • sync repo to cloud hosting • makecatalogs • pre-commit hooks • import artifacts into Munki • code signing • org-specific test scripts

Continuous Integration Continuous Deployment

Linters Hooks Pipelines Basic CI Config

Linters Hooks Pipelines Basic CI Config

Linters Hooks Pipelines Basic CI Config

.gitlab-ci.yml One-line linting command plutil_linting: tags: - macOS script: /usr/bin/plutil -lint *.plist

Linters Hooks Pipelines Basic CI Config

good.plist name value

Linters Hooks Pipelines Basic CI Config

bad_char.plist Unescaped less-than character condition 0 < 3

Linters Hooks Pipelines Basic CI Config

bad_key.plist Key with no value name1 name2 value

Linters Hooks Pipelines Basic CI Config

bad_tag.plist name Incorrect "str" instead of "string" value

Linters Hooks Pipelines Basic CI Config

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Enforcing Peer Review

Linters Hooks Pipelines Git Workflow

add commits create dev branch merge request

peer review merge to master master branch

Linters Hooks Pipelines But... What to Use Pipelines For?

‣ Prevent anything that would blow up production

‣ badly formatted plist

‣ absence of required keys

‣ missing shebangs for scripts in Munki pkginfo files

Linters Hooks Pipelines But... What to Use Pipelines For?

‣ Things best done in the cloud

‣ Running commands like makecatalogs (Note: Does not require macOS)

‣ Syncing to cloud storage

Linters Hooks Pipelines But... What to Use Pipelines For?

‣ Tasks that require elevated access or permissions

‣ Syncing to cloud storage

‣ Signing apps or profiles using a private key

Linters Hooks Pipelines But... What to Use Pipelines For?

‣ Whatever you want!

‣ Catching mistakes made previously

‣ Enforcing code/formatting standards you care about

‣ Preventing rogue files, categories, identifiers, etc.

Linters Hooks Pipelines Pipeline Examples

.gitlab-ci.yml pre_commit: script: /usr/local/bin/pre-commit run --all-files except: - master

(Assumes your runner has pre-commit, python3, and git installed.)

Linters Hooks Pipelines Pipeline Examples Munki Repo

‣ Lint plists, including both pkginfo and manifest files ‣ Ensure required keys are present (including org-specific like category, description) ‣ Ensure catalogs/categories are org-standard ‣ Ensure pkg installers have blocking apps (empty list OK) ‣ Ensure icon file is present ‣ Ensure testing catalog listed before stable in manifests ...and a few more

Linters Hooks Pipelines Pipeline Examples Munki Repo

.gitlab-ci.yml plutil_linting: tags: - macOS script: /usr/bin/plutil -lint *.plist

Linters Hooks Pipelines Pipeline Examples Munki Repo

.gitlab-ci.yml plutil_linting: tags: - macOS Two-line script for plutil script: - /usr/bin/plutil -lint pkgsinfo/*/*.plist - /usr/bin/plutil -lint manifests/*

Linters Hooks Pipelines Pipeline Examples Munki Repo

.gitlab-ci.yml plutil_linting: tags: - macOS script: - /usr/bin/plutil -lint pkgsinfo/*/*.plist - /usr/bin/plutil -lint manifests/* Reference to a separate linting script munki_linting: script: /usr/bin/python ci/munki_linting.py

Linters Hooks Pipelines Pipeline Examples

ci/munki_linting.py #!/usr/bin/python import os import plistlib import sys from glob import glob EMONSTRATION exitcode = 0 D pkginfo_files = globOR("./pkgsinfo/**/*.plist") # os.walk might be better F NLY for path in pkginfo_files: O print("Linting %s..." % path) pkginfo = plistlib.PreadPlistURPOSES(path) # add try/except if pkginfo and "uninstall_script" in pkginfo: # use a function for complex tests if pkginfo.get("uninstall_method") != "uninstall_script": print((extremely"ERROR: %s simplified,has an uninstall not error script, resistent, but thebad uninstallbut easy tomethod walk through)" "is set to %s." % (path, pkginfo.get("uninstall_method"))) exitcode = 1 # more pkginfo tests here sys.exit(exitcode)

Linters Hooks Pipelines Pipeline Examples

ci/munki_linting.py #!/usr/bin/python import os import plistlib import sys from glob import glob Make a list of the files you want to check exitcode = 0 pkginfo_files = glob("./pkgsinfo/**/*.plist") # os.walk might be better for path in pkginfo_files: print("Linting %s..." % path) pkginfo = plistlib.readPlist(path) # add try/except

if pkginfo and "uninstall_script" in pkginfo: # use a function for complex tests if pkginfo.get("uninstall_method") != "uninstall_script": print("ERROR: %s has an uninstall script, but the uninstall method " "is set to %s." % (path, pkginfo.get("uninstall_method"))) exitcode = 1 # more pkginfo tests here sys.exit(exitcode)

Linters Hooks Pipelines Pipeline Examples

ci/munki_linting.py #!/usr/bin/python import os import plistlib import sys from glob import glob exitcode = 0 pkginfo_files = glob("./pkgsinfo/**/*.plist") # os.walk might be better Parse them with plistlib for path in pkginfo_files: print("Linting %s..." % path) pkginfo = plistlib.readPlist(path) # add try/except

if pkginfo and "uninstall_script" in pkginfo: # use a function for complex tests if pkginfo.get("uninstall_method") != "uninstall_script": print("ERROR: %s has an uninstall script, but the uninstall method " "is set to %s." % (path, pkginfo.get("uninstall_method"))) exitcode = 1 # more pkginfo tests here sys.exit(exitcode)

Linters Hooks Pipelines Pipeline Examples

ci/munki_linting.py #!/usr/bin/python import os import plistlib import sys from glob import glob exitcode = 0 pkginfo_files = glob("./pkgsinfo/**/*.plist") # os.walk might be better for path in pkginfo_files: print("Linting %s..." % path) pkginfo = plistlib.readPlist(path) # add try/except Run your checks, set exitcode if pkginfo and "uninstall_script" in pkginfo: # use a function for complex tests if pkginfo.get("uninstall_method") != "uninstall_script": print("ERROR: %s has an uninstall script, but the uninstall method " "is set to %s." % (path, pkginfo.get("uninstall_method"))) exitcode = 1 # more pkginfo tests here sys.exit(exitcode)

Linters Hooks Pipelines Pipeline Examples

ci/munki_linting.py #!/usr/bin/python import os import plistlib import sys from glob import glob exitcode = 0 pkginfo_files = glob("./pkgsinfo/**/*.plist") # os.walk might be better for path in pkginfo_files: print("Linting %s..." % path) pkginfo = plistlib.readPlist(path) # add try/except

if pkginfo and "uninstall_script" in pkginfo: # use a function for complex tests if pkginfo.get("uninstall_method") != "uninstall_script": print("ERROR: %s has an uninstall script, but the uninstall method " "is set to %s." % (path, pkginfo.get("uninstall_method"))) exitcode = 1 # more pkginfo tests here Exit with the appropriate code sys.exit(exitcode)

Linters Hooks Pipelines Pipeline Examples Munki Repo

.gitlab-ci.yml plutil_linting: tags: - macOS script: - /usr/bin/plutil -lint pkgsinfo/*/*.plist - /usr/bin/plutil -lint manifests/* munki_linting: script: /usr/bin/python ci/munki_linting.py

Linters Hooks Pipelines Pipeline Examples Munki Repo

.gitlab-ci.yml plutil_linting: tags: - macOS script: - /usr/bin/plutil -lint pkgsinfo/*/*.plist - /usr/bin/plutil -lint manifests/* munki_linting: script: /usr/bin/python ci/munki_linting.py Python Black autoformatting (x3!) black_format: script: /usr/local/bin/black .

Linters Hooks Pipelines Pipeline Examples Munki Repo

.gitlab-ci.yml plutil_linting: Tag to target a specific runner tags: - macOS script: - /usr/bin/plutil -lint pkgsinfo/*/*.plist - /usr/bin/plutil -lint manifests/* munki_linting: script: /usr/bin/python ci/munki_linting.py Does not require macOS black_format: script: /usr/local/bin/black .

Linters Hooks Pipelines Pipeline Examples Munki Repo

.gitlab-ci.yml # ...continued... makecatalogs: script: /usr/local/munki/makecatalogs Run job only on a specific branch only: - master

Linters Hooks Pipelines Pipeline Examples Munki Repo

.gitlab-ci.yml # ...continued... Sync master branch to AWS S3 s3_sync: variables: REPO_BUCKET: yourmunkirepo script: - aws s3 sync ./catalogs "s3://$REPO_BUCKET/catalogs" --delete --exclude=".*" - aws s3 sync ./icons "s3://$REPO_BUCKET/icons" --delete --exclude=".*" - aws s3 sync ./pkgsinfo "s3://$REPO_BUCKET/pkgsinfo" --delete --exclude=".*" - aws s3 sync ./manifests "s3://$REPO_BUCKET/manifests" --delete --exclude=".*" # The pkgs folder is synced separately. only: - master

Linters Hooks Pipelines Pipeline Examples Munki Repo

product build syncing to prod code inspection "master" branch

Linters Hooks Pipelines Pipeline Examples Munki Repo

makecatalogs aws s3 sync linting scripts "master" branch

Linters Hooks Pipelines CI/CD Pipeline Limitations

‣ Setting up and maintaining runners is significant administrative overhead ‣ Requiring peer review can be a stress on your teammates' time and attention

Linters Hooks Pipelines Let's Review

Linters Hooks Pipelines Let's Review

Mac Software "Dev Ops" Admin Developer

Linters Hooks Pipelines Let's Review

Protected "master" branch

Peer review

Remote Git hosting

Production code in Git

Code standards

Linters Hooks Pipelines Let's Review

Linters Hooks Pipelines Let's Review

Terminal $ git commit -a -m "Apply shellcheck suggestions" Check for added large files...... Passed Check for byte-order marker...... Passed Check for case conflicts...... Passed Check docstring is first...... (no files to check)Skipped Check for merge conflicts...... Passed Check for broken symlinks...... (no files to check)Skipped Check Yaml...... Passed Mixed line ending...... Passed Don't commit to branch...... Passed Trim Trailing Whitespace...... Passed black...... (no files to check)Skipped blacken-docs...... (no files to check)Skipped Check Munki Pkginfo Files...... (no files to check)Skipped Check MunkiAdmin Scripts...... (no files to check)Skipped Check Plists...... (no files to check)Skipped [pre-commit-gitlab-ci 9dc7318] Remove non-working config 1 file changed, 18 deletions(-)

$

Linters Hooks Pipelines Let's Review

Terminal $ git commit -a -m "Apply shellcheck suggestions" Check for added large files...... Passed Check for byte-order marker...... Passed Check for case conflicts...... Passed Check docstring is first...... (no files to check)Skipped Check for merge conflicts...... Passed Check for broken symlinks...... (no files to check)Skipped Check Yaml...... Passed Mixed line ending...... Passed Don't commit to branch...... Passed Trim Trailing Whitespace...... Passed black...... (no files to check)Skipped blacken-docs...... (no files to check)Skipped Check Munki Pkginfo Files...... (no files to check)Skipped Check MunkiAdmin Scripts...... (no files to check)Skipped Check Plists...... (no files to check)Skipped [pre-commit-gitlab-ci 9dc7318] Remove non-working config 1 file changed, 18 deletions(-)

$ git push

Linters Hooks Pipelines Let's Review

Git Remote

Additional linting

Custom check scripts

Linters Hooks Pipelines Let's Review

Git Remote

Peer review

Linters Hooks Pipelines Let's Review

Git Remote

Merge to master branch

Linters Hooks Pipelines Let's Review

Git Remote

Build products

Linters Hooks Pipelines Let's Review

Git Remote

Sync repo/products with prod

Linters Hooks Pipelines ! " homebysix # elliotjordan

Thank you!

Notes and Links: https://bit.ly/2S1cRCC

Linters Hooks Pipelines