release-mirrors #468
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: release-mirrors | |
on: | |
workflow_dispatch: | |
inputs: | |
release_tag: | |
description: Release tag (defaults to the tag of the release event) | |
required: false | |
DEBUG_JOBS: | |
description: 如果需要 debug 特定任务,请输入其 job id,多个任务请用 `,` 连接 | |
env: | |
GITHUB_TOKEN: ${{ secrets.MISTEOWORKFLOW }} | |
TZ: Asia/Shanghai | |
jobs: | |
# 以下是获取需上传的版本的信息(如果用户手动输入了 release_tag 则改为使用该值) | |
getReleaseTag: | |
name: Get release tag | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
show-progress: false | |
- name: Fetch release info | |
id: fetchReleaseInfo | |
if: ${{ !inputs.release_tag }} | |
run: | | |
gh release list --repo 'MaaAssistantArknights/MaaRelease' --limit 10 > ${{ runner.temp }}/release_maa | |
echo "-------------" | |
echo "Last 10 release:" | |
column -t ${{ runner.temp }}/release_maa | |
head -n 1 ${{ runner.temp }}/release_maa | awk '{ print $1 }' > ${{ runner.temp }}/config | |
echo "-------------" | |
echo "config:" | |
cat ${{ runner.temp }}/config | |
echo "release_tag=$(head -n 1 ${{ runner.temp }}/config)" >> $GITHUB_OUTPUT | |
- id: getRateLimitStatus | |
name: Get rate limit status for current token | |
run: | | |
export rateLimitStatus=$(gh api /rate_limit | jq -c '.resources.core') | |
echo "rateLimitStatus=$rateLimitStatus" >> $GITHUB_OUTPUT | |
echo 'Current token rate limit status:' | |
echo $rateLimitStatus | jq -r '.reset |= (tostring | tonumber | strflocaltime("%Y-%m-%d %H:%M:%S %Z")) | to_entries | map([.key, .value]) | .[] | @tsv' | column -t | |
- id: generateDebugOptions | |
name: Parse the debug input | |
uses: ./.github/commaSplitter | |
with: | |
input: ${{ inputs.DEBUG_JOBS }} | |
outputs: | |
release_tag: ${{ inputs.release_tag || steps.fetchReleaseInfo.outputs.release_tag }} | |
rateLimitStatus: ${{ steps.getRateLimitStatus.outputs.rateLimitStatus }} | |
debug_jobs: ${{ steps.generateDebugOptions.outputs.output }} | |
# 以下开始是各镜像站上传流程。其中: | |
# 1. 可以通过对 matrix 的排列组合来实现多个镜像站使用同一组下载数据的上传,减少 api rate limit 的使用; | |
# 2. 如果是支持 S3 api 的对象存储的话,可以用 ./.github/s3Sync 上传; | |
# 其他类型也可以自己写上传脚本; | |
# 3. `notAlpha` 代表不上传 alpha 版本。 | |
# 如果需要新增上传流程,需在下方参考其他上传流程和上方说明添加。 | |
upload: | |
needs: getReleaseTag | |
strategy: | |
fail-fast: false | |
matrix: | |
repo: | |
- MaaAssistantArknights | |
- MaaRelease | |
os: # 必须为逗号分隔格式 | |
- windows,linux,macos,macos-runtime | |
target: # 必须为逗号分隔格式 | |
- ota_server,R2 | |
runs-on: | |
- ubuntu-latest | |
include: # 跟上方必须有属性冲突,并且需要将其他需要继承的属性复制下来,否则不会继承 | |
- repo: MaaRelease | |
os: macos-ota | |
target: R2_MAC | |
runs-on: macos-14 | |
runs-on: ${{ matrix.runs-on }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
show-progress: false | |
- id: generateTargetList | |
name: Generate target list | |
uses: ./.github/commaSplitter | |
with: | |
input: ${{ matrix.target }} | |
- name: Download release files from ${{ matrix.repo }} | |
id: downloadReleaseFiles | |
uses: ./.github/downloadReleaseFiles | |
with: | |
release_tag: ${{ needs.getReleaseTag.outputs.release_tag }} | |
os: ${{ matrix.os }} | |
notTempDir: false | |
repo: ${{ matrix.repo }} | |
- name: Upload to R2 | |
if: github.repository == 'MaaAssistantArknights/MaaRelease' && contains(fromJSON(steps.generateTargetList.outputs.output), 'R2') && (!inputs.DEBUG_JOBS || contains(fromJson(needs.getReleaseTag.outputs.debug_jobs), 'R2')) | |
uses: ./.github/s3Sync | |
continue-on-error: true | |
with: | |
args: --acl public-read --follow-symlinks | |
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} | |
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
AWS_REGION: us-east-1 | |
AWS_S3_ENDPOINT: ${{ secrets.AWS_S3_ENDPOINT }} | |
SOURCE_DIR: ${{ steps.downloadReleaseFiles.outputs.dir }} | |
DEST_DIR: ${{ format('{0}/{1}/{2}', 'MaaAssistantArknights', matrix.repo, 'releases/download') }} | |
- name: Upload to OTA Server | |
if: github.repository == 'MaaAssistantArknights/MaaRelease' && contains(fromJSON(steps.generateTargetList.outputs.output), 'ota_server') && (!inputs.DEBUG_JOBS || contains(fromJson(needs.getReleaseTag.outputs.debug_jobs), 'ota_server')) | |
continue-on-error: true | |
run: | | |
echo "${{ secrets.OTA_SERVER_SSH_KEY }}" > ${{ runner.temp }}/tmp | |
chmod 600 ${{ runner.temp }}/tmp | |
KEY_INFO=$(ssh-keygen -lf ${{ runner.temp }}/tmp) | |
KEY_TYPE=$(echo "$KEY_INFO" | awk '{print $4}' | sed 's/[(|)]//g') | |
echo "KEY_INFO: $KEY_INFO" | |
echo "KEY_TYPE: $KEY_TYPE" | |
export KEY_NAME=id_${KEY_TYPE,,} | |
echo "KEY_NAME: $KEY_NAME" | |
mkdir -p ~/.ssh/ | |
echo "${{ secrets.OTA_SERVER_SSH_KEY }}" > ~/.ssh/$KEY_NAME | |
chmod 600 ~/.ssh/$KEY_NAME | |
ssh-keyscan -H ${{ secrets.OTA_SERVER_SSH_HOST }} >> ~/.ssh/known_hosts | |
echo "::group::rsync version" | |
rsync --version | |
echo "::endgroup::" | |
echo "Start rsync-ing..." | |
set +e | |
i=0 | |
RC=-1 | |
while [[ true ]] | |
do | |
echo "Trying rsync #$i..." | |
cd ${{ steps.downloadReleaseFiles.outputs.dir }} | |
echo "::group::rsync #$i output" | |
rsync -vvcrRtzmhhh --mkpath --info=STATS3 --ignore-errors --delete ${{ needs.getReleaseTag.outputs.release_tag }} ${{ secrets.OTA_SERVER_SSH_USER }}@${{ secrets.OTA_SERVER_SSH_HOST }}:OTA/${{ format('{0}/{1}/{2}', 'MaaAssistantArknights', matrix.repo, 'releases/download') }}/ | |
RC=$? | |
i=$((i+1)) | |
echo "::endgroup::" | |
echo "RC: $RC" | |
REASON_NAME="RSYNC_RETRYABLE_REASON_$RC" | |
if env | grep -q "^$REASON_NAME="; then | |
if [ $i -ge $MAX_RETRIES ]; then | |
echo "Hit maximum number ($MAX_RETRIES) of retries, giving up." | |
exit $RC; | |
fi | |
echo 'Rsync failed due to "'"${!REASON_NAME}"'", retrying...' | |
sleep 5 | |
else | |
if [[ $RC -eq 0 ]]; then | |
echo "Rsync succeeded, exiting..." | |
else | |
echo "Rsync failed due to unknown reason, exiting..." | |
fi | |
exit $RC | |
fi | |
done | |
env: | |
MAX_RETRIES: 5 | |
RSYNC_RETRYABLE_REASON_12: Error in rsync protocol data stream | |
RSYNC_RETRYABLE_REASON_23: Error in partial transfer | |
RSYNC_RETRYABLE_REASON_30: Timeout in data send/receive | |
- name: Upload Appcast Packages for macos-ota | |
if: github.repository == 'MaaAssistantArknights/MaaRelease' && !contains(needs.getReleaseTag.outputs.release_tag, '.g') && contains(fromJSON(steps.generateTargetList.outputs.output), 'R2_MAC') && (!inputs.DEBUG_JOBS || contains(fromJson(needs.getReleaseTag.outputs.debug_jobs), 'R2_MAC')) | |
env: | |
RCLONE_CONFIG_MAA_TYPE: s3 | |
RCLONE_CONFIG_MAA_PROVIDER: Cloudflare | |
RCLONE_CONFIG_MAA_ACCESS_KEY_ID: ${{ secrets.R2_MAC_KEY_ID }} | |
RCLONE_CONFIG_MAA_SECRET_ACCESS_KEY: ${{ secrets.R2_MAC_ACCESS_KEY }} | |
RCLONE_CONFIG_MAA_ENDPOINT: ${{ secrets.R2_MAC_ENDPOINT }} | |
HOMEBREW_NO_ENV_HINTS: true | |
run: | | |
brew install rclone | |
mkdir -p ~/.config/rclone/ | |
touch ~/.config/rclone/rclone.conf | |
rclone sync ${{ steps.downloadReleaseFiles.outputs.dirWithReleaseTag }} MAA:maa-release/macos --log-level INFO | |
rclone delete MAA:maa-release/macos --min-age 90d --rmdirs --log-level INFO | |
webhook: | |
needs: getReleaseTag | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
show-progress: false | |
- name: Setup Node.js | |
uses: actions/setup-node@v4 | |
with: | |
node-version: lts/* | |
check-latest: true | |
# - name: Fire the webhook of MAA_S3 | |
# if: github.repository == 'MaaAssistantArknights/MaaRelease' && (!inputs.DEBUG_JOBS || contains(fromJson(needs.getReleaseTag.outputs.debug_jobs), 'MAA_S3')) | |
# run: | | |
# node scripts/AnnAngela/webhookForJenkins.js | |
# env: | |
# WEBHOOK_URL: https://jenkins.maa-org.net/job/maa-release-s3-sync/buildWithParameters?token=${{ secrets.MAA_JENKINS_PROJECT_TOKEN }}&release_tag=${{ needs.getReleaseTag.outputs.release_tag }} | |
# WEBHOOK_SECRET: ${{ secrets.MAA_JENKINS_ROBOT_TOKEN }} | |
- name: Fire the webhook of AnnAngela's COS | |
if: (github.repository == 'MaaAssistantArknights/MaaRelease' || github.repository == 'AnnAngela/MaaRelease') && !contains(needs.getReleaseTag.outputs.release_tag, '.g') && (!inputs.DEBUG_JOBS || contains(fromJson(needs.getReleaseTag.outputs.debug_jobs), 'AnnAngelaMirror')) | |
run: | | |
node scripts/AnnAngela/webhook.js | |
env: | |
WEBHOOK_URL: https://webhook.annangela.cn/custom?from=MaaRelease | |
WEBHOOK_SECRET: ${{ secrets.ANNANGELA_WEBHOOK_SECRET }} | |
release_tag: ${{ needs.getReleaseTag.outputs.release_tag }} | |
# 以下是发版流程,通过触发生成 maa version api 文件的 workflow 发版。 | |
# 如果新增了上传流程,除非是像 macOS 一样自行维护发版渠道的, | |
# 否则都需要到 MaaAssistantArknights/update_version.py 新增镜像地址生成。 | |
updateVersionApi: | |
name: Dispatch the "update_version_api" workflow | |
runs-on: ubuntu-latest | |
needs: | |
- getReleaseTag | |
if: always() | |
steps: | |
- name: Dispatch the "update_version_api" workflow | |
run: gh workflow run update_version_api --repo ${{ github.repository }} | |
getRateLimitStatus: | |
name: Get rate limit status for current token | |
runs-on: ubuntu-latest | |
if: always() | |
needs: | |
- getReleaseTag | |
- upload | |
- webhook | |
- updateVersionApi | |
steps: | |
- name: Get rate limit status for current token | |
run: | | |
echo '${{ needs.getReleaseTag.outputs.rateLimitStatus }}' > ${{ runner.temp }}/oldRateLimitStatus.json | |
gh api /rate_limit | jq -c '.resources.core' > ${{ runner.temp }}/newRateLimitStatus.json | |
echo 'Current token rate limit status:' | |
jq -r '.reset |= (tostring | tonumber | strflocaltime("%Y-%m-%d %H:%M:%S %Z")) | to_entries | map([.key, .value]) | .[] | @tsv' ${{ runner.temp }}/newRateLimitStatus.json | column -t | |
printf "\n" | |
echo "Rate limit used in this action: $(jq -n 'input as $oldRateLimitStatus | input as $newRateLimitStatus | $newRateLimitStatus.used - $oldRateLimitStatus.used' ${{ runner.temp }}/oldRateLimitStatus.json ${{ runner.temp }}/newRateLimitStatus.json)" |