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
147 changes: 133 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,153 @@
name: Release Version
name: Release

on:
push:
tags:
- "*"
branches:
- main

concurrency:
group: release-${{ github.ref }}

jobs:
build:
quality:
name: Quality
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Check
run: pnpm check

release-please:
name: Release Please
needs: quality
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
release_created: ${{ steps.release.outputs.release_created }}
release_sha: ${{ steps.release.outputs.sha }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- name: Create release token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.RELEASE_APP_CLIENT_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
permission-contents: write
permission-issues: write
permission-pull-requests: write

- name: Run Release Please
id: release
uses: googleapis/release-please-action@v5
with:
token: ${{ steps.app-token.outputs.token }}

- name: Read release pull request branch
id: release-pr
if: steps.release.outputs.prs_created == 'true'
env:
RELEASE_PR: ${{ steps.release.outputs.pr }}
run: echo "branch=$(jq -r '.headBranchName' <<< "$RELEASE_PR")" >> "$GITHUB_OUTPUT"

- name: Checkout release pull request
if: steps.release.outputs.prs_created == 'true'
uses: actions/checkout@v6
with:
ref: ${{ steps.release-pr.outputs.branch }}
token: ${{ steps.app-token.outputs.token }}

- name: Setup Node.js
uses: actions/setup-node@v2
if: steps.release.outputs.prs_created == 'true'
uses: actions/setup-node@v6
with:
node-version: 24

- name: Sync Obsidian release metadata
if: steps.release.outputs.prs_created == 'true'
run: node version-bump.mjs

- name: Commit release metadata
if: steps.release.outputs.prs_created == 'true'
env:
PR_BRANCH: ${{ steps.release-pr.outputs.branch }}
run: |
if git diff --quiet -- manifest.json versions.json; then
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add manifest.json versions.json
git commit -m "chore: sync Obsidian release metadata"
git push origin "HEAD:${PR_BRANCH}"

publish:
name: Publish release
needs: release-please
if: needs.release-please.outputs.release_created == 'true'
runs-on: ubuntu-latest
permissions:
attestations: write
contents: write
id-token: write
steps:
- name: Checkout release commit
uses: actions/checkout@v6
with:
node-version: 20
ref: ${{ needs.release-please.outputs.release_sha }}

- name: Setup pnpm
uses: pnpm/action-setup@v4

- uses: pnpm/action-setup@v3
- name: Setup Node.js
uses: actions/setup-node@v6
with:
version: 8
run_install: true
node-version: 24
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build
run: pnpm build

- name: Release
uses: softprops/action-gh-release@v2
- name: Verify Obsidian release metadata
run: |
node version-bump.mjs
git diff --exit-code -- manifest.json versions.json

- name: Attest release assets
uses: actions/attest@v4
with:
files: |
subject-path: |
main.js
manifest.json
LICENSE

- name: Upload release assets
env:
GH_TOKEN: ${{ github.token }}
TAG_NAME: ${{ needs.release-please.outputs.tag_name }}
run: gh release upload "$TAG_NAME" main.js manifest.json --clobber

- name: Publish draft release
env:
GH_TOKEN: ${{ github.token }}
TAG_NAME: ${{ needs.release-please.outputs.tag_name }}
run: gh release edit "$TAG_NAME" --draft=false
29 changes: 29 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Validate

on:
pull_request:

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Check
run: pnpm check
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"
}
26 changes: 26 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Contributing

Issues and pull requests are welcome. For a substantial behavior change, please open an issue first so the intended scope can be agreed before implementation.

## Local development

Use Node.js 22.13 or newer and the pnpm version declared in `package.json`. CI uses Node.js 24.

```sh
pnpm install
pnpm dev
```

Before opening a pull request, run the same checks as CI:

```sh
pnpm check
```

Keep changes focused and use [Conventional Commits](https://www.conventionalcommits.org/) such as `fix: handle failed font downloads` or `feat: add a font role`. Release Please uses these commits to determine the next version and generate release notes.

## Releases

Merging the Release Please pull request creates a draft GitHub release. CI rebuilds the plugin, attests and uploads `main.js` and `manifest.json`, and publishes the release only after all checks pass.

Releases use the Fontsource GitHub App configured through the `RELEASE_APP_CLIENT_ID` organization variable and `RELEASE_APP_PRIVATE_KEY` organization secret.
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,38 @@

![Hero Image](./hero.png)

This is a simple plugin to import [Fontsource](https://fontsource.org/) custom fonts into Obsidian.
Fontsource imports fonts from the [Fontsource](https://fontsource.org/) directory and makes them available throughout Obsidian, including popout windows.

## Contribution
This version requires Obsidian 1.12 or newer.

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
## Usage

Releasing a new version is done by creating a new tag and pushing it to the repository. The GitHub Actions workflow will build and release the necessary files.
1. Open **Settings → Fontsource**.
2. Import a font from the Fontsource directory.
3. Enable the imported font.
4. Choose where to use it: interface, text, or monospace.

Multiple fonts can be selected for each role and ordered by precedence to cover additional languages.

## Network and storage

The plugin connects only while listing or importing fonts:

- `api.fontsource.org` provides the font directory and metadata.
- `cdn.jsdelivr.net` provides the selected WOFF2 font files.

Imported fonts are encoded into CSS files under your vault's configured Obsidian folder at `fonts/<font-id>.css`. The plugin reads and writes only those cache files through Obsidian's vault adapter and stores your selections in its normal plugin settings. Once imported, fonts load from the local cache.

## Development

This project requires Node.js 22.13 or newer and pnpm 11. CI uses Node.js 24.

```sh
pnpm install
pnpm check
```

Run `pnpm dev` to watch the source and write a development bundle into `test-vault/.obsidian/plugins/fontsource`. See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution and release details.

## License

Expand Down
23 changes: 0 additions & 23 deletions biome.json

This file was deleted.

44 changes: 40 additions & 4 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import esbuild from "esbuild";
import { readFile } from "node:fs/promises";
import { builtinModules } from "node:module";
import process from "node:process";
import builtins from "builtin-modules";

const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
Expand All @@ -9,6 +10,35 @@ if you want to view the source, please visit the github repository of this plugi
`;

const prod = process.argv[2] === "production";
const outfile = prod
? "main.js"
: "test-vault/.obsidian/plugins/fontsource/main.js";

// This plugin does not use lfi's dynamic compose optimization. Removing its
// capability probe lets esbuild omit that unused eval/Function branch.
const disableLfiDynamicCompose = {
name: "disable-lfi-dynamic-compose",
setup(build) {
build.onLoad(
{ filter: /[\\/]lfi[\\/]dist[\\/]index\.js$/ },
async ({ path }) => {
const source = await readFile(path, "utf8");
const dynamicComposeProbe =
'let e=/*@__PURE__*/(()=>{try{return eval(""),!0}catch{return!1}})()';
if (!source.startsWith(dynamicComposeProbe)) {
throw new Error(
"lfi's bundle changed; update the pinned dynamic-compose transform",
);
}

return {
contents: source.replace(dynamicComposeProbe, "let e=!1"),
loader: "js",
};
},
);
},
};

const context = await esbuild.context({
banner: {
Expand All @@ -30,18 +60,24 @@ const context = await esbuild.context({
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins,
...builtinModules,
],
format: "cjs",
target: "es2018",
target: "es2021",
logLevel: "info",
minify: prod,
plugins: [disableLfiDynamicCompose],
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: prod ? "main.js" : "test-vault/.obsidian/plugins/fontsource/main.js",
outfile,
});

if (prod) {
await context.rebuild();
const artifact = await readFile(outfile, "utf8");
if (/\b(?:eval|Function)\s*\(/.test(artifact)) {
throw new Error("Release artifact contains dynamic code evaluation");
}
process.exit(0);
} else {
await context.watch();
Expand Down
Loading
Loading