Skip to content

Commit c9382a7

Browse files
committed
Add release script and release notes
1 parent a49a8fe commit c9382a7

File tree

4 files changed

+210
-0
lines changed

4 files changed

+210
-0
lines changed

.gitattributes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,8 @@ tests/ export-ignore
55
tox.ini export-ignore
66
compile-eslint-server.sh export-ignore
77
unittesting.json export-ignore
8+
9+
# Release script
10+
.release.json export-ignore
11+
scripts export-ignore
12+
VERSION export-ignore

.release.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"push_branch": "master",
3+
"publish_repo": "sublimelsp/LSP-eslint"
4+
}

messages/1.0.0.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=> 1.0.0
2+
3+
Breaking:
4+
5+
* Formatting on save (controlled by "lsp_format_on_save" setting) is no longer supported
6+
by this server. Instead, applying fixes is now handled by "code actions on save" feature,
7+
controlled by "lsp_code_actions_on_save" setting.
8+
9+
Example LSP configuration that enables applying eslint fixes on save:
10+
11+
"lsp_code_actions_on_save": {
12+
"source.fixAll.eslint": true,
13+
},
14+
15+
To enable the feature, open "Preferences: LSP Settings" from the Command Palette and
16+
set those settings.
17+
18+
Change:
19+
20+
* Enable for Typescript files by default.
21+
(Make sure to update your eslint configuration according to
22+
https://github.com/typescript-eslint/typescript-eslint)
23+
24+
Maintenance:
25+
26+
* Update eslint server to latest version

scripts/release.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
from typing import Generator, List, Optional, Tuple
5+
import argparse
6+
import json
7+
import os
8+
import re
9+
import subprocess
10+
import sys
11+
12+
# Internal
13+
PACKAGE_PATH = os.path.realpath(os.path.join(os.path.join(os.path.dirname(__file__), '..')))
14+
MESSAGE_DIR = 'messages'
15+
MESSAGE_PATH = os.path.join(PACKAGE_PATH, MESSAGE_DIR)
16+
17+
with open(os.path.join(PACKAGE_PATH, '.release.json'), 'r') as f:
18+
CONFIGURATION = json.load(f)
19+
20+
# Project configuration
21+
# The name of the branch to push to the remote on releasing.
22+
RELEASE_BRANCH = CONFIGURATION['push_branch']
23+
# The name of the GitHub repository in <owner>/<repo> format
24+
GITHUB_REPO = CONFIGURATION['publish_repo']
25+
# The name of the settings file to get the release token from ("github_token" setting)
26+
SETTINGS = '{}.sublime-settings'.format(__package__)
27+
PYTHON_VERSION_PATH = CONFIGURATION.get('python_version_path', None)
28+
29+
30+
def get_message(fname: str) -> str:
31+
with open(fname, 'r', encoding='utf-8') as file:
32+
message = file.read()
33+
return message
34+
35+
36+
def put_message(fname: str, text: str) -> None:
37+
with open(fname, 'w', encoding='utf-8') as file:
38+
file.write(text)
39+
40+
41+
def build_messages_json(version_history: List[str]) -> None:
42+
"""Write the version history to the messages.json file."""
43+
output = os.path.join(PACKAGE_PATH, 'messages.json')
44+
with open(output, 'w+', encoding='utf-8') as file:
45+
json.dump(
46+
obj={v: MESSAGE_DIR + '/' + v + '.txt' for v in version_history},
47+
fp=file, indent=4, separators=(',', ': '), sort_keys=True)
48+
file.write('\n')
49+
50+
51+
def version_history() -> List[str]:
52+
"""Return a list of all releases."""
53+
def generator() -> Generator[str, None, None]:
54+
for filename in os.listdir(MESSAGE_PATH):
55+
basename, ext = os.path.splitext(filename)
56+
if ext.lower() == '.txt':
57+
yield basename
58+
59+
return sorted(tuple(generator()), key=parse_version)
60+
61+
62+
def parse_version(version: str) -> Tuple[int, int, int]:
63+
"""Convert filename to version tuple (major, minor, patch)."""
64+
match = re.match(
65+
r'(?:(?P<prefix>[^.-]+)\-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-.+)?', version)
66+
if match:
67+
prefix, major, minor, patch = match.groups()
68+
return int(major), int(minor), int(patch)
69+
else:
70+
return 0, 0, 0
71+
72+
73+
def git(*args: str) -> Optional[str]:
74+
"""Run git command within current package path."""
75+
if os.name == 'nt':
76+
startupinfo = subprocess.STARTUPINFO() # type: ignore
77+
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW # type: ignore
78+
else:
79+
startupinfo = None
80+
proc = subprocess.Popen(
81+
args=['git'] + [arg for arg in args], startupinfo=startupinfo,
82+
stdout=subprocess.PIPE, stdin=subprocess.PIPE, cwd=PACKAGE_PATH)
83+
stdout, _ = proc.communicate()
84+
return stdout.decode('utf-8').strip() if stdout else None
85+
86+
87+
def commit_release(version: str) -> None:
88+
"""Create a 'Cut <version>' commit and tag."""
89+
commit_message = 'Cut %s' % version
90+
git('add', '.')
91+
git('commit', '-m', commit_message)
92+
git('tag', '-a', '-m', commit_message, version)
93+
94+
95+
def build_release(args: argparse.Namespace) -> None:
96+
"""Build the new release locally."""
97+
history = version_history()
98+
version = history[-1]
99+
put_message(os.path.join(PACKAGE_PATH, 'VERSION'), version)
100+
if PYTHON_VERSION_PATH:
101+
version_tuple = parse_version(version)
102+
put_message(PYTHON_VERSION_PATH, '__version__ = {}\n'.format(version_tuple))
103+
build_messages_json(history)
104+
commit_release(version)
105+
print("Release %s created!" % version)
106+
107+
108+
def publish_release(args: argparse.Namespace) -> None:
109+
"""Publish the new release."""
110+
if not args.token:
111+
print('The GitHub token must be provided either through argument of GITHUB_TOKEN environment variable.')
112+
sys.exit(1)
113+
114+
version = get_message(os.path.join(PACKAGE_PATH, 'VERSION'))
115+
116+
repo_url = 'https://github.com/{}'.format(GITHUB_REPO)
117+
# push release branch to server
118+
git('push', repo_url, RELEASE_BRANCH)
119+
# push tags to server
120+
git('push', repo_url, 'tag', version)
121+
122+
# publish the release
123+
post_url = '/repos/{}/releases?access_token={}'.format(GITHUB_REPO, args.token)
124+
headers = {
125+
'User-Agent': 'Sublime Text',
126+
'Content-type': 'application/json',
127+
}
128+
# get message from /messages/<version>.txt
129+
text = get_message(os.path.join(MESSAGE_PATH, version + '.txt'))
130+
# strip message header (version)
131+
text = text[text.find('\n') + 1:]
132+
# built the JSON request body
133+
data = json.dumps({
134+
"tag_name": version,
135+
"target_commitish": RELEASE_BRANCH,
136+
"name": version,
137+
"body": text,
138+
"draft": False,
139+
"prerelease": False
140+
})
141+
try:
142+
import http.client
143+
client = http.client.HTTPSConnection('api.github.com')
144+
client.request('POST', post_url, body=data, headers=headers)
145+
response = client.getresponse()
146+
print("Release %s published!" % version
147+
if response.status == 201 else
148+
"Release %s failed!" % version)
149+
finally:
150+
client.close()
151+
152+
153+
"""
154+
======================================
155+
Command Line Interface
156+
======================================
157+
"""
158+
if __name__ == '__main__':
159+
parser = argparse.ArgumentParser(
160+
description='Buils and Publishes {} Releases'.format(__package__))
161+
subparsers = parser.add_subparsers(help='Available commands')
162+
build_parser = subparsers.add_parser('build', help='Build a release')
163+
build_parser.set_defaults(func=build_release)
164+
publish_parser = subparsers.add_parser('publish', help='Publish a release')
165+
publish_parser.add_argument(
166+
'--token',
167+
nargs='?',
168+
default=os.environ.get('GITHUB_TOKEN', None),
169+
help='The GitHub access token used for authentication.')
170+
publish_parser.set_defaults(func=publish_release)
171+
args = parser.parse_args()
172+
if 'func' not in args:
173+
parser.print_help()
174+
else:
175+
args.func(args)

0 commit comments

Comments
 (0)