Skip to content

Commit abdb35d

Browse files
committed
Update workflow script for release
1 parent 62189bc commit abdb35d

File tree

2 files changed

+46
-208
lines changed

2 files changed

+46
-208
lines changed

scripts/requirements.txt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
certifi==2024.2.2
2-
charset-normalizer==3.3.2
3-
click==8.1.7
4-
idna==3.7
5-
requests==2.32.3
6-
urllib3==2.2.1
1+
click==8.3.1

scripts/workflow.py

Lines changed: 45 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,10 @@
11
import json
2-
import os
32
import pathlib
43
import subprocess
54

65
import click
7-
import dotenv
8-
import requests
9-
106

117
SCRIPTS_DIR = pathlib.Path(__file__).parent
12-
dotenv.load_dotenv(SCRIPTS_DIR / '.env')
13-
14-
WEBLATE_PROJECT_WEB_URL = 'https://ds-wizard.org'
15-
16-
17-
class WeblateClient:
18-
19-
def __init__(self, base_url: str, api_token: str,
20-
session: requests.Session | None = None):
21-
self.base_url = base_url.rstrip('/')
22-
self.api_token = api_token
23-
self.session = session or requests.Session()
24-
self.session.headers.update({
25-
'Authorization': f'Token {self.api_token}',
26-
})
27-
28-
def _url(self, path: str) -> str:
29-
return f'{self.base_url}/api/{path}'
30-
31-
def lock_component(self, project_slug: str, component_slug: str) -> bool:
32-
r = self.session.post(
33-
url=self._url(f'projects/{project_slug}/components/{component_slug}/lock/'),
34-
json={
35-
'lock': True,
36-
}
37-
)
38-
r.raise_for_status()
39-
return r.json().get('lock', False)
40-
41-
def get_project(self, project_slug: str) -> dict:
42-
r = self.session.get(
43-
url=self._url(f'projects/{project_slug}/'),
44-
)
45-
r.raise_for_status()
46-
return r.json()
47-
48-
def get_project_components(self, project_slug: str) -> list[dict]:
49-
r = self.session.get(
50-
url=self._url(f'projects/{project_slug}/components/'),
51-
)
52-
r.raise_for_status()
53-
return r.json().get('results', [])
54-
55-
def create_project(self, name: str, slug: str) -> dict:
56-
r = self.session.post(
57-
url=self._url('projects/'),
58-
json={
59-
'name': name,
60-
'slug': slug,
61-
'web': WEBLATE_PROJECT_WEB_URL,
62-
'check_flags': '',
63-
'translation_review': False,
64-
'source_review': False,
65-
'set_language_team': True,
66-
'instructions': '',
67-
'enable_hooks': True,
68-
'language_aliases': '',
69-
'secondary_language': 0,
70-
'enforced_2fa': False
71-
},
72-
)
73-
r.raise_for_status()
74-
return r.json()
75-
76-
def create_component(self, project_slug: str, name: str, slug: str, branch: str,
77-
po_filemask: str, pot_file: str, repoweb: str) -> dict:
78-
r = self.session.post(
79-
url=self._url(f'projects/{project_slug}/components/'),
80-
json={
81-
'name': name,
82-
'slug': slug,
83-
'source_language': {
84-
'code': 'en',
85-
'name': 'English',
86-
'plural': {
87-
'source': 0,
88-
'number': 2,
89-
'formula': 'n != 1'
90-
},
91-
'direction': 'ltr',
92-
'population': 1728003224
93-
},
94-
'vcs': 'git',
95-
'repo': 'git@github.com:ds-wizard/wizard-locales.git',
96-
'git_export': f'https://localize.ds-wizard.org/git/{project_slug}/{slug}/',
97-
'branch': branch,
98-
'push_branch': branch,
99-
'filemask': po_filemask,
100-
'screenshot_filemask': '',
101-
'template': '',
102-
'edit_template': True,
103-
'intermediate': '',
104-
'new_base': pot_file,
105-
'file_format': 'po',
106-
'license': 'CC-BY-4.0',
107-
'agreement': '',
108-
'new_lang': 'add',
109-
'language_code_style': '',
110-
'push': 'git@github.com:ds-wizard/wizard-locales.git',
111-
'check_flags': '',
112-
'priority': 100,
113-
'enforced_checks': [],
114-
'restricted': False,
115-
'repoweb': repoweb,
116-
'report_source_bugs': '',
117-
'merge_style': 'rebase',
118-
'commit_message': 'Translated using Weblate ({{ language_name }})\n\nCurrently translated at {{ stats.translated_percent }}% ({{ stats.translated }} of {{ stats.all }} strings)\n\nTranslation: {{ project_name }}/{{ component_name }}\nTranslate-URL: {{ url }}',
119-
'add_message': 'Added translation using Weblate ({{ language_name }})\n\n',
120-
'delete_message': 'Deleted translation using Weblate ({{ language_name }})\n\n',
121-
'merge_message': 'Merge branch \'{{ component_remote_branch }}\' into Weblate.\n\n',
122-
'addon_message': 'Update translation files\n\nUpdated by \"{{ addon_name }}\" add-on in Weblate.\n\nTranslation: {{ project_name }}/{{ component_name }}\nTranslate-URL: {{ url }}',
123-
'pull_message': 'Translations update from {{ site_title }}\n\nTranslations update from [{{ site_title }}]({{ site_url }}) for [{{ project_name }}/{{ component_name }}]({{url}}).\n\n{% if component_linked_childs %}\nIt also includes following components:\n{% for linked in component_linked_childs %}\n* [{{ linked.project_name }}/{{ linked.name }}]({{ linked.url }})\n{% endfor %}\n{% endif %}\n\nCurrent translation status:\n\n![Weblate translation status]({{widget_url}})\n',
124-
"allow_translation_propagation": True,
125-
"manage_units": False,
126-
"enable_suggestions": True,
127-
"suggestion_voting": False,
128-
"suggestion_autoaccept": 0,
129-
'push_on_commit': True,
130-
'commit_pending_age': 24,
131-
'auto_lock_error': True,
132-
'language_regex': '^[^.]+$',
133-
'key_filter': '',
134-
'secondary_language': None,
135-
'variant_regex': '',
136-
'zipfile': '',
137-
'docfile': '',
138-
'is_glossary': False,
139-
'glossary_color': 'silver',
140-
'disable_autoshare': True,
141-
'category': None,
142-
},
143-
)
144-
r.raise_for_status()
145-
return r.json()
1468

1479

14810
def _parse_version(version: str) -> tuple[int, int, int]:
@@ -153,6 +15,18 @@ def _parse_version(version: str) -> tuple[int, int, int]:
15315
return int(major), int(minor), int(patch)
15416

15517

18+
def _proceed():
19+
proceed = False
20+
while not proceed:
21+
proceed = click.prompt(
22+
text='Are you ready to continue?',
23+
show_default=True,
24+
type=bool,
25+
default=True,
26+
)
27+
click.echo('=' * 60)
28+
29+
15630
def _run(*command: str) -> None:
15731
click.echo(f'Running command: {' '.join(command)}')
15832
try:
@@ -165,88 +39,71 @@ def _run(*command: str) -> None:
16539
raise e
16640

16741

168-
def _create_weblate_project(weblate_client: WeblateClient, project_name: str, project_slug: str):
169-
click.echo(f'Creating Weblate project "{project_name}" with slug "{project_slug}"')
170-
project = weblate_client.create_project(name=project_name, slug=project_slug)
171-
click.echo(f'Created project: {json.dumps(project, indent=2)}')
172-
client_component = weblate_client.create_component(
173-
project_slug=project_slug,
174-
name='DSW Locales',
175-
slug='dsw-locales',
176-
branch='main',
177-
po_filemask='locales/{locale}/LC_MESSAGES/dsw.po',
178-
pot_file='locales/dsw.pot',
179-
repoweb='https://localize.ds-wizard.org/git/{project_slug}/{component_slug}/',
180-
)
181-
mail_template_component = weblate_client.create_component(
182-
project_slug=project_slug,
183-
name='DSW Mail Templates',
184-
slug='dsw-mail-templates',
185-
branch='main',
186-
po_filemask='mail-templates/{locale}/LC_MESSAGES/dsw-mail-templates.po',
187-
pot_file='mail-templates/dsw-mail-templates.pot',
188-
repoweb='https://localize.ds-wizard.org/git/{project_slug}/{component_slug}/',
189-
)
190-
return project
191-
192-
19342
def _release(version: str, locales_dir: pathlib.Path):
194-
weblate_client = WeblateClient(
195-
base_url=os.getenv('WEBLATE_BASE_URL', ''),
196-
api_token=os.getenv('WEBLATE_API_TOKEN', ''),
197-
)
19843
major, minor, patch = _parse_version(version)
19944
# Prepare local environment
200-
click.echo('Locking changes in project on Weblate')
201-
# TODO: weblate lock (API only via all component)
202-
click.echo('Pushing changes from Weblate')
203-
# TODO: push changes from Weblate
204-
click.echo('Waiting for change propagation')
205-
click.echo('Checking out the correct branch')
45+
click.echo('WEBLATE: Go to Weblate and ensure everything is synced and ready for release:')
46+
localize_url = f'https://localize.ds-wizard.org/projects/dsw-{major}-{minor}/#repository'
47+
click.echo('- 0 in pending')
48+
click.echo('- 0 in outgoing')
49+
click.echo('- 0 in missing')
50+
click.echo('- Lock components all components (click Lock button)')
51+
click.echo(f'> {localize_url}')
52+
click.launch(localize_url)
53+
_proceed()
54+
55+
# Wait for changes in Git
56+
click.echo('GITHUB: Go to Git and ensure everything is up to date:')
57+
commits_url = f'https://github.com/ds-wizard/wizard-locales/commits/v{major}.{minor}/'
58+
click.echo(f'> {commits_url}')
59+
click.launch(commits_url)
60+
click.echo('REPO: Also verify that local git repository without any uncommitted changes.')
61+
_proceed()
62+
63+
# Checkout correct branch and pull latest changes
64+
click.echo('REPO: Checking out the correct branch')
20665
_run('git', 'checkout', f'v{major}.{minor}')
207-
click.echo('Pulling latest changes')
66+
click.echo('REPO: Pulling latest changes')
20867
_run('git', 'pull', 'origin', f'v{major}.{minor}')
20968

21069
# Update version in locale.json files
70+
click.echo('REPO: Updating version in locale.json files')
21171
langs = []
21272
for locale_dir in locales_dir.iterdir():
21373
if not locale_dir.is_dir():
21474
continue
21575
locale_id = locale_dir.name
21676
locale_json = locale_dir / 'locale.json'
21777
if not locale_json.is_file():
218-
click.echo(f'Warning: locale.json not found for locale {locale_id}, skipping', err=True)
78+
click.echo(f'- Warning: locale.json not found for locale {locale_id}, skipping', err=True)
21979
continue
22080
data = json.loads(locale_json.read_text(encoding='utf-8'))
221-
process = click.prompt(f'Do you want to release locale {locale_id}?',
81+
process = click.prompt(f'> Do you want to release locale {locale_id}?',
22282
show_default=True, type=bool, default=True)
22383
if not process:
224-
click.echo(f'Skipping locale {locale_id}')
84+
click.echo(f'- Skipping locale {locale_id}')
22585
continue
22686
langs.append(locale_id)
227-
click.echo(f'Processing locale {locale_id}')
87+
click.echo(f'- Processing locale {locale_id}')
22888
data['version'] = version
22989
data['recommendedAppVersion'] = f'{major}.{minor}.0'
23090
locale_json.write_text(json.dumps(data, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
231-
click.echo(f'Updated version in {locale_json}')
91+
click.echo(f'- Updated version in {locale_json}')
23292

23393
# Let user do manual changes
234-
click.echo('You can now make any manual changes to the locale files if necessary.')
235-
proceed = False
236-
while not proceed:
237-
proceed = click.prompt('Are you ready to continue with the release process?',
238-
show_default=True, type=bool, default=True)
94+
click.echo('REPO: You can now make any manual changes if necessary')
95+
_proceed()
23996

24097
# Commit and push changes
241-
click.echo('Committing all changes')
98+
click.echo('REPO: Committing all changes')
24299
_run('git', 'add', '-A')
243100
_run('git', 'commit', '-m', f'Release {version} ({', '.join(langs)})')
244-
click.echo('Pushing changes to remote')
101+
click.echo('REPO: Pushing changes to remote')
245102
_run('git', 'push', 'origin', f'v{major}.{minor}')
246-
click.echo('Creating git tags')
103+
click.echo('REPO: Creating git tags')
247104
for lang in langs:
248105
tag_name = f'v{version}-{lang}'
249-
click.echo(f'Creating tag {tag_name}')
106+
click.echo(f'- Creating tag {tag_name}')
250107
_run('git', 'tag', tag_name)
251108
_run('git', 'push', 'origin', tag_name)
252109

@@ -283,19 +140,5 @@ def prepare(old_version: str, new_version: str):
283140
...
284141

285142

286-
@cli.command()
287-
def test():
288-
weblate_client = WeblateClient(
289-
base_url=os.getenv('WEBLATE_BASE_URL', ''),
290-
api_token=os.getenv('WEBLATE_API_TOKEN', ''),
291-
)
292-
result = weblate_client.get_project('dsw-4-25')
293-
print('Project info:')
294-
print(json.dumps(result, indent=2))
295-
components = weblate_client.get_project_components('dsw-4-25')
296-
print(f'Found {len(components)} components:')
297-
print(json.dumps(components, indent=2))
298-
299-
300143
if __name__ == '__main__':
301144
cli()

0 commit comments

Comments
 (0)