diff --git a/.bowerrc b/.bowerrc deleted file mode 100644 index 1f8ca89..0000000 --- a/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory": "static/lib" -} diff --git a/.editorconfig b/.editorconfig old mode 100644 new mode 100755 index 98e24cf..a257bcd --- a/.editorconfig +++ b/.editorconfig @@ -1,10 +1,14 @@ -; See http://EditorConfig.org for supported IDEs +; https://editorconfig.org -root = true ; top-most EditorConfig file +root = true [*] -indent_style = tab -indent_size = 4 charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space trim_trailing_whitespace = true insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes index dcbaa66..6313b56 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,24 +1 @@ -# Set default behaviour, in case users don't have core.autocrlf set. -* text=auto - -# These files are text and should be normalized (convert crlf => lf) -*.js text -*.css text -*.scss text -*.html text -*.php text -*.inc text -*.xml text -*.txt text -*.md text -*.properties text -*.rb text -*.yml text - - -# Images should be treated as binary -# (binary is a macro for -text -diff) -*.png binary -*.jpeg binary -*.gif binary -*.jar binary +* text=auto eol=lf diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index b097f6a..14956a5 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -2,11 +2,11 @@ name: github pages on: push: branches: - - master + - main jobs: deploy: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 with: @@ -16,20 +16,24 @@ jobs: - name: Setup Hugo uses: peaceiris/actions-hugo@v2 with: - hugo-version: '0.78.2' + hugo-version: '0.115.2' extended: true - name: Setup Node uses: actions/setup-node@v1 with: - node-version: '12.x' + node-version: '16.x' + + - name: Cache dependencies + uses: actions/cache@v1 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - run: npm install - - run: | - cd themes - git clone https://github.com/MunifTanjim/minimo.git minimo - cd minimo - git checkout v2.9.0 - cd ../.. - name: Build run: hugo --minify diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 9bd26a0..ff04c7c --- a/.gitignore +++ b/.gitignore @@ -1,33 +1,9 @@ -# Project -nbproject/* -.sass-cache/ +node_modules/ +public/ +resources/ .DS_Store -jruby*.jar -dist/* -build/lib/vendors* -build/lib/jruby-compiled -_notes/ - -# OS generated files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ethumbs.db -Thumbs.db -/.project -jshint.out.xml -csslint.out.xml -*.orig -*.tmp -*.log -_site/* -vendor/ - -# Hugo -Hugo -public -themes/minimo -static/lib -static/video +.hugo_build.lock +_vendor +hugo_stats.json +package-lock.json +go.sum diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..873eef9 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,59 @@ +{ + "maxerr": 50, + "bitwise": true, + "camelcase": false, + "curly": true, + "eqeqeq": true, + "forin": true, + "freeze": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": false, + "noarg": true, + "noempty": true, + "nonbsp": true, + "nonew": true, + "plusplus": false, + "undef": true, + "unused": false, + "strict": true, + "maxparams": false, + "maxdepth": 4, + "maxstatements": false, + "maxcomplexity": false, + "maxlen": 400, + "browser": true, + "devel": true, + "asi": false, + "boss": false, + "debug": false, + "eqnull": false, + "es3": false, + "es5": false, + "esversion": 12, + "moz": false, + "evil": true, + "expr": true, + "funcscope": false, + "globalstrict": false, + "iterator": false, + "lastsemic": false, + "laxbreak": false, + "laxcomma": false, + "loopfunc": true, + "multistr": true, + "noyield": false, + "notypeof": false, + "proto": false, + "scripturl": false, + "shadow": false, + "sub": false, + "supernew": false, + "validthis": false, + "globals": { + "jQuery": false, + "google": false, + "$": false + } +} diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100755 index 0000000..96d65c6 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,11 @@ +{ + "MD001": false, + "MD003": false, + "MD013": false, + "MD024": false, + "MD025": false, + "MD029": false, + "MD033": false, + "MD034": false, + "MD036": false +} diff --git a/themes/.gitkeep b/.nojekyll similarity index 100% rename from themes/.gitkeep rename to .nojekyll diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..b009dfb --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +lts/* diff --git a/.prettierrc b/.prettierrc new file mode 100755 index 0000000..f222a15 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,12 @@ +{ + "overrides": [ + { + "files": ["*.html"], + "options": { + "parser": "go-template", + "goTemplateBracketSpacing": true, + "bracketSameLine": true + } + } + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..344f03f --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "bradlc.vscode-tailwindcss", + "budparr.language-hugo-vscode", + "tamasfe.even-better-toml" + ] +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..db4821d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,15 @@ +([Français](#code-de-conduite)) + +## Code of Conduct + +I follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). + +Please review before contributing issues, pull requests, or joining the GitHub organization. + +______________________ + +## Code de conduite + +Je suit le [Code de conduite de la CNCF](https://github.com/cncf/foundation/blob/main/code-of-conduct-languages/fr.md). + +Veuillez passer en revue avant de contribuer à des problèmes, des demandes d'extraction ou de rejoindre l'organisation GitHub. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5826cd3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,27 @@ +# Contributing + +([Français](#comment-contribuer)) + +## How to Contribute + +Feel free to propose changes by creating Pull Requests. If you don't have write access, editing a file will create a Fork of this project for you to save your proposed changes to. Submitting a change to a file will write it to a new Branch in your Fork, so you can send a Pull Request. + +If this is your first time contributing on GitHub, don't worry! Let us know if you have any questions. + +### Security + +**Do not post any security issues on the public repository!** See [SECURITY.md](SECURITY.md) + +______________________ + +## Comment contribuer + +Lorsque vous contribuez, veuillez également publier des commentaires et discuter des modifications que vous souhaitez apporter par l'entremise des enjeux (Issues). + +N'hésitez pas à proposer des modifications en créant des demandes de tirage (Pull Requests). Si vous n'avez pas accès au mode de rédaction, la modification d'un fichier créera une copie (Fork) de ce projet afin que vous puissiez enregistrer les modifications que vous proposez. Le fait de proposer une modification à un fichier l'écrira dans une nouvelle branche dans votre copie (Fork), de sorte que vous puissiez envoyer une demande de tirage (Pull Request). + +Si c'est la première fois que vous contribuez à GitHub, ne vous en faites pas! Faites-nous part de vos questions. + +### Sécurité + +**Ne publiez aucun problème de sécurité sur le dépôt publique!** Voir [SECURITY.md](SECURITY.md) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4e65345 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM klakegg/hugo:ext-alpine + +RUN apk add git && \ + git config --global --add safe.directory /src diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..afe1ffe --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,19 @@ +Copyright (c) 2023 William Hearn using Hugoplate template built by Zeon Studio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile deleted file mode 100644 index d0a963b..0000000 --- a/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -NAME = sylus.github.io -VERSION = 1.0.0 - -all: build - -dev: - ./dev.sh - -deploy: - ./deploy.sh diff --git a/README.md b/README.md index 0234daa..b562435 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,76 @@ -Dev Ops -======= +# Website -[![Build Status][ci-badge]][ci] - -Personal website built using Hugo. +Personal website built using [Hugo]. ## Configuration -Please consult the Minimo theme [README.md][readme] file for further instructions. +Please consult the Minimo theme documentation for further instructions. + +## Installing Hugo + +This repository is presently using the Hugo extended 0.101.0 version. + +## Cloning + +The following will give you a project that is set up and ready to use. + +The `hugo server` command builds and serves the site. + +If you just want to build the site, run `hugo` instead. + +```sh +git clone https://github.com/sylus/website +cd website +./dev.sh +``` + +The theme is included as part of a Hugo module: + +```sh +▶ hugo mod graph +project github.com/zeon-studio/hugoplate@v0.0.0-20230718073735-8df5a7032e3d+vendor +project github.com/gethugothemes/hugo-modules/search@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/pwa@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/images@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/videos@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/icons/font-awesome@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/gzip-caching@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/adsense@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/accordion@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/table-of-contents@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/tab@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/modal@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/gallery-slider@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/components/preloader@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/components/social-share@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/components/cookie-consent@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/components/custom-script@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/components/render-link@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/shortcodes/button@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/shortcodes/notice@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/seo-tools/basic-seo@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/seo-tools/site-verifications@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +project github.com/gethugothemes/hugo-modules/seo-tools/google-tag-manager@v0.0.0-20230705095442-1f2d5ac8b18d+vendor +``` + +If you want to do SCSS edits and want to publish these, you need to install `PostCSS` (not needed for `hugo server`): + +```sh +npm install +``` + +## Running the website locally + +Once you've cloned the site repo, from the repo root folder, run: + +```sh +./dev.sh +``` + +## Linting - +To lint all Markdown files in a Node.js project (excluding dependencies), the following commands might be used: -[readme]: https://github.com/MunifTanjim/minimo/blob/master/README.md -[ci]: https://travis-ci.org/sylus/website -[ci-badge]: https://travis-ci.org/sylus/website.svg?branch=master +```sh +markdownlint 'content/**/*.md' --ignore node_modules --fix +``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..3ae646e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +([Français](#sécurité)) + +## Security + +**Do not post any security issues on the public repository!** Security vulnerabilities must be reported by email to `sylus1984@gmail.com` + +______________________ + +## Sécurité + +**Ne publiez aucun problème de sécurité sur le dépôt publique!** Les vulnérabilités de sécurité doivent être signalées par courriel à `sylus1984@gmail.com` diff --git a/archetypes/default.md b/archetypes/default.md deleted file mode 100644 index 2257f3f..0000000 --- a/archetypes/default.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -date: "{{ .Date }}" -title: "{{ replace .TranslationBaseName '-' ' ' | title }}" -authors: [] -categories: - - -tags: - - -draft: true ---- diff --git a/archetypes/page.md b/archetypes/page.md deleted file mode 100644 index ab4a6ac..0000000 --- a/archetypes/page.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -comments: false -menu: main ---- diff --git a/assets/images/404.png b/assets/images/404.png new file mode 100755 index 0000000..fd799aa Binary files /dev/null and b/assets/images/404.png differ diff --git a/assets/images/avatar-md.png b/assets/images/avatar-md.png new file mode 100644 index 0000000..387b035 Binary files /dev/null and b/assets/images/avatar-md.png differ diff --git a/assets/images/avatar-sm.png b/assets/images/avatar-sm.png new file mode 100644 index 0000000..e1699dc Binary files /dev/null and b/assets/images/avatar-sm.png differ diff --git a/assets/images/avatar.png b/assets/images/avatar.png new file mode 100644 index 0000000..c2ea2ec Binary files /dev/null and b/assets/images/avatar.png differ diff --git a/assets/images/banner.png b/assets/images/banner.png new file mode 100644 index 0000000..5f8a9e8 Binary files /dev/null and b/assets/images/banner.png differ diff --git a/assets/images/call-to-action.png b/assets/images/call-to-action.png new file mode 100755 index 0000000..ec2d625 Binary files /dev/null and b/assets/images/call-to-action.png differ diff --git a/assets/images/cloud-architecture.png b/assets/images/cloud-architecture.png new file mode 100755 index 0000000..6d4d5f4 Binary files /dev/null and b/assets/images/cloud-architecture.png differ diff --git a/assets/images/cloud-native-solutions.png b/assets/images/cloud-native-solutions.png new file mode 100755 index 0000000..8b30644 Binary files /dev/null and b/assets/images/cloud-native-solutions.png differ diff --git a/assets/images/data-science.png b/assets/images/data-science.png new file mode 100644 index 0000000..e51efbb Binary files /dev/null and b/assets/images/data-science.png differ diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000..caf8b32 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/images/gallery/01.jpg b/assets/images/gallery/01.jpg new file mode 100644 index 0000000..662fc1d Binary files /dev/null and b/assets/images/gallery/01.jpg differ diff --git a/assets/images/gallery/02.jpg b/assets/images/gallery/02.jpg new file mode 100644 index 0000000..22fb37f Binary files /dev/null and b/assets/images/gallery/02.jpg differ diff --git a/assets/images/gallery/03.jpg b/assets/images/gallery/03.jpg new file mode 100644 index 0000000..cea735f Binary files /dev/null and b/assets/images/gallery/03.jpg differ diff --git a/assets/images/gallery/04.jpg b/assets/images/gallery/04.jpg new file mode 100644 index 0000000..48d7c32 Binary files /dev/null and b/assets/images/gallery/04.jpg differ diff --git a/assets/images/gallery/05.jpg b/assets/images/gallery/05.jpg new file mode 100644 index 0000000..0987809 Binary files /dev/null and b/assets/images/gallery/05.jpg differ diff --git a/assets/images/gallery/06.jpg b/assets/images/gallery/06.jpg new file mode 100644 index 0000000..662fc1d Binary files /dev/null and b/assets/images/gallery/06.jpg differ diff --git a/assets/images/image-placeholder.png b/assets/images/image-placeholder.png new file mode 100755 index 0000000..a61a0c0 Binary files /dev/null and b/assets/images/image-placeholder.png differ diff --git a/assets/images/logo-darkmode.png b/assets/images/logo-darkmode.png new file mode 100644 index 0000000..c5d29d5 Binary files /dev/null and b/assets/images/logo-darkmode.png differ diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000..cbca1a2 Binary files /dev/null and b/assets/images/logo.png differ diff --git a/assets/images/no-search-found.png b/assets/images/no-search-found.png new file mode 100755 index 0000000..1e1e6e1 Binary files /dev/null and b/assets/images/no-search-found.png differ diff --git a/assets/scss/custom.scss b/assets/scss/custom.scss new file mode 100755 index 0000000..9685904 --- /dev/null +++ b/assets/scss/custom.scss @@ -0,0 +1 @@ +// Add your own custom styles here diff --git a/config.toml b/config.toml deleted file mode 100644 index 5a5978e..0000000 --- a/config.toml +++ /dev/null @@ -1,115 +0,0 @@ -baseURL = "http://sylus.ca" -languageCode = "en-us" -title = "William Hearn" -# for smart copyright line, leave this blank and check [params.copyright] -copyright = "" - -theme = "minimo" - -disqusShortname = "" -googleAnalytics = "" - -Paginate = 5 -preserveTaxonomyNames = true - -enableRobotsTXT = true # generate robots.txt - -# Syntax Highlighting ( https://gohugo.io/content-management/syntax-highlighting/ ) -pygmentsCodefences = true - -enableEmoji = true - -# Missing translations will default to this content language -defaultContentLanguage = "en" - -[params.info] -description = "Technical Architect, Cloud Native Team @ Statistics Canada" -title404 = "Nothing's here!" - -[params.assets] -favicon = "favicon.ico" -customCSS = ["css/custom.css"] -customJS = ["js/custom.js"] -gopher = "images/gopher.png" # used in 404 template ( Generator: https://gopherize.me ) - -[params.copyright] -prefix = "" -holder = "Sylus" -startYear = "2017" -suffix = "" - -[params.settings] -# date & time format: https://golang.org/pkg/time/ -dateFormat = "2006, Jan 02" -listDateFormat = "2006, Jan 02" -archiveDateFormat = "Jan 02" -hideEntryNavigation = ["page"] # boolean / array of sections -showReadingTime = true -taxonomyCloudShuffle = true -accentColor = "#0db7ed" -hideMainMenu = false - -[params.sidebar] -enable = true - -[params.widgets] -homepage = ["recent_posts"] -sidebar = ["about","taxonomy_cloud"] -footer = ["social_menu"] - -[params.opengraph.facebook] -admins = [] # array of Facebook IDs -appID = "" -pageID = "" - -[params.opengraph.twitter] -page = "" # Twitter page username - -[params.seo] -# Title Separator: - – — · • * ⋆ | ~ « » < > -titleSeparator = "•" - -[params.social] -email = "william.hearn@canada.ca" -github = "sylus" -gitlab = "sylus" -linkedin = "william-hearn-78688227" -twitter = "william_hearn" - -[params.comments] -enable = false - -[params.comments.staticman] -enable = true -apiEndpoint = "https://api.staticman.net/v2/entry" -maxDepth = 2 - -[params.comments.staticman.github] -username = "sylus" -repository = "website" -branch = "master" - -[taxonomies] -author = "authors" -category = "categories" -series = "series" -tag = "tags" - -[permalinks] -page = "/:slug/" - -[[menu.main]] -name = "Portfolio" -weight = -10 -identifier = "portfolio" -url = "https://github.com/sylus" - -[blackfriday] -hrefTargetBlank = true - -[languages] -# edit this block for your own language -[languages.en] -lang = "en" -languageName = "English" -weight = 1 diff --git a/config/_default/config.toml b/config/_default/config.toml new file mode 100755 index 0000000..1ee83a2 --- /dev/null +++ b/config/_default/config.toml @@ -0,0 +1,129 @@ +######################## default configuration #################### +baseURL = "https://sylus.ca/" +title = "William Hearn" +timeZone = "America/New_York" +paginate = 5 # see https://gohugo.io/extras/pagination/ +summaryLength = 10 # see https://gohugo.io/content-management/excerpts/ +googleAnalytics = "" +disqusShortname = "" +disableLanguages = [] +hasCJKLanguage = false + +########################## Permalinks ############################ +[permalinks.page] +"pages" = "/:slugorfilename/" + +############################# Modules ############################ +[module] +[[module.mounts]] +source = "assets" +target = "assets" + +[[module.mounts]] +source = "hugo_stats.json" +target = "assets/watching/hugo_stats.json" + +############################# Build ############################## +[build] +noJSConfigInAssets = false +useResourceCacheWhen = 'fallback' +[build.buildStats] +enable = true +[[build.cachebusters]] +source = 'assets/.*\.(js|ts|jsx|tsx)' +target = '(js|scripts|javascript)' +[[build.cachebusters]] +source = 'assets/.*\.(css|sass|scss)$' +target = '(css|styles|scss|sass)' +[[build.cachebusters]] +source = '(postcss|tailwind)\.config\.js' +target = '(css|styles|scss|sass)' +[[build.cachebusters]] +source = 'assets/.*\.(.*)$' +target = '$1' + + +############################# Outputs ############################ +[outputs] +home = ["HTML", "RSS", "WebAppManifest", "SearchIndex"] + +############################# Imaging ############################ +[imaging] +# See https://github.com/disintegration/imaging +# Default JPEG or WebP quality setting. Default is 75. +quality = 90 +resampleFilter = "lanczos" + +############################ Caches ############################## +[caches] +[caches.images] +dir = ":resourceDir/_gen" +maxAge = "720h" + +[caches.assets] +dir = ":resourceDir/_gen" +maxAge = "720h" + +############################ Markup ############################## +[markup] +[markup.goldmark.renderer] +unsafe = true + +[markup.highlight] +style = 'monokai' # see https://xyproto.github.io/splash/docs/all.html + +[markup.tableOfContents] +startLevel = 2 +endLevel = 5 +ordered = true + +########################### Media types ########################### +[mediaTypes] +[mediaTypes."application/manifest+json"] +suffixes = ["webmanifest"] + +########################### Output Format ########################## +[outputFormats] +[outputFormats.WebAppManifest] +mediaType = "application/manifest+json" +rel = "manifest" + +[outputFormats.SearchIndex] +mediaType = "application/json" +baseName = "searchindex" +isPlainText = true +notAlternative = true + +############################# Plugins ############################## + +# CSS Plugins +[[params.plugins.css]] +link = "plugins/swiper/swiper-bundle.css" +[[params.plugins.css]] +link = "plugins/glightbox/glightbox.css" +[[params.plugins.css]] +link = "plugins/font-awesome/v6/brands.css" +[[params.plugins.css]] +link = "plugins/font-awesome/v6/solid.css" +[[params.plugins.css]] +link = "plugins/font-awesome/v6/icons.css" + +# JS Plugins +[[params.plugins.js]] +link = "js/search.js" +[[params.plugins.js]] +link = "plugins/swiper/swiper-bundle.js" +[[params.plugins.js]] +link = "plugins/glightbox/glightbox.js" +[[params.plugins.js]] +link = "js/gallery-slider.js" +[[params.plugins.js]] +link = "js/accordion.js" +[[params.plugins.js]] +link = "js/tab.js" +[[params.plugins.js]] +link = "js/modal.js" +[[params.plugins.js]] +link = "plugins/cookie.js" +[[params.plugins.js]] +link = "plugins/youtube-lite.js" diff --git a/config/_default/languages.toml b/config/_default/languages.toml new file mode 100755 index 0000000..e9f14df --- /dev/null +++ b/config/_default/languages.toml @@ -0,0 +1,6 @@ +################ English language ################## +[en] +languageName = "En" +languageCode = "en-ca" +contentDir = "content/english" +weight = 1 diff --git a/config/_default/menus.en.toml b/config/_default/menus.en.toml new file mode 100755 index 0000000..5a6231a --- /dev/null +++ b/config/_default/menus.en.toml @@ -0,0 +1,67 @@ +############# English navigation ############## + +# main menu +[[main]] +name = "Home" +url = "/" +weight = 1 + +[[main]] +name = "About" +url = "about/" +weight = 2 + +[[main]] +name = "Resume" +url = "resume/" +weight = 3 + +[[main]] +name = "Blog" +url = "blog/" +weight = 4 + +[[main]] +weight = 5 +name = "Other" +hasChildren = true + + [[main]] + parent = "Other" + name = "Contact" + url = "contact/" + + [[main]] + parent = "Other" + name = "Authors" + url = "authors/" + + [[main]] + parent = "Other" + name = "Search" + url = "search/" + + [[main]] + parent = "Other" + name = "Categories" + url = "categories/" + +[[footer]] +name = "About" +url = "about/" +weight = 1 + +[[footer]] +name = "Resume" +url = "resume/" +weight = 2 + +[[footer]] +name = "Blog" +url = "blog/" +weight = 3 + +[[footer]] +name = "Contact" +url = "contact/" +weight = 4 diff --git a/config/_default/module.toml b/config/_default/module.toml new file mode 100644 index 0000000..fc960d7 --- /dev/null +++ b/config/_default/module.toml @@ -0,0 +1,93 @@ +[hugoVersion] +extended = true +min = "0.115.2" + +[[imports]] +path = "github.com/zeon-studio/hugoplate" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/search" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/pwa" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/images" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/videos" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/icons/font-awesome" + +# [[imports]] +# path = "github.com/gethugothemes/hugo-modules/icons/themify-icons" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/gzip-caching" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/adsense" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/accordion" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/table-of-contents" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/tab" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/modal" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/gallery-slider" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/components/preloader" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/components/social-share" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/components/cookie-consent" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/components/custom-script" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/components/render-link" + +# [[imports]] +# path = "github.com/gethugothemes/hugo-modules/components/valine-comment" + +# [[imports]] +# path = "github.com/gethugothemes/hugo-modules/components/crisp-chat" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/shortcodes/button" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/shortcodes/notice" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/seo-tools/basic-seo" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/seo-tools/site-verifications" + +[[imports]] +path = "github.com/gethugothemes/hugo-modules/seo-tools/google-tag-manager" + +# [[imports]] +# path = "github.com/gethugothemes/hugo-modules/seo-tools/baidu-analytics" + +# [[imports]] +# path = "github.com/gethugothemes/hugo-modules/seo-tools/matomo-analytics" + +# [[imports]] +# path = "github.com/gethugothemes/hugo-modules/seo-tools/plausible-analytics" + +# [[imports]] +# path = "github.com/gethugothemes/hugo-modules/seo-tools/counter-analytics" diff --git a/config/_default/params.toml b/config/_default/params.toml new file mode 100755 index 0000000..385189e --- /dev/null +++ b/config/_default/params.toml @@ -0,0 +1,95 @@ +#################### default parameters ################################ +# favicon +favicon = "images/favicon.png" +# logo +logo = "images/logo.png" +logo_darkmode = "images/logo-darkmode.png" +# use `px` or `x` with logo_width, example: "100px". +# Note: logo_width is not work with .svg file +logo_width = "160px" +logo_height = "32px" +# if logo_webp set false, will not generate WEBP version of logo | default is true +logo_webp = true +# logo text will only show when logo is missing. +logo_text = "Hugoplate" +# navbar fixed to top +navbar_fixed = true +# theme-mode +theme_switcher = true +theme_default = "dark" # available options [light/dark/system] +# Main Sections +mainSections = ["blog"] +# contact form action +contact_form_action = "https://airform.io/sylus1984@gmail.com" # contact form works with [https://airform.io/] or [https://formspree.io] +# google tag manager, see https://developers.google.com/tag-manager/ +google_tag_manager = "" # example: G-XXXXXXXXXX +google_adsense = "" # example: ca-pub-xxxxxxxxxxxxxxxx +# custom script on header, example: custom_script= "" +custom_script = "" +# copyright +copyright = "Design by [Zeon Studio](https://zeon.studio)" + +# Preloader +[preloader] +enable = false +preloader = "" # use jpg, png, svg or gif format. + +# Navigation button +[navigation_button] +enable = true +label = "Contact" +link = "contact" + +# search +[search] +enable = true +primary_color = "#121212" +include_sections = ["blog"] +show_image = true +show_description = true +show_tags = true +show_categories = true + + +# seo meta data for OpenGraph / Twitter Card +[metadata] +keywords = ["sylus", "William Hearn"] +description = "Personal website of William Hearn" +author = "William Hearn" +image = "images/og-image.png" + +# site verifications +[site_verification] +google = "" # Your verification code +bing = "" # Your verification code +baidu = "" # Your verification code +facebook = "" # Your verification code +mastodon = "" # Your verification code + +# cookies +[cookies] +enable = false +expire_days = 60 +content = "This site uses cookies. By continuing to use this website, you agree to their use." +button = "I Accept" + +######################## sidebar widgets ######################### +[widgets] +sidebar = ["categories", "tags"] + + +# google map +[google_map] +enable = false +map_api_key = "AIzaSyCcABaamniA6OL5YvYSpB3pFMNrXwXnLwU" +map_latitude = "51.5223477" +map_longitude = "-0.1622023" +map_marker = "images/marker.png" + + +# Subscription +[subscription] +enable = false +# mailchimp subsciption +mailchimp_form_action = "https://gmail.us4.list-manage.com/subscribe/post?u=463ee871f45d2d93748e77cad&id=a0a2c6d074" # replace this url with yours +mailchimp_form_name = "b_463ee871f45d2d93748e77cad_a0a2c6d074" diff --git a/content/_index.md b/content/_index.md deleted file mode 100644 index 8b6f3df..0000000 --- a/content/_index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Home -menu: main -weight: -270 ---- -> Kubernetes is the new kernel. We can refer to it as a “cluster kernel” versus the typical operating system kernel. -> -- Jessie Frazelle diff --git a/content/authors/_index.md b/content/authors/_index.md deleted file mode 100644 index c19d421..0000000 --- a/content/authors/_index.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: Authors ---- diff --git a/content/blog/_index.md b/content/blog/_index.md deleted file mode 100644 index c10fa36..0000000 --- a/content/blog/_index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Blog -linkTitle: Blog -menu: main -weight: -250 -slug: blog ---- diff --git a/content/blog/aks-iac.md b/content/blog/aks-iac.md deleted file mode 100644 index 3be4474..0000000 --- a/content/blog/aks-iac.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -date: 2018-07-01T06:00:00+06:00 -lastmod: 2018-07-01T17:30:00+06:00 -title: "AKS: Infrastructure as Code" -authors: ["sylus"] -categories: - - kubernetes -tags: - - azure - - AKS -slug: aks/iac -draft: true ---- - -Azure Kubernetes Service (**AKS**) is now general availability (**GA**) and with that announcement a slew of longstanding issues have been addressed! Most importantly you can now leverage a custom vnet and RBAC with your cluster. These were key blockers for many organizations fully going down the AKS route as opposed to ACS Engine where you still have the master node under your purview. - -Notably the **AKS** workflow in the Azure portal has been noticably improved and for quick one-off installations I would recommend going that route. Of course for production type deployments always following the infrastructure as code methodology is indeed the way to go! The following templates setup a vnet in a separate "production" resource group where the subnet is given to the actual **AKS** deployment. - -* [AKS ARM templates][aks-arm] - -## Create VNET resources - -Specify the name of your resource group that the custom virtual network resides in. - -```sh -export AKS_VNET_RG=network-production-rg -export AKS_VNET_LOCATION=eastus -``` - -If the resouce group was not created beforehand you can simply create it now. - -```sh -az group create -n ${AKS_VNET_RG} \ - -l ${AKS_VNET_LOCATION} -``` - -We can now instantiate the custom vnet template that will create both a local and containers subnet. - -```sh -az group deployment create -n aks-managed-vnet \ - -g ${AKS_VNET_RG} \ - --template-file kube-vnet.json \ - --parameters kube-vnet.parameters.json -``` - -## Service Principal - -Grant the AKS cluster Service Principal access to our custom virtual network using the resource group. - -```sh -az ad sp create-for-rbac --name aks-sylus -``` - -```sh -az role assignment create --role=Contributor \ - --scope=/subscriptions/SUBSCRIPTION_ID/resourceGroups/${AKS_VNET_RG} \ - --assignee SP_CLIENTID -``` - -# Create AKS cluster - -In order to create a managed AKS cluster leveraging the custom vnet simply fill out the missing parameters in the `kube-managed.parameters.json` file. These parameters include but are not limited to the following: - -* dnsPrefix -* resourceName -* servicePrincipalClientId -* servicePrincipalClientSecret -* sshRSAPublicKey -* vnetSubnetID - -```sh -export AKS_RG=aks-sylus-rg -export AKS_NAME=aks-managed-sylus -``` - -```sh -az group create -n ${AKS_RG} \ - -l ${AKS_VNET_LOCATION} -``` - -```sh -az group deployment create -n ${AKS_NAME} \ - -g ${AKS_RG} \ - --template-file kube-managed.json \ - --parameters kube-managed.parameters.json -``` - -# Common Operations - -Establish the kubeconfig context and retrieve cluster configuration. - -```sh -export AKS_RG=aks-sylus-rg -export AKS_NAME=aks-managed-sylus -``` - -```sh -az aks get-credentials --resource-group ${AKS_RG} \ - --name ${AKS_NAME} -``` - -Similarly to `kubectl proxy` with launch and open the Kubernetes dashboard. - -```sh -az aks browse --resource-group ${AKS_RG} \ - --name ${AKS_NAME} -``` - -Will delete the managed cluster service alongside with all its dependencies. - -```sh -az group delete --name ${AKS_RG} -``` - -This will enable dev spaces for the deployed AKS cluster. - -```sh -az aks use-dev-spaces -g ${AKS_RG} \ - -n ${AKS_NAME} \ - -s develop/sylus -y -``` - - - -[aks-arm]: https://github.com/sylus/aks-iac diff --git a/content/blog/devsecops.md b/content/blog/devsecops.md deleted file mode 100644 index 4d98f73..0000000 --- a/content/blog/devsecops.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -date: "2018-07-18T22:00:27-04:00" -title: "DevSecOps" -authors: ["sylus"] -categories: - - devsecops -tags: - - Artifactory - - X-Ray - - Security -draft: true -slug: devsecops ---- - -## Artifactory - -* Package Search in a Universal Repository (name, tag, digest, wildcards) -* Supports majority of most common programming languages (TIOBE) -* PHP - * PHP Composer support / proxy and manage all packagist - * Host your internal packages and index files on private PHP Composer repositories -* C/C++ - * Support conan C/C++ packae manager - * Complete suite for management / distribution of binaries - * Close relationship to creator of conan - * Tight integration with visual studio -* CRAN / R - * Supports R packages using CRAN repositories - * Full control of deployment and resolution process of R packages - * Secure and Local Repositories - * Proxy Remote CRAN resources and cache downloaded R packages - * View R package metadata - * Deploy R sources and binaries directly in the UI - * Version management allowing to keep older versions of the packages - * Cross Zone Sharing enhancements -* Docker - * Ability to promote and copy images from one repo to another in CI/CD - * Different access privileges depending on the CI/CD peipeline - * Artifactory will scan all major package formats that are leveraged inside container - * Build information includes all layers including base layers, as well as dependencies -* Maven - * Resolves longstanding issues with Maven - * Project aggregation may lead to inconsistent deployment - * Context matters with enhanced metadata for every build - * Deployment flexibility and ability to run at different stages - * Keep your POM file tidy - * Cycle Time is too long - * Binaries and their build pipelines should be decoupled -* CI/CD - * Support all major CI's GitLab, Jenkins, VSTS, etc - * Collect exhaustive build information from builds - * Bill of Materials stored in artifactory - * Best practices for working with Docker in a CI Pipeline -* Kubernetes - * First class integration with Azure (AKS, ACS) etc - * Many demos showing a full CI/CD workflow - * Build a Java app using Gradle - * Get base image with all dependencies - * Get a product ready Docker image that’s ready to deploy - * Generate a Helm chart for the build with the specific build numbers needed to deploy it - * Pushed out to ACR and Deploy to AKS - * Helm - * Helm chart resolution to resolve and aggregate deps - * Control domains for search and resolution of artifacts - * Easy access to multiple repostiories - * Secure and private repositories with multiple layers of fine grained access controls - * Seperate your stable and incubator repositories - * Use SemVer2 versioning in your charts -* Supports stricter requirements with airgap solutions (no network, one-way connection) - -* Key Benefits: - * Reliable Access: Overcome network issues restricting you from being able to download / update packages. - * Optimized Build Process: Manage resource sharing within your organization to eliminate unnecessary network traffic. - * Full Support for Docker: Support all Docker Registry APIs providing security features needed by enterprise Docker users. - * Secure Solution: Enable controlled access through secure private PHP Composer repositories. - * Smart Search and Artifactory Query Language (AQL): Find the packages you need using advanced search tools - * Distribution and Sharing: Enable efficient distribution of proprietary packages to give developers access to the same package version, resolve dependencies, and seamlessly share proprietary code regardless of physical location. - * Maintenance and Monitoring: Keep an organized managed system with automatic, timed cleanup processes, eliminating old artifacts. - * Universal, End-to-End Solution: Developers use multiple protocols, build tools, packaging formats, and CI systems as part of software development, integration, and distribution processes. - -### Links - -* [Artifactory with an Air Gap][artifactory-airgap] -* [Benefits of Package Search][artifactory-search] -* [Code to Cloud with Artifactory and AKS][artifactory-aks] -* [Conan joins jFrog][artifactory-conan] -* [Docker Registries and Container Lifecycles in CI Pipelines][artifactory-docker-ci] -* [Packagist for PHP Development][artifactory-php] -* [Maven Deploy Plugin Trip Ups][artifactory-maven] -* [Whale parts in your Docker Registry][artifactory-docker] -* [Whitepaper on PHP Composer + Artifactory][artifactory-whitepaper-composer] -* [Accelerate R Package Development with Artifactory CRAN][artifactory-r] - -## X-Ray - -* Block the download of artifacts for which Xray has detected security vulnerabilities. -* Can manually set tiers to manually approve whether to release the artifact for download or not -* You can detect security vulnerabilities, performance issues across your application -* Be more reactive from the beginning of development always knowing the security of your application -* Xray is triggered to run a scan, and if any issues are detected, DevSec staff can get notified -* Issues graded via a severity level that you control and set custom alters for -* Clear messages given to developers through Rest API / UI -* X-Ray can show you all the licenses used in all indexed artifact -* Define watches to report on banned licenses being used in a project -* Docker - * Reduce security risk running large swatch of containers often based on open source - * All layers of an image will be scanned including the base layer - * Don't let applications with exploitable vulnerabilities slip undetected in pipeline - * Aqua integration with X-Ray provides end-to-end solution for securing containerized apps - * Continous image assurance makes it possible to enforce image usage policies throughout the pipeline - * Ensure only containers that are scanned and pass tests are allowed to be used -* CI/CD - * Automatically fail a CI build job if it uploaded a build with vulnerabilities to Artifactory - -### Links - -* [Blocking downloads with Artifactory / X-Ray][block-downloads] -* [Sweet Dreams with Open Source Licensing][xray-licenses] -* [Xray and Aqua Keeping Your Containers Safe][xray-aqua] -* [JFrog Xray CI/CD Integration][xray-cicd] - -## Migrating from Nexus - -* Migrate from Nexus using nexus2art migrator tool -* Friendly wizard like interface for transferring data -* Data can include instance repositories, artifacts, users, and settings -* Migrator is intelligent enough to validate all naming conventions and repo types -* Any kind of validation failure will be highlighted -* Below is the current full feature-set of nexus2art: - * Migrate users, groups, and privileges - * Migrate scheduled tasks - * Migrate other instance-wide settings - * Support paid Nexus features, such as custom metadata - * Modify virtual repository child lists - * Modify repository package types - * Remove the dependency on the JFrog CLI - * Obfuscate passwords in save files -* Case Study move to Artifactory OpenMRS - -### Links - -* [Migrating from Nexus to Artifactory][artifactory-migrate] -* [OpenMRS migrates to Artifactory][actifactory-migrate-openmrs] - - -Dev Part of DevSecOps (Secure DevOps) - -1) GitLab -2) Jenkins -3) Actors -4) How to maintain it -5) What releases like build / deploy -6) Syscon for each component and how this particular component behaves - - - -[artifactory-airgap]: https://jfrog.com/blog/using-artifactory-with-an-air-gap/ -[artifactory-aks]: https://jfrog.com/blog/from-code-to-cloud-with-jfrog-artifactory-and-azure-aks/ -[artifactory-conan]: https://jfrog.com/blog/conan-joins-jfrog/ -[artifactory-docker]: https://jfrog.com/blog/whale-parts-docker-registry/ -[artifactory-docker-ci]: https://jfrog.com/blog/docker-registries-container-lifecycles-artifactory-ci-pipelines/ -[artifactory-php]: https://jfrog.com/blog/using-satis-packagist-php-development-think-jfrog-artifactory/ -[artifactory-r]: https://jfrog.com/blog/accelerate-r-package-development-with-artifactory-cran-repositories/ -[artifactory-search]: https://jfrog.com/blog/blocking-downloads-with-artifactory-and-xray/ -[artifactory-maven]: https://jfrog.com/blog/dont-let-maven-deploy-plugin-trip-you/ -[artifactory-migrate]: https://jfrog.com/blog/migrate-nexus-artifactory-manage-binaries-better/ -[artifactory-migrate-openmrs]: https://jfrog.com/blog/openmrs-migrates-from-nexus-to-artifactory-and-bintray/ -[artifactory-whitepaper-composer]: https://jfrog.com/whitepaper/php-composer-9-benefits-of-using-a-binary-repository-manager/ -[block-downloads]: https://jfrog.com/blog/blocking-downloads-with-artifactory-and-xray/ -[xray-aqua]: https://jfrog.com/blog/xray-aqua-keeping-containers-safe-waters/ -[xray-cicd]: https://jfrog.com/blog/jfrog-xray-cicd-integration-keeping-builds-safe/ -[xray-licenses]: https://jfrog.com/blog/sweet-dreams-with-open-source-licensing/ diff --git a/content/blog/kops.md b/content/blog/kops.md deleted file mode 100644 index 1a660f2..0000000 --- a/content/blog/kops.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -date: "2019-02-03T23:41:45-05:00" -title: "Leveraging KOPS with Amazon" -authors: ["sylus"] -categories: - - kubernetes -tags: - - kops - - amazon -slug: kops ---- - -The following just documents how to quickly setup a Kubernetes cluster on Amazon infrastructure. - -Unfortunately EKS still needs a bit of work and since you still have to play for the managed control plane, there is very little downside to using KOPS to instantiate the infrastructure. - -## KOPS - -* Support for variety of Kubernetes features such as feature gates -* Auto provisoned nodes -* More flexibility over Kubernetes versions - -## Steps - -1) Proceed to account management screen and click "Programmatic Access Screen" -2) Set up the AWS cli against IAM - -```sh -aws sts get-session-token --serial-number --token-code -aws configure -``` - -3) Take the NS records and put in Azure DNS (NS)for cloud.statcan.ca and add the 4 - -```sh -ID=$(uuidgen) && aws route53 create-hosted-zone --profile mfa --name --caller-reference $ID | jq .DelegationSet.NameServers -``` - -4) Set up the initial IAM policies for the `kops` user - -```sh -aws iam create-group --profile mfa --group-name kops -aws iam attach-group-policy --profile mfa --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess --group-name kops -aws iam attach-group-policy --profile mfa --policy-arn arn:aws:iam::aws:policy/AmazonRoute53FullAccess --group-name kops -aws iam attach-group-policy --profile mfa --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess --group-name kops -aws iam attach-group-policy --profile mfa --policy-arn arn:aws:iam::aws:policy/IAMFullAccess --group-name kops -aws iam attach-group-policy --profile mfa --policy-arn arn:aws:iam::aws:policy/AmazonVPCFullAccess --group-name kops -aws iam create-user --profile mfa --user-name kops -aws iam add-user-to-group --profile mfa --user-name kops --group-name kops -aws iam create-access-key --profile mfa --user-name kops -``` - -5) Configure the S3 Bucket to store the etcd state - -```sh -aws s3api create-bucket --profile mfa --bucket k8s-state-store --region ca-central-1 --create-bucket-configuration LocationConstraint=ca-central-1 -aws s3api create-bucket --profile mfa --bucket k8s-managed-state-store --region ca-central-1 --create-bucket-configuration LocationConstraint=ca-central-1 -aws s3api put-bucket-versioning --profile mfa --bucket k8s-managed-state-store --versioning-configuration Status=Enabled --region ca-central-1 -aws s3api put-bucket-encryption --profile mfa --bucket k8s-managed-state-store --server-side-encryption-configuration '\{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}' -aws s3api put-bucket-encryption --profile mfa --bucket k8s-managed-state-store --server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}' -``` - -6) Instantiate the Kubernetes cluster - -```sh -export NAME= -export KOPS_STATE_STORE=s3://k8s-managed-state-store - -kops create cluster \ - --state=s3://k8s-inno-state-store \ - --zones ca-central-1a,ca-central-1b \ - --encrypt-etcd-storage \ - --master-size c4.large \ - --node-size m5.2xlarge \ - --ssh-public-key id_rsa.pub \ - ${NAME} - -kops edit cluster ${NAME} -additionalPolicies: -master: | - [ - { - "Effect": "Allow", - "Action": "iam:CreateServiceLinkedRole", - "Resource": "arn:aws:iam::*:role/aws-service-role/*" - }, - { - "Effect": "Allow", - "Action": [ - "ec2:DescribeAccountAttributes", - "ec2:DescribeInternetGateways" - ], - "Resource": "*" - } - ] - - kops update cluster ${NAME} --yes -``` - -7) Helm installation and configuration of Tiller - -```sh - -cat < Note: Windows Server 1709 / 1803 currently need patches to address issues with larger numbers of containers - -## Building Windows Containers - -* Nuget Restore -> Build solution -> Image build -> Package -> Deploy -* Common Core / Proper Dependency Handling -* Single repo for separate functionality - -### Kernel / User compatibility - -* Windows kernel major version needs to match -* If built on Windows Server 2019 then must run on same version -* Hyper-V isolation can run older containers on a newer node - -### NuGet - -* NuSpec -> NuGet -> Package.config -* Encrypted Key -* Common Core DLL's pulled in -* Don't store dependencies, only source code - -The NuGet.config file should have the following added to it if leveraging Artifactory: - -```sh - -``` - -Then to authenticate against Artifactory with the NuGet Encrypted API key, run the following command: - -```sh -nuget setapikey : -Source ArtifactoryNuGetV3 -``` - -To configure the NuGet Visual Studio Extension to use Artifactory, you need to add this repository as another Package Source under Nuget Package Manager. - -`Tools > NuGet Package Manager > Settings > Package Sources` - -1. Go to the "Package Manager Settings" -2. Add a custom name for the package source -3. Follow the steps mentioned above - -### *.csproj - -Depending on the project type and architecture you might need to modify the *.csproj file. - -* Support all types of builds (CLI, Visual Studio, Docker) -* In-place building + publishing profile -* No relative includes -* Only use assembly references needed - -### Configuration - -* Environment variables passed at runtime -* Proper overrides of web.config and related configuration -* Leverage KeyVault for connection strings, etc (easily change environments) - -Where possible you should never bake your configuration settings inside your docker container. A much better approach is to supply the transformation at the time of container startup. - -One of the biggest benefits of using containers is the ability to wrap your application and its dependencies into an immutable image. This image is built once and deployed to different environments as it progresses through a continuous delivery pipeline. - -As the application is deployed, we can then pass variables or mount configuration files into the container to configure it for the given environment. This ensures that we deploy the exact same application to production as the one tested in other environments. This approach also helps keep secrets out of container images. - -* [Legacy Support][legacy-support] -* [Configuration Builder][config-builder] - -### Dockerfile - -* Multi-stage build (keep final image small) -* Types of base images (sdk, runtime) -* Handling IIS webservers -* MSBuild Parameters and Publish Profiles - * Difference between msbuild and dotnet (cli) - * 4.6.1+ needs msbuild because dotnet doesn't compile -* Dockerignore (ensure Docker does all the building) - -```sh -bin -obj -packages -Dockerfile -Jenkinsfile -``` - -There are a variety of supported Windows environments: - -* Windows Server 2016 -* Windows Server 1803 -* Windows Server 1809 -* Windows Server 2019 - -For each of the Windows environments you have the supported targets: - -* dotnet (microsoft/dotnet-framework:4.7.2-runtime) -* aspnet (microsoft/aspnet:4.7.2 AS runtime) -* wcf (microsoft/wcf:4.7.2 AS runtime) - -The following is an example of a full Dockerfile for working with WCF. - -#### WCF - -```sh -# escape=` - -# WCF Server 1803 -FROM :443/docker-remote/microsoft/dotnet-framework:4.7.2-sdk AS build -WORKDIR /app - -# Build and package dotnet app -COPY . . -RUN nuget restore -RUN msbuild /p:Configuration=Release /p:DeployOnBuild=true /p:PublishProfile="FolderProfile.pubxml" - -# Multi stage build -FROM :443/docker-remote/microsoft/wcf:4.7.2-windowsservercore-1803 as runtime - -RUN MKDIR AuditLog -WORKDIR AuditLog -COPY --from=build /app/bin/Release/Publish ./ - -# Configuration of the new site in IIS -RUN powershell -NoProfile -Command ` - Import-module IISAdministration; ` - New-IISSite -Name "AuditLog" -PhysicalPath C:\Scripts\AuditLog -BindingInformation "*:83:" - -EXPOSE 83 -``` - -## Building Hybrid Clusters - -* Apps across multiple nodes + operating systems -* Same workflow regardless of node (api, kubectl, helm) -* Manage Linux from Windows, and Windows from Linux -* Same supporting container infrastructure (container registries) - -There are currently two ways to leverage Windows workloads in Kubernetes on Azure. For the purposes of this presentation we will only be talking about how to use AKS Engine. - -1. Leverage AKS-Engine -2. Manually add a Windows virtual machine to the cluster - -> Note: Managed AKS with Windows support should be general availability by the end of March 2019. - -### AKS Engine Cluster Definition - -The following snippet is the initial cluster definition we pass to AKS Engine in order for it to generate the complete ARM deployments along with the configuration that Kubernetes expects. - -```sh -{ - "apiVersion": "vlabs", - "properties": { - "orchestratorProfile": { - "orchestratorType": "Kubernetes", - "orchestratorRelease": "1.12", - "kubernetesConfig": { - "useManagedIdentity": true, - "userAssignedID": "k8s-prod-msi", - "enableDataEncryptionAtRest": true, - "enableEncryptionWithExternalKms": true, - "enableRbac": true, - "privateCluster": { - "enabled": true - }, - "networkPolicy": "azure", - "enableRbac": true, - "kubeletConfig": { - "--allow-privileged": "true", - "--anonymous-auth": "false", - "--max-pods": "60", - "--network-plugin": "cni" - }, - "gcHighThreshold":85, - "gcLowThreshold": 80, - "apiServerConfig": { - "--allow-privileged": "true", - "--anonymous-auth": "false", - }, - "addons": [ - { - "name": "tiller", - "enabled": true - }, - { - "name": "kubernetes-dashboard", - "enabled": true - }, - { - "name": "container-monitoring", - "enabled": true - }, - { - "name": "rescheduler", - "enabled" : true - } - ] - } - }, - "masterProfile": { - "count": 1, - "dnsPrefix": "k8s-aks-production", - "vmSize": "Standard_D4s_v3", - "OSDiskSizeGB": 200, - "vnetSubnetId": "/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/Container-Rz-Master", - "firstConsecutiveStaticIP": "XXX.XX.X.X", - "vnetCidr": "XXX.XX.X.X/24" - }, - "agentPoolProfiles": [ - { - "name": "linuxpool1", - "count": 2, - "customNodeLabels": { - "os": "linux" - }, - "vmSize": "Standard_D8s_v3", - "OSDiskSizeGB": 200, - "storageProfile" : "ManagedDisks", - "availabilityProfile": "AvailabilitySet", - "vnetSubnetId": "/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/" - }, - { - "name": "windowspool1", - "count": 1, - "customNodeLabels": { - "os": "windows" - }, - "osType": "Windows", - "vmSize": "Standard_D8s_v3", - "OSDiskSizeGB": 200, - "storageProfile" : "ManagedDisks", - "availabilityProfile": "AvailabilitySet", - "vnetSubnetId": "/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/" - } - ], - "linuxProfile": { - "adminUsername": "azureuser", - "ssh": { - "publicKeys": [ - { - "keyData": "ssh-rsa ..." - } - ] - } - }, - "windowsProfile": { - "adminUsername": "azureuser", - "adminPassword": "" - }, - "servicePrincipalProfile": { - "clientId": "", - "secret": "", - "objectId": "" - } - } -} -``` - -> Note: Remember to make sure that the Windows version you launch with is the same version you want to build and run your containers in. - -## Orchestration - -* Helm Chart - * How to write / generate a Helm Chart - * Showcase a few examples of what we wrote - -With our Windows Containers built and a Hybrid Kubernetes cluster instantiated we can now discuss how they can be successfully orchestrated in a distributed fashion. - -### Considerations - -* Need to consider where container runs if need a windows server node use node selector -* Option 1: Make use of taints and tolerations for Windows nodes -* Option 2: Update helm charts and yaml files -* Resource consumption: Need higher limits (300MB) as need windows background services -* AKS Engine: Issue with them where do they go, and check GitHub - -## Release Automation - -![DevOps Pipeline](/images/windows/cicd.png) - -* Draft: Generate your skeleton -* Helm: Package manager -* Jenkins: Build process and push to container registry - * How to write Jenkinsfile to support Windows + Linux - * JNLP for Windows + Linux and how to write - * Kubernetes Plugin (latest version) -* Octopus: Continuous deployment with business processes baked in -* Artifactory / XRay: Security scanning for containers against all known CVE's -* Securing cluster(s) - -### Jenkins - -We have built our own containerized Jenkins JNLP agent against Windows 1803 and using the `dotnet-framework` base image. - -Additionally leveraging chocolatey we have added a few other commonly required libraries. - -Repository: [windows-jnlp-agent-windows][windows-jnlp-agent-windows] - -#### Jenkinsfile - -The following is largely a fully complete `Jenkinsfile` tailored to work with Windows nodes inside a Kubernetes cluster. - -Simply replace the values below with your own configuration. - -> Note: We makes use of the Kubernetes plugin where you can specify the specific cluster you wish to run CI tests against. - -```sh -def label = "jnlp-win-${UUID.randomUUID().toString()}" - -podTemplate( - cloud: '', - serviceAccount: 'jenkins-jenkins - label: label, - yaml: """ -apiVersion: v1 -kind: Pod -metadata: - labels: - ci: windows-vs2017 -spec: - containers: - - name: jnlp - image: /jenkins/jenkins-ext:jnlp-windows - env: - - name: JENKINS_URL - value: 'https://' - - name: DOCKER_HOST - value: 'tcp://:2375' - tty: true - working: 'C:\\workspace\\' - tolerations: - - effect: NoSchedule - key: os - operator: Equal - value: windows - nodeSelector: - agentpool: ciwinpool1 -""" -) { - node (label) { - // Update the gitlab status to pending - // https://jenkins.io/doc/pipeline/steps/gitlab-plugin - updateGitlabCommitStatus name: 'build', state: 'pending' - - bat 'powershell.exe mkdir C:\\repo' - dir('C:\\repo\\') { - checkout scm - - withCredentials([usernamePassword(credentialsId: 'docker-pull', passwordVariable: 'pass', usernameVariable: 'user')]) { - bat "docker login -u ${user} -p ${pass} " - bat "docker build . -t / --no-cache" - bat "docker push /" - } - } - bat 'powershell.exe remove-item C:\\repo -recurse -force' - - // Update the gitlab status to success - updateGitlabCommitStatus name: 'build', state: 'success' - } -} -``` - -## Developer Perspective - -* Learn new scripting -* Learn groovy, powershell, azure cli -* Learn Docker + Visual Studio integration with docker -* Limit betwen DevOps + Docker and where the overlap is -* JenkinsFile or Similar CI -* Prepared to learn and read resources (we can provide) -* When should we go to .NET core - * Performance - * Speed of delivery - * Density of Node -* 3 types of Applications - * WCF - * Console - * ASP.Net Web Application (should evaluate for this) -* Showstoppers - * Related to Oracle (management DLL forget about .NET Core) - * Why don't move to managed, we are using UDT only support for this -* Once .NET core is achieved - * Is way less complex so it is encourage - * For new projects should try to do .NET core (simplicity and stability of CI) - * Our project PMDAC was successful for this (web.config improvements) - * For existing project should access this, as runs better in Linux - * Densely pack the node more and build time is dramatically reduced - * Integration with Visual Studio - * Debug container directly in IDE, breakpoint - * Tested with .NET Core - * First class integration in VSCode / Visual Studio -* Building application with Dockerfile - * If don't have automation process for deployment then use visual studio to generate - * We are doing it all in Dockerfile to keep all deps together - * Need to understand how to use it in CLI / multi staging - * Branching strategy, Layering - * Dependency on Windows 10, Docker for Windows (mandatory) (important) -* Agile Practies - * Useful to have CI first - * When starting to fix issues, we can fail fast, fail often to succeed - * CI will reduce time for developers later on - * We use Jenkins -> GitLab -* Separation of repositories - * Helps to focus on task at hand - * Faciliates a micro-services oriented approach - * Challenge to refactor that -* DevOps and Developer perspective -* Ask them which people are coming and there background so we can tailor discussion -* Single Page application / Razor / Angular etc -* Traefik (https) - -### Anti-Patterns - -Right now it is unavoidable that the developer needs to learn a bit about Docker / Kubernetes in order to successfully deploy a Windows application on Kubernetes. - -However this can technically be classified as a anti-pattern as we should strive for the developer workflow to remain consistent and for the orchestration to be seamless without any developer intervention. This is one of the main goals of the KNative initiative which brings serverless functionality to Kubernetes. - -## Debugging - -There a quite a few ways to debug Windows Containers and / or your Windows Node on Kubernetes. Additionally the problem scope differs based on the level of the stack where you are experience an issue. - -Always important to remember to start small and try to isolate the issue. - -Here is some useful information that might help you further troubleshoot. - -## Debugging Windows Container with Docker Compose - -Even though docker swarm is on the way out and being wholly replaced by Kubernetes, docker-compose itself on a single virtual machine can prove very useful in order to prove things work before deploying your applications into a distributed cluster. - -This is very useful for debugging as sometimes don't know where specific issues are in the cluster. - -* Network Connectivity / Latency as everything is local -* Issues with Service Endpoints / Timeouts -* Memory / CPU issues -* Number of Windows Containers - -Another important fact is that if everything works in a local docker-compose environment, it is a safe bet your containers should also work in a Kubernetes cluster with certain caveats applied. - -## Debugging Windows Nodes - -* How to debug Windows Containers -* SSH in to Windows Node -* How to read kubelet logs / journalctl -* Wireshark -* How to know which layer (Traefik) -* Often use a known working trivial example to test communication between services - * [Ping Service][pingsvc] - * [Pong Service][pongsvc] -* Known issues and how to patch Windows Nodes - -## Help - -There are quite a few places where you can get help with issues related to Windows and Kubernetes. We have had particular success in the Slack channel and GitHub issue queue when their was spefic problems related to our Windows node. - -* [Slack][slack] (#sig-windows / #sig-azure) -* [GC Message][gcmessage] -* [GitHub][aks-engine] -* Azure Premium Ticket - -> Note: Please remember to be concise with your issue and always reference your Kubernetes version, Windows Kernel version, and specific network configuration. - -## Referenced Links (In order they appear) - -* [Draft][draft] -* [Helm][helm] -* [Jenkins][jenkins] -* [Octopus][octopus] -* [Artifactory][artifactory] / [XRay][xray] -* [Legacy Support][legacy-support] -* [Configuration Builder][config-builder] -* [windows-jnlp-agent-windows][windows-jnlp-agent-windows] -* [Tutorial: Deploying Windows Apps with Kubernetes][tutorial-windows-kubernetes] -* [Slides: Deploying Windows Apps with Kubernetes][slides-windows-kubernetes] - - - -[aks-engine]: https://github.com/Azure/aks-engine -[artifactory]: https://jfrog.com/artifactory/ -[config-builder]: https://fluentbytes.com/override-classic-asp-net-web-config-configuration-settings-when-using-docker-containers/ -[draft]: https://draft.sh/ -[helm]: https://helm.sh/ -[gcmessage]: https://message.gccollab.ca/home -[jenkins]: https://jenkins.io/ -[legacy-support]: https://anthonychu.ca/post/aspnet-web-config-transforms-windows-containers-revisited/ -[octopus]: https://octopus.com -[pingsvc]: https://gitlab.k8s.cloud.statcan.ca/ndm-cloud-dotnetteam/pingservice -[pongsvc]: https://gitlab.k8s.cloud.statcan.ca/ndm-cloud-dotnetteam/pongservice -[tutorial-windows-kubernetes]: https://jfrog.com/blog/migrate-nexus-artifactory-manage-binaries-better/ -[slack]: https://kubernetes.slack.com -[slides-windows-kubernetes]: https://schd.ws/hosted_files/kccna18/1f/Deploying%20Windows%20Apps.pdf -[windows-jnlp-agent-windows]: https://github.com/sylus/jenkins-jnlp-agent-windows -[xray]: https://jfrog.com/xray/ diff --git a/content/english/_index.md b/content/english/_index.md new file mode 100755 index 0000000..c189fae --- /dev/null +++ b/content/english/_index.md @@ -0,0 +1,48 @@ +--- +# Banner +banner: + title: "Senior Cloud Architect, Cloud Native Team @ Statistics Canada" + content: "I am an Senior Cloud Architect / Open Source advocate working for the Government of Canada. My passions include empowering Project Teams / Solution Builders through the use of Cloud Native (CNCF) technologies." + image: "/images/banner.png" + button: + enable: true + label: "See my resume" + link: "/resume" + +# Features +features: + - title: "Cloud Native Solutions" + image: "/images/cloud-native-solutions.png" + content: "Through an insightful journey of continuous learning in the field of Cloud Native architecture, complemented by many years of hands-on development and operational experience, I have achieved an exceptional level of proficiency in the following areas:" + bulletpoints: + - "Designing a highly scalable and resilient cloud native platform using CNCF technologies." + - "Creating custom controllers and operators to better manage the needs of solution builders." + - "Implementing best practices for cloud security and cost optimization." + - "Leveraging GitOps using Argo CD to declaratively manage infrastructure and applications." + - "Improving the overalll Developer Experience and time to delivery" + button: + enable: false + + - title: "Cloud Architecture" + image: "/images/cloud-architecture.png" + content: "While I have experience in the big three clouds namely AWS, Azure and GKE, I have extensive experience in the Microsoft Azure cloud environment particularly in the following areas:" + bulletpoints: + - "Leveraging Azure Defender to ensure compliance with NIST and Canada PBMM's security policies" + - "Configuring and managing multiple Azure Kubernetes Service (AKS) clusters across the SDLC." + - "Utilization and configuration of Azure Active Directory (Azure AD) for identity and access management." + - "Obtaining Authorization to Operate (ATO) for critical Azure services, including Azure Kubernetes Service (AKS) and Managed Databases (MySQL, and PostgreSQL)." + button: + enable: false + + - title: "Data Science and AI / ML" + image: "/images/data-science.png" + content: "Through my collaboration with many data science teams at Statistics Canada, complemented by my work with the Advanced Analyitics Workspace, I have achieved a base level of proficiency in the following areas:" + bulletpoints: + - "Utilizing Kubeflow for scalable and reproducible machine learning workflows." + - "Orchestrating model training and hyperparameter tuning on Kubernetes." + - "Deploying machine learning models as microservices in production environments." + - "Creating customizable environments to work with data with user-controlled resource provisioning (custom CPU, GPU, RAM and storage)." + - "Managing notebook servers including Code Server, Ubuntu Desktop, R Studio, JupyterLab with Python, R, Julia and SAS" + button: + enable: false +--- diff --git a/content/english/authors/_index.md b/content/english/authors/_index.md new file mode 100644 index 0000000..62eae44 --- /dev/null +++ b/content/english/authors/_index.md @@ -0,0 +1,3 @@ +--- +title: "Authors" +--- diff --git a/content/english/authors/william-hearn.md b/content/english/authors/william-hearn.md new file mode 100644 index 0000000..d0ca3ab --- /dev/null +++ b/content/english/authors/william-hearn.md @@ -0,0 +1,12 @@ +--- +title: William Hearn +email: sylus1984@gmail.com +image: "/images/avatar.png" +description: Senior Technical Architect (IT-04), Cloud Native Team @ Statistics Canada +social: + twitter: https://twitter.com/william_hearn +--- + +I am an Senior Cloud Architect / Open Source advocate working for the Government of Canada. + +My passions include empowering Project Teams / Solution Builders through the use of Cloud Native (CNCF) technologies. diff --git a/content/english/blog/_index.md b/content/english/blog/_index.md new file mode 100755 index 0000000..17564a2 --- /dev/null +++ b/content/english/blog/_index.md @@ -0,0 +1,5 @@ +--- +title: "Blog Posts" +meta_title: "" +description: "this is meta description" +--- diff --git a/content/english/pages/404.md b/content/english/pages/404.md new file mode 100644 index 0000000..4cc39e0 --- /dev/null +++ b/content/english/pages/404.md @@ -0,0 +1,13 @@ +--- +title: "Page Not Found" +meta_title: "" +description: "this is meta description" +image: "/images/404.png" +draft: false + +# don't create a separate page +_build: + render: "never" +--- + +Hugoplate is a free starter template built with Hugo and TailwindCSS, providing everything you need to jumpstart your HTML project and save valuable time. diff --git a/content/english/pages/about.md b/content/english/pages/about.md new file mode 100644 index 0000000..d6c7415 --- /dev/null +++ b/content/english/pages/about.md @@ -0,0 +1,16 @@ +--- +title: "Hey, I am William Hearn!" +meta_title: "About" +description: "Senior Technical Architect (IT-04), Cloud Native Team @ Statistics Canada" +image: "/images/avatar.png" +layout: "about" +draft: false +--- + +I am an Senior Cloud Architect / Open Source advocate currently working at Statistics Canada. Open Source technologies are my passion, and I am dedicated to continuously expanding my skill set and gaining new experiences in this technology domain. + +One of my key areas of focus is empowering Project Teams / Solution Builders to deploy secure and scalable solutions. I achieve this by leveraging the potential of cutting-edge Cloud Native Computing Foundation (CNCF) technologies. These tools play a crucial role in enabling agile and efficient cloud-based solutions that meet the highest standards of security and scalability. + +As a Cloud Architect, my expertise lies in architecting, designing, and implementing cloud-native infrastructures. I collaborate closely with cross-functional teams to address intricate challenges and create robust cloud environments. Ensuring that our solutions are not only innovative but also secure and scalable is at the heart of my approach. + +I am dedicated to staying at the forefront of technology trends and best practices, constantly seeking opportunities to apply my knowledge to build complex cloud projects. By doing so, I contribute to the advancement of cloud technologies and support the Government of Canada in achieving its strategic objectives through innovative, open-source solutions. diff --git a/content/english/pages/contact.md b/content/english/pages/contact.md new file mode 100644 index 0000000..a0e4fc0 --- /dev/null +++ b/content/english/pages/contact.md @@ -0,0 +1,7 @@ +--- +title: "Contact" +meta_title: "" +description: "this is meta description" +layout: "contact" +draft: false +--- diff --git a/content/english/pages/elements.md b/content/english/pages/elements.md new file mode 100755 index 0000000..8d62f01 --- /dev/null +++ b/content/english/pages/elements.md @@ -0,0 +1,238 @@ +--- +title: "Elements" +meta_title: "" +# meta description +description: "This is meta description" +# save as draft +draft: false +--- + +{{< toc >}} + +Here is an example of headings. You can use this heading by the following markdown rules. For example: use `#` for heading 1 and use `######` for heading 6. + +# Heading 1 + +## Heading 2 + +### Heading 3 + +#### Heading 4 + +##### Heading 5 + +###### Heading 6 + +
+ +### Emphasis + +The emphasis, aka italics, with _asterisks_ or _underscores_. + +Strong emphasis, aka bold, with **asterisks** or **underscores**. + +The combined emphasis with **asterisks and _underscores_**. + +Strike through uses two tildes. ~~Scratch this.~~ + +
+ +### Button + +{{< button label="Button" link="/" style="solid" >}} + +
+ +### Link + +[I'm an inline-style link](https://www.google.com) + +[I'm an inline-style link with title](https://www.google.com "Google's Homepage") + +[I'm a reference-style link][Arbitrary case-insensitive reference text] + +[I'm a relative reference to a repository file](../blob/master/LICENSE) + +[You can use numbers for reference-style link definitions][1] + +Or leave it empty and use the [link text itself]. + +URLs and URLs in angle brackets will automatically get turned into links. + or and sometimes +example.com (but not on Github, for example). + +Some text to show that the reference links can follow later. + +[arbitrary case-insensitive reference text]: https://www.themefisher.com +[1]: https://gethugothemes.com +[link text itself]: https://www.getjekyllthemes.com + +
+ +### Paragraph + +Lorem ipsum dolor sit amet consectetur adipisicing elit. Quam nihil enim maxime corporis cumque totam aliquid nam sint inventore optio modi neque laborum officiis necessitatibus, facilis placeat pariatur! Voluptatem, sed harum pariatur adipisci voluptates voluptatum cumque, porro sint minima similique magni perferendis fuga! Optio vel ipsum excepturi tempore reiciendis id quidem? Vel in, doloribus debitis nesciunt fugit sequi magnam accusantium modi neque quis, vitae velit, pariatur harum autem a! Velit impedit atque maiores animi possimus asperiores natus repellendus excepturi sint architecto eligendi non, omnis nihil. Facilis, doloremque illum. Fugit optio laborum minus debitis natus illo perspiciatis corporis voluptatum rerum laboriosam. + +
+ +### Ordered List + +1. List item +2. List item +3. List item +4. List item +5. List item + +
+ +### Unordered List + +- List item +- List item +- List item +- List item +- List item + +
+ +### Notice + +{{< notice "note" >}} +This is a simple note. +{{< /notice >}} + +{{< notice "tip" >}} +This is a simple tip. +{{< /notice >}} + +{{< notice "info" >}} +This is a simple info. +{{< /notice >}} + +{{< notice "warning" >}} +This is a simple warning. +{{< /notice >}} + +
+ +### Tab + +{{< tabs >}} +{{< tab "Tab 1" >}} + +#### Did you come here for something in particular? + +Did you come here for something in particular or just general Riker-bashing? And blowing into maximum warp speed, you appeared for an instant to be in two places at once. We have a saboteur aboard. We know you’re dealing in stolen ore. But I wanna talk about the assassination attempt on Lieutenant Worf. + +{{< /tab >}} + +{{< tab "Tab 2" >}} + +#### I wanna talk about the assassination attempt + +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + +{{< /tab >}} + +{{< tab "Tab 3" >}} + +#### We know you’re dealing in stolen ore + +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo + +{{< /tab >}} +{{< /tabs >}} + +
+ +### Accordions + +{{< accordion "Why should you need to do this?" >}} + +- Lorem ipsum dolor sit amet consectetur adipisicing elit. +- Lorem ipsum dolor sit amet consectetur adipisicing elit. +- Lorem ipsum dolor sit amet consectetur + +{{< /accordion >}} + +{{< accordion "How can I adjust Horizontal centering" >}} + +- Lorem ipsum dolor sit amet consectetur adipisicing elit. +- Lorem ipsum dolor sit amet consectetur adipisicing elit. +- Lorem ipsum dolor sit amet consectetur + +{{< /accordion >}} + +{{< accordion "Should you use Negative margin?" >}} + +- Lorem ipsum dolor sit amet consectetur adipisicing elit. +- Lorem ipsum dolor sit amet consectetur adipisicing elit. +- Lorem ipsum dolor sit amet consectetur + +{{< /accordion >}} + +
+ +### Code and Syntax Highlighting + +This is an `Inline code` sample. + +```javascript +var s = "JavaScript syntax highlighting"; +alert(s); +``` + +```python +s = "Python syntax highlighting" +print s +``` + +
+ +### Blockquote + +> Did you come here for something in particular or just general Riker-bashing? And blowing into maximum warp speed, you appeared for an instant to be in two places at once. + +
+ +### Tables + +| Tables | Are | Cool | +| ------------- | :-----------: | ----: | +| col 3 is | right-aligned | $1600 | +| col 2 is | centered | $12 | +| zebra stripes | are neat | $1 | + +
+ +### Image + +{{< image src="images/image-placeholder.png" caption="" alt="alter-text" height="" width="" position="center" command="fill" option="q100" class="img-fluid" title="image title" webp="false" >}} + +
+ +### Gallery + +{{< gallery dir="images/gallery" class="" height="400" width="400" webp="true" command="Fit" option="" zoomable="true" >}} + +
+ +### Slider + +{{< slider dir="images/gallery" class="max-w-[600px] ml-0" height="400" width="400" webp="true" command="Fit" option="" zoomable="true" >}} + +
+ +### Youtube video + +{{< youtube ResipmZmpDU >}} + +
+ +### Custom video + +{{< video src="https://www.w3schools.com/html/mov_bbb.mp4" width="100%" height="auto" autoplay="false" loop="false" muted="false" controls="true" class="rounded-lg" >}} diff --git a/content/english/pages/resume.md b/content/english/pages/resume.md new file mode 100644 index 0000000..e903726 --- /dev/null +++ b/content/english/pages/resume.md @@ -0,0 +1,210 @@ +--- +title: "Resume" +meta_title: "" +description: "Senior Cloud Architect with a passion for Open Source technologies seeking to gain both new experiences and knowledge in order to increase overall technological skill set." +author: "William Hearn" +tags: ["resume"] +draft: false +--- + +{{< toc >}} + +## Highlights + +### Open Source contributions on GitHub + +- [canada-ca-terraform-modules](https://github.com/canada-ca-terraform-modules) + - [Managed MySQL Flexible Server](https://github.com/canada-ca-terraform-modules/terraform-azurerm-flex-mysql) + - [Managed PostgreSQL Flexible Server](https://github.com/canada-ca-terraform-modules/terraform-azurerm-flex-postgresql) +- [statcan](https://github.com/statcan) + - [Advanced Analytics Workspaces](https://github.com/statcan/aaw) + - [AAW Kubeflow Containers](https://github.com/statcan/aaw-kubeflow-containers) + - [AAW Kubeflow Manifests](https://github.com/statcan/aaw-kubeflow-manifests) + - [GateKeeper Policies](https://github.com/statcan/gatekeeper-policies) + - [Charts](https://github.com/statcan/charts) + - [Argo Controller](https://github.com/statcan/argo-controller) +- [wxt](https://github.com/drupalwxt) + - [Drupal WxT](https://github.com/drupalwxt/wxt) + - [Composer Project for Drupal WxT](https://github.com/drupalwxt/site-wxt) + - [Helm for DrupalWxT](https://github.com/drupalwxt/helm-drupal) + +### Senior Cloud Architect (IT-04) at Statistics Canada + +- Managed diverse Kubernetes clusters across the entire Software Development Life Cycle (SDLC) + - Currently 3+ years, including critical business workloads running on production clusters. +- Senior Cloud Architect (IT-04) of Cloud Native Team @ Statistics Canada +- Successfully obtained Authority to Operate (ATO) for the Cloud Native Platform (Kubernetes) + - Worked with Cyber Security Division and external security vendors to meet stringent security and compliance controls + - Developed the Concept of Operations for the Cloud Native Platform +- Secured GCEARB approval for the Cloud Native Platform, ensuring adherence to standardized government architectural principles. + +### Senior Drupal Technical Architect (IT-03) at Statistics Canada + +- Successfully migrated and deployed the [Statistics Canada Website](https://statcan.gc.ca) onto Drupal + - Open sourced this work which eventually become the Drupal WxT distribution in use by numerous others departments + - Demonstrated longevity and resilience, continuously evolving for almost a decade, with multiple successful upgrades and enhancements +- Successfully launched the [Open Government Portal](https://open.canada.ca) onto Drupal + - Achieved seamless integration between the project and both CKAN and its underlying Solr layer + - Ensuring efficient data management and robust search / faceting capabilities + +## Presentations + +1. Presented the Data Analytics as a Service Platform at Stratosphere 2020 + - [YouTube](https://www.youtube.com/watch?v=quYuuEAqNm0) +2. Presented a two day course at the School of Public Service's Digital Academy on Cloud Native technologies + - [YouTube](https://www.youtube.com/watch?v=QvMWls8OdmM) + +## Education + +### Honors, Bachelor of Computer Science, Carleton University +// September 2006 – August 2010
+// Minor in Psychology + +I graduated from the Computer Science program at Carleton University, specializing in software and computing. Throughout my studies, I focused on two key areas: human-computer interaction and software engineering. Notably, my Honors project was carried out in the HCI lab, where I developed a mobile app using Cordova that delivered an engaging narrative-based geolocative storyline. Additionally, I found great interest in courses such as Computer Graphics and Programming Paradigms, which further enriched my academic experience. + +### Game Development, Algonquin College +// September 2004 - June 2006 + +Completed two out of three years in the game development program at Algonquin College, before I made the decision to transfer into the Bachelor of Computer Science program at Carleton University. During my studies, I delved into diverse subjects such as DirectX, OpenGL, and Object-Oriented Programming with C++, while engaging in captivating projects that involved developing various games. One of the notable projects was the creation and rendering of a chessboard, encompassing all associated rulesets. + +## Language Proficiency + +- Bilingual in English and French (Second Language) +- BBB assessed by the Public Service Commission + +## Security Clearance + +- Secret (Level II) + +## Work + +### Statistics Canada + +#### [2019-11 to Present] Cloud Native Solutions + +Senior Technical Architect (IT-04) for the Cloud Native Solutions team with a focus on improving the Cloud Native Platform and empowering Solution Builders. + +- Creation and ongoing maintenance of the Advanced Analytics Workspace (AAW) data science environment built on top of the Cloud Native Platform using Kubeflow +- Enhance the Cloud Native Platform (2.0) through architectural improvements, strengthening security measures, and implementing an updated GitOps deployment model using Argo CD +- Successfully maintain and improve upon the automatation and onboarding to Horizontal Services such as Artifactory, Argo CD, GitLab, and Hashicorp Vault +- Enhance the developer experience for all Solution Builders at Statistics Canada through continuous improvements to the Cloud Native Platform, which is now the organizational default standard +- Provided ongoing expert guidance on best practice application architecture to Solution Builders, ensuring optimal design and performance +- Collaborated on a DevSecOps-oriented CI/CD pipeline, streamlining onboarding processes and enhancing the developer experience +- Obtained an Authority to Operate (ATO) for the Cloud Native Platform by adhering to the Government of Canada Security Controls and undergoing independent security assessments conducted by 3rd party vendors +- Ensured the organization's status as a pathfinder by actively open-sourcing a significant portion of work as well as sharing experiences and lessons learned to many other departments +- Strengthened the security, networking, and observability of the Cloud Native Platform by utilizing Istio as the Service Mesh and Cilium as the networking layer + +#### [2018-06 to 2019-11] Enterprise Cloud Services + +Technical Architect (IT-03) for the Enterprise Cloud Services team with a focus on open source and Cloud Native (CNCF) technologies such as Kubernetes. + +- Spearheaded the architecture, security, and maintenance of the Cloud Native Platform (Kubernetes) using Infrastructure as Code across the entire SDLC +- Successfully deployed and managed Horizontal Services such as Artifactory, Octopus, GitLab, Hashicorp Vault enhancing operational efficiency +- Led the transition of selected Solution Builders towards a distributed services approach through containerization and cloud-native tooling +- Obtained an interim Authority to Operate (iATO) for the Cloud Native Platform while working on answering the hundreds of security controls +- Started the planning and organizational structure of what a Cloud Native Solutions team would look like and how a platform engineering team could benefit the organization +- Provide guidance and offer best practice guides on containerizing applications across various programming stacks + +#### [2015-03 to 2018-06] Web Content Management + +Technical Architect (IT-03) with a focus on enhancing content delivery leveraging Drupal. + +- TBD + +#### [2011-06 to 2015-03] Web Content Management + +Technical Architect (IT-02) with a focus on enhancing content delivery leveraging Drupal. + +- TBD + +### Open Plus + +#### [2011-03 to Present] Senior Developer + +Consultant for Open Plus, leveraging my expertise as a Senior Technical Architect in both Drupal and the Cloud. + +Development of the [Ontario Public Service Employee Union](https://opseu.org)
+ +- Consulted in the design and architecture +- Migrated content from a internal xml based cms (LifeRay) into Drupal +- Integration with Apache Solr along with improvements to search processors and mappings + +Collaborated on the [University of Ottawa](https://uottawa.ca) website
+ +- Providing guidance on server architecture, CI/CD best practices, as well as provisioning +- Migrated content into the Faculty of Medicine website + +Collaborated on the [City of Ottawa](https://ottawa.ca) website
+ +- Providing guidance on server architecture +- Assisted in migration of thousands of legacy content across disparate services into Drupal + +Development of the [Standards Council of Canada][scc] website
+ +- Assisted in the design and overall architecture +- Indexed content in Solr from a variety of back ends including Oracle, Microsoft SQL Server, REST, and RSS + +### Open Source + +#### [2013-09 to Present] Open Source Developer + +- Led and maintained the Drupal WxT distribution, a widely used Open Source solution in the Government of Canada, for over a decade +- Supported upgrade paths from Drupal 8 to 10 within the Drupal WxT distribution +- Ensured ongoing compliance with Web Content Accessibility Guidelines (WCAG) AA standards +- Collaborated with a diverse team of often remote developers, designers, and stakeholders to implement new features, fix issues, and enhance the overall functionality +- Actively participated in the Drupal community by contributing code, sharing knowledge, and engaging in discussions to improve Drupal +- Received recognition and positive feedback from government agencies and users for delivering high-quality, secure, and scalable solution +- Stayed up-to-date with the latest trends and best practices in Open Source development, Drupal, and web technologies +- Mentored and onboarded new developers to the Drupal WxT project, fostering a collaborative and supportive development environment + +## Awards + +### Excellence in Service Delivery Award +// 2021 + +Received the Excellence in Service Delivery Award as part of the Cloud Team in recognition of outstanding service that has enhanced Statistics Canada's work and reputation amount Canadians. + +### Employee Recognition Award +// 2019 + +Received the Employee Recognition Award as part of the Cloud Team for a distinguished contribution to the effectiveness of Statistics Canada upon moving many Production workloads including the main website to the Cloud. + +### Public Service Excellence Award +// 2014 + +Received the prestigious **Public Service Excellence Award** in the Excellence in Citizen Focused Delivery category from the Governor General. The award was a recognition of exceptional contributions to the development of the Open Data platform ([open.canada.ca][opendata]). + +### Team of the Year Award +// 2014 + +Received the team of the year award at Statistics Canada for the work the team has contributed to the Open Data platform ([open.canada.ca][opendata]) and Statistics Canada's main website ([statcan.gc.ca][statcan]). + +### GTEC Winner +// 2014 + +Won for Excellence in Public Service Delivery for the Next Generation Open Data Portal. A joint submission between Statistics Canada and the Treasury Board Secretariat. + +### GTEC Honoree +// 2013 + +Received an Honoree nomination at GTEC under the category of “Collaboration around a Web Content Management Framework for Government and Public Institutions”. This was a joint submission by Statistics Canada, Canadian Transportation Agency, University of Ottawa, and the City of Ottawa. + +### Merit Award +// 2013 + +Recieved the merit award at Statistics Canada in recognition of an exceptional and distinguished contribution to the effectiveness of Statistics Canada + +### A+ Certified Professional + +Years of experience leading to a designation as a computing certified technician from COMPTIA. + +## References + +References available upon request. + + + +[opendata]: https://open.canada.ca +[openplus]: https://openplus.ca +[scc]: https://scc.ca +[statcan]: https://statcan.gc.ca diff --git a/content/english/search.md b/content/english/search.md new file mode 100644 index 0000000..1c4b6fd --- /dev/null +++ b/content/english/search.md @@ -0,0 +1,4 @@ +--- +title: search +layout: search +--- diff --git a/content/english/sections/call-to-action.md b/content/english/sections/call-to-action.md new file mode 100644 index 0000000..efda148 --- /dev/null +++ b/content/english/sections/call-to-action.md @@ -0,0 +1,14 @@ +--- +enable: true +title: "Ready to build your next project with Hugo?" +image: "/images/call-to-action.png" +description: "Experience the future of web development with Hugoplate and Hugo. Build lightning-fast static sites with ease and flexibility." +button: + enable: true + label: "Get Started Now" + link: "https://github.com/zeon-studio/hugoplate" + +# don't create a separate page +_build: + render: "never" +--- diff --git a/content/english/sections/testimonial.md b/content/english/sections/testimonial.md new file mode 100644 index 0000000..6bb5d5a --- /dev/null +++ b/content/english/sections/testimonial.md @@ -0,0 +1,24 @@ +--- +enable: true +title: "What people are saying about working with William" +description: "Don't just take my word on it - please read from some of the testimonials of people I have collaborated with" + +testimonials: + - name: "Andrew Sinkinson" + designation: "Director Enterprise Cloud Services Division @ Statistics Canada" + avatar: "/images/avatar-sm.png" + content: "William has been a driving force behind our successful cloud-native initiatives at Statistics Canada. His skills in using CNCF technologies and passion for Open Source have transformed the experience of our solution builders, making our projects more agile and reliable." + + - name: "Zachary Seguin" + designation: "IT-04 Project Manager Cloud Native Team @ Statistics Canada" + avatar: "/images/avatar-sm.png" + content: "I can't speak highly enough about William's contributions. His proficiency in CNCF tooling and commitment to Open Source values have revolutionized the way we approach cloud-native development. He has made our project team more agile, collaborative, and forward-thinking." + + - name: "Robin Galipeau" + designation: "Managing Partner and Entrepreneur @ Open Plus" + avatar: "/images/avatar-sm.png" + content: "William is an important member of our team at Open Plus. His comprehensive understanding of DevOps practices and Cloud Architecture has proven to be instrumental in our projects. From designing robust systems to implementing automated deployment pipelines, William delivers." + +_build: + render: "never" +--- diff --git a/content/page/resume.md b/content/page/resume.md deleted file mode 100644 index 2dfff1f..0000000 --- a/content/page/resume.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: Resume -description: Cloud Architect with a passion for Open Source technologies seeking to gain both new experiences and knowledge in order to increase overall technological skill set. Passions include leveraging Cloud Native tools such as Kubernetes, Open Policy Agent, and Kubeflow -menu: main -weight: -210 -tags: - - resume -toc: true ---- - -## Highlights - -1. Open Source contributions on GitHub - - - [canada-ca-terraform-modules](https://github.com/canada-ca-terraform-modules) - - Azure Kubernetes Service // [Infrastructure](https://github.com/canada-ca-terraform-modules/terraform-kubernetes-aks) / [Platform](https://github.com/canada-ca-terraform-modules/terraform-kubernetes-aks-platform) - - IBM Kubernetes Service    // [Infrastructure](https://github.com/canada-ca-terraform-modules/terraform-kubernetes-iks) / [Platform](https://github.com/canada-ca-terraform-modules/terraform-kubernetes-iks-platform) - - [wxt](https://github.com/drupalwxt) - - [Composer Project for Drupal WxT](https://github.com/drupalwxt/site-wxt) - - [WxT Installation Profile](https://github.com/drupalwxt/wxt) - - [Helm for DrupalWxT](https://github.com/drupalwxt/helm-drupal) - - [statcan](https://github.com/statcan) - - [Charts](https://github.com/statcan/charts) - - [Data Analytics as a Service](https://github.com/statcan/daaas) - - [GateKeeper Policies](https://github.com/statcan/gatekeeper-policies) - - [Kubeflow Containers](https://github.com/statcan/kubeflow-containers) - - [Kubeflow Manifest](https://github.com/statcan/kubeflow-manifest) - -2. Cloud Technical Architect at Statistics Canada (Current) - - - Technical Architect of Cloud Native team at Statistics Canada - - Developed Concept of Operations for Kubernetes Cloud Native Platform used internally and shared with other departments - - Recieved Authority to Operate (ATO) for Kubernetes Cloud Native Platform through met Security Controls - - Recieved GCEARB approval for Kubernetes Cloud Native Platform - -3. Drupal Technical Architect at Statistics Canada (Previous) - - - Launched the [Statistics Canada Portal][statcan] on Drupal - - Launched [Open Government Portal][opendata] on Drupal - -## Presentations - -1. Presented the Data Analytics as a Service Platform at Stratosphere 2020 - - [PDF](https://govcloud.blob.core.windows.net/docs/daaas-cncf.pdf) / [YouTube](https://www.youtube.com/watch?v=quYuuEAqNm0) -2. Presented a two day course at the School of Public Service's Digital Academy on Cloud Native technologies - - [PDF](https://govcloud.blob.core.windows.net/docs/digital-academy.pdf) / [YouTube](https://www.youtube.com/watch?v=QvMWls8OdmM) - -## Education - -**Honors, Bachelor of Computer Science, Carleton University
// Minor in Psychology
// September 2006 – August 2010** - -Graduate of Computer Science program at Carleton University in the software and computing stream, with focus on both human-computer interaction and software engineering. My Honors project was done in the HCI lab involving creation of a mobile app (cordova) delivering a narrative based geolocative storyline. Other courses that were of particular interest to me included Computer Graphics, and Programming Paradigms. - -**Game Development, Algonquin College
// September 2004 - June 2006** - -Completed 2 of 3 years in game development program at Algonquin College before transferring into the Bachelor of Computer Science program at Carleton University. Areas of study included both DirectX, Open GL, and Object Oriented Programming with C++. One of the more interesting projects was to create and render a chess board with all of the associated rulesets. - -## Work - -**Government of Canada: Statistics Canada ([statcan.gc.ca][statcan], [open.canada.ca][opendata])
// March 2011 – Present** - -**Cloud Native Team (2018 - Present)** - -Senior Technical Architect for the Cloud Native team with a focus on open source and cloud native (CNCF) technologies such as Kubernetes, OCI Containers, and the Open Policy Agent. - -1. Architecture, security and maintenance of multiple Kubernetes Clusters using IaC across the software development lifecycle -2. Deployment and operation of various Horizontal Services which include Artifactory, GitLab, Elastic Cloud, and Vault -3. Onboarding of teams towards a distributed services approach through containerization and cloud native tooling -4. Provide best practice application architecture for containers as well as tools for tools such as Kustomize and Helm. -5. Provide a DevSecOps oriented CI/CD pipeline leveraging the key principles laid out by the US DoD -6. Assist the Data Analytics as a Service project using their own dedicated auto-scaling cluster as well as tools such as Kubeflow -7. Providing the organization with an Authority to Operate through strict adherance to ITSG-33 Security Controls and alignment with CIS Benchmarks -8. Ensure our departmental status as a pathfinder organization is met through open source of the majority of our tools -9. Sharing all of our experiences, lessons learned through various presentations made to numerous departments as well as Stratosphere - -**Web Content Management Team (2011 - 2017)** - -Technical Architect with a focus on improving content delivery leveraging Drupal. Notable job functions include the following: - -1. Technical Architect of Drupal portal for **[open.canada.ca][opendata] (2013-2018)** - - Architected both Drupal 7/8 versions of the portal - - Maintained, monitored and administered portal for a period of 5 years - - Tight integration between Drupal + Search API / Solr - - Custom integration bewtween Drupal + CKAN leveraging JsonAPI - - Various interactive components including blog, rating, and submission of apps - - Access to Information and all of its associated faceted displays - - Server load and performance testing - - Redesign / Re-architecture to support the various theme updates to WxT -2. Technical Architect of Drupal portal for **[statcan.gc.ca][statcan] (2011-2018)** - - Technical architect for both Drupal 7/8 versions of the portal - - Maintained, monitored and administered portal for a period of 7 years - - Creation of the Drupal WxT variant to share with other departments - - Migration of 100,000+ html pages and other content into the CMS while performing data cleansing and validation (HTML5) - - Adherance to WCAG 2.0 AA and Government of Canada web standards - - Developed a distirbuted content staging model using web services, rest via json, and UUID - - Ensure the platform provides extensive multilingualism support across both the front and back end interfaces - - Assisted in development of the new Dissemination Model to make finding data on portal easier to find - - Work alongside a contractor on extending Drupal to be more of an output metadata framework powered by REST - -**Open Plus ([openplus.ca][openplus])
// September 2010 – March 2011 (Full Time)
// March 2011 – Present (Contract, Part Time)** - -Developer for Open Plus a web development firm advocating Open Source technology. Responsibilities include interacting with different type of clients (NGO, Government, and Private Sector) and implementing a wide array of technical solutions particularly around enterprise content management systems (CMS) as well as enterprise search leveraging either ElasticSearch and / or Solr. Recent work within the last 5 years at Open Plus have included: - -1. Provide architectural guidance to government departments such as Canadian Revenue Agency and Transport Canada - - Deployment of Kubernetes as well as platform level components - - Deployment of the GCEARB approved Drupal WxT PaaS - - Provided guidance and tooling for improved automation through the use of Terraform -2. Research and development for upcoming clients: - - Integration with [Orcid API][orcid] into Drupal 8 - - Improved ui / patterns for migrating content via NodeJS (express / cheerio) + migrate (Drupal) -3. Development for the Ontario Public Service Employee Union website ([opseu.org][opseu]): - - Consulted in the design and architecture - - Migrated content from a internal xml based cms (LifeRay) into Drupal - - Integration with Apache Solr along with improvements to search processors and mappings -4. Consulting for the University of Ottawa website ([uottawa.ca][uottawa]): - - Providing guidance on server architecture, CI/CD best practices, as well as provisioning - - Migrated content into the Faculty of Medicine website -5. Consulting for City of Ottawa ([ottawa.ca][ottawa]): - - Providing guidance on server architecture - - Assisted in migration of thousands of legacy content across disparate services into Drupal -6. Development for the Standards Council of Canada website ([scc.ca][scc]): - - Assisted in the design and overall architecture - - Indexed content in Apache Solr from a variety of back ends including Oracle, Microsoft SQL Server, REST, and RSS - -**Carleton University: Carleton Library ([library.carleton.ca][carleton-library])
// December 2009 - July 2010** - -Web Developer tasked with creating a new Carleton Library site alongside the Senior Web Developer using the Drupal Content Management System. The tasks consist of porting all the content from the old library site into Drupal all the while making the markup more accessible to screen readers and allowing for the users of the library to more easily find the information they were looking for. - -## Awards - -### Public Service Excellence Award 2014 - -Received the public service excellence award in the “Excellence in Citizen Focused Delivery” category from the Governor General for my work on the Open Data platform ([open.canada.ca][opendata]). - -### Team of the Year Award 2014 - -Received the team of the year at Statistics Canada for my work on the Open Data platform ([open.canada.ca][opendata]). - -### GTEC Winner 2014 - -Won for Category 1: Excellence in Public Service Delivery – External for the Next Generation Open Data Portal. A joint effort between Statistics Canada and the Treasury Board Secretariat. - -### GTEC Honoree 2013 - -Received an Honoree nomination at GTEC under the category of “Collaboration around a Web Content Management Framework for Government and Public Institutions”. This was a joint submission by Statistics Canada, Canadian Transportation Agency, University of Ottawa, and the City of Ottawa. - -### A+ Certified Professional - -Years of experience leading to a designation as a computing certified technician from COMPTIA. - -## References - -References are available upon request. - - - -[carleton-library]: https://library.carleton.ca -[chocolately]: https://github.com/Azure/acs-engine/pull/2707 -[drupal]: https://github.com/docker-library/drupal/graphs/contributors -[gh-govcloud]: https://github.com/govcloud -[govcloud]: https://govcloud.ca -[meetup]: https://www.meetup.com/goc-cloud-native -[hadoop]: https://github.com/kubernetes/charts/pull/6688 -[nifi]: https://github.com/kubernetes/charts/pull/5772 -[octopus]: https://github.com/helm/charts/pull/7769 -[kylo]: https://github.com/kubernetes/charts/pull/5773 -[opendata]: https://open.canada.ca -[openplus]: https://openplus.ca -[opseu]: https://opseu.org -[orcid]: https://orcid.org/organizations/integrators/API -[ottawa]: https://ottawa.ca -[statcan]: https://statcan.gc.ca -[scc]: https://www.scc.ca -[uottawa]: httpa://uottawa.ca -[wet-boew-drupal]: https://github.com/wet-boew/wet-boew-drupal -[wetkit]: https://drupal.org/project/wetkit -[wxt]: https://github.com/drupalwxt/wxt -[wxt-d7]: https://drupal.org/project/wetkit -[wxt-d8]: https://drupal.org/project/wxt -[youtube]: https://www.youtube.com/channel/UC00nN9hhb4q6IChP8yEIzGA diff --git a/content/tags/docker/_index.md b/content/tags/docker/_index.md deleted file mode 100644 index abdb9da..0000000 --- a/content/tags/docker/_index.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: Docker ---- diff --git a/content/tags/kubernetes/_index.md b/content/tags/kubernetes/_index.md deleted file mode 100644 index cd640f0..0000000 --- a/content/tags/kubernetes/_index.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: Kubernetes ---- diff --git a/content/tags/og/_index.md b/content/tags/og/_index.md deleted file mode 100644 index 292c1e8..0000000 --- a/content/tags/og/_index.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: Opengraph ---- diff --git a/data/authors/sylus.toml b/data/authors/sylus.toml deleted file mode 100644 index f53ebcd..0000000 --- a/data/authors/sylus.toml +++ /dev/null @@ -1,12 +0,0 @@ -id = "sylus" - -[email] -username = "sylus1984" -host = "gmail.com" - -[name] -display = "William Hearn" - -[social] -email = "" -twitter = "william_hearn" diff --git a/data/config/widgets.toml b/data/config/widgets.toml deleted file mode 100644 index 571ba20..0000000 --- a/data/config/widgets.toml +++ /dev/null @@ -1,36 +0,0 @@ -# About Widget config -[about] -title = "" # default: .Site.Title -description = "" # default: .Site.Params.info.description -logo = "/images/logo.png" # default: "/images/logo.png" - -# Breadcrumbs Widget config -[breadcrumbs] -separator = "/" # default: "/" -excludeParents = ["page"] # default: ["page"] - -# Recent Posts Widget config -[recent_posts] -title = "" # default: "Recent Posts" -length = 4 # default: 5 -excludeTypes = ["page"] # default: ["page"] - -# Search Widget config -[search] -title = "" # default: "Search" - -# Sidebar Menu Widget config -[sidebar_menu] -title = "" # default: "" -mirror = "" # default: "" - -# Social Menu Widget config -[social_menu] -title = "" # default: "" -platforms = ["github","facebook","twitter","instagram","email","codepen","gitlab","linkedin","telegram","google_scholar","youtube"] - -# Taxonomy Cloud Widget config -[taxonomy_cloud] -title = "" # default: Plural Name for `taxonomyCloud.taxonomy` -taxonomy = "tags" # default: "tags" -shuffle = true # default: true diff --git a/data/social.json b/data/social.json new file mode 100644 index 0000000..8a54610 --- /dev/null +++ b/data/social.json @@ -0,0 +1,24 @@ +{ + "main": [ + { + "name": "github", + "icon": "fab fa-github", + "link": "https://github.com/sylus" + }, + { + "name": "mastodon", + "icon": "fab fa-mastodon", + "link": "https://hachyderm.io/@sylus" + }, + { + "name": "twitter", + "icon": "fab fa-twitter", + "link": "https://twitter.com/william_hearn" + }, + { + "name": "linkedin", + "icon": "fab fa-linkedin", + "link": "https://linkedin.com/in/william-hearn-78688227" + } + ] +} diff --git a/data/theme.json b/data/theme.json new file mode 100644 index 0000000..c838223 --- /dev/null +++ b/data/theme.json @@ -0,0 +1,44 @@ +{ + "colors": { + "default": { + "theme_color": { + "primary": "#121212", + "body": "#fff", + "border": "#eaeaea", + "theme_light": "#f6f6f6", + "theme_dark": "" + }, + "text_color": { + "default": "#444444", + "dark": "#040404", + "light": "#717171" + } + }, + "darkmode": { + "theme_color": { + "primary": "#fff", + "body": "#1c1c1c", + "border": "#3E3E3E", + "theme_light": "#222222", + "theme_dark": "" + }, + "text_color": { + "default": "#B4AFB6", + "dark": "#fff", + "light": "#B4AFB6" + } + } + }, + "fonts": { + "font_family": { + "primary": "Heebo:wght@400;600", + "primary_type": "sans-serif", + "secondary": "Signika:wght@500;700", + "secondary_type": "sans-serif" + }, + "font_size": { + "base": "16", + "scale": "1.250" + } + } +} diff --git a/deploy.sh b/deploy.sh deleted file mode 100755 index f4cfcf2..0000000 --- a/deploy.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# https://gohugo.io/tutorials/github-pages-blog/ - -git remote set-url origin "https://${GH_TOKEN}@github.com/sylus/sylus.github.io.git" -git submodule add -f "https://${GH_TOKEN}@github.com/sylus/sylus.github.io.git" public - -echo -e "\033[0;32mDeploying updates to Github...\033[0m" - -# Build the project. -hugo --theme=minimo - -# Go To Public folder -cd public - -# Add changes to git. -git add -A - -# Commit changes. -msg="rebuilding site `date`" -if [ $# -eq 1 ] - then msg="$1" -fi -git commit -m "$msg" - -# Push source and build repos. -git push origin master - -# Come Back -cd .. diff --git a/dev.sh b/dev.sh index 065b4eb..a8c5313 100755 --- a/dev.sh +++ b/dev.sh @@ -2,24 +2,5 @@ echo -e "\033[0;32mStarting Hugo...\033[0m" -cd themes - -REPOSRC=https://github.com/MunifTanjim/minimo -LOCALREPO=minimo - -# We do it this way so that we can abstract if from just git later on -LOCALREPO_VC_DIR=$LOCALREPO/.git - -if [ ! -d $LOCALREPO_VC_DIR ] -then - git clone $REPOSRC $LOCALREPO - git checkout v2.9.0 -else - cd $LOCALREPO - git pull $REPOSRC - cd .. -fi - -cd .. -hugo server --theme=$LOCALREPO \ +hugo server --environment local \ --watch diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..8d62348 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,13 @@ +version: "3.3" + +services: + + site: + image: sylus/website + build: + context: . + command: server + ports: + - "1313:1313" + volumes: + - .:/src diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d088eaf --- /dev/null +++ b/go.mod @@ -0,0 +1,29 @@ +module github.com/sylus/website + +go 1.20 + +require ( + github.com/gethugothemes/hugo-modules/accordion v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/adsense v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/components/cookie-consent v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/components/custom-script v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/components/preloader v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/components/render-link v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/components/social-share v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/gallery-slider v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/gzip-caching v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/icons/font-awesome v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/images v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/modal v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/pwa v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/search v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/seo-tools/basic-seo v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/seo-tools/google-tag-manager v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/seo-tools/site-verifications v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/shortcodes/button v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/shortcodes/notice v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/tab v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/table-of-contents v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/gethugothemes/hugo-modules/videos v0.0.0-20230705095442-1f2d5ac8b18d // indirect + github.com/zeon-studio/hugoplate v0.0.0-20230718073735-8df5a7032e3d // indirect +) diff --git a/i18n/en.yaml b/i18n/en.yaml new file mode 100755 index 0000000..83fa869 --- /dev/null +++ b/i18n/en.yaml @@ -0,0 +1,35 @@ +- id: home + translation: Home + +- id: read_more + translation: Read More + +- id: send + translation: Send + +- id: related_posts + translation: Related Posts + +- id: categories + translation: Categories + +- id: tags + translation: Tags + +- id: toc + translation: Table of Contents + +- id: share + translation: Share + +- id: back_to_home + translation: Back To Home + +- id: search_input_placeholder + translation: Search Post ... + +- id: no_results_for + translation: No results for + +- id: empty_search_results_placeholder + translation: Type something to search.. diff --git a/layouts/partials/essentials/footer.html b/layouts/partials/essentials/footer.html new file mode 100644 index 0000000..f668774 --- /dev/null +++ b/layouts/partials/essentials/footer.html @@ -0,0 +1,57 @@ + diff --git a/package.json b/package.json new file mode 100644 index 0000000..9d80984 --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "website", + "version": "0.1.0", + "description": "Personal website built using Hugo", + "license": "MIT", + "author": "William Hearn", + "bugs": { + "url": "https://github.com/sylus/website/issues" + }, + "engines": { + "node": ">16.0" + }, + "homepage": "https://sylus.ca", + "scripts": { + "dev": "hugo server", + "build": "hugo --gc --minify --templateMetrics --templateMetricsHints --forceSyncStatic", + "test": "hugo server --disableFastRender --navigateToChanged --templateMetrics --templateMetricsHints --watch --forceSyncStatic -e production --minify", + "dev:example": "cd exampleSite; hugo server", + "build:example": "cd exampleSite; hugo --gc --minify --templateMetrics --templateMetricsHints --forceSyncStatic", + "test:example": "cd exampleSite; hugo server --disableFastRender --navigateToChanged --templateMetrics --templateMetricsHints --watch --forceSyncStatic -e production --minify", + "update-modules": "node ./scripts/clearModules.js && hugo mod clean --all && hugo mod get -u ./... && hugo mod tidy", + "remove-darkmode": "node ./scripts/removeDarkmode.js && yarn format", + "project-setup": "node ./scripts/projectSetup.js", + "theme-setup": "node ./scripts/themeSetup.js", + "format": "prettier -w ." + }, + "repository": { + "type": "git", + "url": "git+https://github.com/sylus/website" + }, + "devDependencies": { + "@fullhuman/postcss-purgecss": "^5.0.0", + "@tailwindcss/forms": "^0.5.4", + "@tailwindcss/typography": "^0.5.9", + "autoprefixer": "^10.4.14", + "postcss": "^8.4.26", + "postcss-cli": "^10.1.0", + "prettier": "^2.8.8", + "prettier-plugin-go-template": "0.0.13", + "prettier-plugin-tailwindcss": "^0.4.1", + "tailwind-bootstrap-grid": "^5.0.1", + "tailwindcss": "^3.3.3" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..c5cf22a --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,42 @@ +const purgecss = { + content: ["./hugo_stats.json"], + defaultExtractor: (content) => { + const elements = JSON.parse(content).htmlElements; + return [ + ...(elements.tags || []), + ...(elements.classes || []), + ...(elements.ids || []), + ]; + }, + safelist: [ + /^swiper-/, + /^lb-/, + /^gl/, + /^go/, + /^gc/, + /^gs/, + /^gi/, + /^gz/, + /^desc/, + /^zoom/, + /^search/, + /^:is/, + /dark/, + /show/, + /dragging/, + /fullscreen/, + /loaded/, + /visible/, + /current/, + /active/, + ], +}; + +module.exports = { + plugins: { + tailwindcss: {}, + "@fullhuman/postcss-purgecss": + process.env.HUGO_ENVIRONMENT === "production" ? purgecss : false, + autoprefixer: process.env.HUGO_ENVIRONMENT === "production" ? {} : false, + }, +}; diff --git a/scripts/clearModules.js b/scripts/clearModules.js new file mode 100644 index 0000000..1b9c74a --- /dev/null +++ b/scripts/clearModules.js @@ -0,0 +1,14 @@ +const fs = require("fs"); + +const clearModules = (filePath) => { + if (fs.existsSync(filePath)) { + let fileContent = fs.readFileSync(filePath, "utf8"); + fileContent = fileContent.replace(/require\s*\([\s\S]*?\)/, ""); + fs.writeFileSync(filePath, fileContent, "utf8"); + } else { + console.log("File does not exist."); + } +}; + +clearModules("go.mod"); +clearModules("exampleSite/go.mod"); diff --git a/scripts/projectSetup.js b/scripts/projectSetup.js new file mode 100644 index 0000000..d87a67f --- /dev/null +++ b/scripts/projectSetup.js @@ -0,0 +1,116 @@ +const fs = require("fs"); +const path = require("path"); + +const toggleComment = ({ filepath, regex }) => { + let updatedContent = fs.readFileSync(filepath, "utf8"); + const match = updatedContent.match(regex); + + if (match) { + const matchedContent = match[0]; + const hasComment = matchedContent.startsWith("# "); + if (hasComment) { + updatedContent = updatedContent.replace( + regex, + matchedContent.replace("# ", "") + ); + fs.writeFileSync(filepath, updatedContent, "utf8"); + } else { + const hasBreakline = matchedContent.includes("\n"); + if (hasBreakline) { + const content = matchedContent + .split("\n") + .map((line) => "# " + line) + .join("\n"); + updatedContent = updatedContent.replace(regex, content); + fs.writeFileSync(filepath, updatedContent, "utf8"); + } + } + } +}; + +const getFolderName = (rootfolder) => { + const configPath = path.join(rootfolder, "exampleSite/hugo.toml"); + const getConfig = fs.readFileSync(configPath, "utf8"); + const match = getConfig.match(/theme\s*=\s*\[?"([^"\]]+)"\]?/); + let selectedTheme = null; + if (match && match[1]) { + selectedTheme = match[1]; + } + return selectedTheme; +}; + +const deleteFolder = (folderPath) => { + if (fs.existsSync(folderPath)) { + fs.rmSync(folderPath, { recursive: true, force: true }); + } +}; + +const createNewfolder = (rootfolder, folderName) => { + const newFolder = path.join(rootfolder, folderName); + fs.mkdirSync(newFolder, { recursive: true }); + return newFolder; +}; + +const iterateFilesAndFolders = (rootFolder, { destinationRoot }) => { + const directory = path.join(rootFolder); + const items = fs.readdirSync(directory, { withFileTypes: true }); + items.forEach((item) => { + if (item.isDirectory()) { + createNewfolder(destinationRoot, item.name); + iterateFilesAndFolders(path.join(directory, item.name), { + currentFolder: item.name, + destinationRoot: path.join(destinationRoot, item.name), + }); + } else { + const sourceFile = path.join(directory, item.name); + const destinationFile = path.join(destinationRoot, item.name); + fs.renameSync(sourceFile, destinationFile); + } + }); +}; + +const setupProject = () => { + const rootfolder = path.join(__dirname, "../"); + if (!fs.existsSync(path.join(rootfolder, "themes"))) { + // remove this part if you don't using theme demo as a module + [ + { + filepath: path.join(rootfolder, "exampleSite/hugo.toml"), + regex: /^.*theme\s*=\s*("[^"\]]+"|\S+)/m, + }, + { + filepath: path.join( + rootfolder, + "exampleSite/config/_default/module.toml" + ), + regex: /\[\[imports\]\]\s*\r?\npath = "([^"]+)"/, + }, + ].forEach(toggleComment); + + const folderList = ["layouts", "assets", "static"]; + const folderName = getFolderName(rootfolder); + const newfolderName = createNewfolder( + path.join(rootfolder, "themes"), + folderName + ); + + folderList.forEach((folder) => { + const source = path.join(rootfolder, folder); + const destination = path.join(newfolderName, folder); + if (fs.existsSync(source)) { + fs.mkdirSync(destination, { recursive: true }); + iterateFilesAndFolders(source, { + currentFolder: folder, + destinationRoot: destination, + }); + deleteFolder(source); + } + }); + + const exampleSite = path.join(rootfolder, "exampleSite"); + iterateFilesAndFolders(exampleSite, { destinationRoot: rootfolder }); + deleteFolder(exampleSite); + } +}; + +setupProject(); diff --git a/scripts/removeDarkmode.js b/scripts/removeDarkmode.js new file mode 100644 index 0000000..cb1b863 --- /dev/null +++ b/scripts/removeDarkmode.js @@ -0,0 +1,69 @@ +const fs = require("fs"); +const path = require("path"); + +const rootDirs = ["assets/scss", "layouts"]; +const configFiles = [ + { + filePath: "exampleSite/tailwind.config.js", + patterns: ["darkmode:\\s*{[^}]*},", 'darkMode:\\s*"class",'], + }, + { + filePath: "exampleSite/data/theme.json", + patterns: ["colors.darkmode"], + }, +]; + +rootDirs.forEach(removeDarkModeFromPages); +configFiles.forEach(removeDarkMode); + +function removeDarkModeFromFiles(filePath, regexPatterns) { + const fileContent = fs.readFileSync(filePath, "utf8"); + let updatedContent = fileContent; + regexPatterns.forEach((pattern) => { + const regex = new RegExp(pattern, "g"); + updatedContent = updatedContent.replace(regex, ""); + }); + fs.writeFileSync(filePath, updatedContent, "utf8"); +} + +function removeDarkModeFromPages(directoryPath) { + const files = fs.readdirSync(directoryPath); + + files.forEach((file) => { + const filePath = path.join(directoryPath, file); + const stats = fs.statSync(filePath); + if (stats.isDirectory()) { + removeDarkModeFromPages(filePath); + } else if (stats.isFile()) { + removeDarkModeFromFiles(filePath, [ + '(?:(?!["])\\S)*dark:(?:(?![,;"])\\S)*', + "@apply?(\\s)*;", + ]); + } + }); +} + +function removeDarkMode(configFile) { + const { filePath, patterns } = configFile; + if (filePath === "exampleSite/tailwind.config.js") { + removeDarkModeFromFiles(filePath, patterns); + } else { + const contentFile = JSON.parse(fs.readFileSync(filePath, "utf8")); + patterns.forEach((pattern) => deleteNestedProperty(contentFile, pattern)); + fs.writeFileSync(filePath, JSON.stringify(contentFile)); + } +} + +function deleteNestedProperty(obj, propertyPath) { + const properties = propertyPath.split("."); + let currentObj = obj; + for (let i = 0; i < properties.length - 1; i++) { + const property = properties[i]; + if (currentObj.hasOwnProperty(property)) { + currentObj = currentObj[property]; + } else { + return; // Property not found, no need to continue + } + } + delete currentObj[properties[properties.length - 1]]; +} diff --git a/scripts/themeSetup.js b/scripts/themeSetup.js new file mode 100644 index 0000000..1e90483 --- /dev/null +++ b/scripts/themeSetup.js @@ -0,0 +1,125 @@ +const fs = require("fs"); +const path = require("path"); + +const toggleComment = ({ filepath, regex }) => { + let updatedContent = fs.readFileSync(filepath, "utf8"); + const match = updatedContent.match(regex); + + if (match) { + const matchedContent = match[0]; + const hasComment = matchedContent.startsWith("# "); + if (hasComment) { + const hasBreakline = matchedContent.includes("\n"); + if (hasBreakline) { + updatedContent = updatedContent.replace( + regex, + matchedContent.replace(/# /gm, "") + ); + fs.writeFileSync(filepath, updatedContent, "utf8"); + } + } else { + updatedContent = updatedContent.replace(regex, "# " + matchedContent); + fs.writeFileSync(filepath, updatedContent, "utf8"); + } + } +}; + +const createNewfolder = (rootfolder, folderName) => { + const newFolder = path.join(rootfolder, folderName); + fs.mkdirSync(newFolder, { recursive: true }); + return newFolder; +}; + +const deleteFolder = (folderPath) => { + if (fs.existsSync(folderPath)) { + fs.rmSync(folderPath, { recursive: true, force: true }); + } +}; + +const getFolderName = (rootfolder) => { + const configPath = path.join(rootfolder, "exampleSite/hugo.toml"); + const getConfig = fs.readFileSync(configPath, "utf8"); + const match = getConfig.match(/theme\s*=\s*\[?"([^"\]]+)"\]?/); + let selectedTheme = null; + if (match && match[1]) { + selectedTheme = match[1]; + } + return selectedTheme; +}; + +const iterateFilesAndFolders = (rootFolder, { destinationRoot }) => { + const directory = path.join(rootFolder); + const items = fs.readdirSync(directory, { withFileTypes: true }); + items.forEach((item) => { + if (item.isDirectory()) { + createNewfolder(destinationRoot, item.name); + iterateFilesAndFolders(path.join(directory, item.name), { + currentFolder: item.name, + destinationRoot: path.join(destinationRoot, item.name), + }); + } else { + const sourceFile = path.join(directory, item.name); + const destinationFile = path.join(destinationRoot, item.name); + fs.renameSync(sourceFile, destinationFile); + } + }); +}; + +const setupTheme = () => { + const rootFolder = path.join(__dirname, "../"); + + if (!fs.existsSync(path.join(rootFolder, "exampleSite"))) { + // remove this part if you don't using theme demo as a module + [ + { + filepath: path.join(rootFolder, "config/_default/module.toml"), + regex: /# \[\[imports\]\]\s*\r?\n# path = "([^"]+)"/, + }, + { + filepath: path.join(rootFolder, "hugo.toml"), + regex: /^.*theme\s*=\s*("[^"\]]+"|\S+)/m, + }, + ].forEach(toggleComment); + + const includesFiles = [ + "tailwind.config.js", + "postcss.config.js", + "go.mod", + "hugo.toml", + "assets", + "config", + "data", + "content", + "i18n", + "static", + ]; + + const folder = createNewfolder(rootFolder, "exampleSite"); + + fs.readdirSync(rootFolder, { withFileTypes: true }).forEach((file) => { + if (includesFiles.includes(file.name)) { + if (file.isDirectory()) { + const destination = path.join(rootFolder, "exampleSite", file.name); + fs.mkdirSync(destination, { recursive: true }); + iterateFilesAndFolders(path.join(rootFolder, file.name), { + destinationRoot: destination, + }); + deleteFolder(path.join(rootFolder, file.name)); + } else { + fs.renameSync( + path.join(rootFolder, file.name), + path.join(folder, file.name) + ); + } + } + }); + + const themes = path.join(rootFolder, "themes"); + iterateFilesAndFolders(path.join(themes, getFolderName(rootFolder)), { + destinationRoot: rootFolder, + }); + deleteFolder(themes); + } +}; + +setupTheme(); diff --git a/static/css/custom.css b/static/css/custom.css deleted file mode 100644 index 8a65a82..0000000 --- a/static/css/custom.css +++ /dev/null @@ -1 +0,0 @@ -/* Custom CSS */ diff --git a/static/favicon.ico b/static/favicon.ico deleted file mode 100644 index 01d4f60..0000000 Binary files a/static/favicon.ico and /dev/null differ diff --git a/static/images/gopher.png b/static/images/gopher.png deleted file mode 100644 index 10cb066..0000000 Binary files a/static/images/gopher.png and /dev/null differ diff --git a/static/images/logo.png b/static/images/logo.png deleted file mode 100644 index 1778262..0000000 Binary files a/static/images/logo.png and /dev/null differ diff --git a/static/images/windows/cicd.png b/static/images/windows/cicd.png deleted file mode 100644 index 8ff4aad..0000000 Binary files a/static/images/windows/cicd.png and /dev/null differ diff --git a/static/js/custom.js b/static/js/custom.js deleted file mode 100644 index 2ce536a..0000000 --- a/static/js/custom.js +++ /dev/null @@ -1 +0,0 @@ -/* Custom JS */ diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100755 index 0000000..1990541 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,102 @@ +const fs = require("fs"); +const path = require("path"); +const themePath = path.join(__dirname, "data/theme.json"); +const themeRead = fs.readFileSync(themePath, "utf8"); +const theme = JSON.parse(themeRead); + +let font_base = Number(theme.fonts.font_size.base.replace("px", "")); +let font_scale = Number(theme.fonts.font_size.scale); +let h6 = font_base / font_base; +let h5 = h6 * font_scale; +let h4 = h5 * font_scale; +let h3 = h4 * font_scale; +let h2 = h3 * font_scale; +let h1 = h2 * font_scale; +let fontPrimary, fontPrimaryType, fontSecondary, fontSecondaryType; +if (theme.fonts.font_family.primary) { + fontPrimary = theme.fonts.font_family.primary + .replace(/\+/g, " ") + .replace(/:[ital,]*[ital@]*[wght@]*[0-9,;]+/gi, ""); + fontPrimaryType = theme.fonts.font_family.primary_type; +} +if (theme.fonts.font_family.secondary) { + fontSecondary = theme.fonts.font_family.secondary + .replace(/\+/g, " ") + .replace(/:[ital,]*[ital@]*[wght@]*[0-9,;]+/gi, ""); + fontSecondaryType = theme.fonts.font_family.secondary_type; +} + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./hugo_stats.json"], + safelist: [{ pattern: /^swiper-/ }], + darkMode: "class", + theme: { + screens: { + sm: "540px", + md: "768px", + lg: "1024px", + xl: "1280px", + "2xl": "1536px", + }, + container: { + center: true, + padding: "2rem", + }, + extend: { + colors: { + text: theme.colors.default.text_color.default, + light: theme.colors.default.text_color.light, + dark: theme.colors.default.text_color.dark, + primary: theme.colors.default.theme_color.primary, + secondary: theme.colors.default.theme_color.secondary, + body: theme.colors.default.theme_color.body, + border: theme.colors.default.theme_color.border, + "theme-light": theme.colors.default.theme_color.theme_light, + "theme-dark": theme.colors.default.theme_color.theme_dark, + darkmode: { + text: theme.colors.darkmode.text_color.default, + light: theme.colors.darkmode.text_color.light, + dark: theme.colors.darkmode.text_color.dark, + primary: theme.colors.darkmode.theme_color.primary, + secondary: theme.colors.darkmode.theme_color.secondary, + body: theme.colors.darkmode.theme_color.body, + border: theme.colors.darkmode.theme_color.border, + "theme-light": theme.colors.darkmode.theme_color.theme_light, + "theme-dark": theme.colors.darkmode.theme_color.theme_dark, + }, + }, + fontSize: { + base: font_base + "px", + h1: h1 + "rem", + "h1-sm": h1 * 0.8 + "rem", + h2: h2 + "rem", + "h2-sm": h2 * 0.8 + "rem", + h3: h3 + "rem", + "h3-sm": h3 * 0.8 + "rem", + h4: h4 + "rem", + h5: h5 + "rem", + h6: h6 + "rem", + }, + fontFamily: { + primary: [fontPrimary, fontPrimaryType], + secondary: [fontSecondary, fontSecondaryType], + }, + }, + }, + plugins: [ + require("@tailwindcss/typography"), + require("@tailwindcss/forms"), + require("tailwind-bootstrap-grid")({ + generateContainer: false, + gridGutterWidth: "2rem", + gridGutters: { + 1: "0.25rem", + 2: "0.5rem", + 3: "1rem", + 4: "1.5rem", + 5: "3rem", + }, + }), + ], +};