@delorenj/pjangler 1.1.4 → 1.1.5
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/.mise/scripts/versioning.sh +236 -0
- package/dist/index.js +19 -2
- package/package.json +3 -2
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# versioning.sh — single source of all repo versioning logic.
|
|
3
|
+
#
|
|
4
|
+
# Installed by the `mise-versioning` skill. All version read/write/bump logic
|
|
5
|
+
# lives here; mise tasks are thin wrappers that call into it.
|
|
6
|
+
#
|
|
7
|
+
# Canonical version = the HIGHEST semver across every file in the manifest.
|
|
8
|
+
# All manifest files are kept in parity on every write (bump / set / sync).
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# versioning.sh current Print canonical version as vX.Y.Z
|
|
12
|
+
# versioning.sh bump <patch|minor|major>
|
|
13
|
+
# versioning.sh set <X.Y.Z|vX.Y.Z> Force all files to an explicit version
|
|
14
|
+
# versioning.sh check Exit 0 if all files in parity, else list drift
|
|
15
|
+
# versioning.sh sync Force every file up to the canonical (highest) version
|
|
16
|
+
# versioning.sh files List the manifest
|
|
17
|
+
#
|
|
18
|
+
# Manifest: .mise/version-files.conf — lines of "<type> <path>" (see file-types).
|
|
19
|
+
# types: json toml cargo csproj gradle plain gittag
|
|
20
|
+
#
|
|
21
|
+
# Storage format per type:
|
|
22
|
+
# json/toml/cargo/csproj/gradle/plain -> bare "X.Y.Z"
|
|
23
|
+
# gittag -> tag "vX.Y.Z"
|
|
24
|
+
# `current` always prints with the leading "v".
|
|
25
|
+
|
|
26
|
+
set -euo pipefail
|
|
27
|
+
|
|
28
|
+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
29
|
+
MANIFEST="${VERSION_FILES_CONF:-$REPO_ROOT/.mise/version-files.conf}"
|
|
30
|
+
|
|
31
|
+
die() { printf 'versioning: %s\n' "$1" >&2; exit 1; }
|
|
32
|
+
|
|
33
|
+
# Friendly display path for a manifest entry.
|
|
34
|
+
rel() { # $1=type $2=abspath
|
|
35
|
+
[[ "$1" == gittag ]] && { printf '(git tags)'; return; }
|
|
36
|
+
local p="${2#"$REPO_ROOT"/}"; printf '%s' "${p:-$2}"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# ---- semver helpers ---------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
# Strip a leading v/V and surrounding whitespace; validate X.Y.Z.
|
|
42
|
+
normalize() {
|
|
43
|
+
local v="${1#[vV]}"
|
|
44
|
+
v="$(printf '%s' "$v" | tr -d '[:space:]')"
|
|
45
|
+
[[ "$v" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1
|
|
46
|
+
printf '%s' "$v"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# Return 0 if $1 > $2 (strict), comparing X.Y.Z numerically.
|
|
50
|
+
semver_gt() {
|
|
51
|
+
local a b
|
|
52
|
+
IFS=. read -r a1 a2 a3 <<<"$1"
|
|
53
|
+
IFS=. read -r b1 b2 b3 <<<"$2"
|
|
54
|
+
((a1 != b1)) && { ((a1 > b1)); return; }
|
|
55
|
+
((a2 != b2)) && { ((a2 > b2)); return; }
|
|
56
|
+
((a3 > b3))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
bump_semver() {
|
|
60
|
+
local ver="$1" part="$2" x y z
|
|
61
|
+
IFS=. read -r x y z <<<"$ver"
|
|
62
|
+
case "$part" in
|
|
63
|
+
major) x=$((x + 1)); y=0; z=0 ;;
|
|
64
|
+
minor) y=$((y + 1)); z=0 ;;
|
|
65
|
+
patch) z=$((z + 1)) ;;
|
|
66
|
+
*) die "unknown bump part: $part (expected patch|minor|major)" ;;
|
|
67
|
+
esac
|
|
68
|
+
printf '%s.%s.%s' "$x" "$y" "$z"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# ---- manifest ---------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
[[ -f "$MANIFEST" ]] || die "manifest not found: $MANIFEST (run the mise-versioning init)"
|
|
74
|
+
|
|
75
|
+
# Emits "type<TAB>abspath" per manifest line, skipping blanks/comments.
|
|
76
|
+
manifest_entries() {
|
|
77
|
+
while read -r type path _rest; do
|
|
78
|
+
[[ -z "${type:-}" || "$type" == \#* ]] && continue
|
|
79
|
+
case "$path" in
|
|
80
|
+
/*) : ;;
|
|
81
|
+
.|"") path="$REPO_ROOT" ;;
|
|
82
|
+
*) path="$REPO_ROOT/$path" ;;
|
|
83
|
+
esac
|
|
84
|
+
printf '%s\t%s\n' "$type" "$path"
|
|
85
|
+
done <"$MANIFEST"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# ---- per-type read -----------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
read_version() { # $1=type $2=path -> bare X.Y.Z on stdout, or nothing
|
|
91
|
+
local type="$1" path="$2" raw=""
|
|
92
|
+
case "$type" in
|
|
93
|
+
json)
|
|
94
|
+
[[ -f "$path" ]] || return 0
|
|
95
|
+
raw="$(jq -r '.version // empty' "$path" 2>/dev/null || true)" ;;
|
|
96
|
+
toml|cargo)
|
|
97
|
+
[[ -f "$path" ]] || return 0
|
|
98
|
+
raw="$(grep -m1 -E '^version[[:space:]]*=' "$path" 2>/dev/null \
|
|
99
|
+
| sed -E 's/^version[[:space:]]*=[[:space:]]*["'\'']?([^"'\'' ]+).*/\1/' || true)" ;;
|
|
100
|
+
csproj)
|
|
101
|
+
[[ -f "$path" ]] || return 0
|
|
102
|
+
raw="$(grep -m1 -oE '<Version>[^<]+</Version>' "$path" 2>/dev/null \
|
|
103
|
+
| sed -E 's#</?Version>##g' || true)" ;;
|
|
104
|
+
gradle)
|
|
105
|
+
[[ -f "$path" ]] || return 0
|
|
106
|
+
raw="$(grep -m1 -E '^[[:space:]]*version[[:space:]]*[=]?[[:space:]]*["'\'']' "$path" 2>/dev/null \
|
|
107
|
+
| sed -E 's/.*["'\'']([0-9]+\.[0-9]+\.[0-9]+)["'\''].*/\1/' || true)" ;;
|
|
108
|
+
plain)
|
|
109
|
+
[[ -f "$path" ]] || return 0
|
|
110
|
+
raw="$(head -n1 "$path" 2>/dev/null || true)" ;;
|
|
111
|
+
gittag)
|
|
112
|
+
raw="$(git -C "$REPO_ROOT" tag --list 'v[0-9]*' --sort=-v:refname 2>/dev/null | head -n1 || true)" ;;
|
|
113
|
+
*) die "unknown manifest type: $type" ;;
|
|
114
|
+
esac
|
|
115
|
+
[[ -z "$raw" ]] && return 0
|
|
116
|
+
normalize "$raw" 2>/dev/null || return 0
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# ---- per-type write ----------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
write_version() { # $1=type $2=path $3=bare-new-version
|
|
122
|
+
local type="$1" path="$2" new="$3" tmp
|
|
123
|
+
case "$type" in
|
|
124
|
+
json)
|
|
125
|
+
[[ -f "$path" ]] || return 0
|
|
126
|
+
tmp="$(mktemp)"
|
|
127
|
+
jq --indent 2 --arg v "$new" '.version = $v' "$path" >"$tmp" && mv "$tmp" "$path" ;;
|
|
128
|
+
toml|cargo)
|
|
129
|
+
[[ -f "$path" ]] || return 0
|
|
130
|
+
# Replace only the first top-level `version = "..."` line.
|
|
131
|
+
sed -i -E "0,/^version[[:space:]]*=/{s/^(version[[:space:]]*=[[:space:]]*[\"']?)[^\"' ]+([\"']?)/\1$new\2/}" "$path" ;;
|
|
132
|
+
csproj)
|
|
133
|
+
[[ -f "$path" ]] || return 0
|
|
134
|
+
sed -i -E "0,/<Version>[^<]+<\/Version>/{s#<Version>[^<]+</Version>#<Version>$new</Version>#}" "$path" ;;
|
|
135
|
+
gradle)
|
|
136
|
+
[[ -f "$path" ]] || return 0
|
|
137
|
+
sed -i -E "0,/^[[:space:]]*version[[:space:]]*[=]?[[:space:]]*[\"']/{s/([\"'])[0-9]+\.[0-9]+\.[0-9]+([\"'])/\1$new\2/}" "$path" ;;
|
|
138
|
+
plain)
|
|
139
|
+
printf '%s\n' "$new" >"$path" ;;
|
|
140
|
+
gittag)
|
|
141
|
+
if git -C "$REPO_ROOT" rev-parse "v$new" >/dev/null 2>&1; then
|
|
142
|
+
printf 'versioning: git tag v%s already exists, skipping\n' "$new" >&2
|
|
143
|
+
else
|
|
144
|
+
git -C "$REPO_ROOT" tag -a "v$new" -m "v$new"
|
|
145
|
+
printf 'versioning: created git tag v%s\n' "$new" >&2
|
|
146
|
+
fi ;;
|
|
147
|
+
*) die "unknown manifest type: $type" ;;
|
|
148
|
+
esac
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# ---- canonical resolution ----------------------------------------------------
|
|
152
|
+
|
|
153
|
+
canonical() { # highest version across the manifest -> bare X.Y.Z (empty if none)
|
|
154
|
+
local best="" v
|
|
155
|
+
while IFS=$'\t' read -r type path; do
|
|
156
|
+
v="$(read_version "$type" "$path")" || true
|
|
157
|
+
[[ -z "$v" ]] && continue
|
|
158
|
+
if [[ -z "$best" ]] || semver_gt "$v" "$best"; then best="$v"; fi
|
|
159
|
+
done < <(manifest_entries)
|
|
160
|
+
printf '%s' "$best"
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
write_all() { # $1=bare-new-version : write to every manifest file
|
|
164
|
+
local new="$1" type path
|
|
165
|
+
while IFS=$'\t' read -r type path; do
|
|
166
|
+
write_version "$type" "$path" "$new"
|
|
167
|
+
printf ' %-7s %s -> %s\n' "$type" "$(rel "$type" "$path")" "$new" >&2
|
|
168
|
+
done < <(manifest_entries)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# ---- commands ----------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
cmd_current() {
|
|
174
|
+
local v; v="$(canonical)"
|
|
175
|
+
printf 'v%s\n' "${v:-0.0.0}"
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
cmd_bump() {
|
|
179
|
+
local part="${1:-patch}" cur new
|
|
180
|
+
cur="$(canonical)"; [[ -z "$cur" ]] && cur="0.0.0"
|
|
181
|
+
new="$(bump_semver "$cur" "$part")"
|
|
182
|
+
printf 'versioning: %s bump v%s -> v%s\n' "$part" "$cur" "$new" >&2
|
|
183
|
+
write_all "$new"
|
|
184
|
+
printf 'v%s\n' "$new"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
cmd_set() {
|
|
188
|
+
local new; new="$(normalize "${1:?usage: set <X.Y.Z>}")" || die "invalid version: $1"
|
|
189
|
+
printf 'versioning: set -> v%s\n' "$new" >&2
|
|
190
|
+
write_all "$new"
|
|
191
|
+
printf 'v%s\n' "$new"
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
cmd_check() {
|
|
195
|
+
local canon drift=0 v type path; canon="$(canonical)"
|
|
196
|
+
[[ -z "$canon" ]] && { echo "versioning: no version found in any manifest file" >&2; return 0; }
|
|
197
|
+
while IFS=$'\t' read -r type path; do
|
|
198
|
+
v="$(read_version "$type" "$path")" || true
|
|
199
|
+
[[ -z "$v" ]] && continue
|
|
200
|
+
if [[ "$v" != "$canon" ]]; then
|
|
201
|
+
printf 'DRIFT %-7s %s = v%s (canonical v%s)\n' "$type" "$(rel "$type" "$path")" "$v" "$canon" >&2
|
|
202
|
+
drift=1
|
|
203
|
+
fi
|
|
204
|
+
done < <(manifest_entries)
|
|
205
|
+
if ((drift)); then echo "versioning: files out of parity; run 'versioning.sh sync'" >&2; return 1; fi
|
|
206
|
+
printf 'versioning: all files in parity at v%s\n' "$canon"
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
cmd_sync() {
|
|
210
|
+
local canon; canon="$(canonical)"
|
|
211
|
+
[[ -z "$canon" ]] && die "no version found in any manifest file"
|
|
212
|
+
printf 'versioning: syncing all files to v%s\n' "$canon" >&2
|
|
213
|
+
write_all "$canon"
|
|
214
|
+
printf 'v%s\n' "$canon"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
cmd_files() {
|
|
218
|
+
while IFS=$'\t' read -r type path; do
|
|
219
|
+
printf '%s\t%s\n' "$type" "$(rel "$type" "$path")"
|
|
220
|
+
done < <(manifest_entries)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
main() {
|
|
224
|
+
local cmd="${1:-current}"; shift || true
|
|
225
|
+
case "$cmd" in
|
|
226
|
+
current|cur) cmd_current ;;
|
|
227
|
+
bump) cmd_bump "$@" ;;
|
|
228
|
+
set) cmd_set "$@" ;;
|
|
229
|
+
check) cmd_check ;;
|
|
230
|
+
sync) cmd_sync ;;
|
|
231
|
+
files) cmd_files ;;
|
|
232
|
+
*) die "unknown command: $cmd (current|bump|set|check|sync|files)" ;;
|
|
233
|
+
esac
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
main "$@"
|
package/dist/index.js
CHANGED
|
@@ -1447,7 +1447,8 @@ function systemctlUser(args) {
|
|
|
1447
1447
|
};
|
|
1448
1448
|
}
|
|
1449
1449
|
function templateVersioningScript(ctx) {
|
|
1450
|
-
|
|
1450
|
+
const source = join6(ctx.pjanglerRoot, ".mise", "scripts", "versioning.sh");
|
|
1451
|
+
return existsSync5(source) ? readText(source) : void 0;
|
|
1451
1452
|
}
|
|
1452
1453
|
function templateVersionFilesConf(ctx, repoRoot) {
|
|
1453
1454
|
const packageJson = join6(repoRoot, "package.json");
|
|
@@ -1834,6 +1835,9 @@ var RULES = [
|
|
|
1834
1835
|
}
|
|
1835
1836
|
const versioningPath = join6(ctx.repoRoot, ".mise", "scripts", "versioning.sh");
|
|
1836
1837
|
const expectedScript = templateVersioningScript(ctx);
|
|
1838
|
+
if (expectedScript === void 0) {
|
|
1839
|
+
return { id: finding.id, title: finding.title, status: "blocked", summary: "pjangler install is missing .mise/scripts/versioning.sh \u2014 update @delorenj/pjangler (broken package)", changedFiles, details: [] };
|
|
1840
|
+
}
|
|
1837
1841
|
if (safeReadText(versioningPath) !== expectedScript) {
|
|
1838
1842
|
changedFiles.push(versioningPath);
|
|
1839
1843
|
if (!ctx.dryRun) {
|
|
@@ -2328,7 +2332,20 @@ function runMigration(selector, repoArg, dryRun, all) {
|
|
|
2328
2332
|
if (!selected.length) {
|
|
2329
2333
|
throw new Error(`Unknown parity rule: ${selector}`);
|
|
2330
2334
|
}
|
|
2331
|
-
const results = selected.map((rule) =>
|
|
2335
|
+
const results = selected.map((rule) => {
|
|
2336
|
+
try {
|
|
2337
|
+
return rule.migrate(ctx, rule.audit(ctx));
|
|
2338
|
+
} catch (err) {
|
|
2339
|
+
return {
|
|
2340
|
+
id: rule.id,
|
|
2341
|
+
title: rule.title,
|
|
2342
|
+
status: "blocked",
|
|
2343
|
+
summary: `migrate threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
2344
|
+
changedFiles: [],
|
|
2345
|
+
details: []
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
});
|
|
2332
2349
|
const changedFiles = Array.from(new Set(results.flatMap((result) => result.changedFiles))).sort();
|
|
2333
2350
|
return {
|
|
2334
2351
|
repo: ctx.repoRoot,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delorenj/pjangler",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "Project subsystem bootstrapper CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
|
-
"templates"
|
|
14
|
+
"templates",
|
|
15
|
+
".mise/scripts/versioning.sh"
|
|
15
16
|
],
|
|
16
17
|
"engines": {
|
|
17
18
|
"node": ">=20"
|