Я долго искал идеальный фреймворк для технологического блоггинга. Хотелось чего-то, что позволяет не просто писать текст, а сразу показывать результат работы скриптов, визуализировать данные и делать это красиво. При этом важно, чтобы всё было статическим, чтобы можно было хостить бесплатно, без серверов и лишних заморочек.
И вот, наконец, мне повезло — я наткнулся на Observable Framework. Эта штука умеет всё вышеперечисленное и даже больше. В этой заметке расскажу, что это за зверь, как его запустить вместе с GitHub и Azure Static Web Apps, и как подружить всё это дело с PowerShell.
В двух словах об Observable Framework
Observable Framework — это статический генератор сайтов от команды ObservableHQ, который сочетает в себе силу интерактивных ноутбуков и удобство современного фронтенда. В отличие от типичных блог-платформ, он позволяет:
- запускать JavaScript прямо в заметках,
- использовать визуализации через D3, Vega и Observable Inputs,
- подгружать данные и обрабатывать их хоть на PowerShell, хоть на Python — главное, чтобы результат был на выходе.
Пишете заметки в Markdown, встраиваете интерактив — и вуаля, у вас современный статичный сайт, готовый к деплою в GitHub Pages или Azure Static Web Apps. Всё интерактивное при этом работает в браузере, без серверной части. Просто, быстро, красиво.
Как настроить Observable Framework
Начать можно буквально в пару кликов — вот отсюда. Репозиторий-шаблон уже содержит devcontainer и всё нужное. Делаем свой репозиторий из шаблона — и можно сразу писать посты.
Новая заметка — просто новый файл, например /src/my-new-post/index.md.
Но Markdown сам по себе скучен. А хочется интерактива: таблиц, графиков, визуализаций. Для этого нужны данные. И вот тут начинается магия.
Observable Framework позволяет вместо данных подсовывать исполняемые скрипты. Например, вы пишете:
const allData = await FileAttachment("allData.csv").csv()
А allData.csv на самом деле не существует. Зато есть allData.csv.ps1. Фреймворк вызовет PowerShell-скрипт, получит результат — и подгрузит данные как будто это был обычный CSV-файл.
Чтобы это работало с PowerShell, нужно внести пару изменений.
1. Добавляем PowerShell в devcontainer
В devcontainer.json добавляем поддержку PowerShell:
{
"image": "mcr.microsoft.com/devcontainers/universal:2",
"hostRequirements": {
"cpus": 4
},
// skipping stuff
"features": {
"ghcr.io/devcontainers/features/powershell:1": {
"version": "latest"
}
}
// skipping stuff
}
2. Указываем интерпретатор .ps1 в observablehq.config.js
export default {
// skipping stuff
interpreters: {
".ps1": ["/usr/bin/pwsh"]
},
// skipping more stuff
};
Теперь все .ps1 будут исполняться через pwsh, как и нужно.
Больше про Data Loaders — здесь
Пример PowerShell-скрипта
Создаём /src/my-new-post/data.csv.ps1:
Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/vega/vega/refs/heads/main/docs/data/movies.json' |
ConvertFrom-Json |
Select-Object -Property Title, 'Worldwide Gross', 'US Gross', 'IMDB Rating' |
ConvertTo-Csv
Скрипт просто прочитывает откуда-то из Интернета json файл, фильтрует его, преобразует в csv и возвращает в стандартный вывод, это важно!
Теперь в заметке просто вставляем:
Inputs.table(FileAttachment("data.csv").csv())
И видим интерактивную таблицу прямо на странице.
Примеры живых визуализаций, сделанных по этому принципу:
- Права доступа и группы в Azure
- Разрешения и группы Azure с Vega
- Санкей-граф трафика через Azure Firewall
Azure Static Web Apps
Для публикации используем Azure Static Web Apps — бесплатный сервис от Microsoft для хостинга статических сайтов. Интеграция с GitHub простейшая: подключаете репозиторий, указываете папку сборки (dist) — и готово. При этом ,если вы дадите доступ к своему github, то Azure portal сможет создать workflow-файл и нужные секреты в проекте.
Но есть один нюанс: при сборке Azure использует Oryx, который запускается в контейнере без PowerShell. То есть скрипты .ps1 не сработают. Решение — собирать сайт до запуска таски деплоя, а Oryx пусть просто загрузит результат.
Пример рабочего GitHub Actions workflow, собственно того самого, созданного самим Azure, просто слегка подработанного напильником:
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v3
with:
submodules: true
lfs: false
# ADDING THIS STEP!!!
- name: Install dependencies and build
run: |
npm install
npm run build
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
# SKIPPING SECRETS :)
action: "upload"
# SETTING CORRECT app_location and output_location
app_location: "dist"
api_location: ""
output_location: "dist"
# DISABLING build, as it was built on the previous step!
skip_app_build: true
skip_api_build: true
# SKIPPING OTHER STUFF ....
Теперь ваш сайт собирается на GitHub Actions, а Azure Static Web Apps просто его размещает. Работает стабильно, быстро, и бесплатно.
Если вам нужен блог, в котором можно запускать скрипты, визуализировать данные и при этом не платить за хостинг — Observable Framework + Azure Static Web Apps — это то, что надо.