mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-16 19:00:28 +01:00
Compare commits
3 Commits
fix/ui-and
...
ci/artifac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
132d378346 | ||
|
|
434cb3bd39 | ||
|
|
7a6daa011d |
10
.github/renovate.json
vendored
10
.github/renovate.json
vendored
@@ -30,9 +30,17 @@
|
||||
"customType": "regex",
|
||||
"managerFilePatterns": ["/\\.ya?ml$/"],
|
||||
"matchStrings": [
|
||||
"# renovate: datasource=(?<datasource>\\S+) depName=(?<depName>\\S+)(?: versioning=(?<versioning>\\S+))?\\s+xcode-version:\\s*[\"']?(?<currentValue>[^\"'\\s]+)"
|
||||
"# renovate: datasource=(?<datasource>\\S+) depName=(?<depName>\\S+)(?: versioning=(?<versioning>\\S+))?\\s+[A-Za-z0-9._-]+:\\s*[\"']?(?<currentValue>[^\"'\\s]+)"
|
||||
],
|
||||
"versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}loose{{/if}}"
|
||||
},
|
||||
{
|
||||
"customType": "regex",
|
||||
"description": "Track the Bun version pinned in eas.json build profiles (strict JSON can't hold inline annotations)",
|
||||
"managerFilePatterns": ["/(^|/)eas\\.json$/"],
|
||||
"matchStrings": ["\"bun\"\\s*:\\s*\"(?<currentValue>[^\"]+)\""],
|
||||
"datasourceTemplate": "npm",
|
||||
"depNameTemplate": "bun"
|
||||
}
|
||||
],
|
||||
"customDatasources": {
|
||||
|
||||
129
.github/workflows/artifact-comment.yml
vendored
129
.github/workflows/artifact-comment.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
comment-artifacts:
|
||||
if: github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || (github.event_name == 'workflow_run' && github.event.workflow_run.event == 'pull_request')
|
||||
name: 📦 Post Build Artifacts
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
@@ -144,7 +144,7 @@ jobs:
|
||||
)
|
||||
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
||||
|
||||
console.log(`Found ${buildRuns.length} non-cancelled build workflow runs for this commit`);
|
||||
console.log(`Found ${buildRuns.length} build workflow runs for this commit`);
|
||||
|
||||
// Log current status of each build for debugging
|
||||
buildRuns.forEach(run => {
|
||||
@@ -184,21 +184,35 @@ jobs:
|
||||
const latestAndroidRun = findBestRun('Android APK Build');
|
||||
const latestIOSRun = findBestRun('iOS IPA Build');
|
||||
|
||||
// Map our build targets to their job display names. Exact name is
|
||||
// tried first so a signed target never collides with its
|
||||
// "(Unsigned)" sibling (whose name contains the signed name).
|
||||
const jobMappings = {
|
||||
'Android Phone': ['🤖 Build Android APK (Phone)'],
|
||||
'Android TV': ['🤖 Build Android APK (TV)'],
|
||||
'iOS': ['🍎 Build iOS IPA (Phone)'],
|
||||
'iOS Unsigned': ['🍎 Build iOS IPA (Phone - Unsigned)'],
|
||||
'tvOS': ['🍎 Build tvOS IPA'],
|
||||
'tvOS Unsigned': ['🍎 Build tvOS IPA (Unsigned)']
|
||||
};
|
||||
|
||||
// Prefer an exact name match over a substring match so
|
||||
// '...(Phone)' doesn't swallow '...(Phone - Unsigned)'.
|
||||
const findJobForTarget = (jobs, jobNames) =>
|
||||
jobs.find(j => jobNames.some(name => j.name === name)) ||
|
||||
jobs.find(j => jobNames.some(name => j.name.includes(name)));
|
||||
|
||||
// Format a millisecond duration as "Xm Ys".
|
||||
const fmtDuration = (ms) => {
|
||||
const min = Math.floor(ms / 60000);
|
||||
const sec = Math.floor((ms % 60000) / 1000);
|
||||
return `${min}m ${sec}s`;
|
||||
};
|
||||
|
||||
// For the consolidated workflow, get individual job statuses
|
||||
if (latestAppsRun) {
|
||||
console.log(`Getting individual job statuses for run ${latestAppsRun.id} (status: ${latestAppsRun.status}, conclusion: ${latestAppsRun.conclusion || 'none'})`);
|
||||
|
||||
// Map job names to our build targets. Declared outside the try so
|
||||
// the catch fallback can reuse the same keys.
|
||||
const jobMappings = {
|
||||
'Android Phone': ['🤖 Build Android APK (Phone)', 'build-android-phone'],
|
||||
'Android TV': ['🤖 Build Android APK (TV)', 'build-android-tv'],
|
||||
'iOS': ['🍎 Build iOS IPA (Phone)', 'build-ios-phone'],
|
||||
'iOS Unsigned': ['🍎 Build iOS IPA (Phone - Unsigned)', 'build-ios-phone-unsigned'],
|
||||
'tvOS': ['🍎 Build tvOS IPA', 'build-ios-tv'],
|
||||
'tvOS Unsigned': ['🍎 Build tvOS IPA (Unsigned)', 'build-ios-tv-unsigned']
|
||||
};
|
||||
|
||||
try {
|
||||
// Get all jobs for this workflow run
|
||||
const { data: jobs } = await github.rest.actions.listJobsForWorkflowRun({
|
||||
@@ -229,10 +243,8 @@ jobs:
|
||||
|
||||
// Create individual status for each job
|
||||
for (const [platform, jobNames] of Object.entries(jobMappings)) {
|
||||
const job = jobs.jobs.find(j =>
|
||||
jobNames.some(name => j.name.includes(name) || j.name === name)
|
||||
);
|
||||
|
||||
const job = findJobForTarget(jobs.jobs, jobNames);
|
||||
|
||||
if (job) {
|
||||
buildStatuses[platform] = {
|
||||
name: job.name,
|
||||
@@ -358,6 +370,43 @@ jobs:
|
||||
console.log(`- Artifact: ${artifact.name} (from run ${artifact.workflow_run.id})`);
|
||||
});
|
||||
|
||||
// Pull per-job durations from the latest successful develop build so
|
||||
// in-progress / queued targets can show a realistic ETA instead of
|
||||
// an open-ended spinner. Best-effort: any failure just drops the ETA.
|
||||
let referenceDurations = {};
|
||||
try {
|
||||
const { data: devRuns } = await github.rest.actions.listWorkflowRuns({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'build-apps.yml',
|
||||
branch: 'develop',
|
||||
status: 'success',
|
||||
per_page: 1
|
||||
});
|
||||
|
||||
if (devRuns.workflow_runs.length > 0) {
|
||||
const refRun = devRuns.workflow_runs[0];
|
||||
const { data: refJobs } = await github.rest.actions.listJobsForWorkflowRun({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: refRun.id
|
||||
});
|
||||
|
||||
for (const [platform, jobNames] of Object.entries(jobMappings)) {
|
||||
const job = findJobForTarget(refJobs.jobs, jobNames);
|
||||
if (job && job.conclusion === 'success' && job.started_at && job.completed_at) {
|
||||
referenceDurations[platform] = new Date(job.completed_at) - new Date(job.started_at);
|
||||
}
|
||||
}
|
||||
console.log(`Reference durations from develop run ${refRun.id}:`,
|
||||
Object.fromEntries(Object.entries(referenceDurations).map(([k, v]) => [k, fmtDuration(v)])));
|
||||
} else {
|
||||
console.log('No successful develop build found for ETA reference');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Failed to fetch develop reference durations:', error.message);
|
||||
}
|
||||
|
||||
// Build comment body with progressive status for individual builds
|
||||
let commentBody = `## 🔧 Build Status for PR #${pr.number}\n\n`;
|
||||
commentBody += `🔗 **Commit**: [\`${targetCommitSha.substring(0, 7)}\`](https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${targetCommitSha})\n\n`; // Progressive build status and downloads table
|
||||
@@ -369,9 +418,9 @@ jobs:
|
||||
const buildTargets = [
|
||||
{ name: 'Android Phone', platform: '🤖', device: '📱 Phone', statusKey: 'Android Phone', artifactPattern: /android.*phone/i },
|
||||
{ name: 'Android TV', platform: '🤖', device: '📺 TV', statusKey: 'Android TV', artifactPattern: /android.*tv/i },
|
||||
{ name: 'iOS', platform: '🍎', device: '📱 Phone', statusKey: 'iOS', artifactPattern: /ios.*phone.*ipa(?!.*unsigned)/i },
|
||||
{ name: 'iOS', platform: '🍎', device: '📱 Phone', statusKey: 'iOS', artifactPattern: /^(?!.*unsigned).*ios.*phone.*ipa/i },
|
||||
{ name: 'iOS Unsigned', platform: '🍎', device: '📱 Phone Unsigned', statusKey: 'iOS Unsigned', artifactPattern: /ios.*phone.*unsigned/i },
|
||||
{ name: 'tvOS', platform: '🍎', device: '📺 TV', statusKey: 'tvOS', artifactPattern: /ios.*tv.*ipa(?!.*unsigned)/i },
|
||||
{ name: 'tvOS', platform: '🍎', device: '📺 TV', statusKey: 'tvOS', artifactPattern: /^(?!.*unsigned).*ios.*tv.*ipa/i },
|
||||
{ name: 'tvOS Unsigned', platform: '🍎', device: '📺 TV Unsigned', statusKey: 'tvOS Unsigned', artifactPattern: /ios.*tv.*unsigned/i }
|
||||
];
|
||||
|
||||
@@ -387,11 +436,12 @@ jobs:
|
||||
let status = '⏳ Pending';
|
||||
let downloadLink = '*Waiting for build...*';
|
||||
|
||||
// tvOS builds are temporarily disabled until feat/tv-interface
|
||||
// is merged - show them as disabled instead of stuck pending.
|
||||
if (target.name === 'tvOS' || target.name === 'tvOS Unsigned') {
|
||||
// Signed tvOS stays disabled until EAS has tvOS provisioning
|
||||
// profiles (app + TopShelf targets); non-interactive builds can't
|
||||
// create them. Unsigned tvOS builds, so it flows through normally.
|
||||
if (target.name === 'tvOS') {
|
||||
status = '💤 Disabled';
|
||||
downloadLink = '*Disabled until feat/tv-interface is merged*';
|
||||
downloadLink = '*Disabled — signed tvOS needs EAS provisioning profiles*';
|
||||
} else if (matchingStatus) {
|
||||
if (matchingStatus.conclusion === 'success' && matchingArtifact) {
|
||||
status = '✅ Complete';
|
||||
@@ -406,11 +456,9 @@ jobs:
|
||||
let durationInfo = '';
|
||||
if (matchingStatus.started_at && matchingStatus.completed_at) {
|
||||
const durationMs = new Date(matchingStatus.completed_at) - new Date(matchingStatus.started_at);
|
||||
const durationMin = Math.floor(durationMs / 60000);
|
||||
const durationSec = Math.floor((durationMs % 60000) / 1000);
|
||||
durationInfo = ` - ${durationMin}m ${durationSec}s`;
|
||||
durationInfo = ` - ${fmtDuration(durationMs)}`;
|
||||
}
|
||||
|
||||
|
||||
downloadLink = `[📥 Download ${fileType}](${directLink}) ${sizeInfo}${durationInfo}`;
|
||||
} else if (matchingStatus.conclusion === 'failure') {
|
||||
status = `❌ [Failed](${matchingStatus.url})`;
|
||||
@@ -420,10 +468,16 @@ jobs:
|
||||
downloadLink = '*Build cancelled*';
|
||||
} else if (matchingStatus.status === 'in_progress') {
|
||||
status = `🔄 [Building...](${matchingStatus.url})`;
|
||||
downloadLink = '*Build in progress...*';
|
||||
const ref = referenceDurations[target.statusKey];
|
||||
downloadLink = ref
|
||||
? `*Building… ~${fmtDuration(ref)} (avg on develop)*`
|
||||
: '*Build in progress...*';
|
||||
} else if (matchingStatus.status === 'queued') {
|
||||
status = `⏳ [Queued](${matchingStatus.url})`;
|
||||
downloadLink = '*Waiting to start...*';
|
||||
const ref = referenceDurations[target.statusKey];
|
||||
downloadLink = ref
|
||||
? `*Waiting to start… ~${fmtDuration(ref)} once running (avg on develop)*`
|
||||
: '*Waiting to start...*';
|
||||
} else if (matchingStatus.status === 'completed' && !matchingStatus.conclusion) {
|
||||
// Workflow completed but conclusion not yet available (rare edge case)
|
||||
status = `🔄 [Finishing...](${matchingStatus.url})`;
|
||||
@@ -444,7 +498,22 @@ jobs:
|
||||
|
||||
commentBody += `\n`;
|
||||
|
||||
// Show installation instructions if we have any artifacts
|
||||
// Static rundown of the build optimisations + what each artifact
|
||||
// installs on. Always shown (even mid-build) so testers know what
|
||||
// to expect before downloads are ready.
|
||||
commentBody += `<details>\n`;
|
||||
commentBody += `<summary>📦 Build details & device compatibility</summary>\n\n`;
|
||||
commentBody += `These CI builds are trimmed for size and speed. What that means for installing them:\n\n`;
|
||||
commentBody += `| Artifact | Architectures | Installs on |\n`;
|
||||
commentBody += `|---|---|---|\n`;
|
||||
commentBody += `| 🤖 Android Phone APK | \`arm64-v8a\` | Every 64-bit Android phone (all since ~2017). **Not** an x86_64 emulator or a 32-bit device. |\n`;
|
||||
commentBody += `| 📺 Android TV APK | \`arm64-v8a\` + \`armeabi-v7a\` | Modern boxes **and** older / cheap 32-bit Android TV sticks. No x86_64. |\n`;
|
||||
commentBody += `| 🍎 iOS / tvOS IPA | \`arm64\` | iPhone / Apple TV (all current devices). |\n\n`;
|
||||
commentBody += `**Why no x86_64?** That slice only runs on Android emulators / Chromebooks, never a real phone or TV box — dropping it shrinks the APK and speeds up the build. Local \`bun run android\` is unaffected (it still builds x86_64 from \`app.json\`).\n\n`;
|
||||
commentBody += `**Runners:** Android on \`ubuntu-26.04\`; iOS / tvOS on Apple Silicon (\`macos-26\`). The size/speed win comes from the ABI trim above, not the runner.\n`;
|
||||
commentBody += `</details>\n\n`;
|
||||
|
||||
// Installation instructions only matter once something is downloadable.
|
||||
if (allArtifacts.length > 0) {
|
||||
commentBody += `### 🔧 Installation Instructions\n\n`;
|
||||
commentBody += `- **Android APK**: Download and install directly on your device (enable "Install from unknown sources")\n`;
|
||||
|
||||
103
.github/workflows/build-apps.yml
vendored
103
.github/workflows/build-apps.yml
vendored
@@ -23,7 +23,7 @@ env:
|
||||
jobs:
|
||||
build-android-phone:
|
||||
if: (!contains(github.event.head_commit.message, '[skip ci]'))
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
name: 🤖 Build Android APK (Phone)
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
android: false
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
large-packages: false
|
||||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
@@ -52,31 +52,40 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 💾 Cache Bun dependencies
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-develop
|
||||
${{ runner.os }}-bun-develop
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: 📦 Install dependencies and reload submodules
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
bun run submodule-reload
|
||||
|
||||
- name: ☕ Set up JDK 17
|
||||
# ubuntu-26.04 defaults to JDK 25, which breaks the RN/AGP native build
|
||||
# (Kotlin falls back to JVM_23, the foojay toolchain + CMake configure
|
||||
# fail). Pin Temurin 17 for a deterministic Android build.
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "17"
|
||||
|
||||
- name: 💾 Cache Gradle global
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/caches/modules-2
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||
|
||||
- name: 🛠️ Generate project files
|
||||
run: bun run prebuild
|
||||
@@ -85,12 +94,16 @@ jobs:
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: android/.gradle
|
||||
key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-android-gradle-develop
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-${{ runner.arch }}-android-gradle-develop
|
||||
|
||||
- name: 🚀 Build APK
|
||||
env:
|
||||
EXPO_TV: 0
|
||||
# CI artifact ships arm64 only (phones; emulators/Chromebooks not a
|
||||
# sideload target). Overrides app.json buildArchs for this build only,
|
||||
# so local `bun run android` (x86_64 emulator) is unaffected.
|
||||
ORG_GRADLE_PROJECT_reactNativeArchitectures: arm64-v8a
|
||||
run: bun run build:android:local
|
||||
|
||||
- name: 📅 Set date tag
|
||||
@@ -106,7 +119,7 @@ jobs:
|
||||
|
||||
build-android-tv:
|
||||
if: (!contains(github.event.head_commit.message, '[skip ci]'))
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
name: 🤖 Build Android APK (TV)
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -120,7 +133,7 @@ jobs:
|
||||
android: false
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
large-packages: false
|
||||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
@@ -135,31 +148,40 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 💾 Cache Bun dependencies
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-develop
|
||||
${{ runner.os }}-bun-develop
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: 📦 Install dependencies and reload submodules
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
bun run submodule-reload
|
||||
|
||||
- name: ☕ Set up JDK 17
|
||||
# ubuntu-26.04 defaults to JDK 25, which breaks the RN/AGP native build
|
||||
# (Kotlin falls back to JVM_23, the foojay toolchain + CMake configure
|
||||
# fail). Pin Temurin 17 for a deterministic Android build.
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "17"
|
||||
|
||||
- name: 💾 Cache Gradle global
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/caches/modules-2
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||
|
||||
- name: 🛠️ Generate project files
|
||||
run: bun run prebuild:tv
|
||||
@@ -168,12 +190,15 @@ jobs:
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: android/.gradle
|
||||
key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-android-gradle-develop
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-${{ runner.arch }}-android-gradle-develop
|
||||
|
||||
- name: 🚀 Build APK
|
||||
env:
|
||||
EXPO_TV: 1
|
||||
# TV artifact keeps armeabi-v7a too: many older/cheap Android TV boxes
|
||||
# and sticks are still 32-bit ARM. Drops only x86_64. CI build only.
|
||||
ORG_GRADLE_PROJECT_reactNativeArchitectures: arm64-v8a,armeabi-v7a
|
||||
run: bun run build:android:local
|
||||
|
||||
- name: 📅 Set date tag
|
||||
@@ -206,15 +231,16 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 💾 Cache Bun dependencies
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-cache
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: 📦 Install dependencies and reload submodules
|
||||
run: |
|
||||
@@ -273,15 +299,16 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 💾 Cache Bun dependencies
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-cache
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: 📦 Install dependencies and reload submodules
|
||||
run: |
|
||||
@@ -313,8 +340,10 @@ jobs:
|
||||
retention-days: 7
|
||||
|
||||
build-ios-tv:
|
||||
# Temporarily disabled until feat/tv-interface is merged (TV UI not ready).
|
||||
# Re-enable by removing the `false &&` prefix below.
|
||||
# Disabled: EAS has no provisioning profiles / distribution cert for the tvOS
|
||||
# targets (app + StreamyfinTopShelf extension), so non-interactive signed
|
||||
# builds fail. Set up tvOS credentials in EAS (`eas credentials`), then remove
|
||||
# the `false &&` prefix below. Unsigned tvOS builds run (see job below).
|
||||
if: false && (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin'))
|
||||
runs-on: macos-26
|
||||
name: 🍎 Build tvOS IPA
|
||||
@@ -333,15 +362,16 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 💾 Cache Bun dependencies
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-cache
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: 📦 Install dependencies and reload submodules
|
||||
run: |
|
||||
@@ -401,15 +431,16 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 💾 Cache Bun dependencies
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-cache
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: 📦 Install dependencies and reload submodules
|
||||
run: |
|
||||
|
||||
9
.github/workflows/check-lockfile.yml
vendored
9
.github/workflows/check-lockfile.yml
vendored
@@ -13,7 +13,7 @@ concurrency:
|
||||
jobs:
|
||||
check-lockfile:
|
||||
name: 🔍 Check bun.lock and package.json consistency
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -29,14 +29,17 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 💾 Cache Bun dependencies
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: 🛡️ Verify lockfile consistency
|
||||
run: |
|
||||
|
||||
5
.github/workflows/ci-codeql.yml
vendored
5
.github/workflows/ci-codeql.yml
vendored
@@ -8,11 +8,14 @@ on:
|
||||
schedule:
|
||||
- cron: '24 2 * * *'
|
||||
|
||||
concurrency:
|
||||
group: codeql-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: 🔎 Analyze with CodeQL
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
2
.github/workflows/conflict.yml
vendored
2
.github/workflows/conflict.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
label:
|
||||
name: 🏷️ Labeling Merge Conflicts
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
if: ${{ github.repository == 'streamyfin/streamyfin' }}
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
2
.github/workflows/crowdin.yml
vendored
2
.github/workflows/crowdin.yml
vendored
@@ -19,7 +19,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
sync-translations:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-26.04
|
||||
|
||||
steps:
|
||||
- name: 📥 Checkout Repository
|
||||
|
||||
5
.github/workflows/detect-duplicate.yml
vendored
5
.github/workflows/detect-duplicate.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
detect:
|
||||
name: 🔍 Find similar issues
|
||||
if: github.actor != 'github-actions[bot]'
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
@@ -26,7 +26,8 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 🔍 Detect duplicate issues
|
||||
run: bun scripts/detect-duplicate-issue.mjs
|
||||
|
||||
21
.github/workflows/linting.yml
vendored
21
.github/workflows/linting.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
validate_pr_title:
|
||||
name: "📝 Validate PR Title"
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
|
||||
dependency-review:
|
||||
name: 🔍 Vulnerable Dependencies
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
@@ -65,8 +65,7 @@ jobs:
|
||||
|
||||
expo-doctor:
|
||||
name: 🚑 Expo Doctor Check
|
||||
if: false
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
- name: 🛒 Checkout repository
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -78,17 +77,21 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 📦 Install dependencies (bun)
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: 🚑 Run Expo Doctor
|
||||
# Re-enabled but non-blocking: surfaces doctor warnings in the logs
|
||||
# without failing the gate (some checks are known-noisy for this setup).
|
||||
continue-on-error: true
|
||||
run: bun expo-doctor
|
||||
|
||||
code_quality:
|
||||
name: "🔍 Lint & Test (${{ matrix.command }})"
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -110,12 +113,14 @@ jobs:
|
||||
- name: "🟢 Setup Node.js"
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '24.x'
|
||||
# renovate: datasource=node-version depName=node versioning=node
|
||||
node-version: "24.16.0"
|
||||
|
||||
- name: "🍞 Setup Bun"
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: "📦 Install dependencies"
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
4
.github/workflows/notification.yml
vendored
4
.github/workflows/notification.yml
vendored
@@ -12,7 +12,7 @@ on:
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- name: 🛎️ Notify Discord
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
🔗 ${{ github.event.pull_request.html_url }}
|
||||
|
||||
notify-on-failure:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
if: github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure'
|
||||
steps:
|
||||
- name: 🚨 Notify Discord on Failure
|
||||
|
||||
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@@ -22,8 +22,9 @@ on:
|
||||
jobs:
|
||||
approve:
|
||||
name: 🔐 Approve release
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
environment: production
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: ✅ Release approved
|
||||
run: echo "Release approved for ${{ github.sha }}"
|
||||
@@ -31,7 +32,7 @@ jobs:
|
||||
build:
|
||||
name: 🚀 ${{ matrix.name }}
|
||||
needs: approve
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
@@ -72,15 +73,16 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 💾 Cache Bun dependencies
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-cache
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: 📦 Install dependencies and reload submodules
|
||||
run: |
|
||||
@@ -176,7 +178,7 @@ jobs:
|
||||
name: 📦 Draft GitHub Release
|
||||
needs: build
|
||||
if: ${{ !cancelled() }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: write
|
||||
actions: read # required for `gh run download` to list/fetch this run's artifacts
|
||||
|
||||
18
.github/workflows/trivy-scan.yml
vendored
18
.github/workflows/trivy-scan.yml
vendored
@@ -21,7 +21,7 @@ concurrency:
|
||||
jobs:
|
||||
trivy:
|
||||
name: 🔎 Filesystem scan
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write # upload SARIF to code scanning
|
||||
@@ -29,19 +29,9 @@ jobs:
|
||||
- name: 📥 Checkout repository
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
# Rotate the DB cache weekly (matches the scheduled scan): cache hits within the week
|
||||
# instead of a fresh immutable entry per run, still refreshing the DB every week.
|
||||
- name: 🗓️ Compute weekly Trivy cache key
|
||||
id: trivy-cache-key
|
||||
run: echo "value=trivy-db-${{ runner.os }}-$(date -u +%G-%V)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 💾 Cache Trivy vulnerability DB
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.cache/trivy
|
||||
key: ${{ steps.trivy-cache-key.outputs.value }}
|
||||
restore-keys: trivy-db-${{ runner.os }}-
|
||||
|
||||
# Trivy's own action caches the vulnerability DB + binary internally
|
||||
# (cache-trivy-* / trivy-binary-* entries), so no manual ~/.cache/trivy
|
||||
# step is needed — it only duplicated the cache.
|
||||
- name: 🔎 Run Trivy filesystem scan
|
||||
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
||||
with:
|
||||
|
||||
5
.github/workflows/update-issue-form.yml
vendored
5
.github/workflows/update-issue-form.yml
vendored
@@ -20,7 +20,7 @@ permissions:
|
||||
jobs:
|
||||
update-issue-form:
|
||||
name: 🔢 Populate version dropdown
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
@@ -36,7 +36,8 @@ jobs:
|
||||
- name: 🍞 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
# renovate: datasource=npm depName=bun
|
||||
bun-version: "1.3.14"
|
||||
|
||||
- name: 🔢 Populate version dropdown from GitHub releases
|
||||
id: populate
|
||||
|
||||
8
eas.json
8
eas.json
@@ -52,7 +52,7 @@
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"bun": "1.3.5",
|
||||
"bun": "1.3.14",
|
||||
"environment": "production",
|
||||
"autoIncrement": true,
|
||||
"android": {
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
"production-apk": {
|
||||
"bun": "1.3.5",
|
||||
"bun": "1.3.14",
|
||||
"environment": "production",
|
||||
"autoIncrement": true,
|
||||
"android": {
|
||||
@@ -74,7 +74,7 @@
|
||||
}
|
||||
},
|
||||
"production-apk-tv": {
|
||||
"bun": "1.3.5",
|
||||
"bun": "1.3.14",
|
||||
"environment": "production",
|
||||
"autoIncrement": true,
|
||||
"android": {
|
||||
@@ -87,7 +87,7 @@
|
||||
}
|
||||
},
|
||||
"production_tv": {
|
||||
"bun": "1.3.5",
|
||||
"bun": "1.3.14",
|
||||
"environment": "production",
|
||||
"autoIncrement": true,
|
||||
"env": {
|
||||
|
||||
@@ -302,7 +302,7 @@ function parseArgs(argv: string[]): BuildOptions {
|
||||
if (!configArg) {
|
||||
throw new Error("--configuration requires an argument");
|
||||
}
|
||||
options.configuration = (configArg as "Debug" | "Release") || "Debug";
|
||||
options.configuration = configArg as "Debug" | "Release";
|
||||
break;
|
||||
}
|
||||
case "--device":
|
||||
@@ -997,10 +997,6 @@ async function waitForSimulatorBoot(
|
||||
}
|
||||
} catch {
|
||||
// Simulator not found or not booted yet, continue polling
|
||||
if (pollIntervalMs > 1000) {
|
||||
// Only log if we've been waiting a while to avoid spam
|
||||
// console.warn("Simulator polling failed, retrying...");
|
||||
}
|
||||
}
|
||||
|
||||
// Wait before next poll
|
||||
|
||||
Reference in New Issue
Block a user