superpowers-manager 0.1.6 → 0.3.0
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/README.md +184 -64
- package/bin/superpowers-manager.js +9 -237
- package/dist/adapter-result.js +204 -0
- package/dist/adapter.js +649 -0
- package/dist/atomic.js +142 -0
- package/dist/cli-arguments.js +32 -0
- package/dist/cli.js +293 -0
- package/dist/codex-json.js +89 -0
- package/dist/codex-state.js +78 -0
- package/dist/commands/context.js +1 -0
- package/dist/commands/install.js +376 -0
- package/dist/commands/pin.js +79 -0
- package/dist/commands/prepare.js +527 -0
- package/dist/commands/probe.js +392 -0
- package/dist/commands/track-latest.js +44 -0
- package/dist/commands/uninstall.js +308 -0
- package/dist/commands/unpin.js +95 -0
- package/dist/commands/update.js +138 -0
- package/dist/domain/fingerprint.js +4 -0
- package/dist/domain/refs.js +37 -0
- package/dist/effective-selection.js +123 -0
- package/dist/generated-plugin.js +748 -0
- package/dist/git.js +38 -0
- package/dist/hooks.js +290 -0
- package/dist/lifecycle.js +220 -0
- package/dist/manifest-overlay.js +125 -0
- package/dist/provenance.js +111 -0
- package/dist/python-json-format.js +83 -0
- package/dist/python-json.js +29 -0
- package/dist/python-text.js +74 -0
- package/dist/safe-path.js +137 -0
- package/dist/safety-error.js +10 -0
- package/dist/selection-state-cli.js +85 -0
- package/dist/selection-store.js +172 -0
- package/dist/selection.js +200 -0
- package/dist/status.js +24 -0
- package/dist/strict-json.js +248 -0
- package/dist/upstream-cli.js +110 -0
- package/dist/upstream-version.js +40 -0
- package/dist/upstream.js +253 -0
- package/dist/validate-generated-plugin-cli.js +118 -0
- package/dist/validator.js +317 -0
- package/dist/workspace.js +139 -0
- package/package.json +25 -2
- package/scripts/adapters/codex/adapter +0 -598
- package/scripts/adapters/codex/lib.sh +0 -391
- package/scripts/adapters/codex/validate-generated-plugin.py +0 -314
- package/scripts/core/adapter.sh +0 -92
- package/scripts/core/common.sh +0 -35
- package/scripts/core/lifecycle.sh +0 -127
- package/scripts/core/provenance.sh +0 -135
- package/scripts/core/status.sh +0 -27
- package/scripts/core/upstream.sh +0 -146
- package/scripts/core/validate-adapter-response.py +0 -245
- package/scripts/install +0 -60
- package/scripts/prepare +0 -126
- package/scripts/probe +0 -61
- package/scripts/uninstall +0 -44
- package/scripts/update +0 -31
package/scripts/core/adapter.sh
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Sourced module; callers own set -eu.
|
|
3
|
-
|
|
4
|
-
spw_adapter_package_root() {
|
|
5
|
-
if [ -n "${SPW_PACKAGE_ROOT:-}" ]; then
|
|
6
|
-
printf '%s\n' "$SPW_PACKAGE_ROOT"
|
|
7
|
-
elif [ -n "${root:-}" ]; then
|
|
8
|
-
printf '%s\n' "$root"
|
|
9
|
-
else
|
|
10
|
-
spw_root
|
|
11
|
-
fi
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
SPW_ADAPTER_ROOT=$(spw_adapter_package_root)
|
|
15
|
-
SPW_ADAPTER="${SPW_ADAPTER:-$SPW_ADAPTER_ROOT/scripts/adapters/codex/adapter}"
|
|
16
|
-
SPW_ADAPTER_RESPONSE_VALIDATOR="${SPW_ADAPTER_RESPONSE_VALIDATOR:-$SPW_ADAPTER_ROOT/scripts/core/validate-adapter-response.py}"
|
|
17
|
-
|
|
18
|
-
spw_invoke_adapter() {
|
|
19
|
-
operation="$1"
|
|
20
|
-
result_file="$2"
|
|
21
|
-
inspect_view="$3"
|
|
22
|
-
shift 3
|
|
23
|
-
[ "${1:-}" = "--" ] || spw_die "internal adapter invocation missing --"
|
|
24
|
-
shift
|
|
25
|
-
|
|
26
|
-
response_file="${result_file}.response"
|
|
27
|
-
adapter_exit=0
|
|
28
|
-
"$SPW_ADAPTER" "$operation" "$@" > "$response_file" || adapter_exit=$?
|
|
29
|
-
|
|
30
|
-
args=""
|
|
31
|
-
if [ -n "$inspect_view" ]; then
|
|
32
|
-
args="$inspect_view"
|
|
33
|
-
fi
|
|
34
|
-
if [ -n "$args" ]; then
|
|
35
|
-
python3 -S "$SPW_ADAPTER_RESPONSE_VALIDATOR" \
|
|
36
|
-
--operation "$operation" --adapter-exit "$adapter_exit" \
|
|
37
|
-
--response "$response_file" --result "$result_file" \
|
|
38
|
-
--inspect-view "$inspect_view"
|
|
39
|
-
else
|
|
40
|
-
python3 -S "$SPW_ADAPTER_RESPONSE_VALIDATOR" \
|
|
41
|
-
--operation "$operation" --adapter-exit "$adapter_exit" \
|
|
42
|
-
--response "$response_file" --result "$result_file"
|
|
43
|
-
fi
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
spw_adapter_result_get() {
|
|
47
|
-
result_file="$1"
|
|
48
|
-
dotted_key="$2"
|
|
49
|
-
spw_json_get "$result_file" "$dotted_key"
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
spw_adapter_result_boolean() {
|
|
53
|
-
result_file="$1"
|
|
54
|
-
dotted_key="$2"
|
|
55
|
-
value=$(spw_adapter_result_get "$result_file" "$dotted_key")
|
|
56
|
-
case "$value" in
|
|
57
|
-
true|True)
|
|
58
|
-
printf 'true\n'
|
|
59
|
-
;;
|
|
60
|
-
false|False)
|
|
61
|
-
printf 'false\n'
|
|
62
|
-
;;
|
|
63
|
-
*)
|
|
64
|
-
spw_die "expected Boolean adapter result at $dotted_key in $result_file"
|
|
65
|
-
;;
|
|
66
|
-
esac
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
spw_inspect_fingerprint() {
|
|
70
|
-
result_file="$1"
|
|
71
|
-
spw_invoke_adapter inspect "$result_file" fingerprint -- --view fingerprint
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
spw_inspect_ownership() {
|
|
75
|
-
result_file="$1"
|
|
76
|
-
spw_invoke_adapter inspect "$result_file" ownership -- --view ownership
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
spw_adapter_install() {
|
|
80
|
-
result_file="$1"
|
|
81
|
-
package_root="$2"
|
|
82
|
-
spw_invoke_adapter install "$result_file" "" -- --package-root "$package_root"
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
spw_adapter_uninstall() {
|
|
86
|
-
result_file="$1"
|
|
87
|
-
plugin_present="$2"
|
|
88
|
-
marketplace_present="$3"
|
|
89
|
-
spw_invoke_adapter uninstall "$result_file" "" -- \
|
|
90
|
-
--plugin-present "$plugin_present" \
|
|
91
|
-
--marketplace-present "$marketplace_present"
|
|
92
|
-
}
|
package/scripts/core/common.sh
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Sourced module; callers own set -eu.
|
|
3
|
-
|
|
4
|
-
spw_die() {
|
|
5
|
-
echo "error: $*" >&2
|
|
6
|
-
exit 1
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
spw_require_command() {
|
|
10
|
-
command_name="$1"
|
|
11
|
-
if ! command -v "$command_name" >/dev/null 2>&1; then
|
|
12
|
-
spw_die "required command not found: $command_name"
|
|
13
|
-
fi
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
spw_root() {
|
|
17
|
-
CDPATH= cd -- "$(dirname "$0")/.." && pwd
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
spw_copy_path_if_present() {
|
|
21
|
-
src="$1"
|
|
22
|
-
dst="$2"
|
|
23
|
-
if [ -e "$src" ]; then
|
|
24
|
-
rm -rf "$dst"
|
|
25
|
-
cp -R "$src" "$dst"
|
|
26
|
-
fi
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
spw_require_upstream_path() {
|
|
30
|
-
path="$1"
|
|
31
|
-
label="$2"
|
|
32
|
-
if [ ! -e "$path" ]; then
|
|
33
|
-
spw_die "required upstream path missing: $label"
|
|
34
|
-
fi
|
|
35
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Sourced module; callers own set -eu.
|
|
3
|
-
|
|
4
|
-
# Replace the live tree with a single atomic rename, keeping a backup so a
|
|
5
|
-
# failed swap restores the previous working generated tree. candidate and
|
|
6
|
-
# live_root must be same-filesystem siblings for atomic renames.
|
|
7
|
-
spw_replace_generated_tree() {
|
|
8
|
-
candidate="$1"
|
|
9
|
-
live_root="$2"
|
|
10
|
-
parent=$(dirname "$live_root")
|
|
11
|
-
backup="$parent/.superpowers.bak.$$"
|
|
12
|
-
|
|
13
|
-
rm -rf "$backup"
|
|
14
|
-
if [ -e "$live_root" ]; then
|
|
15
|
-
mv "$live_root" "$backup"
|
|
16
|
-
fi
|
|
17
|
-
if mv "$candidate" "$live_root"; then
|
|
18
|
-
rm -rf "$backup"
|
|
19
|
-
else
|
|
20
|
-
if [ -e "$backup" ]; then
|
|
21
|
-
mv "$backup" "$live_root"
|
|
22
|
-
fi
|
|
23
|
-
rm -rf "$candidate"
|
|
24
|
-
spw_die "failed to install generated tree into $live_root; previous tree restored"
|
|
25
|
-
fi
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
spw_generated_metadata_path() (
|
|
29
|
-
generated_root="$1"
|
|
30
|
-
printf '%s\n' "$generated_root/plugins/superpowers/.superpowers-upstream.json"
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
spw_generated_commit_or_empty() (
|
|
34
|
-
generated_root="$1"
|
|
35
|
-
generated_metadata=$(spw_generated_metadata_path "$generated_root")
|
|
36
|
-
spw_metadata_commit_lenient_or_empty "$generated_metadata"
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
spw_require_no_legacy_state() {
|
|
40
|
-
identity_state="$1"
|
|
41
|
-
case "$identity_state" in
|
|
42
|
-
neither|manager)
|
|
43
|
-
return 0
|
|
44
|
-
;;
|
|
45
|
-
legacy|both)
|
|
46
|
-
printf '%s\n' \
|
|
47
|
-
'Legacy superpowers-wrapper Codex state is installed.' \
|
|
48
|
-
'Run: npx superpowers-wrapper@0.1.1 uninstall' \
|
|
49
|
-
'Then run: npx superpowers-manager install' >&2
|
|
50
|
-
return 1
|
|
51
|
-
;;
|
|
52
|
-
*)
|
|
53
|
-
spw_die "unknown adapter identity state: $identity_state"
|
|
54
|
-
;;
|
|
55
|
-
esac
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
spw_report_legacy_state() {
|
|
59
|
-
case "$1" in
|
|
60
|
-
legacy|both)
|
|
61
|
-
printf '%s\n' \
|
|
62
|
-
'Legacy superpowers-wrapper Codex state remains installed.' \
|
|
63
|
-
'Run: npx superpowers-wrapper@0.1.1 uninstall'
|
|
64
|
-
;;
|
|
65
|
-
neither|manager)
|
|
66
|
-
;;
|
|
67
|
-
*)
|
|
68
|
-
spw_die "unknown adapter identity state: $1"
|
|
69
|
-
;;
|
|
70
|
-
esac
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
spw_verify_installed_fingerprint() {
|
|
74
|
-
desired_commit="$1"
|
|
75
|
-
install_result="$2"
|
|
76
|
-
inspect_result="$3"
|
|
77
|
-
if ! spw_inspect_fingerprint "$inspect_result"; then
|
|
78
|
-
echo "error: installed manager fingerprint inspection failed after install." >&2
|
|
79
|
-
return 1
|
|
80
|
-
fi
|
|
81
|
-
if ! installed_commit=$(spw_adapter_result_get "$inspect_result" "fingerprint"); then
|
|
82
|
-
echo "error: cannot parse installed manager fingerprint inspection result after install." >&2
|
|
83
|
-
return 1
|
|
84
|
-
fi
|
|
85
|
-
printf 'desired_commit=%s\n' "$desired_commit"
|
|
86
|
-
printf 'installed_commit=%s\n' "$installed_commit"
|
|
87
|
-
if [ -n "$installed_commit" ] && spw_commit_matches "$desired_commit" "$installed_commit"; then
|
|
88
|
-
echo "manager updated"
|
|
89
|
-
return 0
|
|
90
|
-
fi
|
|
91
|
-
|
|
92
|
-
hint=""
|
|
93
|
-
if [ -f "$install_result" ]; then
|
|
94
|
-
if [ -n "$installed_commit" ]; then
|
|
95
|
-
hint=$(spw_adapter_result_get "$install_result" "verification_hints.mismatch" || true)
|
|
96
|
-
else
|
|
97
|
-
hint=$(spw_adapter_result_get "$install_result" "verification_hints.missing" || true)
|
|
98
|
-
fi
|
|
99
|
-
fi
|
|
100
|
-
|
|
101
|
-
if [ -n "$installed_commit" ]; then
|
|
102
|
-
echo "error: installed manager fingerprint does not match the prepared plugin after install." >&2
|
|
103
|
-
else
|
|
104
|
-
echo "error: installed manager fingerprint is not detectable after install." >&2
|
|
105
|
-
fi
|
|
106
|
-
if [ -n "$hint" ]; then
|
|
107
|
-
echo "hint: $hint" >&2
|
|
108
|
-
fi
|
|
109
|
-
return 1
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
spw_verify_uninstalled_resources() {
|
|
113
|
-
inspect_result="$1"
|
|
114
|
-
|
|
115
|
-
if ! plugin_present=$(spw_adapter_result_boolean "$inspect_result" "resources.plugin"); then
|
|
116
|
-
return 1
|
|
117
|
-
fi
|
|
118
|
-
if ! marketplace_present=$(spw_adapter_result_boolean "$inspect_result" "resources.marketplace"); then
|
|
119
|
-
return 1
|
|
120
|
-
fi
|
|
121
|
-
if [ "$plugin_present" = true ]; then
|
|
122
|
-
spw_die "owned plugin resource is still installed after removal"
|
|
123
|
-
fi
|
|
124
|
-
if [ "$marketplace_present" = true ]; then
|
|
125
|
-
spw_die "owned marketplace resource is still registered after removal"
|
|
126
|
-
fi
|
|
127
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Sourced module; callers own set -eu.
|
|
3
|
-
|
|
4
|
-
spw_json_get() {
|
|
5
|
-
file="$1"
|
|
6
|
-
key="$2"
|
|
7
|
-
if ! value=$(
|
|
8
|
-
python3 - "$file" "$key" 2>&1 <<'PY'
|
|
9
|
-
import json
|
|
10
|
-
import sys
|
|
11
|
-
|
|
12
|
-
path, dotted_key = sys.argv[1:]
|
|
13
|
-
MAX_JSON_NESTING = 256
|
|
14
|
-
|
|
15
|
-
def reject_constant(constant):
|
|
16
|
-
raise ValueError(f"non-standard numeric constant: {constant}")
|
|
17
|
-
|
|
18
|
-
def nesting_exceeds_limit(value):
|
|
19
|
-
stack = [(value, 0)]
|
|
20
|
-
while stack:
|
|
21
|
-
current, depth = stack.pop()
|
|
22
|
-
if isinstance(current, dict):
|
|
23
|
-
next_depth = depth + 1
|
|
24
|
-
if next_depth > MAX_JSON_NESTING:
|
|
25
|
-
return True
|
|
26
|
-
stack.extend((child, next_depth) for child in current.values())
|
|
27
|
-
elif isinstance(current, list):
|
|
28
|
-
next_depth = depth + 1
|
|
29
|
-
if next_depth > MAX_JSON_NESTING:
|
|
30
|
-
return True
|
|
31
|
-
stack.extend((child, next_depth) for child in current)
|
|
32
|
-
return False
|
|
33
|
-
|
|
34
|
-
try:
|
|
35
|
-
with open(path, "r", encoding="utf-8") as f:
|
|
36
|
-
data = json.load(f, parse_constant=reject_constant)
|
|
37
|
-
except RecursionError:
|
|
38
|
-
sys.exit(f"JSON nesting exceeds limit in {path}")
|
|
39
|
-
except json.JSONDecodeError as exc:
|
|
40
|
-
sys.exit(
|
|
41
|
-
f"invalid JSON in {path}: "
|
|
42
|
-
f"line {exc.lineno} column {exc.colno}: {exc.msg}"
|
|
43
|
-
)
|
|
44
|
-
except (OSError, UnicodeError) as exc:
|
|
45
|
-
sys.exit(f"cannot read JSON in {path}: {exc}")
|
|
46
|
-
except ValueError as exc:
|
|
47
|
-
sys.exit(f"invalid JSON in {path}: {exc}")
|
|
48
|
-
|
|
49
|
-
if nesting_exceeds_limit(data):
|
|
50
|
-
sys.exit(f"JSON nesting exceeds limit in {path}")
|
|
51
|
-
|
|
52
|
-
if not isinstance(data, dict):
|
|
53
|
-
sys.exit(f"JSON value must be an object in {path}")
|
|
54
|
-
|
|
55
|
-
value = data
|
|
56
|
-
for part in dotted_key.split("."):
|
|
57
|
-
if not isinstance(value, dict):
|
|
58
|
-
value = ""
|
|
59
|
-
break
|
|
60
|
-
value = value.get(part, "")
|
|
61
|
-
try:
|
|
62
|
-
print(value if value is not None else "")
|
|
63
|
-
except (OSError, UnicodeError) as exc:
|
|
64
|
-
sys.exit(f"cannot output JSON value from {path}: {exc}")
|
|
65
|
-
PY
|
|
66
|
-
); then
|
|
67
|
-
spw_die "$value"
|
|
68
|
-
fi
|
|
69
|
-
printf '%s\n' "$value"
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
spw_write_metadata_json() {
|
|
73
|
-
file="$1"
|
|
74
|
-
source="$2"
|
|
75
|
-
requested_ref="$3"
|
|
76
|
-
resolved_ref="$4"
|
|
77
|
-
commit="$5"
|
|
78
|
-
upstream_manifest_version="$6"
|
|
79
|
-
python3 - "$file" "$source" "$requested_ref" "$resolved_ref" "$commit" "$upstream_manifest_version" <<'PY'
|
|
80
|
-
import json, sys
|
|
81
|
-
path, source, requested, resolved, commit, version = sys.argv[1:]
|
|
82
|
-
data = {
|
|
83
|
-
"source": source,
|
|
84
|
-
"requested_ref": requested,
|
|
85
|
-
"resolved_ref": resolved,
|
|
86
|
-
"commit": commit,
|
|
87
|
-
"upstream_manifest_version": version,
|
|
88
|
-
}
|
|
89
|
-
try:
|
|
90
|
-
with open(path, "w", encoding="utf-8") as f:
|
|
91
|
-
json.dump(data, f, indent=2, allow_nan=False)
|
|
92
|
-
f.write("\n")
|
|
93
|
-
except RecursionError as exc:
|
|
94
|
-
sys.exit(f"JSON nesting exceeds limit while writing {path}: {exc}")
|
|
95
|
-
except (OSError, UnicodeError, ValueError) as exc:
|
|
96
|
-
sys.exit(f"cannot write JSON to {path}: {exc}")
|
|
97
|
-
PY
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
spw_metadata_commit_or_empty() {
|
|
101
|
-
file="$1"
|
|
102
|
-
if [ -f "$file" ]; then
|
|
103
|
-
spw_json_get "$file" "commit"
|
|
104
|
-
fi
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
spw_metadata_commit_lenient_or_empty() {
|
|
108
|
-
file="$1"
|
|
109
|
-
[ -f "$file" ] || return 0
|
|
110
|
-
|
|
111
|
-
if value=$(
|
|
112
|
-
python3 -S - "$file" 2>/dev/null <<'PY'
|
|
113
|
-
import json
|
|
114
|
-
import re
|
|
115
|
-
import sys
|
|
116
|
-
|
|
117
|
-
path = sys.argv[1]
|
|
118
|
-
|
|
119
|
-
def reject_constant(constant):
|
|
120
|
-
raise ValueError(f"non-standard numeric constant: {constant}")
|
|
121
|
-
|
|
122
|
-
try:
|
|
123
|
-
with open(path, "r", encoding="utf-8") as handle:
|
|
124
|
-
data = json.load(handle, parse_constant=reject_constant)
|
|
125
|
-
except (OSError, UnicodeError, ValueError, RecursionError):
|
|
126
|
-
raise SystemExit(1)
|
|
127
|
-
|
|
128
|
-
commit = data.get("commit") if isinstance(data, dict) else None
|
|
129
|
-
if isinstance(commit, str) and re.fullmatch(r"[0-9a-fA-F]{40}", commit):
|
|
130
|
-
print(commit)
|
|
131
|
-
PY
|
|
132
|
-
); then
|
|
133
|
-
printf '%s\n' "$value"
|
|
134
|
-
fi
|
|
135
|
-
}
|
package/scripts/core/status.sh
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Sourced module; callers own set -eu.
|
|
3
|
-
|
|
4
|
-
spw_commit_matches() {
|
|
5
|
-
desired="$1"
|
|
6
|
-
observed="$2"
|
|
7
|
-
short=$(printf '%s' "$desired" | cut -c 1-7)
|
|
8
|
-
[ -n "$observed" ] && { [ "$observed" = "$desired" ] || [ "$observed" = "$short" ]; }
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
spw_status_for_commits() {
|
|
12
|
-
desired="$1"
|
|
13
|
-
generated="$2"
|
|
14
|
-
installed="$3"
|
|
15
|
-
|
|
16
|
-
if [ -z "$generated" ]; then
|
|
17
|
-
printf '%s\n' "needs prepare"
|
|
18
|
-
elif ! spw_commit_matches "$desired" "$generated"; then
|
|
19
|
-
printf '%s\n' "needs prepare"
|
|
20
|
-
elif [ -z "$installed" ]; then
|
|
21
|
-
printf '%s\n' "needs install"
|
|
22
|
-
elif ! spw_commit_matches "$desired" "$installed"; then
|
|
23
|
-
printf '%s\n' "needs install"
|
|
24
|
-
else
|
|
25
|
-
printf '%s\n' "current"
|
|
26
|
-
fi
|
|
27
|
-
}
|
package/scripts/core/upstream.sh
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Sourced module; callers own set -eu.
|
|
3
|
-
|
|
4
|
-
SPW_UPSTREAM_URL_DEFAULT="https://github.com/obra/superpowers"
|
|
5
|
-
|
|
6
|
-
spw_config_ref() (
|
|
7
|
-
config_root="$1"
|
|
8
|
-
if [ -n "${SUPERPOWERS_REF:-}" ]; then
|
|
9
|
-
printf '%s\n' "$SUPERPOWERS_REF"
|
|
10
|
-
return
|
|
11
|
-
fi
|
|
12
|
-
sed -n '1{s/[[:space:]]*$//;p;}' "$config_root/config/upstream-ref"
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
spw_short_commit() {
|
|
16
|
-
commit="$1"
|
|
17
|
-
printf '%s' "$commit" | cut -c 1-7
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
spw_is_semver_base() {
|
|
21
|
-
version="$1"
|
|
22
|
-
printf '%s' "$version" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?$'
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
spw_sanitize_ref_for_version() {
|
|
26
|
-
ref="$1"
|
|
27
|
-
sanitized=$(printf '%s' "$ref" | sed 's/[^0-9A-Za-z-][^0-9A-Za-z-]*/-/g; s/^-*//; s/-*$//')
|
|
28
|
-
sanitized=$(printf '%s' "$sanitized" | cut -c 1-48 | sed 's/^-*//; s/-*$//')
|
|
29
|
-
if [ -z "$sanitized" ]; then
|
|
30
|
-
sanitized="unknown"
|
|
31
|
-
fi
|
|
32
|
-
printf '%s' "$sanitized"
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
spw_manifest_version_for_ref() {
|
|
36
|
-
requested_ref="$1"
|
|
37
|
-
resolution_kind="$2"
|
|
38
|
-
resolved_ref="$3"
|
|
39
|
-
commit="$4"
|
|
40
|
-
short=$(spw_short_commit "$commit")
|
|
41
|
-
|
|
42
|
-
case "$resolution_kind" in
|
|
43
|
-
latest-release|tag)
|
|
44
|
-
base=$(printf '%s' "$resolved_ref" | sed -n 's/^v//p')
|
|
45
|
-
if [ -n "$base" ] && spw_is_semver_base "$base"; then
|
|
46
|
-
printf '%s+manager.%s\n' "$base" "$short"
|
|
47
|
-
return
|
|
48
|
-
fi
|
|
49
|
-
;;
|
|
50
|
-
ref)
|
|
51
|
-
if [ "$requested_ref" = "main" ]; then
|
|
52
|
-
printf '0.0.0-main+manager.%s\n' "$short"
|
|
53
|
-
return
|
|
54
|
-
fi
|
|
55
|
-
sanitized=$(spw_sanitize_ref_for_version "$requested_ref")
|
|
56
|
-
printf '0.0.0-ref-%s+manager.%s\n' "$sanitized" "$short"
|
|
57
|
-
return
|
|
58
|
-
;;
|
|
59
|
-
raw-commit)
|
|
60
|
-
;;
|
|
61
|
-
esac
|
|
62
|
-
|
|
63
|
-
printf '0.0.0+manager.%s\n' "$short"
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
spw_manifest_version_for_commit() {
|
|
67
|
-
commit="$1"
|
|
68
|
-
spw_manifest_version_for_ref "$commit" "raw-commit" "$commit" "$commit"
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
spw_select_latest_release_from_ls_remote() {
|
|
72
|
-
awk '
|
|
73
|
-
$2 ~ /^refs\/tags\/v[0-9]+\.[0-9]+\.[0-9]+(\^\{\})?$/ {
|
|
74
|
-
ref = $2
|
|
75
|
-
peeled = 0
|
|
76
|
-
if (ref ~ /\^\{\}$/) {
|
|
77
|
-
peeled = 1
|
|
78
|
-
sub(/\^\{\}$/, "", ref)
|
|
79
|
-
}
|
|
80
|
-
tag = ref
|
|
81
|
-
sub(/^refs\/tags\//, "", tag)
|
|
82
|
-
split(substr(tag, 2), parts, ".")
|
|
83
|
-
key = sprintf("%010d.%010d.%010d", parts[1], parts[2], parts[3])
|
|
84
|
-
tag_by_key[key] = tag
|
|
85
|
-
if (peeled || !(tag in sha_by_tag)) {
|
|
86
|
-
sha_by_tag[tag] = $1
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
END {
|
|
90
|
-
for (key in tag_by_key) {
|
|
91
|
-
tag = tag_by_key[key]
|
|
92
|
-
print key, tag, sha_by_tag[tag]
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
' | sort | tail -n 1 | awk '
|
|
96
|
-
NF == 3 { print $2 " " $3; found = 1 }
|
|
97
|
-
END { if (!found) exit 1 }
|
|
98
|
-
'
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
spw_resolve_ref() {
|
|
102
|
-
upstream_url="$1"
|
|
103
|
-
requested_ref="$2"
|
|
104
|
-
spw_require_command git
|
|
105
|
-
|
|
106
|
-
if [ "$requested_ref" = "latest-release" ]; then
|
|
107
|
-
if ! output=$(git ls-remote --tags "$upstream_url" 'refs/tags/v*' 2>&1); then
|
|
108
|
-
spw_die "cannot query upstream tags from $upstream_url: $output"
|
|
109
|
-
fi
|
|
110
|
-
if ! selected=$(printf '%s\n' "$output" | spw_select_latest_release_from_ls_remote); then
|
|
111
|
-
spw_die "no stable semver tag found for latest-release"
|
|
112
|
-
fi
|
|
113
|
-
printf 'latest-release %s\n' "$selected"
|
|
114
|
-
return
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
if printf '%s' "$requested_ref" | grep -Eq '^[0-9a-fA-F]{40}$'; then
|
|
118
|
-
printf 'raw-commit %s %s\n' "$requested_ref" "$requested_ref"
|
|
119
|
-
return
|
|
120
|
-
fi
|
|
121
|
-
|
|
122
|
-
if ! tag_output=$(git ls-remote --tags "$upstream_url" "refs/tags/$requested_ref" "refs/tags/$requested_ref^{}" 2>&1); then
|
|
123
|
-
spw_die "cannot query upstream tag $requested_ref from $upstream_url: $tag_output"
|
|
124
|
-
fi
|
|
125
|
-
resolved=$(printf '%s\n' "$tag_output" | awk -v tag_ref="refs/tags/$requested_ref" '
|
|
126
|
-
NF >= 2 && ($2 == tag_ref || $2 == tag_ref "^{}") {
|
|
127
|
-
sha = $1
|
|
128
|
-
}
|
|
129
|
-
END { if (sha != "") print sha }
|
|
130
|
-
')
|
|
131
|
-
if [ -n "$resolved" ]; then
|
|
132
|
-
printf 'tag %s %s\n' "$requested_ref" "$resolved"
|
|
133
|
-
return
|
|
134
|
-
fi
|
|
135
|
-
|
|
136
|
-
if ! ref_output=$(git ls-remote "$upstream_url" "$requested_ref" 2>&1); then
|
|
137
|
-
spw_die "cannot query upstream ref $requested_ref from $upstream_url: $ref_output"
|
|
138
|
-
fi
|
|
139
|
-
resolved=$(printf '%s\n' "$ref_output" | awk 'NF >= 2 { print $1; exit }')
|
|
140
|
-
if [ -n "$resolved" ]; then
|
|
141
|
-
printf 'ref %s %s\n' "$requested_ref" "$resolved"
|
|
142
|
-
return
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
spw_die "cannot resolve upstream ref: $requested_ref"
|
|
146
|
-
}
|