Guide / Tools, Infrastructure & Workflows
GitHub Actions Became My Publishing System
Versioned articles, validation, static builds and search indexing form a publishing pipeline. Here is how this site connects editorial changes to delivery.
An article in this repository is a file before it is a page. That small fact made the publishing workflow easier to connect to the rest of the project.
The text, metadata and internal links can be reviewed together. A change has a diff. The same build that produces the article also updates the archive, topic pages, feed and search index. There is no second copy of the article waiting in a production database.
For this site, GitHub Actions became the place where those steps are checked and assembled. The approach is useful to me because writing and software changes already share a repository. It would be less comfortable for an editorial team that needs a different authoring interface, and I would not recommend it merely to avoid having a CMS.
The pipeline described here is implemented for this site. Provider access and the first live upload are separate activation checks; a configured workflow alone is not evidence of a completed public deployment.
Give content the same clear boundaries as code
Articles live in Astro’s existing content collection as Markdown or MDX. Frontmatter carries the title, description, type, pillar, topics and publication date. The schema checks those fields rather than allowing each article to invent its own structure.
That matters because metadata drives more than the page header. A topic value affects discovery. A description appears in previews and metadata. A publication date affects ordering and eligibility. A malformed entry can therefore have consequences well beyond a typo in one paragraph.
For an ordinary article, Markdown is enough. MDX remains available when the established editorial components are useful. I would not add interactive content simply because the format permits it; each additional behavior becomes something to verify.
I also treat MDX as trusted source maintained by the author. This is not a public upload surface where arbitrary contributors can submit executable content without review. The convenience of keeping content close to code comes with the responsibility to review what enters the repository.
Define publication once
This project publishes an entry when it is not a draft, is not marked as demonstration content and has a publication date at or before the build time. Listings, article routes, feeds and discovery use the same gate.
The logic is simple enough to explain:
approved content
+ draft is false
+ demo is false
+ publication time has arrived
→ eligible for the production build
Sharing the gate prevents a familiar inconsistency: an article hidden from the main listing but still reachable through a generated route or indexed by search. I want unpublished content excluded from the output that those systems consume, rather than cosmetically hidden on one page.
Dates include a timezone offset. A timestamp without one leaves room for a different interpretation on a developer’s machine and the CI runner. The visible date can remain a friendly calendar date while the underlying publication boundary is unambiguous.
The initial collection uses one launch date with a small ordering sequence. It is not backdated to imply a longer publishing history. The date records this editorial launch, not when the underlying ideas were first discussed.
Build the publication as one connected result
The practical sequence is:
article change → Git commit → GitHub Actions
→ validation → Astro build → Pagefind → verified static output
→ hosting transfer when enabled
Astro generates the pages and the project’s feed and sitemap integrations. Pagefind then indexes the generated HTML. Its static search model adds a browser-searchable bundle to the output, so the hosting server does not need to run a search process.
Order matters. Indexing before the current pages exist would produce a search index for the wrong content. Transferring the site without its new index could make an article visible by direct link but undiscoverable through search.
I want the build artifact to contain the complete public result: articles, navigation targets, feed, sitemap, icons and search files. The host should receive that artifact, not the source repository and an instruction to finish the build itself.
For this site, that means the hosting account serves static files. The presence of ten articles changes the content volume, not the runtime architecture. No article database or persistent Node process is introduced.
Make validation serve the editorial change
A successful build can establish that the content is structurally valid. It cannot establish that an argument is good, that a personal story is accurate or that a linked source supports the sentence beside it. Those need an editorial pass before the automated gates.
The automated checks still do useful work. They can verify that article routes exist, metadata is unique, the archive contains the expected entries and production output excludes demo templates. Browser tests can exercise search and reveal a long heading or table that overflows on a narrow screen.
I would test the effect of adding content without turning it into a redesign. A populated Writing page should use the current layout. If a new article exposes an actual layout defect, fix that defect narrowly and verify the other pages again.
The release guard adds another boundary: generated output must not contain private source, credentials, development information or editorial previews. The upload step should run only after those checks pass.
Publishing confidence comes from several kinds of evidence, each with a limited job. A spellcheck cannot replace fact-checking, and a screenshot cannot prove that the sitemap includes every article.
Scheduled publishing still requires a build
A static page cannot decide to appear later by itself. If an article’s date is in the future, it remains excluded until a build runs after that boundary and the resulting output is deployed.
This site retains a daily workflow at 06:00 UTC. The schedule does not follow Italian daylight-saving time. GitHub’s workflow event documentation explains scheduled and manual runs, including that scheduled jobs can be delayed and run on the default branch. I would not use this arrangement to promise publication at an exact minute.
For a time-sensitive release, I would plan an explicit build and verify its result. For a personal publication, the daily schedule provides a simple way for eligible articles to enter the next published build without configuring a cron job on the host.
A date in the past does not override draft: true. Nor should a demonstration template become public merely because its example date has arrived. Those conditions remain independent parts of the publication gate.
Deployment is another boundary, not a victory message
The prepared hosting transfer uses encrypted access when supported, a verified destination and narrowly scoped synchronization. Credentials belong in the automation platform’s secret storage; they do not belong in article examples or repository files.
The destination also needs checking independently of the build. A domain can still point at an old hosting service during a transfer. An upload to the new host and a request to the public domain may therefore observe different systems.
I want the handoff to distinguish three outcomes: validation passed, files transferred to the verified destination, and the public domain serves the intended release. Combining them into “deployed” makes troubleshooting unnecessarily confusing.
The synchronization process preserves unowned provider files and removes only stale files recorded as belonging to previous builds. It replaces individual files safely, but a collection of files is not switched atomically as one release. That limitation belongs in the operating guide, alongside retry and recovery instructions.
This fits the broader decision to use static hosting: simplify the runtime, then take the remaining delivery obligations seriously.
Recovery should be understandable before it is needed
If an article needs correction, I can edit the source, review the diff and run the same checks. If a change needs reverting, Git records the previous source state. The current deployment tooling should remain in place while the content or code change is reversed.
Rebuilding an old source revision later does have a subtle consequence: time has moved forward. An article that was future-dated in the earlier build may now be eligible. The lockfile helps control dependency versions, but a time-dependent publication gate means the output is not solely a function of the commit.
For that reason, I would record which revision and build produced a release, and inspect the resulting article set during recovery. “Same source” is not a sufficient promise of identical output when build time intentionally matters.
If the host loses files, the public artifact can be rebuilt from source. If only some remote files were altered independently, incremental deployment needs an explicit full-upload recovery path rather than assuming its previous manifest still describes reality.
What I like about this system is the continuity. The article begins as reviewable text, moves through explicit checks and becomes ordinary files a simple host can serve. Future writing can use that same path without adding another application to operate just to publish a paragraph.