sqlite-hub 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmingnore +4 -0
- package/README.md +46 -0
- package/assets/images/logo.webp +0 -0
- package/assets/images/logo_extrasmall.webp +0 -0
- package/assets/images/logo_raw.png +0 -0
- package/assets/images/logo_small.webp +0 -0
- package/assets/mockups/connections.png +0 -0
- package/assets/mockups/data.png +0 -0
- package/assets/mockups/data_edit.png +0 -0
- package/assets/mockups/home.png +0 -0
- package/assets/mockups/overview.png +0 -0
- package/assets/mockups/sql_editor.png +0 -0
- package/assets/mockups/structure.png +0 -0
- package/bin/sqlite-hub.js +116 -0
- package/changelog.md +3 -0
- package/data/.gitkeep +0 -0
- package/index.html +100 -0
- package/js/api.js +193 -0
- package/js/app.js +520 -0
- package/js/components/actionBar.js +8 -0
- package/js/components/appShell.js +17 -0
- package/js/components/badges.js +5 -0
- package/js/components/bottomTabs.js +37 -0
- package/js/components/connectionCard.js +106 -0
- package/js/components/dataGrid.js +47 -0
- package/js/components/emptyState.js +159 -0
- package/js/components/metricCard.js +32 -0
- package/js/components/modal.js +317 -0
- package/js/components/pageHeader.js +33 -0
- package/js/components/queryEditor.js +121 -0
- package/js/components/queryResults.js +107 -0
- package/js/components/rowEditorPanel.js +164 -0
- package/js/components/sidebar.js +57 -0
- package/js/components/statusBar.js +39 -0
- package/js/components/toast.js +39 -0
- package/js/components/topNav.js +27 -0
- package/js/router.js +66 -0
- package/js/store.js +1092 -0
- package/js/utils/format.js +179 -0
- package/js/views/connections.js +133 -0
- package/js/views/data.js +400 -0
- package/js/views/editor.js +259 -0
- package/js/views/landing.js +11 -0
- package/js/views/overview.js +220 -0
- package/js/views/settings.js +109 -0
- package/js/views/structure.js +242 -0
- package/package.json +18 -0
- package/publish_brew.sh +444 -0
- package/publish_npm.sh +241 -0
- package/server/routes/connections.js +146 -0
- package/server/routes/data.js +59 -0
- package/server/routes/export.js +25 -0
- package/server/routes/overview.js +39 -0
- package/server/routes/settings.js +50 -0
- package/server/routes/sql.js +50 -0
- package/server/routes/structure.js +38 -0
- package/server/server.js +136 -0
- package/server/services/sqlite/connectionManager.js +306 -0
- package/server/services/sqlite/dataBrowserService.js +255 -0
- package/server/services/sqlite/exportService.js +34 -0
- package/server/services/sqlite/importService.js +111 -0
- package/server/services/sqlite/introspection.js +302 -0
- package/server/services/sqlite/overviewService.js +109 -0
- package/server/services/sqlite/sqlExecutor.js +434 -0
- package/server/services/sqlite/structureService.js +60 -0
- package/server/services/storage/appStateStore.js +530 -0
- package/server/utils/csv.js +34 -0
- package/server/utils/errors.js +175 -0
- package/server/utils/fileValidation.js +135 -0
- package/server/utils/identifier.js +38 -0
- package/server/utils/sqliteTypes.js +112 -0
- package/styles/base.css +176 -0
- package/styles/components.css +323 -0
- package/styles/layout.css +101 -0
- package/styles/tokens.css +49 -0
- package/styles/views.css +84 -0
package/publish_brew.sh
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
cd "$ROOT_DIR"
|
|
7
|
+
|
|
8
|
+
DRY_RUN=0
|
|
9
|
+
SKIP_AUDIT=0
|
|
10
|
+
ALLOW_DIRTY=0
|
|
11
|
+
VERSION_OVERRIDE=""
|
|
12
|
+
TAP_REPO_OVERRIDE=""
|
|
13
|
+
TAP_DIR_OVERRIDE=""
|
|
14
|
+
FORMULA_NAME_OVERRIDE=""
|
|
15
|
+
GH_REMOTE="origin"
|
|
16
|
+
|
|
17
|
+
usage() {
|
|
18
|
+
cat <<'EOF'
|
|
19
|
+
Usage:
|
|
20
|
+
./publish.sh [options]
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--version X.Y.Z Override the package.json version for this publish run.
|
|
24
|
+
--tap-repo OWNER/REPO
|
|
25
|
+
Homebrew tap repository. Default: <origin-owner>/homebrew-tap
|
|
26
|
+
--tap-dir PATH Local clone path for the tap repo. Default: ../homebrew-tap
|
|
27
|
+
--formula-name NAME Formula name. Default: package.json name
|
|
28
|
+
--allow-dirty Allow publishing with uncommitted changes in the source repo.
|
|
29
|
+
--skip-audit Skip `brew audit` for the generated formula.
|
|
30
|
+
--dry-run Print the steps without pushing or writing changes.
|
|
31
|
+
--help Show this help text.
|
|
32
|
+
|
|
33
|
+
Requirements:
|
|
34
|
+
- git
|
|
35
|
+
- gh (authenticated)
|
|
36
|
+
- curl
|
|
37
|
+
- shasum
|
|
38
|
+
- node
|
|
39
|
+
- brew (optional, only for audit)
|
|
40
|
+
EOF
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
while [[ $# -gt 0 ]]; do
|
|
44
|
+
case "$1" in
|
|
45
|
+
--version)
|
|
46
|
+
VERSION_OVERRIDE="${2:-}"
|
|
47
|
+
shift 2
|
|
48
|
+
;;
|
|
49
|
+
--tap-repo)
|
|
50
|
+
TAP_REPO_OVERRIDE="${2:-}"
|
|
51
|
+
shift 2
|
|
52
|
+
;;
|
|
53
|
+
--tap-dir)
|
|
54
|
+
TAP_DIR_OVERRIDE="${2:-}"
|
|
55
|
+
shift 2
|
|
56
|
+
;;
|
|
57
|
+
--formula-name)
|
|
58
|
+
FORMULA_NAME_OVERRIDE="${2:-}"
|
|
59
|
+
shift 2
|
|
60
|
+
;;
|
|
61
|
+
--allow-dirty)
|
|
62
|
+
ALLOW_DIRTY=1
|
|
63
|
+
shift
|
|
64
|
+
;;
|
|
65
|
+
--skip-audit)
|
|
66
|
+
SKIP_AUDIT=1
|
|
67
|
+
shift
|
|
68
|
+
;;
|
|
69
|
+
--dry-run)
|
|
70
|
+
DRY_RUN=1
|
|
71
|
+
shift
|
|
72
|
+
;;
|
|
73
|
+
--help|-h)
|
|
74
|
+
usage
|
|
75
|
+
exit 0
|
|
76
|
+
;;
|
|
77
|
+
*)
|
|
78
|
+
echo "Unknown argument: $1" >&2
|
|
79
|
+
usage
|
|
80
|
+
exit 1
|
|
81
|
+
;;
|
|
82
|
+
esac
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
require_cmd() {
|
|
86
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
87
|
+
echo "Missing required command: $1" >&2
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
run() {
|
|
93
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
94
|
+
printf '+'
|
|
95
|
+
printf ' %q' "$@"
|
|
96
|
+
printf '\n'
|
|
97
|
+
return 0
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
"$@"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
info() {
|
|
104
|
+
printf '==> %s\n' "$*"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
parse_github_slug() {
|
|
108
|
+
node - "$1" <<'NODE'
|
|
109
|
+
const remote = process.argv[2] ?? "";
|
|
110
|
+
const normalized = remote
|
|
111
|
+
.replace(/^git@github\.com:/, "")
|
|
112
|
+
.replace(/^https?:\/\/github\.com\//, "")
|
|
113
|
+
.replace(/\.git$/, "")
|
|
114
|
+
.replace(/\/+$/, "");
|
|
115
|
+
|
|
116
|
+
if (!/^[^/]+\/[^/]+$/.test(normalized)) {
|
|
117
|
+
console.error(`Could not derive GitHub owner/repo from remote: ${remote}`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
process.stdout.write(normalized);
|
|
122
|
+
NODE
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
json_field() {
|
|
126
|
+
node - "$1" <<'NODE'
|
|
127
|
+
const field = process.argv[2];
|
|
128
|
+
const pkg = require("./package.json");
|
|
129
|
+
const value = pkg[field];
|
|
130
|
+
|
|
131
|
+
if (value === undefined || value === null) {
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
process.stdout.write(String(value));
|
|
136
|
+
NODE
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
ruby_string() {
|
|
140
|
+
node - "$1" <<'NODE'
|
|
141
|
+
process.stdout.write(JSON.stringify(process.argv[2] ?? ""));
|
|
142
|
+
NODE
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
formula_class_name() {
|
|
146
|
+
node - "$1" <<'NODE'
|
|
147
|
+
const name = process.argv[2] ?? "";
|
|
148
|
+
const value = name
|
|
149
|
+
.split(/[^A-Za-z0-9]+/)
|
|
150
|
+
.filter(Boolean)
|
|
151
|
+
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
152
|
+
.join("");
|
|
153
|
+
|
|
154
|
+
process.stdout.write(value);
|
|
155
|
+
NODE
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
ensure_clean_worktree() {
|
|
159
|
+
if [[ "$ALLOW_DIRTY" == "1" ]]; then
|
|
160
|
+
return
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
if ! git diff --quiet --ignore-submodules HEAD -- || ! git diff --cached --quiet --ignore-submodules --; then
|
|
164
|
+
echo "Working tree is not clean. Commit or stash changes first, or rerun with --allow-dirty." >&2
|
|
165
|
+
exit 1
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
wait_for_tap_repo() {
|
|
170
|
+
local repo_url="https://github.com/${TAP_REPO}.git"
|
|
171
|
+
local attempts=12
|
|
172
|
+
|
|
173
|
+
for ((attempt = 1; attempt <= attempts; attempt += 1)); do
|
|
174
|
+
if gh repo view "$TAP_REPO" >/dev/null 2>&1 || git ls-remote "$repo_url" >/dev/null 2>&1; then
|
|
175
|
+
return 0
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
sleep 2
|
|
179
|
+
done
|
|
180
|
+
|
|
181
|
+
echo "Tap repository did not become available in time: $TAP_REPO" >&2
|
|
182
|
+
exit 1
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
clone_tap_repo() {
|
|
186
|
+
local repo_url="https://github.com/${TAP_REPO}.git"
|
|
187
|
+
run git clone "$repo_url" "$TAP_DIR"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
ensure_tap_repo() {
|
|
191
|
+
if [[ -d "$TAP_DIR/.git" ]]; then
|
|
192
|
+
info "Using existing tap checkout at $TAP_DIR"
|
|
193
|
+
return
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
if gh repo view "$TAP_REPO" >/dev/null 2>&1; then
|
|
197
|
+
info "Cloning existing tap repo $TAP_REPO"
|
|
198
|
+
clone_tap_repo
|
|
199
|
+
return
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
info "Creating tap repo $TAP_REPO"
|
|
203
|
+
run gh repo create "$TAP_REPO" --public --clone=false --description "Homebrew tap for $OWNER tools"
|
|
204
|
+
if [[ "$DRY_RUN" != "1" ]]; then
|
|
205
|
+
wait_for_tap_repo
|
|
206
|
+
fi
|
|
207
|
+
clone_tap_repo
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
ensure_git_tag() {
|
|
211
|
+
if git rev-parse "$TAG" >/dev/null 2>&1; then
|
|
212
|
+
info "Tag $TAG already exists locally"
|
|
213
|
+
else
|
|
214
|
+
info "Creating git tag $TAG"
|
|
215
|
+
run git tag -a "$TAG" -m "Release $TAG"
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
if git ls-remote --exit-code --tags "$GH_REMOTE" "refs/tags/$TAG" >/dev/null 2>&1; then
|
|
219
|
+
info "Tag $TAG already exists on $GH_REMOTE"
|
|
220
|
+
else
|
|
221
|
+
info "Pushing tag $TAG to $GH_REMOTE"
|
|
222
|
+
run git push "$GH_REMOTE" "$TAG"
|
|
223
|
+
fi
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
ensure_github_release() {
|
|
227
|
+
if gh release view "$TAG" --repo "$SOURCE_REPO" >/dev/null 2>&1; then
|
|
228
|
+
info "GitHub release $TAG already exists"
|
|
229
|
+
return
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
info "Creating GitHub release $TAG"
|
|
233
|
+
run gh release create "$TAG" \
|
|
234
|
+
--repo "$SOURCE_REPO" \
|
|
235
|
+
--title "$TAG" \
|
|
236
|
+
--generate-notes
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
download_source_archive() {
|
|
240
|
+
ARCHIVE_DIR="$(mktemp -d)"
|
|
241
|
+
ARCHIVE_PATH="$ARCHIVE_DIR/${PACKAGE_NAME}-${VERSION}.tar.gz"
|
|
242
|
+
trap 'rm -rf "$ARCHIVE_DIR"' EXIT
|
|
243
|
+
|
|
244
|
+
info "Downloading source archive $ARCHIVE_URL"
|
|
245
|
+
run curl -LfsS "$ARCHIVE_URL" -o "$ARCHIVE_PATH"
|
|
246
|
+
|
|
247
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
248
|
+
ARCHIVE_SHA256="DRY_RUN_SHA256"
|
|
249
|
+
else
|
|
250
|
+
ARCHIVE_SHA256="$(shasum -a 256 "$ARCHIVE_PATH" | awk '{print $1}')"
|
|
251
|
+
fi
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
write_formula() {
|
|
255
|
+
local formula_dir="$TAP_DIR/Formula"
|
|
256
|
+
local formula_path="$formula_dir/${FORMULA_NAME}.rb"
|
|
257
|
+
|
|
258
|
+
mkdir -p "$formula_dir"
|
|
259
|
+
|
|
260
|
+
cat >"$formula_path" <<EOF
|
|
261
|
+
class ${FORMULA_CLASS} < Formula
|
|
262
|
+
desc ${DESC_RUBY}
|
|
263
|
+
homepage ${HOMEPAGE_RUBY}
|
|
264
|
+
url ${ARCHIVE_URL_RUBY}
|
|
265
|
+
sha256 ${SHA256_RUBY}
|
|
266
|
+
|
|
267
|
+
depends_on "node"
|
|
268
|
+
depends_on "python" => :build
|
|
269
|
+
|
|
270
|
+
def install
|
|
271
|
+
ENV["npm_config_build_from_source"] = "true"
|
|
272
|
+
system "npm", "install", *std_npm_args
|
|
273
|
+
cd libexec/"lib/node_modules/${PACKAGE_NAME}" do
|
|
274
|
+
system "npm", "rebuild", "better-sqlite3"
|
|
275
|
+
end
|
|
276
|
+
bin.install_symlink libexec.glob("bin/*")
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
test do
|
|
280
|
+
output = shell_output(
|
|
281
|
+
"cd #{libexec}/lib/node_modules/${PACKAGE_NAME} && #{Formula["node"].opt_bin}/node -e " \
|
|
282
|
+
"'const Database = require(\"better-sqlite3\"); const db = new Database(\":memory:\"); " \
|
|
283
|
+
"console.log(db.prepare(\"select 1 as value\").get().value); db.close();'"
|
|
284
|
+
)
|
|
285
|
+
assert_equal "1\n", output
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
EOF
|
|
289
|
+
|
|
290
|
+
FORMULA_PATH="$formula_path"
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
commit_and_push_tap() {
|
|
294
|
+
local formula_rel_path="Formula/${FORMULA_NAME}.rb"
|
|
295
|
+
|
|
296
|
+
if [[ -z "$(git -C "$TAP_DIR" status --porcelain -- "$formula_rel_path")" ]]; then
|
|
297
|
+
info "Formula already up to date in tap repo"
|
|
298
|
+
return
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
info "Committing formula update to tap repo"
|
|
302
|
+
run git -C "$TAP_DIR" add "$formula_rel_path"
|
|
303
|
+
run git -C "$TAP_DIR" commit -m "${FORMULA_NAME} ${VERSION}"
|
|
304
|
+
run git -C "$TAP_DIR" push origin HEAD
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
run_brew_audit() {
|
|
308
|
+
if [[ "$SKIP_AUDIT" == "1" ]]; then
|
|
309
|
+
info "Skipping brew audit"
|
|
310
|
+
return
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
314
|
+
info "Dry run, skipping brew audit"
|
|
315
|
+
return
|
|
316
|
+
fi
|
|
317
|
+
|
|
318
|
+
if ! command -v brew >/dev/null 2>&1; then
|
|
319
|
+
info "brew not found, skipping audit"
|
|
320
|
+
return
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
local audit_tap="sqlite-hub/publish-audit"
|
|
324
|
+
local brew_repo
|
|
325
|
+
local audit_owner
|
|
326
|
+
local audit_repo
|
|
327
|
+
local audit_tap_path
|
|
328
|
+
local formula_ref
|
|
329
|
+
local status=0
|
|
330
|
+
|
|
331
|
+
brew_repo="$(brew --repository)"
|
|
332
|
+
audit_owner="${audit_tap%%/*}"
|
|
333
|
+
audit_repo="${audit_tap##*/}"
|
|
334
|
+
audit_tap_path="${brew_repo}/Library/Taps/${audit_owner}/homebrew-${audit_repo}"
|
|
335
|
+
formula_ref="${audit_tap}/${FORMULA_NAME}"
|
|
336
|
+
|
|
337
|
+
mkdir -p "$(dirname "$audit_tap_path")"
|
|
338
|
+
rm -rf "$audit_tap_path"
|
|
339
|
+
ln -s "$TAP_DIR" "$audit_tap_path"
|
|
340
|
+
|
|
341
|
+
info "Running brew audit on generated formula"
|
|
342
|
+
brew audit --strict --formula "$formula_ref" || status=$?
|
|
343
|
+
|
|
344
|
+
rm -f "$audit_tap_path"
|
|
345
|
+
|
|
346
|
+
if [[ "$status" -ne 0 ]]; then
|
|
347
|
+
exit "$status"
|
|
348
|
+
fi
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
require_cmd git
|
|
352
|
+
require_cmd gh
|
|
353
|
+
require_cmd curl
|
|
354
|
+
require_cmd shasum
|
|
355
|
+
require_cmd node
|
|
356
|
+
|
|
357
|
+
ensure_clean_worktree
|
|
358
|
+
|
|
359
|
+
SOURCE_REMOTE_URL="$(git config --get remote.${GH_REMOTE}.url)"
|
|
360
|
+
SOURCE_REPO="$(parse_github_slug "$SOURCE_REMOTE_URL")"
|
|
361
|
+
OWNER="${SOURCE_REPO%%/*}"
|
|
362
|
+
REPO_NAME="${SOURCE_REPO##*/}"
|
|
363
|
+
PACKAGE_NAME="$(json_field name)"
|
|
364
|
+
VERSION="${VERSION_OVERRIDE:-$(json_field version)}"
|
|
365
|
+
DESCRIPTION="$(json_field description)"
|
|
366
|
+
HOMEPAGE="https://github.com/${SOURCE_REPO}"
|
|
367
|
+
TAG="v${VERSION}"
|
|
368
|
+
ARCHIVE_URL="https://github.com/${SOURCE_REPO}/archive/refs/tags/${TAG}.tar.gz"
|
|
369
|
+
TAP_REPO="${TAP_REPO_OVERRIDE:-${OWNER}/homebrew-tap}"
|
|
370
|
+
TAP_DIR="${TAP_DIR_OVERRIDE:-$(cd "$ROOT_DIR/.." && pwd)/homebrew-tap}"
|
|
371
|
+
TAP_OWNER="${TAP_REPO%%/*}"
|
|
372
|
+
TAP_REPO_NAME="${TAP_REPO##*/}"
|
|
373
|
+
BREW_TAP_NAME="${TAP_OWNER}/${TAP_REPO_NAME#homebrew-}"
|
|
374
|
+
FORMULA_NAME="${FORMULA_NAME_OVERRIDE:-$PACKAGE_NAME}"
|
|
375
|
+
FORMULA_CLASS="$(formula_class_name "$FORMULA_NAME")"
|
|
376
|
+
CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
|
|
377
|
+
|
|
378
|
+
DESC_RUBY="$(ruby_string "$DESCRIPTION")"
|
|
379
|
+
HOMEPAGE_RUBY="$(ruby_string "$HOMEPAGE")"
|
|
380
|
+
ARCHIVE_URL_RUBY="$(ruby_string "$ARCHIVE_URL")"
|
|
381
|
+
|
|
382
|
+
info "Publishing ${PACKAGE_NAME} ${VERSION}"
|
|
383
|
+
info "Source repo: ${SOURCE_REPO}"
|
|
384
|
+
info "Tap repo: ${TAP_REPO}"
|
|
385
|
+
info "Brew tap: ${BREW_TAP_NAME}"
|
|
386
|
+
info "Tap dir: ${TAP_DIR}"
|
|
387
|
+
info "Formula: ${FORMULA_NAME}"
|
|
388
|
+
|
|
389
|
+
if git ls-remote --exit-code --heads "$GH_REMOTE" "$CURRENT_BRANCH" >/dev/null 2>&1; then
|
|
390
|
+
info "Pushing branch $CURRENT_BRANCH to $GH_REMOTE"
|
|
391
|
+
run git push "$GH_REMOTE" "$CURRENT_BRANCH"
|
|
392
|
+
else
|
|
393
|
+
info "Branch $CURRENT_BRANCH does not exist on $GH_REMOTE yet, pushing it"
|
|
394
|
+
run git push -u "$GH_REMOTE" "$CURRENT_BRANCH"
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
ensure_git_tag
|
|
398
|
+
ensure_github_release
|
|
399
|
+
download_source_archive
|
|
400
|
+
|
|
401
|
+
SHA256_RUBY="$(ruby_string "$ARCHIVE_SHA256")"
|
|
402
|
+
|
|
403
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
404
|
+
info "Dry run formula preview"
|
|
405
|
+
FORMULA_PATH="${TAP_DIR}/Formula/${FORMULA_NAME}.rb"
|
|
406
|
+
cat <<EOF
|
|
407
|
+
class ${FORMULA_CLASS} < Formula
|
|
408
|
+
desc ${DESC_RUBY}
|
|
409
|
+
homepage ${HOMEPAGE_RUBY}
|
|
410
|
+
url ${ARCHIVE_URL_RUBY}
|
|
411
|
+
sha256 ${SHA256_RUBY}
|
|
412
|
+
|
|
413
|
+
depends_on "node"
|
|
414
|
+
depends_on "python" => :build
|
|
415
|
+
|
|
416
|
+
def install
|
|
417
|
+
ENV["npm_config_build_from_source"] = "true"
|
|
418
|
+
system "npm", "install", *std_npm_args
|
|
419
|
+
cd libexec/"lib/node_modules/${PACKAGE_NAME}" do
|
|
420
|
+
system "npm", "rebuild", "better-sqlite3"
|
|
421
|
+
end
|
|
422
|
+
bin.install_symlink libexec.glob("bin/*")
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
test do
|
|
426
|
+
output = shell_output(
|
|
427
|
+
"cd #{libexec}/lib/node_modules/${PACKAGE_NAME} && #{Formula["node"].opt_bin}/node -e " \
|
|
428
|
+
"'const Database = require(\"better-sqlite3\"); const db = new Database(\":memory:\"); " \
|
|
429
|
+
"console.log(db.prepare(\"select 1 as value\").get().value); db.close();'"
|
|
430
|
+
)
|
|
431
|
+
assert_equal "1\n", output
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
EOF
|
|
435
|
+
exit 0
|
|
436
|
+
fi
|
|
437
|
+
|
|
438
|
+
ensure_tap_repo
|
|
439
|
+
write_formula
|
|
440
|
+
run_brew_audit
|
|
441
|
+
commit_and_push_tap
|
|
442
|
+
|
|
443
|
+
info "Published ${FORMULA_NAME} ${VERSION}"
|
|
444
|
+
info "Install with: brew install ${BREW_TAP_NAME}/${FORMULA_NAME}"
|
package/publish_npm.sh
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
cd "$ROOT_DIR"
|
|
7
|
+
|
|
8
|
+
DRY_RUN=0
|
|
9
|
+
ALLOW_DIRTY=0
|
|
10
|
+
DIST_TAG=""
|
|
11
|
+
ACCESS=""
|
|
12
|
+
OTP=""
|
|
13
|
+
GH_REMOTE="origin"
|
|
14
|
+
|
|
15
|
+
usage() {
|
|
16
|
+
cat <<'EOF'
|
|
17
|
+
Usage:
|
|
18
|
+
./publish_npm.sh [options]
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
--tag NAME Publish to a custom npm dist-tag instead of `latest`.
|
|
22
|
+
--access LEVEL Forward `--access` to `npm publish` (for scoped packages).
|
|
23
|
+
--otp CODE One-time password for npm 2FA protected publishes.
|
|
24
|
+
--allow-dirty Allow publishing from a dirty worktree.
|
|
25
|
+
--dry-run Build the tarball and run `npm publish --dry-run`.
|
|
26
|
+
--help Show this help text.
|
|
27
|
+
|
|
28
|
+
Requirements:
|
|
29
|
+
- git
|
|
30
|
+
- node
|
|
31
|
+
- npm
|
|
32
|
+
|
|
33
|
+
Notes:
|
|
34
|
+
- A clean worktree is required by default so the published package matches git.
|
|
35
|
+
- In `--allow-dirty` mode, git branch/tag sync is skipped on purpose.
|
|
36
|
+
EOF
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
while [[ $# -gt 0 ]]; do
|
|
40
|
+
case "$1" in
|
|
41
|
+
--tag)
|
|
42
|
+
DIST_TAG="${2:-}"
|
|
43
|
+
shift 2
|
|
44
|
+
;;
|
|
45
|
+
--access)
|
|
46
|
+
ACCESS="${2:-}"
|
|
47
|
+
shift 2
|
|
48
|
+
;;
|
|
49
|
+
--otp)
|
|
50
|
+
OTP="${2:-}"
|
|
51
|
+
shift 2
|
|
52
|
+
;;
|
|
53
|
+
--allow-dirty)
|
|
54
|
+
ALLOW_DIRTY=1
|
|
55
|
+
shift
|
|
56
|
+
;;
|
|
57
|
+
--dry-run)
|
|
58
|
+
DRY_RUN=1
|
|
59
|
+
shift
|
|
60
|
+
;;
|
|
61
|
+
--help|-h)
|
|
62
|
+
usage
|
|
63
|
+
exit 0
|
|
64
|
+
;;
|
|
65
|
+
*)
|
|
66
|
+
echo "Unknown argument: $1" >&2
|
|
67
|
+
usage
|
|
68
|
+
exit 1
|
|
69
|
+
;;
|
|
70
|
+
esac
|
|
71
|
+
done
|
|
72
|
+
|
|
73
|
+
require_cmd() {
|
|
74
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
75
|
+
echo "Missing required command: $1" >&2
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
run() {
|
|
81
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
82
|
+
printf '+'
|
|
83
|
+
printf ' %q' "$@"
|
|
84
|
+
printf '\n'
|
|
85
|
+
return 0
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
"$@"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
info() {
|
|
92
|
+
printf '==> %s\n' "$*"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
json_field() {
|
|
96
|
+
node - "$1" <<'NODE'
|
|
97
|
+
const field = process.argv[2];
|
|
98
|
+
const pkg = require("./package.json");
|
|
99
|
+
const value = pkg[field];
|
|
100
|
+
|
|
101
|
+
if (value === undefined || value === null) {
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
process.stdout.write(String(value));
|
|
106
|
+
NODE
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
is_clean_worktree() {
|
|
110
|
+
git diff --quiet --ignore-submodules HEAD -- && git diff --cached --quiet --ignore-submodules --
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
ensure_clean_worktree() {
|
|
114
|
+
if [[ "$ALLOW_DIRTY" == "1" ]]; then
|
|
115
|
+
return
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
if ! is_clean_worktree; then
|
|
119
|
+
echo "Working tree is not clean. Commit or stash changes first, or rerun with --allow-dirty." >&2
|
|
120
|
+
exit 1
|
|
121
|
+
fi
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
ensure_branch_on_remote() {
|
|
125
|
+
local current_branch
|
|
126
|
+
current_branch="$(git rev-parse --abbrev-ref HEAD)"
|
|
127
|
+
|
|
128
|
+
if [[ "$current_branch" == "HEAD" ]]; then
|
|
129
|
+
info "Detached HEAD detected, skipping branch push"
|
|
130
|
+
return
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
if git ls-remote --exit-code --heads "$GH_REMOTE" "$current_branch" >/dev/null 2>&1; then
|
|
134
|
+
info "Pushing branch $current_branch to $GH_REMOTE"
|
|
135
|
+
run git push "$GH_REMOTE" "$current_branch"
|
|
136
|
+
else
|
|
137
|
+
info "Branch $current_branch does not exist on $GH_REMOTE yet, pushing it"
|
|
138
|
+
run git push -u "$GH_REMOTE" "$current_branch"
|
|
139
|
+
fi
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
ensure_git_tag() {
|
|
143
|
+
if git rev-parse "$GIT_TAG" >/dev/null 2>&1; then
|
|
144
|
+
info "Tag $GIT_TAG already exists locally"
|
|
145
|
+
else
|
|
146
|
+
info "Creating git tag $GIT_TAG"
|
|
147
|
+
run git tag -a "$GIT_TAG" -m "Release $GIT_TAG"
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
if git ls-remote --exit-code --tags "$GH_REMOTE" "refs/tags/$GIT_TAG" >/dev/null 2>&1; then
|
|
151
|
+
info "Tag $GIT_TAG already exists on $GH_REMOTE"
|
|
152
|
+
else
|
|
153
|
+
info "Pushing tag $GIT_TAG to $GH_REMOTE"
|
|
154
|
+
run git push "$GH_REMOTE" "$GIT_TAG"
|
|
155
|
+
fi
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
ensure_npm_auth() {
|
|
159
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
160
|
+
info "Dry run, skipping npm auth check"
|
|
161
|
+
return
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
info "Checking npm auth"
|
|
165
|
+
npm whoami >/dev/null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
ensure_version_not_published() {
|
|
169
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
170
|
+
info "Dry run, skipping npm registry version check"
|
|
171
|
+
return
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
if npm view "${PACKAGE_NAME}@${VERSION}" version >/dev/null 2>&1; then
|
|
175
|
+
echo "Version ${PACKAGE_NAME}@${VERSION} is already published on npm." >&2
|
|
176
|
+
exit 1
|
|
177
|
+
fi
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
pack_package() {
|
|
181
|
+
PACK_DIR="$(mktemp -d)"
|
|
182
|
+
trap 'rm -rf "$PACK_DIR"' EXIT
|
|
183
|
+
|
|
184
|
+
info "Packing npm tarball"
|
|
185
|
+
TARBALL_NAME="$(cd "$PACK_DIR" && npm pack "$ROOT_DIR")"
|
|
186
|
+
TARBALL_PATH="$PACK_DIR/$TARBALL_NAME"
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
publish_tarball() {
|
|
190
|
+
local publish_cmd=(npm publish "$TARBALL_PATH")
|
|
191
|
+
|
|
192
|
+
if [[ -n "$DIST_TAG" ]]; then
|
|
193
|
+
publish_cmd+=(--tag "$DIST_TAG")
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
if [[ -n "$ACCESS" ]]; then
|
|
197
|
+
publish_cmd+=(--access "$ACCESS")
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
if [[ -n "$OTP" ]]; then
|
|
201
|
+
publish_cmd+=(--otp "$OTP")
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
205
|
+
publish_cmd+=(--dry-run)
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
info "Publishing ${PACKAGE_NAME}@${VERSION} to npm"
|
|
209
|
+
"${publish_cmd[@]}"
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
require_cmd git
|
|
213
|
+
require_cmd node
|
|
214
|
+
require_cmd npm
|
|
215
|
+
|
|
216
|
+
ensure_clean_worktree
|
|
217
|
+
|
|
218
|
+
PACKAGE_NAME="$(json_field name)"
|
|
219
|
+
VERSION="$(json_field version)"
|
|
220
|
+
GIT_TAG="v${VERSION}"
|
|
221
|
+
|
|
222
|
+
info "Preparing npm publish for ${PACKAGE_NAME}@${VERSION}"
|
|
223
|
+
|
|
224
|
+
if is_clean_worktree; then
|
|
225
|
+
ensure_branch_on_remote
|
|
226
|
+
ensure_git_tag
|
|
227
|
+
else
|
|
228
|
+
info "Dirty worktree allowed, skipping git branch/tag sync"
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
ensure_npm_auth
|
|
232
|
+
ensure_version_not_published
|
|
233
|
+
pack_package
|
|
234
|
+
publish_tarball
|
|
235
|
+
|
|
236
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
237
|
+
info "Dry run completed for ${PACKAGE_NAME}@${VERSION}"
|
|
238
|
+
else
|
|
239
|
+
info "Published ${PACKAGE_NAME}@${VERSION}"
|
|
240
|
+
info "Install with: npm install -g ${PACKAGE_NAME}"
|
|
241
|
+
fi
|