Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: release-please
# Keeps a Release PR open that bumps the version (app/package.json, version.txt) and
# prepends CHANGELOG.md from the conventional commits merged since the last release.
# Merging that PR (owner only) tags vX.Y.Z[-beta.N] and publishes the GitHub release
# (A99). Config: release-please-config.json + .release-please-manifest.json. After
# v2.0.0-beta.1 is out, delete "release-as" and "last-release-sha" from the config
# (they only seed the first release); see README "Releasing".
#
# Token: a PR opened with GITHUB_TOKEN triggers no workflows, so the gate does not run
# on the Release PR (it only touches CHANGELOG.md and version files). To run CI on it,
# add a fine-grained PAT (this repo only; Contents and Pull requests: read and write)
# as the secret RELEASE_PLEASE_TOKEN; it is used when present. Without it, the repo
# setting "Allow GitHub Actions to create and approve pull requests" must be on.
on:
push:
branches: [main]
workflow_dispatch:
permissions: {}
concurrency:
group: release-please
cancel-in-progress: false
jobs:
release-please:
if: github.repository == 'CMaintz/tech-atlas'
runs-on: ubuntu-latest
permissions:
contents: write # release commits, tags, GitHub releases
pull-requests: write # the Release PR
steps:
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "1.0.0"
}
75 changes: 75 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Changelog

Atlas follows [semantic versioning](https://semver.org) with beta pre-releases
(v2.0.0-beta.1, beta.2, ... then v2.0.0 at launch). New entries are written by
release-please from conventional commits; see README "Releasing".

## v1.0.0 to v2.0.0-beta.1: summary

Written by hand. Atlas changed from a searchable dictionary into a bilingual map for
learning; everything below landed after v1.0.0 (PRs #2 to #70).

### Content

- Over 430 terms across security, computer science, AI and platforms, up from the v1
seed: batch 3 (150 terms), AI and platform domains, batch 5 (appsec, detection and
response, identity, EU/DK regulation), batch 6, full course-compendium coverage and
an AI dictionary of 104 terms, plus 16 new machine-learning terms after a web-verified
review.
- Long-form articles (EN + DA), era years for the timeline, an extended technical deep
dive with sources for every term, and a prose refinement pass over all terms and
articles.
- Auto-linked prose, mentions, disambiguation pages and search intents.

### Explorer

- A 2D and 3D graph explorer with depth axis, routes between terms and learn-first
paths; the Time layout and a swim-lane timeline.
- Domain colour families, flowing directed edges, a stable backbone overview with
lanes and 3D galaxies, and the relationship types that carry structure shown by
default.
- A term side panel with in-place expand, previous/next through connections and
history.
- A floating control bar (responsive, never two rows), a collapsible legend, hover
cards, drag feedback, WASD keyboard navigation and named 3D galaxies.
- A hidden visual lab to compare old and new effects with a frame meter.

### Search

- Static semantic search across English and Danish, later moved to a backend
(Supabase pgvector and an Edge Function) with looser deadlines.
- "Find a term" in the Explorer matches by name and by meaning, like the home search.

### Learning

- A study layer: generated quizzes, spaced repetition and a personal knowledge map.
- A hand-written question bank (180 questions), domain quizzes, questions never
answered by their own page, and a review queue.
- A guided tour with eased motion.

### Accounts and privacy

- Optional sign-in with synced learner progress (Supabase), sign-in first, with
LinkedIn (OIDC); email sign-in hidden.
- A privacy page. Atlas is free forever: no ads, no paywall, no tracking.

### Security

- Secret scanning, a Content Security Policy, least-privilege CI and a security review.
- Dependabot for npm and GitHub Actions, with a cooldown and grouped updates.

### Design

- A home page, mobile navigation and a UX baseline; a complete light theme with a
cream map; a language menu; a BETA ribbon (a badge on phones).
- A mobile pass: 44px tap targets, dynamic viewport units, safe areas and 16px inputs.
- An About dialog with credits; no em or en dashes in anything a reader sees.
- Open data (JSON, CSV, Anki, graph), RSS feeds, SEO, a 404 page, an A-Z index and
short URLs.

### Docs and CI

- The Foundry gate (lint, typecheck, tests with a full build, audit) on every PR, and
a deploy that ships only what passed the gate.
- A README with a demo GIF and screenshots, a content licence (CC BY-SA 4.0) and
regular reviews of the decision log.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,40 @@ was drafted with AI assistance and is being reviewed by hand, entry by entry; un
entry is reviewed it is marked as a draft on the site. Corrections are welcome as issues
or pull requests.

## Releasing

Versions follow [semantic versioning](https://semver.org) with beta pre-releases:
v2.0.0-beta.1, beta.2, beta.3 and so on, then v2.0.0 at launch. The version lives in
`app/package.json` and is shown in the site footer and the About dialog, linked to
[CHANGELOG.md](CHANGELOG.md).

[release-please](https://github.com/googleapis/release-please)
(`.github/workflows/release-please.yml`) runs on every push to `main` and keeps one
**Release PR** open, titled like `chore(main): release 2.0.0-beta.2`. It bumps the
version (`app/package.json`, its lock file, `version.txt`, `.release-please-manifest.json`)
and prepends the new entry to `CHANGELOG.md`, built from the conventional commits since
the last release: `feat` (Features), `fix` (Fixes), `content` (Content), `docs`
(Documentation); `chore`, `ci`, `refactor`, `test` are left out and do not start a
release on their own.

- **Cut a release**: the owner merges the Release PR. release-please then tags the
merge (e.g. `v2.0.0-beta.2`) and publishes a GitHub pre-release; the push to `main`
runs the gate and deploys, so the footer shows the new version.
- **CI on the Release PR**: a PR opened with the default `GITHUB_TOKEN` triggers no
workflows, so the gate does not run on it (it only touches the changelog and version
files). To run it, add a fine-grained token (this repository only; Contents and Pull
requests: read and write) as the secret `RELEASE_PLEASE_TOKEN`; the workflow uses it
when present. Without that secret, **Settings > Actions > General > Allow GitHub
Actions to create and approve pull requests** must be on, or the workflow cannot open
the PR.
- **After v2.0.0-beta.1**: delete `release-as` and `last-release-sha` from
`release-please-config.json`. They only seed the first release (the hand-written
summary of everything since v1.0.0 is already in the changelog); left in, every
release would propose beta.1 again.
- **Launch**: in the config set `"release-as": "2.0.0"` and `"prerelease": false`,
and delete `versioning` and `prerelease-type`; merge the Release PR, then delete
`release-as` again.

## Data

The site publishes its content as open data: every term as JSON (`/api/terms.json`,
Expand Down
4 changes: 2 additions & 2 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "lexicon",
"type": "module",
"version": "0.0.1",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "astro dev",
Expand Down
17 changes: 16 additions & 1 deletion app/src/components/AboutButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const Avatar = ({ name, photo, position }: { name: string; photo?: string; posit
* preventDefault, so the tour and term panel stand down), backdrop click, and focus
* back on the opener.
*/
export default function AboutButton({ ui, links, people, variant = 'circle' }: Props) {
export default function AboutButton({ ui, links, people, version, variant = 'circle' }: Props) {
const dialog = useRef<HTMLDialogElement>(null);
const opener = useRef<HTMLButtonElement>(null);
const titleId = useId();
Expand Down Expand Up @@ -182,6 +182,21 @@ export default function AboutButton({ ui, links, people, variant = 'circle' }: P
<hr class="border-border" />
{section('credits', ui.credits)}
{section('thanks', ui.thanks)}
<p class="text-xs text-subtle">
<a
href={version.href}
target="_blank"
rel="noopener noreferrer"
class="hover:text-fg-soft hover:underline"
title={ui.releaseNotes}
>
Atlas {version.label}
<span class="sr-only">
{' '}
({ui.releaseNotes}) {ui.opensInNewTab}
</span>
</a>
</p>
</div>
</dialog>
</>
Expand Down
5 changes: 5 additions & 0 deletions app/src/layouts/Base.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ACCOUNTS, LANGS, REPO, TOUR_STEPS, UI, swapLang, url, type Lang } from
import { isCurrentNav } from '../lib/prefs';
import { aboutProps } from '../lib/about-assets';
import { UI_EXTRA } from '../lib/ui-extra';
import { CHANGELOG_URL, VERSION } from '../lib/version';

interface Props {
lang?: Lang;
Expand Down Expand Up @@ -369,6 +370,10 @@ const tourUi = {
{ui.tourStart}
</button>
<AboutButton client:idle variant="link" {...aboutProps(lang)} />
<a class="hover:text-fg-soft" href={CHANGELOG_URL} title={extra.releaseNotes}>
{VERSION}
<span class="sr-only"> ({extra.releaseNotes})</span>
</a>
</footer>
)}
{!wide && (
Expand Down
2 changes: 2 additions & 0 deletions app/src/lib/about-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { existsSync } from 'node:fs';
import { resolve } from 'node:path';
import { ABOUT_LINKS, ABOUT_PEOPLE, ABOUT_UI, url, type Lang } from './site';
import { visibleLinks } from './about';
import { CHANGELOG_URL, VERSION } from './version';

export const aboutProps = (lang: Lang) => ({
ui: { ...ABOUT_UI[lang] },
Expand All @@ -26,6 +27,7 @@ export const aboutProps = (lang: Lang) => ({
: undefined,
photoPosition: p.photoPosition ?? 'center',
})),
version: { label: VERSION, href: CHANGELOG_URL },
});

export type AboutProps = ReturnType<typeof aboutProps>;
2 changes: 2 additions & 0 deletions app/src/lib/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,7 @@ const ABOUT_EN = {
thanks: 'Thanks',
close: 'Close',
opensInNewTab: '(opens in a new tab)',
releaseNotes: 'Release notes',
};

export const ABOUT_UI: Record<Lang, Record<keyof typeof ABOUT_EN, string>> = {
Expand All @@ -904,5 +905,6 @@ export const ABOUT_UI: Record<Lang, Record<keyof typeof ABOUT_EN, string>> = {
thanks: 'Tak til',
close: 'Luk',
opensInNewTab: '(åbner i en ny fane)',
releaseNotes: 'Udgivelsesnoter',
},
};
2 changes: 2 additions & 0 deletions app/src/lib/ui-extra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const UI_EXTRA = {
azIntro: '{n} terms, alphabetically.',
notFoundAz: 'Browse all terms A-Z',
licenceFooter: 'Content CC BY-SA 4.0',
releaseNotes: 'Release notes',
tiers: {
standard: 'Standards & official texts',
'official-doc': 'Official documentation',
Expand Down Expand Up @@ -63,6 +64,7 @@ export const UI_EXTRA = {
azIntro: '{n} begreber i alfabetisk rækkefølge.',
notFoundAz: 'Se alle begreber A-Å',
licenceFooter: 'Indhold CC BY-SA 4.0',
releaseNotes: 'Udgivelsesnoter',
tiers: {
standard: 'Standarder og officielle tekster',
'official-doc': 'Officiel dokumentation',
Expand Down
20 changes: 20 additions & 0 deletions app/src/lib/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* The site's version, read from app/package.json when the site is built (A99).
* release-please bumps that version in its Release PR, so the footer and the About
* dialog always show the release that is deployed. Server-only (it reads the file
* system): import it from .astro frontmatter or about-assets.ts, never from an island.
*/
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { REPO } from './site';

// Builds run from app/ (npm scripts, mise `dir = "app"`), as in about-assets.ts.
const pkg = JSON.parse(readFileSync(resolve(process.cwd(), 'package.json'), 'utf8')) as {
version: string;
};

/** e.g. "v2.0.0-beta.1". */
export const VERSION = `v${pkg.version}`;

/** Release notes. The CHANGELOG on main always exists, unlike a tag's page on release day. */
export const CHANGELOG_URL = `${REPO}/blob/main/CHANGELOG.md`;
Loading
Loading