From 4f35ba24aeb3b4568725389c5d6740bb156f0ee6 Mon Sep 17 00:00:00 2001 From: Nathaniel Landau Date: Sun, 23 Oct 2022 11:49:22 -0400 Subject: [PATCH] build(precommit): add new hooks --- .ansible-lint.yml | 43 +++--- .pre-commit-config.yaml | 140 +++++++++-------- default_variables.yml | 194 ++++++++++++------------ scripts/ansible-vault-precommit.sh | 41 +++++ vault.yml | 234 ++++++++++++++--------------- 5 files changed, 352 insertions(+), 300 deletions(-) create mode 100755 scripts/ansible-vault-precommit.sh diff --git a/.ansible-lint.yml b/.ansible-lint.yml index d0793e3..d201f36 100644 --- a/.ansible-lint.yml +++ b/.ansible-lint.yml @@ -1,27 +1,30 @@ --- # Full documentation: https://ansible-lint.readthedocs.io/en/latest/index.html exclude_paths: - - ../../.cache/ - - .cache/ - - .github/ - - .hooks/ - - .vscode/ - - archived_data/ - - galaxy-roles/ + - ../../.cache/ + - .cache/ + - .github/ + - .hooks/ + - .vscode/ + - archived_data/ + - galaxy-roles/ + - .cz.yaml + - vault.yml skip_list: - - command-instead-of-shell - - name[template] - - ignore-errors - - meta-incorrect - - meta-no-info - - package-latest - - role-name - - unnamed-task - - var-naming - - name[casing] + - command-instead-of-shell + - name[template] + - ignore-errors + - meta-incorrect + - meta-no-info + - package-latest + - role-name + - unnamed-task + - var-naming + - name[casing] + - latest[git] warn_list: - - experimental - - risky-file-permissions - - command-instead-of-module + - experimental + - risky-file-permissions + - command-instead-of-module diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad691bc..4ef0ac5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,68 +1,76 @@ --- repos: - - repo: https://github.com/commitizen-tools/commitizen - rev: v2.35.0 - hooks: - - id: "commitizen" - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: check-added-large-files - - id: check-ast - - id: check-builtin-literals - - id: check-case-conflict - - id: check-docstring-first - - id: check-json - exclude: ^files/certs - - id: check-merge-conflict - - id: check-shebang-scripts-are-executable - - id: check-executables-have-shebangs - - id: check-merge-conflict - - id: check-symlinks - - id: check-toml - - id: check-vcs-permalinks - - id: check-xml - - id: check-yaml - - id: detect-private-key - - id: mixed-line-ending - - id: trailing-whitespace - types: [python] - args: [--markdown-linebreak-ext=md] - - repo: https://github.com/adrienverge/yamllint.git - rev: v1.28.0 - hooks: - - id: yamllint - exclude: vault\.yml|\.pre-commit-config.yaml - files: \.(yaml|yml)$ - types: [file, yaml] - entry: yamllint --strict --config-file .yamllint.yml - - repo: local - hooks: - - id: ansible-lint - name: running ansible-lint - language: system - exclude: | - (?x)^( - (inventory|vault|variables|default_variables)\.ya?ml| - \..*\.ya?ml| - galaxy-roles/.*\.ya?ml| - templates/.*\.ya?ml| - files/.*\.ya?ml - )$ - files: \.ya?ml$ - entry: ansible-lint --force-color --parseable --config-file .ansible-lint.yml - - id: lint-shellscript-templates - name: lint shellscript templates - language: system - files: \.sh\.j2$ - entry: shellcheck -x --exclude=1009,1054,1056,1072,1073,1083,2001,2148 - - id: lint-shellscripts - name: lint shellscripts - language: system - files: \.sh$ - entry: shellcheck -x --exclude=2001,2148 - - id: run-shellscripts-bats-tests - name: run bats unit tests - language: system - files: \.bats$ - entry: bats -t + - repo: "https://github.com/commitizen-tools/commitizen" + rev: v2.35.0 + hooks: + - id: "commitizen" + + - repo: "https://github.com/pre-commit/pre-commit-hooks" + rev: v4.3.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: check-builtin-literals + - id: check-case-conflict + - id: check-docstring-first + - id: check-json + exclude: ^files/certs + - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-symlinks + - id: check-toml + - id: check-vcs-permalinks + - id: check-xml + - id: check-yaml + - id: detect-private-key + - id: mixed-line-ending + - id: trailing-whitespace + types: [python] + args: [--markdown-linebreak-ext=md] + + - repo: "https://github.com/adrienverge/yamllint.git" + rev: v1.28.0 + hooks: + - id: yamllint + files: \.(yaml|yml)$ + exclude: | + (?x)^( + \.cz| + vault + )\.(yaml|yml)$ + entry: yamllint --strict --config-file .yamllint.yml + + - repo: local + hooks: + - id: ansible-lint + name: running ansible-lint + language: system + files: \.(yaml|yml)$ + pass_filenames: false + entry: ansible-lint --force-color --config-file .ansible-lint.yml + + - id: "lint-shellscript-templates" + name: lint shellscript templates + language: system + files: \.sh\.j2$ + entry: shellcheck -x --exclude=1009,1054,1056,1072,1073,1083,2001,2148 + + - id: "lint-shellscripts" + name: lint shellscripts + language: system + files: \.sh$ + entry: shellcheck -x --exclude=2001,2148 + + - id: "run-shellscripts-bats-tests" + name: run bats unit tests + language: system + files: \.bats$ + entry: bats -t + + - id: "ansible-encryption-check" + name: Ansible Encryption Check + language: system + entry: scripts/ansible-vault-precommit.sh + files: vault\.ya?ml$ diff --git a/default_variables.yml b/default_variables.yml index 36aedf9..125bb20 100644 --- a/default_variables.yml +++ b/default_variables.yml @@ -1,6 +1,6 @@ --- # ---------------------------------- SOFTWARE VERSIONS -authelia_version: 4.36.9 +authelia_version: 4.37.0 consul_version: 1.13.2 influxdb_version: 1.8.10 nomad_version: 1.4.1 @@ -20,24 +20,24 @@ tdarr_webui_port: "8265" # ---------------------------------- DIRECTORIES FOR SERVICE LOCAL STORAGE # These folders must be created, even if empty, to allow mounting nomad local storage end-points service_localfs_dirs: - - influxdb - - lidarr - - prowlarr - - radarr - - sonarr - - uptimekuma + - influxdb + - lidarr + - prowlarr + - radarr + - sonarr + - uptimekuma # ---------------------------------- SHARED FILE STORAGE rpi_usb_drive_mount_point: /mnt/usbDrive rpi_localfs_service_storage: "{{ rpi_usb_drive_mount_point }}/docker" rpi_nfs_mount_point: /mnt rpi_nfs_mounts_list: - - { local: "{{ rpi_nfs_mount_point }}/pi-cluster", src: "10.0.30.6:/volume1/pi-cluster" } - - { local: "{{ rpi_nfs_mount_point }}/syncthing", src: "10.0.30.6:/volume1/syncthing" } - - { local: "{{ rpi_nfs_mount_point }}/media", src: "10.0.30.6:/volume1/media" } - - { local: "{{ rpi_nfs_mount_point }}/nate", src: "10.0.30.6:/volume1/nate" } + - { local: "{{ rpi_nfs_mount_point }}/pi-cluster", src: "10.0.30.6:/volume1/pi-cluster" } + - { local: "{{ rpi_nfs_mount_point }}/syncthing", src: "10.0.30.6:/volume1/syncthing" } + - { local: "{{ rpi_nfs_mount_point }}/media", src: "10.0.30.6:/volume1/media" } + - { local: "{{ rpi_nfs_mount_point }}/nate", src: "10.0.30.6:/volume1/nate" } rpi_nfs_mounts_remove: - - { local: "{{ rpi_nfs_mount_point }}/downloads", src: "10.0.30.6:/volume1/downloads" } + - { local: "{{ rpi_nfs_mount_point }}/downloads", src: "10.0.30.6:/volume1/downloads" } # mac_autofs_type is one of 'smb,nfs,afp' mac_autofs_type: smb @@ -45,23 +45,23 @@ mac_localfs_service_storage: "/Users/{{ ansible_user }}/Library/docker" mac_storage_mount_point: /System/Volumes/Data/mnt mac_keep_alive_file: "{{ mac_storage_mount_point }}/pi-cluster/keepalive.txt" mac_nfs_mounts_list: - - { local: "{{ mac_storage_mount_point }}/pi-cluster", src: "10.0.0.6:/volume1/pi-cluster" } - - { local: "{{ mac_storage_mount_point }}/syncthing", src: "10.0.0.6:/volume1/syncthing" } - - { local: "{{ mac_storage_mount_point }}/media", src: "10.0.0.6:/volume1/media" } - - { local: "{{ mac_storage_mount_point }}/nate", src: "10.0.0.6:/volume1/nate" } + - { local: "{{ mac_storage_mount_point }}/pi-cluster", src: "10.0.0.6:/volume1/pi-cluster" } + - { local: "{{ mac_storage_mount_point }}/syncthing", src: "10.0.0.6:/volume1/syncthing" } + - { local: "{{ mac_storage_mount_point }}/media", src: "10.0.0.6:/volume1/media" } + - { local: "{{ mac_storage_mount_point }}/nate", src: "10.0.0.6:/volume1/nate" } # Add mounts to remove from auto_nfs to the dict below if needed mac_nfs_mounts_remove: - # - { local: "{{ mac_storage_mount_point }}/pi-cluster", src: "10.0.0.6:/volume1/pi-cluster" } + # - { local: "{{ mac_storage_mount_point }}/pi-cluster", src: "10.0.0.6:/volume1/pi-cluster" } mac_afp_or_smb_mounts_list: - - { local: "{{ mac_storage_mount_point }}/pi-cluster", src: "10.0.0.6:/pi-cluster" } - - { local: "{{ mac_storage_mount_point }}/syncthing", src: "10.0.0.6:/syncthing" } - - { local: "{{ mac_storage_mount_point }}/media", src: "10.0.0.6:/media" } - - { local: "{{ mac_storage_mount_point }}/nate", src: "10.0.0.6:/nate" } + - { local: "{{ mac_storage_mount_point }}/pi-cluster", src: "10.0.0.6:/pi-cluster" } + - { local: "{{ mac_storage_mount_point }}/syncthing", src: "10.0.0.6:/syncthing" } + - { local: "{{ mac_storage_mount_point }}/media", src: "10.0.0.6:/media" } + - { local: "{{ mac_storage_mount_point }}/nate", src: "10.0.0.6:/nate" } mac_afp_or_smb_mounts_remove: - # - { local: "{{ mac_storage_mount_point }}/pi-cluster", src: "10.0.0.6:/pi-cluster" } + # - { local: "{{ mac_storage_mount_point }}/pi-cluster", src: "10.0.0.6:/pi-cluster" } # ---------------------------------- SERVICE CONFIGURATION VARIABLES @@ -81,83 +81,83 @@ mac_tdarr_file_location: "/Users/{{ ansible_user }}/Library/tdarr" # ---------------------------------- PACKAGES apt_packages_list: - - bc - - coreutils - - curl - - dnsutils - - exa - - fzf - - git - - git-extras - - htop - - iftop - - iotop - - iperf - - jq - - less - - lnav - - logrotate - - lsof - - nano - - net-tools - - nmap - - openssh-server - - p7zip-full - - python3-pip - - rsync - - shellcheck - - unzip - - wget - - yamllint - - zsh + - bc + - coreutils + - curl + - dnsutils + - exa + - fzf + - git + - git-extras + - htop + - iftop + - iotop + - iperf + - jq + - less + - lnav + - logrotate + - lsof + - nano + - net-tools + - nmap + - openssh-server + - p7zip-full + - python3-pip + - rsync + - shellcheck + - unzip + - wget + - yamllint + - zsh homebrew_package_list: - - ansible - - ansible-lint - - bash - - bash-completion - - bashdb - - bat - - bats-core - - coreutils - - diff-so-fancy - - exa - - ffmpeg - - findutils - - fping - - fzf - - gawk - - git - - git-extras - - git-flow - - gnu-sed - - gnu-tar - - gnutls - - gpg - - grep - - handbrake - - htop - - httpie - - iperf - - jq - - nano - - ncurses - - nmap - - openssl - - pandoc - - prettier - - readline - - shellcheck - - shfmt - - source-highlight - - sqlite - - ssh-copy-id - - tealdeer - - tree - - wget - - yamllint - - zsh + - ansible + - ansible-lint + - bash + - bash-completion + - bashdb + - bat + - bats-core + - coreutils + - diff-so-fancy + - exa + - ffmpeg + - findutils + - fping + - fzf + - gawk + - git + - git-extras + - git-flow + - gnu-sed + - gnu-tar + - gnutls + - gpg + - grep + - handbrake + - htop + - httpie + - iperf + - jq + - nano + - ncurses + - nmap + - openssl + - pandoc + - prettier + - readline + - shellcheck + - shfmt + - source-highlight + - sqlite + - ssh-copy-id + - tealdeer + - tree + - wget + - yamllint + - zsh homebrew_cask_install_dir: /Applications homebrew_casks_list: - - lingon-x + - lingon-x diff --git a/scripts/ansible-vault-precommit.sh b/scripts/ansible-vault-precommit.sh new file mode 100755 index 0000000..d45717f --- /dev/null +++ b/scripts/ansible-vault-precommit.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +if ! GITROOT=$(git rev-parse --show-toplevel 2>/dev/null); then + error "We do not seem to be running in a git repository" + _safeExit_ 1 +fi + +FILES_PATTERN='.*vault.*\.ya?ml$' +REQUIRED='ANSIBLE_VAULT' + +EXIT_STATUS=0 +wipe="\033[1m\033[0m" +yellow='\033[1;33m' +# carriage return hack. Leave it on 2 lines. +cr=' +' +for f in $(git diff --cached --name-only | grep -E "${FILES_PATTERN}"); do + # test for the presence of the required bit. + MATCH="$(head -n1 "${GITROOT}/${f}" | grep --no-messages "${REQUIRED}")" + echo "$MATCH" + if [ ! "${MATCH}" ]; then + # Build the list of unencrypted files if any + UNENCRYPTED_FILES="${f}${cr}${UNENCRYPTED_FILES}" + EXIT_STATUS=1 + fi +done +if [ ! $EXIT_STATUS = 0 ]; then + echo '# COMMIT REJECTED' + echo '# Looks like unencrypted ansible-vault files are part of the commit:' + echo '#' + while read -r line; do + if [ -n "${line}" ]; then + echo -e "#\t${yellow}unencrypted: ${line}${wipe}" + fi + done <<<"${UNENCRYPTED_FILES}" + echo '#' + echo "# Please encrypt them with 'ansible-vault encrypt '" + echo "# (or force the commit with '--no-verify')." + exit $EXIT_STATUS +fi +exit $EXIT_STATUS diff --git a/vault.yml b/vault.yml index 7a48898..d2a467a 100644 --- a/vault.yml +++ b/vault.yml @@ -1,118 +1,118 @@ $ANSIBLE_VAULT;1.1;AES256 -64663833313761613465336531333331376533306663343739643561356662393831386562646261 -6663623065373237633664376664623166383839353237350a323139343936303539626436643131 -33633735323264363833626331306464313862643932303330316166623666333530333430343565 -3063313964633161630a326637373363376464326330303936643563373038323337643533623562 -34636134646434356664653465663335616432343232343561383761343161663033623937643231 -30396137646630666134333432383632333535313735386434386161643835383032343965346436 -31613965396435376466656566313739306335346665656566303133626437353438316563663961 -61653838366463656134396365373832303834323037383332363233376332303735363738396362 -37306136633336386637633336333136323739666634356264383562383333643236373238323962 -61323231373036616137616138366465376530623565323939333835386361323335396434666131 -30316530393366376262363363353730393065306366313339313236313364303836376661313566 -31653837656530313632333331323461316666346431656235316436626631383762666166346463 -63333561326633663636623461663965376231313539353530633161613434363039363030663261 -61613062396135343939393965386637313234633935313462336366616530333134383334333739 -30353638646630353838663733306437663661386238376365373134343065316666336663393061 -62383539373035323933326161653836343437643932663263646634343333303765646631643836 -61306234663132313032343231386566346532386437343838653766306262613437653463313862 -33623339643539383737353830633561316634313633313634323634663335326363653162363330 -38653761636531643236313534643032633831666566376561313965663664626232326137373763 -64653135326230313034663832373534306664366433623039306630333735633263356263646530 -32346437353839633363646334316665643737353233333864663234363537343939383064383233 -31343763626561346631363135363762323135626630653630306134333030383836333164383431 -62623138333732303431303962323662356136323835623636386361333636636165333037636530 -61323738303536666566306566616535316338393561303532393965303439663238393330303332 -38303432313963393131393733333330353337616462363566666434616239343131613562393230 -66376437653461316664346436663164643066633137343830366565323131646235623631306638 -30393438626439333762323733623463623030353261653763396437396539316234363834666162 -66326437326666396333326564663730396565643436623037313737343134333337356630656464 -31663237663564376663633963393539383939303932353362333561653533313261656662623734 -34336166613135353930613635616330326664656566333635386436663035646539633738373036 -33313363333262396537613565393634336161386133386336343637633566303463643961616164 -61303563336538313563323431636636653634393232316132366562336438306336306530366462 -34393162633032633765613963393466643361643162393833383363396563663331313535643437 -64366434613364393564633236343363663137653532313566343163633765633264376165333135 -37363436646633383636316232336663363764366230363430643637663233313232386334303061 -63333662613365386437363466326538626463373966343737633563373832616332386364376632 -61346437343635653434656337333135653163373961666639643236616234336435393838653964 -33393533316164656437333866313636363762663366343136343732323435393731623731653438 -35616232376131353832326363363931366631376133303632353235393162613038386534326639 -61636336366464633365386462656135356138393630313864616566386336326538333765363162 -31616335666432336231663939303732346364313930623765656631323737623566383334363930 -63353633356230643139326233313333646239313361303263323537623437363663356234343166 -62363163646562336364373832643333333261323339653136306163383661316430663732333233 -61333035323131623765663163643931663533653439306139363436326566653435383837383738 -34636231326366626535663062393133643363663133663433303135303339323238616639666230 -32633331653633633763383131633836646333356161323262383763343263633935306134333038 -65363134306635363236396461316261363431353533356437363130313235633762613732346262 -62363663373738373831356432663232623133646538643962323736656638636661333834623166 -66363037373637303532373665613232313563373631366265386266366133373136626665626635 -31663737356232346537646462336138333530336637623961613961346365656338343537376334 -37356461636530343963313564393864383437636566393232626539336531636432613964636564 -66333833616662626233623031353531343236303935316565353939316634656561323234623563 -64336436336165336666653533336233623965373438336365323738666330656666613033613364 -37346138336239316438383739343239656332376236366639656431383031626463323936373539 -35383839656561333536623633336233316265343932326539323161363064646239373934656264 -30653032653864303231336630343039343734343263373465656339313162373861633339616338 -37393230313231313563323961353233383337616432353164366139333064306535353333666438 -65363933333231306661396639313635366531323936333534343139626637663936303734383666 -66373862666437646235616430636330663633626438633234346239666566336464643037353661 -65646333343434613637636663623765306361303735633137376666393465376132616435646664 -36666337626339313265613239663365333961366131613533313062643034343930343661643031 -35306337313366363136396534653035383864666533336639353865653131376161346436313834 -38333033313233643739653966633166306231396639356331643031393864616464376537393436 -32313363643132666665396437313463373837623937346230653965626631363639646537316631 -36396536333266323738366535393866313139636439663066373239636564663064306463393265 -38313832356638323034303133316431356531376261616261373930303065306435386466393432 -32303862316166323938363739336161313666363935646632396136633833616330323462363035 -36393963653137323737383635343366383735383635623931333532343534646435656431383034 -61663937336534653438353662336632326465636262623530346333323630316435323739373531 -31626630343537393333383537326330383834666262633261356565646134643565666639616163 -39343237323933663866373539356435613962636663323533386362363236333236386139623536 -33303964313037353330323566626536663634343863366362666262666638393633333762343466 -63396264366537316637373231643338393431316138653430386366656433306537656236666635 -63643337623263363637646432396363616138623264393435306339323531613835623262356338 -63353564353666363734643563666231363338623138366232393963303139363464666265626236 -66643533366563303137373032636236353535356432653334363561626663633738303266666237 -36336465386537353732666533386131656663633962663966313064636130393565363639616332 -66633933623434323836666665656135393863333032323665363634313764333038323366663462 -62356561613462613439643333386533646234613432343430336231663430386339343830613839 -33666230373962386536336530646165396437303838613132303032303538366530633964636135 -31323264376230626438623635396339346666363134343965626336326530343531666261616136 -39323532643532633662373537346366636636633133623563656330383330343361616464363031 -31313263303764666339613639353830646231633161633733653233356330353466333734346233 -33666231386562333538623965363130396434316539316536333733636431656637663965303963 -61623962643236386266396539653934323935623361323535396238636163366639303261656362 -37646239333539303831636335636432393964363165306139633932343663373264313839336662 -37383464643332313733393734326264663732396538666464346261316261653931353161646133 -34343763396566666139376463656633646436343033333431386461376431303634333864313961 -33323837613537613762376464643161313964646661613135323235356633366534383566336430 -33373536656231623332383934383835353833626432626133633938376430383965633536656239 -32636632326661363135376661633165313065333433663832333866623130613666663865343965 -37623232363134303662653335613935376332353163356630653430336531643538303862623530 -35623531376339663136356231643130353130316137383764656438663862306132353364316163 -64313534636562613365656134396461636233666363396533353865323566663163383736633434 -31356635306636316262396537353433393038633635653230396437663337323831396631393739 -64333432303639396436326330613137383332386534356666616335313835383466623561633261 -65356130646230376232633938393066303362653231643435613261363638633232616630363365 -64626463353766366561666262366232303463383332363831343131336131633461346139333661 -32613562373233393564636466373430333734656565343534393765326434646531363864326334 -35366230326465633339323236383762316665656138663432336339343935353431316530666536 -30626334376535326137663831623462333030633561333439303230303966626337383861366564 -33626266366539356533626166313330303535396239656162366635633634656134393463356564 -64633535333933313036316336666430373864393134366664633139343532386264373438303234 -37653033396632646132616331393832393566373566363334343337626362646462636135383039 -36613135386538383735346637643764356431366261363933653861633262373163333834633533 -30363836616666643163633638373732336462396336303430363439336134313634393533373164 -32396362353433373439666538393132623938356434363061383931633035336461643131323935 -36386562373230663132326232653936653565303934333762383262316464633261363132323733 -61303234386361386434363936393063626633636138333837356232373732643932313962313530 -64396361333930303734363335353861313034643261313834343763333061623739653166666433 -38353330633936623731303638326464306665393030643834633835393031313463616430336263 -33336532666665343839363166386336643235386431373334326433303238306363613961393233 -30323964363730396133646363613930636238303233386130353936353934323931356332363262 -64336135383835643261383430343636633833386166626437333762656365336164636331326564 -37303765623736643430653934633935363231376330393130313538353235646232343035636664 -66356362616264393637323264353962306333613937363537326331633864663432376265326262 -6338 +30623635333734313065373830313834623163353536653638363735383739616430646139333663 +6164633533343764316539376636343435363037633838650a643166646263633162616132626238 +66663531616238373931663931333161633332643037303338323861396136306165313437316238 +6134653233656639640a653738393230666438396631303039393134393064353862306537363232 +33333634303163343838376633393635386532313230323938366561366464313135376135343636 +32613733663261653238633831316661326530346663313231386433323961613835666563346538 +36643433353436393435636135326637356261636263333435653561653037356238313230653030 +61613635393261363333306336316437333031356561356532396163396631643431373234373263 +38396464613133396262653032356662343537613136626133656234636232643139623366303664 +61626334306637323562663832643065636635396334386338393966343732623634386266373863 +35333330626137633439623434393662396463643363333965393933623663326432346166613638 +65663237616665333262656231613765326537396536386266396431623464376135643031616537 +37623239396431316461326163313165663638306437623265353761666361616233333836643131 +34363065373437303231343236633839323465363461313236303566363235356264633435613564 +33326665623161623062316435646334356164333265376131636162326131613962393036666533 +32613964393239353730656431383831366333323637353936373936616639616465643563653465 +30376665393136653738376534663534316236666434643437646635383634356132666361623762 +62306364363961303030383236616666623561376233636632373638306464323632323766303964 +64373232333661316339616135383038656135643365306435643437303136336430393237373236 +62383830653162623030353034623665313562366661633430313662353539323766373834333966 +66336137613161386537653038623664643333336534663231396363633132386136373835613539 +33386130366264633333373262393038356636653535303036313565336339316430333236333836 +31313264666534613138656564396436346632636163613162323338393834373434623238306235 +62386330316530333335646532623832653131323134646265613833393435333365326538633937 +66313630656662613234363962636136653132656361633437613932353234663463643339613866 +64333233396337343531666261613937306138373638356162666362643934383762663061623462 +61616338343137666531343663373862316239363839353734623432653438613265613532353334 +30333533653836333762316431383765333438313633663234656263343935313638363939386661 +36636532623562393265316665393534343965303565383631626465613931353436393737646538 +66626439363730666233626161353336623164323236386431643436396132363736646430336236 +30343736316234656330353766326236633465393236323938383964323430323539383039363631 +35366661373836316163363030626533373734633638316238656261396364646438336330653361 +66613166663238653338316430306638663231393662343933383136383632343062613032333066 +37613235313466306230316133333961666231666163323735326561343337306431363237643761 +30623162653865356238303835386362643062326236366162326237373661653436343663643736 +61306331656333333633353663393861633130336262346438626365646330646432663833623132 +65383465376131386266616566306564323334303862306531623032353163633038303838633634 +66396165633930363764663865333032346633393763646535356364303163343539323935363934 +61376637656562643836653835326236303838393738303833366663626264346464343332646163 +38386164666632623136313831643931623639633564356164633230346533376632363234346539 +63393564346536353837626265373962316332326530613833663135373932393665336430306363 +35333030373462653930393430623735376634636666633563643739313465643239313464343339 +39646134366461303661306631616666376434323238363337346163386662343034356365656433 +65653430393964623632336637366138353931633033366332666332643138616535353834323163 +66353037376435613237643337633632306238623838616166343766323065336263323530633130 +62366332333964346162366366616334313466383331326234626662343237353765633661333165 +33633735653661643636306262613137326334643663616436326437313039396635623434633636 +32373732333130376261663139643732313934373863633139376335313537353735343366653166 +37333039333932336537633831313531336663623965653637613532376165383336646336386438 +39613038376234356131396530613066323463386365313232336464636530653839636565396433 +64636435383630313037643432656539643437663532333165356436323234653732396265623033 +31326264663565383437613632306134303832363733333134303661633237316130346261306436 +33353965613830313766363830363865363137353238663832626337343135333732343733343564 +61613835623538356262653832613538313233346262633464386232643362643265663738633237 +62663132333737376432323232663436343232326365343638636666376530353234313064616365 +33633361363362653731306165666636353666613766343339623261633939656533613131363030 +62336233393566313737356533613936333863393833636464313266646265663331303766326361 +35303461383964363965313439613436323064663163336538636464353361643931326438393462 +61656638313236666465653162633831363964313365333764616231376137326333346534656138 +31663034303332383262373035323662373565336230633931316338333561366335336530653630 +34633265653665643936646365643166303535356266623732343234373465323637313539303938 +33653261306433643166336434623563333136386333303366633761613264333738613330633836 +39303538366161363831396635303265316566363035366562316439616339333163306234663365 +64346638366361383134663933643134613566616235356537353561363061393736313266623938 +30613462343065666437326333613861393538313534333238373432353832613465663230616431 +62343164613361323635353536353138313061303537353266313138666339613562386536346139 +64356266656232633836616161363731303761396635646262643635383766336661346530373834 +37386465633965346130643131663631373462316136333336383864616365306633643036393538 +64623662383337663961383039376365366139653662633165313063386562636266666365386336 +65383738633933663966626461613062346636303536333564623336613333656264306661613664 +66616635363166643334653064386537656530663631633465656234646161636138393531373830 +65633130393136333430336630366433376262396232613736313238663139353637316131376632 +63643636373532343730326662396465356339616163363035663564363766303134646462663639 +62333833303361373337613231373863316363643931373034633666656362363464313561363465 +39343066353333353466323030633761336636383334666264356662376338646561386263366533 +35646363313337393938643565653236353930336163356330363033373365346138343164393234 +36373963373463373538393835636234333964623361356238626132613338616538633465396635 +34356438386231376665373439646235626130646636653731366133373436373735373566356362 +63343663626337326631386564363336343162333039643363363630393039616165346535383332 +34306463326336656461383336373063356564353363656130356230303564346565656532653435 +33666664376638663838613061643938356230663565336130316635353131633862643363333361 +39383537323063666534626436383262393631656261326530643137323165616236373236653734 +32633161646139626166643134313662356230336366356165393734333130653663333437346230 +32613237333032353733383362643938363632376132323936396538643766353036306464613134 +32616531653963363338383132353334356662613236303262653934613932376431616263663861 +63346338323735313563303662623734396361323935613861303331313061326363616364386263 +31656132303835343866343962633866346330333765653430613633376361353966396532366639 +62373239363738633831326263653434656263326531323435386437356537636464333937316231 +31623361343966323133666633663137633565396464633266643263663430386237663464346461 +37366336383365353665666564663236633439343333386536306665343965616433333666313862 +39653866303932663533333431316636333665396266343065323636376139333739613337663539 +31306533396665376636353236363165613834366338653961666638643664383433636261363433 +35356330353239653962616436373430303735333837643032613738633561633132323538393538 +31376630376130323561656334306161373263373030636363633465663162613763613162353336 +38346264636130353437383333303766306337313631626562323666343338353961663439366132 +31356135666438633434383832396435353263316662313963303965386334646633646239313566 +62653538663566363164326361306133313536363265643636646235333461633566396535373865 +38663033623030653932386531653465653336383732346266353033306564353134646536363563 +65333839343764656662366330393562383264613864323837633265616463363964313935353231 +64633765613662303961396237316330323763663666386235326363643633336233613665353163 +36326331643139643935343461643737353463643133353066323133373139663564333063313164 +36353864396438363135323638393762343466666636633639343830303331343061666164386565 +65363866303233343163633432653433633333393065373863393061643930633233643762316665 +65383033623063333730656164623230373836313935353237306134616235396161393136616566 +36626535633962636661366137316337623233326631396663616163623765643131386235653962 +36643633663634626135653531303331313330333965383036373933396331633737343438613637 +33303933346533636134633364336533383862323961323262646261326264393931313836313538 +34656530343530386461366534396332646564373430333736396661353038623364646266366466 +38626436333936313834336132333834383566376664613161643664366164633636336566303665 +62663830666238623964646566346338383963306637393766346337653938616637636138633637 +61363064323463306637643733316166303066356165383236333066623936626433376438613035 +64363261343065633538656166353731646338346361333735376666643462363566393933656432 +66303132386435653665343535323862336132323830313063636337316536613238343331326337 +37393037383262393362396163343963326262366437336633366336646466313431656561636661 +36333066333536646165626632343464323365353233383638326639666432323039623730383934 +62656338636535306262353330653739363239623633616132373030383039386562663131343636 +6463