mirror of
https://github.com/seerr-team/seerr.git
synced 2026-08-02 19:22:24 -04:00
chore(release): prepare v3.4.1 release
This commit is contained in:
62
.github/workflows/ci.yml
vendored
62
.github/workflows/ci.yml
vendored
@@ -39,13 +39,14 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
- parallel:
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@@ -118,14 +119,15 @@ jobs:
|
||||
CI: true
|
||||
run: pnpm install
|
||||
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
- parallel:
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
|
||||
- name: Formatting
|
||||
run: pnpm format:check
|
||||
- name: Formatting
|
||||
run: pnpm format:check
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
unit-test:
|
||||
name: Unit Tests
|
||||
@@ -186,18 +188,19 @@ jobs:
|
||||
arch: arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
|
||||
- name: Commit timestamp
|
||||
id: ts
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
|
||||
- name: Warm cache (no push) — ${{ matrix.platform }}
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
with:
|
||||
@@ -222,17 +225,14 @@ jobs:
|
||||
packages: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Commit timestamp
|
||||
id: ts
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
@@ -247,6 +247,10 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Commit timestamp
|
||||
id: ts
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
|
||||
|
||||
19
.github/workflows/codeql.yml
vendored
19
.github/workflows/codeql.yml
vendored
@@ -36,16 +36,17 @@ jobs:
|
||||
matrix:
|
||||
language: [actions, javascript]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1
|
||||
|
||||
46
.github/workflows/create-tag.yml
vendored
46
.github/workflows/create-tag.yml
vendored
@@ -22,16 +22,17 @@ jobs:
|
||||
outputs:
|
||||
tag_version: ${{ steps.git-cliff.outputs.tag_version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install git-cliff
|
||||
uses: taiki-e/install-action@25435dc8dd3baed7417e0c96d3fe89013a5b2e09 # v2.81.3
|
||||
with:
|
||||
tool: git-cliff
|
||||
- name: Install git-cliff
|
||||
uses: taiki-e/install-action@25435dc8dd3baed7417e0c96d3fe89013a5b2e09 # v2.81.3
|
||||
with:
|
||||
tool: git-cliff
|
||||
|
||||
- name: Get tag version
|
||||
id: git-cliff
|
||||
@@ -59,21 +60,22 @@ jobs:
|
||||
with:
|
||||
ssh-key: '${{ secrets.COMMIT_KEY }}'
|
||||
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
- parallel:
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
# For workflows with elevated privileges we recommend disabling automatic caching.
|
||||
# https://github.com/actions/setup-node
|
||||
package-manager-cache: false
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
# For workflows with elevated privileges we recommend disabling automatic caching.
|
||||
# https://github.com/actions/setup-node
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Configure git
|
||||
run: |
|
||||
git config --global user.name "${{ github.actor }}"
|
||||
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
||||
- name: Configure git
|
||||
run: |
|
||||
git config --global user.name "${{ github.actor }}"
|
||||
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
||||
|
||||
- name: Bump package.json
|
||||
run: npm version ${TAG_VERSION} --no-commit-hooks --no-git-tag-version
|
||||
|
||||
16
.github/workflows/cypress.yml
vendored
16
.github/workflows/cypress.yml
vendored
@@ -40,15 +40,15 @@ jobs:
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: package.json
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: package.json
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
13
.github/workflows/detect-duplicate.yml
vendored
13
.github/workflows/detect-duplicate.yml
vendored
@@ -25,13 +25,14 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
- parallel:
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
|
||||
16
.github/workflows/docs-deploy.yml
vendored
16
.github/workflows/docs-deploy.yml
vendored
@@ -28,15 +28,15 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: package.json
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: package.json
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: sh
|
||||
|
||||
113
.github/workflows/helm.yml
vendored
113
.github/workflows/helm.yml
vendored
@@ -27,24 +27,25 @@ jobs:
|
||||
outputs:
|
||||
has_artifacts: ${{ steps.check-artifacts.outputs.has_artifacts }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
|
||||
- name: Install helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
|
||||
|
||||
- name: Install Oras
|
||||
uses: oras-project/setup-oras@38de303aac69abb66f3e6255b7198bff35f323e3 # v2
|
||||
- name: Install Oras
|
||||
uses: oras-project/setup-oras@38de303aac69abb66f3e6255b7198bff35f323e3 # v2
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Package helm charts
|
||||
run: |
|
||||
@@ -92,33 +93,34 @@ jobs:
|
||||
needs: [package-helm-chart]
|
||||
if: needs.package-helm-chart.outputs.has_artifacts == 'true'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
|
||||
- name: Install helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
|
||||
|
||||
- name: Install Oras
|
||||
uses: oras-project/setup-oras@38de303aac69abb66f3e6255b7198bff35f323e3 # v2
|
||||
- name: Install Oras
|
||||
uses: oras-project/setup-oras@38de303aac69abb66f3e6255b7198bff35f323e3 # v2
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
|
||||
- name: Downloads artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: artifacts
|
||||
path: .cr-release-packages/
|
||||
- name: Downloads artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: artifacts
|
||||
path: .cr-release-packages/
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Push charts to GHCR
|
||||
env:
|
||||
@@ -150,27 +152,28 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
|
||||
- name: Downloads artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: artifacts
|
||||
path: .cr-release-packages/
|
||||
- name: Downloads artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: artifacts
|
||||
path: .cr-release-packages/
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Verify signatures for each chart tag
|
||||
run: |
|
||||
|
||||
40
.github/workflows/lint-helm-charts.yml
vendored
40
.github/workflows/lint-helm-charts.yml
vendored
@@ -27,29 +27,31 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
|
||||
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@6ec842c01de15ebb84c8627d2744a0c2f2755c9f # v2.8.0
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@6ec842c01de15ebb84c8627d2744a0c2f2755c9f # v2.8.0
|
||||
|
||||
- name: Ensure documentation is updated
|
||||
uses: docker://jnorwood/helm-docs:v1.14.2@sha256:7e562b49ab6b1dbc50c3da8f2dd6ffa8a5c6bba327b1c6335cc15ce29267979c
|
||||
- parallel:
|
||||
- name: Ensure documentation is updated
|
||||
uses: docker://jnorwood/helm-docs:v1.14.2@sha256:7e562b49ab6b1dbc50c3da8f2dd6ffa8a5c6bba327b1c6335cc15ce29267979c
|
||||
|
||||
- name: Run chart-testing (list-changed)
|
||||
id: list-changed
|
||||
run: |
|
||||
changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }})
|
||||
if [[ -n "$changed" ]]; then
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
echo "$changed"
|
||||
fi
|
||||
- name: Run chart-testing (list-changed)
|
||||
id: list-changed
|
||||
run: |
|
||||
changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }})
|
||||
if [[ -n "$changed" ]]; then
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
echo "$changed"
|
||||
fi
|
||||
|
||||
- name: Run chart-testing
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
|
||||
387
.github/workflows/pr-validation.yml
vendored
387
.github/workflows/pr-validation.yml
vendored
@@ -31,82 +31,83 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
if: always() && steps.lint_pr_title.outputs.error_message != null
|
||||
env:
|
||||
ERROR_MESSAGE: ${{ steps.lint_pr_title.outputs.error_message }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const message = process.env.ERROR_MESSAGE;
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
- parallel:
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
if: always() && steps.lint_pr_title.outputs.error_message != null
|
||||
env:
|
||||
ERROR_MESSAGE: ${{ steps.lint_pr_title.outputs.error_message }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const message = process.env.ERROR_MESSAGE;
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
|
||||
const body = [
|
||||
`### PR Title Validation Failed\n`,
|
||||
message,
|
||||
`\n---\n`,
|
||||
`PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).`,
|
||||
`*This check will re-run when you update your PR title.*`,
|
||||
].join('\n');
|
||||
const body = [
|
||||
`### PR Title Validation Failed\n`,
|
||||
message,
|
||||
`\n---\n`,
|
||||
`PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).`,
|
||||
`*This check will re-run when you update your PR title.*`,
|
||||
].join('\n');
|
||||
|
||||
const allComments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
const allComments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
|
||||
const botComment = allComments.find(
|
||||
c => c.user.type === 'Bot' && c.body && c.body.includes('### PR Title Validation Failed')
|
||||
);
|
||||
const botComment = allComments.find(
|
||||
c => c.user.type === 'Bot' && c.body && c.body.includes('### PR Title Validation Failed')
|
||||
);
|
||||
|
||||
if (botComment) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
body,
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body,
|
||||
});
|
||||
}
|
||||
if (botComment) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
body,
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
if: always() && steps.lint_pr_title.outputs.error_message == null
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
if: always() && steps.lint_pr_title.outputs.error_message == null
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
|
||||
const allComments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
const allComments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
|
||||
const botComment = allComments.find(
|
||||
c => c.user.type === 'Bot' && c.body && c.body.includes('### PR Title Validation Failed')
|
||||
);
|
||||
const botComment = allComments.find(
|
||||
c => c.user.type === 'Bot' && c.body && c.body.includes('### PR Title Validation Failed')
|
||||
);
|
||||
|
||||
if (botComment) {
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
});
|
||||
}
|
||||
if (botComment) {
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
});
|
||||
}
|
||||
|
||||
template-check:
|
||||
name: Validate PR Template
|
||||
@@ -117,10 +118,16 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Skip bot PRs
|
||||
id: bot-check
|
||||
if: github.event.pull_request.user.type == 'Bot'
|
||||
run: echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
@@ -128,11 +135,6 @@ jobs:
|
||||
node-version-file: 'package.json'
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Skip bot PRs
|
||||
id: bot-check
|
||||
if: github.event.pull_request.user.type == 'Bot'
|
||||
run: echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Write PR body to file
|
||||
if: steps.bot-check.outputs.skip != 'true'
|
||||
env:
|
||||
@@ -156,130 +158,131 @@ jobs:
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
|
||||
- name: Label and comment on failure
|
||||
if: steps.bot-check.outputs.skip != 'true' && steps.check.outputs.exit_code != '0'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
env:
|
||||
ISSUES_JSON: ${{ steps.check.outputs.issues }}
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const issues = JSON.parse(process.env.ISSUES_JSON);
|
||||
const author = process.env.PR_AUTHOR;
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const LABEL = 'blocked:template';
|
||||
- parallel:
|
||||
- name: Label and comment on failure
|
||||
if: steps.bot-check.outputs.skip != 'true' && steps.check.outputs.exit_code != '0'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
env:
|
||||
ISSUES_JSON: ${{ steps.check.outputs.issues }}
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const issues = JSON.parse(process.env.ISSUES_JSON);
|
||||
const author = process.env.PR_AUTHOR;
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const LABEL = 'blocked:template';
|
||||
|
||||
const issueList = issues.map(i => `- ${i}`).join('\n');
|
||||
const issueList = issues.map(i => `- ${i}`).join('\n');
|
||||
|
||||
const commentBody = [
|
||||
`Hey @${author}, thanks for submitting this PR! However, it looks like the PR template hasn't been fully filled out.\n`,
|
||||
`### Issues found:\n`,
|
||||
issueList,
|
||||
`\n---\n`,
|
||||
`**Please update your PR description to follow the [PR template](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/develop/.github/PULL_REQUEST_TEMPLATE.md).**`,
|
||||
`Incomplete or missing PR descriptions may indicate insufficient review of the changes, and PRs that do not follow the template **may be closed without review**.`,
|
||||
`See our [Contributing Guide](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/develop/CONTRIBUTING.md) for more details.\n`,
|
||||
`*This check will automatically re-run when you edit your PR description.*`,
|
||||
].join('\n');
|
||||
const commentBody = [
|
||||
`Hey @${author}, thanks for submitting this PR! However, it looks like the PR template hasn't been fully filled out.\n`,
|
||||
`### Issues found:\n`,
|
||||
issueList,
|
||||
`\n---\n`,
|
||||
`**Please update your PR description to follow the [PR template](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/develop/.github/PULL_REQUEST_TEMPLATE.md).**`,
|
||||
`Incomplete or missing PR descriptions may indicate insufficient review of the changes, and PRs that do not follow the template **may be closed without review**.`,
|
||||
`See our [Contributing Guide](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/develop/CONTRIBUTING.md) for more details.\n`,
|
||||
`*This check will automatically re-run when you edit your PR description.*`,
|
||||
].join('\n');
|
||||
|
||||
const allComments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
const allComments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
|
||||
const botComment = allComments.find(
|
||||
c => c.user.type === 'Bot' && c.body && c.body.includes('### Issues found:')
|
||||
);
|
||||
const botComment = allComments.find(
|
||||
c => c.user.type === 'Bot' && c.body && c.body.includes('### Issues found:')
|
||||
);
|
||||
|
||||
if (botComment) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
body: commentBody,
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: commentBody,
|
||||
});
|
||||
}
|
||||
if (botComment) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
body: commentBody,
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: commentBody,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: [LABEL],
|
||||
});
|
||||
} catch (e) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: LABEL,
|
||||
color: 'B60205',
|
||||
description: 'PR template not properly filled out',
|
||||
});
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: [LABEL],
|
||||
});
|
||||
} catch (e2) {
|
||||
console.log('Could not create/add label:', e2.message);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: [LABEL],
|
||||
});
|
||||
} catch (e) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: LABEL,
|
||||
color: 'B60205',
|
||||
description: 'PR template not properly filled out',
|
||||
});
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: [LABEL],
|
||||
});
|
||||
} catch (e2) {
|
||||
console.log('Could not create/add label:', e2.message);
|
||||
}
|
||||
}
|
||||
|
||||
core.setFailed('PR template is not properly filled out.');
|
||||
core.setFailed('PR template is not properly filled out.');
|
||||
|
||||
- name: Remove label on success
|
||||
if: steps.bot-check.outputs.skip != 'true' && steps.check.outputs.exit_code == '0'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const LABEL = 'blocked:template';
|
||||
- name: Remove label on success
|
||||
if: steps.bot-check.outputs.skip != 'true' && steps.check.outputs.exit_code == '0'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const LABEL = 'blocked:template';
|
||||
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: LABEL,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('Could not remove label', e.message);
|
||||
}
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: LABEL,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('Could not remove label', e.message);
|
||||
}
|
||||
|
||||
const allComments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
const allComments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
|
||||
const botComment = allComments.find(
|
||||
c => c.user.type === 'Bot' && c.body && c.body.includes('### Issues found:')
|
||||
);
|
||||
const botComment = allComments.find(
|
||||
c => c.user.type === 'Bot' && c.body && c.body.includes('### Issues found:')
|
||||
);
|
||||
|
||||
if (botComment) {
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
});
|
||||
}
|
||||
if (botComment) {
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
});
|
||||
}
|
||||
|
||||
73
.github/workflows/preview.yml
vendored
73
.github/workflows/preview.yml
vendored
@@ -32,28 +32,29 @@ jobs:
|
||||
arch: arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
|
||||
- name: Derive preview version from tag
|
||||
id: ver
|
||||
shell: bash
|
||||
run: |
|
||||
TAG="${GITHUB_REF_NAME}"
|
||||
VER="${TAG#preview-}"
|
||||
VER="${VER#v}"
|
||||
echo "version=${VER}" >> "$GITHUB_OUTPUT"
|
||||
echo "Building preview version: ${VER}"
|
||||
|
||||
- name: Commit timestamp
|
||||
id: ts
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
|
||||
- name: Derive preview version from tag
|
||||
id: ver
|
||||
shell: bash
|
||||
run: |
|
||||
TAG="${GITHUB_REF_NAME}"
|
||||
VER="${TAG#preview-}"
|
||||
VER="${VER#v}"
|
||||
echo "version=${VER}" >> "$GITHUB_OUTPUT"
|
||||
echo "Building preview version: ${VER}"
|
||||
|
||||
- name: Warm cache (no push) — ${{ matrix.platform }}
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
with:
|
||||
@@ -78,17 +79,24 @@ jobs:
|
||||
packages: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Commit timestamp
|
||||
id: ts
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT"
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
- name: Derive preview version from tag
|
||||
id: ver
|
||||
shell: bash
|
||||
run: |
|
||||
TAG="${GITHUB_REF_NAME}"
|
||||
VER="${TAG#preview-}"
|
||||
VER="${VER#v}"
|
||||
echo "version=${VER}" >> "$GITHUB_OUTPUT"
|
||||
echo "Publishing preview version: ${VER}"
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
@@ -102,16 +110,9 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Derive preview version from tag
|
||||
id: ver
|
||||
shell: bash
|
||||
run: |
|
||||
TAG="${GITHUB_REF_NAME}"
|
||||
VER="${TAG#preview-}"
|
||||
VER="${VER#v}"
|
||||
echo "version=${VER}" >> "$GITHUB_OUTPUT"
|
||||
echo "Publishing preview version: ${VER}"
|
||||
- name: Commit timestamp
|
||||
id: ts
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
|
||||
15
.github/workflows/rebuild-issue-index.yml
vendored
15
.github/workflows/rebuild-issue-index.yml
vendored
@@ -3,7 +3,7 @@ name: Rebuild Issue Index
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * *"
|
||||
- cron: '0 3 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
@@ -23,13 +23,14 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
- parallel:
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
|
||||
121
.github/workflows/release.yml
vendored
121
.github/workflows/release.yml
vendored
@@ -75,18 +75,19 @@ jobs:
|
||||
env:
|
||||
VERSION: ${{ github.ref_name }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
|
||||
- name: Commit timestamp
|
||||
id: ts
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
|
||||
- name: Warm cache [${{ matrix.platform }}]
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
with:
|
||||
@@ -114,17 +115,14 @@ jobs:
|
||||
env:
|
||||
VERSION: ${{ github.ref_name }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Commit timestamp
|
||||
id: ts
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
@@ -139,6 +137,10 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Commit timestamp
|
||||
id: ts
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
|
||||
@@ -172,23 +174,24 @@ jobs:
|
||||
cache-to: type=gha,mode=max
|
||||
provenance: false
|
||||
|
||||
- name: Resolve manifest digest
|
||||
id: digests
|
||||
run: |
|
||||
DIGEST=$(docker buildx imagetools inspect "${{ env.DOCKER_HUB }}:${{ env.VERSION }}" --format '{{json .Manifest.Digest}}' | tr -d '"')
|
||||
echo "IMAGE_DIGEST=$DIGEST" >> $GITHUB_OUTPUT
|
||||
- parallel:
|
||||
- name: Resolve manifest digest
|
||||
id: digests
|
||||
run: |
|
||||
DIGEST=$(docker buildx imagetools inspect "${{ env.DOCKER_HUB }}:${{ env.VERSION }}" --format '{{json .Manifest.Digest}}' | tr -d '"')
|
||||
echo "IMAGE_DIGEST=$DIGEST" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Also tag :latest (non-pre-release only)
|
||||
shell: bash
|
||||
if: ${{ !contains(env.VERSION, '-') }}
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ env.DOCKER_HUB }}:latest \
|
||||
${{ env.DOCKER_HUB }}:${{ env.VERSION }}
|
||||
- name: Also tag :latest (non-pre-release only)
|
||||
shell: bash
|
||||
if: ${{ !contains(env.VERSION, '-') }}
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ${{ env.DOCKER_HUB }}:latest \
|
||||
${{ env.DOCKER_HUB }}:${{ env.VERSION }}
|
||||
|
||||
docker buildx imagetools create \
|
||||
-t ghcr.io/${{ github.repository }}:latest \
|
||||
ghcr.io/${{ github.repository }}:${{ env.VERSION }}
|
||||
docker buildx imagetools create \
|
||||
-t ghcr.io/${{ github.repository }}:latest \
|
||||
ghcr.io/${{ github.repository }}:${{ env.VERSION }}
|
||||
|
||||
sign:
|
||||
name: Sign images and create SBOM attestations
|
||||
@@ -202,16 +205,17 @@ jobs:
|
||||
VERSION: ${{ github.ref_name }}
|
||||
COSIGN_YES: 'true'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
|
||||
- name: Install Trivy
|
||||
uses: aquasecurity/setup-trivy@81e514348e19b6112ce2a7e3ecbafe19c1e1f567 # v0.3.1
|
||||
- name: Install Trivy
|
||||
uses: aquasecurity/setup-trivy@81e514348e19b6112ce2a7e3ecbafe19c1e1f567 # v0.3.1
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
@@ -271,27 +275,28 @@ jobs:
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
|
||||
- name: Verify signatures
|
||||
run: |
|
||||
cosign verify "ghcr.io/${{ github.repository }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
- parallel:
|
||||
- name: Verify signatures
|
||||
run: |
|
||||
cosign verify "ghcr.io/${{ github.repository }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
|
||||
cosign verify "${{ env.DOCKER_HUB }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
cosign verify "${{ env.DOCKER_HUB }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
|
||||
# - name: Verify attestations
|
||||
# run: |
|
||||
# cosign verify-attestation "ghcr.io/${{ github.repository }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
# --type cyclonedx \
|
||||
# --certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
# --certificate-oidc-issuer "https://token.actions.githubusercontent.com" > /dev/null
|
||||
- name: Verify attestations
|
||||
run: |
|
||||
cosign verify-attestation "ghcr.io/${{ github.repository }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
--type cyclonedx \
|
||||
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
|
||||
# cosign verify-attestation "${{ env.DOCKER_HUB }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
# --type cyclonedx \
|
||||
# --certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
# --certificate-oidc-issuer "https://token.actions.githubusercontent.com" > /dev/null
|
||||
cosign verify-attestation "${{ env.DOCKER_HUB }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
--type cyclonedx \
|
||||
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
|
||||
publish-release:
|
||||
name: Publish release
|
||||
|
||||
25
.github/workflows/renovate-helm-custom-hooks.yml
vendored
25
.github/workflows/renovate-helm-custom-hooks.yml
vendored
@@ -24,20 +24,21 @@ jobs:
|
||||
pull-requests: write
|
||||
if: github.actor == 'renovate[bot]'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- parallel:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: 2138788
|
||||
private-key: ${{ secrets.APP_SEERR_HELM_PRIVATE_KEY }}
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
client-id: ${{ secrets.APP_SEERR_HELM_CLIENT_ID }}
|
||||
private-key: ${{ secrets.APP_SEERR_HELM_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@6ec842c01de15ebb84c8627d2744a0c2f2755c9f # v2.8.0
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@6ec842c01de15ebb84c8627d2744a0c2f2755c9f # v2.8.0
|
||||
|
||||
- name: Run chart-testing (list-changed)
|
||||
id: list-changed
|
||||
|
||||
114
.github/workflows/seerr-labeller.yml
vendored
114
.github/workflows/seerr-labeller.yml
vendored
@@ -27,39 +27,40 @@ jobs:
|
||||
NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
steps:
|
||||
- name: Label added, comment and close pull request
|
||||
if: github.event.action == 'labeled' && github.event.label.name == 'ai-generated'
|
||||
shell: bash
|
||||
env:
|
||||
BODY: >
|
||||
:wave: @${{ env.PR_AUTHOR }}, thank you for your contribution!
|
||||
- parallel:
|
||||
- name: Label added, comment and close pull request
|
||||
if: github.event.action == 'labeled' && github.event.label.name == 'ai-generated'
|
||||
shell: bash
|
||||
env:
|
||||
BODY: >
|
||||
:wave: @${{ env.PR_AUTHOR }}, thank you for your contribution!
|
||||
|
||||
However, this pull request has been closed because it appears to contain a significant amount of AI-generated code without sufficient human review or supervision.
|
||||
However, this pull request has been closed because it appears to contain a significant amount of AI-generated code without sufficient human review or supervision.
|
||||
|
||||
AI-generated code can often introduce subtle bugs, poor design patterns, or inconsistent styles that make long-term maintenance difficult and reduce overall code quality. For the sake of the project's future stability and readability, we require that all contributions meet our established coding standards and demonstrate clear developer oversight.
|
||||
AI-generated code can often introduce subtle bugs, poor design patterns, or inconsistent styles that make long-term maintenance difficult and reduce overall code quality. For the sake of the project's future stability and readability, we require that all contributions meet our established coding standards and demonstrate clear developer oversight.
|
||||
|
||||
This pull request is also too large for effective human review. Please discuss with us on how to break down these changes into smaller, more focused PRs to ensure a thorough and efficient review process.
|
||||
If you'd like to revise and resubmit your changes with careful review and cleanup, we'd be happy to take another look.
|
||||
run: |
|
||||
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
|
||||
retry gh pr comment "$NUMBER" -R "$GH_REPO" -b "$BODY" || true
|
||||
retry gh pr close "$NUMBER" -R "$GH_REPO" || true
|
||||
gh pr lock "$NUMBER" -R "$GH_REPO" -r "spam" || true
|
||||
This pull request is also too large for effective human review. Please discuss with us on how to break down these changes into smaller, more focused PRs to ensure a thorough and efficient review process.
|
||||
If you'd like to revise and resubmit your changes with careful review and cleanup, we'd be happy to take another look.
|
||||
run: |
|
||||
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
|
||||
retry gh pr comment "$NUMBER" -R "$GH_REPO" -b "$BODY" || true
|
||||
retry gh pr close "$NUMBER" -R "$GH_REPO" || true
|
||||
gh pr lock "$NUMBER" -R "$GH_REPO" -r "spam" || true
|
||||
|
||||
- name: Label removed, reopen and unlock pull request
|
||||
if: github.event.action == 'unlabeled' && github.event.label.name == 'ai-generated'
|
||||
shell: bash
|
||||
run: |
|
||||
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
|
||||
retry gh pr reopen "$NUMBER" -R "$GH_REPO" || true
|
||||
gh pr unlock "$NUMBER" -R "$GH_REPO" || true
|
||||
- name: Label removed, reopen and unlock pull request
|
||||
if: github.event.action == 'unlabeled' && github.event.label.name == 'ai-generated'
|
||||
shell: bash
|
||||
run: |
|
||||
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
|
||||
retry gh pr reopen "$NUMBER" -R "$GH_REPO" || true
|
||||
gh pr unlock "$NUMBER" -R "$GH_REPO" || true
|
||||
|
||||
- name: Remove AI-generated label on manual reopen
|
||||
if: github.event.action == 'reopened'
|
||||
shell: bash
|
||||
run: |
|
||||
gh pr edit "$NUMBER" -R "$GH_REPO" --remove-label "ai-generated" || true
|
||||
gh pr unlock "$NUMBER" -R "$GH_REPO" || true
|
||||
- name: Remove AI-generated label on manual reopen
|
||||
if: github.event.action == 'reopened'
|
||||
shell: bash
|
||||
run: |
|
||||
gh pr edit "$NUMBER" -R "$GH_REPO" --remove-label "ai-generated" || true
|
||||
gh pr unlock "$NUMBER" -R "$GH_REPO" || true
|
||||
|
||||
support:
|
||||
if: >
|
||||
@@ -78,34 +79,35 @@ jobs:
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
|
||||
steps:
|
||||
- name: Label added, comment and close issue
|
||||
if: github.event.action == 'labeled' && github.event.label.name == 'support'
|
||||
shell: bash
|
||||
env:
|
||||
BODY: >
|
||||
:wave: @${{ env.ISSUE_AUTHOR }}, we use the issue tracker exclusively
|
||||
for bug reports and feature requests. However, this issue appears
|
||||
to be a support request. Please use our support channels
|
||||
to get help with Seerr.
|
||||
- parallel:
|
||||
- name: Label added, comment and close issue
|
||||
if: github.event.action == 'labeled' && github.event.label.name == 'support'
|
||||
shell: bash
|
||||
env:
|
||||
BODY: >
|
||||
:wave: @${{ env.ISSUE_AUTHOR }}, we use the issue tracker exclusively
|
||||
for bug reports and feature requests. However, this issue appears
|
||||
to be a support request. Please use our support channels
|
||||
to get help with Seerr.
|
||||
|
||||
- [Discord](https://discord.gg/seerr)
|
||||
run: |
|
||||
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
|
||||
retry gh issue comment "$NUMBER" -R "$GH_REPO" -b "$BODY" || true
|
||||
retry gh issue close "$NUMBER" -R "$GH_REPO" || true
|
||||
gh issue lock "$NUMBER" -R "$GH_REPO" -r "off_topic" || true
|
||||
- [Discord](https://discord.gg/seerr)
|
||||
run: |
|
||||
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
|
||||
retry gh issue comment "$NUMBER" -R "$GH_REPO" -b "$BODY" || true
|
||||
retry gh issue close "$NUMBER" -R "$GH_REPO" || true
|
||||
gh issue lock "$NUMBER" -R "$GH_REPO" -r "off_topic" || true
|
||||
|
||||
- name: Label removed, reopen and unlock issue
|
||||
if: github.event.action == 'unlabeled' && github.event.label.name == 'support'
|
||||
shell: bash
|
||||
run: |
|
||||
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
|
||||
retry gh issue reopen "$NUMBER" -R "$GH_REPO" || true
|
||||
gh issue unlock "$NUMBER" -R "$GH_REPO" || true
|
||||
- name: Label removed, reopen and unlock issue
|
||||
if: github.event.action == 'unlabeled' && github.event.label.name == 'support'
|
||||
shell: bash
|
||||
run: |
|
||||
retry() { n=0; until "$@"; do n=$((n+1)); [ $n -ge 3 ] && break; echo "retry $n: $*" >&2; sleep 2; done; }
|
||||
retry gh issue reopen "$NUMBER" -R "$GH_REPO" || true
|
||||
gh issue unlock "$NUMBER" -R "$GH_REPO" || true
|
||||
|
||||
- name: Remove support label on manual reopen
|
||||
if: github.event.action == 'reopened'
|
||||
shell: bash
|
||||
run: |
|
||||
gh issue edit "$NUMBER" -R "$GH_REPO" --remove-label "support" || true
|
||||
gh issue unlock "$NUMBER" -R "$GH_REPO" || true
|
||||
- name: Remove support label on manual reopen
|
||||
if: github.event.action == 'reopened'
|
||||
shell: bash
|
||||
run: |
|
||||
gh issue edit "$NUMBER" -R "$GH_REPO" --remove-label "support" || true
|
||||
gh issue unlock "$NUMBER" -R "$GH_REPO" || true
|
||||
|
||||
15
.github/workflows/test-docs.yml
vendored
15
.github/workflows/test-docs.yml
vendored
@@ -29,14 +29,15 @@ jobs:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: package.json
|
||||
package-manager-cache: false
|
||||
- parallel:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: package.json
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
- name: Pnpm Setup
|
||||
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: sh
|
||||
|
||||
@@ -3,9 +3,9 @@ kubeVersion: '>=1.23.0-0'
|
||||
name: seerr-chart
|
||||
description: Seerr helm chart for Kubernetes
|
||||
type: application
|
||||
version: 3.7.0
|
||||
version: 3.9.0
|
||||
# renovate: image=ghcr.io/seerr-team/seerr
|
||||
appVersion: 'v3.3.0'
|
||||
appVersion: 'v3.4.0'
|
||||
maintainers:
|
||||
- name: Seerr Team
|
||||
url: https://github.com/orgs/seerr-team/people
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# seerr-chart
|
||||
|
||||
  
|
||||
  
|
||||
|
||||
Seerr helm chart for Kubernetes
|
||||
|
||||
@@ -53,9 +53,12 @@ If `replicaCount` value was used - remove it. Helm update should work fine after
|
||||
| config.persistence.storageClass | string | `""` | Storage class for the PVC. Set to "-" to disable dynamic provisioning. Uses default storage class if no value is provided |
|
||||
| config.persistence.subPath | string | `""` | Subpath of the pvc which should be mounted |
|
||||
| config.persistence.volumeName | string | `""` | Name of the permanent volume to reference in the claim. Can be used to bind to existing volumes. |
|
||||
| dnsConfig | object | `{}` | docs: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config |
|
||||
| dnsPolicy | string | `""` | docs: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy |
|
||||
| extraEnv | list | `[]` | Environment variables to add to the seerr pods |
|
||||
| extraEnvFrom | list | `[]` | Environment variables from secrets or configmaps to add to the seerr pods |
|
||||
| fullnameOverride | string | `""` | |
|
||||
| hostUsers | bool | `true` | docs: https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/ |
|
||||
| image.pullPolicy | string | `"IfNotPresent"` | |
|
||||
| image.registry | string | `"ghcr.io"` | |
|
||||
| image.repository | string | `"seerr-team/seerr"` | |
|
||||
@@ -75,6 +78,7 @@ If `replicaCount` value was used - remove it. Helm update should work fine after
|
||||
| podLabels | object | `{}` | |
|
||||
| podSecurityContext.fsGroup | int | `1000` | |
|
||||
| podSecurityContext.fsGroupChangePolicy | string | `"OnRootMismatch"` | |
|
||||
| priorityClassName | string | `nil` | Specify a priorityclass, or use default if unset. |
|
||||
| probes.livenessProbe | object | `{"initialDelaySeconds":20,"periodSeconds":15,"timeoutSeconds":3}` | Configure liveness probe |
|
||||
| probes.readinessProbe | object | `{"initialDelaySeconds":60,"periodSeconds":15,"timeoutSeconds":3}` | Configure readiness probe |
|
||||
| probes.startupProbe | string | `nil` | Configure startup probe |
|
||||
|
||||
@@ -28,6 +28,19 @@ spec:
|
||||
serviceAccountName: {{ include "seerr.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
{{- with .Values.priorityClassName }}
|
||||
priorityClassName: {{ . }}
|
||||
{{- end }}
|
||||
{{- if kindIs "bool" .Values.hostUsers }}
|
||||
hostUsers: {{ .Values.hostUsers }}
|
||||
{{- end }}
|
||||
{{- with .Values.dnsPolicy }}
|
||||
dnsPolicy: {{ . }}
|
||||
{{- end }}
|
||||
{{- with .Values.dnsConfig }}
|
||||
dnsConfig:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
|
||||
@@ -67,6 +67,31 @@ securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
# -- Set a DNS policy, likely not needed unless seerr only interacts with external services.
|
||||
# -- docs: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy
|
||||
dnsPolicy: ''
|
||||
|
||||
# -- Set DNS config options
|
||||
# -- Setting ndots lower can prevent queries like `raw.githubusercontent.com.seerr.svc.cluster.local.`
|
||||
# -- docs: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config
|
||||
dnsConfig: {}
|
||||
# nameservers:
|
||||
# - 1.2.3.4
|
||||
# searches:
|
||||
# - ns1.svc.cluster-domain.example
|
||||
# - my.dns.search.suffix
|
||||
# options:
|
||||
# - name: ndots
|
||||
# value: "2"
|
||||
|
||||
# -- Set to false to run in an idmaped user namespace for additional isolation
|
||||
# -- requires cluster support, and PV support of idmaped volumes.
|
||||
# -- docs: https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/
|
||||
hostUsers: true
|
||||
|
||||
# -- Specify a priorityclass, or use default if unset.
|
||||
priorityClassName: null
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
packages = [
|
||||
pkgs.prettier
|
||||
pkgs.husky
|
||||
pkgs.helm-docs
|
||||
];
|
||||
|
||||
languages.javascript = {
|
||||
|
||||
119
server/api/servarr/radarr.test.ts
Normal file
119
server/api/servarr/radarr.test.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { afterEach, describe, it, mock } from 'node:test';
|
||||
|
||||
import type { AxiosInstance } from 'axios';
|
||||
|
||||
import RadarrAPI from '@server/api/servarr/radarr';
|
||||
|
||||
function buildRadarr(): RadarrAPI {
|
||||
return new RadarrAPI({ url: 'http://localhost:7878/api/v3', apiKey: 'test' });
|
||||
}
|
||||
|
||||
function getAxios(radarr: RadarrAPI): AxiosInstance {
|
||||
return (radarr as unknown as { axios: AxiosInstance }).axios;
|
||||
}
|
||||
|
||||
describe('RadarrAPI removeMovie', () => {
|
||||
afterEach(() => mock.restoreAll());
|
||||
|
||||
it('removes the movie when it exists in the library', async () => {
|
||||
const radarr = buildRadarr();
|
||||
mock.method(RadarrAPI.prototype, 'getMovieByTmdbId', async () => ({
|
||||
id: 7,
|
||||
title: 'Test Movie',
|
||||
}));
|
||||
const del = mock.method(getAxios(radarr), 'delete', async () => ({}));
|
||||
|
||||
await radarr.removeMovie(550);
|
||||
|
||||
assert.strictEqual(del.mock.callCount(), 1);
|
||||
assert.strictEqual(del.mock.calls[0].arguments[0], '/movie/7');
|
||||
});
|
||||
|
||||
it('does nothing when the movie is not in the library', async () => {
|
||||
const radarr = buildRadarr();
|
||||
mock.method(getAxios(radarr), 'get', async () => ({
|
||||
data: [{ id: 0, title: 'Fight Club' }],
|
||||
}));
|
||||
const del = mock.method(getAxios(radarr), 'delete', async () => ({}));
|
||||
|
||||
await assert.doesNotReject(() => radarr.removeMovie(550));
|
||||
assert.strictEqual(del.mock.callCount(), 0);
|
||||
});
|
||||
|
||||
it('rejects when the tmdbId is unknown to the lookup', async () => {
|
||||
const radarr = buildRadarr();
|
||||
mock.method(getAxios(radarr), 'get', async () => ({ data: [] }));
|
||||
const del = mock.method(getAxios(radarr), 'delete', async () => ({}));
|
||||
|
||||
await assert.rejects(() => radarr.removeMovie(550), /Movie not found/);
|
||||
assert.strictEqual(del.mock.callCount(), 0);
|
||||
});
|
||||
|
||||
it('ignores a 404 when the movie was already removed in Radarr', async () => {
|
||||
const radarr = buildRadarr();
|
||||
mock.method(RadarrAPI.prototype, 'getMovieByTmdbId', async () => ({
|
||||
id: 7,
|
||||
title: 'Test Movie',
|
||||
}));
|
||||
mock.method(getAxios(radarr), 'delete', async () => {
|
||||
throw { response: { status: 404 } };
|
||||
});
|
||||
|
||||
await assert.doesNotReject(() => radarr.removeMovie(550));
|
||||
});
|
||||
|
||||
it('rethrows errors other than 404', async () => {
|
||||
const radarr = buildRadarr();
|
||||
mock.method(RadarrAPI.prototype, 'getMovieByTmdbId', async () => ({
|
||||
id: 7,
|
||||
title: 'Test Movie',
|
||||
}));
|
||||
mock.method(getAxios(radarr), 'delete', async () => {
|
||||
throw { response: { status: 500 } };
|
||||
});
|
||||
|
||||
await assert.rejects(() => radarr.removeMovie(550));
|
||||
});
|
||||
|
||||
it('rethrows a 404 from the lookup instead of treating it as removed', async () => {
|
||||
const radarr = buildRadarr();
|
||||
mock.method(getAxios(radarr), 'get', async () => {
|
||||
throw { response: { status: 404 } };
|
||||
});
|
||||
const del = mock.method(getAxios(radarr), 'delete', async () => ({}));
|
||||
|
||||
await assert.rejects(
|
||||
() => radarr.removeMovie(550),
|
||||
(e: unknown) =>
|
||||
(e as { response?: { status?: number } }).response?.status === 404
|
||||
);
|
||||
assert.strictEqual(del.mock.callCount(), 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RadarrAPI getMovieByTmdbId', () => {
|
||||
afterEach(() => mock.restoreAll());
|
||||
|
||||
it('rethrows a 401 from the lookup with the status intact', async () => {
|
||||
const radarr = buildRadarr();
|
||||
mock.method(getAxios(radarr), 'get', async () => {
|
||||
throw { response: { status: 401 } };
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
() => radarr.getMovieByTmdbId(550),
|
||||
(e: unknown) =>
|
||||
(e as { response?: { status?: number } }).response?.status === 401
|
||||
);
|
||||
});
|
||||
|
||||
it('throws "Movie not found" when the lookup returns no results', async () => {
|
||||
const radarr = buildRadarr();
|
||||
mock.method(getAxios(radarr), 'get', async () => ({ data: [] }));
|
||||
|
||||
await assert.rejects(() => radarr.getMovieByTmdbId(550), {
|
||||
message: 'Movie not found',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import logger from '@server/logger';
|
||||
import type { AxiosResponse } from 'axios';
|
||||
import ServarrBase from './base';
|
||||
|
||||
export interface RadarrMovieOptions {
|
||||
@@ -93,26 +94,27 @@ class RadarrAPI extends ServarrBase<{ movieId: number }> {
|
||||
};
|
||||
|
||||
public async getMovieByTmdbId(id: number): Promise<RadarrMovie> {
|
||||
let response: AxiosResponse<RadarrMovie[]>;
|
||||
try {
|
||||
const response = await this.axios.get<RadarrMovie[]>('/movie/lookup', {
|
||||
response = await this.axios.get<RadarrMovie[]>('/movie/lookup', {
|
||||
params: {
|
||||
term: `tmdb:${id}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.data[0]) {
|
||||
throw new Error('Movie not found');
|
||||
}
|
||||
|
||||
return response.data[0];
|
||||
} catch (e) {
|
||||
logger.error('Error retrieving movie by TMDB ID', {
|
||||
label: 'Radarr API',
|
||||
errorMessage: e.message,
|
||||
tmdbId: id,
|
||||
});
|
||||
throw new Error('Movie not found', { cause: e });
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (!response.data[0]) {
|
||||
throw new Error('Movie not found');
|
||||
}
|
||||
|
||||
return response.data[0];
|
||||
}
|
||||
|
||||
public addMovie = async (
|
||||
@@ -267,9 +269,17 @@ class RadarrAPI extends ServarrBase<{ movieId: number }> {
|
||||
);
|
||||
}
|
||||
}
|
||||
public removeMovie = async (movieId: number): Promise<void> => {
|
||||
public removeMovie = async (tmdbId: number): Promise<void> => {
|
||||
const { id, title } = await this.getMovieByTmdbId(tmdbId);
|
||||
|
||||
if (!id) {
|
||||
logger.info(`[Radarr] Movie not in library, nothing to remove`, {
|
||||
tmdbId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { id, title } = await this.getMovieByTmdbId(movieId);
|
||||
await this.axios.delete(`/movie/${id}`, {
|
||||
params: {
|
||||
deleteFiles: true,
|
||||
@@ -278,9 +288,13 @@ class RadarrAPI extends ServarrBase<{ movieId: number }> {
|
||||
});
|
||||
logger.info(`[Radarr] Removed movie ${title}`);
|
||||
} catch (e) {
|
||||
throw new Error(`[Radarr] Failed to remove movie: ${e.message}`, {
|
||||
cause: e,
|
||||
});
|
||||
if (e?.response?.status === 404) {
|
||||
logger.info(`[Radarr] Movie already removed from Radarr`, {
|
||||
tmdbId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
119
server/api/servarr/sonarr.test.ts
Normal file
119
server/api/servarr/sonarr.test.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { afterEach, describe, it, mock } from 'node:test';
|
||||
|
||||
import type { AxiosInstance } from 'axios';
|
||||
|
||||
import SonarrAPI from '@server/api/servarr/sonarr';
|
||||
|
||||
function buildSonarr(): SonarrAPI {
|
||||
return new SonarrAPI({ url: 'http://localhost:8989/api/v3', apiKey: 'test' });
|
||||
}
|
||||
|
||||
function getAxios(sonarr: SonarrAPI): AxiosInstance {
|
||||
return (sonarr as unknown as { axios: AxiosInstance }).axios;
|
||||
}
|
||||
|
||||
describe('SonarrAPI removeSeries', () => {
|
||||
afterEach(() => mock.restoreAll());
|
||||
|
||||
it('removes the series when it exists in the library', async () => {
|
||||
const sonarr = buildSonarr();
|
||||
mock.method(SonarrAPI.prototype, 'getSeriesByTvdbId', async () => ({
|
||||
id: 9,
|
||||
title: 'Test Series',
|
||||
}));
|
||||
const del = mock.method(getAxios(sonarr), 'delete', async () => ({}));
|
||||
|
||||
await sonarr.removeSeries(1234);
|
||||
|
||||
assert.strictEqual(del.mock.callCount(), 1);
|
||||
assert.strictEqual(del.mock.calls[0].arguments[0], '/series/9');
|
||||
});
|
||||
|
||||
it('does nothing when the series is not in the library', async () => {
|
||||
const sonarr = buildSonarr();
|
||||
mock.method(getAxios(sonarr), 'get', async () => ({
|
||||
data: [{ id: 0, title: 'Breaking Bad' }],
|
||||
}));
|
||||
const del = mock.method(getAxios(sonarr), 'delete', async () => ({}));
|
||||
|
||||
await assert.doesNotReject(() => sonarr.removeSeries(1234));
|
||||
assert.strictEqual(del.mock.callCount(), 0);
|
||||
});
|
||||
|
||||
it('rejects when the tvdbId is unknown to the lookup', async () => {
|
||||
const sonarr = buildSonarr();
|
||||
mock.method(getAxios(sonarr), 'get', async () => ({ data: [] }));
|
||||
const del = mock.method(getAxios(sonarr), 'delete', async () => ({}));
|
||||
|
||||
await assert.rejects(() => sonarr.removeSeries(1234), /Series not found/);
|
||||
assert.strictEqual(del.mock.callCount(), 0);
|
||||
});
|
||||
|
||||
it('ignores a 404 when the series was already removed in Sonarr', async () => {
|
||||
const sonarr = buildSonarr();
|
||||
mock.method(SonarrAPI.prototype, 'getSeriesByTvdbId', async () => ({
|
||||
id: 9,
|
||||
title: 'Test Series',
|
||||
}));
|
||||
mock.method(getAxios(sonarr), 'delete', async () => {
|
||||
throw { response: { status: 404 } };
|
||||
});
|
||||
|
||||
await assert.doesNotReject(() => sonarr.removeSeries(1234));
|
||||
});
|
||||
|
||||
it('rethrows errors other than 404', async () => {
|
||||
const sonarr = buildSonarr();
|
||||
mock.method(SonarrAPI.prototype, 'getSeriesByTvdbId', async () => ({
|
||||
id: 9,
|
||||
title: 'Test Series',
|
||||
}));
|
||||
mock.method(getAxios(sonarr), 'delete', async () => {
|
||||
throw { response: { status: 500 } };
|
||||
});
|
||||
|
||||
await assert.rejects(() => sonarr.removeSeries(1234));
|
||||
});
|
||||
|
||||
it('rethrows a 404 from the lookup instead of treating it as removed', async () => {
|
||||
const sonarr = buildSonarr();
|
||||
mock.method(getAxios(sonarr), 'get', async () => {
|
||||
throw { response: { status: 404 } };
|
||||
});
|
||||
const del = mock.method(getAxios(sonarr), 'delete', async () => ({}));
|
||||
|
||||
await assert.rejects(
|
||||
() => sonarr.removeSeries(1234),
|
||||
(e: unknown) =>
|
||||
(e as { response?: { status?: number } }).response?.status === 404
|
||||
);
|
||||
assert.strictEqual(del.mock.callCount(), 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SonarrAPI getSeriesByTvdbId', () => {
|
||||
afterEach(() => mock.restoreAll());
|
||||
|
||||
it('rethrows a 401 from the lookup with the status intact', async () => {
|
||||
const sonarr = buildSonarr();
|
||||
mock.method(getAxios(sonarr), 'get', async () => {
|
||||
throw { response: { status: 401 } };
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
() => sonarr.getSeriesByTvdbId(1234),
|
||||
(e: unknown) =>
|
||||
(e as { response?: { status?: number } }).response?.status === 401
|
||||
);
|
||||
});
|
||||
|
||||
it('throws "Series not found" when the lookup returns no results', async () => {
|
||||
const sonarr = buildSonarr();
|
||||
mock.method(getAxios(sonarr), 'get', async () => ({ data: [] }));
|
||||
|
||||
await assert.rejects(() => sonarr.getSeriesByTvdbId(1234), {
|
||||
message: 'Series not found',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import logger from '@server/logger';
|
||||
import type { AxiosResponse } from 'axios';
|
||||
import ServarrBase from './base';
|
||||
|
||||
export interface SonarrSeason {
|
||||
@@ -166,26 +167,27 @@ class SonarrAPI extends ServarrBase<{
|
||||
}
|
||||
|
||||
public async getSeriesByTvdbId(id: number): Promise<SonarrSeries> {
|
||||
let response: AxiosResponse<SonarrSeries[]>;
|
||||
try {
|
||||
const response = await this.axios.get<SonarrSeries[]>('/series/lookup', {
|
||||
response = await this.axios.get<SonarrSeries[]>('/series/lookup', {
|
||||
params: {
|
||||
term: `tvdb:${id}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.data[0]) {
|
||||
throw new Error('Series not found');
|
||||
}
|
||||
|
||||
return response.data[0];
|
||||
} catch (e) {
|
||||
logger.error('Error retrieving series by tvdb ID', {
|
||||
label: 'Sonarr API',
|
||||
errorMessage: e.message,
|
||||
tvdbId: id,
|
||||
});
|
||||
throw new Error('Series not found', { cause: e });
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (!response.data[0]) {
|
||||
throw new Error('Series not found');
|
||||
}
|
||||
|
||||
return response.data[0];
|
||||
}
|
||||
|
||||
public async addSeries(options: AddSeriesOptions): Promise<SonarrSeries> {
|
||||
@@ -410,9 +412,17 @@ class SonarrAPI extends ServarrBase<{
|
||||
|
||||
return newSeasons;
|
||||
}
|
||||
public removeSeries = async (serieId: number): Promise<void> => {
|
||||
public removeSeries = async (tvdbId: number): Promise<void> => {
|
||||
const { id, title } = await this.getSeriesByTvdbId(tvdbId);
|
||||
|
||||
if (!id) {
|
||||
logger.info(`[Sonarr] Series not in library, nothing to remove`, {
|
||||
tvdbId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { id, title } = await this.getSeriesByTvdbId(serieId);
|
||||
await this.axios.delete(`/series/${id}`, {
|
||||
params: {
|
||||
deleteFiles: true,
|
||||
@@ -421,9 +431,13 @@ class SonarrAPI extends ServarrBase<{
|
||||
});
|
||||
logger.info(`[Sonarr] Removed series ${title}`);
|
||||
} catch (e) {
|
||||
throw new Error(`[Sonarr] Failed to remove series: ${e.message}`, {
|
||||
cause: e,
|
||||
});
|
||||
if (e?.response?.status === 404) {
|
||||
logger.info(`[Sonarr] Series already removed from Sonarr`, {
|
||||
tvdbId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -203,17 +203,21 @@ class Media {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
|
||||
public resetServiceData(): void {
|
||||
this.serviceId = null;
|
||||
this.serviceId4k = null;
|
||||
this.externalServiceId = null;
|
||||
this.externalServiceId4k = null;
|
||||
this.externalServiceSlug = null;
|
||||
this.externalServiceSlug4k = null;
|
||||
this.ratingKey = null;
|
||||
this.ratingKey4k = null;
|
||||
this.jellyfinMediaId = null;
|
||||
this.jellyfinMediaId4k = null;
|
||||
public resetServiceData(is4k?: boolean): void {
|
||||
if (is4k === undefined || !is4k) {
|
||||
this.serviceId = null;
|
||||
this.externalServiceId = null;
|
||||
this.externalServiceSlug = null;
|
||||
this.ratingKey = null;
|
||||
this.jellyfinMediaId = null;
|
||||
}
|
||||
if (is4k === undefined || is4k) {
|
||||
this.serviceId4k = null;
|
||||
this.externalServiceId4k = null;
|
||||
this.externalServiceSlug4k = null;
|
||||
this.ratingKey4k = null;
|
||||
this.jellyfinMediaId4k = null;
|
||||
}
|
||||
}
|
||||
|
||||
@AfterLoad()
|
||||
|
||||
@@ -243,18 +243,18 @@ mediaRoutes.delete(
|
||||
}
|
||||
|
||||
if (!serviceSettings) {
|
||||
logger.warn(
|
||||
`There is no default ${
|
||||
is4k ? '4K ' : '' + isMovie ? 'Radarr' : 'Sonarr'
|
||||
}/ server configured. Did you set any of your ${
|
||||
is4k ? '4K ' : '' + isMovie ? 'Radarr' : 'Sonarr'
|
||||
} servers as default?`,
|
||||
const arrName = `${is4k ? '4K ' : ''}${isMovie ? 'Radarr' : 'Sonarr'}`;
|
||||
logger.info(
|
||||
`There is no default ${arrName} server configured. Did you set any of your ${arrName} servers as default?`,
|
||||
{
|
||||
label: 'Media Request',
|
||||
mediaId: media.id,
|
||||
}
|
||||
);
|
||||
return;
|
||||
return next({
|
||||
status: 409,
|
||||
message: `No ${arrName} server configured to delete media files`,
|
||||
});
|
||||
}
|
||||
|
||||
let service;
|
||||
@@ -280,15 +280,27 @@ mediaRoutes.delete(
|
||||
throw new Error('TVDB ID not found');
|
||||
}
|
||||
await (service as SonarrAPI).removeSeries(tvdbId);
|
||||
|
||||
for (const season of media.seasons) {
|
||||
season[is4k ? 'status4k' : 'status'] = MediaStatus.DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
media[is4k ? 'status4k' : 'status'] = MediaStatus.DELETED;
|
||||
media.resetServiceData(is4k);
|
||||
await mediaRepository.save(media);
|
||||
|
||||
return res.status(204).send();
|
||||
} catch (e) {
|
||||
logger.error('Something went wrong fetching media in delete request', {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
return next({ status: 404, message: 'Media not found' });
|
||||
}
|
||||
logger.error('Something went wrong deleting media file', {
|
||||
label: 'Media',
|
||||
mediaId: req.params.id,
|
||||
message: e.message,
|
||||
});
|
||||
next({ status: 404, message: 'Media not found' });
|
||||
next({ status: 500, message: 'Failed to delete media file' });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import DownloadBlock from '@app/components/DownloadBlock';
|
||||
import IssueBlock from '@app/components/IssueBlock';
|
||||
import RequestBlock from '@app/components/RequestBlock';
|
||||
import useSettings from '@app/hooks/useSettings';
|
||||
import useToasts from '@app/hooks/useToasts';
|
||||
import { Permission, useUser } from '@app/hooks/useUser';
|
||||
import globalMessages from '@app/i18n/globalMessages';
|
||||
import defineMessages from '@app/utils/defineMessages';
|
||||
@@ -64,6 +65,8 @@ const messages = defineMessages('components.ManageSlideOver', {
|
||||
removearr: 'Remove from {arr}',
|
||||
openarr4k: 'Open in 4K {arr}',
|
||||
removearr4k: 'Remove from 4K {arr}',
|
||||
clearmediadataerror: 'Something went wrong while clearing the media data.',
|
||||
removemediaerror: 'Something went wrong while removing the media.',
|
||||
downloadstatus: 'Downloads',
|
||||
markavailable: 'Mark as Available',
|
||||
mark4kavailable: 'Mark as Available in 4K',
|
||||
@@ -109,6 +112,7 @@ const ManageSlideOver = ({
|
||||
}: ManageSlideOverMovieProps | ManageSlideOverTvProps) => {
|
||||
const { user: currentUser, hasPermission } = useUser();
|
||||
const intl = useIntl();
|
||||
const { addToast } = useToasts();
|
||||
const settings = useSettings();
|
||||
const { data: watchData } = useSWR<MediaWatchDataResponse>(
|
||||
settings.currentSettings.mediaServerType === MediaServerType.PLEX &&
|
||||
@@ -126,18 +130,35 @@ const ManageSlideOver = ({
|
||||
|
||||
const deleteMedia = async () => {
|
||||
if (data.mediaInfo) {
|
||||
await axios.delete(`/api/v1/media/${data.mediaInfo.id}`);
|
||||
revalidate();
|
||||
onClose();
|
||||
try {
|
||||
await axios.delete(`/api/v1/media/${data.mediaInfo.id}`);
|
||||
revalidate();
|
||||
onClose();
|
||||
} catch {
|
||||
addToast(intl.formatMessage(messages.clearmediadataerror), {
|
||||
appearance: 'error',
|
||||
autoDismiss: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const deleteMediaFile = async (is4k = false) => {
|
||||
if (data.mediaInfo) {
|
||||
await axios.delete(
|
||||
`/api/v1/media/${data.mediaInfo.id}/file?is4k=${is4k}`
|
||||
);
|
||||
await axios.delete(`/api/v1/media/${data.mediaInfo.id}`);
|
||||
try {
|
||||
await axios.delete(
|
||||
`/api/v1/media/${data.mediaInfo.id}/file?is4k=${is4k}`
|
||||
);
|
||||
} catch (e) {
|
||||
if (!axios.isAxiosError(e) || e.response?.status !== 404) {
|
||||
addToast(intl.formatMessage(messages.removemediaerror), {
|
||||
appearance: 'error',
|
||||
autoDismiss: true,
|
||||
});
|
||||
revalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
revalidate();
|
||||
onClose();
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ const messages = defineMessages('components.RequestList.RequestItem', {
|
||||
tvdbid: 'TheTVDB ID',
|
||||
unknowntitle: 'Unknown Title',
|
||||
removearr: 'Remove from {arr}',
|
||||
removemediaerror: 'Something went wrong while removing the media.',
|
||||
profileName: 'Profile',
|
||||
});
|
||||
|
||||
@@ -354,10 +355,20 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => {
|
||||
|
||||
const deleteMediaFile = async () => {
|
||||
if (request.media) {
|
||||
await axios.delete(
|
||||
`/api/v1/media/${request.media.id}/file?is4k=${request.is4k}`
|
||||
);
|
||||
await axios.delete(`/api/v1/media/${request.media.id}`);
|
||||
try {
|
||||
await axios.delete(
|
||||
`/api/v1/media/${request.media.id}/file?is4k=${request.is4k}`
|
||||
);
|
||||
} catch (e) {
|
||||
if (!axios.isAxiosError(e) || e.response?.status !== 404) {
|
||||
addToast(intl.formatMessage(messages.removemediaerror), {
|
||||
autoDismiss: true,
|
||||
appearance: 'error',
|
||||
});
|
||||
revalidateList();
|
||||
return;
|
||||
}
|
||||
}
|
||||
revalidateList();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -750,12 +750,12 @@ const UserList = () => {
|
||||
>
|
||||
{intl.formatMessage(messages.created)}
|
||||
</SortableColumnHeader>
|
||||
<Table.TH className="w-1/12 whitespace-nowrap text-right">
|
||||
<Table.TH className="w-1/12 min-w-[12rem] whitespace-nowrap text-right">
|
||||
{(data.results ?? []).length > 1 && (
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
buttonType="warning"
|
||||
className="w-full sm:min-w-[12rem]"
|
||||
className="w-full"
|
||||
onClick={() => setShowBulkEditModal(true)}
|
||||
disabled={selectedUsers.length === 0}
|
||||
>
|
||||
@@ -874,33 +874,32 @@ const UserList = () => {
|
||||
day: 'numeric',
|
||||
})}
|
||||
</Table.TD>
|
||||
<Table.TD
|
||||
alignText="right"
|
||||
className="grid grid-cols-1 gap-1 sm:grid-cols-2 sm:gap-2"
|
||||
>
|
||||
<Button
|
||||
buttonType="warning"
|
||||
disabled={user.id === 1 && currentUser?.id !== 1}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
'/users/[userId]/settings',
|
||||
`/users/${user.id}/settings`
|
||||
)
|
||||
}
|
||||
>
|
||||
{intl.formatMessage(globalMessages.edit)}
|
||||
</Button>
|
||||
<Button
|
||||
buttonType="danger"
|
||||
disabled={
|
||||
user.id === 1 ||
|
||||
(currentUser?.id !== 1 &&
|
||||
hasPermission(Permission.ADMIN, user.permissions))
|
||||
}
|
||||
onClick={() => setDeleteModal({ isOpen: true, user })}
|
||||
>
|
||||
{intl.formatMessage(globalMessages.delete)}
|
||||
</Button>
|
||||
<Table.TD alignText="right">
|
||||
<div className="grid grid-cols-1 gap-1 sm:grid-cols-2 sm:gap-2">
|
||||
<Button
|
||||
buttonType="warning"
|
||||
disabled={user.id === 1 && currentUser?.id !== 1}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
'/users/[userId]/settings',
|
||||
`/users/${user.id}/settings`
|
||||
)
|
||||
}
|
||||
>
|
||||
{intl.formatMessage(globalMessages.edit)}
|
||||
</Button>
|
||||
<Button
|
||||
buttonType="danger"
|
||||
disabled={
|
||||
user.id === 1 ||
|
||||
(currentUser?.id !== 1 &&
|
||||
hasPermission(Permission.ADMIN, user.permissions))
|
||||
}
|
||||
onClick={() => setDeleteModal({ isOpen: true, user })}
|
||||
>
|
||||
{intl.formatMessage(globalMessages.delete)}
|
||||
</Button>
|
||||
</div>
|
||||
</Table.TD>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@@ -305,6 +305,7 @@
|
||||
"components.Login.validationservertyperequired": "Please select a server type",
|
||||
"components.Login.validationusernamerequired": "Username required",
|
||||
"components.ManageSlideOver.alltime": "All Time",
|
||||
"components.ManageSlideOver.clearmediadataerror": "Something went wrong while clearing the media data.",
|
||||
"components.ManageSlideOver.downloadstatus": "Downloads",
|
||||
"components.ManageSlideOver.manageModalAdvanced": "Advanced",
|
||||
"components.ManageSlideOver.manageModalClearMedia": "Clear Data",
|
||||
@@ -329,6 +330,7 @@
|
||||
"components.ManageSlideOver.plays": "<strong>{playCount, number}</strong> {playCount, plural, one {play} other {plays}}",
|
||||
"components.ManageSlideOver.removearr": "Remove from {arr}",
|
||||
"components.ManageSlideOver.removearr4k": "Remove from 4K {arr}",
|
||||
"components.ManageSlideOver.removemediaerror": "Something went wrong while removing the media.",
|
||||
"components.ManageSlideOver.tvshow": "series",
|
||||
"components.MediaSlider.ShowMoreCard.seemore": "See More",
|
||||
"components.MetadataSelector.selectMetdataProvider": "Select a metadata provider",
|
||||
@@ -528,6 +530,7 @@
|
||||
"components.RequestList.RequestItem.modifieduserdate": "{date} by {user}",
|
||||
"components.RequestList.RequestItem.profileName": "Profile",
|
||||
"components.RequestList.RequestItem.removearr": "Remove from {arr}",
|
||||
"components.RequestList.RequestItem.removemediaerror": "Something went wrong while removing the media.",
|
||||
"components.RequestList.RequestItem.requested": "Requested",
|
||||
"components.RequestList.RequestItem.requesteddate": "Requested",
|
||||
"components.RequestList.RequestItem.seasons": "{seasonCount, plural, one {Season} other {Seasons}}",
|
||||
|
||||
Reference in New Issue
Block a user