Skip to content

Commit 8333395

Browse files
author
埃博拉酱
committed
Merge branch 'main' of https://github.com/Acode-Foundation/Acode into feat/terminal-install-recovery
# Conflicts: # src/settings/terminalSettings.js
2 parents 62cf100 + 0470cf7 commit 8333395

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+12185
-1980
lines changed
Lines changed: 108 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,125 @@
11
name: community-release-notifier
2+
23
on:
34
release:
45
types: [ released ]
56
workflow_call:
67
inputs:
7-
tag_name:
8-
required: true
9-
description: "Release tag_name"
10-
type: 'string'
11-
url:
12-
required: true
13-
description: "release URL"
14-
type: 'string'
15-
body:
16-
required: true
17-
description: "Release Body"
18-
type: 'string'
19-
default: ''
8+
tag_name:
9+
required: true
10+
description: "Release tag_name"
11+
type: 'string'
12+
url:
13+
required: true
14+
description: "release URL"
15+
type: 'string'
16+
body:
17+
required: true
18+
description: "Release Body"
19+
type: 'string'
20+
default: ''
2021
secrets:
2122
DISCORD_WEBHOOK_RELEASE_NOTES:
2223
description: 'Discord Webhook for Notifying Releases to Discord'
2324
required: true
25+
TELEGRAM_BOT_TOKEN:
26+
description: 'Telegram Bot Token'
27+
required: true
28+
TELEGRAM_CHAT_ID:
29+
description: 'Telegram Chat ID (group/channel/supergroup)'
30+
required: true
31+
TELEGRAM_MESSAGE_THREAD_ID:
32+
description: 'Topic / message_thread_id for Telegram forum/topic'
33+
required: true
2434

2535
jobs:
26-
discord-release:
36+
notify:
2737
if: github.repository_owner == 'Acode-Foundation'
2838
runs-on: ubuntu-latest
2939
steps:
30-
- name: Get Release Content
31-
id: get-release-content
32-
uses: 2428392/gh-truncate-string-action@b3ff790d21cf42af3ca7579146eedb93c8fb0757 # v1.4.1
33-
with:
34-
maxLength: 2000
35-
stringToTruncate: |
36-
📢 Acode [${{ github.event.release.tag_name || inputs.tag_name }}](<${{ github.event.release.url || inputs.url }}>) was just Released 🎉!
40+
- name: Prepare release variables
41+
id: vars
42+
env:
43+
INPUT_TAG: ${{ github.event.release.tag_name || inputs.tag_name }}
44+
INPUT_URL: ${{ github.event.release.url || inputs.url }}
45+
INPUT_BODY: ${{ github.event.release.body || inputs.body }}
46+
run: |
47+
TAG="$INPUT_TAG"
48+
URL="$INPUT_URL"
49+
50+
# Generate a random delimiter (hex string, safe and collision-resistant)
51+
DELIMITER=$(openssl rand -hex 16 || head -c 16 /dev/urandom | xxd -p -c 16)
3752
38-
${{ github.event.release.body || inputs.body }}
53+
# Escape problematic characters for MarkdownV2 (very conservative escaping)
54+
# We escape: _ * [ ] ( ) ~ ` > # + - = | { } . ! \
55+
BODY_SAFE=$(printf '%s' "$INPUT_BODY" | \
56+
sed 's/[_*[\]()~`>#+=|{}.!\\-]/\\&/g')
57+
TAG_SAFE=$(printf '%s' "$TAG" | sed 's/[_*[\]()~`>#+=|{}.!\\-]/\\&/g')
58+
59+
if [[ "$TAG" == *"-nightly"* ]]; then
60+
SUFFIX=" \\(Nightly Release\\)"
61+
SUFFIXPLAIN=" (Nightly Release)"
62+
else
63+
SUFFIX=""
64+
SUFFIXPLAIN=""
65+
fi
66+
67+
# Announcement line — also escape for safety
68+
ANNOUNCE_SAFE="📢 Acode [$TAG_SAFE]($URL) was just Released 🎉${SUFFIX}\\!"
69+
70+
echo "announce=$ANNOUNCE_SAFE" >> $GITHUB_OUTPUT
71+
{
72+
echo "body_safe<<$DELIMITER"
73+
printf '%s\n' "$BODY_SAFE"
74+
echo "$DELIMITER"
75+
} >> $GITHUB_OUTPUT
76+
77+
# Plain (MD) Announcement for Discord
78+
ANNOUNCE_PLAIN="📢 Acode [$TAG]($URL) was just Released 🎉${SUFFIXPLAIN}!"
79+
echo "announce_plain=$ANNOUNCE_PLAIN" >> $GITHUB_OUTPUT
80+
{
81+
echo "body_plain<<$DELIMITER"
82+
printf '%s\n' "$INPUT_BODY"
83+
echo "$DELIMITER"
84+
} >> $GITHUB_OUTPUT
85+
86+
# ────────────────────────────────────────────────
87+
# Truncate for Discord
88+
# ────────────────────────────────────────────────
89+
- name: Truncate message for Discord
90+
id: truncate-discord
91+
uses: 2428392/gh-truncate-string-action@b3ff790d21cf42af3ca7579146eedb93c8fb0757 # v1.4.1
92+
with:
93+
maxLength: 2000
94+
stringToTruncate: |
95+
${{ steps.vars.outputs.announce_plain }}
96+
97+
${{ steps.vars.outputs.body_plain }}
98+
99+
# ────────────────────────────────────────────────
100+
# Discord notification
101+
# ────────────────────────────────────────────────
102+
- name: Discord Webhook (Publishing)
103+
uses: tsickert/discord-webhook@c840d45a03a323fbc3f7507ac7769dbd91bfb164 # v5.3.0
104+
with:
105+
webhook-url: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }}
106+
content: ${{ steps.truncate-discord.outputs.string }}
107+
108+
# ────────────────────────────────────────────────
109+
# Telegram notification — MarkdownV2 + no link preview
110+
# ────────────────────────────────────────────────
111+
- name: Send to Telegram
112+
#if: ${{ secrets.TELEGRAM_BOT_TOKEN != '' && secrets.TELEGRAM_CHAT_ID != '' && secrets.TELEGRAM_MESSAGE_THREAD_ID != '' }}
113+
uses: Salmansha08/telegram-github-action@17c9ce6b4210d2659dca29d34028b02fa29d70ad # or newer tag if available
114+
with:
115+
to: ${{ secrets.TELEGRAM_CHAT_ID }}
116+
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
117+
message: |
118+
${{ steps.vars.outputs.announce }}
39119
40-
- name: Discord Webhook Action (Publishing)
41-
uses: tsickert/discord-webhook@c840d45a03a323fbc3f7507ac7769dbd91bfb164 # v5.3.0
42-
with:
43-
webhook-url: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }}
44-
content: ${{ steps.get-release-content.outputs.string }}
120+
${{ steps.vars.outputs.body_safe }}
121+
parse_mode: MarkdownV2
122+
disable_web_page_preview: true
123+
# Only needed for topic-enabled supergroups/channels
124+
message_thread_id: ${{ secrets.TELEGRAM_MESSAGE_THREAD_ID }}
125+
continue-on-error: true

.github/workflows/nightly-build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,6 @@
283283
body: ${{ needs.build.outputs.RELEASE_NOTES }}
284284
secrets:
285285
DISCORD_WEBHOOK_RELEASE_NOTES: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }}
286+
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
287+
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
288+
TELEGRAM_MESSAGE_THREAD_ID: ${{ secrets.TELEGRAM_MESSAGE_THREAD_ID }}

package-lock.json

Lines changed: 7 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
"cordova": "13.0.0",
152152
"core-js": "^3.47.0",
153153
"dayjs": "^1.11.19",
154-
"dompurify": "^3.3.0",
154+
"dompurify": "^3.3.2",
155155
"escape-string-regexp": "^5.0.0",
156156
"esprima": "^4.0.1",
157157
"filesize": "^11.0.13",

src/cm/lsp/api.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { defineBundle, defineServer, installers } from "./providerUtils";
2+
import {
3+
getServerBundle,
4+
listServerBundles,
5+
registerServerBundle,
6+
unregisterServerBundle,
7+
} from "./serverCatalog";
8+
import {
9+
getServer,
10+
getServersForLanguage,
11+
listServers,
12+
onRegistryChange,
13+
type RegisterServerOptions,
14+
registerServer,
15+
type ServerUpdater,
16+
unregisterServer,
17+
updateServer,
18+
} from "./serverRegistry";
19+
import type {
20+
LspServerBundle,
21+
LspServerDefinition,
22+
LspServerManifest,
23+
} from "./types";
24+
25+
export { defineBundle, defineServer, installers };
26+
27+
export type LspRegistrationEntry = LspServerManifest | LspServerBundle;
28+
29+
function isBundleEntry(entry: LspRegistrationEntry): entry is LspServerBundle {
30+
return typeof (entry as LspServerBundle)?.getServers === "function";
31+
}
32+
33+
export function register(
34+
entry: LspRegistrationEntry,
35+
options?: RegisterServerOptions & { replace?: boolean },
36+
): LspServerDefinition | LspServerBundle {
37+
if (isBundleEntry(entry)) {
38+
return registerServerBundle(entry, options);
39+
}
40+
41+
return registerServer(entry, options);
42+
}
43+
44+
export function upsert(
45+
entry: LspRegistrationEntry,
46+
): LspServerDefinition | LspServerBundle {
47+
return register(entry, { replace: true });
48+
}
49+
50+
export const servers = {
51+
get(id: string): LspServerDefinition | null {
52+
return getServer(id);
53+
},
54+
list(): LspServerDefinition[] {
55+
return listServers();
56+
},
57+
listForLanguage(
58+
languageId: string,
59+
options?: { includeDisabled?: boolean },
60+
): LspServerDefinition[] {
61+
return getServersForLanguage(languageId, options);
62+
},
63+
update(id: string, updater: ServerUpdater): LspServerDefinition | null {
64+
return updateServer(id, updater);
65+
},
66+
unregister(id: string): boolean {
67+
return unregisterServer(id);
68+
},
69+
onChange(listener: Parameters<typeof onRegistryChange>[0]): () => void {
70+
return onRegistryChange(listener);
71+
},
72+
};
73+
74+
export const bundles = {
75+
list(): LspServerBundle[] {
76+
return listServerBundles();
77+
},
78+
getForServer(id: string): LspServerBundle | null {
79+
return getServerBundle(id);
80+
},
81+
unregister(id: string): boolean {
82+
return unregisterServerBundle(id);
83+
},
84+
};
85+
86+
const lspApi = {
87+
defineServer,
88+
defineBundle,
89+
register,
90+
upsert,
91+
installers,
92+
servers,
93+
bundles,
94+
};
95+
96+
export default lspApi;

src/cm/lsp/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
export {
2+
bundles,
3+
default as lspApi,
4+
defineBundle,
5+
defineServer,
6+
installers,
7+
register,
8+
servers,
9+
upsert,
10+
} from "./api";
111
export { default as clientManager, LspClientManager } from "./clientManager";
212
export type { CodeActionItem } from "./codeActions";
313
export {

src/cm/lsp/installRuntime.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
function getExecutor(): Executor {
2+
const executor = (globalThis as unknown as { Executor?: Executor }).Executor;
3+
if (!executor) {
4+
throw new Error("Executor plugin is not available");
5+
}
6+
return executor;
7+
}
8+
9+
function getBackgroundExecutor(): Executor {
10+
const executor = getExecutor();
11+
return executor.BackgroundExecutor ?? executor;
12+
}
13+
14+
export function quoteArg(value: unknown): string {
15+
const str = String(value ?? "");
16+
if (!str.length) return "''";
17+
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(str)) return str;
18+
return `'${str.replace(/'/g, "'\\''")}'`;
19+
}
20+
21+
export function formatCommand(
22+
command: string | string[] | null | undefined,
23+
): string {
24+
if (Array.isArray(command)) {
25+
return command.map((part) => quoteArg(part)).join(" ");
26+
}
27+
if (typeof command === "string") {
28+
return command.trim();
29+
}
30+
return "";
31+
}
32+
33+
function wrapShellCommand(command: string): string {
34+
const script = command.trim();
35+
return `sh -lc ${quoteArg(`set -e\n${script}`)}`;
36+
}
37+
38+
export async function runQuickCommand(command: string): Promise<string> {
39+
const wrapped = wrapShellCommand(command);
40+
return getBackgroundExecutor().execute(wrapped, true);
41+
}
42+
43+
export async function runForegroundCommand(command: string): Promise<string> {
44+
const wrapped = wrapShellCommand(command);
45+
return getExecutor().execute(wrapped, true);
46+
}

0 commit comments

Comments
 (0)