diff --git a/.github/scripts/nightly-version.mjs b/.github/scripts/nightly-version.mjs index 33d2e0c8e..5ccb29c7c 100644 --- a/.github/scripts/nightly-version.mjs +++ b/.github/scripts/nightly-version.mjs @@ -1,10 +1,9 @@ #!/usr/bin/env node /* oxlint-disable no-console */ +import { execSync } from 'node:child_process'; import { readFileSync } from 'node:fs'; -const runNumber = process.argv[2]; -const runAttempt = process.argv[3]; const packageJson = JSON.parse(readFileSync('package.json', 'utf8')); const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(packageJson.version); @@ -13,10 +12,29 @@ if (!match) { process.exit(1); } -if (!/^\d+$/.test(runNumber ?? '') || !/^\d+$/.test(runAttempt ?? '')) { - console.error('Usage: nightly-version.mjs '); - process.exit(1); +let timestamp; +try { + // Read the HEAD commit's committer timestamp in UTC epoch seconds + const commitTimestamp = execSync('git log -1 --format=%ct', { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); + if (/^\d+$/.test(commitTimestamp)) { + timestamp = new Date(Number(commitTimestamp) * 1000); + } +} catch { + // Fallback to current system time if git is unavailable } +timestamp ||= new Date(); + +const year = String(timestamp.getUTCFullYear()).slice(-2); +const month = String(timestamp.getUTCMonth() + 1).padStart(2, '0'); +const day = String(timestamp.getUTCDate()).padStart(2, '0'); +const hours = String(timestamp.getUTCHours()).padStart(2, '0'); +const minutes = String(timestamp.getUTCMinutes()).padStart(2, '0'); + +const timeIdentifier = `${year}-${month}-${day}-${hours}${minutes}`; + const [, major, minor, patch] = match; -console.log(`${major}.${minor}.${Number(patch) + 1}-nightly.${runNumber}.${runAttempt}`); +console.log(`${major}.${minor}.${Number(patch) + 1}-nightly.${timeIdentifier}`); diff --git a/.github/scripts/update-altstore-source.mjs b/.github/scripts/update-altstore-source.mjs index ebdc5a25c..a254d9c84 100644 --- a/.github/scripts/update-altstore-source.mjs +++ b/.github/scripts/update-altstore-source.mjs @@ -32,12 +32,22 @@ if (!app) { process.exit(1); } -// Strip marketplaceID: SideStore rejects PAL-marked sources. -delete app.marketplaceID; +// Clean version string for AltStore/SideStore compatibility: +// Apple Info.plist CFBundleShortVersionString / CFBundleVersion replaces hyphens/prereleases (e.g. -nightly.) with numeric/dot delimiters. +const normalizedVersion = version + .replace(/-nightly\./g, '.') + .replace(/[^0-9.]/g, '.') + .replace(/\.+/g, '.') + .replace(/^\.|\.$/g, ''); +const normalizedBuild = build + .replace(/-nightly\./g, '.') + .replace(/[^0-9.]/g, '.') + .replace(/\.+/g, '.') + .replace(/^\.|\.$/g, ''); const entry = { - version, - buildVersion: build, + version: normalizedVersion, + buildVersion: normalizedBuild, date, size, downloadURL, diff --git a/.github/workflows/cloudflare-web-dev.yml b/.github/workflows/cloudflare-web-dev.yml index 35ffa572c..ef2f711a9 100644 --- a/.github/workflows/cloudflare-web-dev.yml +++ b/.github/workflows/cloudflare-web-dev.yml @@ -46,7 +46,7 @@ jobs: shell: bash working-directory: ${{ github.workspace }} run: | - VERSION="$(node .github/scripts/nightly-version.mjs "${{ github.run_number }}" "${{ github.run_attempt }}")" + VERSION="$(node .github/scripts/nightly-version.mjs)" echo "version=$VERSION" >> "$GITHUB_OUTPUT" - name: Prepare OpenTofu deployment diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 82d8d00c9..7b6ed21bc 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -58,7 +58,7 @@ jobs: echo "value=${TAG#v}" >> "$GITHUB_OUTPUT" echo "is_release=true" >> "$GITHUB_OUTPUT" elif [[ "${GITHUB_REF}" == "refs/heads/dev" ]]; then - NIGHTLY_VERSION="$(node .github/scripts/nightly-version.mjs "${{ github.run_number }}" "${{ github.run_attempt }}")" + NIGHTLY_VERSION="$(node .github/scripts/nightly-version.mjs)" echo "value=${NIGHTLY_VERSION}" >> "$GITHUB_OUTPUT" echo "is_release=true" >> "$GITHUB_OUTPUT" else diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index ac3dd679e..1360a3ff3 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -57,7 +57,7 @@ jobs: VERSION="${TAG#v}" elif [ "$GIT_REF" = "refs/heads/dev" ]; then TAG="nightly"; REF="$GIT_SHA"; NIGHTLY=true - VERSION="$(node .github/scripts/nightly-version.mjs "$RUN_NUMBER" "$RUN_ATTEMPT")" + VERSION="$(node .github/scripts/nightly-version.mjs)" else TAG="$GIT_REF_NAME"; REF="$GIT_REF"; NIGHTLY=false VERSION="${TAG#v}" @@ -485,9 +485,11 @@ jobs: NORMALIZED_IPA="$(dirname "$IPA")/Sable-${VERSION}-ios-arm64.ipa" [ "$IPA" = "$NORMALIZED_IPA" ] || mv "$IPA" "$NORMALIZED_IPA" IPA="$NORMALIZED_IPA" + IPA_VERSION="$(unzip -p "$IPA" 'Payload/*.app/Info.plist' 2>/dev/null | plutil -extract CFBundleShortVersionString raw -o - - 2>/dev/null || true)" echo "size=$(stat -f%z "$IPA")" >> "$GITHUB_OUTPUT" echo "path=$IPA" >> "$GITHUB_OUTPUT" echo "name=$(basename "$IPA")" >> "$GITHUB_OUTPUT" + echo "ipa_version=${IPA_VERSION:-$VERSION}" >> "$GITHUB_OUTPUT" - name: Attest iOS bundle uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 @@ -505,6 +507,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} REPO: ${{ github.repository }} + ALTSTORE_VERSION: ${{ steps.ipa.outputs.ipa_version }} run: | DOWNLOAD_URL="https://github.com/$REPO/releases/download/$TAG/${{ steps.ipa.outputs.name }}" DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" @@ -514,7 +517,7 @@ jobs: echo "Using repo altstore-source.json" fi node .github/scripts/update-altstore-source.mjs \ - altstore-source.json "$VERSION" "$VERSION" "${{ steps.ipa.outputs.size }}" "$DOWNLOAD_URL" "$DATE" "Sable $VERSION" + altstore-source.json "${ALTSTORE_VERSION:-$VERSION}" "${ALTSTORE_VERSION:-$VERSION}" "${{ steps.ipa.outputs.size }}" "$DOWNLOAD_URL" "$DATE" "Sable $VERSION" gh release upload "$TAG" altstore-source.json --clobber updater-manifest: