rlsbl 0.8.0 → 0.8.2
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/package.json +1 -1
- package/rlsbl/__init__.py +8 -2
- package/rlsbl/commands/config.py +3 -3
- package/rlsbl/commands/discover.py +4 -2
- package/rlsbl/commands/init_cmd.py +1 -1
- package/rlsbl/commands/pre_push_check.py +8 -39
- package/rlsbl/commands/record_gif.py +10 -4
- package/rlsbl/commands/release.py +12 -0
- package/rlsbl/commands/undo.py +11 -3
- package/rlsbl/commands/watch.py +81 -74
- package/rlsbl/config.py +8 -5
- package/rlsbl/registries/go.py +2 -2
- package/rlsbl/registries/npm.py +3 -3
- package/rlsbl/registries/pypi.py +2 -2
- package/rlsbl/tagging.py +8 -3
- package/{templates → rlsbl/templates}/shared/hooks/pre-release.sh.tpl +9 -5
- /package/{templates → rlsbl/templates}/go/VERSION.tpl +0 -0
- /package/{templates → rlsbl/templates}/go/ci.yml.tpl +0 -0
- /package/{templates → rlsbl/templates}/go/goreleaser.yml.tpl +0 -0
- /package/{templates → rlsbl/templates}/go/publish.yml.tpl +0 -0
- /package/{templates → rlsbl/templates}/merged/publish.yml.tpl +0 -0
- /package/{templates → rlsbl/templates}/npm/ci.yml.tpl +0 -0
- /package/{templates → rlsbl/templates}/npm/publish.yml.tpl +0 -0
- /package/{templates → rlsbl/templates}/pypi/ci.yml.tpl +0 -0
- /package/{templates → rlsbl/templates}/pypi/publish.yml.tpl +0 -0
- /package/{templates → rlsbl/templates}/shared/CHANGELOG.md.tpl +0 -0
- /package/{templates → rlsbl/templates}/shared/CLAUDE.md.tpl +0 -0
- /package/{templates → rlsbl/templates}/shared/LICENSE.tpl +0 -0
- /package/{templates → rlsbl/templates}/shared/claude-settings.json.tpl +0 -0
- /package/{templates → rlsbl/templates}/shared/gitignore.tpl +0 -0
- /package/{templates → rlsbl/templates}/shared/hooks/post-release.sh.tpl +0 -0
package/package.json
CHANGED
package/rlsbl/__init__.py
CHANGED
|
@@ -56,7 +56,8 @@ Usage:
|
|
|
56
56
|
rlsbl discover [--mine] List rlsbl ecosystem projects
|
|
57
57
|
rlsbl watch [<commit-sha>] Watch CI runs for a commit
|
|
58
58
|
rlsbl pre-push-check Verify CHANGELOG entry for current version
|
|
59
|
-
rlsbl record-gif
|
|
59
|
+
rlsbl record-gif [--width N] [--height N] [--font-size N] [--duration N]
|
|
60
|
+
Record a demo GIF with vhs
|
|
60
61
|
|
|
61
62
|
Options:
|
|
62
63
|
--registry <npm|pypi|go> Target a specific registry (auto-detected if omitted)
|
|
@@ -86,7 +87,7 @@ def parse_args(argv):
|
|
|
86
87
|
Flags listed in VALUE_FLAGS consume the next token as their value
|
|
87
88
|
(e.g. --registry npm). All other --flags are boolean.
|
|
88
89
|
"""
|
|
89
|
-
VALUE_FLAGS = ("registry",)
|
|
90
|
+
VALUE_FLAGS = ("registry", "width", "height", "font-size", "duration")
|
|
90
91
|
raw = argv[1:]
|
|
91
92
|
positional = []
|
|
92
93
|
flags = {}
|
|
@@ -166,6 +167,11 @@ def main():
|
|
|
166
167
|
args = positional[1:]
|
|
167
168
|
registry = flags.get("registry")
|
|
168
169
|
|
|
170
|
+
# --registry was the last arg with no value following it
|
|
171
|
+
if registry is True:
|
|
172
|
+
print("Error: --registry requires a value (npm, pypi, or go).", file=sys.stderr)
|
|
173
|
+
sys.exit(1)
|
|
174
|
+
|
|
169
175
|
# Validate --registry if provided
|
|
170
176
|
if registry and registry not in REGISTRIES:
|
|
171
177
|
print(
|
package/rlsbl/commands/config.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Config command: show resolved project configuration."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
-
from ..config import
|
|
4
|
+
from ..config import _project_config, USER_CONFIG, read_json_config, should_tag
|
|
5
5
|
from ..registries import REGISTRIES
|
|
6
6
|
|
|
7
7
|
|
|
@@ -50,12 +50,12 @@ def run_cmd(registry, args, flags):
|
|
|
50
50
|
print("\nEcosystem tagging:")
|
|
51
51
|
enabled = should_tag(flags)
|
|
52
52
|
# Determine why it's enabled/disabled
|
|
53
|
-
project_cfg = read_json_config(
|
|
53
|
+
project_cfg = read_json_config(_project_config())
|
|
54
54
|
user_cfg = read_json_config(USER_CONFIG)
|
|
55
55
|
if flags.get("no-tag"):
|
|
56
56
|
source = "CLI flag"
|
|
57
57
|
elif "tag" in project_cfg:
|
|
58
|
-
source = f"project config ({
|
|
58
|
+
source = f"project config ({_project_config()})"
|
|
59
59
|
elif "tag" in user_cfg:
|
|
60
60
|
source = f"user config ({USER_CONFIG})"
|
|
61
61
|
else:
|
|
@@ -47,10 +47,12 @@ def _parse_next_link(headers):
|
|
|
47
47
|
return None
|
|
48
48
|
for part in link.split(","):
|
|
49
49
|
if 'rel="next"' in part:
|
|
50
|
-
# Extract URL between < and >
|
|
51
50
|
start = part.index("<") + 1
|
|
52
51
|
end = part.index(">")
|
|
53
|
-
|
|
52
|
+
url = part[start:end]
|
|
53
|
+
if not url.startswith("https://api.github.com/"):
|
|
54
|
+
return None
|
|
55
|
+
return url
|
|
54
56
|
return None
|
|
55
57
|
|
|
56
58
|
|
|
@@ -480,7 +480,7 @@ def run_cmd_multi(registries_list, args, flags):
|
|
|
480
480
|
|
|
481
481
|
# Process merged publish workflow template
|
|
482
482
|
merged_tpl_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)),
|
|
483
|
-
"
|
|
483
|
+
"templates", "merged")
|
|
484
484
|
merged_created, merged_skipped, merged_warnings, merged_hashes = process_mappings(
|
|
485
485
|
merged_tpl_dir,
|
|
486
486
|
[{"template": "publish.yml.tpl", "target": ".github/workflows/publish.yml"}],
|
|
@@ -1,52 +1,21 @@
|
|
|
1
1
|
"""Pre-push-check command: verify CHANGELOG.md has an entry for the current version."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import os
|
|
5
4
|
import re
|
|
6
5
|
import sys
|
|
7
6
|
|
|
7
|
+
from ..registries import REGISTRIES
|
|
8
|
+
|
|
8
9
|
|
|
9
10
|
def _detect_version():
|
|
10
|
-
"""Detect
|
|
11
|
+
"""Detect version using registry adapters.
|
|
11
12
|
|
|
12
|
-
Returns (version_string,
|
|
13
|
+
Returns (version_string, registry_name) or (None, None) if undetectable.
|
|
13
14
|
"""
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
with open(version_path, "r", encoding="utf-8") as f:
|
|
19
|
-
version = f.read().strip()
|
|
20
|
-
if version:
|
|
21
|
-
return version, "go"
|
|
22
|
-
return None, None
|
|
23
|
-
|
|
24
|
-
if os.path.exists("package.json"):
|
|
25
|
-
try:
|
|
26
|
-
with open("package.json", "r", encoding="utf-8") as f:
|
|
27
|
-
data = json.load(f)
|
|
28
|
-
version = data.get("version", "")
|
|
29
|
-
if version:
|
|
30
|
-
return version, "npm"
|
|
31
|
-
except Exception:
|
|
32
|
-
pass
|
|
33
|
-
return None, None
|
|
34
|
-
|
|
35
|
-
if os.path.exists("pyproject.toml"):
|
|
36
|
-
try:
|
|
37
|
-
try:
|
|
38
|
-
import tomllib
|
|
39
|
-
except ModuleNotFoundError:
|
|
40
|
-
import tomli as tomllib # type: ignore[no-redef]
|
|
41
|
-
with open("pyproject.toml", "rb") as f:
|
|
42
|
-
data = tomllib.load(f)
|
|
43
|
-
version = data.get("project", {}).get("version", "")
|
|
44
|
-
if version:
|
|
45
|
-
return version, "pypi"
|
|
46
|
-
except Exception:
|
|
47
|
-
pass
|
|
48
|
-
return None, None
|
|
49
|
-
|
|
15
|
+
for name in ("go", "npm", "pypi"):
|
|
16
|
+
reg = REGISTRIES[name]
|
|
17
|
+
if reg.check_project_exists("."):
|
|
18
|
+
return reg.read_version("."), name
|
|
50
19
|
return None, None
|
|
51
20
|
|
|
52
21
|
|
|
@@ -43,19 +43,25 @@ def run_cmd(registry, args, flags):
|
|
|
43
43
|
print("Ensure package.json, pyproject.toml, or go.mod exists with a CLI entry point.", file=sys.stderr)
|
|
44
44
|
sys.exit(1)
|
|
45
45
|
|
|
46
|
+
# Parse configurable VHS parameters from flags
|
|
47
|
+
width = int(flags.get("width", 1200))
|
|
48
|
+
height = int(flags.get("height", 600))
|
|
49
|
+
font_size = int(flags.get("font-size", 24))
|
|
50
|
+
duration = int(flags.get("duration", 10))
|
|
51
|
+
|
|
46
52
|
assets_dir = "assets"
|
|
47
53
|
os.makedirs(assets_dir, exist_ok=True)
|
|
48
54
|
|
|
49
55
|
# Create a temporary VHS tape file in the project directory
|
|
50
56
|
tape_content = (
|
|
51
57
|
'Set FontFamily "monospace"\n'
|
|
52
|
-
"Set FontSize
|
|
53
|
-
"Set Width
|
|
54
|
-
"Set Height
|
|
58
|
+
f"Set FontSize {font_size}\n"
|
|
59
|
+
f"Set Width {width}\n"
|
|
60
|
+
f"Set Height {height}\n"
|
|
55
61
|
"Set TypingSpeed 50ms\n"
|
|
56
62
|
f'Type "{bin_command} --help"\n'
|
|
57
63
|
"Enter\n"
|
|
58
|
-
"Sleep
|
|
64
|
+
f"Sleep {duration}s\n"
|
|
59
65
|
)
|
|
60
66
|
|
|
61
67
|
tape_fd, tape_path = tempfile.mkstemp(suffix=".tape", dir=".")
|
|
@@ -210,6 +210,18 @@ def run_cmd(registry, args, flags):
|
|
|
210
210
|
except Exception:
|
|
211
211
|
pass
|
|
212
212
|
|
|
213
|
+
# Update .rlsbl/version marker so it's included in the release commit
|
|
214
|
+
rlsbl_version_marker = os.path.join(".rlsbl", "version")
|
|
215
|
+
if os.path.exists(os.path.dirname(rlsbl_version_marker)):
|
|
216
|
+
try:
|
|
217
|
+
from .. import __version__ as rlsbl_ver
|
|
218
|
+
with open(rlsbl_version_marker, "w") as f:
|
|
219
|
+
f.write(rlsbl_ver + "\n")
|
|
220
|
+
if rlsbl_version_marker not in files_to_commit:
|
|
221
|
+
files_to_commit.append(rlsbl_version_marker)
|
|
222
|
+
except Exception:
|
|
223
|
+
pass
|
|
224
|
+
|
|
213
225
|
# Commit if anything was actually modified (version bump or tagging)
|
|
214
226
|
needs_commit = new_version != current_version or not is_clean_tree()
|
|
215
227
|
if files_to_commit and needs_commit:
|
package/rlsbl/commands/undo.py
CHANGED
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
|
|
5
|
-
from ..utils import run, check_gh_installed, check_gh_auth, get_push_timeout
|
|
5
|
+
from ..utils import run, check_gh_installed, check_gh_auth, get_push_timeout, is_clean_tree
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def run_cmd(registry, args, flags):
|
|
9
|
-
check_gh_installed()
|
|
10
|
-
|
|
9
|
+
if not check_gh_installed():
|
|
10
|
+
print("Error: gh CLI is not installed.", file=sys.stderr)
|
|
11
|
+
sys.exit(1)
|
|
12
|
+
if not check_gh_auth():
|
|
13
|
+
print("Error: gh CLI is not authenticated.", file=sys.stderr)
|
|
14
|
+
sys.exit(1)
|
|
15
|
+
|
|
16
|
+
if not is_clean_tree():
|
|
17
|
+
print("Error: working tree is not clean. Commit your changes first.", file=sys.stderr)
|
|
18
|
+
sys.exit(1)
|
|
11
19
|
|
|
12
20
|
# Find the latest tag
|
|
13
21
|
try:
|
package/rlsbl/commands/watch.py
CHANGED
|
@@ -13,9 +13,12 @@ def _notify(title, body):
|
|
|
13
13
|
"""Send a desktop notification. Non-fatal if unavailable."""
|
|
14
14
|
try:
|
|
15
15
|
if sys.platform == "darwin":
|
|
16
|
+
# Escape double quotes to prevent AppleScript injection
|
|
17
|
+
escaped_title = title.replace('"', '\\"')
|
|
18
|
+
escaped_body = body.replace('"', '\\"')
|
|
16
19
|
subprocess.run(
|
|
17
20
|
["osascript", "-e",
|
|
18
|
-
f'display notification "{
|
|
21
|
+
f'display notification "{escaped_body}" with title "{escaped_title}"'],
|
|
19
22
|
timeout=5, capture_output=True,
|
|
20
23
|
)
|
|
21
24
|
elif shutil.which("notify-send"):
|
|
@@ -33,83 +36,87 @@ def run_cmd(registry, args, flags):
|
|
|
33
36
|
Usage: rlsbl watch [<commit-sha>]
|
|
34
37
|
Defaults to HEAD if no commit SHA is provided.
|
|
35
38
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
try:
|
|
40
|
+
# Get commit SHA
|
|
41
|
+
if args:
|
|
42
|
+
commit_sha = args[0]
|
|
43
|
+
else:
|
|
44
|
+
try:
|
|
45
|
+
commit_sha = run("git", ["rev-parse", "HEAD"])
|
|
46
|
+
except Exception:
|
|
47
|
+
print("Error: not a git repository and no commit SHA provided.", file=sys.stderr)
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
|
|
50
|
+
# Get repo info for display and URLs
|
|
40
51
|
try:
|
|
41
|
-
|
|
52
|
+
repo_info = run("gh", ["repo", "view", "--json", "nameWithOwner,name"])
|
|
53
|
+
info = json.loads(repo_info)
|
|
54
|
+
repo_slug = info.get("nameWithOwner", "")
|
|
55
|
+
repo_name = info.get("name", "")
|
|
42
56
|
except Exception:
|
|
43
|
-
print("Error: not
|
|
57
|
+
print("Error: could not get repo info. Is gh installed and authenticated?", file=sys.stderr)
|
|
44
58
|
sys.exit(1)
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
try:
|
|
48
|
-
repo_info = run("gh", ["repo", "view", "--json", "nameWithOwner,name"])
|
|
49
|
-
info = json.loads(repo_info)
|
|
50
|
-
repo_slug = info.get("nameWithOwner", "")
|
|
51
|
-
repo_name = info.get("name", "")
|
|
52
|
-
except Exception:
|
|
53
|
-
print("Error: could not get repo info. Is gh installed and authenticated?", file=sys.stderr)
|
|
54
|
-
sys.exit(1)
|
|
55
|
-
|
|
56
|
-
# Try to find a tag for this commit for nicer display
|
|
57
|
-
try:
|
|
58
|
-
tag = run("git", ["describe", "--tags", "--exact-match", commit_sha])
|
|
59
|
-
except Exception:
|
|
60
|
-
tag = commit_sha[:12]
|
|
61
|
-
|
|
62
|
-
label = f"{repo_name} {tag}" if repo_name else tag
|
|
63
|
-
|
|
64
|
-
# Poll until at least one run appears (retry up to 30s)
|
|
65
|
-
runs = []
|
|
66
|
-
for _ in range(15):
|
|
60
|
+
# Try to find a tag for this commit for nicer display
|
|
67
61
|
try:
|
|
68
|
-
|
|
69
|
-
"--json", "databaseId,name,status"])
|
|
70
|
-
parsed = json.loads(raw)
|
|
71
|
-
if parsed:
|
|
72
|
-
runs = parsed
|
|
73
|
-
break
|
|
62
|
+
tag = run("git", ["describe", "--tags", "--exact-match", commit_sha])
|
|
74
63
|
except Exception:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
64
|
+
tag = commit_sha[:12]
|
|
65
|
+
|
|
66
|
+
label = f"{repo_name} {tag}" if repo_name else tag
|
|
67
|
+
|
|
68
|
+
# Poll until at least one run appears (retry up to 30s)
|
|
69
|
+
runs = []
|
|
70
|
+
for _ in range(15):
|
|
71
|
+
try:
|
|
72
|
+
raw = run("gh", ["run", "list", "--commit", commit_sha,
|
|
73
|
+
"--json", "databaseId,name,status"])
|
|
74
|
+
parsed = json.loads(raw)
|
|
75
|
+
if parsed:
|
|
76
|
+
runs = parsed
|
|
77
|
+
break
|
|
78
|
+
except Exception:
|
|
79
|
+
pass
|
|
80
|
+
time.sleep(2)
|
|
81
|
+
|
|
82
|
+
if not runs:
|
|
83
|
+
print(f"rlsbl: {label}: no CI runs found after 30s", file=sys.stderr)
|
|
84
|
+
sys.exit(1)
|
|
89
85
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
86
|
+
print(f"rlsbl: {label}: found {len(runs)} CI run(s), watching...", file=sys.stderr)
|
|
87
|
+
|
|
88
|
+
# Watch each run sequentially, collecting results
|
|
89
|
+
any_failed = False
|
|
90
|
+
for ci_run in runs:
|
|
91
|
+
run_id = str(ci_run["databaseId"])
|
|
92
|
+
workflow_name = ci_run.get("name", f"run {run_id}")
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
# gh run watch blocks until the run completes;
|
|
96
|
+
# --exit-status makes it exit 1 on failure; check=True raises
|
|
97
|
+
# CalledProcessError so we can distinguish pass from fail
|
|
98
|
+
subprocess.run(
|
|
99
|
+
["gh", "run", "watch", run_id, "--exit-status"],
|
|
100
|
+
capture_output=True, text=True, timeout=3600, check=True,
|
|
101
|
+
)
|
|
102
|
+
print(f"rlsbl: {label}: {workflow_name} passed", file=sys.stderr)
|
|
103
|
+
except subprocess.CalledProcessError:
|
|
104
|
+
any_failed = True
|
|
105
|
+
print(f"rlsbl: {label}: {workflow_name} FAILED", file=sys.stderr)
|
|
106
|
+
if repo_slug:
|
|
107
|
+
print(f"rlsbl: https://github.com/{repo_slug}/actions/runs/{run_id}",
|
|
108
|
+
file=sys.stderr)
|
|
109
|
+
except subprocess.TimeoutExpired:
|
|
110
|
+
any_failed = True
|
|
111
|
+
print(f"rlsbl: {label}: {workflow_name} timed out after 1h", file=sys.stderr)
|
|
112
|
+
|
|
113
|
+
# Desktop notification for overall result
|
|
114
|
+
if any_failed:
|
|
115
|
+
_notify(f"{label}: CI FAILED", "One or more workflows failed")
|
|
116
|
+
else:
|
|
117
|
+
_notify(f"{label}: CI passed", "All workflows passed")
|
|
118
|
+
|
|
119
|
+
sys.exit(1 if any_failed else 0)
|
|
120
|
+
except KeyboardInterrupt:
|
|
121
|
+
print("\nWatch cancelled.", file=sys.stderr)
|
|
122
|
+
sys.exit(130)
|
package/rlsbl/config.py
CHANGED
|
@@ -11,7 +11,10 @@ import json
|
|
|
11
11
|
import os
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
def _project_config():
|
|
15
|
+
"""Resolve project config path at call time (respects cwd changes)."""
|
|
16
|
+
return os.path.join(".rlsbl", "config.json")
|
|
17
|
+
|
|
15
18
|
USER_CONFIG = os.path.expanduser("~/.rlsbl/config.json")
|
|
16
19
|
|
|
17
20
|
|
|
@@ -31,7 +34,7 @@ def should_tag(flags):
|
|
|
31
34
|
return False
|
|
32
35
|
|
|
33
36
|
# Project-level config
|
|
34
|
-
project = read_json_config(
|
|
37
|
+
project = read_json_config(_project_config())
|
|
35
38
|
if "tag" in project:
|
|
36
39
|
return bool(project["tag"])
|
|
37
40
|
|
|
@@ -46,9 +49,9 @@ def should_tag(flags):
|
|
|
46
49
|
|
|
47
50
|
def write_project_config(key, value):
|
|
48
51
|
"""Write or update a key in .rlsbl/config.json (creates dir if needed)."""
|
|
49
|
-
os.makedirs(os.path.dirname(
|
|
50
|
-
existing = read_json_config(
|
|
52
|
+
os.makedirs(os.path.dirname(_project_config()), exist_ok=True)
|
|
53
|
+
existing = read_json_config(_project_config())
|
|
51
54
|
existing[key] = value
|
|
52
|
-
with open(
|
|
55
|
+
with open(_project_config(), "w", encoding="utf-8") as f:
|
|
53
56
|
json.dump(existing, f, indent=2)
|
|
54
57
|
f.write("\n")
|
package/rlsbl/registries/go.py
CHANGED
|
@@ -39,12 +39,12 @@ def get_version_file():
|
|
|
39
39
|
|
|
40
40
|
def get_template_dir():
|
|
41
41
|
"""Returns path to the go-specific template directory."""
|
|
42
|
-
return os.path.join(os.path.dirname(__file__), "..", "
|
|
42
|
+
return os.path.join(os.path.dirname(__file__), "..", "templates", "go")
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def get_shared_template_dir():
|
|
46
46
|
"""Returns path to the shared template directory."""
|
|
47
|
-
return os.path.join(os.path.dirname(__file__), "..", "
|
|
47
|
+
return os.path.join(os.path.dirname(__file__), "..", "templates", "shared")
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
def get_template_vars(dir_path):
|
package/rlsbl/registries/npm.py
CHANGED
|
@@ -32,7 +32,7 @@ def write_version(dir_path, version):
|
|
|
32
32
|
|
|
33
33
|
# Preserve trailing newline if present
|
|
34
34
|
trailing_newline = "\n" if raw.endswith("\n") else ""
|
|
35
|
-
output = json.dumps(pkg, indent=indent) + trailing_newline
|
|
35
|
+
output = json.dumps(pkg, indent=indent, ensure_ascii=False) + trailing_newline
|
|
36
36
|
# Atomic write: write to temp file, then rename
|
|
37
37
|
tmp_path = pkg_path + ".tmp"
|
|
38
38
|
with open(tmp_path, "w", encoding="utf-8") as f:
|
|
@@ -47,12 +47,12 @@ def get_version_file():
|
|
|
47
47
|
|
|
48
48
|
def get_template_dir():
|
|
49
49
|
"""Returns path to the npm-specific template directory."""
|
|
50
|
-
return os.path.join(os.path.dirname(__file__), "..", "
|
|
50
|
+
return os.path.join(os.path.dirname(__file__), "..", "templates", "npm")
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def get_shared_template_dir():
|
|
54
54
|
"""Returns path to the shared template directory."""
|
|
55
|
-
return os.path.join(os.path.dirname(__file__), "..", "
|
|
55
|
+
return os.path.join(os.path.dirname(__file__), "..", "templates", "shared")
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
def get_template_vars(dir_path):
|
package/rlsbl/registries/pypi.py
CHANGED
|
@@ -67,12 +67,12 @@ def get_version_file():
|
|
|
67
67
|
|
|
68
68
|
def get_template_dir():
|
|
69
69
|
"""Returns path to the pypi-specific template directory."""
|
|
70
|
-
return os.path.join(os.path.dirname(__file__), "..", "
|
|
70
|
+
return os.path.join(os.path.dirname(__file__), "..", "templates", "pypi")
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
def get_shared_template_dir():
|
|
74
74
|
"""Returns path to the shared template directory."""
|
|
75
|
-
return os.path.join(os.path.dirname(__file__), "..", "
|
|
75
|
+
return os.path.join(os.path.dirname(__file__), "..", "templates", "shared")
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
def get_template_vars(dir_path):
|
package/rlsbl/tagging.py
CHANGED
|
@@ -32,7 +32,7 @@ def ensure_npm_keyword(dir_path=".", quiet=False):
|
|
|
32
32
|
|
|
33
33
|
# Preserve trailing newline if present
|
|
34
34
|
trailing_newline = "\n" if raw.endswith("\n") else ""
|
|
35
|
-
output = json.dumps(pkg, indent=indent) + trailing_newline
|
|
35
|
+
output = json.dumps(pkg, indent=indent, ensure_ascii=False) + trailing_newline
|
|
36
36
|
|
|
37
37
|
# Atomic write: write to temp file, then rename
|
|
38
38
|
tmp_path = pkg_path + ".tmp"
|
|
@@ -82,11 +82,16 @@ def ensure_pypi_keyword(dir_path=".", quiet=False):
|
|
|
82
82
|
# Find the indent used for existing items
|
|
83
83
|
item_indent_match = re.search(r'\n( +)"', array_content)
|
|
84
84
|
item_indent = item_indent_match.group(1) if item_indent_match else " "
|
|
85
|
-
|
|
85
|
+
# Strip trailing comma to avoid double comma when the list
|
|
86
|
+
# already has a trailing comma before the closing bracket
|
|
87
|
+
stripped = array_content.rstrip()
|
|
88
|
+
stripped = stripped.rstrip(",")
|
|
89
|
+
new_array_content = stripped + f',\n{item_indent}"rlsbl"\n'
|
|
86
90
|
else:
|
|
87
91
|
# Single-line
|
|
88
92
|
if array_content.strip():
|
|
89
|
-
|
|
93
|
+
stripped_sl = array_content.rstrip().rstrip(",")
|
|
94
|
+
new_array_content = stripped_sl + ', "rlsbl"'
|
|
90
95
|
else:
|
|
91
96
|
new_array_content = '"rlsbl"'
|
|
92
97
|
new_field = prefix + new_array_content + "]"
|
|
@@ -8,15 +8,19 @@ set -euo pipefail
|
|
|
8
8
|
echo "Running pre-release checks..."
|
|
9
9
|
|
|
10
10
|
if [ -f go.mod ]; then
|
|
11
|
-
echo "
|
|
11
|
+
echo " Go: vet + build + test"
|
|
12
12
|
go vet ./...
|
|
13
13
|
go build ./...
|
|
14
14
|
go test ./... -race -short -count=1
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
if [ -f package.json ] && node -e "process.exit(require('./package.json').scripts?.test ? 0 : 1)" 2>/dev/null; then
|
|
18
|
+
echo " npm: test"
|
|
17
19
|
npm test
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [ -f pyproject.toml ]; then
|
|
23
|
+
echo " Python: pytest"
|
|
20
24
|
if command -v uv &>/dev/null; then
|
|
21
25
|
uv run pytest
|
|
22
26
|
elif command -v pytest &>/dev/null; then
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|