Merge branch 'chore/native-core-buffer' into chore/native-core-rotate

This commit is contained in:
Santo Shakil
2026-09-28 19:17:35 +06:00
1091 changed files with 27008 additions and 14305 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
with:
github_token: ${{ steps.token.outputs.token }}
label_pattern: '^backport:(release/.+)$'
add_team_reviewers: team
add_team_reviewers: employees
add_labels: 'backport'
auto_merge_enabled: true
auto_merge_method: squash
+4 -28
View File
@@ -172,32 +172,8 @@ jobs:
fi
- name: Verify native library
# the rust build hook links the library for 16 KB pages and against the app's minSdk;
# Google Play rejects the first regression, old devices the second
run: |
set -o pipefail
apk=mobile/build/app/outputs/flutter-apk/app-release.apk
min_sdk=$(sed -nE 's/^ *minSdk = ([0-9]+)$/\1/p' mobile/android/app/build.gradle)
readelf=$(find "${ANDROID_HOME:-$ANDROID_SDK_ROOT}/ndk" -name llvm-readelf | sort -V | tail -n 1)
[[ $min_sdk && -x $readelf ]] || { echo "minSdk or llvm-readelf not found" >&2; exit 1; }
for abi in armeabi-v7a arm64-v8a x86_64; do
lib=$RUNNER_TEMP/$abi.so
unzip -p "$apk" "lib/$abi/libimmich_core_ffi.so" > "$lib"
# every loadable segment must be aligned to 16 KB (0x4000)
"$readelf" --program-headers --wide "$lib" | awk '$1 == "LOAD" && $NF != "0x4000" { bad = 1 } END { exit bad }' \
|| { echo "$abi: a LOAD segment is not 16 KB aligned" >&2; exit 1; }
# the Android ELF note starts with the API level the library was linked against, 4 bytes little endian
note=$("$readelf" --notes "$lib" | sed -nE '/^ *Android/,$ s/.*description data: *//p')
[[ $note ]] || { echo "$abi: no Android ELF note" >&2; exit 1; }
read -r b0 b1 b2 b3 _ <<< "$note"
api=$((16#$b3$b2$b1$b0))
(( api <= min_sdk )) || { echo "$abi: linked against API $api, minSdk is $min_sdk" >&2; exit 1; }
echo "$abi ok: 16 KB aligned, API $api"
done
working-directory: ./mobile
run: bash ./scripts/verify_native_lib.sh
- name: Publish Android Artifact
id: upload-apk
@@ -299,9 +275,9 @@ jobs:
working_directory: ./mobile
- name: Setup Ruby
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
uses: ruby/setup-ruby@984c0c890880bbf811283d6f09c4607c62d210a4 # v1.323.0
with:
ruby-version: '3.3'
ruby-version: '3.4'
bundler-cache: true # zizmor: ignore[cache-poisoning] release branches cannot read caches written by pull requests
working-directory: ./mobile/ios
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
persist-credentials: false
- name: Check for breaking API changes
uses: oasdiff/oasdiff-action/breaking@2649ebe137aeb72a95707671204e829f86e091fc # v0.1.13
uses: oasdiff/oasdiff-action/breaking@5e81b5c380accc6b523f9d32a637ca630e33620b # v0.1.17
with:
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
revision: open-api/immich-openapi-specs.json
+3 -3
View File
@@ -77,10 +77,10 @@ jobs:
token: ${{ steps.token.outputs.token }}
- name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Login to GitHub Container Registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
@@ -109,7 +109,7 @@ jobs:
type=raw,value=latest,enable=${{ github.event_name == 'release' && !github.event.release.prerelease }}
- name: Build and push image
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
file: packages/cli/Dockerfile
platforms: linux/amd64,linux/arm64
+1 -1
View File
@@ -39,7 +39,7 @@ jobs:
needs: [get_body, should_run]
if: ${{ needs.should_run.outputs.should_run == 'true' }}
container:
image: ghcr.io/immich-app/mdq:main@sha256:74b93dfbf13f8ee73eb35d8d5195ff667f93f9bc55df5ae453dd43a461c821fa
image: ghcr.io/immich-app/mdq:main@sha256:ef9eab1055b07aed9111b6dd3f0d70e1b2e08c862d982d4f4560d6353c9e2b9d
outputs:
checked: ${{ steps.get_checkbox.outputs.checked }}
steps:
+3 -3
View File
@@ -58,7 +58,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -71,7 +71,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/autobuild@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -84,6 +84,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
with:
category: '/language:${{matrix.language}}'
+1 -1
View File
@@ -80,7 +80,7 @@ jobs:
# TODO move to mise
- name: Install uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
- name: Bump version
env:
+1 -4
View File
@@ -81,11 +81,8 @@ jobs:
- name: Perform codegen
run: mise //mobile:codegen
- name: Verify generated files have not changed
run: git diff --exit-code -- ../native/crates/immich_db/src/schema.g.rs
- name: Run analyze
run: mise //mobile:analyze
run: mise //mobile:analyze --full
- name: Run format
run: mise //mobile:format
+2 -5
View File
@@ -125,9 +125,6 @@ jobs:
- name: Run tests
run: mise //native:test
- name: Verify generated files have not changed
run: git diff --exit-code -- native/
- name: Save Rust Cache
id: cache-rust-save
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
@@ -492,7 +489,7 @@ jobs:
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
@@ -571,7 +568,7 @@ jobs:
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
+1 -1
View File
@@ -1 +1 @@
24.15.0
24.21.0
+3 -2
View File
@@ -13,7 +13,7 @@
"editor.wordBasedSuggestions": "off"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true
},
"[json]": {
@@ -30,13 +30,14 @@
"tailwindCSS.lint.suggestCanonicalClasses": "ignore"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": true
},
"cSpell.words": ["immich"],
"css.lint.unknownAtRules": "ignore",
"editor.bracketPairColorization.enabled": true,
"editor.formatOnSave": true,
"eslint.format.enable": true,
"eslint.useFlatConfig": true,
"eslint.validate": ["javascript", "typescript", "svelte"],
"eslint.workingDirectories": [
+4
View File
@@ -17,6 +17,10 @@ If you are looking for something to work on, there are discussions and issues wi
We usually do not assign issues to new contributors, since it happens often that a PR is never even opened. Again, reach out on Discord if you fear putting a lot of time into fixing an issue, but ending up with a duplicate PR.
### Opening new issues
If you find bugs or other problems while working on something, we appreciate that you open an issue, if there is no open issue covering the problem yet. To make it easier to debug and fix the issue, please read and fill out the whole issue template.
## Use of generative AI
We ask you not to open PRs generated with an LLM. We find that code generated like this tends to need a large amount of back-and-forth, which is a very inefficient use of our time. Even a one line change can have significant impact. We cannot have any confidence in an LLM, so if it's non-trivial for us to verify it works and you don't show that you fully understand all implications of the change, reviewing the PR is not worth our time. If we want LLM-generated code, it's much faster for us to use an LLM ourselves than to go through an intermediary via a pull request.
+4
View File
@@ -1,5 +1,7 @@
# FAQ
import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
## Commercial Guidelines
### Are you open to commercial partnerships and collaborations?
@@ -411,6 +413,8 @@ You can change the user in the container by setting the `user` argument in `dock
[Example docker-compose.rootless.yml file](https://github.com/immich-app/immich/blob/main/docker/docker-compose.rootless.yml)
<ComposeBuilder query="rootless.enabled=true" />
You may need to add mount points or docker volumes for the following internal container paths:
- `redis:/data`
@@ -1,5 +1,7 @@
# Pre-existing Postgres
import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
While not officially recommended, it is possible to run Immich using a pre-existing Postgres server. To use this setup, you should have a baseline level of familiarity with Postgres and the Linux command line. If you do not have these, we recommend using the default setup with a dedicated Postgres container.
By default, Immich expects superuser permission on the Postgres database and requires certain extensions to be installed. This guide outlines the steps required to prepare a pre-existing Postgres server to be used by Immich.
@@ -27,6 +29,8 @@ The current accepted range for VectorChord is `>= 0.3, < 2.0`.
## Specifying the connection URL
<ComposeBuilder query="database.external=true" />
You can connect to your pre-existing Postgres server by setting the `DB_URL` environment variable in the `.env` file.
```
@@ -1,5 +1,7 @@
# Hardware Transcoding
import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
This feature allows you to use a GPU to accelerate transcoding and reduce CPU load.
Note that hardware transcoding produces significantly larger videos than software transcoding with similar settings, typically with lower quality. Using slow presets and preferring more efficient codecs can narrow this gap.
As this is a new feature, it is still experimental and may not work on all systems.
@@ -57,6 +59,8 @@ For RKMPP to work:
## Setup
<ComposeBuilder />
#### Basic Setup
1. If you do not already have it, download the latest [`hwaccel.transcoding.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
+4
View File
@@ -1,5 +1,7 @@
# External Libraries
import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
:::info
Currently an external library can only belong to a single user which is selected when the library is initially created.
:::
@@ -98,6 +100,8 @@ First, we need to plan how we want to organize the libraries. The christmas trip
### Mount Docker Volumes
<ComposeBuilder query="storage.externalLibraries.0.path=&storage.externalLibraries.0.readOnly=true" />
The `immich-server` container will need access to the gallery. Modify your docker compose file as follows
```diff title="docker-compose.yml"
+25
View File
@@ -0,0 +1,25 @@
# Locked Folder
The Locked Folder keeps selected photos and videos out of normal library views and protects access to them with a PIN.
Assets can be moved to the Locked Folder from the asset actions menu. Access to the Locked Folder is protected by a PIN.
## Unlocking the Locked Folder
Unlocking the Locked Folder temporarily gives the current session access to locked assets.
While the session is unlocked, locked assets may also appear elsewhere in Immich where they are relevant, such as search results or utilities such as Review large files.
When the session is locked again, locked assets are hidden from those views.
## Session duration
Unlocking the Locked Folder initially grants elevated access to the current session for 15 minutes.
If the session is still being used near the end of that period, the expiry can be extended to keep the session unlocked for a short time longer.
Selecting **Lock** ends the elevated access immediately. Signing out also ends access because the session is destroyed.
## Moving assets out of the Locked Folder
Assets can be moved back out of the Locked Folder while the session is unlocked. Once moved out, they return to their normal visibility in Immich.
@@ -1,5 +1,7 @@
# Hardware-Accelerated Machine Learning
import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
This feature allows you to use a GPU to accelerate machine learning tasks, such as Smart Search and Facial Recognition, while reducing CPU load.
As this is a new feature, it is still experimental and may not work on all systems.
@@ -87,6 +89,8 @@ You do not need to redo any machine learning jobs after enabling hardware accele
## Setup
<ComposeBuilder />
1. If you do not already have it, download the latest [`hwaccel.ml.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
2. In `immich-machine-learning`, add one of -[armnn, cuda, rocm, openvino, rknn] to the `image` section's tag at the end of the line.
3. Still in the `docker-compose.yml` under `immich-machine-learning`, uncomment the `extends` section and change `cpu` to the appropriate backend.
+1 -1
View File
@@ -42,7 +42,7 @@ Make the following changes:
#### 2. Run Reset Jobs
Go to:
**Admin → Administration → Settings → Jobs**
**Administration → Job Queues**
Perform the following:
+4
View File
@@ -1,7 +1,11 @@
# Files Custom Locations
import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
This guide explains how to store generated and raw files with docker's volume mount in different locations.
<ComposeBuilder query="storage.customFolders=true" />
:::caution Backup
It is important to remember to update the backup settings after following the guide to back up the new backup paths if using automatic backup tools, especially `profile/`.
:::
+4
View File
@@ -1,11 +1,15 @@
# External Library
import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
This guide walks you through adding an [External Library](/features/libraries).
This guide assumes you are running Immich in Docker and that the files you wish to access are stored
in a directory on the same machine.
# Mount the directory into the containers.
<ComposeBuilder query="storage.externalLibraries.0.path=&storage.externalLibraries.0.readOnly=true" />
Edit `docker-compose.yml` to add one or more new mount points in the section `immich-server:` under `volumes:`.
If you want Immich to be able to delete the images in the external library or add metadata ([XMP sidecars](/features/xmp-sidecars)), remove `:ro` from the end of the mount point.
@@ -1,5 +1,7 @@
# Remote Machine Learning
import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
To alleviate [performance issues on low-memory systems](/FAQ.mdx#why-is-immich-slow-on-low-memory-systems-like-the-raspberry-pi) like the Raspberry Pi, you may also host Immich's machine learning container on a more powerful system, such as your laptop or desktop computer. The server container will send requests containing the image preview to the remote machine learning container for processing. The machine learning container does not persist this data or associate it with a particular user.
:::info
@@ -53,6 +55,8 @@ Version mismatches between both hosts may cause bugs and instability, so remembe
Adding a new URL to the settings is recommended over replacing the existing URL (http://immich-machine-learning:3003). This is because it will allow machine learning tasks to be processed successfully when the remote server is down by falling back to the local machine learning container. If you do not want machine learning tasks to be processed locally when the remote server is not available, you can instead replace the existing URL and only provide the remote container's URL. If doing this, you can remove the `immich-machine-learning` section of the local `docker-compose.yml` file to save resources, as this service will never be used.
<ComposeBuilder query="machineLearning.external=true" />
Do note that this will mean that Smart Search and Face Detection jobs will fail to be processed when the remote instance is not available. This in turn means that tasks dependent on these features—Duplicate Detection and Facial Recognition—will not run for affected assets. If this occurs, you must manually click the _Missing_ button next to Smart Search and Face Detection in the [Job Status](http://my.immich.app/admin/queues) page for the jobs to be retried.
## Load balancing
+3
View File
@@ -6,8 +6,11 @@ sidebar_position: 30
Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose.
import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';
<ComposeBuilder />
<DockerComposeSteps />
:::info Docker version
+3
View File
@@ -0,0 +1,3 @@
:::tip Docker Compose Builder
You can also use the <a href={`https://immich.app/docker-compose-builder${props.query ? `?${props.query}` : ''}`}>Docker Compose Builder</a> instead.
:::
+1 -1
View File
@@ -1,7 +1,7 @@
# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html
[[tools.wrangler]]
version = "4.127.0"
version = "4.135.0"
backend = "npm:wrangler"
[tools.wrangler.options]
+1 -1
View File
@@ -28,4 +28,4 @@ run = "prettier --write ."
run = "wrangler pages deploy build --project-name=${PROJECT_NAME} --branch=${BRANCH_NAME}"
[tools]
wrangler = "4.127.0"
wrangler = "4.135.0"
+5
View File
@@ -55,6 +55,11 @@ export default typescriptEslint.config([
'unicorn/prefer-event-target': 'off',
'unicorn/no-thenable': 'off',
'object-shorthand': ['error', 'always'],
'unicorn/prefer-combined-guards': 'off',
'unicorn/prefer-ternary': 'off',
'unicorn/prefer-early-return': 'off',
'unicorn/prefer-continue': 'off',
'unicorn/no-immediate-mutation': 'off',
},
},
]);
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "3.2.0-rc.0",
"version": "3.2.0",
"description": "",
"main": "index.js",
"type": "module",
@@ -32,7 +32,7 @@
"@playwright/test": "^1.44.1",
"@socket.io/component-emitter": "^3.1.2",
"@types/luxon": "^3.4.2",
"@types/node": "^24.13.3",
"@types/node": "^24.13.5",
"@types/pg": "^8.15.1",
"@types/pngjs": "^6.0.4",
"@types/supertest": "^7.0.0",
@@ -40,7 +40,7 @@
"eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^73.0.0",
"eslint-plugin-unicorn": "^75.0.0",
"exiftool-vendored": "^35.0.0",
"globals": "^17.0.0",
"luxon": "^3.4.4",
@@ -1,6 +1,6 @@
import { LoginResponseDto, ManualJobName } from '@immich/sdk';
import { errorDto } from 'src/responses';
import { app, utils } from 'src/utils';
import { errorDto } from 'src/responses.js';
import { app, utils } from 'src/utils.js';
import request from 'supertest';
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
@@ -1,6 +1,6 @@
import { LoginResponseDto } from '@immich/sdk';
import { errorDto } from 'src/responses';
import { app, utils } from 'src/utils';
import { errorDto } from 'src/responses.js';
import { app, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';
@@ -1,6 +1,6 @@
import { LoginResponseDto } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { utils } from 'src/utils';
import { utils } from 'src/utils.js';
test.describe.configure({ mode: 'serial' });
@@ -1,6 +1,6 @@
import { LoginResponseDto } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { utils } from 'src/utils';
import { utils } from 'src/utils.js';
test.describe.configure({ mode: 'serial' });
+3 -3
View File
@@ -9,9 +9,9 @@ import {
LoginResponseDto,
SharedLinkType,
} from '@immich/sdk';
import { createUserDto } from 'src/fixtures';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, utils } from 'src/utils';
import { createUserDto } from 'src/fixtures.js';
import { errorDto } from 'src/responses.js';
import { app, asBearerAuth, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
+3 -3
View File
@@ -1,7 +1,7 @@
import { LoginResponseDto, Permission, createApiKey } from '@immich/sdk';
import { createUserDto } from 'src/fixtures';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, utils } from 'src/utils';
import { createUserDto } from 'src/fixtures.js';
import { errorDto } from 'src/responses.js';
import { app, asBearerAuth, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
+4 -4
View File
@@ -14,10 +14,10 @@ import { randomBytes } from 'node:crypto';
import { readFile, writeFile } from 'node:fs/promises';
import { basename, join } from 'node:path';
import { Socket } from 'socket.io-client';
import { createUserDto, uuidDto } from 'src/fixtures';
import { makeRandomImage } from 'src/generators';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, tempDir, testAssetDir, utils } from 'src/utils';
import { createUserDto, uuidDto } from 'src/fixtures.js';
import { makeRandomImage } from 'src/generators.js';
import { errorDto } from 'src/responses.js';
import { app, asBearerAuth, tempDir, testAssetDir, utils } from 'src/utils.js';
import request from 'supertest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
@@ -1,6 +1,6 @@
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
import { readFile, writeFile } from 'node:fs/promises';
import { app, tempDir, utils } from 'src/utils';
import { app, tempDir, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';
@@ -7,7 +7,7 @@ import {
QueueName,
} from '@immich/sdk';
import { readFile } from 'node:fs/promises';
import { app, testAssetDir, utils } from 'src/utils';
import { app, testAssetDir, utils } from 'src/utils.js';
import request from 'supertest';
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
+1 -1
View File
@@ -2,7 +2,7 @@ import { LoginResponseDto, QueueCommand, QueueName, updateConfig } from '@immich
import { cpSync, rmSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { basename } from 'node:path';
import { asBearerAuth, testAssetDir, utils } from 'src/utils';
import { asBearerAuth, testAssetDir, utils } from 'src/utils.js';
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
describe('/jobs', () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { LoginResponseDto } from '@immich/sdk';
import { cpSync } from 'node:fs';
import { Socket } from 'socket.io-client';
import { testAssetDir, testAssetDirInternal, utils } from 'src/utils';
import { testAssetDir, testAssetDirInternal, utils } from 'src/utils.js';
import { utimes } from 'utimes';
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
+2 -2
View File
@@ -2,8 +2,8 @@ import { AssetVisibility, LoginResponseDto } from '@immich/sdk';
import { readFile } from 'node:fs/promises';
import { basename, join } from 'node:path';
import { Socket } from 'socket.io-client';
import { createUserDto } from 'src/fixtures';
import { app, testAssetDir, utils } from 'src/utils';
import { createUserDto } from 'src/fixtures.js';
import { app, testAssetDir, utils } from 'src/utils.js';
import request from 'supertest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
+2 -2
View File
@@ -9,8 +9,8 @@ import {
updateConfig,
} from '@immich/sdk';
import { createHash, randomBytes } from 'node:crypto';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, baseUrl, utils } from 'src/utils';
import { errorDto } from 'src/responses.js';
import { app, asBearerAuth, baseUrl, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';
+3 -3
View File
@@ -1,7 +1,7 @@
import { getPerson, LoginResponseDto, PersonResponseDto } from '@immich/sdk';
import { uuidDto } from 'src/fixtures';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, utils } from 'src/utils';
import { uuidDto } from 'src/fixtures.js';
import { errorDto } from 'src/responses.js';
import { app, asBearerAuth, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
+1 -1
View File
@@ -11,7 +11,7 @@ import { DateTime } from 'luxon';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { Socket } from 'socket.io-client';
import { app, asBearerAuth, TEN_TIMES, testAssetDir, utils } from 'src/utils';
import { app, asBearerAuth, TEN_TIMES, testAssetDir, utils } from 'src/utils.js';
import request from 'supertest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
const today = DateTime.now();
+2 -2
View File
@@ -1,6 +1,6 @@
import { LoginResponseDto } from '@immich/sdk';
import { createUserDto } from 'src/fixtures';
import { app, utils } from 'src/utils';
import { createUserDto } from 'src/fixtures.js';
import { app, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';
+3 -3
View File
@@ -1,7 +1,7 @@
import { LoginResponseDto, getSessions, login, signUpAdmin } from '@immich/sdk';
import { loginDto, signupDto, uuidDto } from 'src/fixtures';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, utils } from 'src/utils';
import { loginDto, signupDto, uuidDto } from 'src/fixtures.js';
import { errorDto } from 'src/responses.js';
import { app, asBearerAuth, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeEach, describe, expect, it } from 'vitest';
@@ -7,9 +7,9 @@ import {
createAlbum,
deleteUserAdmin,
} from '@immich/sdk';
import { createUserDto, uuidDto } from 'src/fixtures';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, baseUrl, shareUrl, utils } from 'src/utils';
import { createUserDto, uuidDto } from 'src/fixtures.js';
import { errorDto } from 'src/responses.js';
import { app, asBearerAuth, baseUrl, shareUrl, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';
+2 -2
View File
@@ -1,6 +1,6 @@
import { LoginResponseDto, searchStacks } from '@immich/sdk';
import { createUserDto } from 'src/fixtures';
import { app, asBearerAuth, utils } from 'src/utils';
import { createUserDto } from 'src/fixtures.js';
import { app, asBearerAuth, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';
@@ -1,6 +1,6 @@
import { LoginResponseDto, getConfig } from '@immich/sdk';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, utils } from 'src/utils';
import { errorDto } from 'src/responses.js';
import { app, asBearerAuth, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';
@@ -1,5 +1,5 @@
import { LoginResponseDto, getServerConfig } from '@immich/sdk';
import { app, utils } from 'src/utils';
import { app, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';
+3 -3
View File
@@ -9,9 +9,9 @@ import {
tagAssets,
upsertTags,
} from '@immich/sdk';
import { createUserDto } from 'src/fixtures';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, utils } from 'src/utils';
import { createUserDto } from 'src/fixtures.js';
import { errorDto } from 'src/responses.js';
import { app, asBearerAuth, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
+1 -1
View File
@@ -1,7 +1,7 @@
import { LoginResponseDto, getAssetInfo, getAssetStatistics } from '@immich/sdk';
import { existsSync } from 'node:fs';
import { Socket } from 'socket.io-client';
import { app, asBearerAuth, testAssetDir, testAssetDirInternal, utils } from 'src/utils';
import { app, asBearerAuth, testAssetDir, testAssetDirInternal, utils } from 'src/utils.js';
import request from 'supertest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
@@ -9,8 +9,8 @@ import {
login,
} from '@immich/sdk';
import { Socket } from 'socket.io-client';
import { createUserDto } from 'src/fixtures';
import { app, asBearerAuth, utils } from 'src/utils';
import { createUserDto } from 'src/fixtures.js';
import { app, asBearerAuth, utils } from 'src/utils.js';
import request from 'supertest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
+2 -2
View File
@@ -1,6 +1,6 @@
import { LoginResponseDto, getMyUser, login } from '@immich/sdk';
import { createUserDto } from 'src/fixtures';
import { app, asBearerAuth, utils } from 'src/utils';
import { createUserDto } from 'src/fixtures.js';
import { app, asBearerAuth, utils } from 'src/utils.js';
import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest';
+1 -1
View File
@@ -1,6 +1,6 @@
import { Permission } from '@immich/sdk';
import { stat } from 'node:fs/promises';
import { app, immichCli, utils } from 'src/utils';
import { app, immichCli, utils } from 'src/utils.js';
import { beforeEach, describe, expect, it } from 'vitest';
describe(`immich login`, () => {
@@ -1,4 +1,4 @@
import { immichCli, utils } from 'src/utils';
import { immichCli, utils } from 'src/utils.js';
import { beforeAll, describe, expect, it } from 'vitest';
describe(`immich server-info`, () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { LoginResponseDto, getAllAlbums, getAssetStatistics } from '@immich/sdk';
import { cpSync, readFileSync } from 'node:fs';
import { mkdir, readdir, rm, symlink } from 'node:fs/promises';
import { asKeyAuth, immichCli, specialCharStrings, testAssetDir, utils } from 'src/utils';
import { asKeyAuth, immichCli, specialCharStrings, testAssetDir, utils } from 'src/utils.js';
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
interface Test {
+1 -1
View File
@@ -1,5 +1,5 @@
import { readFileSync } from 'node:fs';
import { immichCli } from 'src/utils';
import { immichCli } from 'src/utils.js';
import { describe, expect, it } from 'vitest';
const pkg = JSON.parse(readFileSync('../packages/cli/package.json', 'utf8'));
@@ -1,4 +1,4 @@
import { immichAdmin, utils } from 'src/utils';
import { immichAdmin, utils } from 'src/utils.js';
import { beforeAll, describe, expect, it } from 'vitest';
describe(`immich-admin`, () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { LoginResponseDto } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { readFileSync } from 'node:fs';
import { testAssetDir, utils } from 'src/utils';
import { testAssetDir, utils } from 'src/utils.js';
test.describe('Album', () => {
let admin: LoginResponseDto;
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
import { readFile } from 'node:fs/promises';
import { basename, join } from 'node:path';
import type { Socket } from 'socket.io-client';
import { testAssetDir, utils } from 'src/utils';
import { testAssetDir, utils } from 'src/utils.js';
test.describe('Detail Panel', () => {
let admin: LoginResponseDto;
@@ -1,6 +1,6 @@
import { AssetMediaResponseDto, LoginResponseDto, SharedLinkType } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { utils } from 'src/utils';
import { utils } from 'src/utils.js';
test.describe('Asset Viewer Navbar', () => {
let admin: LoginResponseDto;
@@ -1,6 +1,6 @@
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
import { expect, type Page, test } from '@playwright/test';
import { utils } from 'src/utils';
import { utils } from 'src/utils.js';
test.describe('Slideshow', () => {
let admin: LoginResponseDto;
+1 -1
View File
@@ -1,5 +1,5 @@
import { expect, test } from '@playwright/test';
import { utils } from 'src/utils';
import { utils } from 'src/utils.js';
test.describe('Registration', () => {
test.beforeAll(() => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { AssetMediaResponseDto, LoginResponseDto, updateAssets } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import crypto from 'node:crypto';
import { asBearerAuth, utils } from 'src/utils';
import { asBearerAuth, utils } from 'src/utils.js';
test.describe('Duplicates Utility', () => {
let admin: LoginResponseDto;
+1 -1
View File
@@ -1,6 +1,6 @@
import { LoginResponseDto, ManualJobName, QueueName } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { utils } from 'src/utils';
import { utils } from 'src/utils.js';
test.describe.configure({ mode: 'serial' });
+1 -1
View File
@@ -1,7 +1,7 @@
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import type { Socket } from 'socket.io-client';
import { utils } from 'src/utils';
import { utils } from 'src/utils.js';
test.describe('Photo Viewer', () => {
let admin: LoginResponseDto;
+1 -1
View File
@@ -7,7 +7,7 @@ import {
createAlbum,
} from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { asBearerAuth, utils } from 'src/utils';
import { asBearerAuth, utils } from 'src/utils.js';
test.describe('Shared Links', () => {
let admin: LoginResponseDto;
+1 -1
View File
@@ -1,6 +1,6 @@
import { getUserAdmin } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { asBearerAuth, utils } from 'src/utils';
import { asBearerAuth, utils } from 'src/utils.js';
test.describe('User Administration', () => {
test.beforeAll(() => {
+1 -1
View File
@@ -1,6 +1,6 @@
import { LoginResponseDto } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { utils } from 'src/utils';
import { utils } from 'src/utils.js';
test.describe('Websocket', () => {
let admin: LoginResponseDto;
@@ -1,9 +1,9 @@
import { faker } from '@faker-js/faker';
import { MemoryType, type MemoryResponseDto, type OnThisDayDto } from '@immich/sdk';
import { MemoryType, type MemoryResponseDto } from '@immich/sdk';
import { DateTime } from 'luxon';
import { toAssetResponseDto } from 'src/ui/generators/timeline/rest-response';
import type { MockTimelineAsset } from 'src/ui/generators/timeline/timeline-config';
import { SeededRandom, selectRandomMultiple } from 'src/ui/generators/timeline/utils';
import { toAssetResponseDto } from 'src/ui/generators/timeline/rest-response.js';
import type { MockTimelineAsset } from 'src/ui/generators/timeline/timeline-config.js';
import { SeededRandom, selectRandomMultiple } from 'src/ui/generators/timeline/utils.js';
export type MemoryConfig = {
id?: string;
@@ -25,7 +25,7 @@ export function generateMemory(config: MemoryConfig, assets: MockTimelineAsset[]
return {
id: memoryId,
assets: assets.map((asset) => toAssetResponseDto(asset)),
data: { year: config.year } as OnThisDayDto,
data: { year: config.year },
memoryAt: config.memoryAt,
createdAt: now,
updatedAt: now,
@@ -1,5 +1,5 @@
import { generateConsecutiveDays, generateDayAssets } from 'src/ui/generators/timeline/model-objects';
import { SeededRandom, selectRandomDays } from 'src/ui/generators/timeline/utils';
import { generateConsecutiveDays, generateDayAssets } from 'src/ui/generators/timeline/model-objects.js';
import { SeededRandom, selectRandomDays } from 'src/ui/generators/timeline/utils.js';
import type { MockTimelineAsset } from './timeline-config';
import { GENERATION_CONSTANTS } from './timeline-config';
+1 -1
View File
@@ -1,5 +1,5 @@
import sharp from 'sharp';
import { SeededRandom } from 'src/ui/generators/timeline/utils';
import { SeededRandom } from 'src/ui/generators/timeline/utils.js';
export const randomThumbnail = async (seed: string, ratio: number) => {
const height = 235;
@@ -6,7 +6,7 @@ import { faker } from '@faker-js/faker';
import { AssetVisibility } from '@immich/sdk';
import { DateTime } from 'luxon';
import { writeFileSync } from 'node:fs';
import { SeededRandom } from 'src/ui/generators/timeline/utils';
import { SeededRandom } from 'src/ui/generators/timeline/utils.js';
import type { DayPattern, MonthDistribution } from './distribution-patterns';
import { ASSET_DISTRIBUTION, DAY_DISTRIBUTION } from './distribution-patterns';
import type { MockTimelineAsset, MockTimelineData, SerializedTimelineData, TimelineConfig } from './timeline-config';
@@ -15,8 +15,8 @@ import {
type UserResponseDto,
} from '@immich/sdk';
import { DateTime } from 'luxon';
import { signupDto } from 'src/fixtures';
import { parseTimeBucketKey } from 'src/ui/generators/timeline/utils';
import { signupDto } from 'src/fixtures.js';
import { parseTimeBucketKey } from 'src/ui/generators/timeline/utils.js';
import type { MockTimelineAsset, MockTimelineData } from './timeline-config';
/**
@@ -1,5 +1,5 @@
import type { AssetVisibility } from '@immich/sdk';
import { DayPattern, MonthDistribution } from 'src/ui/generators/timeline/distribution-patterns';
import { DayPattern, MonthDistribution } from 'src/ui/generators/timeline/distribution-patterns.js';
// Constants for generation parameters
export const GENERATION_CONSTANTS = {
+1 -1
View File
@@ -1,5 +1,5 @@
import { DateTime } from 'luxon';
import { GENERATION_CONSTANTS, MockTimelineAsset } from 'src/ui/generators/timeline/timeline-config';
import { GENERATION_CONSTANTS, MockTimelineAsset } from 'src/ui/generators/timeline/timeline-config.js';
/**
* Linear Congruential Generator for deterministic pseudo-random numbers
+1 -1
View File
@@ -1,5 +1,5 @@
import { BrowserContext } from '@playwright/test';
import { playwrightHost } from 'src/../playwright.config';
import { playwrightHost } from 'src/../playwright.config.js';
export const setupBaseMockApiRoutes = async (context: BrowserContext, adminUserId: string) => {
await context.addCookies([
@@ -1,7 +1,7 @@
import { faker } from '@faker-js/faker';
import { AssetTypeEnum, AssetVisibility, type AssetResponseDto, type StackResponseDto } from '@immich/sdk';
import { BrowserContext } from '@playwright/test';
import { randomPreview, randomThumbnail } from 'src/ui/generators/timeline';
import { randomPreview, randomThumbnail } from 'src/ui/generators/timeline.js';
export type MockStack = {
id: string;
@@ -1,5 +1,5 @@
import { BrowserContext } from '@playwright/test';
import { randomThumbnail } from 'src/ui/generators/timeline';
import { randomThumbnail } from 'src/ui/generators/timeline.js';
// Minimal valid H.264 MP4 (8x8px, 1 frame) that browsers can decode to get videoWidth/videoHeight
const MINIMAL_MP4_BASE64 =
+1 -1
View File
@@ -11,7 +11,7 @@ import {
randomThumbnail,
TimelineData,
} from 'src/ui/generators/timeline';
import { sleep } from 'src/ui/specs/timeline/utils';
import { sleep } from 'src/ui/specs/timeline/utils.js';
import { MINIMAL_MP4_BUFFER } from './face-editor-network';
export class TimelineTestContext {
@@ -9,9 +9,9 @@ import {
TimelineAssetConfig,
TimelineData,
} from 'src/ui/generators/timeline';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network';
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network';
import { utils } from 'src/utils';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network.js';
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network.js';
import { utils } from 'src/utils.js';
import { assetViewerUtils } from '../timeline/utils';
test.describe.configure({ mode: 'parallel' });
@@ -1,5 +1,5 @@
import { expect, test } from '@playwright/test';
import { toAssetResponseDto } from 'src/ui/generators/timeline';
import { toAssetResponseDto } from 'src/ui/generators/timeline.js';
import {
createMockStack,
createMockStackAsset,
@@ -1,5 +1,5 @@
import { expect, Page, test } from '@playwright/test';
import { SeededRandom, selectRandom, TimelineAssetConfig } from 'src/ui/generators/timeline';
import { SeededRandom, selectRandom, TimelineAssetConfig } from 'src/ui/generators/timeline.js';
import {
createMockPeople,
FaceCreateCapture,
@@ -1,13 +1,13 @@
import type { AssetOcrResponseDto, AssetResponseDto } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { toAssetResponseDto } from 'src/ui/generators/timeline';
import { toAssetResponseDto } from 'src/ui/generators/timeline.js';
import {
createMockStack,
createMockStackAsset,
MockStack,
setupBrokenAssetMockApiRoutes,
} from 'src/ui/mock-network/broken-asset-network';
import { createMockOcrData, setupOcrMockApiRoutes } from 'src/ui/mock-network/ocr-network';
import { createMockOcrData, setupOcrMockApiRoutes } from 'src/ui/mock-network/ocr-network.js';
import { assetViewerUtils } from '../timeline/utils';
import { setupAssetViewerFixture } from './utils';
@@ -1,7 +1,7 @@
import { faker } from '@faker-js/faker';
import type { AssetResponseDto } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { toAssetResponseDto } from 'src/ui/generators/timeline';
import { toAssetResponseDto } from 'src/ui/generators/timeline.js';
import {
createMockStack,
createMockStackAsset,
+3 -3
View File
@@ -11,9 +11,9 @@ import {
TimelineData,
toAssetResponseDto,
} from 'src/ui/generators/timeline';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network';
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network';
import { utils } from 'src/utils';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network.js';
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network.js';
import { utils } from 'src/utils.js';
export type AssetViewerTestFixture = {
adminUserId: string;
@@ -1,7 +1,7 @@
import { faker } from '@faker-js/faker';
import type { MemoryResponseDto } from '@immich/sdk';
import { test } from '@playwright/test';
import { generateMemoriesFromTimeline } from 'src/ui/generators/memory';
import { generateMemoriesFromTimeline } from 'src/ui/generators/memory.js';
import {
Changes,
createDefaultTimelineConfig,
@@ -9,9 +9,9 @@ import {
TimelineAssetConfig,
TimelineData,
} from 'src/ui/generators/timeline';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network';
import { MemoryChanges, setupMemoryMockApiRoutes } from 'src/ui/mock-network/memory-network';
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network.js';
import { MemoryChanges, setupMemoryMockApiRoutes } from 'src/ui/mock-network/memory-network.js';
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network.js';
import { memoryAssetViewerUtils, memoryGalleryUtils, memoryViewerUtils } from './utils';
test.describe.configure({ mode: 'parallel' });
@@ -8,8 +8,8 @@ import {
TimelineData,
toAssetResponseDto,
} from 'src/ui/generators/timeline';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network';
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network.js';
import { setupTimelineMockApiRoutes, TimelineTestContext } from 'src/ui/mock-network/timeline-network.js';
import { assetViewerUtils } from '../timeline/utils';
const buildSearchUrl = (assetId: string) => {
@@ -13,13 +13,13 @@ import {
TimelineAssetConfig,
TimelineData,
} from 'src/ui/generators/timeline';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network';
import { setupBaseMockApiRoutes } from 'src/ui/mock-network/base-network.js';
import {
pageRoutePromise,
setupTimelineMockApiRoutes,
TimelineTestContext,
} from 'src/ui/mock-network/timeline-network';
import { utils } from 'src/utils';
import { utils } from 'src/utils.js';
import { assetViewerUtils, padYearMonth, pageUtils, poll, thumbnailUtils, timelineUtils } from './utils';
test.describe.configure({ mode: 'parallel' });
+1 -1
View File
@@ -1,6 +1,6 @@
import { expect, Page } from '@playwright/test';
import { DateTime } from 'luxon';
import { TimelineAssetConfig } from 'src/ui/generators/timeline';
import { TimelineAssetConfig } from 'src/ui/generators/timeline.js';
export const sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
+2 -2
View File
@@ -67,8 +67,8 @@ import { promisify } from 'node:util';
import { createGzip } from 'node:zlib';
import pg from 'pg';
import { io, type Socket } from 'socket.io-client';
import { loginDto, signupDto } from 'src/fixtures';
import { makeRandomImage } from 'src/generators';
import { loginDto, signupDto } from 'src/fixtures.js';
import { makeRandomImage } from 'src/generators.js';
import request from 'supertest';
import { playwrightDbHost, playwrightHost, playwriteBaseUrl } from '../playwright.config';
+15 -2
View File
@@ -35,7 +35,7 @@
"add_to_albums": "Add to albums",
"add_to_albums_count": "Add to albums ({count})",
"add_to_bottom_bar": "Add to",
"add_upload_to_stack": "Add upload to stack",
"add_upload_to_stack": "Upload and {isStack, select, true {add to stack} other {create stack}}",
"add_url": "Add URL",
"add_user": "Add user",
"added_to_archive": "Added to archive",
@@ -369,11 +369,17 @@
"storage_template_migration_info": "The storage template will convert all extensions to lowercase. Template changes will only apply to new assets. To retroactively apply the template to previously uploaded assets, run the <link>{job}</link>.",
"storage_template_migration_job": "Storage Template Migration Job",
"storage_template_more_details": "For more details about this feature, refer to the <template-link>Storage Template</template-link> and its <implications-link>implications</implications-link>",
"storage_template_onboarding_description_v2": "When enabled, this feature will auto-organize files based on a user-defined template. For more information, please see the <link>documentation</link>.",
"storage_template_onboarding_description_v3": "Storage templates allow you to define a custom folder structure on disk for your uploaded assets. This can always be changed later.",
"storage_template_option_custom": "Custom",
"storage_template_option_custom_description": "Build your own folder structure with template variables",
"storage_template_option_date_description": "Sort files into year, month, and day folders",
"storage_template_option_none": "No template",
"storage_template_option_none_description": "Keep the folder structure Immich uses by default",
"storage_template_path_length": "Approximate path length limit: <b>{length, number}</b>/{limit, number}",
"storage_template_settings": "Storage Template",
"storage_template_settings_description": "Manage the folder structure and file name of the upload asset",
"storage_template_user_label": "<code>{label}</code> is the user's Storage Label",
"storage_template_variables": "Template variables",
"system_settings": "System Settings",
"tag_cleanup_job": "Tag cleanup",
"template_email_available_tags": "You can use the following variables in your template: {tags}",
@@ -663,6 +669,7 @@
"biometric_no_options": "No biometric options available",
"biometric_not_available": "Biometric authentication is not available on this device",
"birthdate_set_description": "Date of birth is used to calculate the age of this person at the time of a photo.",
"birthday_memory_title": "{name}'s birthday",
"blurred_background": "Blurred background",
"browse_templates": "Browse templates",
"bugs_and_feature_requests": "Bugs & Feature Requests",
@@ -727,6 +734,8 @@
"clear_all_recent_searches": "Clear all recent searches",
"clear_failed_count": "Clear failed ({count})",
"clear_file_cache": "Clear File Cache",
"clear_file_cache_error": "Failed to clear file cache",
"clear_file_cache_success": "File cache cleared",
"clear_message": "Clear message",
"clear_value": "Clear value",
"client_cert_import": "Import",
@@ -1201,6 +1210,7 @@
"height": "Height",
"hi_user": "Hi {name} ({email})",
"hide_all_people": "Hide all people",
"hide_asset_owners": "Hide asset owners",
"hide_gallery": "Hide gallery",
"hide_named_person": "Hide person {name}",
"hide_password": "Hide password",
@@ -1796,6 +1806,7 @@
"removed_from_archive": "Removed from archive",
"removed_from_favorites": "Removed from favorites",
"removed_from_favorites_count": "{count, plural, other {Removed #}} from favorites",
"removed_from_stack": "Removed asset from stack",
"removed_memory": "Removed memory",
"removed_tagged_assets": "Removed tag from {count, plural, one {# asset} other {# assets}}",
"rename": "Rename",
@@ -1814,6 +1825,7 @@
"reset_sqlite_confirmation": "Are you sure you want to clear the app data? This will remove all settings and sign you out.",
"reset_sqlite_confirmation_note": "Note: You will need to restart the app after clearing.",
"reset_sqlite_done": "App data has been cleared. Please restart Immich and log in again.",
"reset_sqlite_error_hint": "Something went wrong, reset the local database with the button below",
"reset_sqlite_success": "Successfully reset the SQLite database",
"reset_to_default": "Reset to default",
"resolution": "Resolution",
@@ -2094,6 +2106,7 @@
"source": "Source",
"stack": "Stack",
"stack_action_prompt": "{count} stacked",
"stack_created": "Stack created",
"stack_duplicates": "Stack duplicates",
"stack_selected_photos": "Stack selected photos",
"stacked_assets_count": "Stacked {count, plural, one {# asset} other {# assets}}",
+18 -24
View File
@@ -1,15 +1,15 @@
ARG DEVICE=cpu
FROM python:3.11-bookworm@sha256:20ec607c68642c64c73269ce245aa0f060913f4129d15d9687850aa158e6269c AS builder-cpu
FROM python:3.13-slim-trixie@sha256:9d2e5553305c7c7b0097999bb17187c69b921ccd6bc9d40e4bb5ebe652c00285 AS builder-cpu
FROM python:3.13-slim-trixie@sha256:f82c96458eedc847b233e582eb31336f4954b39cae020b6dcf5b3ed0e5cbcd74 AS builder-openvino
FROM builder-cpu AS builder-openvino
FROM builder-cpu AS builder-cuda
FROM builder-cpu AS builder-armnn
FROM python:3.13-trixie@sha256:4a0820d8b1b1a1e012a23ff9a1296a3e28b48896a4d27a328a91ee197ce619d9 AS builder-armnn
# renovate: datasource=github-releases depName=ARM-software/armnn
ARG ARMNN_VERSION="v24.05"
ARG ARMNN_VERSION="v26.07"
ENV ARMNN_PATH=/opt/armnn
COPY ann /opt/ann
@@ -18,7 +18,7 @@ RUN mkdir /opt/armnn && \
cd /opt/ann && \
sh build.sh
FROM builder-cpu AS builder-rknn
FROM python:3.12-slim-trixie@sha256:78387bc3881b8273120a12ebe6c1ab22b018ccc2c9adf565ae1ac9b536e184ea AS builder-rknn
# Warning: 25GiB+ disk space required to pull this image
# TODO: find a way to reduce the image size
@@ -39,12 +39,11 @@ RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --extra ${DEVICE} --no-dev --no-editable --no-install-project --compile-bytecode --no-progress --active --link-mode copy
FROM python:3.11-slim-bookworm@sha256:e2d3af735aff6eeee600b1933bedd99da6645fedf572cc12ef4cc1331f2ceebe AS prod-cpu
FROM python:3.13-slim-trixie@sha256:9d2e5553305c7c7b0097999bb17187c69b921ccd6bc9d40e4bb5ebe652c00285 AS prod-cpu
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \
MACHINE_LEARNING_MODEL_ARENA=false
ENV MACHINE_LEARNING_MODEL_ARENA=false
FROM python:3.13-slim-trixie@sha256:f82c96458eedc847b233e582eb31336f4954b39cae020b6dcf5b3ed0e5cbcd74 AS prod-openvino
FROM python:3.13-slim-trixie@sha256:9d2e5553305c7c7b0097999bb17187c69b921ccd6bc9d40e4bb5ebe652c00285 AS prod-openvino
RUN apt-get update && \
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
@@ -63,8 +62,7 @@ RUN apt-get update && \
FROM nvidia/cuda:12.2.2-runtime-ubuntu22.04@sha256:94c1577b2cd9dd6c0312dc04dff9cb2fdce2b268018abc3d7c2dbcacf1155000 AS prod-cuda
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \
MACHINE_LEARNING_MODEL_ARENA=false
ENV MACHINE_LEARNING_MODEL_ARENA=false
RUN apt-get update && \
# Pascal support was dropped in 9.11
@@ -73,8 +71,8 @@ RUN apt-get update && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder-cuda /usr/local/bin/python3 /usr/local/bin/python3
COPY --from=builder-cuda /usr/local/lib/python3.11 /usr/local/lib/python3.11
COPY --from=builder-cuda /usr/local/lib/libpython3.11.so /usr/local/lib/libpython3.11.so
COPY --from=builder-cuda /usr/local/lib/python3.13 /usr/local/lib/python3.13
COPY --from=builder-cuda /usr/local/lib/libpython3.13.so /usr/local/lib/libpython3.13.so
FROM rocm/dev-ubuntu-24.04:7.2-complete@sha256:86e11093b4a7ec2a79b1b6701d10e840a6994f21c7e05929b51eb9be361c683a AS prod-rocm
@@ -85,7 +83,6 @@ RUN apt-get update && apt-get install --no-install-recommends -yqq migraphx miop
FROM prod-cpu AS prod-armnn
ENV LD_LIBRARY_PATH=/opt/armnn \
LD_PRELOAD=/usr/lib/libmimalloc.so.2 \
MACHINE_LEARNING_MODEL_ARENA=false
RUN apt-get update && apt-get install -y --no-install-recommends ocl-icd-libopencl1 mesa-opencl-icd libgomp1 && \
@@ -96,21 +93,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends ocl-icd-libopen
COPY --from=builder-armnn \
/opt/armnn/libarmnn.so.?? \
/opt/armnn/libarmnnOnnxParser.so.?? \
/opt/armnn/libarmnnDeserializer.so.?? \
/opt/armnn/libarmnnTfLiteParser.so.?? \
/opt/armnn/libprotobuf.so.?.??.?.? \
/opt/ann/libann.s[o] \
/opt/ann/build.sh \
/opt/armnn/
FROM prod-cpu AS prod-rknn
FROM python:3.12-slim-trixie@sha256:78387bc3881b8273120a12ebe6c1ab22b018ccc2c9adf565ae1ac9b536e184ea AS prod-rknn
# renovate: datasource=github-tags depName=airockchip/rknn-toolkit2
ARG RKNN_TOOLKIT_VERSION="v2.3.0"
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \
MACHINE_LEARNING_MODEL_ARENA=false
ENV MACHINE_LEARNING_MODEL_ARENA=false
ADD --chmod=644 --checksum=sha256:73993ed4b440460825f21611731564503cc1d5a0c123746477da6cd574f34885 "https://github.com/airockchip/rknn-toolkit2/raw/refs/tags/${RKNN_TOOLKIT_VERSION}/rknpu2/runtime/Linux/librknn_api/aarch64/librknnrt.so" /usr/lib/
@@ -118,13 +112,13 @@ FROM prod-${DEVICE} AS prod
ARG DEVICE
RUN apt-get update && \
apt-get install -y --no-install-recommends tini ccache libgl1 libglib2.0-0 libgomp1 $(if ! [ "$DEVICE" = "openvino" ] && ! [ "$DEVICE" = "rocm" ]; then echo "libmimalloc2.0"; fi) && \
RUN MIMALLOC=$(case "$DEVICE" in cuda) echo "libmimalloc2.0";; openvino|rocm);; *) echo "libmimalloc3";; esac); \
apt-get update && \
apt-get install -y --no-install-recommends tini ccache libgl1 libglib2.0-0 libgomp1 $MIMALLOC && \
apt-get autoremove -yqq && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN ln -s "/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" /usr/lib/libmimalloc.so.2
rm -rf /var/lib/apt/lists/* && \
{ [ -z "$MIMALLOC" ] || ls /usr/lib/*-linux-gnu/libmimalloc.so.? > /etc/ld.so.preload; }
WORKDIR /usr/src
ENV TRANSFORMERS_CACHE=/cache \
+3 -18
View File
@@ -6,8 +6,6 @@
#include "armnn/INetwork.hpp"
#include "armnn/Types.hpp"
#include "armnnDeserializer/IDeserializer.hpp"
#include "armnnTfLiteParser/ITfLiteParser.hpp"
#include "armnnOnnxParser/IOnnxParser.hpp"
using namespace armnn;
@@ -163,22 +161,9 @@ private:
INetworkPtr loadModel(const char *modelPath)
{
const auto path = std::string(modelPath);
if (path.rfind(".tflite") == path.length() - 7) // endsWith()
{
auto parser = armnnTfLiteParser::ITfLiteParser::CreateRaw();
return parser->CreateNetworkFromBinaryFile(modelPath);
}
else if (path.rfind(".onnx") == path.length() - 5) // endsWith()
{
auto parser = armnnOnnxParser::IOnnxParser::CreateRaw();
return parser->CreateNetworkFromBinaryFile(modelPath);
}
else
{
std::ifstream ifs(path, std::ifstream::in | std::ifstream::binary);
auto parser = armnnDeserializer::IDeserializer::CreateRaw();
return parser->CreateNetworkFromBinary(ifs);
}
std::ifstream ifs(path, std::ifstream::in | std::ifstream::binary);
auto parser = armnnDeserializer::IDeserializer::CreateRaw();
return parser->CreateNetworkFromBinary(ifs);
}
static BindingPointInfo getInputTensorInfo(LayerBindingId inputBindingId, TensorInfo info)
+1 -1
View File
@@ -1,3 +1,3 @@
#!/usr/bin/env sh
g++ -shared -O3 -o libann.so -fuse-ld=gold -std=c++17 -I"$ARMNN_PATH"/include -larmnn -larmnnDeserializer -larmnnTfLiteParser -larmnnOnnxParser -L"$ARMNN_PATH" ann.cpp
g++ -shared -fPIC -O3 -o libann.so -std=c++17 -I"$ARMNN_PATH"/include ann.cpp -L"$ARMNN_PATH" -larmnn -larmnnDeserializer
+15 -15
View File
@@ -1,42 +1,42 @@
# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html
[[tools.python]]
version = "3.11.16"
version = "3.12.14"
backend = "core:python"
[tools.python."platforms.linux-arm64"]
checksum = "sha256:6a250f5c9326eebaa528d5d132cd66ebd1d7540cf45ac703f19e13f41d1be9b6"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.11.16+20260814-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz"
checksum = "sha256:577b4bec0793ad1ff0cbff9adbd0df078eddde38a4c41bf5d83ad381a85ee39d"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz"
provenance = "github-attestations"
[tools.python."platforms.linux-arm64-musl"]
checksum = "sha256:24f6a6a45c2007cf4c883bef736a6d3776dc8895c7c38aae16d6cae1eb51b768"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.11.16+20260814-aarch64-unknown-linux-musl-install_only_stripped.tar.gz"
checksum = "sha256:a0ad6f01b9204eba573a08927097b78143c564f98bea68a41ea1e172f041da3a"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-aarch64-unknown-linux-musl-install_only_stripped.tar.gz"
provenance = "github-attestations"
[tools.python."platforms.linux-x64"]
checksum = "sha256:3b437f08720d3ddd9c1904532476ec9fb373e800d1598c72c907dd6d5f29b79b"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.11.16+20260814-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz"
checksum = "sha256:72748da13197c1fb161e3afeef20a6a385ff24f2165e6e2758e47008e7faba4c"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz"
provenance = "github-attestations"
[tools.python."platforms.linux-x64-musl"]
checksum = "sha256:8aff4da777da0c10846ae602db71ce0419f4f70b46eb30f2b5c7bf8fb0db5bba"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.11.16+20260814-x86_64-unknown-linux-musl-install_only_stripped.tar.gz"
checksum = "sha256:1f37044c8cdbd74d5ee112a753c65ef209fedd169c98f3e4e748a93e27eb27a4"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-x86_64-unknown-linux-musl-install_only_stripped.tar.gz"
provenance = "github-attestations"
[tools.python."platforms.macos-arm64"]
checksum = "sha256:a394f2bf78a48990fc88e7c5586c7e1be5e69f0c9bd027883211b68b768e11c5"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.11.16+20260814-aarch64-apple-darwin-install_only_stripped.tar.gz"
checksum = "blake3:0b66e8cda277c7f537d1954b89c1ef2797a68cb39909ec9bb9a59b5175dac11a"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-aarch64-apple-darwin-install_only_stripped.tar.gz"
provenance = "github-attestations"
[tools.python."platforms.macos-x64"]
checksum = "sha256:4d60589b702aff21379ddf6bd648e75c7023a257a078fdd71df4c148cf609cc2"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.11.16+20260814-x86_64-apple-darwin-install_only_stripped.tar.gz"
checksum = "sha256:65b195c9cedc1fef6767f044f9822069adbd1bd9204d424ece4628776fdc04bb"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-x86_64-apple-darwin-install_only_stripped.tar.gz"
provenance = "github-attestations"
[tools.python."platforms.windows-x64"]
checksum = "sha256:c6de1a7781580d13f68dece33ac10b7a608c7ac21fc08cf0f4e0678a7d134905"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.11.16+20260814-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"
checksum = "sha256:7c45c9622400d578709a9b2cddbe8124cc21d382409d9f13406d706d28e31b14"
url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260901/cpython-3.12.14+20260901-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"
provenance = "github-attestations"
[[tools.uv]]
+1 -1
View File
@@ -1,5 +1,5 @@
[tools]
python = "3.11"
python = "3.12"
uv = "0.8.15"
[tasks.install]
+4 -4
View File
@@ -1,9 +1,9 @@
[project]
name = "immich-ml"
version = "3.2.0rc0"
version = "3.2.0"
description = ""
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
requires-python = ">=3.11,<4.0"
requires-python = ">=3.12,<4.0"
readme = "README.md"
dependencies = [
"aiocache>=0.12.1,<1.0",
@@ -68,7 +68,7 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.mypy]
python_version = "3.11"
python_version = "3.12"
plugins = "pydantic.mypy"
follow_imports = "silent"
warn_redundant_casts = true
@@ -85,7 +85,7 @@ warn_untyped_fields = true
[tool.ruff]
line-length = 120
target-version = "py311"
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I"]
+472 -795
View File
File diff suppressed because it is too large Load Diff
+34 -34
View File
@@ -212,36 +212,36 @@ checksum = "sha256:b6c17e747ae78cdd6de4d7532b3164b277daee97c007d3eaa2b39cca99882
url = "https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2_windows-x64_bin.zip"
[[tools.node]]
version = "24.15.0"
version = "24.21.0"
backend = "core:node"
[tools.node."platforms.linux-arm64"]
checksum = "sha256:73afc234d558c24919875f51c2d1ea002a2ada4ea6f83601a383869fefa64eed"
url = "https://nodejs.org/dist/v24.15.0/node-v24.15.0-linux-arm64.tar.gz"
checksum = "sha256:724282c3b43aec998aa9527380465b45d229e021b58035f5f4f63095eabfe5d5"
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-linux-arm64.tar.gz"
[tools.node."platforms.linux-arm64-musl"]
checksum = "sha256:31e98aa960a067da91edffd5d93bc46657b5d2a8029612c359f5f2ac0060152a"
url = "https://unofficial-builds.nodejs.org/download/release/v24.15.0/node-v24.15.0-linux-arm64-musl.tar.gz"
checksum = "sha256:3048b0811e158ca0d8672b59c839861763e144492980d8d29b369dfee45747e4"
url = "https://unofficial-builds.nodejs.org/download/release/v24.21.0/node-v24.21.0-linux-arm64-musl.tar.gz"
[tools.node."platforms.linux-x64"]
checksum = "sha256:44836872d9aec49f1e6b52a9a922872db9a2b02d235a616a5681b6a85fec8d89"
url = "https://nodejs.org/dist/v24.15.0/node-v24.15.0-linux-x64.tar.gz"
checksum = "sha256:6e1db87ef58b8819e5d5402eff1536491b18edd8eb7bee5ef7897876e88dc5ff"
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-linux-x64.tar.gz"
[tools.node."platforms.linux-x64-musl"]
checksum = "sha256:f55af5bd489c5347b113ca6594cae00a54b30ba57ac5875324311bfc6f4762e3"
url = "https://unofficial-builds.nodejs.org/download/release/v24.15.0/node-v24.15.0-linux-x64-musl.tar.gz"
checksum = "sha256:01a713e34da98e7b4d2a3191ee7ca3f93dd0384d1a2ef23c379b2161ee6e133a"
url = "https://unofficial-builds.nodejs.org/download/release/v24.21.0/node-v24.21.0-linux-x64-musl.tar.gz"
[tools.node."platforms.macos-arm64"]
checksum = "sha256:372331b969779ab5d15b949884fc6eaf88d5afe87bde8ba881d6400b9100ffc4"
url = "https://nodejs.org/dist/v24.15.0/node-v24.15.0-darwin-arm64.tar.gz"
checksum = "sha256:bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057"
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-darwin-arm64.tar.gz"
[tools.node."platforms.macos-x64"]
checksum = "sha256:ffd5ee293467927f3ee731a553eb88fd1f48cf74eebc2d74a6babe4af228673b"
url = "https://nodejs.org/dist/v24.15.0/node-v24.15.0-darwin-x64.tar.gz"
checksum = "sha256:1462cb3b3046b815cf8ea436d3da450ec1a9f11dac7e5a46b0ada5305d7e8097"
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-darwin-x64.tar.gz"
[tools.node."platforms.windows-x64"]
checksum = "sha256:cc5149eabd53779ce1e7bdc5401643622d0c7e6800ade18928a767e940bb0e62"
url = "https://nodejs.org/dist/v24.15.0/node-v24.15.0-win-x64.zip"
checksum = "sha256:158f7685b44de51f6c0df1d153526cbcd3e1bc739a8dfc607721cef75de9e541"
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-win-x64.zip"
[[tools."npm:@openapitools/openapi-generator-cli"]]
version = "2.41.0"
@@ -291,43 +291,43 @@ url = "https://github.com/opentofu/opentofu/releases/download/v1.12.6/tofu_1.12.
url_api = "https://api.github.com/repos/opentofu/opentofu/releases/assets/520762708"
[[tools.pnpm]]
version = "11.24.0"
version = "11.27.0"
backend = "aqua:pnpm/pnpm"
[tools.pnpm."platforms.linux-arm64"]
checksum = "sha256:52f9cd410ef3bbb1cdd79c399b27772fa6d929bc3cf7c80ff3923a81e1c93be3"
url = "https://github.com/pnpm/pnpm/releases/download/v11.24.0/pnpm-linux-arm64.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/527747053"
checksum = "sha256:1f3c5dbdfe20b011ea86950b4fd61b5918cbde874c099032fe9064f7a53816bf"
url = "https://github.com/pnpm/pnpm/releases/download/v11.27.0/pnpm-linux-arm64.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/559064653"
provenance = "github-attestations"
[tools.pnpm."platforms.linux-arm64-musl"]
checksum = "sha256:950160e3d1d039e0bd1f234da5a307fdc9c1d6b9c030683adfcb85f607c7d500"
url = "https://github.com/pnpm/pnpm/releases/download/v11.24.0/pnpm-linux-arm64-musl.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/527747056"
checksum = "sha256:ee24db9e6beb2f7a8b40051e008dd4eae8d6feb4fc8d4d8447ed84a34bb7477c"
url = "https://github.com/pnpm/pnpm/releases/download/v11.27.0/pnpm-linux-arm64-musl.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/559064657"
provenance = "github-attestations"
[tools.pnpm."platforms.linux-x64"]
checksum = "sha256:0a9b76dedb5fe8cc5e7f3a9fd70854181cfb08a487ebaf0c9cfb044dc860936c"
url = "https://github.com/pnpm/pnpm/releases/download/v11.24.0/pnpm-linux-x64.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/527747059"
checksum = "sha256:14ed04e631e4563abf3570dda1a365316683149a482b741a2db37e495f6b9cdb"
url = "https://github.com/pnpm/pnpm/releases/download/v11.27.0/pnpm-linux-x64.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/559064655"
provenance = "github-attestations"
[tools.pnpm."platforms.linux-x64-musl"]
checksum = "sha256:2fce00e10729d0c1f3836d499b908697fd3cd0832332651fbcee79c0a7442dcb"
url = "https://github.com/pnpm/pnpm/releases/download/v11.24.0/pnpm-linux-x64-musl.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/527747057"
checksum = "sha256:27d5c9481f12fbb5dbcf85df5677b5bae1e164af1fe05a182481da6e5043cd07"
url = "https://github.com/pnpm/pnpm/releases/download/v11.27.0/pnpm-linux-x64-musl.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/559064658"
provenance = "github-attestations"
[tools.pnpm."platforms.macos-arm64"]
checksum = "sha256:c4df428b6c348b3037ba851359215065ce070d711dc96a9750052fdbfbe240d3"
url = "https://github.com/pnpm/pnpm/releases/download/v11.24.0/pnpm-darwin-arm64.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/527747061"
checksum = "sha256:9e8724db1d733d651cfe9a55d6de3d59fc8c5e472b9b4e9c2844acafd38c015e"
url = "https://github.com/pnpm/pnpm/releases/download/v11.27.0/pnpm-darwin-arm64.tar.gz"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/559064654"
provenance = "github-attestations"
[tools.pnpm."platforms.windows-x64"]
checksum = "sha256:c5ca642230551342a9fffafe508c181f346e2c79fcae2b1cae4c37ceb423481a"
url = "https://github.com/pnpm/pnpm/releases/download/v11.24.0/pnpm-win32-x64.zip"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/527747055"
checksum = "sha256:fed6d7039f9379d446b9edebb7b9d6db79e5c336bc4a86bf854730ec6c7dd53a"
url = "https://github.com/pnpm/pnpm/releases/download/v11.27.0/pnpm-win32-x64.zip"
url_api = "https://api.github.com/repos/pnpm/pnpm/releases/assets/559064652"
provenance = "github-attestations"
[[tools.terragrunt]]

Some files were not shown because too many files have changed in this diff Show More