diff --git a/README.md b/README.md index e69de29..11be59e 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,65 @@ +# Adding an article + +Steps to publish a new article and regenerate the public artifacts that depend on it. + +## 1. Add the article data + +Add a new entry to the category's `articles` array in `src/App/src/Fixture/articles_cleaned.json`: + +```json +{ + "post_title": "Your article title", + "post_date": "YYYY-MM-DD HH:MM:SS", + "post_status": "publish", + "author": { + "display_name": "admin", + "github": "arhimede" + }, + "isObsolete": false, + "opengraph_img": null, + "excerpt": "Short excerpt shown in listings.", + "tl_dr": "One or two sentence summary." +} +``` + +`author.display_name` can either match an existing author or be a new name — `bin/doctrine-fixtures` creates a new `Author` automatically for any name not already in the database. The category (top-level `slug`) must already exist, though. The article's slug is derived automatically from the title (lowercased, non-alphanumeric characters collapsed to `-`) by `PostLoader::slugify()`. + +`opengraph_img` is the image shown as the social-media (Twitter/OG) preview card. Leave it `null` to fall back to the site-wide default image (`config/autoload/local.php` → `application.meta.image`). To set one, put the image file at `public/opengraph/article/your-image.png` and reference it here as a root-relative path: `"opengraph_img": "/opengraph/article/your-image.png"`. This is unrelated to the in-article images described in step 3 — it is placed by hand, not by `bin/create-uploads-dir`. + +**Important:** you can set `"post_status": "draft"` instead of `"publish"` to keep an article out of sight — anything other than `publish`/`private` is treated as a draft by `PostLoader`, and `getPublishedPosts()` (used by both `bin/generate-feed` and `bin/sitemap`) only returns posts with `publish` status. After changing it, follow the same steps: re-run `bin/doctrine-fixtures`, then `bin/generate-feed` and `bin/sitemap`. This applies generally, not just to status changes — **any** edit to `articles_cleaned.json` (title, excerpt, status, date, etc.) needs `bin/doctrine-fixtures` re-run to update the database, followed by re-running the 3 generators in step 4 so `feed.xml`/`sitemap.xml`/`llms-full.txt` reflect it. One exception: `bin/generate-llms-full` reads straight from the `.md` files on disk and does **not** check `post_status` at all — a `draft` article's `.md` file will still be included in `llms-full.txt` unless you also remove or rename that file. + +## 2. Create the templates + +- `src/Blog/templates/page/blog-resource/{category-slug}/{article-slug}.html.twig` — the page body, extending `@layout/blog-post.html.twig`. +- `src/Blog/templates/page/JSON-LD/{category-slug}/{article-slug}.jsonld.twig` — the `@graph` of `TechArticle` + `BreadcrumbList` + `FAQPage` structured data. +- `public/md-articles/{category-slug}/{article-slug}.md` — the markdown version, with YAML front matter (`title`, `description`, `author`, `date_published`, `canonical_url`, `category`, `language`) followed by the article body (`TL;DR`, sections, `FAQ`). This feeds `llms-full.txt`. + +Copy an existing set of these three files in the same category as a starting point, to match the established structure (FAQ block matching the `FAQPage` entries, etc.). + +If the article body uses images (via `asset('uploads/article/' ~ article.id ~ '/filename.png')` in the `.html.twig`), just drop the image file anywhere under `public/uploads` — `bin/create-uploads-dir` (step 4) finds it by filename and copies it to the right place. No manual path/folder creation needed. + +## 3. At deploy — run in this order + +```shell +php bin/doctrine-fixtures +php bin/create-uploads-dir +``` + +- `bin/doctrine-fixtures` loads `articles_cleaned.json` into the database, creating the `Post` entity (with its database-generated UUID) for the new article. +- `bin/create-uploads-dir` must run *after* it — it resolves the post by slug to get that UUID, creates `public/uploads/article/{post-id}/`, and copies each image referenced in the `.html.twig` there from wherever it already lives under `public/uploads`. + +## 4. Regenerate the public artifacts — any order + +```shell +php bin/generate-feed +php bin/sitemap +php bin/generate-llms-full +``` + +- `bin/generate-feed` rewrites `public/feed.xml` from the published posts in the database. +- `bin/sitemap` rewrites `public/sitemap.xml` from the published posts in the database. +- `bin/generate-llms-full` rewrites `public/llms-full.txt` by concatenating `public/md-articles/index.md` and every other `public/md-articles/*/*.md` file, sorted by path. Requires the `llms.sourceDir` / `llms.outputFile` keys in `config/autoload/local.php` (see `local.php.dist`). + +These three have no ordering dependency on each other, only on step 3 being done first. + +Note: none of this is wired into an automated deploy pipeline in this repository — there is no `deploy` script or CI job that runs these `bin/` scripts. They're run manually (or via cron, as already set up for `bin/generate-packages`). `public/feed.xml`, `public/sitemap.xml`, and `public/llms-full.txt` are committed generated artifacts, so re-running these scripts leaves them modified in git until committed.