From 0bd550b561437fd73ae48c189a9a73ddaf9e5eff Mon Sep 17 00:00:00 2001 From: Erick Ruiz de Chavez Date: Tue, 7 Jan 2025 08:38:30 -0500 Subject: [PATCH] Remove Jekyll; Add new workflow scripts --- .gitignore | 6 +- Dockerfile.gemini => Dockerfile | 8 +- Dockerfile.jekyll | 9 -- Gemfile | 9 -- Gemfile.lock | 84 -------------- Makefile | 40 ++----- _config.yml | 19 ---- _draft.py | 13 +++ .../live-long-and-prosper-with-these-tools.md | 5 +- generate_capsule.py => _generate.py | 12 +- _includes/footer.html | 3 - _includes/head.html | 7 -- _includes/header.html | 22 ---- _layouts/article.gmi | 3 +- _layouts/article.html | 14 --- _layouts/default.html | 15 --- _layouts/home.html | 21 ---- ...2-22-regenerating-my-blog-a-fresh-start.md | 1 - ...at-minimum-automating-laptop-power-mode.md | 2 +- _publish.py | 62 +++++++++++ _sass/_pygments_default.scss | 61 ---------- _sass/main.scss | 105 ------------------ _unpublish.py | 59 ++++++++++ assets/panda.webp | Bin 4362 -> 0 bytes assets/styles.scss | 3 - compose.yaml | 17 +-- requirements.txt | 2 + 27 files changed, 168 insertions(+), 434 deletions(-) rename Dockerfile.gemini => Dockerfile (52%) delete mode 100644 Dockerfile.jekyll delete mode 100644 Gemfile delete mode 100644 Gemfile.lock delete mode 100644 _config.yml create mode 100644 _draft.py rename generate_capsule.py => _generate.py (89%) delete mode 100644 _includes/footer.html delete mode 100644 _includes/head.html delete mode 100644 _includes/header.html delete mode 100644 _layouts/article.html delete mode 100644 _layouts/default.html delete mode 100644 _layouts/home.html create mode 100644 _publish.py delete mode 100644 _sass/_pygments_default.scss delete mode 100644 _sass/main.scss create mode 100644 _unpublish.py delete mode 100644 assets/panda.webp delete mode 100644 assets/styles.scss diff --git a/.gitignore b/.gitignore index 5fb49ab..79dbf74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,2 @@ -.sass-cache -.jekyll-cache -.jekyll-metadata -vendor -_site +.venv _capsule diff --git a/Dockerfile.gemini b/Dockerfile similarity index 52% rename from Dockerfile.gemini rename to Dockerfile index 426d8c4..a66f14b 100644 --- a/Dockerfile.gemini +++ b/Dockerfile @@ -2,7 +2,9 @@ FROM alpine:latest WORKDIR /app COPY requirements.txt . -RUN apk add python3 py3-pip lowdown \ +RUN apk update \ + && apk add python3 py3-pip lowdown \ && python3 -m pip install -r requirements.txt --break-system-packages -COPY generate_capsule.py . -ENTRYPOINT [ "python3", "generate_capsule.py" ] +COPY *.py ./ +ENTRYPOINT [ "python3" ] +CMD [ "_generate.py" ] diff --git a/Dockerfile.jekyll b/Dockerfile.jekyll deleted file mode 100644 index 1ffcae4..0000000 --- a/Dockerfile.jekyll +++ /dev/null @@ -1,9 +0,0 @@ -FROM ruby:3.3 - -EXPOSE 4000 -WORKDIR /app - -COPY Gemfile . -RUN gem install bundler && bundle install -ENTRYPOINT [ "bundle", "exec", "jekyll" ] -CMD [ "serve", "-H", "0.0.0.0", "--force_polling", "--livereload", "--drafts" ] diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 3cc3941..0000000 --- a/Gemfile +++ /dev/null @@ -1,9 +0,0 @@ -source "https://rubygems.org" - -gem "jekyll", "~> 4.3.4" - -group :jekyll_plugins do - gem "jekyll-feed", "~> 0.12" - gem "jekyll-compose", "~> 0.12.0" - gem "jekyll-commonmark" -end diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 1183025..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,84 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) - bigdecimal (3.1.9) - colorator (1.1.0) - commonmarker (0.23.11) - concurrent-ruby (1.3.4) - em-websocket (0.5.3) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0) - eventmachine (1.2.7) - ffi (1.17.0-x86_64-linux-gnu) - forwardable-extended (2.6.0) - google-protobuf (4.29.2-x86_64-linux) - bigdecimal - rake (>= 13) - http_parser.rb (0.8.0) - i18n (1.14.6) - concurrent-ruby (~> 1.0) - jekyll (4.3.4) - addressable (~> 2.4) - colorator (~> 1.0) - em-websocket (~> 0.5) - i18n (~> 1.0) - jekyll-sass-converter (>= 2.0, < 4.0) - jekyll-watch (~> 2.0) - kramdown (~> 2.3, >= 2.3.1) - kramdown-parser-gfm (~> 1.0) - liquid (~> 4.0) - mercenary (>= 0.3.6, < 0.5) - pathutil (~> 0.9) - rouge (>= 3.0, < 5.0) - safe_yaml (~> 1.0) - terminal-table (>= 1.8, < 4.0) - webrick (~> 1.7) - jekyll-commonmark (1.4.0) - commonmarker (~> 0.22) - jekyll-compose (0.12.0) - jekyll (>= 3.7, < 5.0) - jekyll-feed (0.17.0) - jekyll (>= 3.7, < 5.0) - jekyll-sass-converter (3.0.0) - sass-embedded (~> 1.54) - jekyll-watch (2.2.1) - listen (~> 3.0) - kramdown (2.5.1) - rexml (>= 3.3.9) - kramdown-parser-gfm (1.1.0) - kramdown (~> 2.0) - liquid (4.0.4) - listen (3.9.0) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) - mercenary (0.4.0) - pathutil (0.16.2) - forwardable-extended (~> 2.6) - public_suffix (6.0.1) - rake (13.2.1) - rb-fsevent (0.11.2) - rb-inotify (0.11.1) - ffi (~> 1.0) - rexml (3.4.0) - rouge (4.5.1) - safe_yaml (1.0.5) - sass-embedded (1.83.0-x86_64-linux-gnu) - google-protobuf (~> 4.28) - terminal-table (3.0.2) - unicode-display_width (>= 1.1.1, < 3) - unicode-display_width (2.6.0) - webrick (1.9.1) - -PLATFORMS - x86_64-linux-gnu - -DEPENDENCIES - jekyll (~> 4.3.4) - jekyll-commonmark - jekyll-compose (~> 0.12.0) - jekyll-feed (~> 0.12) - -BUNDLED WITH - 2.6.2 diff --git a/Makefile b/Makefile index 5693257..3d96269 100644 --- a/Makefile +++ b/Makefile @@ -1,39 +1,21 @@ -args := +env := development -default: clean start - -start: - docker compose up -d - -rebuild: - docker compose up -d --build - -stop: - docker compose down +default: clean generate clean: - \rm -rf .jekyll-cache .saas-cache _site _capsule - docker compose down -v + \rm -rf _capsule -logs: - docker compose logs --follow --since 1m - -build: build-jekyll build-gemini - -build-gemini: - docker compose run --rm gemini - -build-jekyll: - docker compose run --rm jekyll build - -install: - docker compose run --rm --entrypoint bash jekyll -c bundle add ${args} +generate: + docker compose run --rm -e GEMINI_ENV=${env} gemini draft: - docker compose run --rm jekyll draft ${args} + docker compose run --rm -e GEMINI_ENV=${env} gemini _draft.py publish: - docker compose run --rm jekyll publish ${args} + docker compose run --rm -e GEMINI_ENV=${env} gemini _publish.py unpublish: - docker compose run --rm jekyll unpublish ${args} + docker compose run --rm -e GEMINI_ENV=${env} gemini _unpublish.py + +build: + docker compose build --no-cache diff --git a/_config.yml b/_config.yml deleted file mode 100644 index fe06aa8..0000000 --- a/_config.yml +++ /dev/null @@ -1,19 +0,0 @@ -title: Erick Ruiz de Chavez -author: Erick Ruiz de Chavez -url: https://erickruizdechavez.com - -exclude: - - Dockerfile* - - compose.yaml - - Makefile - - generate_capsule.py - - requirements.txt - -jekyll_compose: - default_front_matter: - drafts: - layout: article - posts: - layout: article - -markdown: CommonMark diff --git a/_draft.py b/_draft.py new file mode 100644 index 0000000..d0cb4c8 --- /dev/null +++ b/_draft.py @@ -0,0 +1,13 @@ +from slugify import slugify + +title = input("Enter draft's title: ") + +slug = slugify(title) +body = f""" +--- +title: {title} +--- +""" + +with open(f"_drafts/{slug}.gmi", mode="w", encoding="utf8") as file: + file.write(body) diff --git a/_drafts/live-long-and-prosper-with-these-tools.md b/_drafts/live-long-and-prosper-with-these-tools.md index 1360e09..79acca6 100644 --- a/_drafts/live-long-and-prosper-with-these-tools.md +++ b/_drafts/live-long-and-prosper-with-these-tools.md @@ -1,7 +1,6 @@ --- -layout: article -title: Live Long and Prosper with These Tools links: inline +title: Live Long and Prosper with These Tools --- I'm not entirely sure why, but I love trying out and testing productivity tools—especially those geared toward programming. Over the years, I've explored and used a wide variety of tools, and these are the ones I can't live without. Below, I've listed them in alphabetical order. For each, I'll include a brief description and explain why it stands out to me. When time and inspiration allow, I plan to write dedicated articles diving deeper into each tool. I'll also do my best to keep this list updated as I discover new favorites or stop using the current ones. @@ -77,4 +76,4 @@ To help you understand the labels I've used, here's a quick guide to the emojis ## Honorary Mentions -- Keyboard Maestro (💻,1️⃣,🌐) +- Keyboard Maestro (💻,1️⃣,🌐) \ No newline at end of file diff --git a/generate_capsule.py b/_generate.py similarity index 89% rename from generate_capsule.py rename to _generate.py index 3a20158..b6a5f88 100644 --- a/generate_capsule.py +++ b/_generate.py @@ -20,10 +20,12 @@ with open("_includes/links.gmi", mode="r", encoding="utf8") as file: links_body = file.read() articles = glob.glob("_posts/*.md") +articles.sort() + if os.getenv("GEMINI_ENV") != "production": articles += glob.glob("_drafts/*.md") -today = datetime.today().strftime("%Y-%m-%d") +today = datetime.today().strftime("%F") year = datetime.today().strftime("%Y") articles_list = [] @@ -38,8 +40,9 @@ for article in articles: links = fm.get("links", "end") result = subprocess.run( - ["lowdown", "-tgemini", f"--gemini-link-{links}", article], + ["lowdown", "-tgemini", f"--gemini-link-{links}", "--out-no-smarty", article], capture_output=True, + check=True, text=True, ) article_content = result.stdout @@ -66,7 +69,10 @@ for article in articles: articles_list.sort(reverse=True) result = subprocess.run( - ["lowdown", "-tgemini", "index.md"], capture_output=True, text=True + ["lowdown", "-tgemini", "index.md"], + capture_output=True, + check=True, + text=True, ) home_content = result.stdout diff --git a/_includes/footer.html b/_includes/footer.html deleted file mode 100644 index 1983435..0000000 --- a/_includes/footer.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/_includes/head.html b/_includes/head.html deleted file mode 100644 index 38ed18d..0000000 --- a/_includes/head.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - {{ site.title }} - diff --git a/_includes/header.html b/_includes/header.html deleted file mode 100644 index 09d4bbf..0000000 --- a/_includes/header.html +++ /dev/null @@ -1,22 +0,0 @@ - diff --git a/_layouts/article.gmi b/_layouts/article.gmi index 85e7f08..79cf1f8 100644 --- a/_layouts/article.gmi +++ b/_layouts/article.gmi @@ -1,5 +1,6 @@ %%header%% -=> index.gmi Front page +=> / Front page + ## %%title%% %%body%% %%links%% diff --git a/_layouts/article.html b/_layouts/article.html deleted file mode 100644 index ab0296a..0000000 --- a/_layouts/article.html +++ /dev/null @@ -1,14 +0,0 @@ ---- -layout: default ---- -
-

- {{ page.title | escape }} -

- -
- {{ content }} -
-
diff --git a/_layouts/default.html b/_layouts/default.html deleted file mode 100644 index e3d9223..0000000 --- a/_layouts/default.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - {%- include head.html -%} - - - {%- include header.html -%} - -
- {{ content }} -
- - {%- include footer.html -%} - - diff --git a/_layouts/home.html b/_layouts/home.html deleted file mode 100644 index b4c2d6b..0000000 --- a/_layouts/home.html +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: default ---- -
- {{ content }} - - {%- if site.posts.size > 0 -%} -
-

Articles

- -
- {%- endif -%} -
diff --git a/_posts/2024-12-22-regenerating-my-blog-a-fresh-start.md b/_posts/2024-12-22-regenerating-my-blog-a-fresh-start.md index 72e7ad1..1f69659 100644 --- a/_posts/2024-12-22-regenerating-my-blog-a-fresh-start.md +++ b/_posts/2024-12-22-regenerating-my-blog-a-fresh-start.md @@ -1,5 +1,4 @@ --- -layout: article title: 'Regenerating My Blog: A Fresh Start' date: 2024-12-22 00:45 +0000 --- diff --git a/_posts/2024-12-29-energy-shields-at-minimum-automating-laptop-power-mode.md b/_posts/2024-12-29-energy-shields-at-minimum-automating-laptop-power-mode.md index 0fc4b93..f9f4e67 100644 --- a/_posts/2024-12-29-energy-shields-at-minimum-automating-laptop-power-mode.md +++ b/_posts/2024-12-29-energy-shields-at-minimum-automating-laptop-power-mode.md @@ -1,8 +1,8 @@ --- -layout: article title: 'Energy Shields at Minimum: Automating Laptop Power Mode' date: 2024-12-29 12:35 +0000 --- + I love automation—not just home automation but anything that simplifies my life. For example, I use a Shortcut on my iPhone that automatically turns on Low Power Mode when the battery drops below a certain percentage. This morning, while working on my laptop, I realized that macOS allows Low Power Mode to be set to "always on" when on battery power, but it doesn't provide an option like "only when the battery level is below X%." Naturally, I decided to create a script to achieve the same functionality on my laptop that I already enjoy on my phone. diff --git a/_publish.py b/_publish.py new file mode 100644 index 0000000..d10256c --- /dev/null +++ b/_publish.py @@ -0,0 +1,62 @@ +import glob +import os +import sys +from datetime import UTC, datetime + +import frontmatter + +articles = glob.glob("_drafts/*.md") +articles.sort() +titles = [] + +if len(articles) == 0: + print("No drafts found") + sys.exit(1) + +for article in articles: + fm = frontmatter.load(article) + titles.append(fm.get("title")) + +print("\nAvailable drafts:\n") + +for index, title in enumerate(titles): + print(f"\t[{index + 1}] {title}") + +print("") + +article = "" +selection = "" +index = -1 + +while index < 0 or index > len(articles): + try: + index = int(selection) - 1 + + if index == -1: + sys.exit(0) + + article = articles[index] + + except (ValueError, IndexError): + selection = input("Enter a number (0 to exit): ") + +now = datetime.now(UTC) +file_date = now.strftime("%F") +frontmatter_date = now.strftime("%F %H:%m %z") + +fm = frontmatter.load(article) +fm.metadata["date"] = frontmatter_date + +with open( + article.replace("_drafts/", f"_posts/{file_date}-"), + mode="w", + encoding="utf8", +) as file: + file.write(frontmatter.dumps(fm)) + +try: + os.remove(article) +except PermissionError: + print(f"Permission denied to delete '{article}'.") +except Exception as e: + print(f"An error occurred: {e}") diff --git a/_sass/_pygments_default.scss b/_sass/_pygments_default.scss deleted file mode 100644 index 8070f2f..0000000 --- a/_sass/_pygments_default.scss +++ /dev/null @@ -1,61 +0,0 @@ -.highlight .hll { background-color: #ffffcc } -.highlight .c { color: #408080; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #008000; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #BC7A00 } /* Comment.Preproc */ -.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #FF0000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #808080 } /* Generic.Output */ -.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0040D0 } /* Generic.Traceback */ -.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #008000 } /* Keyword.Pseudo */ -.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #B00040 } /* Keyword.Type */ -.highlight .m { color: #666666 } /* Literal.Number */ -.highlight .s { color: #BA2121 } /* Literal.String */ -.highlight .na { color: #7D9029 } /* Name.Attribute */ -.highlight .nb { color: #008000 } /* Name.Builtin */ -.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.highlight .no { color: #880000 } /* Name.Constant */ -.highlight .nd { color: #AA22FF } /* Name.Decorator */ -.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #0000FF } /* Name.Function */ -.highlight .nl { color: #A0A000 } /* Name.Label */ -.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #19177C } /* Name.Variable */ -.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #666666 } /* Literal.Number.Float */ -.highlight .mh { color: #666666 } /* Literal.Number.Hex */ -.highlight .mi { color: #666666 } /* Literal.Number.Integer */ -.highlight .mo { color: #666666 } /* Literal.Number.Oct */ -.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ -.highlight .sc { color: #BA2121 } /* Literal.String.Char */ -.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ -.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -.highlight .sx { color: #008000 } /* Literal.String.Other */ -.highlight .sr { color: #BB6688 } /* Literal.String.Regex */ -.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ -.highlight .ss { color: #19177C } /* Literal.String.Symbol */ -.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #19177C } /* Name.Variable.Class */ -.highlight .vg { color: #19177C } /* Name.Variable.Global */ -.highlight .vi { color: #19177C } /* Name.Variable.Instance */ -.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ diff --git a/_sass/main.scss b/_sass/main.scss deleted file mode 100644 index 42d803a..0000000 --- a/_sass/main.scss +++ /dev/null @@ -1,105 +0,0 @@ -@use "pygments_default"; - -html { - box-sizing: border-box; -} - -*, -*:before, -*:after { - box-sizing: inherit; -} - -@media screen and (min-width: 801px) { - body { - align-items: center; - } -} - -html, body { - width: 100%; - height: 100%; - margin: auto; -} - -body { - font-family: sans-serif; - max-width: 800px; - display: flex; - flex-direction: column; -} - -h1 { - font-size: 1.3rem; -} - -h2 { - font-size: 1.2rem; -} - -h3 { - font-size: 1.1rem; -} - -h4,h5,h6 { - font-size: 1rem; -} - -main { - padding: 20px; - opacity: .8; - line-height: 1.5rem; -} - -a { - text-decoration: none; -} - -.site_header { - padding-top: 40px; - display: flex; - flex-direction: column; - align-items: center; - -} - -.panda { - width: 200px; - height: 200px; - border-radius: 100px; -} - -.site_links { - display: flex; - flex-wrap: wrap; - justify-content: center; - padding: 40px 0; - - &__link { - display: block; - padding: 0 20px; - opacity: .8; - } -} - -.site_footer { - padding: 40px 20px; - opacity: .3; -} - -.articles { - opacity: .8; - padding-top: 40px; -} - -.article { - &__back { - font-size: .9rem; - } - - &__published { - font-style: italic; - font-size: .9rem; - opacity: .6; - } -} diff --git a/_unpublish.py b/_unpublish.py new file mode 100644 index 0000000..8c75ce6 --- /dev/null +++ b/_unpublish.py @@ -0,0 +1,59 @@ +import glob +import os +import sys +from datetime import datetime + +import frontmatter + +articles = glob.glob("_posts/*.md") +articles.sort(reverse=True) +titles = [] + +if len(articles) == 0: + print("No articles found") + sys.exit(1) + +for article in articles: + fm = frontmatter.load(article) + titles.append(fm.get("title")) + +print("\nAvailable articles:\n") + +for index, title in enumerate(titles): + print(f"\t[{index + 1}] {title}") + +print("") + +article = "" +selection = "" +index = -1 + +while index < 0 or index > len(articles): + try: + index = int(selection) - 1 + + if index == -1: + sys.exit(0) + + article = articles[index] + + except (ValueError, IndexError): + selection = input("Enter a number (0 to exit): ") + +fm = frontmatter.load(article) +file_date = datetime.fromisoformat(fm.metadata["date"]).strftime("%Y-%m-%d") +del fm.metadata["date"] + +with open( + article.replace(f"_posts/{file_date}-", "_drafts/"), + mode="w", + encoding="utf8", +) as file: + file.write(frontmatter.dumps(fm)) + +try: + os.remove(article) +except PermissionError: + print(f"Permission denied to delete '{article}'.") +except Exception as e: + print(f"An error occurred: {e}") diff --git a/assets/panda.webp b/assets/panda.webp deleted file mode 100644 index ca5b8c7406791e0ef23931b8c20776c4af07e28c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4362 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