daily-writing 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- daily_writing-0.1.0/PKG-INFO +224 -0
- daily_writing-0.1.0/README.md +190 -0
- daily_writing-0.1.0/daily_writing/__init__.py +0 -0
- daily_writing-0.1.0/daily_writing/__main__.py +44 -0
- daily_writing-0.1.0/daily_writing/artifacts.py +58 -0
- daily_writing-0.1.0/daily_writing/atom.py +51 -0
- daily_writing-0.1.0/daily_writing/build.py +261 -0
- daily_writing-0.1.0/daily_writing/build_context.py +6 -0
- daily_writing-0.1.0/daily_writing/cms.py +316 -0
- daily_writing-0.1.0/daily_writing/fonts.py +485 -0
- daily_writing-0.1.0/daily_writing/html.py +565 -0
- daily_writing-0.1.0/daily_writing/i18n.py +46 -0
- daily_writing-0.1.0/daily_writing/models.py +561 -0
- daily_writing-0.1.0/daily_writing/normalize.py +98 -0
- daily_writing-0.1.0/daily_writing/serve.py +103 -0
- daily_writing-0.1.0/daily_writing/settings.py +423 -0
- daily_writing-0.1.0/daily_writing/social_preview.py +140 -0
- daily_writing-0.1.0/daily_writing/static/style.css +250 -0
- daily_writing-0.1.0/daily_writing/utils.py +101 -0
- daily_writing-0.1.0/pyproject.toml +142 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: daily-writing
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python-based static site generator for writing challenges (such as writober)
|
|
5
|
+
Author: Joachim Jablon
|
|
6
|
+
Author-email: Joachim Jablon <ewjoachim@gmail.com>
|
|
7
|
+
Requires-Dist: htpy>=25.10.0
|
|
8
|
+
Requires-Dist: pydantic>=2.12.4
|
|
9
|
+
Requires-Dist: pydantic-extra-types[pycountry]>=2.10.6
|
|
10
|
+
Requires-Dist: pydantic-settings>=2.12.0
|
|
11
|
+
Requires-Dist: brotli>=1.1.0
|
|
12
|
+
Requires-Dist: httpx>=0.28.1
|
|
13
|
+
Requires-Dist: fonttools>=4.60.1
|
|
14
|
+
Requires-Dist: pillow>=11.3.0
|
|
15
|
+
Requires-Dist: numpy>=2.3.4
|
|
16
|
+
Requires-Dist: feedgen>=1.0.0
|
|
17
|
+
Requires-Dist: tzlocal>=5.3.1
|
|
18
|
+
Requires-Dist: babel>=2.17.0
|
|
19
|
+
Requires-Dist: langcodes>=3.5.1
|
|
20
|
+
Requires-Dist: python-frontmatter>=1.1.0
|
|
21
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
22
|
+
Requires-Dist: flowmark>=0.6.1
|
|
23
|
+
Requires-Dist: markdown-it-py[plugins]>=4.0.0
|
|
24
|
+
Requires-Dist: yarl>=1.24.2
|
|
25
|
+
Requires-Dist: typing-extensions>=4.15.0
|
|
26
|
+
Requires-Dist: fastapi>=0.121.0 ; extra == 'server'
|
|
27
|
+
Requires-Dist: pdbpp>=0.11.7 ; extra == 'server'
|
|
28
|
+
Requires-Dist: uvicorn>=0.38.0 ; extra == 'server'
|
|
29
|
+
Requires-Dist: watchfiles>=1.1.1 ; extra == 'server'
|
|
30
|
+
Requires-Dist: websockets>=15.0.1 ; extra == 'server'
|
|
31
|
+
Requires-Python: >=3.14
|
|
32
|
+
Provides-Extra: server
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Daily Writing
|
|
36
|
+
|
|
37
|
+
This repo hosts an open source project that let you create a website presenting
|
|
38
|
+
daily writings, it was created with challenges like Writober or Writever in mind, but
|
|
39
|
+
it's not tied to a specific challenge.
|
|
40
|
+
|
|
41
|
+
Daily Writing mainly turns a bunch of markdown files (one per writing, at most one per
|
|
42
|
+
day), a configuration file, and a directory of static files into a static HTML website
|
|
43
|
+
that can be deployed.
|
|
44
|
+
|
|
45
|
+
Daily Writing offers various features, such as:
|
|
46
|
+
- A page for each writing and a homepage. A menu displaying a calendar with all the
|
|
47
|
+
writing for all months.
|
|
48
|
+
- Each day is linked to a "prompt" (as in writever/writober prompt), and can have a
|
|
49
|
+
title distinct from the prompt. This is useful if the title of your writing isn't
|
|
50
|
+
excatly the prompt you were given for the day. Also, if your prompts are in a given
|
|
51
|
+
language and the title is the prompt you translated to your language, it's nice.
|
|
52
|
+
The prompt will be displayed in the menu over the title.
|
|
53
|
+
- A given writing can span over multiple days and their associated prompts (e.g. if
|
|
54
|
+
you're late), and the calendar will reflect that. Those days may even be
|
|
55
|
+
non-consecutive. It's assumed that the title will be composed of multiple parts each
|
|
56
|
+
related to the associated prompts.
|
|
57
|
+
- Each day in a month is linked to a unique color, and the calendar is colored
|
|
58
|
+
accordingly. The homepage displays all the month's colors.
|
|
59
|
+
- Posts can be written in advance and will only be published of their date.
|
|
60
|
+
- Links to the previous and next writings, for binge-reading.
|
|
61
|
+
- Most metadata can be extracted from the name of the file and the writing markdown
|
|
62
|
+
title, but can then be normalized to frontmatter. Having a frontmatter is necessaryÒ when
|
|
63
|
+
using a CMS editor.
|
|
64
|
+
- Writings spanning multiple days will have a gradient between the colors of the days.
|
|
65
|
+
- A CMS, at `/admin` to update your writings and the site settings directly from your
|
|
66
|
+
browser.
|
|
67
|
+
- A RSS feed.
|
|
68
|
+
- Nice preview images when you post links to social networks.
|
|
69
|
+
- Discreet links to the GitHub source of each writing, to ease online edition or typo
|
|
70
|
+
reporting.
|
|
71
|
+
- Privacy-minded Google fonts, downloaded and optimized at build time so that you can
|
|
72
|
+
get various fonts without offering your reader's data to Google (damn, that was a
|
|
73
|
+
mess to code)
|
|
74
|
+
- Dark mode. No light mode. Contributions welcome on that.
|
|
75
|
+
- Support for extra custom CSS. If you want to add a light mode but just for yourself.
|
|
76
|
+
- Support of various locales, for date formats. Apart from dates and numbers, all the
|
|
77
|
+
words that appear on the interface are configurable, so technically, we support all
|
|
78
|
+
languages that Unicode supports.
|
|
79
|
+
- If you rename a file but want to keep its original URL too, you can add aliases.
|
|
80
|
+
|
|
81
|
+
Author's personal undying love of handcrafted open source and meticulous yak shaving
|
|
82
|
+
went into this project. He likes to think that it shows.
|
|
83
|
+
|
|
84
|
+
## An example:
|
|
85
|
+
|
|
86
|
+
https://writober.ewjoach.im/ is deployed from https://github.com/ewjoachim/writober,
|
|
87
|
+
using this project.
|
|
88
|
+
|
|
89
|
+
## Extraction rules
|
|
90
|
+
|
|
91
|
+
From a single file without a frontmatter, these are the informations being extracted.
|
|
92
|
+
Say the file is at `2026/10/03-hello.md` and its content starts with a title that says:
|
|
93
|
+
`# 03 - Bonjour`. The system will find that this writing covers a single day
|
|
94
|
+
(`2026-10-03`), its prompt is "hello" and its official title is "Bonjour".
|
|
95
|
+
|
|
96
|
+
Say the file is at `2026/10/03-hello-darkness.md` and its content starts with a title
|
|
97
|
+
that says: `# 03&04 - Bonjour, Obscurité`. The system will find that this writing covers
|
|
98
|
+
2 days:
|
|
99
|
+
- `2026-10-03` for which the prompt is `hello` and the title `Bonjour`,
|
|
100
|
+
- `2026-10-04` for which the prompt is `darkness` and the title `Obscurité`.
|
|
101
|
+
|
|
102
|
+
You can always override those values by editing the frontmatter.
|
|
103
|
+
|
|
104
|
+
## Installation
|
|
105
|
+
|
|
106
|
+
### Github
|
|
107
|
+
|
|
108
|
+
- [Create a repository from the template](https://repo.new?template_name=daily-writing-template&template_owner=ewjoachim)
|
|
109
|
+
- Adjust configuration with your details
|
|
110
|
+
- Your repository is deployed through GitHub actions (`https://<username>.github.io/<repo name>`)
|
|
111
|
+
- Write your stories
|
|
112
|
+
|
|
113
|
+
### Elsewhere
|
|
114
|
+
|
|
115
|
+
(Feel free to contribute installation instructions for other platforms).
|
|
116
|
+
|
|
117
|
+
## Configuration
|
|
118
|
+
|
|
119
|
+
Your website can be configured through either:
|
|
120
|
+
|
|
121
|
+
- `daily-writing.toml`,
|
|
122
|
+
- `pyproject.toml` within the `[tool.daily-writing]` section
|
|
123
|
+
- environments variables,
|
|
124
|
+
- CLI flags.
|
|
125
|
+
|
|
126
|
+
The same options are available in all ways, but they're not spelled out exactly the
|
|
127
|
+
same way. In the toml configuration and environment variables, words are
|
|
128
|
+
separated by `_` (underscores). In the flags, they're separated by `-` (dashes).
|
|
129
|
+
Environment variables are all upper case and prefixed with `DAILY_WRITING_`
|
|
130
|
+
|
|
131
|
+
Examples:
|
|
132
|
+
|
|
133
|
+
- `daily-writing.toml`: `server_url = "https://writober.ewjoach.im"`
|
|
134
|
+
- environments variables: `SERVER_URL="https://writober.ewjoach.im"`
|
|
135
|
+
- CLI flags: `--server-url="https://writober.ewjoach.im"`
|
|
136
|
+
|
|
137
|
+
The easiest and most up-to-date way to learn about every configuration option is
|
|
138
|
+
through: `daily-writing -h`. Feel free to consult [daily-writing.toml](https://github.com/ewjoachim/writober/blob/main/daily-writing.toml) for a working example.
|
|
139
|
+
|
|
140
|
+
> [!NOTE] There is one extra configuration element: setting the `GITHUB_TOKEN`
|
|
141
|
+
> environment variable will help you avoid rate limiting to GitHub's API. It's not added
|
|
142
|
+
> in the normal configuration elements to avoid accidentally committing it to your
|
|
143
|
+
> repository.
|
|
144
|
+
|
|
145
|
+
## CLI
|
|
146
|
+
|
|
147
|
+
There are 2 main subcommands:
|
|
148
|
+
|
|
149
|
+
- `daily-writing build` builds the website
|
|
150
|
+
- `daily-writing serve` launches a local development server that autobuilds the
|
|
151
|
+
website and auto-reload pages in your browser when changes are detected.
|
|
152
|
+
|
|
153
|
+
## CI/CD Deployment
|
|
154
|
+
|
|
155
|
+
Solutions like [GitHub Pages](https://docs.github.com/en/pages), [Cloudflare
|
|
156
|
+
Pages](https://developers.cloudflare.com/pages/), [GitLab
|
|
157
|
+
Pages](https://docs.gitlab.com/user/project/pages/), etc will make your life easier, as
|
|
158
|
+
you can edit the markdown files online or push them through git, and have them get
|
|
159
|
+
redeployed automatically daily and upon changes.
|
|
160
|
+
|
|
161
|
+
Here's a GitHub Actions workflow example:
|
|
162
|
+
|
|
163
|
+
```yaml
|
|
164
|
+
name: Deploy
|
|
165
|
+
|
|
166
|
+
on:
|
|
167
|
+
push:
|
|
168
|
+
branches: ["main"]
|
|
169
|
+
workflow_dispatch:
|
|
170
|
+
schedule:
|
|
171
|
+
- cron: "1 22,23 * 10 *" # Adjust to whenever relvant to you
|
|
172
|
+
|
|
173
|
+
permissions:
|
|
174
|
+
contents: read
|
|
175
|
+
pages: write
|
|
176
|
+
id-token: write
|
|
177
|
+
|
|
178
|
+
concurrency:
|
|
179
|
+
group: "pages"
|
|
180
|
+
cancel-in-progress: false
|
|
181
|
+
|
|
182
|
+
jobs:
|
|
183
|
+
build:
|
|
184
|
+
runs-on: ubuntu-latest
|
|
185
|
+
steps:
|
|
186
|
+
- uses: actions/checkout@v5
|
|
187
|
+
|
|
188
|
+
- name: Setup Pages
|
|
189
|
+
uses: actions/configure-pages@v5
|
|
190
|
+
|
|
191
|
+
- uses: astral-sh/setup-uv@v6
|
|
192
|
+
|
|
193
|
+
- name: Sphinx build
|
|
194
|
+
run: uv run daily-writing build
|
|
195
|
+
|
|
196
|
+
- name: Upload artifact
|
|
197
|
+
uses: actions/upload-pages-artifact@v4
|
|
198
|
+
with:
|
|
199
|
+
path: _build
|
|
200
|
+
|
|
201
|
+
# Deployment job
|
|
202
|
+
deploy:
|
|
203
|
+
environment:
|
|
204
|
+
name: github-pages
|
|
205
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
206
|
+
runs-on: ubuntu-latest
|
|
207
|
+
needs: build
|
|
208
|
+
steps:
|
|
209
|
+
- name: Deploy to GitHub Pages
|
|
210
|
+
id: deployment
|
|
211
|
+
uses: actions/deploy-pages@v4
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Adding new content
|
|
215
|
+
|
|
216
|
+
### Manual
|
|
217
|
+
Add and edit your markdown files in the repository via Git or via your
|
|
218
|
+
platform's Web interface.
|
|
219
|
+
|
|
220
|
+
### Online
|
|
221
|
+
Daily-Writing can come with a [CMS](https://github.com/sveltia/sveltia-cms), allowing
|
|
222
|
+
you to get a nice admin interface that doesn't require to interact with Git.
|
|
223
|
+
|
|
224
|
+
Integration is not yet 100% streamlined, but it's coming.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Daily Writing
|
|
2
|
+
|
|
3
|
+
This repo hosts an open source project that let you create a website presenting
|
|
4
|
+
daily writings, it was created with challenges like Writober or Writever in mind, but
|
|
5
|
+
it's not tied to a specific challenge.
|
|
6
|
+
|
|
7
|
+
Daily Writing mainly turns a bunch of markdown files (one per writing, at most one per
|
|
8
|
+
day), a configuration file, and a directory of static files into a static HTML website
|
|
9
|
+
that can be deployed.
|
|
10
|
+
|
|
11
|
+
Daily Writing offers various features, such as:
|
|
12
|
+
- A page for each writing and a homepage. A menu displaying a calendar with all the
|
|
13
|
+
writing for all months.
|
|
14
|
+
- Each day is linked to a "prompt" (as in writever/writober prompt), and can have a
|
|
15
|
+
title distinct from the prompt. This is useful if the title of your writing isn't
|
|
16
|
+
excatly the prompt you were given for the day. Also, if your prompts are in a given
|
|
17
|
+
language and the title is the prompt you translated to your language, it's nice.
|
|
18
|
+
The prompt will be displayed in the menu over the title.
|
|
19
|
+
- A given writing can span over multiple days and their associated prompts (e.g. if
|
|
20
|
+
you're late), and the calendar will reflect that. Those days may even be
|
|
21
|
+
non-consecutive. It's assumed that the title will be composed of multiple parts each
|
|
22
|
+
related to the associated prompts.
|
|
23
|
+
- Each day in a month is linked to a unique color, and the calendar is colored
|
|
24
|
+
accordingly. The homepage displays all the month's colors.
|
|
25
|
+
- Posts can be written in advance and will only be published of their date.
|
|
26
|
+
- Links to the previous and next writings, for binge-reading.
|
|
27
|
+
- Most metadata can be extracted from the name of the file and the writing markdown
|
|
28
|
+
title, but can then be normalized to frontmatter. Having a frontmatter is necessaryÒ when
|
|
29
|
+
using a CMS editor.
|
|
30
|
+
- Writings spanning multiple days will have a gradient between the colors of the days.
|
|
31
|
+
- A CMS, at `/admin` to update your writings and the site settings directly from your
|
|
32
|
+
browser.
|
|
33
|
+
- A RSS feed.
|
|
34
|
+
- Nice preview images when you post links to social networks.
|
|
35
|
+
- Discreet links to the GitHub source of each writing, to ease online edition or typo
|
|
36
|
+
reporting.
|
|
37
|
+
- Privacy-minded Google fonts, downloaded and optimized at build time so that you can
|
|
38
|
+
get various fonts without offering your reader's data to Google (damn, that was a
|
|
39
|
+
mess to code)
|
|
40
|
+
- Dark mode. No light mode. Contributions welcome on that.
|
|
41
|
+
- Support for extra custom CSS. If you want to add a light mode but just for yourself.
|
|
42
|
+
- Support of various locales, for date formats. Apart from dates and numbers, all the
|
|
43
|
+
words that appear on the interface are configurable, so technically, we support all
|
|
44
|
+
languages that Unicode supports.
|
|
45
|
+
- If you rename a file but want to keep its original URL too, you can add aliases.
|
|
46
|
+
|
|
47
|
+
Author's personal undying love of handcrafted open source and meticulous yak shaving
|
|
48
|
+
went into this project. He likes to think that it shows.
|
|
49
|
+
|
|
50
|
+
## An example:
|
|
51
|
+
|
|
52
|
+
https://writober.ewjoach.im/ is deployed from https://github.com/ewjoachim/writober,
|
|
53
|
+
using this project.
|
|
54
|
+
|
|
55
|
+
## Extraction rules
|
|
56
|
+
|
|
57
|
+
From a single file without a frontmatter, these are the informations being extracted.
|
|
58
|
+
Say the file is at `2026/10/03-hello.md` and its content starts with a title that says:
|
|
59
|
+
`# 03 - Bonjour`. The system will find that this writing covers a single day
|
|
60
|
+
(`2026-10-03`), its prompt is "hello" and its official title is "Bonjour".
|
|
61
|
+
|
|
62
|
+
Say the file is at `2026/10/03-hello-darkness.md` and its content starts with a title
|
|
63
|
+
that says: `# 03&04 - Bonjour, Obscurité`. The system will find that this writing covers
|
|
64
|
+
2 days:
|
|
65
|
+
- `2026-10-03` for which the prompt is `hello` and the title `Bonjour`,
|
|
66
|
+
- `2026-10-04` for which the prompt is `darkness` and the title `Obscurité`.
|
|
67
|
+
|
|
68
|
+
You can always override those values by editing the frontmatter.
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
### Github
|
|
73
|
+
|
|
74
|
+
- [Create a repository from the template](https://repo.new?template_name=daily-writing-template&template_owner=ewjoachim)
|
|
75
|
+
- Adjust configuration with your details
|
|
76
|
+
- Your repository is deployed through GitHub actions (`https://<username>.github.io/<repo name>`)
|
|
77
|
+
- Write your stories
|
|
78
|
+
|
|
79
|
+
### Elsewhere
|
|
80
|
+
|
|
81
|
+
(Feel free to contribute installation instructions for other platforms).
|
|
82
|
+
|
|
83
|
+
## Configuration
|
|
84
|
+
|
|
85
|
+
Your website can be configured through either:
|
|
86
|
+
|
|
87
|
+
- `daily-writing.toml`,
|
|
88
|
+
- `pyproject.toml` within the `[tool.daily-writing]` section
|
|
89
|
+
- environments variables,
|
|
90
|
+
- CLI flags.
|
|
91
|
+
|
|
92
|
+
The same options are available in all ways, but they're not spelled out exactly the
|
|
93
|
+
same way. In the toml configuration and environment variables, words are
|
|
94
|
+
separated by `_` (underscores). In the flags, they're separated by `-` (dashes).
|
|
95
|
+
Environment variables are all upper case and prefixed with `DAILY_WRITING_`
|
|
96
|
+
|
|
97
|
+
Examples:
|
|
98
|
+
|
|
99
|
+
- `daily-writing.toml`: `server_url = "https://writober.ewjoach.im"`
|
|
100
|
+
- environments variables: `SERVER_URL="https://writober.ewjoach.im"`
|
|
101
|
+
- CLI flags: `--server-url="https://writober.ewjoach.im"`
|
|
102
|
+
|
|
103
|
+
The easiest and most up-to-date way to learn about every configuration option is
|
|
104
|
+
through: `daily-writing -h`. Feel free to consult [daily-writing.toml](https://github.com/ewjoachim/writober/blob/main/daily-writing.toml) for a working example.
|
|
105
|
+
|
|
106
|
+
> [!NOTE] There is one extra configuration element: setting the `GITHUB_TOKEN`
|
|
107
|
+
> environment variable will help you avoid rate limiting to GitHub's API. It's not added
|
|
108
|
+
> in the normal configuration elements to avoid accidentally committing it to your
|
|
109
|
+
> repository.
|
|
110
|
+
|
|
111
|
+
## CLI
|
|
112
|
+
|
|
113
|
+
There are 2 main subcommands:
|
|
114
|
+
|
|
115
|
+
- `daily-writing build` builds the website
|
|
116
|
+
- `daily-writing serve` launches a local development server that autobuilds the
|
|
117
|
+
website and auto-reload pages in your browser when changes are detected.
|
|
118
|
+
|
|
119
|
+
## CI/CD Deployment
|
|
120
|
+
|
|
121
|
+
Solutions like [GitHub Pages](https://docs.github.com/en/pages), [Cloudflare
|
|
122
|
+
Pages](https://developers.cloudflare.com/pages/), [GitLab
|
|
123
|
+
Pages](https://docs.gitlab.com/user/project/pages/), etc will make your life easier, as
|
|
124
|
+
you can edit the markdown files online or push them through git, and have them get
|
|
125
|
+
redeployed automatically daily and upon changes.
|
|
126
|
+
|
|
127
|
+
Here's a GitHub Actions workflow example:
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
name: Deploy
|
|
131
|
+
|
|
132
|
+
on:
|
|
133
|
+
push:
|
|
134
|
+
branches: ["main"]
|
|
135
|
+
workflow_dispatch:
|
|
136
|
+
schedule:
|
|
137
|
+
- cron: "1 22,23 * 10 *" # Adjust to whenever relvant to you
|
|
138
|
+
|
|
139
|
+
permissions:
|
|
140
|
+
contents: read
|
|
141
|
+
pages: write
|
|
142
|
+
id-token: write
|
|
143
|
+
|
|
144
|
+
concurrency:
|
|
145
|
+
group: "pages"
|
|
146
|
+
cancel-in-progress: false
|
|
147
|
+
|
|
148
|
+
jobs:
|
|
149
|
+
build:
|
|
150
|
+
runs-on: ubuntu-latest
|
|
151
|
+
steps:
|
|
152
|
+
- uses: actions/checkout@v5
|
|
153
|
+
|
|
154
|
+
- name: Setup Pages
|
|
155
|
+
uses: actions/configure-pages@v5
|
|
156
|
+
|
|
157
|
+
- uses: astral-sh/setup-uv@v6
|
|
158
|
+
|
|
159
|
+
- name: Sphinx build
|
|
160
|
+
run: uv run daily-writing build
|
|
161
|
+
|
|
162
|
+
- name: Upload artifact
|
|
163
|
+
uses: actions/upload-pages-artifact@v4
|
|
164
|
+
with:
|
|
165
|
+
path: _build
|
|
166
|
+
|
|
167
|
+
# Deployment job
|
|
168
|
+
deploy:
|
|
169
|
+
environment:
|
|
170
|
+
name: github-pages
|
|
171
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
172
|
+
runs-on: ubuntu-latest
|
|
173
|
+
needs: build
|
|
174
|
+
steps:
|
|
175
|
+
- name: Deploy to GitHub Pages
|
|
176
|
+
id: deployment
|
|
177
|
+
uses: actions/deploy-pages@v4
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Adding new content
|
|
181
|
+
|
|
182
|
+
### Manual
|
|
183
|
+
Add and edit your markdown files in the repository via Git or via your
|
|
184
|
+
platform's Web interface.
|
|
185
|
+
|
|
186
|
+
### Online
|
|
187
|
+
Daily-Writing can come with a [CMS](https://github.com/sveltia/sveltia-cms), allowing
|
|
188
|
+
you to get a nice admin interface that doesn't require to interact with Git.
|
|
189
|
+
|
|
190
|
+
Integration is not yet 100% streamlined, but it's coming.
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from . import build, normalize
|
|
4
|
+
from . import settings as settings_module
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NoCommandSelected(Exception):
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MissingExtraDependency(Exception):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
settings = settings_module.Settings() # pyright: ignore[reportCallIssue]
|
|
17
|
+
logging.basicConfig(level=settings.verbosity)
|
|
18
|
+
logging.getLogger("fontTools").setLevel("WARNING")
|
|
19
|
+
logging.getLogger("markdown_it.rules_block").setLevel("INFO")
|
|
20
|
+
|
|
21
|
+
match settings.subcommand:
|
|
22
|
+
case settings_module.Build():
|
|
23
|
+
build.build(settings=settings)
|
|
24
|
+
case settings_module.Serve():
|
|
25
|
+
serve_website(settings=settings)
|
|
26
|
+
case settings_module.Normalize():
|
|
27
|
+
normalize.normalize(settings=settings)
|
|
28
|
+
case None:
|
|
29
|
+
raise NoCommandSelected("No command selected")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def serve_website(settings: settings_module.Settings):
|
|
33
|
+
try:
|
|
34
|
+
from . import serve # noqa: PLC0415
|
|
35
|
+
except ImportError as exc:
|
|
36
|
+
raise MissingExtraDependency(
|
|
37
|
+
"Extra dependencies `server` is required for daily-writing serve (`pip install daily-writing[server]`)"
|
|
38
|
+
) from exc
|
|
39
|
+
|
|
40
|
+
serve.serve(settings=settings)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
main()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import pathlib
|
|
3
|
+
from typing import Protocol, override
|
|
4
|
+
|
|
5
|
+
import pydantic
|
|
6
|
+
from pydantic import dataclasses as pdataclasses
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseArtifact(Protocol):
|
|
10
|
+
def write(self, destination: pathlib.Path): ...
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pdataclasses.dataclass(kw_only=True)
|
|
14
|
+
class TextArtifact:
|
|
15
|
+
path: pathlib.Path
|
|
16
|
+
contents: str
|
|
17
|
+
|
|
18
|
+
def write(self, destination: pathlib.Path):
|
|
19
|
+
if self.path.is_absolute():
|
|
20
|
+
raise ValueError("Only relative paths are accepted")
|
|
21
|
+
|
|
22
|
+
(destination / self.path).parent.mkdir(exist_ok=True, parents=True)
|
|
23
|
+
(destination / self.path).write_text(self.contents)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pdataclasses.dataclass(kw_only=True)
|
|
27
|
+
class HTMLArtifact(TextArtifact):
|
|
28
|
+
@override
|
|
29
|
+
def write(self, destination: pathlib.Path):
|
|
30
|
+
super().write(destination=destination)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pdataclasses.dataclass(
|
|
34
|
+
kw_only=True, config=pydantic.ConfigDict(arbitrary_types_allowed=True)
|
|
35
|
+
)
|
|
36
|
+
class BytesArtifact:
|
|
37
|
+
path: pathlib.Path
|
|
38
|
+
contents: io.BytesIO
|
|
39
|
+
|
|
40
|
+
def write(self, destination: pathlib.Path):
|
|
41
|
+
if self.path.is_absolute():
|
|
42
|
+
raise ValueError("Only relative paths are accepted")
|
|
43
|
+
|
|
44
|
+
(destination / self.path).parent.mkdir(exist_ok=True, parents=True)
|
|
45
|
+
(destination / self.path).write_bytes(self.contents.getvalue())
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pdataclasses.dataclass(kw_only=True)
|
|
49
|
+
class FileArtifact:
|
|
50
|
+
path: pathlib.Path
|
|
51
|
+
source: pathlib.Path
|
|
52
|
+
destination: pathlib.Path
|
|
53
|
+
|
|
54
|
+
def write(self, destination: pathlib.Path):
|
|
55
|
+
rel = self.path.relative_to(self.source)
|
|
56
|
+
destination = destination / self.destination / rel
|
|
57
|
+
destination.parent.mkdir(exist_ok=True, parents=True)
|
|
58
|
+
self.path.copy(destination)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import io
|
|
3
|
+
import pathlib
|
|
4
|
+
import zoneinfo
|
|
5
|
+
from typing import cast
|
|
6
|
+
|
|
7
|
+
from feedgen.feed import FeedGenerator
|
|
8
|
+
from pydantic import dataclasses as pdataclasses
|
|
9
|
+
|
|
10
|
+
from daily_writing import artifacts
|
|
11
|
+
|
|
12
|
+
from . import settings as settings_module
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pdataclasses.dataclass(kw_only=True)
|
|
16
|
+
class FeedEntryArtifact:
|
|
17
|
+
artifact_id: str
|
|
18
|
+
title: str
|
|
19
|
+
link: str
|
|
20
|
+
date: datetime.date
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Feed:
|
|
24
|
+
def __init__(self, settings: settings_module.Settings):
|
|
25
|
+
self.feed_gen: FeedGenerator = FeedGenerator()
|
|
26
|
+
self.feed_gen.id(str(settings.site_full_url))
|
|
27
|
+
self.feed_gen.title(settings.site_name)
|
|
28
|
+
self.feed_gen.author(name=settings.author)
|
|
29
|
+
self.feed_gen.link(href=str(settings.site_full_url), rel="self")
|
|
30
|
+
self.feed_gen.subtitle(settings.description)
|
|
31
|
+
self.feed_gen.language(str(settings.locale.locale))
|
|
32
|
+
self.timezone: str = settings.timezone
|
|
33
|
+
self.atom_path: pathlib.Path = settings.atom_path
|
|
34
|
+
|
|
35
|
+
def add_entry(self, item_id: str, title: str, link: str, date: datetime.date):
|
|
36
|
+
entry = self.feed_gen.add_entry() # pyright: ignore[reportUnknownVariableType]
|
|
37
|
+
entry.id(item_id)
|
|
38
|
+
entry.title(title)
|
|
39
|
+
entry.link(href=link)
|
|
40
|
+
entry.updated(
|
|
41
|
+
datetime.datetime.combine(
|
|
42
|
+
date, datetime.time(), tzinfo=zoneinfo.ZoneInfo(self.timezone)
|
|
43
|
+
).isoformat()
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def get_artifact(self) -> artifacts.BytesArtifact:
|
|
47
|
+
contents: bytes = cast(bytes, self.feed_gen.atom_str(pretty=True))
|
|
48
|
+
|
|
49
|
+
return artifacts.BytesArtifact(
|
|
50
|
+
path=self.atom_path, contents=io.BytesIO(contents)
|
|
51
|
+
)
|