Extract weblate types, constants, logic
This commit is contained in:
@@ -10,7 +10,8 @@ import yargs from 'yargs';
|
|||||||
import { hideBin } from 'yargs/helpers';
|
import { hideBin } from 'yargs/helpers';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { createCommitChangelog } from './release/CommitChangelog.utils.ts';
|
import { createCommitChangelog } from './release/CommitChangelog.utils.ts';
|
||||||
import { createTranslationChangelog, validateWeblateDates } from './release/TranslationChangelog.utils.ts';
|
import { createTranslationChangelog } from './release/TranslationChangelog.utils.ts';
|
||||||
|
import { validateWeblateDates } from './weblate/Weblate.utils.ts';
|
||||||
|
|
||||||
const { sha, date } = yargs(hideBin(process.argv))
|
const { sha, date } = yargs(hideBin(process.argv))
|
||||||
.options({
|
.options({
|
||||||
|
|||||||
@@ -8,10 +8,8 @@
|
|||||||
|
|
||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
import { hideBin } from 'yargs/helpers';
|
import { hideBin } from 'yargs/helpers';
|
||||||
import {
|
import { createTranslationChangelog } from './release/TranslationChangelog.utils.ts';
|
||||||
createTranslationChangelog,
|
import { TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT } from './weblate/Weblate.constants.ts';
|
||||||
TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT,
|
|
||||||
} from './release/TranslationChangelog.utils.ts';
|
|
||||||
|
|
||||||
const { afterDate, beforeDate, requiredContributionCount, keepKnownContributors } = yargs(hideBin(process.argv))
|
const { afterDate, beforeDate, requiredContributionCount, keepKnownContributors } = yargs(hideBin(process.argv))
|
||||||
.options({
|
.options({
|
||||||
|
|||||||
@@ -11,136 +11,16 @@ import fs from 'fs';
|
|||||||
import readline from 'readline';
|
import readline from 'readline';
|
||||||
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
|
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
|
||||||
import tokens from '../tokens.json';
|
import tokens from '../tokens.json';
|
||||||
|
import {
|
||||||
enum WeblateChangeActions {
|
ActionByTranslationUrlByUserUrl,
|
||||||
RESOURCE_UPDATED = 0,
|
ContributionsByLanguage,
|
||||||
TRANSLATION_COMPLETED = 1,
|
LanguageNameByTranslationUrl,
|
||||||
TRANSLATION_CHANGED = 2,
|
UsernameByUserUrl,
|
||||||
COMMENT_ADDED = 3,
|
UserUrlsByTranslationUrl,
|
||||||
SUGGESTION_ADDED = 4,
|
WeblateChangeResult,
|
||||||
TRANSLATION_ADDED = 5,
|
} from '../weblate/Weblate.types.ts';
|
||||||
SUGGESTION_ACCEPTED = 7,
|
import { fetchWeblateChanges, getLanguageName, getUsername, validateWeblateDates } from '../weblate/Weblate.utils.ts';
|
||||||
TRANSLATION_REVERTED = 8,
|
import { creditRelevantActions, TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT } from '../weblate/Weblate.constants.ts';
|
||||||
TRANSLATION_UPLOADED = 9,
|
|
||||||
COMPONENT_LOCKED = 14,
|
|
||||||
COMPONENT_UNLOCKED = 15,
|
|
||||||
CHANGES_COMMITTED = 17,
|
|
||||||
CHANGES_PUSHED = 18,
|
|
||||||
REPOSITORY_RESET = 19,
|
|
||||||
REPOSITORY_MERGED = 20,
|
|
||||||
REPOSITORY_REBASED = 21,
|
|
||||||
REPOSITORY_MERGE_FAILED = 22,
|
|
||||||
REPOSITORY_REBASE_FAILED = 23,
|
|
||||||
PARSING_FAILED = 24,
|
|
||||||
TRANSLATION_REMOVED = 25,
|
|
||||||
SUGGESTION_REMOVED = 26,
|
|
||||||
MARKED_FOR_EDIT = 37,
|
|
||||||
COMPONENT_RENAMED = 42,
|
|
||||||
CONTRIBUTOR_JOINED = 45,
|
|
||||||
LANGUAGE_ADDED = 48,
|
|
||||||
COMPONENT_CREATED = 51,
|
|
||||||
ADD_ON_CONFIGURATION_CHANGED = 61,
|
|
||||||
}
|
|
||||||
|
|
||||||
const creditRelevantActions: WeblateChangeActions[] = [
|
|
||||||
WeblateChangeActions.TRANSLATION_CHANGED,
|
|
||||||
WeblateChangeActions.TRANSLATION_ADDED,
|
|
||||||
WeblateChangeActions.TRANSLATION_REVERTED,
|
|
||||||
WeblateChangeActions.TRANSLATION_UPLOADED,
|
|
||||||
WeblateChangeActions.TRANSLATION_REMOVED,
|
|
||||||
WeblateChangeActions.MARKED_FOR_EDIT,
|
|
||||||
];
|
|
||||||
|
|
||||||
interface WeblateChangeResult {
|
|
||||||
translation: string;
|
|
||||||
action: WeblateChangeActions;
|
|
||||||
action_name: keyof WeblateChangeActions;
|
|
||||||
user: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WeblateChangePayload {
|
|
||||||
next: string;
|
|
||||||
results: WeblateChangeResult[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WeblateUserPayload {
|
|
||||||
full_name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WeblateLanguagePayload {
|
|
||||||
language: {
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type Username = string;
|
|
||||||
type UserUrl = string;
|
|
||||||
|
|
||||||
type LanguageName = string;
|
|
||||||
type TranslationUrl = string;
|
|
||||||
|
|
||||||
type UsernameByUserUrl = Record<UserUrl, Username>;
|
|
||||||
type LanguageNameByTranslationUrl = Record<TranslationUrl, LanguageName>;
|
|
||||||
type UserUrlsByTranslationUrl = Record<TranslationUrl, UserUrl[]>;
|
|
||||||
type ActionByTranslationUrlByUserUrl = Record<
|
|
||||||
UserUrl,
|
|
||||||
Record<TranslationUrl, { count: number; actions: Record<WeblateChangeActions, number> }>
|
|
||||||
>;
|
|
||||||
|
|
||||||
interface Contributor {
|
|
||||||
username: Username;
|
|
||||||
contributionCount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ContributionsByLanguage = Record<LanguageName, Contributor[]>;
|
|
||||||
|
|
||||||
export const TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT = {
|
|
||||||
requiredContributionCount: 10,
|
|
||||||
keepKnownContributors: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchData = async <T = any>(url: string, authToken: string): Promise<T> => {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Token ${authToken}`,
|
|
||||||
Accept: 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Weblate request failed with status ${response.status} - ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchWeblateChanges = async (
|
|
||||||
url: string,
|
|
||||||
authToken: string,
|
|
||||||
weblateChangeResults: WeblateChangeResult[] = [],
|
|
||||||
): Promise<WeblateChangeResult[]> => {
|
|
||||||
const weblateChangePage = await fetchData<WeblateChangePayload>(url, authToken);
|
|
||||||
|
|
||||||
if (!weblateChangePage.next) {
|
|
||||||
return [...weblateChangePage.results, ...weblateChangeResults];
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetchWeblateChanges(weblateChangePage.next, authToken, [
|
|
||||||
...weblateChangePage.results,
|
|
||||||
...weblateChangeResults,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getUsername = async (url: string, authToken: string): Promise<string> => {
|
|
||||||
const userPayload = await fetchData<WeblateUserPayload>(url, authToken);
|
|
||||||
return userPayload.full_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLanguageName = async (url: string, authToken: string): Promise<string> => {
|
|
||||||
const languagePayload = await fetchData<WeblateLanguagePayload>(url, authToken);
|
|
||||||
return languagePayload.language.name.replace(/\(([a-zA-Z]+) Han script\)/g, '($1)');
|
|
||||||
};
|
|
||||||
|
|
||||||
const extractContributionInfoFromChanges = (
|
const extractContributionInfoFromChanges = (
|
||||||
changes: WeblateChangeResult[],
|
changes: WeblateChangeResult[],
|
||||||
@@ -305,17 +185,6 @@ const getKnownContributorsByLanguage = async (): Promise<Record<string, string[]
|
|||||||
return contributorByLanguage;
|
return contributorByLanguage;
|
||||||
};
|
};
|
||||||
|
|
||||||
const dateRegex = /[0-9]{4}-[0-9]{2}-[0-9]{2}/g;
|
|
||||||
const isValidIS08601Date = (date: string): boolean => !!date.match(dateRegex);
|
|
||||||
|
|
||||||
export const validateWeblateDates = (afterDate: string, beforeDate: string) => {
|
|
||||||
if (!isValidIS08601Date(afterDate) || !isValidIS08601Date(beforeDate)) {
|
|
||||||
throw new Error(
|
|
||||||
`The passed timestamps are not properly formatted. They have to match "${dateRegex}" (e.g. 2024-05-11)`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createTranslationChangelog = async (
|
export const createTranslationChangelog = async (
|
||||||
afterDate: string,
|
afterDate: string,
|
||||||
beforeDate: string,
|
beforeDate: string,
|
||||||
|
|||||||
23
tools/scripts/weblate/Weblate.constants.ts
Normal file
23
tools/scripts/weblate/Weblate.constants.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { WeblateChangeActions } from '@/../tools/scripts/weblate/Weblate.types';
|
||||||
|
|
||||||
|
export const creditRelevantActions: WeblateChangeActions[] = [
|
||||||
|
WeblateChangeActions.TRANSLATION_CHANGED,
|
||||||
|
WeblateChangeActions.TRANSLATION_ADDED,
|
||||||
|
WeblateChangeActions.TRANSLATION_REVERTED,
|
||||||
|
WeblateChangeActions.TRANSLATION_UPLOADED,
|
||||||
|
WeblateChangeActions.TRANSLATION_REMOVED,
|
||||||
|
WeblateChangeActions.MARKED_FOR_EDIT,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const TRANSLATION_CHANGELOG_YARG_OPTIONS_DEFAULT = {
|
||||||
|
requiredContributionCount: 10,
|
||||||
|
keepKnownContributors: true,
|
||||||
|
};
|
||||||
80
tools/scripts/weblate/Weblate.types.ts
Normal file
80
tools/scripts/weblate/Weblate.types.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export enum WeblateChangeActions {
|
||||||
|
RESOURCE_UPDATED = 0,
|
||||||
|
TRANSLATION_COMPLETED = 1,
|
||||||
|
TRANSLATION_CHANGED = 2,
|
||||||
|
COMMENT_ADDED = 3,
|
||||||
|
SUGGESTION_ADDED = 4,
|
||||||
|
TRANSLATION_ADDED = 5,
|
||||||
|
SUGGESTION_ACCEPTED = 7,
|
||||||
|
TRANSLATION_REVERTED = 8,
|
||||||
|
TRANSLATION_UPLOADED = 9,
|
||||||
|
COMPONENT_LOCKED = 14,
|
||||||
|
COMPONENT_UNLOCKED = 15,
|
||||||
|
CHANGES_COMMITTED = 17,
|
||||||
|
CHANGES_PUSHED = 18,
|
||||||
|
REPOSITORY_RESET = 19,
|
||||||
|
REPOSITORY_MERGED = 20,
|
||||||
|
REPOSITORY_REBASED = 21,
|
||||||
|
REPOSITORY_MERGE_FAILED = 22,
|
||||||
|
REPOSITORY_REBASE_FAILED = 23,
|
||||||
|
PARSING_FAILED = 24,
|
||||||
|
TRANSLATION_REMOVED = 25,
|
||||||
|
SUGGESTION_REMOVED = 26,
|
||||||
|
MARKED_FOR_EDIT = 37,
|
||||||
|
COMPONENT_RENAMED = 42,
|
||||||
|
CONTRIBUTOR_JOINED = 45,
|
||||||
|
LANGUAGE_ADDED = 48,
|
||||||
|
COMPONENT_CREATED = 51,
|
||||||
|
ADD_ON_CONFIGURATION_CHANGED = 61,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WeblateChangeResult {
|
||||||
|
translation: string;
|
||||||
|
action: WeblateChangeActions;
|
||||||
|
action_name: keyof WeblateChangeActions;
|
||||||
|
user: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WeblateChangePayload {
|
||||||
|
next: string;
|
||||||
|
results: WeblateChangeResult[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WeblateUserPayload {
|
||||||
|
full_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WeblateLanguagePayload {
|
||||||
|
language: {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Username = string;
|
||||||
|
export type UserUrl = string;
|
||||||
|
|
||||||
|
export type LanguageName = string;
|
||||||
|
export type TranslationUrl = string;
|
||||||
|
|
||||||
|
export type UsernameByUserUrl = Record<UserUrl, Username>;
|
||||||
|
export type LanguageNameByTranslationUrl = Record<TranslationUrl, LanguageName>;
|
||||||
|
export type UserUrlsByTranslationUrl = Record<TranslationUrl, UserUrl[]>;
|
||||||
|
export type ActionByTranslationUrlByUserUrl = Record<
|
||||||
|
UserUrl,
|
||||||
|
Record<TranslationUrl, { count: number; actions: Record<WeblateChangeActions, number> }>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export interface Contributor {
|
||||||
|
username: Username;
|
||||||
|
contributionCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ContributionsByLanguage = Record<LanguageName, Contributor[]>;
|
||||||
67
tools/scripts/weblate/Weblate.utils.ts
Normal file
67
tools/scripts/weblate/Weblate.utils.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
WeblateChangePayload,
|
||||||
|
WeblateChangeResult,
|
||||||
|
WeblateLanguagePayload,
|
||||||
|
WeblateUserPayload,
|
||||||
|
} from '@/../tools/scripts/weblate/Weblate.types.ts';
|
||||||
|
|
||||||
|
export const fetchData = async <T = any>(url: string, authToken: string): Promise<T> => {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Token ${authToken}`,
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Weblate request failed with status ${response.status} - ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchWeblateChanges = async (
|
||||||
|
url: string,
|
||||||
|
authToken: string,
|
||||||
|
weblateChangeResults: WeblateChangeResult[] = [],
|
||||||
|
): Promise<WeblateChangeResult[]> => {
|
||||||
|
const weblateChangePage = await fetchData<WeblateChangePayload>(url, authToken);
|
||||||
|
|
||||||
|
if (!weblateChangePage.next) {
|
||||||
|
return [...weblateChangePage.results, ...weblateChangeResults];
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetchWeblateChanges(weblateChangePage.next, authToken, [
|
||||||
|
...weblateChangePage.results,
|
||||||
|
...weblateChangeResults,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUsername = async (url: string, authToken: string): Promise<string> => {
|
||||||
|
const userPayload = await fetchData<WeblateUserPayload>(url, authToken);
|
||||||
|
return userPayload.full_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLanguageName = async (url: string, authToken: string): Promise<string> => {
|
||||||
|
const languagePayload = await fetchData<WeblateLanguagePayload>(url, authToken);
|
||||||
|
return languagePayload.language.name.replace(/\(([a-zA-Z]+) Han script\)/g, '($1)');
|
||||||
|
};
|
||||||
|
|
||||||
|
const dateRegex = /[0-9]{4}-[0-9]{2}-[0-9]{2}/g;
|
||||||
|
const isValidIS08601Date = (date: string): boolean => !!date.match(dateRegex);
|
||||||
|
export const validateWeblateDates = (afterDate: string, beforeDate: string) => {
|
||||||
|
if (!isValidIS08601Date(afterDate) || !isValidIS08601Date(beforeDate)) {
|
||||||
|
throw new Error(
|
||||||
|
`The passed timestamps are not properly formatted. They have to match "${dateRegex}" (e.g. 2024-05-11)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user