diff --git a/.github/workflows/conflict.yml b/.github/workflows/conflict.yml index 7793851c2..9f2e68a50 100644 --- a/.github/workflows/conflict.yml +++ b/.github/workflows/conflict.yml @@ -1,24 +1,29 @@ -name: 🏷️🔀Merge Conflict Labeler - -on: - push: - branches: [develop] - pull_request_target: - branches: [develop] - types: [synchronize] - -jobs: - label: - name: 🏷️ Labeling Merge Conflicts - runs-on: ubuntu-24.04 - if: ${{ github.repository == 'streamyfin/streamyfin' }} - permissions: - contents: read - pull-requests: write - steps: - - name: 🚩 Apply merge conflict label - uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 - with: - dirtyLabel: '⚔️ merge-conflict' - commentOnDirty: 'This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.' - repoToken: '${{ secrets.GITHUB_TOKEN }}' +name: 🏷️🔀Merge Conflict Labeler + +on: + push: + branches: [develop] + # SECURITY: pull_request_target runs with the base repo's write token and secrets. + # This job only labels via the API and is safe ONLY because it never checks out or + # runs the PR head's code. NEVER add `actions/checkout` of the PR head (or any `run:` + # that interpolates PR-controlled data) to this workflow — that would turn it into a + # full repo-compromise vector. + pull_request_target: + branches: [develop] + types: [synchronize] + +jobs: + label: + name: 🏷️ Labeling Merge Conflicts + runs-on: ubuntu-24.04 + if: ${{ github.repository == 'streamyfin/streamyfin' }} + permissions: + contents: read + pull-requests: write + steps: + - name: 🚩 Apply merge conflict label + uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 + with: + dirtyLabel: '⚔️ merge-conflict' + commentOnDirty: 'This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.' + repoToken: '${{ secrets.GITHUB_TOKEN }}' diff --git a/scripts/automerge.sh b/scripts/automerge.sh index d66a09413..1077a8b10 100755 --- a/scripts/automerge.sh +++ b/scripts/automerge.sh @@ -1,12 +1,22 @@ #!/bin/bash -[[ -z $(git status --porcelain) ]] && -git checkout master && -git pull --ff-only && -git checkout develop && -git merge master && -git push --follow-tags && -git checkout master && -git merge develop --ff-only && -git push && -git checkout develop || -(echo "Error: Failed to merge" && exit 1) \ No newline at end of file +# Local helper: fast-forward master into develop and back. Aborts on any failure and +# restores the branch you started on. Not used in CI. +set -euo pipefail + +if [[ -n $(git status --porcelain) ]]; then + echo "Error: working tree is not clean — commit or stash first." >&2 + exit 1 +fi + +start_branch=$(git rev-parse --abbrev-ref HEAD) +trap 'git checkout "$start_branch" >/dev/null 2>&1 || true' EXIT + +git checkout master +git pull --ff-only +git checkout develop +git merge master +git push --follow-tags +git checkout master +git merge develop --ff-only +git push +git checkout develop diff --git a/scripts/symlink-native-dirs.js b/scripts/symlink-native-dirs.js index dd014c996..dab4688c6 100644 --- a/scripts/symlink-native-dirs.js +++ b/scripts/symlink-native-dirs.js @@ -1,62 +1,28 @@ #!/usr/bin/env node -const _fs = require("node:fs"); +// Symlinks the platform-specific native dirs to `ios` / `android` depending on EXPO_TV. +// Uses fs APIs (no shell) so there is no command-injection surface. + +const fs = require("node:fs"); const path = require("node:path"); -const process = require("node:process"); -const { execSync } = require("node:child_process"); const root = process.cwd(); -// const tvosPath = path.join(root, 'iostv'); -// const iosPath = path.join(root, 'iosmobile'); -// const androidPath = path.join(root, 'androidmobile'); -// const androidTVPath = path.join(root, 'androidtv'); -// const device = process.argv[2]; -// const platform = process.argv[2]; -const isTV = process.env.EXPO_TV || false; +const isTV = process.env.EXPO_TV && process.env.EXPO_TV !== "0"; -const paths = new Map([ - ["tvos", path.join(root, "iostv")], - ["ios", path.join(root, "iosmobile")], - ["android", path.join(root, "androidmobile")], - ["androidtv", path.join(root, "androidtv")], -]); +const links = isTV + ? { ios: path.join(root, "iostv"), android: path.join(root, "androidtv") } + : { + ios: path.join(root, "iosmobile"), + android: path.join(root, "androidmobile"), + }; -// const platformPath = paths.get(platform); - -if (isTV) { - stdout = execSync( - `mkdir -p ${paths.get("tvos")}; ln -nsf ${paths.get("tvos")} ios`, - ); - console.log(stdout.toString()); - stdout = execSync( - `mkdir -p ${paths.get("androidtv")}; ln -nsf ${paths.get( - "androidtv", - )} android`, - ); - console.log(stdout.toString()); -} else { - stdout = execSync( - `mkdir -p ${paths.get("ios")}; ln -nsf ${paths.get("ios")} ios`, - ); - console.log(stdout.toString()); - stdout = execSync( - `mkdir -p ${paths.get("android")}; ln -nsf ${paths.get("android")} android`, - ); - console.log(stdout.toString()); +for (const [link, target] of Object.entries(links)) { + fs.mkdirSync(target, { recursive: true }); + try { + fs.unlinkSync(link); // replace an existing symlink/file (ln -nsf) + } catch { + // nothing to remove + } + fs.symlinkSync(target, link); + console.log(`${link} -> ${target}`); } - -// target = ""; -// switch (platform) { -// case "tvos": -// target = "ios"; -// break; -// case "ios": -// target = "ios"; -// break; -// case "android": -// target = "android"; -// break; -// case "androidtv": -// target = "android"; -// break; -// }