Simplify usage of custom justfile targets
This commit is contained in:
parent
7ae5406548
commit
2b450e1b51
7 changed files with 53 additions and 31 deletions
5
content/newsletter/craft-letter-14.md
Normal file
5
content/newsletter/craft-letter-14.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
Title: Lettre n°14 — 09 mars 2026
|
||||||
|
Date: 2026-03-09 09:00
|
||||||
|
Category: Newsletter
|
||||||
|
JsonLD: <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BlogPosting", "name": "Lettre n°14", "description": "Lettre de veille technologique en développement logiciel", "image": [ "https://www.craftletter.fr/images/craftletter.svg" ], "datePublished": "Mon Mar 09 2026 09:00:00 GMT+0200 (Coordinated Universal Time)", "author": { "@type": "Person", "name": "Pascal Le Merrer", "url": "https://www.linkedin.com/in/pascal-le-merrer/" } } </script>
|
||||||
|
<img class="logo" alt="Logo Craft Letter" src="{static}/images/craftletter.svg">
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
Title: Accueil
|
Title: Accueil
|
||||||
Date: 2026-03-02 09:00
|
Date: 2026-03-09 09:00
|
||||||
URL:
|
URL:
|
||||||
save_as: index.html
|
save_as: index.html
|
||||||
Category: Home
|
Category: Home
|
||||||
JsonLD: { "@context": "https://schema.org", "@type": "WebPage", "name": "Accueil", "description": "Lettre de veille technologique en développement logiciel", "image": [ "https://www.craftletter.fr/images/craftletter.svg" ], "datePublished": "Mon Mar 02 2026 09:00:00 GMT+0200 (Coordinated Universal Time)", "author": { "@type": "Person", "name": "Pascal Le Merrer", "url": "https://www.linkedin.com/in/pascal-le-merrer/" } }
|
JsonLD: { "@context": "https://schema.org", "@type": "WebPage", "name": "Accueil", "description": "Lettre de veille technologique en développement logiciel", "image": [ "https://www.craftletter.fr/images/craftletter.svg" ], "datePublished": "Mon Mar 09 2026 09:00:00 GMT+0200 (Coordinated Universal Time)", "author": { "@type": "Person", "name": "Pascal Le Merrer", "url": "https://www.linkedin.com/in/pascal-le-merrer/" } }
|
||||||
|
|
||||||
<img class="logo" alt="Logo Craft Letter" src="{static}/images/craftletter.svg">
|
<img class="logo" alt="Logo Craft Letter" src="{static}/images/craftletter.svg">
|
||||||
|
|
||||||
|
|
||||||
# La [lettre n°13]({filename}/newsletter/craft-letter-13.md) est parue !
|
# La [lettre n°14]({filename}/newsletter/craft-letter-14.md) est parue !
|
||||||
|
|
||||||
La Craft Letter est une newsletter hebdomadaire dans laquelle je partage des articles
|
La Craft Letter est une newsletter hebdomadaire dans laquelle je partage des articles
|
||||||
issus de ma veille technologique. Vous y trouverez des articles relatifs au développement logiciel d'une façon générale, qu'il soit front-end, back-end ou autre. Mais aussi des articles consacrés à l'architecture logicielle, la méthodologie, les outils, des projets open source, des conférences...
|
issus de ma veille technologique. Vous y trouverez des articles relatifs au développement logiciel d'une façon générale, qu'il soit front-end, back-end ou autre. Mais aussi des articles consacrés à l'architecture logicielle, la méthodologie, les outils, des projets open source, des conférences...
|
||||||
|
|
@ -37,6 +37,7 @@ Pour savoir qui je suis, ou pourquoi j'écris cette lettre, je vous invite à vo
|
||||||
|
|
||||||
# Archives
|
# Archives
|
||||||
|
|
||||||
|
* [Lettre n°14]({filename}/newsletter/craft-letter-14.md)
|
||||||
* [Lettre n°13]({filename}/newsletter/craft-letter-13.md)
|
* [Lettre n°13]({filename}/newsletter/craft-letter-13.md)
|
||||||
* [Lettre n°12]({filename}/newsletter/craft-letter-12.md)
|
* [Lettre n°12]({filename}/newsletter/craft-letter-12.md)
|
||||||
* [Lettre n°11]({filename}/newsletter/craft-letter-11.md)
|
* [Lettre n°11]({filename}/newsletter/craft-letter-11.md)
|
||||||
|
|
|
||||||
18
justfile
18
justfile
|
|
@ -40,8 +40,8 @@ devserver-global:
|
||||||
pelican -lr content -o output -s "{{CONFFILE}}" {{PELICANOPTS}} -b 0.0.0.0
|
pelican -lr content -o output -s "{{CONFFILE}}" {{PELICANOPTS}} -b 0.0.0.0
|
||||||
|
|
||||||
# generate using production settings
|
# generate using production settings
|
||||||
publish number:
|
publish:
|
||||||
just format {{number}}
|
just format
|
||||||
pelican content -o output -s "{{PUBLISHCONF}}" {{PELICANOPTS}}
|
pelican content -o output -s "{{PUBLISHCONF}}" {{PELICANOPTS}}
|
||||||
rsync -e ssh -av --delete-after /Users/pascal/Documents/craft-letter/output/ craftletter@ssh-craftletter.alwaysdata.net:/home/craftletter/www
|
rsync -e ssh -av --delete-after /Users/pascal/Documents/craft-letter/output/ craftletter@ssh-craftletter.alwaysdata.net:/home/craftletter/www
|
||||||
|
|
||||||
|
|
@ -50,15 +50,15 @@ ssh:
|
||||||
ssh craftletter@ssh-craftletter.alwaysdata.net
|
ssh craftletter@ssh-craftletter.alwaysdata.net
|
||||||
|
|
||||||
# Create the skeleton for a new issue of the newsletter, and reference it into the home page
|
# Create the skeleton for a new issue of the newsletter, and reference it into the home page
|
||||||
new number:
|
new:
|
||||||
PYTHONPATH=PWD venv/bin/python ./scripts/create_newsletter.py --number={{number}}
|
PYTHONPATH=PWD venv/bin/python ./scripts/create_newsletter.py
|
||||||
|
|
||||||
# generate HTML email
|
# generate HTML email
|
||||||
mail number:
|
mail:
|
||||||
just format {{number}}
|
just format
|
||||||
PYTHONPATH=PWD venv/bin/python ./scripts/prepare_email.py --number={{number}}
|
PYTHONPATH=PWD venv/bin/python ./scripts/prepare_email.py
|
||||||
|
|
||||||
# Format the content of a given newsletter
|
# Format the content of a given newsletter
|
||||||
format number:
|
format:
|
||||||
PYTHONPATH=PWD venv/bin/python ./scripts/format.py --number={{number}}
|
PYTHONPATH=PWD venv/bin/python ./scripts/format.py
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,17 +30,18 @@ def set_publication_date_for_seo(text: str, publication_date: datetime) -> str:
|
||||||
return text.replace("{DATE_UTC}", date_utc)
|
return text.replace("{DATE_UTC}", date_utc)
|
||||||
|
|
||||||
|
|
||||||
# Parse the command line arguments
|
# Find the number of the letter to create
|
||||||
parser = argparse.ArgumentParser()
|
destination_path = Path(f"./content/newsletter")
|
||||||
parser.add_argument("-n", "--number", required=True, type=int, help="Newsletter number")
|
p = destination_path.glob("craft-letter-*.md")
|
||||||
args = parser.parse_args()
|
files = [x for x in p if x.is_file()]
|
||||||
|
letter_number = len(files) + 1
|
||||||
|
|
||||||
# Load the newsletter template
|
# Load the newsletter template
|
||||||
template = Path("./template/newsletter.md")
|
template = Path("./template/newsletter.md")
|
||||||
content = template.read_text()
|
content = template.read_text()
|
||||||
|
|
||||||
|
|
||||||
new_content = content.replace("{LETTER_NUMBER}", str(args.number))
|
new_content = content.replace("{LETTER_NUMBER}", str(letter_number))
|
||||||
|
|
||||||
today = datetime.today()
|
today = datetime.today()
|
||||||
next_monday = get_next_weekday(today, MONDAY)
|
next_monday = get_next_weekday(today, MONDAY)
|
||||||
|
|
@ -52,9 +53,11 @@ new_content = set_publication_date_for_pelican(new_content, next_monday)
|
||||||
new_content = set_publication_date_for_seo(new_content, next_monday)
|
new_content = set_publication_date_for_seo(new_content, next_monday)
|
||||||
|
|
||||||
# Create the new file
|
# Create the new file
|
||||||
destination = Path(f"./content/newsletter/craft-letter-{args.number}.md")
|
destination = destination_path / f"craft-letter-{letter_number}.md"
|
||||||
destination.write_text(new_content)
|
destination.write_text(new_content)
|
||||||
|
|
||||||
|
print(f"Created letter #{letter_number}: {destination}")
|
||||||
|
|
||||||
# Load the homepage template
|
# Load the homepage template
|
||||||
template = Path("./template/index.md")
|
template = Path("./template/index.md")
|
||||||
content = template.read_text()
|
content = template.read_text()
|
||||||
|
|
@ -63,15 +66,15 @@ new_content = set_publication_date_for_pelican(content, next_monday)
|
||||||
|
|
||||||
new_content = set_publication_date_for_seo(new_content, next_monday)
|
new_content = set_publication_date_for_seo(new_content, next_monday)
|
||||||
|
|
||||||
link = (
|
link = f"[lettre n°{letter_number}]({{filename}}/newsletter/craft-letter-{letter_number}.md)"
|
||||||
f"[lettre n°{args.number}]({{filename}}/newsletter/craft-letter-{args.number}.md)"
|
|
||||||
)
|
|
||||||
new_content = new_content.replace("{LINK}", link)
|
new_content = new_content.replace("{LINK}", link)
|
||||||
|
|
||||||
for i in reversed(range(args.number)):
|
for i in reversed(range(letter_number)):
|
||||||
link = f"* [Lettre n°{i + 1}]({{filename}}/newsletter/craft-letter-{i + 1}.md)\n"
|
link = f"* [Lettre n°{i + 1}]({{filename}}/newsletter/craft-letter-{i + 1}.md)\n"
|
||||||
new_content += link
|
new_content += link
|
||||||
|
|
||||||
# Update the index page
|
# Update the index page
|
||||||
destination = Path("./content/pages/index.md")
|
destination = Path("./content/pages/index.md")
|
||||||
destination.write_text(new_content)
|
destination.write_text(new_content)
|
||||||
|
|
||||||
|
print(f"Updated index page: {destination}")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from util import get_latest_newsletter_number
|
||||||
|
|
||||||
REPLACEMENTS = {
|
REPLACEMENTS = {
|
||||||
" :": " :",
|
" :": " :",
|
||||||
|
|
@ -9,11 +10,12 @@ REPLACEMENTS = {
|
||||||
"'": "’",
|
"'": "’",
|
||||||
}
|
}
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("-n", "--number", required=True, type=int, help="Newsletter number")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
file = Path(f"./content/newsletter/craft-letter-{args.number}.md")
|
letter_number = get_latest_newsletter_number()
|
||||||
|
|
||||||
|
file = Path(f"./content/newsletter/craft-letter-{letter_number}.md")
|
||||||
|
|
||||||
|
print(f"Formatting letter #{letter_number}: {file}")
|
||||||
|
|
||||||
content = file.read_text()
|
content = file.read_text()
|
||||||
for value, replacement in REPLACEMENTS.items():
|
for value, replacement in REPLACEMENTS.items():
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,15 @@ import argparse
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from util import get_latest_newsletter_number
|
||||||
|
|
||||||
MAIL_GENERATOR = "/opt/homebrew/bin/mdtosendy"
|
MAIL_GENERATOR = "/opt/homebrew/bin/mdtosendy"
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
# Find the number of the letter to create
|
||||||
parser.add_argument("-n", "--number", required=True, type=int, help="Newsletter number")
|
letter_number = get_latest_newsletter_number()
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
source = Path(f"./content/newsletter/craft-letter-{args.number}.md")
|
source = Path(f"./content/newsletter/craft-letter-{letter_number}.md")
|
||||||
|
|
||||||
if not source.is_file():
|
if not source.is_file():
|
||||||
print(f"ERROR: file not found {source}")
|
print(f"ERROR: file not found {source}")
|
||||||
|
|
@ -35,7 +36,7 @@ destination.write_text(output)
|
||||||
|
|
||||||
subprocess.run([MAIL_GENERATOR, str(destination)])
|
subprocess.run([MAIL_GENERATOR, str(destination)])
|
||||||
|
|
||||||
generated_mail = Path("mail") / f"craft-letter-{args.number}.html"
|
generated_mail = Path("mail") / f"craft-letter-{letter_number}.html"
|
||||||
today = date.today()
|
today = date.today()
|
||||||
mail_content = generated_mail.read_text()
|
mail_content = generated_mail.read_text()
|
||||||
mail_content = mail_content.replace("{{YEAR}}", str(today.year))
|
mail_content = mail_content.replace("{{YEAR}}", str(today.year))
|
||||||
|
|
|
||||||
10
scripts/util.py
Normal file
10
scripts/util.py
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def get_latest_newsletter_number():
|
||||||
|
# Find the number of the latest newsletter
|
||||||
|
destination_path = Path(f"./content/newsletter")
|
||||||
|
p = destination_path.glob("craft-letter-*.md")
|
||||||
|
files = [x for x in p if x.is_file()]
|
||||||
|
letter_number = len(files)
|
||||||
|
return letter_number
|
||||||
Loading…
Add table
Add a link
Reference in a new issue