Help us shape the future of Go! Take the 2026 Go Developer Survey

gazeta

package module
v0.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: GPL-3.0 Imports: 33 Imported by: 0

README

Gazeta

Git-managed blogs on Margo. Gazeta owns publication rules. Margo renders Markdown, layouts, search, themes, browser metadata and JSON-LD.

Extracted from Guigoes, its first production consumer. Content, author identity and visual assets stay in each blog repository.

Run a blog

Requires Go 1.27.3+. Copy examples/minimal into your blog repository and use the pinned CLI:

go run github.com/araihu/gazeta/cmd/gazeta@v0.0.4 build
go run github.com/araihu/gazeta/cmd/gazeta@v0.0.4 check
go run github.com/araihu/gazeta/cmd/gazeta@v0.0.4 serve

serve exposes the existing output at http://127.0.0.1:1313. Run build after editing. Each command accepts an optional publication root argument.

A Go consumer can pin github.com/araihu/gazeta in go.mod and call gazeta.Run(os.Args[1:]). For an injected build clock, use gazeta.Build(ctx, root, now) or gazeta.Check(root, now). There is no global mutable site identity.

Configuration

site.yaml is native Margo configuration. It is the single source for site.name, site.description, site.base_url, logos, icons, social fallback image, webmanifest, navigation, theme and custom CSS. Set the default locale and readable date format there too. See the runnable example for the complete required identity.

Gazeta requires source: .build/content, output: public, site.home: index.md and an origin without a path. Declare languages in locales.supported. Default-language sources stay at the content root; other sources use lowercase locale directories such as content/pt-br/ and content/es/. Posts inherit the locale of their path; an explicit conflicting language fails validation. Each translation keeps its own canonical URL and JSON-LD language.

blog.yaml holds editorial options:

version: 1
home_limit: 4
authors:
  Alex Example:
    url: /about/
page_types:
  /about/: ProfilePage
  /contact/: ContactPage
  /projects/: CollectionPage
# Optional compatibility checks and additional public files:
required_urls: tests/legacy-urls.txt
files:
  - {source: assets/brand/favicon.ico, target: favicon.ico}
labels:
  articles: Articles
  all_articles: All articles

Authors match frontmatter names exactly. An optional type: Organization declares an institutional author; omitted types remain Person. Organization biographies use AboutPage, while ProfilePage remains restricted to one person. Profiles may use root-relative or HTTPS URLs. Root-relative profiles follow the current locale prefix; provide the corresponding biography in each locale. External HTTPS profiles retain their configured URL. Unknown guests keep their names without inheriting another author's profile. A ProfilePage requires exactly one configured author whose profile URL matches the page. It describes that author as mainEntity. Generic AboutPage behavior remains available. page_types accepts WebPage, AboutPage, ProfilePage, ContactPage and CollectionPage, keyed by directory routes or standalone .html routes. See structured data for the schema contract and live validation procedure.

Labels default to English. Available keys are articles, blog, tags, categories, categories_body, not_found, not_found_body, all_articles, series, part, series_index, continue_series, start_series, start_first, previous, next, redirecting and open_article. The two *_body fields accept Markdown. files copies regular files or directories, rejects symlinks and refuses to overwrite a different generated file. It cannot copy content or build directories around visibility filtering. The optional static/ directory supplies hosting files.

Markup uses neutral .blog-home, .blog-content, .blog-posts, .blog-post, .blog-post-image, .blog-post-text, .blog-series, .blog-series-entry and .blog-series-pagination classes. Style them through Margo's custom_css. Gazeta does not bundle a consumer's palette, logos or article artwork. examples/minimal/assets/editorial.css supplies a small starting layout.

Multilingual publications

One publication has independent home pages, archives, tags, RSS feeds and series for each configured locale. Translate the same relative source path, for example content/blogs/welcome/index.md, content/pt-br/blogs/welcome/index.md and content/es/blogs/welcome/index.md. Matching paths identify equivalents; no translation is inferred from article titles, dates or tags. Missing translations remain absent. Drafts and future posts contribute neither equivalent links nor assets. Series names and part numbers are unique within their locale, and series navigation never crosses languages.

blog.yaml accepts locale-specific label overrides:

locales:
  pt-BR:
    articles: Artigos
    blog: Artigos
    all_articles: Ver todos os artigos
    categories_body: Encontre publicações por [assunto](/pt-br/tags/).

These fields have the same keys as labels; unspecified values inherit the shared labels. Translate all visible editorial copy and use locale-specific URLs in Markdown labels. Margo localizes native English, pt-BR and Spanish controls. Header/footer destinations in site.yaml accept locale, for example {label: Sobre, href: /pt-br/about/, locale: pt-BR}. Shared links omit it.

The native Margo head includes self and reciprocal hreflang links for visible, indexable equivalents. x-default points to the equivalent default-language page when available; it is omitted when that page does not exist. A single sitemap covers every indexable canonical page, while /index.xml, /pt-br/index.xml and /es/index.xml each contain only their own locale. The root 404 document uses the default language and stays out of discovery. Gazeta validates final equivalents, canonical identities, schema and locale feeds before replacing the publication. No browser-language or IP routing is added.

Content and publication

Posts live in content/blogs/<slug>/index.md. Other Markdown pages live under content/. TOML frontmatter uses +++ delimiters:

+++
title = "Our first post"
description = "What we learned."
authors = ["Alex Example"]
publishedAt = "2026-10-29T09:00:00Z"
tags = ["Engineering"]
cover = "./assets/cover.png"
cover_alt = "A description of the article illustration."
# Optional: modifiedAt, aliases, draft, series, seriesPart
+++

Timestamps require RFC 3339 with a timezone. modifiedAt cannot precede publishedAt. A draft or future post and its assets stay out of pages, lists, sitemap and feeds. Future posts need another build after their timestamp; Gazeta does not schedule one.

Series use series plus a positive, unique seriesPart. Navigation only includes visible parts. The newest series gets a first-part entry when that part is available. Post ordering uses publication dates, then series and part, without changing timestamps.

Gazeta generates /blogs/, /tags/, /categories/, 404.html, RSS, safe redirect stubs and Cloudflare-compatible _redirects. These generated page names are reserved. Root home and page content stay consumer-owned. Covers must be real local assets/ files under the post. Native Margo social metadata and BlogPosting.image use the same cover; posts without a cover omit the JSON-LD image.

Builds stage content in .build/, render through Margo, assemble public assets and use site.MarshalManifest for final byte identities. Validation checks canonical URLs, locale, JSON-LD, local links and fragments, social covers, RSS and configured legacy URLs before replacing public/. check also verifies the existing artifact bytes against their native Margo manifest. A failed build preserves the previous publication. A build lock prevents two writers on one root. After a crashed build, remove .build/build.lock only once its process has stopped.

Verify a deployed publication

After publishing a validated public/ artifact, run:

go run github.com/araihu/gazeta/cmd/gazeta@v0.0.4 verify -timeout 3m

verify reads the existing artifact and site.base_url; it never rebuilds or deploys. It reuses Margo's manifest serializer to check local artifact integrity, then requires the public manifest to match before and after each complete pass. It checks every public asset, HTML content, canonical URL, locale, JSON-LD, social metadata, legacy HTTP redirects and missing-page status. Hosting control files such as _headers and _redirects are not requested as public resources.

Non-HTML bytes must match exactly. HTML comparison allows JSON object formatting changes and the known hidden Cloudflare protection script, while retaining all publication content and metadata. This normalization changes comparison trees only. It never rewrites generated or served files. Redirects to unrelated destinations, stale content, duplicated JSON properties and soft 404 pages fail verification.

The default total deadline is two minutes, with five seconds between failed passes. Use -timeout and -interval to change these limits. A successful command emits a JSON receipt with origin, manifest digest, page/asset/redirect counts and attempts. Keep the same validated artifact in the verification job; a fresh build could describe a different publication. Google Rich Results Test remains a separate external check.

Go callers can use gazeta.Verify(ctx, root, gazeta.VerifyOptions{}). Options may supply a custom HTTP client, for example one trusting a private CA; verification still disables automatic redirects.

Indexing and sitemaps

Gazeta marks the generated 404 document with Margo's native noindex: true frontmatter. Margo emits robots metadata and leaves the error document out of sitemap.xml and llms.txt; missing URLs still need the hosting platform's HTTP 404 behavior.

gazeta check requires the sitemap to contain each indexable canonical HTML page exactly once. It rejects missing pages, duplicate or unknown URLs, and pages marked noindex. Legacy aliases continue to use HTTP redirects and canonical targets instead of separate sitemap entries. The generated 404 document must retain its native noindex, follow metadata.

Trust and release scope

Configuration and Git content are trusted build inputs. Margo layout commands and modules inherit the build process's access. Gazeta is not an execution sandbox for submitted repositories.

Authored raw HTML is denied by default through Margo's compiler policy. unsafe_html: true explicitly permits reviewed HTML/JavaScript demos. Gazeta's own escaped listing and series markup renders in either mode. Do not enable this option for unreviewed content.

v0.0.x is an initial extraction, with domain-root hosting and locale-scoped content. No content scheduler or provider-specific deploy credentials are included. A Git push can run the build and deploy public/ through your existing pipeline.

Repository language

Write repository documentation, GoDoc, source-code identifiers, code comments and developer-facing tooling messages in English, including upstream contributions. Published content and UI labels follow the configured locale. Preserve intentional localization/Unicode fixtures and compatibility-sensitive values.

Verify and release

./scripts/check.sh

This runs formatting, module download, Go tests, vet, build and a standalone example publication. Integration tests exercise two independent domains, author profiles, consumer CSS, private content exclusion, explicit HTML opt-in and preservation after failed validation.

CI runs the same script. A v* tag must pass it and point to a commit on main before the workflow publishes a GitHub release. Consumers should pin a tested module release without a local replace.

GPL-3.0, matching the source publication. Margo keeps its own MIT license.

Documentation

Overview

Package gazeta builds Git-managed publications on Margo. Content and configuration belong to each consumer; rendering and document metadata belong to Margo.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Build

func Build(ctx context.Context, root string, now time.Time) error

Build renders and validates a publication before replacing root/public. now controls draft/future visibility. Only reviewed Git content should be built.

func Check

func Check(root string, now time.Time) error

Check validates an existing root/public against its content and configuration.

func Run

func Run(args []string) error

Run implements build, check, verify and serve for a thin consumer command. An optional second argument selects the publication root.

Types

type Verification added in v0.0.3

type Verification struct {
	Origin         string `json:"origin"`
	ManifestDigest string `json:"manifestDigest"`
	Pages          int    `json:"pages"`
	Assets         int    `json:"assets"`
	Redirects      int    `json:"redirects"`
	Attempts       int    `json:"attempts"`
}

Verification identifies the validated artifact observed on the public origin.

func Verify added in v0.0.3

func Verify(ctx context.Context, root string, options VerifyOptions) (Verification, error)

Verify checks an already-built root/public against the configured public origin. It never builds or publishes. Non-HTML assets must match exactly; HTML must retain content, canonical, locale, JSON-LD and head metadata despite hosting injections. The remote manifest must match before and after each complete pass.

type VerifyOptions added in v0.0.3

type VerifyOptions struct {
	// Client optionally supplies a transport, for example a private CA. Redirects remain disabled.
	Client *http.Client
	// Timeout is the total verification deadline. The default is two minutes.
	Timeout time.Duration
	// Interval is the delay between failed passes. The default is five seconds.
	Interval time.Duration
}

VerifyOptions bounds publication propagation checks. Zero values use defaults.

Directories

Path Synopsis
cmd
gazeta command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL