Skip to content

Commit 92db56c

Browse files
Публикация в npm: пакет открыт, flang входит, публикует тег
Пакет был помечен "private": true, поэтому не публиковался вовсе, а в документации нельзя было честно написать npm install. Флаг снят. В пакет добавлен сам язык — flang/{bin,src,stdlib,core,examples,SPEC.md} и запись flang в bin. Прежде туда попадало только ядро FTS и инструменты, то есть главное, что в репозитории есть, до потребителя не доезжало. Пока самоприменение не сведено и flang требует Node, npm остаётся его рабочим каналом доставки. Попутно из пакета убраны тесты: их туда уезжало 50 файлов. Потребителю они не нужны, а вес несут. Проверено на распакованном тарболе, а не на словах: flang из пакета запускается и проверяет модуль stdlib. Состав — 320 файлов, 941 КБ, ноль тестовых файлов. Публикация вынесена в workflow на тег v*, а не на push в main. Причина в необратимости: версию нельзя переиздать под тем же номером, а снять можно лишь 72 часа и ценой репутации пакета. Тег — явное решение человека «эту версию наружу», и принимать его надо отдельно от «влить ветку». Перед публикацией workflow прогоняет весь набор тестов, сверяет версию в теге с package.json (расхождение — самая частая причина того, что в реестр уезжает не то), проверяет, что в тарбол не попали тесты, и запускает flang из собранного пакета. Включён --provenance: npm свяжет версию с коммитом и workflow, и любой сможет проверить происхождение тарбола. Без секрета NPM_TOKEN шаг публикации пропускается с предупреждением, а не падает: отсутствие секрета на форке не поломка.
1 parent 18d3b40 commit 92db56c

2 files changed

Lines changed: 139 additions & 2 deletions

File tree

.github/workflows/publish-npm.yml

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Публикация пакета @digitable/fts в npm.
2+
#
3+
# Запускается только на теге вида v0.4.0 — не на каждый push в main. Причина в
4+
# необратимости: опубликованную версию нельзя переиздать под тем же номером, а
5+
# снять её можно лишь в первые 72 часа и ценой репутации пакета. Тег — это
6+
# явное решение человека «вот эту версию наружу», и оно должно приниматься
7+
# отдельно от решения «влить ветку».
8+
#
9+
# Проверки идут ДО публикации и в полном объёме: npm test (ядро, инструменты и
10+
# язык), сборка TypeScript и сверка версии в теге с версией в package.json.
11+
# Расхождение тега и package.json — самая частая причина того, что в реестр
12+
# уезжает не то, что ожидали, поэтому это отдельный шаг с внятным сообщением.
13+
#
14+
# Требуется секрет NPM_TOKEN — токен npm с правом публикации в scope
15+
# @digitable. Без него шаг публикации не выполняется, а workflow сообщает об
16+
# этом предупреждением, а не падает: отсутствие секрета на форке не должно
17+
# выглядеть поломкой.
18+
#
19+
# Провенанс (--provenance) включён: npm свяжет опубликованную версию с этим
20+
# workflow и коммитом, и любой сможет проверить, откуда взялся тарбол. Для
21+
# этого нужны права id-token: write, они выданы ниже.
22+
23+
name: Публикация в npm
24+
25+
on:
26+
push:
27+
tags: ["v*"]
28+
workflow_dispatch:
29+
inputs:
30+
dry-run:
31+
description: "Только проверить, не публикуя"
32+
type: boolean
33+
default: true
34+
35+
permissions:
36+
contents: read
37+
id-token: write
38+
39+
jobs:
40+
publish:
41+
runs-on: ubuntu-latest
42+
steps:
43+
- uses: actions/checkout@v4
44+
45+
- uses: actions/setup-node@v4
46+
with:
47+
node-version: "20"
48+
registry-url: "https://registry.npmjs.org"
49+
50+
- name: Установить зависимости
51+
run: npm ci
52+
53+
- name: Проверить, что тег совпадает с версией пакета
54+
if: startsWith(github.ref, 'refs/tags/v')
55+
run: |
56+
set -eu
57+
тег="${GITHUB_REF_NAME#v}"
58+
пакет="$(node -p 'require("./package.json").version')"
59+
if [ "$тег" != "$пакет" ]; then
60+
echo "::error::тег $GITHUB_REF_NAME обещает версию $тег, а package.json говорит $пакет"
61+
exit 1
62+
fi
63+
echo "версия $пакет подтверждена тегом"
64+
65+
- name: Собрать
66+
run: npm run build
67+
68+
- name: Прогнать весь набор тестов
69+
# Ядро, инструменты и язык. prepublishOnly сделал бы то же самое, но
70+
# отдельным шагом видно, что именно упало, не разбирая вывод publish.
71+
run: npm test
72+
73+
- name: Проверить состав пакета
74+
# Тесты и служебное в тарбол попадать не должны: однажды туда уже
75+
# уезжали 50 тестовых файлов. Проверка дешёвая, а находка дорогая.
76+
run: |
77+
set -eu
78+
npm pack --dry-run --json > /tmp/pack.json
79+
node -e '
80+
const [pkg] = require("/tmp/pack.json")
81+
const лишнее = pkg.files.map((f) => f.path).filter((p) => /(^|\/)test\/|\.test\.mjs$/.test(p))
82+
if (лишнее.length > 0) {
83+
console.error("в пакет попали тестовые файлы:\n " + лишнее.slice(0, 10).join("\n "))
84+
process.exit(1)
85+
}
86+
console.log(`файлов ${pkg.entryCount}, размер ${(pkg.size / 1024).toFixed(0)} КБ`)
87+
'
88+
89+
- name: Проверить, что flang запускается из пакета
90+
# Пакет объявляет исполняемый файл flang; если он не находит свои
91+
# модули, потребитель узнает об этом первым. Дешевле узнать здесь.
92+
run: |
93+
set -eu
94+
npm pack --pack-destination /tmp/pkg >/dev/null
95+
mkdir -p /tmp/pkg/распакованный
96+
tar -xzf /tmp/pkg/*.tgz -C /tmp/pkg/распакованный
97+
node /tmp/pkg/распакованный/package/flang/bin/flang.mjs check \
98+
/tmp/pkg/распакованный/package/flang/stdlib/lists.flang > /tmp/pkg/итог.json
99+
node -e '
100+
const итог = require("/tmp/pkg/итог.json")
101+
if (итог.valid !== true) { console.error("flang из пакета не работает"); process.exit(1) }
102+
console.log(`flang из пакета проверил модуль «${итог.module}»: ${итог.functions.length} функций`)
103+
'
104+
105+
- name: Проверить, настроен ли секрет
106+
id: guard
107+
env:
108+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
109+
run: |
110+
if [ -z "${NPM_TOKEN:-}" ]; then
111+
echo "настроен=нет" >> "$GITHUB_OUTPUT"
112+
echo "::warning::NPM_TOKEN не задан — публикация пропущена. Заведите токен с правом публикации в scope @digitable."
113+
else
114+
echo "настроен=да" >> "$GITHUB_OUTPUT"
115+
fi
116+
117+
- name: Опубликовать
118+
if: steps.guard.outputs.настроен == 'да' && inputs.dry-run != true
119+
env:
120+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
121+
# --access public обязателен: пакеты в scope по умолчанию приватные, и
122+
# без флага первая публикация упрётся в платный план.
123+
run: npm publish --access public --provenance
124+
125+
- name: Показать, что было бы опубликовано
126+
if: inputs.dry-run == true
127+
env:
128+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
129+
run: npm publish --access public --dry-run

package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"description": "flang: a checkable language with total and ordinary functions, emitted to C, Go and JavaScript, over the FTS executable-specification surface.",
55
"type": "module",
66
"license": "BSD-2-Clause",
7-
"private": true,
87
"repository": {
98
"type": "git",
109
"url": "git+https://github.com/digitable-lol/flang.git"
@@ -19,7 +18,8 @@
1918
"fts-mcp": "./dist/src/mcp.js",
2019
"ftsc": "./tools/ftsc/bin/ftsc.mjs",
2120
"ftsvm": "./tools/ftsvm/bin/ftsvm.mjs",
22-
"ftspec": "./tools/ftspec/bin/ftspec.mjs"
21+
"ftspec": "./tools/ftspec/bin/ftspec.mjs",
22+
"flang": "./flang/bin/flang.mjs"
2323
},
2424
"main": "./dist/src/index.js",
2525
"types": "./dist/src/index.d.ts",
@@ -43,6 +43,14 @@
4343
"schema",
4444
"examples",
4545
"tools",
46+
"flang/bin",
47+
"flang/src",
48+
"flang/stdlib",
49+
"flang/core",
50+
"flang/examples",
51+
"flang/SPEC.md",
52+
"!**/test",
53+
"!**/*.test.mjs",
4654
"README.md",
4755
"README.ru.md",
4856
"CONTRIBUTING.md",

0 commit comments

Comments
 (0)