feat:移除了弹窗,服务器添加sls
This commit is contained in:
113
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
113
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
name: 🐛 错误报告
|
||||
description: 报告 Axolotl Launcher 中可以复现的问题
|
||||
title: '[错误] '
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢你帮助改进 Axolotl Launcher。请尽量完整地填写以下信息,以便我们定位问题。
|
||||
上传日志或截图前,请移除账号、访问令牌、设备路径等隐私信息。
|
||||
|
||||
- type: checkboxes
|
||||
id: confirmations
|
||||
attributes:
|
||||
label: 提交前确认
|
||||
options:
|
||||
- label: 我已搜索[现有 Issues](https://github.com/Mystic-Stars/Axolotl/issues),没有发现相同问题
|
||||
required: true
|
||||
- label: 我正在使用最新版本的 Axolotl Launcher,或已确认该问题在最新版本中仍然存在
|
||||
required: true
|
||||
- label: 我已从日志、截图和描述中移除账号、访问令牌等敏感信息
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Axolotl Launcher 版本
|
||||
description: 可在启动器设置或“关于”页面中查看,例如 1.2.3。
|
||||
placeholder: 例如 1.2.3
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: operating_system
|
||||
attributes:
|
||||
label: 操作系统
|
||||
options:
|
||||
- Windows 10/11(x64)
|
||||
- macOS(Apple Silicon)
|
||||
- macOS(Intel)
|
||||
- Linux(x64)
|
||||
- 其他
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: system_details
|
||||
attributes:
|
||||
label: 系统详细信息
|
||||
description: 如果选择 Linux 或“其他”,请填写发行版、桌面环境或架构等信息。
|
||||
placeholder: 例如 Ubuntu 24.04,GNOME,x64
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: dropdown
|
||||
id: installation_source
|
||||
attributes:
|
||||
label: 安装来源
|
||||
options:
|
||||
- GitHub Releases 安装包
|
||||
- 启动器内自动更新
|
||||
- 软件包管理器或第三方分发渠道
|
||||
- 本地开发或源码构建
|
||||
- 其他
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: 问题描述
|
||||
description: 请清楚说明发生了什么,以及问题出现的频率。
|
||||
placeholder: 描述实际发生的现象……
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: 复现步骤
|
||||
description: 请从启动 Axolotl 开始,按顺序列出能够触发问题的操作。
|
||||
placeholder: |
|
||||
1. 打开……
|
||||
2. 点击……
|
||||
3. 选择……
|
||||
4. 出现……
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected_behavior
|
||||
attributes:
|
||||
label: 预期行为
|
||||
description: 说明你原本期望看到的结果。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: 相关日志
|
||||
description: 请粘贴与问题相关的日志片段,并确保其中不含敏感信息。没有日志时可以留空。
|
||||
render: shell
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: additional_context
|
||||
attributes:
|
||||
label: 截图与补充信息
|
||||
description: 可直接拖入截图,并补充可能有助于定位问题的信息。
|
||||
validations:
|
||||
required: false
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
blank_issues_enabled: true
|
||||
52
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
52
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
name: 💡 功能建议
|
||||
description: 为 Axolotl Launcher 提议新功能或体验改进
|
||||
title: '[建议] '
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢你为 Axolotl Launcher 提出建议。请重点描述使用场景和希望解决的问题。
|
||||
|
||||
- type: checkboxes
|
||||
id: confirmations
|
||||
attributes:
|
||||
label: 提交前确认
|
||||
options:
|
||||
- label: 我已搜索[现有 Issues](https://github.com/Mystic-Stars/Axolotl/issues),没有发现相同或高度相似的建议
|
||||
required: true
|
||||
- label: 这项建议与 Axolotl Launcher 桌面应用直接相关
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: 使用场景与现有问题
|
||||
description: 请说明你在什么情况下遇到什么困难,以及该问题会带来哪些影响。
|
||||
placeholder: 当我……时,目前需要……,这会导致……
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: proposed_solution
|
||||
attributes:
|
||||
label: 期望的解决方案
|
||||
description: 请描述你希望 Axolotl 如何改进,以及理想的使用流程。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: 考虑过的替代方案
|
||||
description: 如果你尝试过临时解决方法或考虑过其他设计,请在这里说明。
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: additional_context
|
||||
attributes:
|
||||
label: 补充信息
|
||||
description: 可添加示意图、截图、相关链接或其他背景信息。
|
||||
validations:
|
||||
required: false
|
||||
18
.github/actions/replace-gradle-mirror/action.yml
vendored
Normal file
18
.github/actions/replace-gradle-mirror/action.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: Replace Gradle mirror with official distribution URL
|
||||
description: Replaces the Tencent Cloud Gradle mirror URL with the official Gradle distribution URL in gradle-wrapper.properties
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Replace (Linux / macOS)
|
||||
if: runner.os != 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i.bak 's|https\\://mirrors\.cloud\.tencent\.com/gradle/|https\\://services.gradle.org/distributions/|' packages/app-lib/java/gradle/wrapper/gradle-wrapper.properties
|
||||
rm -f packages/app-lib/java/gradle/wrapper/gradle-wrapper.properties.bak
|
||||
|
||||
- name: Replace (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
(Get-Content packages/app-lib/java/gradle/wrapper/gradle-wrapper.properties) -replace 'https\\://mirrors\.cloud\.tencent\.com/gradle/', 'https\://services.gradle.org/distributions/' | Set-Content packages/app-lib/java/gradle/wrapper/gradle-wrapper.properties
|
||||
BIN
.github/assets/api_cover.png
vendored
Normal file
BIN
.github/assets/api_cover.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
BIN
.github/assets/app_cover.png
vendored
Normal file
BIN
.github/assets/app_cover.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
.github/assets/codeflow-logo.png
vendored
Normal file
BIN
.github/assets/codeflow-logo.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
.github/assets/monorepo_cover.png
vendored
Normal file
BIN
.github/assets/monorepo_cover.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 262 KiB |
BIN
.github/assets/web_cover.png
vendored
Normal file
BIN
.github/assets/web_cover.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
86
.github/instructions/i18n-convert.instructions.md
vendored
Normal file
86
.github/instructions/i18n-convert.instructions.md
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
applyTo: '**/*.vue'
|
||||
---
|
||||
|
||||
You are given a Nuxt/Vue single-file component (.vue). Your task is to convert every hard-coded natural-language string in the <template> into our localization system using vue-i18n with utilities from `@modrinth/ui`.
|
||||
|
||||
Please follow these rules precisely:
|
||||
|
||||
1. Identify translatable strings
|
||||
|
||||
- Scan the <template> for all user-visible strings (inner text, alt attributes, placeholders, button labels, etc.). Do not extract dynamic expressions (like {{ user.name }}) or HTML tags. Only extract static human-readable text.
|
||||
- There may be strings within the <script> block, e.g dropdown option labels, notifications etc.
|
||||
|
||||
2. Create message definitions
|
||||
|
||||
- In the <script setup> block, import `defineMessage` or `defineMessages` from `@modrinth/ui`.
|
||||
- For each extracted string, define a message with a unique `id` (use a descriptive prefix based on the component path, e.g. `auth.welcome.long-title`) and a `defaultMessage` equal to the original English string.
|
||||
Example:
|
||||
const messages = defineMessages({
|
||||
welcomeTitle: { id: 'auth.welcome.title', defaultMessage: 'Welcome' },
|
||||
welcomeDescription: { id: 'auth.welcome.description', defaultMessage: 'You're now part of the community…' },
|
||||
})
|
||||
|
||||
3. Handle variables and ICU formats
|
||||
|
||||
- Replace dynamic parts with ICU placeholders: "Hello, ${user.name}!" → `{name}` and defaultMessage: 'Hello, {name}!'
|
||||
- For numbers/dates/times, use ICU options (e.g., currency): `{price, number, ::currency/USD}`
|
||||
- For plurals/selects, use ICU: `'{count, plural, one {# message} other {# messages}}'`
|
||||
|
||||
4. Rich-text messages (links/markup)
|
||||
|
||||
- In `defaultMessage`, wrap link/markup ranges with tags, e.g.:
|
||||
"By creating an account, you agree to our <terms-link>Terms</terms-link> and <privacy-link>Privacy Policy</privacy-link>."
|
||||
- Render rich-text messages with `<IntlFormatted>` from `@modrinth/ui` using named slots:
|
||||
<IntlFormatted :message-id="messages.tosLabel">
|
||||
<template #terms-link="{ children }">
|
||||
<NuxtLink to="/terms">
|
||||
<component :is="() => children" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<template #privacy-link="{ children }">
|
||||
<NuxtLink to="/privacy">
|
||||
<component :is="() => children" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
- For simple emphasis: `'Welcome to <strong>Modrinth</strong>!'` with a slot:
|
||||
<template #strong="{ children }">
|
||||
<strong><component :is="() => children" /></strong>
|
||||
</template>
|
||||
- For more complex child handling, use `normalizeChildren` from `@modrinth/ui`:
|
||||
<template #bold="{ children }">
|
||||
<strong><component :is="() => normalizeChildren(children)" /></strong>
|
||||
</template>
|
||||
|
||||
5. Formatting in templates
|
||||
|
||||
- Import and use `useVIntl()` from `@modrinth/ui`; prefer `formatMessage` for simple strings:
|
||||
`const { formatMessage } = useVIntl()`
|
||||
`<button>{{ formatMessage(messages.welcomeTitle) }}</button>`
|
||||
- Pass variables as a second argument:
|
||||
`{{ formatMessage(messages.greeting, { name: user.name }) }}`
|
||||
|
||||
6. Naming conventions and id stability
|
||||
|
||||
- Make `id`s descriptive and stable (e.g., `error.generic.default.title`). Group related messages with `defineMessages`.
|
||||
|
||||
7. Avoid Vue/ICU delimiter collisions
|
||||
|
||||
- If an ICU placeholder would end right before `}}` in a Vue template, insert a space so it becomes `} }` to avoid parsing issues.
|
||||
|
||||
8. Update imports and remove literals
|
||||
|
||||
- Ensure imports from `@modrinth/ui` are present: `defineMessage`/`defineMessages`, `useVIntl`, `IntlFormatted`, and optionally `normalizeChildren`.
|
||||
- Replace all hard-coded strings with `formatMessage(...)` or `<IntlFormatted>` and remove the literals.
|
||||
|
||||
9. Preserve functionality
|
||||
|
||||
- Do not change logic, layout, reactivity, or bindings—only refactor strings into i18n.
|
||||
|
||||
Use existing patterns from our codebase:
|
||||
|
||||
- Variables/plurals: see `apps/app-frontend/src/components/home/HomeDailyChallenge.vue`
|
||||
- Rich-text link tags: see `apps/website/src/pages/index.vue`
|
||||
|
||||
When you finish, there should be no hard-coded English strings left in the template—everything comes from `formatMessage` or `<IntlFormatted>`.
|
||||
171
.github/merge-queue-ci-skipper/action.yml
vendored
Normal file
171
.github/merge-queue-ci-skipper/action.yml
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2024 CARIAD SE
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
name: 'Merge Queue CI Check Skipper'
|
||||
description: 'Outputs `skip-check` as `true` if this is running as part of merge queue checks and the same checks have already been executed in the PR itself.'
|
||||
inputs:
|
||||
secret:
|
||||
description: 'Optional GitHub Secret that can access branch protection rules using the administration:read permission'
|
||||
required: false
|
||||
outputs:
|
||||
skip-check:
|
||||
description: 'Skip Check (boolean)'
|
||||
value: ${{ steps.passed-checks.outputs.can-skip-checks }}
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Extract PR Number and Commit ID
|
||||
id: extract-pr-info
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const githubRef = process.env.GITHUB_REF;
|
||||
const regex = /^refs\/heads\/gh-readonly-queue\/([a-zA-Z0-9.\-_\/]+)\/pr-(\d+)-([a-f0-9]+)$/;
|
||||
|
||||
if (regex.test(githubRef)) {
|
||||
const [, targetBranchName, prNumber, commitId] = githubRef.match(regex);
|
||||
core.setOutput('targetBranchName', targetBranchName);
|
||||
core.setOutput('prNumber', prNumber);
|
||||
core.setOutput('commitId', commitId);
|
||||
} else {
|
||||
console.log(`GITHUB_REF is not a merge queue ref, setting CAN_SKIP_CHECKS to false: ${githubRef}`);
|
||||
core.exportVariable('CAN_SKIP_CHECKS', 'false');
|
||||
}
|
||||
|
||||
- name: Print PR Number and Target Commit ID
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Target Branch Name: ${{ steps.extract-pr-info.outputs.targetBranchName }}"
|
||||
echo "PR Number: ${{ steps.extract-pr-info.outputs.prNumber }}"
|
||||
echo "Target Commit ID: ${{ steps.extract-pr-info.outputs.commitId }}"
|
||||
|
||||
- name: Check if merge queue entry was enqueued as head of the queue
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
targetBranchHead=$(git rev-parse origin/${{ steps.extract-pr-info.outputs.targetBranchName }})
|
||||
if [[ "$targetBranchHead" != "${{ steps.extract-pr-info.outputs.commitId }}" ]]; then
|
||||
echo "'${{ steps.extract-pr-info.outputs.targetBranchName }}' branch commit ID does not match PR commit ID. This Merge Queue run was not head of the queue when it was enqueued. Setting CAN_SKIP_CHECKS to false."
|
||||
echo "CAN_SKIP_CHECKS=false" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "This merge queue entry is targeting '${{ steps.extract-pr-info.outputs.targetBranchName }}' directly."
|
||||
fi
|
||||
|
||||
- name: Get PR Branch
|
||||
id: get-pr-branch
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
prNumber=${{ steps.extract-pr-info.outputs.prNumber }}
|
||||
branchName=$(gh pr view ${prNumber} --json headRefName -q '.headRefName')
|
||||
echo "prBranch=$branchName" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Print PR Branch
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "PR Branch: ${{ steps.get-pr-branch.outputs.prBranch }}"
|
||||
|
||||
- name: Check if PR branch contains the Merge Queue target commit ID
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
# Get the branch name from previous steps
|
||||
branch_name="origin/${{ steps.get-pr-branch.outputs.prBranch }}"
|
||||
commit_id="${{ steps.extract-pr-info.outputs.commitId }}"
|
||||
|
||||
# Check if the branch history contains the commit
|
||||
if git branch -r --contains "$commit_id" | grep -q "$branch_name"; then
|
||||
echo "Branch '$branch_name' contains commit '$commit_id'. It is up to date with ${{ steps.extract-pr-info.outputs.targetBranchName }}."
|
||||
else
|
||||
echo "Branch '$branch_name' does not contain commit '$commit_id'. It is outdated. Setting CAN_SKIP_CHECKS to false."
|
||||
echo "CAN_SKIP_CHECKS=false" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Compare PR Branch with Current Branch
|
||||
if: env.CAN_SKIP_CHECKS != 'false'
|
||||
shell: bash
|
||||
run: |
|
||||
if git diff --quiet "origin/${{ steps.get-pr-branch.outputs.prBranch }}"; then
|
||||
echo "No differences found. PR branch is identical with this merge queue branch."
|
||||
else
|
||||
echo "Differences detected. PR branch has been updated after PR was added to merge queue. Setting CAN_SKIP_CHECKS to false."
|
||||
echo "CAN_SKIP_CHECKS=false" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Compute/publish skip result
|
||||
id: passed-checks
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SECRET: ${{ inputs.secret }}
|
||||
with:
|
||||
github-token: ${{ inputs.secret != '' && inputs.secret || github.token }}
|
||||
script: |
|
||||
if (process.env.CAN_SKIP_CHECKS == "false") {
|
||||
console.log("Setting CAN_SKIP_CHECKS to false");
|
||||
core.setOutput("can-skip-checks", false);
|
||||
return;
|
||||
}
|
||||
|
||||
const secretProvided = !!process.env.SECRET;
|
||||
if (!secretProvided) {
|
||||
console.log("secret input not set, assuming all checks have passed. Ensure 'Require status checks to pass before merging' is enabled, or provide a secret with administration:read.");
|
||||
core.setOutput("can-skip-checks", true);
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: branchProtection } = await github.rest.repos.getBranchProtection({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
branch: "${{ steps.extract-pr-info.outputs.targetBranchName }}",
|
||||
});
|
||||
const requiredCheckNames = branchProtection.required_status_checks.contexts;
|
||||
console.log(`requiredCheckNames = ${requiredCheckNames}`);
|
||||
|
||||
const { data: checks } = await github.rest.checks.listForRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: "refs/heads/${{ steps.get-pr-branch.outputs.prBranch }}",
|
||||
});
|
||||
|
||||
console.log(`checks.check_runs = ${checks.check_runs.map(check => `${check.status},${check.conclusion},${check.name};`)}`);
|
||||
const nonSuccessfulChecks = checks.check_runs.filter(check => check.status !== "completed" || check.conclusion !== "success");
|
||||
const nonSuccessfulCheckNames = nonSuccessfulChecks.map(check => check.name);
|
||||
console.log(`nonSuccessfulCheckNames = ${nonSuccessfulCheckNames}`);
|
||||
|
||||
const missingChecks = requiredCheckNames.filter(checkName => nonSuccessfulCheckNames.includes(checkName));
|
||||
|
||||
if (missingChecks.length > 0) {
|
||||
console.log(`Required checks not passed, cannot skip merge queue checks. Setting CAN_SKIP_CHECKS to false. Missing checks: ${missingChecks.join(', ')}`);
|
||||
core.setOutput("can-skip-checks", false);
|
||||
} else {
|
||||
console.log("No missing checks. Setting CAN_SKIP_CHECKS to true.");
|
||||
core.setOutput("can-skip-checks", true);
|
||||
}
|
||||
4
.github/templates/crowdin-pr.md
vendored
Normal file
4
.github/templates/crowdin-pr.md
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
This pull request is created according to the `.github/workflows/i18n-pull.yml` file.
|
||||
|
||||
- 🌐 [Contribute to translations on Crowdin](https://translate.modrinth.com/)
|
||||
- 🔄 [Dispatch this workflow again to update this PR](https://github.com/Modrinth/code/actions/workflows/i18n-pull.yml)
|
||||
3
.github/workflows/COPYING.md
vendored
Normal file
3
.github/workflows/COPYING.md
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Copying
|
||||
|
||||
Modrinth's Github workflows are licensed under the MIT License, which is provided in the file [LICENSE](./LICENSE).
|
||||
7
.github/workflows/LICENSE
vendored
Normal file
7
.github/workflows/LICENSE
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
Copyright 2025 Rinth, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
130
.github/workflows/axolotl-ci.yml
vendored
Normal file
130
.github/workflows/axolotl-ci.yml
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
name: Axolotl desktop CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- 'apps/telemetry-dashboard/**'
|
||||
- 'apps/telemetry-worker/**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'apps/telemetry-dashboard/**'
|
||||
- 'apps/telemetry-worker/**'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
guardrails:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: pnpm/action-setup@008330803749db0355799c700092d9a85fd074e9 # v6.0.9
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: pnpm
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- name: Check immutable database migrations
|
||||
env:
|
||||
MIGRATION_BASE_REF: ${{ github.event.pull_request.base.sha || github.event.before }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
node scripts/axolotl/check-migrations.mjs --base "${MIGRATION_BASE_REF:-HEAD^}"
|
||||
node scripts/axolotl/check-migrations.mjs --release
|
||||
- run: node scripts/axolotl/brand-guard.mjs
|
||||
- run: node scripts/axolotl/i18n-check.mjs
|
||||
- run: cargo fmt --all --check
|
||||
|
||||
desktop:
|
||||
needs: guardrails
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, macos-latest, ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
SQLX_OFFLINE: true
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: pnpm/action-setup@008330803749db0355799c700092d9a85fd074e9 # v6.0.9
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: pnpm
|
||||
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
||||
- name: Set up Rust compiler cache
|
||||
if: runner.os == 'Windows'
|
||||
uses: mozilla-actions/sccache-action@2df7dbab909c49ab7d3382d05da469f3f975c2d6 # v0.0.7
|
||||
- name: Check Rust compiler cache availability
|
||||
id: sccache
|
||||
if: runner.os == 'Windows'
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: 'true'
|
||||
run: ${SCCACHE_PATH} rustc -vV
|
||||
- name: Enable Rust compiler cache
|
||||
if: runner.os == 'Windows' && steps.sccache.outcome == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'RUSTC_WRAPPER=sccache' >> "$GITHUB_ENV"
|
||||
echo 'SCCACHE_GHA_ENABLED=true' >> "$GITHUB_ENV"
|
||||
echo 'SCCACHE_CACHE_SIZE=4G' >> "$GITHUB_ENV"
|
||||
- if: runner.os == 'Linux'
|
||||
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- name: Install Blockbench skin editor dependencies
|
||||
run: npm ci
|
||||
working-directory: third-party/blockbench
|
||||
- uses: ./.github/actions/replace-gradle-mirror
|
||||
- run: pnpm --filter @modrinth/app-frontend build
|
||||
- name: Test version metadata writes
|
||||
run: cargo test --package theseus writing_version_info_creates_missing_parent_directory
|
||||
- run: cargo check --package theseus_gui --features updater
|
||||
- name: Build modern Windows installer
|
||||
if: runner.os == 'Windows'
|
||||
run: pnpm --filter @modrinth/app tauri build --config tauri-release.conf.json --config tauri-modern.conf.json
|
||||
- name: Verify modern Windows installer
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$installers = @(Get-ChildItem target/release/bundle/nsis -Filter '*-setup.exe' -File)
|
||||
if ($installers.Count -ne 1) {
|
||||
throw "Expected one modern NSIS installer, found $($installers.Count)"
|
||||
}
|
||||
Write-Output "Built modern installer: $($installers[0].Name)"
|
||||
- name: Build native NSIS Windows installer
|
||||
if: runner.os == 'Windows'
|
||||
run: pnpm --filter @modrinth/app tauri build
|
||||
- name: Verify native NSIS Windows installer
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$installers = @(Get-ChildItem target/release/bundle/nsis -Filter '*-setup.exe' -File)
|
||||
if ($installers.Count -ne 1) {
|
||||
throw "Expected one native NSIS installer, found $($installers.Count)"
|
||||
}
|
||||
Write-Output "Built native NSIS installer: $($installers[0].Name)"
|
||||
- name: Show Rust compiler cache statistics
|
||||
if: runner.os == 'Windows' && steps.sccache.outcome == 'success' && always()
|
||||
shell: bash
|
||||
run: ${SCCACHE_PATH} --show-stats
|
||||
|
||||
website:
|
||||
needs: guardrails
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: pnpm/action-setup@008330803749db0355799c700092d9a85fd074e9 # v6.0.9
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: pnpm
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm --filter @axolotl/website build
|
||||
400
.github/workflows/axolotl-release.yml
vendored
Normal file
400
.github/workflows/axolotl-release.yml
vendored
Normal file
@ -0,0 +1,400 @@
|
||||
name: Axolotl release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: axolotl-release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
preflight:
|
||||
name: Release preflight
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
SQLX_OFFLINE: true
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up pnpm
|
||||
uses: pnpm/action-setup@008330803749db0355799c700092d9a85fd074e9 # v6.0.9
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: pnpm
|
||||
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Install Blockbench skin editor dependencies
|
||||
run: npm ci
|
||||
working-directory: third-party/blockbench
|
||||
|
||||
- name: Run release guardrails
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
node scripts/axolotl/check-migrations.mjs --release
|
||||
node scripts/axolotl/brand-guard.mjs
|
||||
node scripts/axolotl/i18n-check.mjs
|
||||
cargo fmt --all --check
|
||||
|
||||
- name: Prepare draft release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
release_notes="${RUNNER_TEMP}/axolotl-release-notes.md"
|
||||
node scripts/axolotl/create-release-notes.mjs "${GITHUB_REF_NAME}" "$release_notes"
|
||||
existing_release="$(
|
||||
gh release list --limit 100 --json tagName \
|
||||
--jq ".[] | select(.tagName == \"${GITHUB_REF_NAME}\") | .tagName"
|
||||
)"
|
||||
if [[ -n "$existing_release" ]]; then
|
||||
echo "Release ${GITHUB_REF_NAME} already exists"
|
||||
gh release edit "${GITHUB_REF_NAME}" --notes-file "$release_notes"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
release_args=(
|
||||
--draft
|
||||
--notes-file "$release_notes"
|
||||
--title "Axolotl Launcher ${GITHUB_REF_NAME}"
|
||||
)
|
||||
if [[ "${GITHUB_REF_NAME}" == *-* ]]; then
|
||||
release_args+=(--prerelease)
|
||||
fi
|
||||
gh release create "${GITHUB_REF_NAME}" "${release_args[@]}"
|
||||
|
||||
build:
|
||||
name: Build (${{ matrix.name }})
|
||||
needs: preflight
|
||||
strategy:
|
||||
fail-fast: true
|
||||
max-parallel: 4
|
||||
matrix:
|
||||
include:
|
||||
- name: Linux x64
|
||||
runner: ubuntu-24.04
|
||||
args: --config tauri-release.conf.json --config tauri-updater.conf.json
|
||||
- name: Linux arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
args: --config tauri-release.conf.json --config tauri-updater.conf.json
|
||||
- name: Windows x64
|
||||
runner: windows-2025
|
||||
args: --config tauri-release.conf.json --config tauri-modern.conf.json --config tauri-updater.conf.json
|
||||
- name: macOS universal
|
||||
runner: macos-15
|
||||
args: --target universal-apple-darwin --config tauri-release.conf.json --config tauri-updater.conf.json
|
||||
runs-on: ${{ matrix.runner }}
|
||||
env:
|
||||
SQLX_OFFLINE: true
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up pnpm
|
||||
uses: pnpm/action-setup@008330803749db0355799c700092d9a85fd074e9 # v6.0.9
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: pnpm
|
||||
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
|
||||
|
||||
- name: Set up Rust compiler cache
|
||||
if: runner.os == 'Windows'
|
||||
uses: mozilla-actions/sccache-action@2df7dbab909c49ab7d3382d05da469f3f975c2d6 # v0.0.7
|
||||
|
||||
- name: Check Rust compiler cache availability
|
||||
id: sccache
|
||||
if: runner.os == 'Windows'
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: 'true'
|
||||
run: ${SCCACHE_PATH} rustc -vV
|
||||
|
||||
- name: Enable Rust compiler cache
|
||||
if: runner.os == 'Windows' && steps.sccache.outcome == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'RUSTC_WRAPPER=sccache' >> "$GITHUB_ENV"
|
||||
echo 'SCCACHE_GHA_ENABLED=true' >> "$GITHUB_ENV"
|
||||
echo 'SCCACHE_CACHE_SIZE=4G' >> "$GITHUB_ENV"
|
||||
|
||||
- name: Add macOS universal targets
|
||||
if: runner.os == 'macOS'
|
||||
run: rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||
|
||||
- name: Install Linux dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get -o Acquire::ForceIPv4=true update && sudo apt-get -o Acquire::ForceIPv4=true install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf xdg-utils
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Install Blockbench skin editor dependencies
|
||||
run: npm ci
|
||||
working-directory: third-party/blockbench
|
||||
|
||||
- uses: ./.github/actions/replace-gradle-mirror
|
||||
|
||||
- name: Set release version
|
||||
run: node scripts/axolotl/set-version.mjs "${{ github.ref_name }}"
|
||||
|
||||
- name: Build and upload release assets
|
||||
uses: tauri-apps/tauri-action@1ddc7b49b13c3038297360482fcb3e058764d7c9 # v0.6.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
TAURI_BUNDLER_DMG_IGNORE_CI: ${{ runner.os == 'macOS' && 'true' || '' }}
|
||||
with:
|
||||
projectPath: apps/app
|
||||
tauriScript: pnpm tauri
|
||||
args: ${{ matrix.args }}
|
||||
tagName: ${{ github.ref_name }}
|
||||
releaseName: Axolotl Launcher ${{ github.ref_name }}
|
||||
releaseDraft: true
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generateReleaseNotes: false
|
||||
includeUpdaterJson: false
|
||||
updaterJsonPreferNsis: true
|
||||
retryAttempts: 2
|
||||
|
||||
- name: Show Rust compiler cache statistics
|
||||
if: runner.os == 'Windows' && steps.sccache.outcome == 'success' && always()
|
||||
shell: bash
|
||||
run: ${SCCACHE_PATH} --show-stats
|
||||
|
||||
- name: Publish Windows distribution variants
|
||||
if: runner.os == 'Windows'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
$version = "${{ github.ref_name }}" -replace '^v', ''
|
||||
$appName = "Axolotl Launcher"
|
||||
$outDir = "target/release/bundle/nsis"
|
||||
$buildDir = "target/portable-build"
|
||||
$publishDir = Join-Path $env:RUNNER_TEMP "windows-assets"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $publishDir | Out-Null
|
||||
|
||||
$modernInstallers = @(Get-ChildItem $outDir -Filter '*-setup.exe' -File)
|
||||
if ($modernInstallers.Count -ne 1) {
|
||||
throw "Expected one modern installer, found $($modernInstallers.Count)"
|
||||
}
|
||||
|
||||
$modernName = "Axolotl_Launcher_${version}_x64_modern-setup.exe"
|
||||
$modernPath = Join-Path $publishDir $modernName
|
||||
Copy-Item -Force $modernInstallers[0].FullName $modernPath
|
||||
gh release upload "${{ github.ref_name }}" $modernPath --clobber
|
||||
|
||||
$modernSignature = "$($modernInstallers[0].FullName).sig"
|
||||
if (Test-Path $modernSignature) {
|
||||
$modernSignaturePath = "$modernPath.sig"
|
||||
Copy-Item -Force $modernSignature $modernSignaturePath
|
||||
gh release upload "${{ github.ref_name }}" $modernSignaturePath --clobber
|
||||
gh release delete-asset "${{ github.ref_name }}" "$($modernInstallers[0].Name).sig" --yes
|
||||
}
|
||||
gh release delete-asset "${{ github.ref_name }}" $modernInstallers[0].Name --yes
|
||||
|
||||
pnpm --filter @modrinth/app tauri build --config tauri-release.conf.json --config tauri-native.conf.json
|
||||
|
||||
$nativeInstallers = @(Get-ChildItem $outDir -Filter '*-setup.exe' -File)
|
||||
if ($nativeInstallers.Count -ne 1) {
|
||||
throw "Expected one native NSIS installer, found $($nativeInstallers.Count)"
|
||||
}
|
||||
|
||||
$nativeName = "Axolotl_Launcher_${version}_x64_nsis-setup.exe"
|
||||
$nativePath = Join-Path $publishDir $nativeName
|
||||
Copy-Item -Force $nativeInstallers[0].FullName $nativePath
|
||||
gh release upload "${{ github.ref_name }}" $nativePath --clobber
|
||||
|
||||
$nativeSignature = "$($nativeInstallers[0].FullName).sig"
|
||||
if (Test-Path $nativeSignature) {
|
||||
$nativeSignaturePath = "$nativePath.sig"
|
||||
Copy-Item -Force $nativeSignature $nativeSignaturePath
|
||||
gh release upload "${{ github.ref_name }}" $nativeSignaturePath --clobber
|
||||
}
|
||||
|
||||
# Reuse the binary already built by tauri-action
|
||||
$appExe = "target/release/${appName}.exe"
|
||||
if (Test-Path $appExe) {
|
||||
New-Item -ItemType Directory -Force -Path "$buildDir\Axolotl" | Out-Null
|
||||
Copy-Item -Force $appExe "$buildDir\Axolotl\"
|
||||
|
||||
# Create empty .Axolotl folder to trigger portable mode
|
||||
New-Item -ItemType Directory -Force -Path "$buildDir\Axolotl\.Axolotl" | Out-Null
|
||||
|
||||
# Create portable zip (for manual download)
|
||||
$zipName = "Axolotl_Launcher_${version}_x64_portable.zip"
|
||||
$zipPath = Join-Path $publishDir $zipName
|
||||
Compress-Archive -Path "$buildDir\Axolotl" -DestinationPath $zipPath -Force
|
||||
Write-Output "Created portable zip: $zipPath"
|
||||
|
||||
# Upload portable zip (for manual download only)
|
||||
gh release upload "${{ github.ref_name }}" "$zipPath" --clobber
|
||||
} else {
|
||||
throw "Binary not found at $appExe"
|
||||
}
|
||||
|
||||
verify-and-publish:
|
||||
name: Verify and publish
|
||||
needs: build
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: main
|
||||
|
||||
- name: Download updater signatures
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh release download "${GITHUB_REF_NAME}" --pattern '*.sig' --dir release-check/signatures
|
||||
|
||||
- name: Download release metadata
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
release_id="$(
|
||||
gh api "repos/${GITHUB_REPOSITORY}/releases" \
|
||||
--paginate \
|
||||
--jq ".[] | select(.tag_name == \"${GITHUB_REF_NAME}\") | .id"
|
||||
)"
|
||||
if [[ -z "$release_id" ]]; then
|
||||
echo "Release ${GITHUB_REF_NAME} was not found"
|
||||
exit 1
|
||||
fi
|
||||
gh api "repos/${GITHUB_REPOSITORY}/releases/${release_id}" > release-check/release.json
|
||||
|
||||
- name: Generate update manifest
|
||||
run: node scripts/axolotl/create-update-manifest.mjs release-check/release.json release-check/signatures "${GITHUB_REF_NAME}" release-check/latest.json
|
||||
|
||||
- name: Verify update manifest
|
||||
run: node scripts/axolotl/verify-update-manifest.mjs release-check/latest.json "${GITHUB_REF_NAME}"
|
||||
|
||||
- name: Upload update manifest
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh release upload "${GITHUB_REF_NAME}" release-check/latest.json --clobber
|
||||
|
||||
- name: Publish release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
if [[ "${GITHUB_REF_NAME}" == *-* ]]; then
|
||||
gh release edit "${GITHUB_REF_NAME}" --draft=false --prerelease
|
||||
else
|
||||
gh release edit "${GITHUB_REF_NAME}" --draft=false --latest
|
||||
fi
|
||||
|
||||
- name: Refresh published GitHub release metadata
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
release_id="$(
|
||||
gh api "repos/${GITHUB_REPOSITORY}/releases" \
|
||||
--paginate \
|
||||
--jq ".[] | select(.tag_name == \"${GITHUB_REF_NAME}\") | .id"
|
||||
)"
|
||||
if [[ -z "$release_id" ]]; then
|
||||
echo "Published release ${GITHUB_REF_NAME} was not found"
|
||||
exit 1
|
||||
fi
|
||||
gh api "repos/${GITHUB_REPOSITORY}/releases/${release_id}" > release-check/release.json
|
||||
|
||||
- name: Build Update Server artifact catalog
|
||||
run: |
|
||||
node scripts/axolotl/create-update-server-catalog.mjs \
|
||||
release-check/release.json \
|
||||
"${GITHUB_REF_NAME}" \
|
||||
release-check/update-server-catalog.json
|
||||
node scripts/axolotl/create-update-server-catalog.test.mjs
|
||||
|
||||
- name: Notify Update Server to publish release assets
|
||||
env:
|
||||
UPDATE_SERVER_URL: ${{ secrets.UPDATE_SERVER_URL }}
|
||||
UPDATE_SERVER_WEBHOOK_SECRET: ${{ secrets.UPDATE_SERVER_WEBHOOK_SECRET }}
|
||||
UPDATE_SERVER_FORCE_UPDATE: 'false'
|
||||
run: |
|
||||
node scripts/axolotl/publish-update-server.mjs \
|
||||
release-check/update-server-catalog.json \
|
||||
release-check/release.json \
|
||||
"${GITHUB_REF_NAME}"
|
||||
|
||||
- name: Publish website download metadata
|
||||
if: ${{ !contains(github.ref_name, '-') }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
node scripts/axolotl/create-website-release-metadata.mjs \
|
||||
release-check/release.json \
|
||||
apps/website/releases/latest.json
|
||||
node scripts/axolotl/create-website-release-catalog.mjs \
|
||||
apps/website/releases/catalog.json
|
||||
|
||||
git config user.name "Axolotl Release Bot"
|
||||
git config user.email "release@axolotl.local"
|
||||
git add apps/website/releases/latest.json apps/website/releases/catalog.json
|
||||
if git diff --cached --quiet; then
|
||||
echo "Website metadata is unchanged"
|
||||
else
|
||||
git commit -m "chore(website): update download metadata to ${GITHUB_REF_NAME} [skip ci]"
|
||||
git push origin main
|
||||
fi
|
||||
|
||||
- name: Mirror published release to CNB
|
||||
env:
|
||||
CNB_REPO_URL_HTTPS: ${{ secrets.CNB_REPO_URL_HTTPS }}
|
||||
CNB_TOKEN: ${{ secrets.CNB_TOKEN }}
|
||||
CNB_TOKEN_USER_NAME: ${{ secrets.CNB_TOKEN_USER_NAME }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ -z "${CNB_TOKEN:-}" ]]; then
|
||||
echo "CNB_TOKEN is not configured"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cnb_url="${CNB_REPO_URL_HTTPS:-https://cnb.cool/axlmc/Axolotl.git}"
|
||||
token_user="${CNB_TOKEN_USER_NAME:-cnb}"
|
||||
auth="$(printf '%s:%s' "$token_user" "$CNB_TOKEN" | base64 -w0)"
|
||||
git_cnb() {
|
||||
git -c "http.extraHeader=Authorization: Basic $auth" "$@"
|
||||
}
|
||||
|
||||
git fetch origin "refs/tags/${GITHUB_REF_NAME}:refs/tags/${GITHUB_REF_NAME}"
|
||||
git_cnb push --force "$cnb_url" \
|
||||
"refs/tags/${GITHUB_REF_NAME}:refs/tags/${GITHUB_REF_NAME}"
|
||||
|
||||
node scripts/axolotl/cnb-release.mjs finalize "${GITHUB_REF_NAME}" cnb-release/final
|
||||
node scripts/axolotl/verify-update-manifest.mjs \
|
||||
cnb-release/final/latest.json "${GITHUB_REF_NAME}" cnb
|
||||
22
.github/workflows/cancel-pr-workflow-on-merge.yml
vendored
Normal file
22
.github/workflows/cancel-pr-workflow-on-merge.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
name: Cancel PR Workflows on Merge
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
cancel:
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # 0.12.1
|
||||
with:
|
||||
workflow_id: all
|
||||
access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
ignore_sha: true
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
123
.github/workflows/changelog-comment.yml
vendored
Normal file
123
.github/workflows/changelog-comment.yml
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
name: Changelog Comment
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to post the changelog comment on (for testing)'
|
||||
required: true
|
||||
type: number
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
name: Post changelog comment
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Post or update changelog comment
|
||||
uses: actions/github-script@d746ffe35508b1917358783b479e04febd2b8f71 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
script: |
|
||||
const marker = '<!-- changelog -->';
|
||||
const mergedMarker = '<!-- changelog-merged -->';
|
||||
|
||||
const sections = ['### Added', '', '### Changed', '', '### Deprecated', '', '### Removed', '', '### Fixed', '', '### Security'].join('\n');
|
||||
const productBlock = (name) => `<details>\n<summary>${name}</summary>\n\n${sections}\n\n</details>`;
|
||||
|
||||
const template = [
|
||||
marker,
|
||||
'## Pull request changelog',
|
||||
'',
|
||||
'<!-- Fill in the changelog under each product area this PR affects.',
|
||||
' Empty sections are ignored. Leave a product collapsed/empty',
|
||||
' if it doesn\'t apply. -->',
|
||||
'',
|
||||
productBlock('App'),
|
||||
'',
|
||||
productBlock('Website'),
|
||||
].join('\n');
|
||||
|
||||
// Resolve PR number from event or workflow_dispatch input
|
||||
const prNumber = context.payload.pull_request?.number
|
||||
?? parseInt('${{ github.event.inputs.pr_number }}', 10);
|
||||
|
||||
if (!prNumber || isNaN(prNumber)) {
|
||||
core.setFailed('Could not determine PR number');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get PR details (need base ref for child PR detection)
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
// Check if bot comment already exists
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
});
|
||||
|
||||
const existingComment = comments.find(c => c.body.includes(marker));
|
||||
if (existingComment) {
|
||||
core.info('Changelog comment already exists, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
// Post the template comment
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: template,
|
||||
});
|
||||
|
||||
core.info(`Posted changelog comment on PR #${prNumber}`);
|
||||
|
||||
// Detect child PR: check if this PR's base branch is another open PR's head branch
|
||||
const baseRef = pr.base.ref;
|
||||
|
||||
if (baseRef === 'main' || baseRef === 'prod') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for a parent PR whose head branch matches our base branch
|
||||
const { data: candidatePRs } = await github.rest.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
head: `${context.repo.owner}:${baseRef}`,
|
||||
});
|
||||
|
||||
if (candidatePRs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentPR = candidatePRs[0];
|
||||
core.info(`Detected parent PR #${parentPR.number} for child PR #${prNumber}`);
|
||||
|
||||
// Add admonition to child PR's changelog comment
|
||||
const { data: childComments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
});
|
||||
|
||||
const childChangelogComment = childComments.find(c => c.body.includes(marker));
|
||||
if (childChangelogComment && !childChangelogComment.body.includes(mergedMarker)) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: childChangelogComment.id,
|
||||
body: `${mergedMarker}\n> [!NOTE]\n> This changelog has been merged into the changelog for #${parentPR.number}\n\n${childChangelogComment.body}`,
|
||||
});
|
||||
}
|
||||
29
.github/workflows/check-generic.yml
vendored
Normal file
29
.github/workflows/check-generic.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Repository checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
SQLX_OFFLINE: true
|
||||
|
||||
jobs:
|
||||
typos:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: crate-ci/typos@6ac2ebd1b93eade61faf7e12688ad87a073fea59 # v1.46.0
|
||||
|
||||
# see <https://github.com/influxdata/datafusion-udf-wasm/pull/275>
|
||||
tombi:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: taiki-e/install-action@fd2f5e3d644b484055ebf4268f474c565f148f25 # v2.81.9
|
||||
with:
|
||||
tool: tombi@1.1.2
|
||||
- run: tombi lint
|
||||
- run: tombi fmt --check
|
||||
26
.github/workflows/check-rust.yml
vendored
Normal file
26
.github/workflows/check-rust.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
name: Rust checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
SQLX_OFFLINE: true
|
||||
|
||||
jobs:
|
||||
shear:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
||||
- uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
|
||||
- run: cargo binstall --no-confirm cargo-shear
|
||||
- run: cargo shear
|
||||
34
.github/workflows/deploy-telemetry-dashboard.yml
vendored
Normal file
34
.github/workflows/deploy-telemetry-dashboard.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
name: Deploy telemetry dashboard
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'apps/telemetry-dashboard/**'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: telemetry-dashboard-deploy
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: pnpm/action-setup@008330803749db0355799c700092d9a85fd074e9 # v6.0.9
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: pnpm
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm --filter @axolotl/telemetry-dashboard build
|
||||
- uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
workingDirectory: apps/telemetry-dashboard
|
||||
packageManager: pnpm
|
||||
command: deploy
|
||||
40
.github/workflows/prepare-pnpm-cache.yml
vendored
Normal file
40
.github/workflows/prepare-pnpm-cache.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
name: Prepare pnpm cache
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/prepare-pnpm-cache.yml
|
||||
- package.json
|
||||
- pnpm-lock.yaml
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/prod' }}
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
- name: Enable Corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Get pnpm store path
|
||||
id: pnpm-store
|
||||
run: echo "store-path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache pnpm
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ${{ steps.pnpm-store.outputs.store-path }}
|
||||
key: pnpm-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm recursive install --frozen-lockfile
|
||||
69
.github/workflows/sync-cnb-source.yml
vendored
Normal file
69
.github/workflows/sync-cnb-source.yml
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
name: Sync source to CNB
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags:
|
||||
- '**'
|
||||
delete:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: sync-cnb-source-${{ github.event_name == 'delete' && format('{0}/{1}', github.event.ref_type, github.event.ref) || github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync Git ref
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check out source
|
||||
if: ${{ github.event_name != 'delete' }}
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
filter: blob:none
|
||||
|
||||
- name: Push Git ref to CNB
|
||||
env:
|
||||
CNB_REPO_URL_HTTPS: ${{ secrets.CNB_REPO_URL_HTTPS }}
|
||||
CNB_TOKEN: ${{ secrets.CNB_TOKEN }}
|
||||
CNB_TOKEN_USER_NAME: ${{ secrets.CNB_TOKEN_USER_NAME }}
|
||||
DELETED_REF: ${{ github.event.ref }}
|
||||
DELETED_REF_TYPE: ${{ github.event.ref_type }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ -z "${CNB_TOKEN:-}" ]]; then
|
||||
echo "CNB_TOKEN is not configured"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cnb_url="${CNB_REPO_URL_HTTPS:-https://cnb.cool/axlmc/Axolotl.git}"
|
||||
token_user="${CNB_TOKEN_USER_NAME:-cnb}"
|
||||
auth="$(printf '%s:%s' "$token_user" "$CNB_TOKEN" | base64 -w0)"
|
||||
git_cnb() {
|
||||
git -c "http.extraHeader=Authorization: Basic $auth" "$@"
|
||||
}
|
||||
|
||||
if [[ "$GITHUB_EVENT_NAME" == "delete" ]]; then
|
||||
git init
|
||||
if [[ "$DELETED_REF_TYPE" == "tag" ]]; then
|
||||
ref="refs/tags/$DELETED_REF"
|
||||
else
|
||||
ref="refs/heads/$DELETED_REF"
|
||||
fi
|
||||
git_cnb push "$cnb_url" --delete "$ref"
|
||||
else
|
||||
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
||||
git fetch --force origin "$GITHUB_REF:$GITHUB_REF"
|
||||
source_ref="$GITHUB_REF"
|
||||
else
|
||||
source_ref="HEAD"
|
||||
fi
|
||||
git_cnb push --force "$cnb_url" "$source_ref:$GITHUB_REF"
|
||||
fi
|
||||
101
.github/workflows/sync-lobehub-models.yml
vendored
Normal file
101
.github/workflows/sync-lobehub-models.yml
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
name: Sync LobeHub models
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '17 3 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: sync-lobehub-models
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout Axolotl Launcher
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
- name: Checkout LobeHub
|
||||
id: upstream
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git clone --depth 1 --branch main https://github.com/lobehub/lobehub.git "$RUNNER_TEMP/lobehub"
|
||||
sha="$(git -C "$RUNNER_TEMP/lobehub" rev-parse HEAD)"
|
||||
echo "sha=$sha" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Generate model catalog
|
||||
run: >-
|
||||
node --disable-warning=ExperimentalWarning --experimental-vm-modules
|
||||
scripts/axolotl/sync-lobehub-models.mjs
|
||||
--upstream "$RUNNER_TEMP/lobehub"
|
||||
--commit "${{ steps.upstream.outputs.sha }}"
|
||||
|
||||
- name: Detect changes
|
||||
id: changes
|
||||
run: |
|
||||
if git diff --quiet -- packages/app-lib/src/api/ai.rs packages/app-lib/src/api/lobehub_text_models.json; then
|
||||
echo "changed=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Close obsolete pull request
|
||||
if: steps.changes.outputs.changed == 'false'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
base="${{ github.event.repository.default_branch }}"
|
||||
branch="automation/sync-lobehub-models"
|
||||
pr_number="$(gh pr list --state open --base "$base" --head "$branch" --json number --jq '.[0].number // empty')"
|
||||
if [ -n "$pr_number" ]; then
|
||||
gh pr close "$pr_number" --delete-branch --comment "Closing because the current LobeHub model catalog no longer differs from $base."
|
||||
fi
|
||||
|
||||
- name: Push model update and create pull request
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
base="${{ github.event.repository.default_branch }}"
|
||||
branch="automation/sync-lobehub-models"
|
||||
upstream_sha="${{ steps.upstream.outputs.sha }}"
|
||||
short_sha="${upstream_sha:0:12}"
|
||||
title="chore(ai): sync LobeHub models"
|
||||
body="Automated synchronization of supported chat models from [LobeHub](https://github.com/lobehub/lobehub/tree/${upstream_sha}/packages/model-bank/src/aiModels) at \`${upstream_sha}\`."
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
gh auth setup-git
|
||||
|
||||
expected=""
|
||||
if git ls-remote --exit-code --heads origin "refs/heads/$branch" >/dev/null 2>&1; then
|
||||
git fetch --no-tags origin "refs/heads/$branch:refs/remotes/origin/$branch"
|
||||
expected="$(git rev-parse "refs/remotes/origin/$branch")"
|
||||
fi
|
||||
|
||||
git switch -C "$branch"
|
||||
git add packages/app-lib/src/api/ai.rs packages/app-lib/src/api/lobehub_text_models.json
|
||||
git commit -m "chore(ai): sync LobeHub models at $short_sha"
|
||||
git push --force-with-lease="refs/heads/$branch:$expected" origin "HEAD:refs/heads/$branch"
|
||||
|
||||
pr_number="$(gh pr list --state open --base "$base" --head "$branch" --json number --jq '.[0].number // empty')"
|
||||
if [ -n "$pr_number" ]; then
|
||||
gh api --method PATCH "repos/$GITHUB_REPOSITORY/pulls/$pr_number" -f title="$title" -f body="$body" >/dev/null
|
||||
else
|
||||
gh pr create --base "$base" --head "$branch" --title "$title" --body "$body"
|
||||
fi
|
||||
Reference in New Issue
Block a user