diff --git a/.github/workflows/upload.yaml b/.github/workflows/upload.yaml index cab2777..eb96ac4 100644 --- a/.github/workflows/upload.yaml +++ b/.github/workflows/upload.yaml @@ -6,54 +6,102 @@ on: - main pull_request: {} +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - - uses: actions/setup-node@v6 - with: - node-version: 24 - package-manager-cache: false - - - name: Enable Corepack - run: | - corepack enable - corepack prepare yarn@4.12.0 --activate - yarn --version - - - uses: calcit-lang/setup-calcit@ca701be5a471759e442aa053cd100720bbe1f09f # v1.5.0 - - - name: "install modules" - run: caps --ci - - - name: "validate snapshot and compile" - run: | - cr calcit.cirru edit format - git diff --exit-code -- calcit.cirru - cr calcit.cirru --check-only - cr calcit.cirru analyze check-types --summary-only --format json - cr calcit.cirru analyze weak-types --only schema-dynamic,code-dynamic --intent unresolved --summary-only --format json - cr calcit.cirru analyze deprecated --summary-only --format json - cr calcit.cirru js - yarn install --immutable - yarn vite build --base=./ - node --test scripts/upgrade.test.mjs - - - name: Deploy to server - id: deploy - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: Pendect/action-rsyncer@v2.0.0 - env: - DEPLOY_KEY: ${{secrets.rsync_private_key}} - with: - flags: '-avzr --progress' - options: '' - ssh_options: '' - src: 'dist/*' - dest: 'rsync-user@tiye.me:/web-assets/repo/${{ github.repository }}' - - - name: Display status from deploy - if: steps.deploy.outcome != 'skipped' - run: echo "${{ steps.deploy.outputs.status }}" + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0 + with: + node-version: 24 + package-manager-cache: false + + - uses: calcit-lang/setup-calcit@ca701be5a471759e442aa053cd100720bbe1f09f # v1.5.0 + with: + tools: calcit,caps + caps-version: "0.1.1" + + - name: Enable Corepack + run: | + corepack enable + corepack prepare yarn@4.18.0 --activate + yarn --version + + # Current Respo modules still request preceding compatible releases, so + # strict Caps resolution rejects the highest SemVer set selected here. + - name: Install and validate dependencies + run: | + caps --ci + yarn install --immutable + caps verify --toolchain + + - name: Validate Calcit snapshot + run: | + calcit calcit.cirru fix --workflow strict --verify --format edn + calcit calcit.cirru edit format + git diff --exit-code -- calcit.cirru + calcit calcit.cirru --check-only + calcit calcit.cirru analyze check-types --summary-only --format json + calcit calcit.cirru analyze weak-types --only schema-dynamic,code-dynamic --intent unresolved --summary-only --format json + calcit calcit.cirru analyze deprecated --summary-only --format json + calcit calcit.cirru analyze dynamic-methods --summary-only --format json | jq -e '.data.summary.findings == 0' + + - name: Build client with CDN asset URLs + id: asset-path + env: + CDN_ORIGIN: https://cos-sh.tiye.me + run: | + if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then + asset_prefix="${GITHUB_REPOSITORY}/pr/" + elif [[ "$GITHUB_REF" == "refs/heads/main" ]]; then + asset_prefix="${GITHUB_REPOSITORY}/" + else + asset_prefix="${GITHUB_REPOSITORY}/branches/${GITHUB_REF_NAME}/" + fi + export VITE_BASE_URL="${CDN_ORIGIN}/${asset_prefix}" + echo "VITE_BASE_URL=${VITE_BASE_URL}" >> "$GITHUB_ENV" + echo "prefix=${asset_prefix}" >> "$GITHUB_OUTPUT" + calcit calcit.cirru js + node --test scripts/upgrade.test.mjs + yarn vite build --base="$VITE_BASE_URL" + + - name: Verify CDN asset references + run: node scripts/verify-cdn-build.mjs + + - name: Upload static assets to COS + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + uses: worktools/cos-upload-action@0ce57b26b03dbdd27a4a544a06d453415932c62d # v1.0.0 + with: + source-dir: dist + bucket: ${{ secrets.COS_BUCKET }} + region: ap-shanghai + prefix: ${{ steps.asset-path.outputs.prefix }} + secret-id: ${{ secrets.COS_SECRET_ID }} + secret-key: ${{ secrets.COS_SECRET_KEY }} + + - name: Verify uploaded CDN assets + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + run: node scripts/verify-cdn-build.mjs --remote + + - name: Deploy to server + id: deploy + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: Pendect/action-rsyncer@8e05ffa5c93e5d9c9b167796b26044d2c616b2b9 # v2.0.0 + env: + DEPLOY_KEY: ${{secrets.rsync_private_key}} + with: + flags: "-avzr --progress" + options: "" + ssh_options: "" + src: "dist/*" + dest: "rsync-user@tiye.me:/web-assets/repo/${{ github.repository }}" + + - name: Display status from deploy + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: echo "${{ steps.deploy.outputs.status }}" diff --git a/.yarnrc.yml b/.yarnrc.yml index 3186f3f..6f63f08 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1 +1,3 @@ nodeLinker: node-modules +npmPreapprovedPackages: + - "@calcit/procs@0.27.0" diff --git a/deps.cirru b/deps.cirru index e5d39bf..f5b1222 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,7 +1,7 @@ -{} (:calcit-version |0.22.0-alpha.3) +{} (:calcit-version |0.27.0) :version |0.1.3 - :dependencies $ {} (|Respo/reel.calcit |0.6.30) - |Respo/respo-markdown.calcit |0.4.43 - |Respo/respo-ui.calcit |0.7.30 - |Respo/respo.calcit |0.16.112 + :dependencies $ {} (|Respo/reel.calcit |0.6.33-alpha.1) + |Respo/respo-markdown.calcit |0.4.46 + |Respo/respo-ui.calcit |0.7.32-alpha.2 + |Respo/respo.calcit |0.16.114-alpha.5 diff --git a/package.json b/package.json index 1f5303e..c396578 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,13 @@ }, "author": "jiyinyiyong", "license": "MIT", - "packageManager": "yarn@4.12.0", + "packageManager": "yarn@4.18.0", "devDependencies": { "bottom-tip": "^0.1.5", "vite": "^7.3.1" }, "dependencies": { - "@calcit/procs": "0.22.0-alpha.3", + "@calcit/procs": "0.27.0", "diff": "^8.0.2" } } diff --git a/scripts/verify-cdn-build.mjs b/scripts/verify-cdn-build.mjs new file mode 100644 index 0000000..44ddf7a --- /dev/null +++ b/scripts/verify-cdn-build.mjs @@ -0,0 +1,64 @@ +import { existsSync, readFileSync, statSync } from 'node:fs'; +import { join } from 'node:path'; + +const base = process.env.VITE_BASE_URL; + +if (!base || !base.startsWith('https://') || !base.endsWith('/')) { + throw new Error('VITE_BASE_URL must be an absolute HTTPS URL ending in /'); +} + +const html = readFileSync('dist/index.html', 'utf8'); +const assetUrls = [...html.matchAll(/(?:src|href)="(https:\/\/cos-sh\.tiye\.me\/[^\"]+)"/g)].map( + (match) => match[1], +); + +if (!assetUrls.some((url) => url.endsWith('.js'))) { + throw new Error('Built HTML must load JavaScript from COS'); +} + +const assets = assetUrls.map((url) => { + if (!url.startsWith(base)) { + throw new Error(`Asset URL is outside VITE_BASE_URL: ${url}`); + } + + const relativePath = decodeURIComponent(url.slice(base.length)); + const localPath = join('dist', relativePath); + if (!relativePath.startsWith('assets/') || !existsSync(localPath)) { + throw new Error(`Asset URL has no matching local build output: ${url}`); + } + + return { url, localPath }; +}); + +console.log(`Verified ${assets.length} local COS asset reference(s) under ${base}`); + +if (process.argv.includes('--remote')) { + for (const { url, localPath } of assets) { + let verified = false; + + for (let attempt = 0; attempt < 6; attempt += 1) { + try { + const separator = url.includes('?') ? '&' : '?'; + const response = await fetch( + `${url}${separator}run=${process.env.GITHUB_RUN_ID ?? 'local'}-${attempt}`, + { cache: 'no-store' }, + ); + const body = await response.arrayBuffer(); + if (response.ok && body.byteLength === statSync(localPath).size) { + verified = true; + break; + } + } catch { + // CDN propagation can briefly fail after the upload. + } + + await new Promise((resolve) => setTimeout(resolve, 2000)); + } + + if (!verified) { + throw new Error(`Uploaded asset was not available from the CDN: ${url}`); + } + } + + console.log(`Verified ${assets.length} uploaded asset(s) through the public CDN`); +} diff --git a/yarn.lock b/yarn.lock index 0ae6345..b869472 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,17 +2,17 @@ # Manual changes might be lost - proceed with caution! __metadata: - version: 8 + version: 10 cacheKey: 10c0 -"@calcit/procs@npm:0.22.0-alpha.3": - version: 0.22.0-alpha.3 - resolution: "@calcit/procs@npm:0.22.0-alpha.3" +"@calcit/procs@npm:0.27.0": + version: 0.27.0 + resolution: "@calcit/procs@npm:0.27.0" dependencies: "@calcit/ternary-tree": "npm:0.0.26" "@cirru/parser.ts": "npm:^0.0.9" "@cirru/writer.ts": "npm:^0.1.9" - checksum: 10c0/b6aa28ae40918bef2e5363adbd4f5b36ec2ee26eda1a45b5e6c628b12c0d2746554d1106c5b0c86e2ff1be0fc726bed7173df1984e1dcb9a07d846b849db2a4e + checksum: 10c0/85edf1724beaf5e3b895f456b9d81ae93100316802b19bc20ab7cbcc61e295892e787c2f9cca0c9c231ba1cc3c3d320ae158d3f8c9fd39eda24500c56ebe99b1 languageName: node linkType: hard @@ -445,7 +445,7 @@ __metadata: version: 0.0.0-use.local resolution: "calcit-workflow@workspace:." dependencies: - "@calcit/procs": "npm:0.22.0-alpha.3" + "@calcit/procs": "npm:0.27.0" bottom-tip: "npm:^0.1.5" diff: "npm:^8.0.2" vite: "npm:^7.3.1"