@@ -121,6 +121,9 @@ jobs:
121121 release/latest*.yml
122122 retention-days : 7
123123
124+ # ──────────────────────────────────────────────────────────────
125+ # Job: Publish to GitHub Releases
126+ # ──────────────────────────────────────────────────────────────
124127 publish :
125128 needs : release
126129 runs-on : ubuntu-latest
@@ -195,3 +198,153 @@ jobs:
195198 💬 Found an issue? Please submit an [Issue](https://github.com/${{ github.repository }}/issues)
196199 env :
197200 GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
201+
202+ # ──────────────────────────────────────────────────────────────
203+ # Job: Upload to Alibaba Cloud OSS
204+ # Uploads all release artifacts to OSS for:
205+ # - Official website downloads (via release-info.json)
206+ # - electron-updater auto-update (via latest-*.yml)
207+ #
208+ # Directory structure on OSS:
209+ # latest/ → always overwritten with the newest version
210+ # releases/vX.Y.Z/ → permanent archive, never deleted
211+ # ──────────────────────────────────────────────────────────────
212+ upload-oss :
213+ needs : release
214+ runs-on : ubuntu-latest
215+
216+ steps :
217+ - name : Checkout code
218+ uses : actions/checkout@v4
219+
220+ - name : Download all artifacts
221+ uses : actions/download-artifact@v4
222+ with :
223+ path : release-artifacts
224+
225+ - name : Extract version
226+ id : version
227+ run : |
228+ if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
229+ VERSION="${GITHUB_REF#refs/tags/v}"
230+ else
231+ VERSION="${{ github.event.inputs.version }}"
232+ fi
233+ echo "version=${VERSION}" >> $GITHUB_OUTPUT
234+ echo "tag=v${VERSION}" >> $GITHUB_OUTPUT
235+ echo "Detected version: ${VERSION}"
236+
237+ - name : Prepare upload directories
238+ run : |
239+ VERSION="${{ steps.version.outputs.version }}"
240+ TAG="${{ steps.version.outputs.tag }}"
241+
242+ mkdir -p staging/latest
243+ mkdir -p staging/releases/${TAG}
244+
245+ # Flatten all platform artifacts into staging directories
246+ find release-artifacts/ -type f | while read file; do
247+ filename=$(basename "$file")
248+ cp "$file" "staging/latest/${filename}"
249+ cp "$file" "staging/releases/${TAG}/${filename}"
250+ done
251+
252+ echo "=== staging/latest/ ==="
253+ ls -lh staging/latest/
254+ echo ""
255+ echo "=== staging/releases/${TAG}/ ==="
256+ ls -lh staging/releases/${TAG}/
257+
258+ - name : Generate release-info.json
259+ run : |
260+ VERSION="${{ steps.version.outputs.version }}"
261+ BASE_URL="https://valuecell-clawx.oss-cn-hangzhou.aliyuncs.com/latest"
262+
263+ jq -n \
264+ --arg version "$VERSION" \
265+ --arg date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
266+ --arg base "$BASE_URL" \
267+ --arg changelog "https://github.com/${{ github.repository }}/releases/tag/v${VERSION}" \
268+ '{
269+ version: $version,
270+ releaseDate: $date,
271+ downloads: {
272+ mac: {
273+ x64: ($base + "/ClawX-" + $version + "-mac-x64.dmg"),
274+ arm64: ($base + "/ClawX-" + $version + "-mac-arm64.dmg")
275+ },
276+ win: {
277+ x64: ($base + "/ClawX-" + $version + "-win-x64.exe"),
278+ arm64: ($base + "/ClawX-" + $version + "-win-arm64.exe")
279+ },
280+ linux: {
281+ deb_amd64: ($base + "/ClawX-" + $version + "-linux-amd64.deb"),
282+ deb_arm64: ($base + "/ClawX-" + $version + "-linux-arm64.deb"),
283+ appimage_x64: ($base + "/ClawX-" + $version + "-linux-x86_64.AppImage"),
284+ appimage_arm64: ($base + "/ClawX-" + $version + "-linux-arm64.AppImage"),
285+ rpm_x64: ($base + "/ClawX-" + $version + "-linux-x86_64.rpm")
286+ }
287+ },
288+ changelog: $changelog
289+ }' > staging/latest/release-info.json
290+
291+ echo "=== release-info.json ==="
292+ cat staging/latest/release-info.json
293+
294+ - name : Install and configure ossutil
295+ env :
296+ OSS_ACCESS_KEY_ID : ${{ secrets.OSS_ACCESS_KEY_ID }}
297+ OSS_ACCESS_KEY_SECRET : ${{ secrets.OSS_ACCESS_KEY_SECRET }}
298+ run : |
299+ curl -sL https://gosspublic.alicdn.com/ossutil/install.sh | sudo bash
300+
301+ # Write config file for non-interactive use
302+ cat > $HOME/.ossutilconfig << EOF
303+ [Credentials]
304+ language=EN
305+ endpoint=oss-cn-hangzhou.aliyuncs.com
306+ accessKeyID=${OSS_ACCESS_KEY_ID}
307+ accessKeySecret=${OSS_ACCESS_KEY_SECRET}
308+ EOF
309+
310+ ossutil --version
311+
312+ - name : " Upload to OSS: latest/ (overwrite)"
313+ run : |
314+ # Clean old latest/ to remove stale version files
315+ ossutil rm -r -f oss://valuecell-clawx/latest/ || true
316+
317+ # Upload all files with no-cache so clients always get the freshest version
318+ ossutil cp -r -f \
319+ staging/latest/ \
320+ oss://valuecell-clawx/latest/ \
321+ --meta "Cache-Control:no-cache,no-store,must-revalidate"
322+
323+ echo "Uploaded to latest/"
324+
325+ - name : " Upload to OSS: releases/vX.Y.Z/ (archive)"
326+ run : |
327+ TAG="${{ steps.version.outputs.tag }}"
328+
329+ # Upload to permanent archive (long cache, immutable)
330+ ossutil cp -r \
331+ staging/releases/${TAG}/ \
332+ oss://valuecell-clawx/releases/${TAG}/ \
333+ --meta "Cache-Control:public,max-age=31536000,immutable"
334+
335+ echo "Uploaded to releases/${TAG}/"
336+
337+ - name : Verify OSS upload
338+ run : |
339+ TAG="${{ steps.version.outputs.tag }}"
340+
341+ echo "=== latest/ ==="
342+ ossutil ls oss://valuecell-clawx/latest/ --short
343+
344+ echo ""
345+ echo "=== releases/${TAG}/ ==="
346+ ossutil ls oss://valuecell-clawx/releases/${TAG}/ --short
347+
348+ echo ""
349+ echo "=== Verify release-info.json ==="
350+ curl -sL "https://valuecell-clawx.oss-cn-hangzhou.aliyuncs.com/latest/release-info.json" | jq .
0 commit comments