Compare commits
457 Commits
v1.130.2
...
refactor/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5d9bebd84 | ||
|
|
f16bdb2a01 | ||
|
|
adb17c4d58 | ||
|
|
56156b97e7 | ||
|
|
c411c1472a | ||
|
|
0bbe70e6a3 | ||
|
|
a65c905621 | ||
|
|
61d784f4e7 | ||
|
|
b63d6cdcd6 | ||
|
|
fa45a26cff | ||
|
|
8f045bc602 | ||
|
|
5353658114 | ||
|
|
21880aec14 | ||
|
|
48d746d9d5 | ||
|
|
8ab5040351 | ||
|
|
1219fd82a0 | ||
|
|
28d8357cc5 | ||
|
|
a9e7d0388b | ||
|
|
86d64f3483 | ||
|
|
c1150fe7e3 | ||
|
|
ecb66fdb2c | ||
|
|
c046651f23 | ||
|
|
6117329057 | ||
|
|
585997d46f | ||
|
|
7146ec99b1 | ||
|
|
b7b0b9b6d8 | ||
|
|
4935f3e0bb | ||
|
|
709a7b70aa | ||
|
|
6a4d21205f | ||
|
|
3a0ddfb92d | ||
|
|
cd03d0c0f2 | ||
|
|
117b263887 | ||
|
|
f357f3324f | ||
|
|
7d95bad5cb | ||
|
|
77b0505006 | ||
|
|
fac1beb7d8 | ||
|
|
3944f5d73b | ||
|
|
4445288758 | ||
|
|
4efc41d5d9 | ||
|
|
c9d45eee86 | ||
|
|
b3b774cfe5 | ||
|
|
15e894b9b5 | ||
|
|
ca06d0aa83 | ||
|
|
0cd51ae9c5 | ||
|
|
68f6111b77 | ||
|
|
668288ca20 | ||
|
|
3fdc1df89c | ||
|
|
989d9dbe51 | ||
|
|
48112d84a3 | ||
|
|
80dfe7a5e9 | ||
|
|
ce90a2ec1a | ||
|
|
dccbe0b3ed | ||
|
|
c0ad12f279 | ||
|
|
9c484b23a9 | ||
|
|
eed014482d | ||
|
|
d271e6a3ae | ||
|
|
60c43081ed | ||
|
|
81d959a27e | ||
|
|
bb775110ef | ||
|
|
7280331b76 | ||
|
|
93ee6ee0a5 | ||
|
|
7544a678ec | ||
|
|
3066c8198c | ||
|
|
eb8dfa283e | ||
|
|
41a127e2ab | ||
|
|
feb475561e | ||
|
|
4c4c67f0d2 | ||
|
|
381b66bf70 | ||
|
|
a89f3ad97c | ||
|
|
c473511133 | ||
|
|
0d66a6b51f | ||
|
|
66400b2e8e | ||
|
|
87cdf0ebd9 | ||
|
|
3f719bd8d7 | ||
|
|
55af925ab3 | ||
|
|
ff63b0fa8f | ||
|
|
f21fe8716c | ||
|
|
6a69dafd31 | ||
|
|
47b1938f17 | ||
|
|
2ffcfe06f3 | ||
|
|
89551edee5 | ||
|
|
cb6c541ae1 | ||
|
|
b1e1362246 | ||
|
|
ccc2b191dd | ||
|
|
bb7010b2bb | ||
|
|
8db666bc38 | ||
|
|
eace0f716d | ||
|
|
96743b6c33 | ||
|
|
ff181cf346 | ||
|
|
0cd5960007 | ||
|
|
698592c1b0 | ||
|
|
f75d853e9a | ||
|
|
3a1e3e82e7 | ||
|
|
0beb3ac4c1 | ||
|
|
894545aeed | ||
|
|
5250269fa4 | ||
|
|
a169fb6a79 | ||
|
|
09ced9a171 | ||
|
|
a6e5e4f625 | ||
|
|
bbd8de177b | ||
|
|
867f6e64f9 | ||
|
|
ec6379b0b2 | ||
|
|
2a80251dc3 | ||
|
|
d33ce13561 | ||
|
|
016d7a6ceb | ||
|
|
8ff25a4f7a | ||
|
|
61a3eba1bd | ||
|
|
7072e48cbe | ||
|
|
ece977d9ca | ||
|
|
2af8095880 | ||
|
|
30822fcd10 | ||
|
|
c578273e7a | ||
|
|
118a3fc9db | ||
|
|
1138f6dcce | ||
|
|
33f3751b72 | ||
|
|
b8509e6411 | ||
|
|
bd43edbcd7 | ||
|
|
c8b4a7e1f1 | ||
|
|
f34f83e164 | ||
|
|
df2cf5d106 | ||
|
|
52975eadb3 | ||
|
|
12610e4a9f | ||
|
|
2b3efa02d8 | ||
|
|
a21a997f21 | ||
|
|
7d61ed7ce4 | ||
|
|
8f7baf8336 | ||
|
|
44923acfd6 | ||
|
|
ab95881ebb | ||
|
|
8801ae5821 | ||
|
|
ea9f11bf39 | ||
|
|
62fc5b3c7d | ||
|
|
15d431ba6a | ||
|
|
5d21ba3166 | ||
|
|
da7a81b752 | ||
|
|
becdc3dcf5 | ||
|
|
84b51e3cbb | ||
|
|
b845184c80 | ||
|
|
1fde02ee1e | ||
|
|
526c02297c | ||
|
|
732b06eec8 | ||
|
|
436cff72b5 | ||
|
|
be5cc2cdf5 | ||
|
|
094a41ac9a | ||
|
|
ebad6a008f | ||
|
|
0c261ffbe2 | ||
|
|
6df6103c67 | ||
|
|
8c5116bc1d | ||
|
|
e3812a0e36 | ||
|
|
4b1ced439b | ||
|
|
2e8a286540 | ||
|
|
038a82c4f1 | ||
|
|
2c2dd01bf0 | ||
|
|
ac73e163df | ||
|
|
d89e88bb3f | ||
|
|
3ce353393a | ||
|
|
0e4cf9ac57 | ||
|
|
07290580a6 | ||
|
|
d9ce74b896 | ||
|
|
4c0f79b162 | ||
|
|
9851d24628 | ||
|
|
fe6cbd93b1 | ||
|
|
df20788088 | ||
|
|
3d042cc7f1 | ||
|
|
85446c5862 | ||
|
|
fb52ac0f5b | ||
|
|
48bcbee6ed | ||
|
|
f621f8ef2c | ||
|
|
7f69abbf0d | ||
|
|
895b2bf5cd | ||
|
|
f64e6f5dc3 | ||
|
|
64e738f79d | ||
|
|
a17390a422 | ||
|
|
1b5fc9c665 | ||
|
|
23717ce981 | ||
|
|
2fd05e8447 | ||
|
|
c664d99a34 | ||
|
|
21c7d70336 | ||
|
|
ad272333db | ||
|
|
460d594791 | ||
|
|
e6c575c33e | ||
|
|
85ac0512a6 | ||
|
|
205260d31c | ||
|
|
3858973be5 | ||
|
|
02994883fe | ||
|
|
a1f8150c30 | ||
|
|
d85ef19bfc | ||
|
|
d0014bdf94 | ||
|
|
e822e3eca9 | ||
|
|
644defa4a1 | ||
|
|
1fe3c7b9b3 | ||
|
|
0d60be3d87 | ||
|
|
765da7b182 | ||
|
|
b037158028 | ||
|
|
a03902f174 | ||
|
|
1d610ad9cb | ||
|
|
dab4870fed | ||
|
|
37f5e6e2cb | ||
|
|
57d622bc43 | ||
|
|
c167e46ec7 | ||
|
|
6ce8a1deeb | ||
|
|
f659ef4b7a | ||
|
|
bb6cdc99ab | ||
|
|
830b4dadcb | ||
|
|
d2f2f8d672 | ||
|
|
be1062474b | ||
|
|
64000d9d76 | ||
|
|
59fa8fbd0e | ||
|
|
19746a8685 | ||
|
|
987e5ab76c | ||
|
|
1b5e981a45 | ||
|
|
b7a0cf2470 | ||
|
|
13d6bd67b1 | ||
|
|
1de2eae12d | ||
|
|
bc5875ba8d | ||
|
|
0426b574fe | ||
|
|
2c3658e642 | ||
|
|
a493dab294 | ||
|
|
699fdd0d1b | ||
|
|
a774153f67 | ||
|
|
ca12aff3a4 | ||
|
|
550c1c0a10 | ||
|
|
92ac1193e6 | ||
|
|
2a95eccf6a | ||
|
|
ee017803bf | ||
|
|
0986a71ce3 | ||
|
|
af36eaa61b | ||
|
|
fda68f972f | ||
|
|
a8eec92da7 | ||
|
|
ad8511c978 | ||
|
|
fe8c5e8107 | ||
|
|
c70140e707 | ||
|
|
010b144754 | ||
|
|
b71039e83c | ||
|
|
56a4aa9ffe | ||
|
|
488dc4efbd | ||
|
|
f0ff8581da | ||
|
|
c49fd2065b | ||
|
|
21a6eb30ff | ||
|
|
9e063c993c | ||
|
|
dd1fcd5be5 | ||
|
|
52ae06c119 | ||
|
|
854ea13d6a | ||
|
|
504930947d | ||
|
|
0e6ac87645 | ||
|
|
bd2deda50c | ||
|
|
160bb492a2 | ||
|
|
6474a78b8b | ||
|
|
e275f2d8b3 | ||
|
|
81ed54aa61 | ||
|
|
067338b0ed | ||
|
|
5e68f8c519 | ||
|
|
242a559e0f | ||
|
|
ed2b54527c | ||
|
|
8b38f8a58d | ||
|
|
29b30570bf | ||
|
|
586a7a173b | ||
|
|
1bbfacfc09 | ||
|
|
85c2d36d99 | ||
|
|
8cefa0b84b | ||
|
|
f50e5d006c | ||
|
|
8f8ff3adc0 | ||
|
|
c4c35ed140 | ||
|
|
be2f670d35 | ||
|
|
7efcba2b12 | ||
|
|
459c815086 | ||
|
|
36fa61c013 | ||
|
|
8da5f21fcf | ||
|
|
76db8cf604 | ||
|
|
21becbf1b0 | ||
|
|
26f0ea4cb5 | ||
|
|
19e5a6f68f | ||
|
|
78f8e23834 | ||
|
|
5bceefce75 | ||
|
|
b710ad36f3 | ||
|
|
270d178a2e | ||
|
|
309528c807 | ||
|
|
7c422363fb | ||
|
|
3eb316abea | ||
|
|
b3371e16f2 | ||
|
|
b2c903c000 | ||
|
|
17e720440d | ||
|
|
a522130122 | ||
|
|
cecd9c24a4 | ||
|
|
f189c7b101 | ||
|
|
c5f087a3ca | ||
|
|
72f6d7791e | ||
|
|
f73fce1046 | ||
|
|
f2edcde1b2 | ||
|
|
9d0dd9dff8 | ||
|
|
c3d10c5be2 | ||
|
|
bd92748ddd | ||
|
|
aad5c3bada | ||
|
|
b2753103c6 | ||
|
|
e3f3baadb0 | ||
|
|
0b69d1c147 | ||
|
|
5a51ad3622 | ||
|
|
664c99278a | ||
|
|
184e142d87 | ||
|
|
8b00578c7b | ||
|
|
7562088fac | ||
|
|
79d4ce2d6d | ||
|
|
983f656a6b | ||
|
|
ab2a7006f9 | ||
|
|
1f18fe31f0 | ||
|
|
a373034629 | ||
|
|
5dac315af7 | ||
|
|
8309b73a02 | ||
|
|
e440cbe353 | ||
|
|
5548eb0dad | ||
|
|
3bec8dc337 | ||
|
|
5bcb58c3e7 | ||
|
|
c62fc155c8 | ||
|
|
40e3322b25 | ||
|
|
25f2b9602f | ||
|
|
ae6653392e | ||
|
|
d7a782da34 | ||
|
|
08b5952c87 | ||
|
|
584e5894bf | ||
|
|
52d4b2fe57 | ||
|
|
92f0973a46 | ||
|
|
75c83cb704 | ||
|
|
0b22d3348e | ||
|
|
abde0fbe60 | ||
|
|
eaa0e07329 | ||
|
|
9fd2c5220d | ||
|
|
7fcab4b251 | ||
|
|
e3995fb5f4 | ||
|
|
6d3f3d8616 | ||
|
|
4412680679 | ||
|
|
7df2c9c905 | ||
|
|
7a1e8ce6d8 | ||
|
|
8aea07b750 | ||
|
|
94dba29298 | ||
|
|
9e49783e49 | ||
|
|
43e3075f93 | ||
|
|
d03647904b | ||
|
|
206545356d | ||
|
|
3e372500b0 | ||
|
|
8943ec23ba | ||
|
|
04b03f2924 | ||
|
|
cf2c0260a6 | ||
|
|
ae8af84101 | ||
|
|
4794eeca88 | ||
|
|
ac65d46ec6 | ||
|
|
e5ca79dd44 | ||
|
|
49be6d7fd8 | ||
|
|
15c6506aee | ||
|
|
2c31a11e41 | ||
|
|
b6c5a03533 | ||
|
|
75bc32b47b | ||
|
|
fdbe6d649f | ||
|
|
2b131fe935 | ||
|
|
6ae24fbbd4 | ||
|
|
7f116d8e98 | ||
|
|
bd0840c411 | ||
|
|
a5123dec1a | ||
|
|
ffd18c5459 | ||
|
|
8242ff9bab | ||
|
|
8203b6c450 | ||
|
|
b352cf3336 | ||
|
|
96ed9a8c4a | ||
|
|
e7a5b96ed0 | ||
|
|
51c2c60231 | ||
|
|
43d585ce55 | ||
|
|
042da669d1 | ||
|
|
a724f147fe | ||
|
|
1e4b9ae5b7 | ||
|
|
99cddf1fd6 | ||
|
|
30d33f968f | ||
|
|
31ee19181a | ||
|
|
b58a450152 | ||
|
|
b87ba6865b | ||
|
|
565cceb323 | ||
|
|
f096dd0cc0 | ||
|
|
a3c3f9cfcb | ||
|
|
7b6a4be30c | ||
|
|
720189e2c2 | ||
|
|
dfab32c8f2 | ||
|
|
60174d662d | ||
|
|
8b6a765e12 | ||
|
|
2248a38567 | ||
|
|
97e52c5156 | ||
|
|
e8b4ac0522 | ||
|
|
548298b0c7 | ||
|
|
40cff2893c | ||
|
|
b621281351 | ||
|
|
4336afd6bf | ||
|
|
5a456ef277 | ||
|
|
5cb5fcbf62 | ||
|
|
95e3b15776 | ||
|
|
50335dc363 | ||
|
|
6e62c09d84 | ||
|
|
00d3b8d83a | ||
|
|
d911b76c08 | ||
|
|
502854cee1 | ||
|
|
59e5c82569 | ||
|
|
e4b0c00885 | ||
|
|
946507231d | ||
|
|
20ba800a50 | ||
|
|
f434e858ed | ||
|
|
3e03c47fbf | ||
|
|
9aa3850769 | ||
|
|
628dcdeebf | ||
|
|
11bfde2aa8 | ||
|
|
69b1ac47ea | ||
|
|
4f81265694 | ||
|
|
3428a876c7 | ||
|
|
bd822657d3 | ||
|
|
9e7744a9ab | ||
|
|
7729fe80fa | ||
|
|
68e24ad168 | ||
|
|
186c573565 | ||
|
|
5b63b9fc8b | ||
|
|
5c80e8734b | ||
|
|
a5093a9434 | ||
|
|
637ad1fdcb | ||
|
|
6789c2ac19 | ||
|
|
838a8dd9a6 | ||
|
|
d71c5602c3 | ||
|
|
8c50e3e80e | ||
|
|
efcb1129ce | ||
|
|
faabda4446 | ||
|
|
b8b2898c87 | ||
|
|
b25914c2a5 | ||
|
|
d613f15606 | ||
|
|
a831876fdc | ||
|
|
09f4476f97 | ||
|
|
238c151ac3 | ||
|
|
e4f83680d9 | ||
|
|
74f7fd4b53 | ||
|
|
f4dbfd856e | ||
|
|
55a3c30664 | ||
|
|
6fa0cb534a | ||
|
|
9f0dbfc150 | ||
|
|
6419ac74af | ||
|
|
d2bcf5d716 | ||
|
|
c8331f111f | ||
|
|
4b4bcd23f4 | ||
|
|
3fde5a8328 | ||
|
|
cc3ea32cd2 | ||
|
|
431cf281da | ||
|
|
8f786fd7dd | ||
|
|
3e73765375 | ||
|
|
411521b21d | ||
|
|
e163808348 | ||
|
|
411772123f | ||
|
|
84c35e35d6 | ||
|
|
f7d730eb05 | ||
|
|
16e0166d22 | ||
|
|
43f8f473e9 | ||
|
|
cc393b2b7b | ||
|
|
6341962de4 | ||
|
|
c26b28f6a4 | ||
|
|
c72c82c401 | ||
|
|
fecf3809a6 | ||
|
|
619bd72de9 | ||
|
|
fd4a5f71b5 |
@@ -1,4 +1,4 @@
|
||||
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:2ef23730ec68d8511ec8e6e0b82550ca728b256805d81f60ed890f3bfb21cfb9
|
||||
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:a20b8a3538313487ac9266875bbf733e544c1aa2091df2bb99ab592a6d4f7399
|
||||
FROM ${BASEIMAGE}
|
||||
|
||||
# Flutter SDK
|
||||
|
||||
3
.gitattributes
vendored
@@ -6,6 +6,9 @@ mobile/openapi/**/*.dart linguist-generated=true
|
||||
mobile/lib/**/*.g.dart -diff -merge
|
||||
mobile/lib/**/*.g.dart linguist-generated=true
|
||||
|
||||
mobile/lib/**/*.drift.dart -diff -merge
|
||||
mobile/lib/**/*.drift.dart linguist-generated=true
|
||||
|
||||
open-api/typescript-sdk/fetch-client.ts -diff -merge
|
||||
open-api/typescript-sdk/fetch-client.ts linguist-generated=true
|
||||
|
||||
|
||||
2
.github/.nvmrc
vendored
@@ -1 +1 @@
|
||||
22.14.0
|
||||
22.15.0
|
||||
|
||||
118
.github/actions/image-build/action.yml
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
name: 'Single arch image build'
|
||||
description: 'Build single-arch image on platform appropriate runner'
|
||||
inputs:
|
||||
image:
|
||||
description: 'Name of the image to build'
|
||||
required: true
|
||||
ghcr-token:
|
||||
description: 'GitHub Container Registry token'
|
||||
required: true
|
||||
platform:
|
||||
description: 'Platform to build for'
|
||||
required: true
|
||||
artifact-key-base:
|
||||
description: 'Base key for artifact name'
|
||||
required: true
|
||||
context:
|
||||
description: 'Path to build context'
|
||||
required: true
|
||||
dockerfile:
|
||||
description: 'Path to Dockerfile'
|
||||
required: true
|
||||
build-args:
|
||||
description: 'Docker build arguments'
|
||||
required: false
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Prepare
|
||||
id: prepare
|
||||
shell: bash
|
||||
env:
|
||||
PLATFORM: ${{ inputs.platform }}
|
||||
run: |
|
||||
echo "platform-pair=${PLATFORM//\//-}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ inputs.ghcr-token }}
|
||||
|
||||
- name: Generate cache key suffix
|
||||
id: cache-key-suffix
|
||||
shell: bash
|
||||
env:
|
||||
REF: ${{ github.ref_name }}
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo "cache-key-suffix=pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g')
|
||||
echo "suffix=${SUFFIX}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Generate cache target
|
||||
id: cache-target
|
||||
shell: bash
|
||||
env:
|
||||
BUILD_ARGS: ${{ inputs.build-args }}
|
||||
IMAGE: ${{ inputs.image }}
|
||||
SUFFIX: ${{ steps.cache-key-suffix.outputs.suffix }}
|
||||
PLATFORM_PAIR: ${{ steps.prepare.outputs.platform-pair }}
|
||||
run: |
|
||||
HASH=$(sha256sum <<< "${BUILD_ARGS}" | cut -d' ' -f1)
|
||||
CACHE_KEY="${PLATFORM_PAIR}-${HASH}"
|
||||
echo "cache-key-base=${CACHE_KEY}" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "cache-to=type=registry,ref=${IMAGE}-build-cache:${CACHE_KEY}-${SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
env:
|
||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||
|
||||
- name: Build and push image
|
||||
id: build
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
with:
|
||||
context: ${{ inputs.context }}
|
||||
file: ${{ inputs.dockerfile }}
|
||||
platforms: ${{ inputs.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||
cache-from: |
|
||||
type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-${{ steps.cache-key-suffix.outputs.suffix }}
|
||||
type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-main
|
||||
outputs: type=image,"name=${{ inputs.image }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
||||
build-args: |
|
||||
BUILD_ID=${{ github.run_id }}
|
||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.meta.outputs.tags }}
|
||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||
${{ inputs.build-args }}
|
||||
|
||||
- name: Export digest
|
||||
shell: bash
|
||||
run: | # zizmor: ignore[template-injection]
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: ${{ inputs.artifact-key-base }}-${{ steps.cache-target.outputs.cache-key-base }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
45
.github/workflows/build-mobile.yml
vendored
@@ -7,6 +7,15 @@ on:
|
||||
ref:
|
||||
required: false
|
||||
type: string
|
||||
secrets:
|
||||
KEY_JKS:
|
||||
required: true
|
||||
ALIAS:
|
||||
required: true
|
||||
ANDROID_KEY_PASSWORD:
|
||||
required: true
|
||||
ANDROID_STORE_PASSWORD:
|
||||
required: true
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
@@ -15,16 +24,23 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pre-job:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- id: found_paths
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
with:
|
||||
filters: |
|
||||
mobile:
|
||||
@@ -38,31 +54,26 @@ jobs:
|
||||
build-sign-android:
|
||||
name: Build and sign Android
|
||||
needs: pre-job
|
||||
permissions:
|
||||
contents: read
|
||||
# Skip when PR from a fork
|
||||
if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && needs.pre-job.outputs.should_run == 'true' }}
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- name: Determine ref
|
||||
id: get-ref
|
||||
run: |
|
||||
input_ref="${{ inputs.ref }}"
|
||||
github_ref="${{ github.sha }}"
|
||||
ref="${input_ref:-$github_ref}"
|
||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ steps.get-ref.outputs.ref }}
|
||||
ref: ${{ inputs.ref || github.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
|
||||
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Setup Flutter SDK
|
||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version-file: ./mobile/pubspec.yaml
|
||||
@@ -78,6 +89,10 @@ jobs:
|
||||
working-directory: ./mobile
|
||||
run: flutter pub get
|
||||
|
||||
- name: Generate translation file
|
||||
run: make translation
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Build Android App Bundle
|
||||
working-directory: ./mobile
|
||||
env:
|
||||
@@ -89,7 +104,7 @@ jobs:
|
||||
flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
|
||||
|
||||
- name: Publish Android Artifact
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: release-apk-signed
|
||||
path: mobile/build/app/outputs/flutter-apk/*.apk
|
||||
|
||||
19
.github/workflows/cache-cleanup.yml
vendored
@@ -8,31 +8,38 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Cleanup
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REF: ${{ github.ref }}
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
REPO=${{ github.repository }}
|
||||
BRANCH=${{ github.ref }}
|
||||
|
||||
echo "Fetching list of cache keys"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B ${REF} -L 100 | cut -f 1 )
|
||||
|
||||
## Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
gh actions-cache delete $cacheKey -R "$REPO" -B "${REF}" --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
25
.github/workflows/cli.yml
vendored
@@ -16,21 +16,25 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: CLI Publish
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./cli
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Setup .npmrc file to publish to npm
|
||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './cli/.nvmrc'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
@@ -48,11 +52,16 @@ jobs:
|
||||
docker:
|
||||
name: Docker
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
needs: publish
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
@@ -61,7 +70,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -76,7 +85,7 @@ jobs:
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: metadata
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
flavor: |
|
||||
latest=false
|
||||
@@ -87,7 +96,7 @@ jobs:
|
||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
||||
with:
|
||||
file: cli/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
12
.github/workflows/codeql-analysis.yml
vendored
@@ -24,6 +24,8 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
@@ -42,11 +44,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3
|
||||
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -59,7 +63,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@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3
|
||||
uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||
|
||||
# ℹ️ 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
|
||||
@@ -72,6 +76,6 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3
|
||||
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||
with:
|
||||
category: '/language:${{matrix.language}}'
|
||||
|
||||
440
.github/workflows/docker.yml
vendored
@@ -12,20 +12,23 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pre-job:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||
should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- id: found_paths
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
with:
|
||||
filters: |
|
||||
server:
|
||||
@@ -37,6 +40,8 @@ jobs:
|
||||
- 'machine-learning/**'
|
||||
workflow:
|
||||
- '.github/workflows/docker.yml'
|
||||
- '.github/workflows/multi-runner-build.yml'
|
||||
- '.github/actions/image-build'
|
||||
|
||||
- name: Check if we should force jobs to run
|
||||
id: should_force
|
||||
@@ -45,6 +50,9 @@ jobs:
|
||||
retag_ml:
|
||||
name: Re-Tag ML
|
||||
needs: pre-job
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'false' && !github.event.pull_request.head.repo.fork }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
@@ -52,24 +60,28 @@ jobs:
|
||||
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Re-tag image
|
||||
env:
|
||||
REGISTRY_NAME: 'ghcr.io'
|
||||
REPOSITORY: ${{ github.repository_owner }}/immich-machine-learning
|
||||
TAG_OLD: main${{ matrix.suffix }}
|
||||
TAG_PR: ${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||
TAG_COMMIT: commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
||||
run: |
|
||||
REGISTRY_NAME="ghcr.io"
|
||||
REPOSITORY=${{ github.repository_owner }}/immich-machine-learning
|
||||
TAG_OLD=main${{ matrix.suffix }}
|
||||
TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||
TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||
|
||||
retag_server:
|
||||
name: Re-Tag Server
|
||||
needs: pre-job
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'false' && !github.event.pull_request.head.repo.fork }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
@@ -77,376 +89,91 @@ jobs:
|
||||
suffix: ['']
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Re-tag image
|
||||
env:
|
||||
REGISTRY_NAME: 'ghcr.io'
|
||||
REPOSITORY: ${{ github.repository_owner }}/immich-server
|
||||
TAG_OLD: main${{ matrix.suffix }}
|
||||
TAG_PR: ${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||
TAG_COMMIT: commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
||||
run: |
|
||||
REGISTRY_NAME="ghcr.io"
|
||||
REPOSITORY=${{ github.repository_owner }}/immich-server
|
||||
TAG_OLD=main${{ matrix.suffix }}
|
||||
TAG_PR=${{ github.event.number == 0 && github.ref_name || format('pr-{0}', github.event.number) }}${{ matrix.suffix }}
|
||||
TAG_COMMIT=commit-${{ github.event_name != 'pull_request' && github.sha || github.event.pull_request.head.sha }}${{ matrix.suffix }}
|
||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_PR $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||
docker buildx imagetools create -t $REGISTRY_NAME/$REPOSITORY:$TAG_COMMIT $REGISTRY_NAME/$REPOSITORY:$TAG_OLD
|
||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||
|
||||
build_and_push_ml:
|
||||
machine-learning:
|
||||
name: Build and Push ML
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
env:
|
||||
image: immich-machine-learning
|
||||
context: machine-learning
|
||||
file: machine-learning/Dockerfile
|
||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
||||
strategy:
|
||||
# Prevent a failure in one image from stopping the other builds
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
device: cpu
|
||||
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
device: cpu
|
||||
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
device: cuda
|
||||
suffix: -cuda
|
||||
|
||||
- platform: linux/amd64
|
||||
runner: mich
|
||||
device: rocm
|
||||
suffix: -rocm
|
||||
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
device: openvino
|
||||
suffix: -openvino
|
||||
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
device: armnn
|
||||
suffix: -armnn
|
||||
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
device: rknn
|
||||
suffix: -rknn
|
||||
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Generate cache key suffix
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Generate cache target
|
||||
id: cache-target
|
||||
run: |
|
||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build and push image
|
||||
id: build
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
with:
|
||||
context: ${{ env.context }}
|
||||
file: ${{ env.file }}
|
||||
platforms: ${{ matrix.platforms }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||
cache-from: |
|
||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }}
|
||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-main
|
||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
||||
build-args: |
|
||||
DEVICE=${{ matrix.device }}
|
||||
BUILD_ID=${{ github.run_id }}
|
||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: ml-digests-${{ matrix.device }}-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge_ml:
|
||||
name: Merge & Push ML
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' && !github.event.pull_request.head.repo.fork }}
|
||||
env:
|
||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
||||
DOCKER_REPO: altran1502/immich-machine-learning
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- device: cpu
|
||||
tag-suffix: ''
|
||||
- device: cuda
|
||||
suffix: -cuda
|
||||
- device: rocm
|
||||
suffix: -rocm
|
||||
tag-suffix: '-cuda'
|
||||
platforms: linux/amd64
|
||||
- device: openvino
|
||||
suffix: -openvino
|
||||
tag-suffix: '-openvino'
|
||||
platforms: linux/amd64
|
||||
- device: armnn
|
||||
suffix: -armnn
|
||||
tag-suffix: '-armnn'
|
||||
platforms: linux/arm64
|
||||
- device: rknn
|
||||
suffix: -rknn
|
||||
needs:
|
||||
- build_and_push_ml
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: ml-digests-${{ matrix.device }}-*
|
||||
merge-multiple: true
|
||||
tag-suffix: '-rknn'
|
||||
platforms: linux/arm64
|
||||
- device: rocm
|
||||
tag-suffix: '-rocm'
|
||||
platforms: linux/amd64
|
||||
runner-mapping: '{"linux/amd64": "mich"}'
|
||||
uses: ./.github/workflows/multi-runner-build.yml
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
packages: write
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
with:
|
||||
image: immich-machine-learning
|
||||
context: machine-learning
|
||||
dockerfile: machine-learning/Dockerfile
|
||||
platforms: ${{ matrix.platforms }}
|
||||
runner-mapping: ${{ matrix.runner-mapping }}
|
||||
tag-suffix: ${{ matrix.tag-suffix }}
|
||||
dockerhub-push: ${{ github.event_name == 'release' }}
|
||||
build-args: |
|
||||
DEVICE=${{ matrix.device }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
env:
|
||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||
with:
|
||||
flavor: |
|
||||
# Disable latest tag
|
||||
latest=false
|
||||
suffix=${{ matrix.suffix }}
|
||||
images: |
|
||||
name=${{ env.GHCR_REPO }}
|
||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
# Tag with branch name
|
||||
type=ref,event=branch
|
||||
# Tag with pr-number
|
||||
type=ref,event=pr
|
||||
# Tag with long commit sha hash
|
||||
type=sha,format=long,prefix=commit-
|
||||
# Tag with git tag on release
|
||||
type=ref,event=tag
|
||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
||||
|
||||
build_and_push_server:
|
||||
server:
|
||||
name: Build and Push Server
|
||||
runs-on: ${{ matrix.runner }}
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||
env:
|
||||
uses: ./.github/workflows/multi-runner-build.yml
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
packages: write
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
with:
|
||||
image: immich-server
|
||||
context: .
|
||||
file: server/Dockerfile
|
||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Generate cache key suffix
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "CACHE_KEY_SUFFIX=$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Generate cache target
|
||||
id: cache-target
|
||||
run: |
|
||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "cache-to=type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build and push image
|
||||
id: build
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
with:
|
||||
context: ${{ env.context }}
|
||||
file: ${{ env.file }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||
cache-from: |
|
||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ env.CACHE_KEY_SUFFIX }}
|
||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-main
|
||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
||||
build-args: |
|
||||
DEVICE=cpu
|
||||
BUILD_ID=${{ github.run_id }}
|
||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: server-digests-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge_server:
|
||||
name: Merge & Push Server
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' && !github.event.pull_request.head.repo.fork }}
|
||||
env:
|
||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
||||
DOCKER_REPO: altran1502/immich-server
|
||||
needs:
|
||||
- build_and_push_server
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: server-digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
env:
|
||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||
with:
|
||||
flavor: |
|
||||
# Disable latest tag
|
||||
latest=false
|
||||
suffix=${{ matrix.suffix }}
|
||||
images: |
|
||||
name=${{ env.GHCR_REPO }}
|
||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
# Tag with branch name
|
||||
type=ref,event=branch
|
||||
# Tag with pr-number
|
||||
type=ref,event=pr
|
||||
# Tag with long commit sha hash
|
||||
type=sha,format=long,prefix=commit-
|
||||
# Tag with git tag on release
|
||||
type=ref,event=tag
|
||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
||||
dockerfile: server/Dockerfile
|
||||
dockerhub-push: ${{ github.event_name == 'release' }}
|
||||
build-args: |
|
||||
DEVICE=cpu
|
||||
|
||||
success-check-server:
|
||||
name: Docker Build & Push Server Success
|
||||
needs: [merge_server, retag_server]
|
||||
needs: [server, retag_server]
|
||||
permissions: {}
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
@@ -455,11 +182,13 @@ jobs:
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
# zizmor: ignore[template-injection]
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
|
||||
success-check-ml:
|
||||
name: Docker Build & Push ML Success
|
||||
needs: [merge_ml, retag_ml]
|
||||
needs: [machine-learning, retag_ml]
|
||||
permissions: {}
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
@@ -468,4 +197,5 @@ jobs:
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
# zizmor: ignore[template-injection]
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
|
||||
21
.github/workflows/docs-build.yml
vendored
@@ -10,16 +10,22 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pre-job:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- id: found_paths
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
with:
|
||||
filters: |
|
||||
docs:
|
||||
@@ -33,6 +39,8 @@ jobs:
|
||||
build:
|
||||
name: Docs Build
|
||||
needs: pre-job
|
||||
permissions:
|
||||
contents: read
|
||||
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
@@ -41,10 +49,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './docs/.nvmrc'
|
||||
|
||||
@@ -58,8 +68,9 @@ jobs:
|
||||
run: npm run build
|
||||
|
||||
- name: Upload build output
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: docs-build-output
|
||||
path: docs/build/
|
||||
include-hidden-files: true
|
||||
retention-days: 1
|
||||
|
||||
55
.github/workflows/docs-deploy.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Docs deploy
|
||||
on:
|
||||
workflow_run:
|
||||
workflow_run: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
||||
workflows: ['Docs build']
|
||||
types:
|
||||
- completed
|
||||
@@ -9,6 +9,9 @@ jobs:
|
||||
checks:
|
||||
name: Docs Deploy Checks
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
pull-requests: read
|
||||
outputs:
|
||||
parameters: ${{ steps.parameters.outputs.result }}
|
||||
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||
@@ -17,7 +20,7 @@ jobs:
|
||||
run: echo 'The triggering workflow did not succeed' && exit 1
|
||||
- name: Get artifact
|
||||
id: get-artifact
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
script: |
|
||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
@@ -35,7 +38,9 @@ jobs:
|
||||
return { found: true, id: matchArtifact.id };
|
||||
- name: Determine deploy parameters
|
||||
id: parameters
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
env:
|
||||
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||
with:
|
||||
script: |
|
||||
const eventType = context.payload.workflow_run.event;
|
||||
@@ -57,7 +62,8 @@ jobs:
|
||||
} else if (eventType == "pull_request") {
|
||||
let pull_number = context.payload.workflow_run.pull_requests[0]?.number;
|
||||
if(!pull_number) {
|
||||
const response = await github.rest.search.issuesAndPullRequests({q: 'repo:${{ github.repository }} is:pr sha:${{ github.event.workflow_run.head_sha }}',per_page: 1,})
|
||||
const {HEAD_SHA} = process.env;
|
||||
const response = await github.rest.search.issuesAndPullRequests({q: `repo:${{ github.repository }} is:pr sha:${HEAD_SHA}`,per_page: 1,})
|
||||
const items = response.data.items
|
||||
if (items.length < 1) {
|
||||
throw new Error("No pull request found for the commit")
|
||||
@@ -95,30 +101,36 @@ jobs:
|
||||
name: Docs Deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs: checks
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
pull-requests: write
|
||||
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Load parameters
|
||||
id: parameters
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
env:
|
||||
PARAM_JSON: ${{ needs.checks.outputs.parameters }}
|
||||
with:
|
||||
script: |
|
||||
const json = `${{ needs.checks.outputs.parameters }}`;
|
||||
const parameters = JSON.parse(json);
|
||||
const parameters = JSON.parse(process.env.PARAM_JSON);
|
||||
core.setOutput("event", parameters.event);
|
||||
core.setOutput("name", parameters.name);
|
||||
core.setOutput("shouldDeploy", parameters.shouldDeploy);
|
||||
|
||||
- run: |
|
||||
echo "Starting docs deployment for ${{ steps.parameters.outputs.event }} ${{ steps.parameters.outputs.name }}"
|
||||
|
||||
- name: Download artifact
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
env:
|
||||
ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }}
|
||||
with:
|
||||
script: |
|
||||
let artifact = ${{ needs.checks.outputs.artifact }};
|
||||
let artifact = JSON.parse(process.env.ARTIFACT_JSON);
|
||||
let download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -138,7 +150,7 @@ jobs:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2.1.5
|
||||
with:
|
||||
tg_version: '0.58.12'
|
||||
tofu_version: '1.7.1'
|
||||
@@ -153,7 +165,7 @@ jobs:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2.1.5
|
||||
with:
|
||||
tg_version: '0.58.12'
|
||||
tofu_version: '1.7.1'
|
||||
@@ -162,12 +174,15 @@ jobs:
|
||||
|
||||
- name: Output Cleaning
|
||||
id: clean
|
||||
env:
|
||||
TG_OUTPUT: ${{ steps.docs-output.outputs.tg_action_output }}
|
||||
run: |
|
||||
TG_OUT=$(echo '${{ steps.docs-output.outputs.tg_action_output }}' | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .)
|
||||
echo "output=$TG_OUT" >> $GITHUB_OUTPUT
|
||||
CLEANED=$(echo "$TG_OUTPUT" | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .)
|
||||
echo "output=$CLEANED" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish to Cloudflare Pages
|
||||
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1
|
||||
# TODO: Action is deprecated
|
||||
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1.5.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
@@ -184,7 +199,7 @@ jobs:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2.1.5
|
||||
with:
|
||||
tg_version: '0.58.12'
|
||||
tofu_version: '1.7.1'
|
||||
@@ -192,7 +207,7 @@ jobs:
|
||||
tg_command: 'apply'
|
||||
|
||||
- name: Comment
|
||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3
|
||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
|
||||
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
||||
with:
|
||||
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
||||
|
||||
15
.github/workflows/docs-destroy.yml
vendored
@@ -1,15 +1,22 @@
|
||||
name: Docs destroy
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
||||
types: [closed]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Docs Destroy
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Destroy Docs Subdomain
|
||||
env:
|
||||
@@ -18,7 +25,7 @@ jobs:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
|
||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2
|
||||
uses: gruntwork-io/terragrunt-action@9559e51d05873b0ea467c42bbabcb5c067642ccc # v2.1.5
|
||||
with:
|
||||
tg_version: '0.58.12'
|
||||
tofu_version: '1.7.1'
|
||||
@@ -26,7 +33,7 @@ jobs:
|
||||
tg_command: 'destroy -refresh=false'
|
||||
|
||||
- name: Comment
|
||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3
|
||||
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
|
||||
with:
|
||||
number: ${{ github.event.number }}
|
||||
delete: true
|
||||
|
||||
14
.github/workflows/fix-format.yml
vendored
@@ -4,28 +4,32 @@ on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
fix-formatting:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.label.name == 'fix:formatting' }}
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1
|
||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
persist-credentials: true
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
|
||||
@@ -33,13 +37,13 @@ jobs:
|
||||
run: make install-all && make format-all
|
||||
|
||||
- name: Commit and push
|
||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9
|
||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||
with:
|
||||
default_author: github_actions
|
||||
message: 'chore: fix formatting'
|
||||
|
||||
- name: Remove label
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
if: always()
|
||||
with:
|
||||
script: |
|
||||
|
||||
185
.github/workflows/multi-runner-build.yml
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
name: 'Multi-runner container image build'
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image:
|
||||
description: 'Name of the image'
|
||||
type: string
|
||||
required: true
|
||||
context:
|
||||
description: 'Path to build context'
|
||||
type: string
|
||||
required: true
|
||||
dockerfile:
|
||||
description: 'Path to Dockerfile'
|
||||
type: string
|
||||
required: true
|
||||
tag-suffix:
|
||||
description: 'Suffix to append to the image tag'
|
||||
type: string
|
||||
default: ''
|
||||
dockerhub-push:
|
||||
description: 'Push to Docker Hub'
|
||||
type: boolean
|
||||
default: false
|
||||
build-args:
|
||||
description: 'Docker build arguments'
|
||||
type: string
|
||||
required: false
|
||||
platforms:
|
||||
description: 'Platforms to build for'
|
||||
type: string
|
||||
runner-mapping:
|
||||
description: 'Mapping from platforms to runners'
|
||||
type: string
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME:
|
||||
required: false
|
||||
DOCKERHUB_TOKEN:
|
||||
required: false
|
||||
|
||||
env:
|
||||
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ inputs.image }}
|
||||
DOCKERHUB_IMAGE: altran1502/${{ inputs.image }}
|
||||
|
||||
jobs:
|
||||
matrix:
|
||||
name: 'Generate matrix'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.matrix.outputs.matrix }}
|
||||
key: ${{ steps.artifact-key.outputs.base }}
|
||||
steps:
|
||||
- name: Generate build matrix
|
||||
id: matrix
|
||||
shell: bash
|
||||
env:
|
||||
PLATFORMS: ${{ inputs.platforms || 'linux/amd64,linux/arm64' }}
|
||||
RUNNER_MAPPING: ${{ inputs.runner-mapping || '{"linux/amd64":"ubuntu-latest","linux/arm64":"ubuntu-24.04-arm"}' }}
|
||||
run: |
|
||||
matrix=$(jq -R -c \
|
||||
--argjson runner_mapping "${RUNNER_MAPPING}" \
|
||||
'split(",") | map({platform: ., runner: $runner_mapping[.]})' \
|
||||
<<< "${PLATFORMS}")
|
||||
echo "${matrix}"
|
||||
echo "matrix=${matrix}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Determine artifact key
|
||||
id: artifact-key
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE: ${{ inputs.image }}
|
||||
SUFFIX: ${{ inputs.tag-suffix }}
|
||||
run: |
|
||||
if [[ -n "${SUFFIX}" ]]; then
|
||||
base="${IMAGE}${SUFFIX}-digests"
|
||||
else
|
||||
base="${IMAGE}-digests"
|
||||
fi
|
||||
echo "${base}"
|
||||
echo "base=${base}" >> $GITHUB_OUTPUT
|
||||
|
||||
build:
|
||||
needs: matrix
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.matrix.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: ./.github/actions/image-build
|
||||
with:
|
||||
context: ${{ inputs.context }}
|
||||
dockerfile: ${{ inputs.dockerfile }}
|
||||
image: ${{ env.GHCR_IMAGE }}
|
||||
ghcr-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
platform: ${{ matrix.platform }}
|
||||
artifact-key-base: ${{ needs.matrix.outputs.key }}
|
||||
build-args: ${{ inputs.build-args }}
|
||||
|
||||
merge:
|
||||
needs: [matrix, build]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: ${{ needs.matrix.outputs.key }}-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ inputs.dockerhub-push }}
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
env:
|
||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||
with:
|
||||
flavor: |
|
||||
# Disable latest tag
|
||||
latest=false
|
||||
suffix=${{ inputs.tag-suffix }}
|
||||
images: |
|
||||
name=${{ env.GHCR_IMAGE }}
|
||||
name=${{ env.DOCKERHUB_IMAGE }},enable=${{ inputs.dockerhub-push }}
|
||||
tags: |
|
||||
# Tag with branch name
|
||||
type=ref,event=branch
|
||||
# Tag with pr-number
|
||||
type=ref,event=pr
|
||||
# Tag with long commit sha hash
|
||||
type=sha,format=long,prefix=commit-
|
||||
# Tag with git tag on release
|
||||
type=ref,event=tag
|
||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
# Process annotations
|
||||
declare -a ANNOTATIONS=()
|
||||
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
||||
while IFS= read -r annotation; do
|
||||
# Extract key and value by removing the manifest: prefix
|
||||
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
||||
key="${BASH_REMATCH[1]}"
|
||||
value="${BASH_REMATCH[2]}"
|
||||
# Use array to properly handle arguments with spaces
|
||||
ANNOTATIONS+=(--annotation "index:$key=$value")
|
||||
fi
|
||||
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
||||
fi
|
||||
|
||||
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
SOURCE_ARGS=$(printf "${GHCR_IMAGE}@sha256:%s " *)
|
||||
|
||||
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
||||
6
.github/workflows/pr-label-validation.yml
vendored
@@ -1,9 +1,11 @@
|
||||
name: PR Label Validation
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
||||
types: [opened, labeled, unlabeled, synchronize]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
validate-release-label:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -12,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Require PR to have a changelog label
|
||||
uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5
|
||||
uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5.5.0
|
||||
with:
|
||||
mode: exactly
|
||||
count: 1
|
||||
|
||||
6
.github/workflows/pr-labeler.yml
vendored
@@ -1,6 +1,8 @@
|
||||
name: 'Pull Request Labeler'
|
||||
on:
|
||||
- pull_request_target
|
||||
- pull_request_target # zizmor: ignore[dangerous-triggers] no attacker inputs are used here
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
@@ -9,4 +11,4 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
|
||||
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
|
||||
|
||||
@@ -4,9 +4,13 @@ on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
validate-pr-title:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: PR Conventional Commit Validation
|
||||
uses: ytanikin/PRConventionalCommits@b628c5a234cc32513014b7bfdd1e47b532124d98 # 1.3.0
|
||||
|
||||
41
.github/workflows/prepare-release.yml
vendored
@@ -21,35 +21,40 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-root
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
bump_version:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
|
||||
|
||||
permissions: {} # No job-level permissions are needed because it uses the app-token
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1
|
||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
persist-credentials: true
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@22695119d769bdb6f7032ad67b9bca0ef8c4a174 # v5
|
||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
|
||||
- name: Bump version
|
||||
run: misc/release/pump-version.sh -s "${{ inputs.serverBump }}" -m "${{ inputs.mobileBump }}"
|
||||
env:
|
||||
SERVER_BUMP: ${{ inputs.serverBump }}
|
||||
MOBILE_BUMP: ${{ inputs.mobileBump }}
|
||||
run: misc/release/pump-version.sh -s "${SERVER_BUMP}" -m "${MOBILE_BUMP}"
|
||||
|
||||
- name: Commit and tag
|
||||
id: push-tag
|
||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9
|
||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||
with:
|
||||
default_author: github_actions
|
||||
message: 'chore: version ${{ env.IMMICH_VERSION }}'
|
||||
@@ -59,37 +64,47 @@ jobs:
|
||||
build_mobile:
|
||||
uses: ./.github/workflows/build-mobile.yml
|
||||
needs: bump_version
|
||||
secrets: inherit
|
||||
permissions:
|
||||
contents: read
|
||||
secrets:
|
||||
KEY_JKS: ${{ secrets.KEY_JKS }}
|
||||
ALIAS: ${{ secrets.ALIAS }}
|
||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
||||
with:
|
||||
ref: ${{ needs.bump_version.outputs.ref }}
|
||||
|
||||
prepare_release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build_mobile
|
||||
|
||||
permissions:
|
||||
actions: read # To download the app artifact
|
||||
# No content permissions are needed because it uses the app-token
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1
|
||||
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
|
||||
with:
|
||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download APK
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: release-apk-signed
|
||||
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
|
||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
||||
with:
|
||||
draft: true
|
||||
tag_name: ${{ env.IMMICH_VERSION }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
generate_release_notes: true
|
||||
body_path: misc/release/notes.tmpl
|
||||
files: |
|
||||
|
||||
6
.github/workflows/preview-label.yaml
vendored
@@ -4,6 +4,8 @@ on:
|
||||
pull_request:
|
||||
types: [labeled, closed]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
comment-status:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -11,7 +13,7 @@ jobs:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2
|
||||
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
|
||||
with:
|
||||
message-id: 'preview-status'
|
||||
message: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.cloud/'
|
||||
@@ -22,7 +24,7 @@ jobs:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.removeLabel({
|
||||
|
||||
12
.github/workflows/sdk.yml
vendored
@@ -4,20 +4,24 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish `@immich/sdk`
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./open-api/typescript-sdk
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Setup .npmrc file to publish to npm
|
||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
57
.github/workflows/static_analysis.yml
vendored
@@ -9,16 +9,22 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pre-job:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- id: found_paths
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
with:
|
||||
filters: |
|
||||
mobile:
|
||||
@@ -33,15 +39,17 @@ jobs:
|
||||
name: Run Dart Code Analysis
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Flutter SDK
|
||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version-file: ./mobile/pubspec.yaml
|
||||
@@ -50,12 +58,16 @@ jobs:
|
||||
run: dart pub get
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Generate translation file
|
||||
run: make translation; dart format lib/generated/codegen_loader.g.dart
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Run Build Runner
|
||||
run: make build
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||
id: verify-changed-files
|
||||
with:
|
||||
files: |
|
||||
@@ -65,9 +77,11 @@ jobs:
|
||||
|
||||
- name: Verify files have not changed
|
||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
||||
run: |
|
||||
echo "ERROR: Generated files not up to date! Run make_build inside the mobile directory"
|
||||
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||
echo "Changed files: ${CHANGED_FILES}"
|
||||
exit 1
|
||||
|
||||
- name: Run dart analyze
|
||||
@@ -81,3 +95,30 @@ jobs:
|
||||
- name: Run dart custom_lint
|
||||
run: dart run custom_lint
|
||||
working-directory: ./mobile
|
||||
|
||||
zizmor:
|
||||
name: zizmor
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
actions: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
run: uvx zizmor --format=sarif . > results.sarif
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
|
||||
271
.github/workflows/test.yml
vendored
@@ -9,10 +9,15 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pre-job:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
should_run_i18n: ${{ steps.found_paths.outputs.i18n == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||
should_run_web: ${{ steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||
should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||
should_run_cli: ${{ steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }}
|
||||
@@ -24,11 +29,16 @@ jobs:
|
||||
should_run_.github: ${{ steps.found_paths.outputs['.github'] == 'true' || steps.should_force.outputs.should_force == 'true' }} # redundant to have should_force but if someone changes the trigger then this won't have to be changed
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- id: found_paths
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
with:
|
||||
filters: |
|
||||
i18n:
|
||||
- 'i18n/**'
|
||||
web:
|
||||
- 'web/**'
|
||||
- 'i18n/**'
|
||||
@@ -58,16 +68,20 @@ jobs:
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./server
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
|
||||
@@ -95,16 +109,20 @@ jobs:
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./cli
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './cli/.nvmrc'
|
||||
|
||||
@@ -136,16 +154,20 @@ jobs:
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }}
|
||||
runs-on: windows-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./cli
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './cli/.nvmrc'
|
||||
|
||||
@@ -165,21 +187,25 @@ jobs:
|
||||
run: npm run test:cov
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
web-unit-tests:
|
||||
name: Test & Lint Web
|
||||
web-lint:
|
||||
name: Lint Web
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_web == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: mich
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./web
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './web/.nvmrc'
|
||||
|
||||
@@ -191,7 +217,7 @@ jobs:
|
||||
run: npm ci
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
run: npm run lint:p
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run formatter
|
||||
@@ -202,6 +228,35 @@ jobs:
|
||||
run: npm run check:svelte
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
web-unit-tests:
|
||||
name: Test Web
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_web == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./web
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './web/.nvmrc'
|
||||
|
||||
- name: Run setup typescript-sdk
|
||||
run: npm ci && npm run build
|
||||
working-directory: ./open-api/typescript-sdk
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
|
||||
- name: Run tsc
|
||||
run: npm run check:typescript
|
||||
if: ${{ !cancelled() }}
|
||||
@@ -210,21 +265,65 @@ jobs:
|
||||
run: npm run test:cov
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
i18n-tests:
|
||||
name: Test i18n
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_i18n == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './web/.nvmrc'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm --prefix=web ci
|
||||
|
||||
- name: Format
|
||||
run: npm --prefix=web run format:i18n
|
||||
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||
id: verify-changed-files
|
||||
with:
|
||||
files: |
|
||||
i18n/**
|
||||
|
||||
- name: Verify files have not changed
|
||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
||||
run: |
|
||||
echo "ERROR: i18n files not up to date!"
|
||||
echo "Changed files: ${CHANGED_FILES}"
|
||||
exit 1
|
||||
|
||||
e2e-tests-lint:
|
||||
name: End-to-End Lint
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_e2e == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./e2e
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './e2e/.nvmrc'
|
||||
|
||||
@@ -254,16 +353,20 @@ jobs:
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./server
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
|
||||
@@ -278,19 +381,25 @@ jobs:
|
||||
name: End-to-End Tests (Server & CLI)
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_e2e_server_cli == 'true' }}
|
||||
runs-on: mich
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./e2e
|
||||
strategy:
|
||||
matrix:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './e2e/.nvmrc'
|
||||
|
||||
@@ -320,19 +429,25 @@ jobs:
|
||||
name: End-to-End Tests (Web)
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_e2e_web == 'true' }}
|
||||
runs-on: mich
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./e2e
|
||||
strategy:
|
||||
matrix:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './e2e/.nvmrc'
|
||||
|
||||
@@ -357,15 +472,35 @@ jobs:
|
||||
run: npx playwright test
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
success-check-e2e:
|
||||
name: End-to-End Tests Success
|
||||
needs: [e2e-tests-server-cli, e2e-tests-web]
|
||||
permissions: {}
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Any jobs failed?
|
||||
if: ${{ contains(needs.*.result, 'failure') }}
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
# zizmor: ignore[template-injection]
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
|
||||
mobile-unit-tests:
|
||||
name: Unit Test Mobile
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Flutter SDK
|
||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2
|
||||
uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 # v2.19.0
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version-file: ./mobile/pubspec.yaml
|
||||
@@ -378,14 +513,19 @@ jobs:
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./machine-learning
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@22695119d769bdb6f7032ad67b9bca0ef8c4a174 # v5
|
||||
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5
|
||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
# TODO: add caching when supported (https://github.com/actions/setup-python/pull/818)
|
||||
# with:
|
||||
# python-version: 3.11
|
||||
@@ -395,32 +535,36 @@ jobs:
|
||||
uv sync --extra cpu
|
||||
- name: Lint with ruff
|
||||
run: |
|
||||
uv run ruff check --output-format=github app export
|
||||
uv run ruff check --output-format=github immich_ml
|
||||
- name: Check black formatting
|
||||
run: |
|
||||
uv run black --check app export
|
||||
uv run black --check immich_ml
|
||||
- name: Run mypy type checking
|
||||
run: |
|
||||
uv run mypy --strict app/
|
||||
uv run mypy --strict immich_ml/
|
||||
- name: Run tests and coverage
|
||||
run: |
|
||||
uv run pytest app --cov=app --cov-report term-missing
|
||||
uv run pytest --cov=immich_ml --cov-report term-missing
|
||||
|
||||
github-files-formatting:
|
||||
name: .github Files Formatting
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs['should_run_.github'] == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./.github
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './.github/.nvmrc'
|
||||
|
||||
@@ -434,25 +578,34 @@ jobs:
|
||||
shellcheck:
|
||||
name: ShellCheck
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@master
|
||||
with:
|
||||
ignore_paths: >-
|
||||
**/open-api/**
|
||||
**/openapi/**
|
||||
**/openapi**
|
||||
**/node_modules/**
|
||||
|
||||
generated-api-up-to-date:
|
||||
name: OpenAPI Clients
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
|
||||
@@ -466,7 +619,7 @@ jobs:
|
||||
run: make open-api
|
||||
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||
id: verify-changed-files
|
||||
with:
|
||||
files: |
|
||||
@@ -476,14 +629,18 @@ jobs:
|
||||
|
||||
- name: Verify files have not changed
|
||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
||||
run: |
|
||||
echo "ERROR: Generated files not up to date!"
|
||||
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||
echo "Changed files: ${CHANGED_FILES}"
|
||||
exit 1
|
||||
|
||||
generated-typeorm-migrations-up-to-date:
|
||||
name: TypeORM Checks
|
||||
sql-schema-up-to-date:
|
||||
name: SQL Schema Checks
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
services:
|
||||
postgres:
|
||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||
@@ -504,10 +661,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: './server/.nvmrc'
|
||||
|
||||
@@ -518,27 +677,29 @@ jobs:
|
||||
run: npm run build
|
||||
|
||||
- name: Run existing migrations
|
||||
run: npm run typeorm:migrations:run
|
||||
run: npm run migrations:run
|
||||
|
||||
- name: Test npm run schema:reset command works
|
||||
run: npm run typeorm:schema:reset
|
||||
run: npm run schema:reset
|
||||
|
||||
- name: Generate new migrations
|
||||
continue-on-error: true
|
||||
run: npm run typeorm:migrations:generate ./src/migrations/TestMigration
|
||||
run: npm run migrations:generate src/TestMigration
|
||||
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||
id: verify-changed-files
|
||||
with:
|
||||
files: |
|
||||
server/src/migrations/
|
||||
server/src
|
||||
- name: Verify migration files have not changed
|
||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }}
|
||||
run: |
|
||||
echo "ERROR: Generated migration files not up to date!"
|
||||
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||
cat ./src/migrations/*-TestMigration.ts
|
||||
echo "Changed files: ${CHANGED_FILES}"
|
||||
cat ./src/*-TestMigration.ts
|
||||
exit 1
|
||||
|
||||
- name: Run SQL generation
|
||||
@@ -547,7 +708,7 @@ jobs:
|
||||
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
||||
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||
id: verify-changed-sql-files
|
||||
with:
|
||||
files: |
|
||||
@@ -555,9 +716,11 @@ jobs:
|
||||
|
||||
- name: Verify SQL files have not changed
|
||||
if: steps.verify-changed-sql-files.outputs.files_changed == 'true'
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.verify-changed-sql-files.outputs.changed_files }}
|
||||
run: |
|
||||
echo "ERROR: Generated SQL files not up to date!"
|
||||
echo "Changed files: ${{ steps.verify-changed-sql-files.outputs.changed_files }}"
|
||||
echo "Changed files: ${CHANGED_FILES}"
|
||||
exit 1
|
||||
|
||||
# mobile-integration-tests:
|
||||
|
||||
20
.github/workflows/weblate-lock.yml
vendored
@@ -4,30 +4,32 @@ on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pre-job:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- id: found_paths
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
with:
|
||||
filters: |
|
||||
i18n:
|
||||
- 'i18n/!(en)**\.json'
|
||||
- name: Debug
|
||||
run: |
|
||||
echo "Should run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}}"
|
||||
echo "Found i18n paths: ${{ steps.found_paths.outputs.i18n }}"
|
||||
echo "Head ref: ${{ github.head_ref }}"
|
||||
|
||||
enforce-lock:
|
||||
name: Check Weblate Lock
|
||||
needs: [pre-job]
|
||||
runs-on: ubuntu-latest
|
||||
permissions: {}
|
||||
if: ${{ needs.pre-job.outputs.should_run == 'true' }}
|
||||
steps:
|
||||
- name: Check weblate lock
|
||||
@@ -36,7 +38,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Find Pull Request
|
||||
uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1
|
||||
uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0
|
||||
id: find-pr
|
||||
with:
|
||||
branch: chore/translations
|
||||
@@ -47,6 +49,7 @@ jobs:
|
||||
name: Weblate Lock Check Success
|
||||
needs: [enforce-lock]
|
||||
runs-on: ubuntu-latest
|
||||
permissions: {}
|
||||
if: always()
|
||||
steps:
|
||||
- name: Any jobs failed?
|
||||
@@ -54,4 +57,5 @@ jobs:
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
# zizmor: ignore[template-injection]
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
|
||||
77
.vscode/settings.json
vendored
@@ -1,44 +1,63 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[svelte]": {
|
||||
"editor.defaultFormatter": "svelte.svelte-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"svelte.enable-ts-plugin": true,
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"svelte"
|
||||
],
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"[dart]": {
|
||||
"editor.defaultFormatter": "Dart-Code.dart-code",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.selectionHighlight": false,
|
||||
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||
"editor.suggestSelection": "first",
|
||||
"editor.tabCompletion": "onlySnippets",
|
||||
"editor.wordBasedSuggestions": "off",
|
||||
"editor.defaultFormatter": "Dart-Code.dart-code"
|
||||
"editor.wordBasedSuggestions": "off"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"immich"
|
||||
],
|
||||
"[javascript]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "explicit",
|
||||
"source.removeUnusedImports": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[svelte]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "explicit",
|
||||
"source.removeUnusedImports": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "svelte.svelte-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "explicit",
|
||||
"source.removeUnusedImports": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
"cSpell.words": ["immich"],
|
||||
"editor.formatOnSave": true,
|
||||
"eslint.validate": ["javascript", "svelte"],
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
|
||||
"*.ts": "${capture}.spec.ts,${capture}.mock.ts"
|
||||
}
|
||||
}
|
||||
},
|
||||
"svelte.enable-ts-plugin": true,
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative"
|
||||
}
|
||||
|
||||
3
Makefile
@@ -17,6 +17,9 @@ e2e:
|
||||
prod:
|
||||
docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
|
||||
|
||||
prod-down:
|
||||
docker compose -f ./docker/docker-compose.prod.yml down --remove-orphans
|
||||
|
||||
prod-scale:
|
||||
docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
|
||||
|
||||
|
||||
@@ -61,9 +61,7 @@
|
||||
|
||||
## Demo
|
||||
|
||||
Access the demo [here](https://demo.immich.app). The demo is running on a Free-tier Oracle VM in Amsterdam with a 2.4Ghz quad-core ARM64 CPU and 24GB RAM.
|
||||
|
||||
For the mobile app, you can use `https://demo.immich.app` for the `Server Endpoint URL`
|
||||
Access the demo [here](https://demo.immich.app). For the mobile app, you can use `https://demo.immich.app` for the `Server Endpoint URL`.
|
||||
|
||||
### Login credentials
|
||||
|
||||
@@ -104,7 +102,7 @@ For the mobile app, you can use `https://demo.immich.app` for the `Server Endpoi
|
||||
| Read-only gallery | Yes | Yes |
|
||||
| Stacked Photos | Yes | Yes |
|
||||
| Tags | No | Yes |
|
||||
| Folder View | No | Yes |
|
||||
| Folder View | Yes | Yes |
|
||||
|
||||
## Translations
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
22.14.0
|
||||
22.15.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:22.14.0-alpine3.20@sha256:40be979442621049f40b1d51a26b55e281246b5de4e5f51a18da7beb6e17e3f9 AS core
|
||||
FROM node:22.15.0-alpine3.20@sha256:686b8892b69879ef5bfd6047589666933508f9a5451c67320df3070ba0e9807b AS core
|
||||
|
||||
WORKDIR /usr/src/open-api/typescript-sdk
|
||||
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
||||
|
||||
@@ -1,39 +1,29 @@
|
||||
import { FlatCompat } from '@eslint/eslintrc';
|
||||
import js from '@eslint/js';
|
||||
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
||||
import globals from 'globals';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import typescriptEslint from 'typescript-eslint';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default [
|
||||
export default typescriptEslint.config([
|
||||
eslintPluginUnicorn.configs.recommended,
|
||||
eslintPluginPrettierRecommended,
|
||||
js.configs.recommended,
|
||||
typescriptEslint.configs.recommended,
|
||||
{
|
||||
ignores: ['eslint.config.mjs', 'dist'],
|
||||
},
|
||||
...compat.extends(
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:unicorn/recommended',
|
||||
),
|
||||
{
|
||||
plugins: {
|
||||
'@typescript-eslint': typescriptEslint,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
|
||||
parser: tsParser,
|
||||
parser: typescriptEslint.parser,
|
||||
ecmaVersion: 5,
|
||||
sourceType: 'module',
|
||||
|
||||
@@ -58,4 +48,4 @@ export default [
|
||||
'object-shorthand': ['error', 'always'],
|
||||
},
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
3013
cli/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.56",
|
||||
"version": "2.2.65",
|
||||
"description": "Command Line Interface (CLI) for Immich",
|
||||
"type": "module",
|
||||
"exports": "./dist/index.js",
|
||||
@@ -21,9 +21,7 @@
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/micromatch": "^4.0.9",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^22.13.10",
|
||||
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
||||
"@typescript-eslint/parser": "^8.15.0",
|
||||
"@types/node": "^22.15.16",
|
||||
"@vitest/coverage-v8": "^3.0.0",
|
||||
"byte-size": "^9.0.0",
|
||||
"cli-progress": "^3.12.0",
|
||||
@@ -31,12 +29,13 @@
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-unicorn": "^56.0.1",
|
||||
"eslint-plugin-unicorn": "^57.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"mock-fs": "^5.2.0",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-organize-imports": "^4.0.0",
|
||||
"typescript": "^5.3.3",
|
||||
"typescript-eslint": "^8.28.0",
|
||||
"vite": "^6.0.0",
|
||||
"vite-tsconfig-paths": "^5.0.0",
|
||||
"vitest": "^3.0.0",
|
||||
@@ -70,6 +69,6 @@
|
||||
"micromatch": "^4.0.8"
|
||||
},
|
||||
"volta": {
|
||||
"node": "22.14.0"
|
||||
"node": "22.15.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
@@ -90,7 +90,7 @@ services:
|
||||
container_name: immich_prometheus
|
||||
ports:
|
||||
- 9090:9090
|
||||
image: prom/prometheus@sha256:502ad90314c7485892ce696cb14a99fceab9fc27af29f4b427f41bd39701a199
|
||||
image: prom/prometheus@sha256:e2b8aa62b64855956e3ec1e18b4f9387fb6203174a4471936f4662f437f04405
|
||||
volumes:
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus-data:/prometheus
|
||||
@@ -102,7 +102,7 @@ services:
|
||||
command: [ './run.sh', '-disable-reporting' ]
|
||||
ports:
|
||||
- 3000:3000
|
||||
image: grafana/grafana:11.5.2-ubuntu@sha256:8b5858c447e06fd7a89006b562ba7bba7c4d5813600c7982374c41852adefaeb
|
||||
image: grafana/grafana:11.6.1-ubuntu@sha256:6fc273288470ef499dd3c6b36aeade093170d4f608f864c5dd3a7fabeae77b50
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
# The location where your uploaded files are stored
|
||||
UPLOAD_LOCATION=./library
|
||||
# The location where your database files are stored
|
||||
|
||||
# The location where your database files are stored. Network shares are not supported for the database
|
||||
DB_DATA_LOCATION=./postgres
|
||||
|
||||
# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
||||
|
||||
@@ -1 +1 @@
|
||||
22.14.0
|
||||
22.15.0
|
||||
|
||||
@@ -23,23 +23,32 @@ Refer to the official [postgres documentation](https://www.postgresql.org/docs/c
|
||||
It is not recommended to directly backup the `DB_DATA_LOCATION` folder. Doing so while the database is running can lead to a corrupted backup that cannot be restored.
|
||||
:::
|
||||
|
||||
### Automatic Database Backups
|
||||
### Automatic Database Dumps
|
||||
|
||||
For convenience, Immich will automatically create database backups by default. The backups are stored in `UPLOAD_LOCATION/backups`.
|
||||
As mentioned above, you should make your own backup of these together with the asset folders as noted below.
|
||||
You can adjust the schedule and amount of kept backups in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
||||
By default, Immich will keep the last 14 backups and create a new backup every day at 2:00 AM.
|
||||
:::warning
|
||||
The automatic database dumps can be used to restore the database in the event of damage to the Postgres database files.
|
||||
There is no monitoring for these dumps and you will not be notified if they are unsuccessful.
|
||||
:::
|
||||
|
||||
#### Trigger Backup
|
||||
:::caution
|
||||
The database dumps do **NOT** contain any pictures or videos, only metadata. They are only usable with a copy of the other files in `UPLOAD_LOCATION` as outlined below.
|
||||
:::
|
||||
|
||||
You are able to trigger a backup in the [admin job status page](http://my.immich.app/admin/jobs-status).
|
||||
Visit the page, open the "Create job" modal from the top right, select "Backup Database" and click "Confirm".
|
||||
A job will run and trigger a backup, you can verify this worked correctly by checking the logs or the backup folder.
|
||||
This backup will count towards the last X backups that will be kept based on your settings.
|
||||
For disaster-recovery purposes, Immich will automatically create database dumps. The dumps are stored in `UPLOAD_LOCATION/backups`.
|
||||
Please be sure to make your own, independent backup of the database together with the asset folders as noted below.
|
||||
You can adjust the schedule and amount of kept database dumps in the [admin settings](http://my.immich.app/admin/system-settings?isOpen=backup).
|
||||
By default, Immich will keep the last 14 database dumps and create a new dump every day at 2:00 AM.
|
||||
|
||||
#### Trigger Dump
|
||||
|
||||
You are able to trigger a database dump in the [admin job status page](http://my.immich.app/admin/jobs-status).
|
||||
Visit the page, open the "Create job" modal from the top right, select "Create Database Dump" and click "Confirm".
|
||||
A job will run and trigger a dump, you can verify this worked correctly by checking the logs or the `backups/` folder.
|
||||
This dumps will count towards the last `X` dumps that will be kept based on your settings.
|
||||
|
||||
#### Restoring
|
||||
|
||||
We hope to make restoring simpler in future versions, for now you can find the backups in the `UPLOAD_LOCATION/backups` folder on your host.
|
||||
We hope to make restoring simpler in future versions, for now you can find the database dumps in the `UPLOAD_LOCATION/backups` folder on your host.
|
||||
Then please follow the steps in the following section for restoring the database.
|
||||
|
||||
### Manual Backup and Restore
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
@@ -22,7 +22,7 @@ server {
|
||||
client_max_body_size 50000M;
|
||||
|
||||
# Set headers
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
@@ -31,7 +31,7 @@ Admin can send a welcome email if the Email option is set, you can learn here ho
|
||||
|
||||
Admin can specify the storage quota for the user as the instance's admin; once the limit is reached, the user won't be able to upload to the instance anymore.
|
||||
|
||||
In order to select a storage quota, click on the pencil icon and enter the storage quota in GiB. You can choose an unlimited quota using the value 0 (default).
|
||||
In order to select a storage quota, click on the pencil icon and enter the storage quota in GiB. You can choose an unlimited quota by leaving it empty (default).
|
||||
|
||||
:::tip
|
||||
The system administrator can see the usage quota percentage of all users in Server Stats page.
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# Database Migrations
|
||||
|
||||
After making any changes in the `server/src/entities`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.
|
||||
After making any changes in the `server/src/schema`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.
|
||||
|
||||
1. Run the command
|
||||
|
||||
```bash
|
||||
npm run typeorm:migrations:generate <migration-name>
|
||||
npm run migrations:generate <migration-name>
|
||||
```
|
||||
|
||||
2. Check if the migration file makes sense.
|
||||
3. Move the migration file to folder `./server/src/migrations` in your code editor.
|
||||
3. Move the migration file to folder `./server/src/schema/migrations` in your code editor.
|
||||
|
||||
The server will automatically detect `*.ts` file changes and restart. Part of the server start-up process includes running any new migrations, so it will be applied immediately.
|
||||
|
||||
@@ -63,6 +63,13 @@ If you only want to do web development connected to an existing, remote backend,
|
||||
IMMICH_SERVER_URL=https://demo.immich.app/ npm run dev
|
||||
```
|
||||
|
||||
If you're using PowerShell on Windows you may need to set the env var separately like so:
|
||||
|
||||
```powershell
|
||||
$env:IMMICH_SERVER_URL = "https://demo.immich.app/"
|
||||
npm run dev
|
||||
```
|
||||
|
||||
#### `@immich/ui`
|
||||
|
||||
To see local changes to `@immich/ui` in Immich, do the following:
|
||||
@@ -76,9 +83,20 @@ To see local changes to `@immich/ui` in Immich, do the following:
|
||||
|
||||
### Mobile app
|
||||
|
||||
The mobile app `(/mobile)` will required Flutter toolchain 3.13.x and FVM to be installed on your system.
|
||||
#### Setup
|
||||
|
||||
Please refer to the [Flutter's official documentation](https://flutter.dev/docs/get-started/install) for more information on setting up the toolchain on your machine.
|
||||
1. Setup Flutter toolchain using FVM.
|
||||
2. Run `flutter pub get` to install the dependencies.
|
||||
3. Run `make translation` to generate the translation file.
|
||||
4. Run `fvm flutter run` to start the app.
|
||||
|
||||
#### Translation
|
||||
|
||||
To add a new translation text, enter the key-value pair in the `i18n/en.json` in the root of the immich project. Then, from the `mobile/` directory, run
|
||||
|
||||
```bash
|
||||
make translation
|
||||
```
|
||||
|
||||
The mobile app asks you what backend to connect to. You can utilize the demo backend (https://demo.immich.app/) if you don't need to change server code or upload photos. Alternatively, you can run the server yourself per the instructions above.
|
||||
|
||||
|
||||
@@ -42,6 +42,12 @@ docker run -it -v "$(pwd)":/import:ro -e IMMICH_INSTANCE_URL=https://your-immich
|
||||
|
||||
Please modify the `IMMICH_INSTANCE_URL` and `IMMICH_API_KEY` environment variables as suitable. You can also use a Docker env file to store your sensitive API key.
|
||||
|
||||
This `docker run` command will directly run the command `immich` inside the container. You can directly append the desired parameters (see under "usage") to the commandline like this:
|
||||
|
||||
```bash
|
||||
docker run -it -v "$(pwd)":/import:ro -e IMMICH_INSTANCE_URL=https://your-immich-instance/api -e IMMICH_API_KEY=your-api-key ghcr.io/immich-app/immich-cli:latest upload -a -c 5 --recursive directory/
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
<details>
|
||||
@@ -112,7 +118,7 @@ You begin by authenticating to your Immich server. For instance:
|
||||
immich login http://192.168.1.216:2283/api HFEJ38DNSDUEG
|
||||
```
|
||||
|
||||
This will store your credentials in a `auth.yml` file in the configuration directory which defaults to `~/.config/`. The directory can be set with the `-d` option or the environment variable `IMMICH_CONFIG_DIR`. Please keep the file secure, either by performing the logout command after you are done, or deleting it manually.
|
||||
This will store your credentials in a `auth.yml` file in the configuration directory which defaults to `~/.config/immich/`. The directory can be set with the `-d` option or the environment variable `IMMICH_CONFIG_DIR`. Please keep the file secure, either by performing the logout command after you are done, or deleting it manually.
|
||||
|
||||
Once you are authenticated, you can upload assets to your Immich server.
|
||||
|
||||
|
||||
@@ -121,6 +121,6 @@ Once this is done, you can continue to step 3 of "Basic Setup".
|
||||
|
||||
[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
|
||||
[nvct]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
|
||||
[jellyfin-lp]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#configure-and-verify-lp-mode-on-linux
|
||||
[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#known-issues-and-limitations
|
||||
[jellyfin-lp]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#low-power-encoding
|
||||
[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#known-issues-and-limitations-on-linux
|
||||
[libmali-rockchip]: https://github.com/tsukumijima/libmali-rockchip/releases
|
||||
|
||||
|
Before Width: | Height: | Size: 4.9 MiB After Width: | Height: | Size: 4.9 MiB |
@@ -72,7 +72,7 @@ In rare cases, the library watcher can hang, preventing Immich from starting up.
|
||||
|
||||
### Nightly job
|
||||
|
||||
There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library managment page.
|
||||
There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
||||
|
||||
- The GPU must have compute capability 5.2 or greater.
|
||||
- The server must have the official NVIDIA driver installed.
|
||||
- The installed driver must be >= 535 (it must support CUDA 12.2).
|
||||
- The installed driver must be >= 545 (it must support CUDA 12.3).
|
||||
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.
|
||||
|
||||
#### ROCm
|
||||
@@ -71,7 +71,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele
|
||||
|
||||
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 the `docker-compose.yml` under `immich-machine-learning`, uncomment the `extends` section and change `cpu` to the appropriate backend.
|
||||
3. Still in `immich-machine-learning`, add one of -[armnn, cuda, rocm, openvino] to the `image` section's tag at the end of the line.
|
||||
3. Still in `immich-machine-learning`, add one of -[armnn, cuda, rocm, openvino, rknn] to the `image` section's tag at the end of the line.
|
||||
4. Redeploy the `immich-machine-learning` container with these updated settings.
|
||||
|
||||
### Confirming Device Usage
|
||||
|
||||
@@ -45,7 +45,7 @@ Some search examples:
|
||||
</TabItem>
|
||||
<TabItem value="Mobile" label="Mobile">
|
||||
|
||||
<img src={require('./img/moblie-smart-serach.webp').default} width="30%" title='Smart search on mobile' />
|
||||
<img src={require('./img/mobile-smart-search.webp').default} width="30%" title='Smart search on mobile' />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
@@ -56,7 +56,20 @@ Navigating to `Administration > Settings > Machine Learning Settings > Smart Sea
|
||||
|
||||
### CLIP models
|
||||
|
||||
More powerful models can be used for more accurate search results, but are slower and can require more server resources. Check the dropdowns below to see how they compare in memory usage, speed and quality by language.
|
||||
The default search model is fast, but there are many other options that can provide better search results. The tradeoff of using these models is that they're slower and/or use more memory (both when indexing images with background Smart Search jobs and when searching).
|
||||
|
||||
The first step of choosing the right model for you is to know which languages your users will search in.
|
||||
|
||||
If your users will only search in English, then the [CLIP][huggingface-clip] section is the first place to look. This is a curated list of the models that generally perform the best for their size class. The models here are ordered from higher to lower quality. This means that the top models will generally rank the most relevant results higher and have a higher capacity to understand descriptive, detailed, and/or niche queries. The models are also generally ordered from larger to smaller, so consider the impact on memory usage, job processing and search speed when deciding on one. The smaller models in this list are not too different in quality and many times faster.
|
||||
|
||||
[Multilingual models][huggingface-multilingual-clip] are also available so users can search in their native language. Use these models if you expect non-English searches to be common. They can be separated into two search patterns:
|
||||
|
||||
- `nllb` models expect the search query to be in the language specified in the user settings
|
||||
- `xlm` and `siglip2` models understand search text regardless of the current language setting
|
||||
|
||||
`nllb` models tend to perform the best and are recommended when users primarily searches in their native, non-English language. `xlm` and `siglip2` models are more flexible and are recommended for mixed language search, where the same user might search in different languages at different times.
|
||||
|
||||
For more details, check the tables below to see how they compare in memory usage, speed and quality by language.
|
||||
|
||||
Once you've chosen a model, follow these steps:
|
||||
|
||||
@@ -79,9 +92,9 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
|
||||
**Execution Time (ms)**: After warming up the model with one pass, the mean execution time of 100 passes with the same input.
|
||||
|
||||
**Memory (MiB)**: The peak RSS usage of the process afer performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.
|
||||
**Memory (MiB)**: The peak RSS usage of the process after performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.
|
||||
|
||||
**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval.
|
||||
**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval. Chinese (Simplified), English, French, German, Italian, Japanese, Korean, Polish, Russian, Spanish and Turkish are additionally tested on XTD-10. Chinese (Simplified) and English are additionally tested on Flickr30k. The recall metrics are the average across all tested datasets.
|
||||
|
||||
**Pareto Optimal**: Whether the model is not completely outclassed by another model. Try to use models that are optimal for the languages relevant to you. Specifically, for a given model and language, if there's another model that's better for that language in at least one respect (memory usage, execution time, recall) while being at least as good for that language in every other way, then the model is not optimal for that language.
|
||||
|
||||
@@ -93,59 +106,59 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
<summary>English</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 75.73 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 75.44 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 75.19 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 75.09 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 75.07 | ❌ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 75.01 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 74.92 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 74.9 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 74.87 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 74.87 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 74.77 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 74.28 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 74.26 | ✅ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 73.15 | ✅ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 72.78 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 72.58 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 72.57 | ❌ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 72.47 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 72.45 | ✅ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 72.44 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 72.37 | ❌ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 71.64 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 71.63 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 71.45 | ❌ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 71.33 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 71.19 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 69.86 | ❌ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 69.66 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 69.38 | ❌ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 68.78 | ✅ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 68.53 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 68.53 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 68.53 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 68.51 | ❌ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 68.41 | ❌ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 68.41 | ❌ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 68.33 | ❌ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 66.96 | ✅ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 66.95 | ✅ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 65.65 | ✅ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 65.49 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 60.12 | ❌ |
|
||||
| ViT-B-32__openai | 1004 | 2.26 | 59.37 | ✅ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 59.36 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 59.17 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 59.09 | ❌ |
|
||||
| RN50__openai | 913 | 2.39 | 58.32 | ✅ |
|
||||
| ViT-B-16__openai | 985 | 5.03 | 58.27 | ❌ |
|
||||
| RN50x4__openai | 1416 | 5.85 | 57.88 | ❌ |
|
||||
| RN50__cc12m | 914 | 2.37 | 57.75 | ✅ |
|
||||
| RN101__openai | 1111 | 3.21 | 57.7 | ❌ |
|
||||
| RN101__yfcc15m | 1111 | 3.22 | 50.11 | ❌ |
|
||||
| RN50__yfcc15m | 908 | 2.34 | 48.28 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 85.99 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 85.96 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 85.96 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 85.93 | ❌ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 85.78 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 85.75 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 85.62 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 85.53 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 85.48 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 85.47 | ✅ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 85.09 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 85.03 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 84.86 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 84.61 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 84.17 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 83.51 | ❌ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 83.28 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 83.24 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.23 | ❌ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 83.19 | ✅ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 82.54 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.43 | ❌ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 82.36 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 82.28 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 81.9 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 81.9 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 80.82 | ❌ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 80.65 | ❌ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 80.16 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.78 | ❌ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 78.64 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 78.6 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 78.06 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 78.06 | ❌ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 77.62 | ✅ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 77.47 | ❌ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 76.91 | ❌ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 76.43 | ✅ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 76.35 | ❌ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 73.83 | ✅ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 73.62 | ❌ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 73.34 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 72.99 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 72.76 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 72.59 | ❌ |
|
||||
| RN50x4__openai | 1416 | 5.85 | 70.8 | ❌ |
|
||||
| ViT-B-16__openai | 985 | 5.03 | 70.01 | ❌ |
|
||||
| ViT-B-32__openai | 1004 | 2.26 | 69.9 | ✅ |
|
||||
| RN101__openai | 1111 | 3.21 | 69.3 | ❌ |
|
||||
| RN50__openai | 913 | 2.39 | 69.02 | ✅ |
|
||||
| RN50__cc12m | 914 | 2.37 | 64.59 | ✅ |
|
||||
| RN101__yfcc15m | 1111 | 3.22 | 55.21 | ❌ |
|
||||
| RN50__yfcc15m | 908 | 2.34 | 53.63 | ✅ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Arabic</summary>
|
||||
@@ -156,8 +169,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 74.03 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 73.19 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 69.31 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 69.29 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 69.29 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 69.29 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 68.64 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 68.35 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 68.25 | ✅ |
|
||||
@@ -195,25 +208,25 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
<summary>Chinese (Simplified)</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 77.49 | ✅ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 77.19 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 76.98 | ❌ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 72.89 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 72.65 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 72.52 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 67.83 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 67.81 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 67.51 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 67.39 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 67.33 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 67.23 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 67.05 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 66.87 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 66.24 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 66.1 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 65.56 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 64.39 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 62.56 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 79.7 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 78.94 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 75.22 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 74.8 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 73.91 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 72.8 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 72.77 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 72.41 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 72.36 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 71.59 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 71.37 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 71.3 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 71.11 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 70.95 | ✅ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 70.51 | ✅ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 67.48 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 66.84 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 65.7 | ✅ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 63.38 | ❌ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Croatian</summary>
|
||||
@@ -324,8 +337,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 80.05 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 79.81 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 79.72 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 79.72 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 79.72 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 79.64 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 79.49 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 79.41 | ❌ |
|
||||
@@ -357,8 +370,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 29.56 | ❌ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 29.54 | ✅ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 29.36 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 27.76 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 27.76 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 27.76 | ❌ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 25.67 | ✅ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 25.59 | ❌ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 25.53 | ❌ |
|
||||
@@ -384,8 +397,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 34.27 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 34.14 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 33.98 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 30.57 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 30.57 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 30.57 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 30.05 | ✅ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 24.92 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 24.02 | ❌ |
|
||||
@@ -422,110 +435,111 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
<summary>French</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 88.01 | ✅ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 87.74 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 87.69 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 87.6 | ✅ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 87.58 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 87.51 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 87.23 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 86.9 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 86.9 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 86.44 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 86.44 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 86.28 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 86.11 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 86.08 | ✅ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 84.49 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 84.3 | ✅ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 83.03 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 82.93 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 82.27 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 82.14 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 80.96 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 80.64 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 80.28 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 79.65 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 77.4 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 76.88 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 76.3 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.68 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 69.59 | ❌ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 68.36 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 61.78 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 58.4 | ❌ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 58.35 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 57.17 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 57.17 | ❌ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 57.05 | ✅ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 56.08 | ✅ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 52.96 | ✅ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 52.83 | ✅ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 51.88 | ❌ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 51.82 | ✅ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 42.86 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 42.81 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 42.54 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 41.72 | ❌ |
|
||||
| RN50x4__openai | 1416 | 5.85 | 38.85 | ❌ |
|
||||
| RN101__openai | 1111 | 3.21 | 36.79 | ❌ |
|
||||
| ViT-B-16__openai | 985 | 5.03 | 36.47 | ❌ |
|
||||
| ViT-B-32__openai | 1004 | 2.26 | 35.17 | ✅ |
|
||||
| RN50__openai | 913 | 2.39 | 34.44 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 86.5 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 86.5 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 86.39 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 86.15 | ❌ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 86.1 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 86.07 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 86.06 | ❌ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 85.89 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 85.67 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 85.67 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 85.63 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 85.39 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 85.35 | ✅ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 84.97 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 83.8 | ✅ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.96 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 82.91 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 82.52 | ❌ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 81.21 | ✅ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 80.23 | ✅ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 79.85 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.47 | ✅ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 79.3 | ❌ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 77.49 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 76.82 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 75.94 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 75.3 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.24 | ❌ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 69.33 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 64.41 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 62.86 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 59.27 | ❌ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 59.09 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 58.25 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 58.25 | ❌ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 56.97 | ✅ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 56.21 | ✅ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 53.36 | ✅ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 53.33 | ✅ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 53.26 | ❌ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 53.22 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 46.34 | ❌ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 46.3 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 45.95 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 45.69 | ❌ |
|
||||
| RN50x4__openai | 1416 | 5.85 | 42.48 | ❌ |
|
||||
| RN101__openai | 1111 | 3.21 | 40.16 | ❌ |
|
||||
| ViT-B-16__openai | 985 | 5.03 | 40.1 | ❌ |
|
||||
| ViT-B-32__openai | 1004 | 2.26 | 38.27 | ✅ |
|
||||
| RN50__openai | 913 | 2.39 | 37.8 | ✅ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>German</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 90.04 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 89.97 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 89.85 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 89.81 | ✅ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 89.77 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 89.69 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 89.45 | ✅ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 89.44 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 89.39 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 89.35 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 89.03 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 88.82 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 88.55 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 88.42 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 87.19 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 86.44 | ✅ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 84.81 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 84.81 | ❌ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 84.58 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 84.44 | ✅ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 83.33 | ✅ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 82.75 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 82.32 | ❌ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 81.63 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 76.76 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 76.33 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 75.19 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.07 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 64.61 | ❌ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 52.81 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 42.88 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 38.65 | ❌ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 38.37 | ❌ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 37.65 | ✅ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 36.6 | ✅ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 35.44 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 35.44 | ❌ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 32.46 | ✅ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 32.31 | ✅ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 31.85 | ✅ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 31.81 | ❌ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 28.41 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 27.63 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 27.09 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 24.48 | ❌ |
|
||||
| RN50x4__openai | 1416 | 5.85 | 23.49 | ❌ |
|
||||
| RN50__openai | 913 | 2.39 | 20.91 | ✅ |
|
||||
| ViT-B-16__openai | 985 | 5.03 | 20.83 | ❌ |
|
||||
| RN101__openai | 1111 | 3.21 | 20.39 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 87.32 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 87.29 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 87.29 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 87.21 | ✅ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 87.18 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 87.14 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 87.07 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 86.83 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 86.81 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 86.75 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 86.74 | ✅ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 86.68 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 86.56 | ✅ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 86.16 | ✅ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 84.54 | ❌ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 84.41 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 84.25 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 83.8 | ❌ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 82.59 | ✅ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 81.53 | ✅ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 81.34 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 81.15 | ✅ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 81.05 | ❌ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 78.35 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 76.56 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 76.0 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 75.21 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.14 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 65.86 | ❌ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 56.87 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 47.19 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 43.36 | ❌ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 43.0 | ❌ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 41.81 | ✅ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 40.43 | ✅ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 40.41 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 40.41 | ❌ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 37.71 | ✅ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 37.64 | ✅ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 36.04 | ✅ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 35.9 | ❌ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 34.19 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 33.1 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 32.25 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 30.56 | ❌ |
|
||||
| RN50x4__openai | 1416 | 5.85 | 29.2 | ❌ |
|
||||
| ViT-B-16__openai | 985 | 5.03 | 25.77 | ❌ |
|
||||
| RN101__openai | 1111 | 3.21 | 25.46 | ❌ |
|
||||
| RN50__openai | 913 | 2.39 | 24.92 | ✅ |
|
||||
| ViT-B-32__openai | 1004 | 2.26 | 24.13 | ✅ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Greek</summary>
|
||||
@@ -542,10 +556,10 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 60.63 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 60.41 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 60.1 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 60.06 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 60.06 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 59.44 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 60.06 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 59.44 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 59.44 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 59.43 | ✅ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 58.78 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 53.42 | ❌ |
|
||||
@@ -670,99 +684,104 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
<summary>Italian</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 88.6 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 88.25 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 88.12 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 88.04 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 87.97 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 87.69 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 87.29 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 87.06 | ❌ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 86.91 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 86.88 | ✅ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 86.68 | ✅ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 86.61 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 85.55 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 85.37 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 83.78 | ✅ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 83.0 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 81.81 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 81.77 | ❌ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 81.32 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 80.97 | ❌ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 80.53 | ✅ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 80.1 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.71 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 77.31 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 75.19 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 74.49 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 74.04 | ❌ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 73.68 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 73.57 | ✅ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 51.04 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 41.73 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 36.87 | ❌ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 36.84 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 34.68 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 34.68 | ❌ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 34.64 | ✅ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 33.8 | ✅ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 30.11 | ✅ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 30.04 | ❌ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 29.89 | ❌ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 29.88 | ✅ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 26.67 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 25.51 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 25.3 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 21.37 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 87.17 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 86.91 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 86.83 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 86.77 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 86.67 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 86.42 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 86.35 | ✅ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 86.34 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 86.18 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 86.17 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 85.84 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 85.8 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 85.7 | ✅ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 85.67 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 83.32 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 82.95 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.73 | ❌ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 82.72 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 81.07 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 80.8 | ✅ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 80.6 | ✅ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 80.35 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 78.79 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 76.62 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 76.51 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 76.08 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 75.29 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.29 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 74.84 | ❌ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 56.32 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 47.25 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 43.09 | ❌ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 42.99 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 40.29 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 40.29 | ❌ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 39.67 | ✅ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 39.03 | ✅ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 36.14 | ✅ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 35.89 | ❌ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 35.59 | ❌ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 35.56 | ✅ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 33.53 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 32.19 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 30.95 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 28.85 | ❌ |
|
||||
| RN50x4__openai | 1416 | 5.85 | 25.75 | ❌ |
|
||||
| ViT-B-16__openai | 985 | 5.03 | 25.18 | ❌ |
|
||||
| RN101__openai | 1111 | 3.21 | 24.48 | ❌ |
|
||||
| RN50__openai | 913 | 2.39 | 23.89 | ✅ |
|
||||
| ViT-B-32__openai | 1004 | 2.26 | 23.39 | ✅ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Japanese</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 86.97 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 85.15 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 84.69 | ❌ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 81.77 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 81.26 | ❌ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 81.19 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 69.99 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 68.58 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 68.35 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 68.29 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 67.99 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 67.68 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 67.67 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 66.85 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 66.54 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 65.77 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 61.48 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 58.1 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 55.31 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 83.95 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 82.21 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 81.55 | ❌ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 78.72 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 78.53 | ❌ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 75.93 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 66.86 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 65.59 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 65.48 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 65.36 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 64.47 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 64.17 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 64.08 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 63.69 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 63.33 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 63.02 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 58.39 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 56.38 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 53.16 | ❌ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Korean</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 77.21 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 76.89 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 75.72 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 75.06 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 74.94 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 74.36 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 74.09 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 73.61 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 73.55 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 73.41 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 73.18 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 72.79 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 72.27 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 71.73 | ✅ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 71.12 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 70.25 | ✅ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 67.54 | ✅ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 67.37 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 65.44 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 80.56 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 80.53 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 77.09 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 77.08 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 76.97 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 76.92 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 76.58 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 76.2 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 75.95 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 75.86 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 75.67 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 75.49 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 74.6 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 74.52 | ✅ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 73.88 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 71.09 | ✅ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 68.87 | ✅ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 67.94 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 66.39 | ✅ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Maori</summary>
|
||||
@@ -834,34 +853,34 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
<summary>Polish</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 80.6 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 80.17 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 80.06 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 80.04 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 79.98 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 79.8 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 79.72 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 79.66 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 79.45 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 79.26 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 79.21 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 79.14 | ✅ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 78.23 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 75.33 | ✅ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 74.7 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 74.63 | ❌ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 73.69 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 73.44 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 70.34 | ❌ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 59.4 | ❌ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 59.14 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 48.74 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 48.35 | ❌ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 40.76 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 39.13 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 39.09 | ❌ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 38.55 | ❌ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 38.46 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.49 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 83.45 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 83.11 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 82.99 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 82.96 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 82.93 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 82.61 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 82.26 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 82.24 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 82.03 | ✅ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.03 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 81.92 | ✅ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 81.27 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 80.0 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 79.65 | ✅ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 76.75 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 76.52 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 75.1 | ✅ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 73.9 | ❌ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 65.03 | ❌ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 64.89 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 51.6 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 51.29 | ❌ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 46.15 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 41.55 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 41.17 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 40.9 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 40.76 | ✅ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Portuguese</summary>
|
||||
@@ -955,84 +974,87 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
<summary>Russian</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 87.65 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 87.62 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 87.4 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 87.39 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 86.88 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 86.87 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 86.74 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 86.26 | ✅ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 85.98 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 85.66 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 85.54 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 84.69 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 84.29 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 84.24 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 82.86 | ✅ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 81.59 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 80.56 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 80.44 | ❌ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 79.99 | ❌ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 39.51 | ❌ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 39.16 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 23.33 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 22.4 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 84.54 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 84.41 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 84.36 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 84.31 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 84.22 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 83.9 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 83.69 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 83.5 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.31 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 83.21 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 83.11 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 82.7 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.69 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 80.91 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 79.75 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.35 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 78.91 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 78.06 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 76.44 | ✅ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 42.81 | ❌ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 42.1 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 24.95 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 24.25 | ❌ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 20.85 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 20.44 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 20.41 | ❌ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Spanish</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 84.24 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 83.94 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 83.91 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 83.78 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 83.71 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 83.59 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 83.2 | ✅ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 83.0 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 82.91 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 82.58 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 82.5 | ✅ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 82.48 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 82.22 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 81.34 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 80.18 | ❌ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 80.14 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 78.99 | ✅ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 78.19 | ✅ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 78.15 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 77.93 | ✅ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 77.64 | ❌ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 77.21 | ❌ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 76.36 | ❌ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 75.73 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 75.56 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 75.01 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 74.62 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 74.6 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 70.31 | ❌ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 58.31 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 49.56 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 46.69 | ❌ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 46.53 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 44.05 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 44.05 | ❌ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 43.67 | ✅ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 42.5 | ✅ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 41.03 | ✅ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 40.91 | ❌ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 40.3 | ✅ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 40.3 | ❌ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 37.92 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 37.7 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 37.59 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 34.75 | ❌ |
|
||||
| ViT-B-16__openai | 985 | 5.03 | 32.1 | ❌ |
|
||||
| RN50x4__openai | 1416 | 5.85 | 32.08 | ❌ |
|
||||
| RN101__openai | 1111 | 3.21 | 30.77 | ❌ |
|
||||
| RN50__openai | 913 | 2.39 | 30.2 | ✅ |
|
||||
| ViT-B-32__openai | 1004 | 2.26 | 29.84 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 85.47 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 85.44 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 85.32 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 85.22 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 85.15 | ❌ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 84.81 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 84.68 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 84.6 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 84.55 | ✅ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 84.27 | ❌ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 84.15 | ✅ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 83.87 | ❌ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.74 | ❌ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 83.61 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 83.15 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 81.7 | ❌ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 80.91 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 80.73 | ✅ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 80.69 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 80.3 | ❌ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 79.8 | ❌ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.71 | ✅ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 79.64 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 78.0 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 77.83 | ❌ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 76.87 | ✅ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 76.66 | ❌ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 75.99 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 71.96 | ❌ |
|
||||
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 62.06 | ❌ |
|
||||
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 53.78 | ❌ |
|
||||
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 50.13 | ❌ |
|
||||
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 50.0 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 47.39 | ❌ |
|
||||
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 47.39 | ❌ |
|
||||
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 46.47 | ✅ |
|
||||
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 45.68 | ✅ |
|
||||
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 44.0 | ✅ |
|
||||
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 43.98 | ✅ |
|
||||
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 43.8 | ❌ |
|
||||
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 43.73 | ✅ |
|
||||
| RN50x64__openai | 5079 | 48.79 | 43.01 | ❌ |
|
||||
| ViT-L-14__openai | 2212 | 19.91 | 42.96 | ❌ |
|
||||
| ViT-L-14-336__openai | 2616 | 43.45 | 41.67 | ❌ |
|
||||
| RN50x16__openai | 2221 | 15.87 | 40.21 | ❌ |
|
||||
| RN50x4__openai | 1416 | 5.85 | 36.06 | ❌ |
|
||||
| ViT-B-16__openai | 985 | 5.03 | 35.67 | ❌ |
|
||||
| RN101__openai | 1111 | 3.21 | 34.62 | ❌ |
|
||||
| ViT-B-32__openai | 1004 | 2.26 | 32.6 | ✅ |
|
||||
| RN50__openai | 913 | 2.39 | 31.79 | ✅ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Swahili</summary>
|
||||
@@ -1057,8 +1079,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 72.1 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 72.06 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 71.84 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 71.7 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 71.7 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 71.7 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 71.61 | ❌ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 71.51 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 71.45 | ✅ |
|
||||
@@ -1115,31 +1137,34 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|
||||
<summary>Turkish</summary>
|
||||
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|
||||
|------------------------------------------------------|--------------|---------------------|------------|----------------|
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 81.15 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 80.89 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 78.11 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 77.51 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 77.36 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 77.28 | ❌ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 77.24 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 77.01 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 76.37 | ❌ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 75.92 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 75.69 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 75.68 | ❌ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 75.54 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 75.16 | ✅ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 73.83 | ✅ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 70.15 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 69.19 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 66.72 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 64.76 | ❌ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 38.8 | ❌ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 38.48 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 30.83 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 30.28 | ❌ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 21.31 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 20.08 | ✅ |
|
||||
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.91 | ✅ |
|
||||
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 83.74 | ✅ |
|
||||
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 81.26 | ✅ |
|
||||
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 80.21 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 79.34 | ✅ |
|
||||
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 79.22 | ✅ |
|
||||
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 78.9 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 78.85 | ✅ |
|
||||
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 78.29 | ✅ |
|
||||
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 78.27 | ❌ |
|
||||
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 78.0 | ❌ |
|
||||
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 77.81 | ✅ |
|
||||
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 77.67 | ✅ |
|
||||
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 77.33 | ✅ |
|
||||
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 76.42 | ✅ |
|
||||
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 72.44 | ✅ |
|
||||
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 69.84 | ✅ |
|
||||
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 69.83 | ❌ |
|
||||
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 67.13 | ❌ |
|
||||
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 44.43 | ❌ |
|
||||
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 43.87 | ❌ |
|
||||
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 35.1 | ❌ |
|
||||
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 34.92 | ❌ |
|
||||
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 25.2 | ✅ |
|
||||
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 24.55 | ✅ |
|
||||
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 24.13 | ✅ |
|
||||
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 24.08 | ❌ |
|
||||
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 23.95 | ❌ |
|
||||
</details>
|
||||
<details>
|
||||
<summary>Ukrainian</summary>
|
||||
|
||||
@@ -14,14 +14,14 @@ online generators you can use.
|
||||
2. Paste the link to your JSON style in either the **Light Style** or **Dark Style**. (You can add different styles which will help make the map style more appropriate depending on whether you set **Immich** to Light or Dark mode.)
|
||||
3. Save your selections. Reload the map, and enjoy your custom map style!
|
||||
|
||||
## Use Maptiler to build a custom style
|
||||
## Use MapTiler to build a custom style
|
||||
|
||||
Customizing the map style can be done easily using Maptiler, if you do not want to write an entire JSON document by hand.
|
||||
Customizing the map style can be done easily using MapTiler, if you do not want to write an entire JSON document by hand.
|
||||
|
||||
1. Create a free account at https://cloud.maptiler.com
|
||||
2. Once logged in, you can either create a brand new map by clicking on **New Map**, selecting a starter map, and then clicking **Customize**, OR by selecting a **Standard Map** and customizing it from there.
|
||||
3. The **editor** interface is self-explanatory. You can change colors, remove visible layers, or add optional layers (e.g., administrative, topo, hydro, etc.) in the composer.
|
||||
4. Once you have your map composed, click on **Save** at the top right. Give it a unique name to save it to your account.
|
||||
5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. Maptiler will present an interactive side-by-side map with the original and your changes prior to publication.<br/>
|
||||
6. Maptiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay.
|
||||
7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to Maptiler.
|
||||
5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. MapTiler will present an interactive side-by-side map with the original and your changes prior to publication.<br/>
|
||||
6. MapTiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay.
|
||||
7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to MapTiler.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Database Queries
|
||||
|
||||
:::danger
|
||||
Keep in mind that mucking around in the database might set the moon on fire. Avoid modifying the database directly when possible, and always have current backups.
|
||||
Keep in mind that mucking around in the database might set the Moon on fire. Avoid modifying the database directly when possible, and always have current backups.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
|
||||
@@ -23,12 +23,12 @@ name: immich_remote_ml
|
||||
services:
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino] to the image tag.
|
||||
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
|
||||
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
||||
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
||||
# extends:
|
||||
# file: hwaccel.ml.yml
|
||||
# service: # set to one of [armnn, cuda, rocm, openvino, openvino-wsl] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
||||
# service: # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
||||
volumes:
|
||||
- model-cache:/cache
|
||||
restart: always
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
sidebar_position: 100
|
||||
---
|
||||
|
||||
# Config File
|
||||
|
||||
A config file can be provided as an alternative to the UI configuration.
|
||||
|
||||
@@ -2,53 +2,13 @@
|
||||
sidebar_position: 30
|
||||
---
|
||||
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
import ExampleEnv from '!!raw-loader!../../../docker/example.env';
|
||||
|
||||
# Docker Compose [Recommended]
|
||||
|
||||
Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose.
|
||||
|
||||
## Step 1 - Download the required files
|
||||
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';
|
||||
|
||||
Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files.
|
||||
|
||||
```bash title="Move to the directory you created"
|
||||
mkdir ./immich-app
|
||||
cd ./immich-app
|
||||
```
|
||||
|
||||
Download [`docker-compose.yml`][compose-file] and [`example.env`][env-file] by running the following commands:
|
||||
|
||||
```bash title="Get docker-compose.yml file"
|
||||
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||
```
|
||||
|
||||
```bash title="Get .env file"
|
||||
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||
```
|
||||
|
||||
You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`.
|
||||
|
||||
## Step 2 - Populate the .env file with custom values
|
||||
|
||||
<CodeBlock language="bash" title="Default environmental variable content">
|
||||
{ExampleEnv}
|
||||
</CodeBlock>
|
||||
|
||||
- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets. It should be a new directory on the server with enough free space.
|
||||
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publicly exposed, so this password is only used for local authentication.
|
||||
To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`. `pwgen` is a handy utility for this.
|
||||
- Set your timezone by uncommenting the `TZ=` line.
|
||||
- Populate custom database information if necessary.
|
||||
|
||||
## Step 3 - Start the containers
|
||||
|
||||
From the directory you created in Step 1 (which should now contain your customized `docker-compose.yml` and `.env` files), run the following command to start Immich as a background service:
|
||||
|
||||
```bash title="Start the containers"
|
||||
docker compose up -d
|
||||
```
|
||||
<DockerComposeSteps />
|
||||
|
||||
:::info Docker version
|
||||
If you get an error such as `unknown shorthand flag: 'd' in -d` or `open <location of your .env file>: permission denied`, you are probably running the wrong Docker version. (This happens, for example, with the docker.io package in Ubuntu 22.04.3 LTS.) You can correct the problem by following the complete [Docker Engine install](https://docs.docker.com/engine/install/) procedure for your distribution, crucially the "Uninstall old versions" and "Install using the apt/rpm repository" sections. These replace the distro's Docker packages with Docker's official ones.
|
||||
@@ -69,39 +29,4 @@ If you get an error `can't set healthcheck.start_interval as feature require Doc
|
||||
|
||||
## Next Steps
|
||||
|
||||
Read the [Post Installation](/docs/install/post-install.mdx) steps or setup optional features below.
|
||||
|
||||
### Setting up optional features
|
||||
|
||||
- [External Libraries](/docs/features/libraries.md): Adding your existing photo library to Immich
|
||||
- [Hardware Transcoding](/docs/features/hardware-transcoding.md): Speeding up video transcoding
|
||||
- [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich
|
||||
|
||||
### Upgrading
|
||||
|
||||
:::danger Read the release notes
|
||||
Immich is currently under heavy development, which means you can expect [breaking changes][breaking] and bugs. Therefore, we recommend reading the release notes prior to updating and to take special care when using automated tools like [Watchtower][watchtower].
|
||||
|
||||
You can see versions that had breaking changes [here][breaking].
|
||||
:::
|
||||
|
||||
If `IMMICH_VERSION` is set, it will need to be updated to the latest or desired version.
|
||||
|
||||
When a new version of Immich is [released][releases], the application can be upgraded and restarted with the following commands, run in the directory with the `docker-compose.yml` file:
|
||||
|
||||
```bash title="Upgrade and restart Immich"
|
||||
docker compose pull && docker compose up -d
|
||||
```
|
||||
|
||||
To clean up disk space, the old version's obsolete container images can be deleted with the following command:
|
||||
|
||||
```bash title="Clean up unused Docker images"
|
||||
docker image prune
|
||||
```
|
||||
|
||||
[compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||
[env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||
[watchtower]: https://containrrr.dev/watchtower/
|
||||
[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created
|
||||
[container-auth]: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-to-the-container-registry
|
||||
[releases]: https://github.com/immich-app/immich/releases
|
||||
Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md).
|
||||
|
||||
@@ -80,6 +80,7 @@ Information on the current workers can be found [here](/docs/administration/jobs
|
||||
| `DB_USERNAME` | Database user | `postgres` | server, database<sup>\*1</sup> |
|
||||
| `DB_PASSWORD` | Database password | `postgres` | server, database<sup>\*1</sup> |
|
||||
| `DB_DATABASE_NAME` | Database name | `immich` | server, database<sup>\*1</sup> |
|
||||
| `DB_SSL_MODE` | Database SSL mode | | server |
|
||||
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database vector extension (one of [`pgvector`, `pgvecto.rs`]) | `pgvecto.rs` | server |
|
||||
| `DB_SKIP_MIGRATIONS` | Whether to skip running migrations on startup (one of [`true`, `false`]) | `false` | server |
|
||||
|
||||
|
||||
@@ -41,3 +41,9 @@ A list of common steps to take after installing Immich include:
|
||||
## Step 7 - Setup Server Backups
|
||||
|
||||
<ServerBackup />
|
||||
|
||||
## Setting up optional features
|
||||
|
||||
- [External Libraries](/docs/features/libraries.md): Adding your existing photo library to Immich
|
||||
- [Hardware Transcoding](/docs/features/hardware-transcoding.md): Speeding up video transcoding
|
||||
- [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich
|
||||
|
||||
@@ -29,7 +29,7 @@ Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/la
|
||||
|
||||
## Step 2 - Populate the .env file with custom values
|
||||
|
||||
Follow [Step 2 in Docker Compose](./docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.
|
||||
Follow [Step 2 in Docker Compose](/docs/install/docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.
|
||||
|
||||
## Step 3 - Create a new project in Container Manager
|
||||
|
||||
@@ -67,10 +67,4 @@ Click "**Edit Rules**" and add the following firewall rules:
|
||||
|
||||
## Next Steps
|
||||
|
||||
Read the [Post Installation](/docs/install/post-install.mdx) steps or setup optional features below.
|
||||
|
||||
### Setting up optional features
|
||||
|
||||
- [External Libraries](/docs/features/libraries.md): Adding your existing photo library to Immich
|
||||
- [Hardware Transcoding](/docs/features/hardware-transcoding.md): Speeding up video transcoding
|
||||
- [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich
|
||||
Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
sidebar_position: 80
|
||||
---
|
||||
|
||||
# TrueNAS SCALE [Community]
|
||||
# TrueNAS [Community]
|
||||
|
||||
:::note
|
||||
This is a community contribution and not officially supported by the Immich team, but included here for convenience.
|
||||
@@ -12,17 +12,17 @@ Community support can be found in the dedicated channel on the [Discord Server](
|
||||
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).**
|
||||
:::
|
||||
|
||||
Immich can easily be installed on TrueNAS SCALE via the **Community** train application.
|
||||
Consider reviewing the TrueNAS [Apps tutorial](https://www.truenas.com/docs/scale/scaletutorials/apps/) if you have not previously configured applications on your system.
|
||||
Immich can easily be installed on TrueNAS Community Edition via the **Community** train application.
|
||||
Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system.
|
||||
|
||||
TrueNAS SCALE makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries.
|
||||
TrueNAS Community Edition makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries.
|
||||
|
||||
## First Steps
|
||||
|
||||
The Immich app in TrueNAS SCALE installs, completes the initial configuration, then starts the Immich web portal.
|
||||
When updates become available, SCALE alerts and provides easy updates.
|
||||
The Immich app in TrueNAS Community Edition installs, completes the initial configuration, then starts the Immich web portal.
|
||||
When updates become available, TrueNAS alerts and provides easy updates.
|
||||
|
||||
Before installing the Immich app in SCALE, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation.
|
||||
Before installing the Immich app in TrueNAS, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation.
|
||||
You may also configure environment variables at any time after deploying the application.
|
||||
|
||||
### Setting up Storage Datasets
|
||||
@@ -126,9 +126,9 @@ className="border rounded-xl"
|
||||
|
||||
Accept the default port `30041` in **WebUI Port** or enter a custom port number.
|
||||
:::info Allowed Port Numbers
|
||||
Only numbers within the range 9000-65535 may be used on SCALE versions below TrueNAS Scale 24.10 Electric Eel.
|
||||
Only numbers within the range 9000-65535 may be used on TrueNAS versions below TrueNAS Community Edition 24.10 Electric Eel.
|
||||
|
||||
Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/references/defaultports/).
|
||||
Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/solutions/optimizations/security/#truenas-default-ports).
|
||||
:::
|
||||
|
||||
### Storage Configuration
|
||||
@@ -173,7 +173,7 @@ className="border rounded-xl"
|
||||
|
||||
You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**.
|
||||
The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich.
|
||||
The **Host Path** is the location on the TrueNAS SCALE server where your external library is located.
|
||||
The **Host Path** is the location on the TrueNAS Community Edition server where your external library is located.
|
||||
|
||||
<!-- A section for Labels would go here but I don't know what they do. -->
|
||||
|
||||
@@ -188,17 +188,17 @@ className="border rounded-xl"
|
||||
|
||||
Accept the default **CPU** limit of `2` threads or specify the number of threads (CPUs with Multi-/Hyper-threading have 2 threads per core).
|
||||
|
||||
Accept the default **Memory** limit of `4096` MB or specify the number of MB of RAM. If you're using Machine Learning you should probably set this above 8000 MB.
|
||||
Specify the **Memory** limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB.
|
||||
|
||||
:::info Older SCALE Versions
|
||||
Before TrueNAS SCALE version 24.10 Electric Eel:
|
||||
:::info Older TrueNAS Versions
|
||||
Before TrueNAS Community Edition version 24.10 Electric Eel:
|
||||
|
||||
The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads.
|
||||
|
||||
The **Memory** value was specified in a different format with a default of `8Gi` which is 8 GiB of RAM. The value was specified in bytes or a number with a measurement suffix. Examples: `129M`, `123Mi`, `1000000000`
|
||||
:::
|
||||
|
||||
Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://www.truenas.com/docs/truenasapps/#gpu-passthrough)
|
||||
Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough)
|
||||
|
||||
### Install
|
||||
|
||||
@@ -240,14 +240,18 @@ className="border rounded-xl"
|
||||
/>
|
||||
|
||||
:::info
|
||||
Some Environment Variables are not available for the TrueNAS SCALE app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
|
||||
Some Environment Variables are not available for the TrueNAS Community Edition app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).
|
||||
|
||||
Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`.
|
||||
:::
|
||||
|
||||
## Updating the App
|
||||
|
||||
When updates become available, SCALE alerts and provides easy updates.
|
||||
:::danger
|
||||
Make sure to read the general [upgrade instructions](/docs/install/upgrading.md).
|
||||
:::
|
||||
|
||||
When updates become available, TrueNAS alerts and provides easy updates.
|
||||
To update the app to the latest version:
|
||||
|
||||
- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
|
||||
|
||||
@@ -131,6 +131,10 @@ For more information on how to use the application once installed, please refer
|
||||
|
||||
## Updating Steps
|
||||
|
||||
:::danger
|
||||
Make sure to read the general [upgrade instructions](/docs/install/upgrading.md).
|
||||
:::
|
||||
|
||||
Updating is extremely easy however it's important to be aware that containers managed via the Docker Compose Manager plugin do not integrate with Unraid's native dockerman UI, the label "_update ready_" will always be present on containers installed via the Docker Compose Manager.
|
||||
|
||||
<img
|
||||
|
||||
29
docs/docs/install/upgrading.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
sidebar_position: 95
|
||||
---
|
||||
|
||||
# Upgrading
|
||||
|
||||
:::danger Read the release notes
|
||||
Immich is currently under heavy development, which means you can expect [breaking changes][breaking] and bugs. You should read the release notes prior to updating and take special care when using automated tools like [Watchtower][watchtower].
|
||||
|
||||
You can see versions that had breaking changes [here][breaking].
|
||||
:::
|
||||
|
||||
When a new version of Immich is [released][releases], you should read the release notes and account for any breaking changes noted (as mentioned above).
|
||||
If you use `IMMICH_VERSION` in your `.env` file, it will need to be updated to the latest or desired version.
|
||||
After that, the application can be upgraded and restarted with the following commands, run in the directory with the `docker-compose.yml` file:
|
||||
|
||||
```bash title="Upgrade and restart Immich"
|
||||
docker compose pull && docker compose up -d
|
||||
```
|
||||
|
||||
To clean up disk space, the old version's obsolete container images can be deleted with the following command:
|
||||
|
||||
```bash title="Clean up unused Docker images"
|
||||
docker image prune
|
||||
```
|
||||
|
||||
[watchtower]: https://containrrr.dev/watchtower/
|
||||
[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created
|
||||
[releases]: https://github.com/immich-app/immich/releases
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Comparison
|
||||
|
||||
BIN
docs/docs/overview/img/social-preview-light.webp
Normal file
|
After Width: | Height: | Size: 233 KiB |
@@ -1,5 +1,5 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Quick start
|
||||
@@ -10,11 +10,20 @@ to install and use it.
|
||||
|
||||
## Requirements
|
||||
|
||||
Check the [requirements page](/docs/install/requirements) to get started.
|
||||
- A system with at least 4GB of RAM and 2 CPU cores.
|
||||
- [Docker](https://docs.docker.com/engine/install/)
|
||||
|
||||
> For a more detailed list of requirements, see the [requirements page](/docs/install/requirements).
|
||||
|
||||
---
|
||||
|
||||
## Set up the server
|
||||
|
||||
Follow the [Docker Compose (Recommended)](/docs/install/docker-compose) instructions to install the server.
|
||||
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';
|
||||
|
||||
<DockerComposeSteps />
|
||||
|
||||
---
|
||||
|
||||
## Try the web app
|
||||
|
||||
@@ -26,6 +35,8 @@ Try uploading a picture from your browser.
|
||||
|
||||
<img src={require('./img/upload-button.webp').default} title="Upload button" />
|
||||
|
||||
---
|
||||
|
||||
## Try the mobile app
|
||||
|
||||
### Download the Mobile App
|
||||
@@ -56,6 +67,8 @@ You can select the **Jobs** tab to see Immich processing your photos.
|
||||
|
||||
<img src={require('/docs/guides/img/jobs-tab.webp').default} title="Jobs tab" width={300} />
|
||||
|
||||
---
|
||||
|
||||
## Review the database backup and restore process
|
||||
|
||||
Immich has built-in database backups. You can refer to the
|
||||
@@ -65,6 +78,8 @@ Immich has built-in database backups. You can refer to the
|
||||
The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Where to go from here?
|
||||
|
||||
You may decide you'd like to install the server a different way; the Install category on the left menu provides many options.
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Introduction
|
||||
# Welcome to Immich
|
||||
|
||||
<img src={require('./img/feature-panel.webp').default} alt="Immich - Self-hosted photos and videos backup tool" />
|
||||
<img
|
||||
src={require('./img/social-preview-light.webp').default}
|
||||
alt="Immich - Self-hosted photos and videos backup tool"
|
||||
data-theme="light"
|
||||
/>
|
||||
|
||||
## Welcome!
|
||||
|
||||
43
docs/docs/partials/_docker-compose-install-steps.mdx
Normal file
@@ -0,0 +1,43 @@
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
import ExampleEnv from '!!raw-loader!../../../docker/example.env';
|
||||
|
||||
### Step 1 - Download the required files
|
||||
|
||||
Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files.
|
||||
|
||||
```bash title="Move to the directory you created"
|
||||
mkdir ./immich-app
|
||||
cd ./immich-app
|
||||
```
|
||||
|
||||
Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) by running the following commands:
|
||||
|
||||
```bash title="Get docker-compose.yml file"
|
||||
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
|
||||
```
|
||||
|
||||
```bash title="Get .env file"
|
||||
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
|
||||
```
|
||||
|
||||
You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`.
|
||||
|
||||
### Step 2 - Populate the .env file with custom values
|
||||
|
||||
<CodeBlock language="bash" title="Default environmental variable content">
|
||||
{ExampleEnv}
|
||||
</CodeBlock>
|
||||
|
||||
- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets. It should be a new directory on the server with enough free space.
|
||||
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publicly exposed, so this password is only used for local authentication.
|
||||
To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`. `pwgen` is a handy utility for this.
|
||||
- Set your timezone by uncommenting the `TZ=` line.
|
||||
- Populate custom database information if necessary.
|
||||
|
||||
### Step 3 - Start the containers
|
||||
|
||||
From the directory you created in Step 1 (which should now contain your customized `docker-compose.yml` and `.env` files), run the following command to start Immich as a background service:
|
||||
|
||||
```bash title="Start the containers"
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -1,2 +1,7 @@
|
||||
Now that you have imported some pictures, you should setup server backups to preserve your memories.
|
||||
You can do so by following our [backup guide](/docs/administration/backup-and-restore.md).
|
||||
|
||||
:::danger
|
||||
Immich is still under heavy development _and_ handles very important data.
|
||||
It is essential that you set up good backups, and test them.
|
||||
:::
|
||||
|
||||
@@ -95,7 +95,7 @@ const config = {
|
||||
position: 'right',
|
||||
},
|
||||
{
|
||||
to: '/docs/overview/introduction',
|
||||
to: '/docs/overview/welcome',
|
||||
position: 'right',
|
||||
label: 'Docs',
|
||||
},
|
||||
@@ -124,6 +124,12 @@ const config = {
|
||||
label: 'Discord',
|
||||
position: 'right',
|
||||
},
|
||||
{
|
||||
type: 'html',
|
||||
position: 'right',
|
||||
value:
|
||||
'<a href="https://buy.immich.app" target="_blank" class="no-underline hover:no-underline"><button class="buy-button bg-immich-primary dark:bg-immich-dark-primary text-white dark:text-black rounded-xl">Buy Immich</button></a>',
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
@@ -134,7 +140,7 @@ const config = {
|
||||
items: [
|
||||
{
|
||||
label: 'Welcome',
|
||||
to: '/docs/overview/introduction',
|
||||
to: '/docs/overview/welcome',
|
||||
},
|
||||
{
|
||||
label: 'Installation',
|
||||
|
||||
5324
docs/package-lock.json
generated
@@ -57,6 +57,6 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"volta": {
|
||||
"node": "22.14.0"
|
||||
"node": "22.15.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,9 @@ const projects: CommunityProjectProps[] = [
|
||||
},
|
||||
{
|
||||
title: 'Lightroom Immich Plugin: lrc-immich-plugin',
|
||||
description: 'Another Lightroom plugin to publish or export photos from Lightroom to Immich.',
|
||||
url: 'https://github.com/bmachek/lrc-immich-plugin',
|
||||
description:
|
||||
'Lightroom plugin to publish, export photos from Lightroom to Immich. Import from Immich to Lightroom is also supported.',
|
||||
url: 'https://blog.fokuspunk.de/lrc-immich-plugin/',
|
||||
},
|
||||
{
|
||||
title: 'Immich Duplicate Finder',
|
||||
|
||||
@@ -7,14 +7,22 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Be Vietnam Pro', serif;
|
||||
font-optical-sizing: auto;
|
||||
/* font-size: 1.125rem;
|
||||
@font-face {
|
||||
font-family: 'Overpass';
|
||||
src: url('/fonts/overpass/Overpass.ttf') format('truetype-variations');
|
||||
font-weight: 1 999;
|
||||
font-style: normal;
|
||||
ascent-override: 106.25%;
|
||||
size-adjust: 106.25%; */
|
||||
size-adjust: 106.25%;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Overpass Mono';
|
||||
src: url('/fonts/overpass/OverpassMono.ttf') format('truetype-variations');
|
||||
font-weight: 1 999;
|
||||
font-style: normal;
|
||||
ascent-override: 106.25%;
|
||||
size-adjust: 106.25%;
|
||||
}
|
||||
|
||||
.breadcrumbs__link {
|
||||
@@ -29,6 +37,7 @@ img {
|
||||
|
||||
/* You can override the default Infima variables here. */
|
||||
:root {
|
||||
font-family: 'Overpass', sans-serif;
|
||||
--ifm-color-primary: #4250af;
|
||||
--ifm-color-primary-dark: #4250af;
|
||||
--ifm-color-primary-darker: #4250af;
|
||||
@@ -59,14 +68,12 @@ div[class^='announcementBar_'] {
|
||||
}
|
||||
|
||||
.menu__link {
|
||||
padding: 10px;
|
||||
padding-left: 16px;
|
||||
padding: 10px 10px 10px 16px;
|
||||
border-radius: 24px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.menu__list-item-collapsible {
|
||||
border-radius: 10px;
|
||||
margin-right: 16px;
|
||||
border-radius: 24px;
|
||||
}
|
||||
@@ -83,3 +90,12 @@ div[class*='navbar__items'] > li:has(a[class*='version-switcher-34ab39']) {
|
||||
code {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.buy-button {
|
||||
padding: 8px 14px;
|
||||
border: 1px solid transparent;
|
||||
font-family: 'Overpass', sans-serif;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 5px 2px rgba(181, 206, 254, 0.4);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
mdiBug,
|
||||
mdiCalendarToday,
|
||||
mdiCrosshairsOff,
|
||||
mdiCrop,
|
||||
mdiDatabase,
|
||||
mdiLeadPencil,
|
||||
mdiLockOff,
|
||||
@@ -22,6 +23,18 @@ const withLanguage = (date: Date) => (language: string) => date.toLocaleDateStri
|
||||
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
|
||||
|
||||
const items: Item[] = [
|
||||
{
|
||||
icon: mdiCrop,
|
||||
iconColor: 'tomato',
|
||||
title: 'Image dimensions in EXIF metadata are cursed',
|
||||
description:
|
||||
'The dimensions in EXIF metadata can be different from the actual dimensions of the image, causing issues with cropping and resizing.',
|
||||
link: {
|
||||
url: 'https://github.com/immich-app/immich/pull/17974',
|
||||
text: '#17974',
|
||||
},
|
||||
date: new Date(2025, 5, 5),
|
||||
},
|
||||
{
|
||||
icon: mdiMicrosoftWindows,
|
||||
iconColor: '#357EC7',
|
||||
|
||||
5
docs/src/pages/errors.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Errors
|
||||
|
||||
## TypeORM Upgrade
|
||||
|
||||
The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again.
|
||||
@@ -4,6 +4,7 @@ import Layout from '@theme/Layout';
|
||||
import { discordPath, discordViewBox } from '@site/src/components/svg-paths';
|
||||
import ThemedImage from '@theme/ThemedImage';
|
||||
import Icon from '@mdi/react';
|
||||
|
||||
function HomepageHeader() {
|
||||
return (
|
||||
<header>
|
||||
@@ -12,11 +13,14 @@ function HomepageHeader() {
|
||||
<div className="w-full h-[120vh] absolute left-0 top-0 backdrop-blur-3xl bg-immich-bg/40 dark:bg-transparent"></div>
|
||||
</div>
|
||||
<section className="text-center pt-12 sm:pt-24 bg-immich-bg/50 dark:bg-immich-dark-bg/80">
|
||||
<ThemedImage
|
||||
sources={{ dark: 'img/logomark-dark.svg', light: 'img/logomark-light.svg' }}
|
||||
className="h-[115px] w-[115px] mb-2 antialiased rounded-none"
|
||||
alt="Immich logo"
|
||||
/>
|
||||
<a href="https://futo.org" target="_blank" rel="noopener noreferrer">
|
||||
<ThemedImage
|
||||
sources={{ dark: 'img/logomark-dark-with-futo.svg', light: 'img/logomark-light-with-futo.svg' }}
|
||||
className="h-[125px] w-[125px] mb-2 antialiased rounded-none"
|
||||
alt="Immich logo"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<div className="mt-8">
|
||||
<p className="text-3xl md:text-5xl sm:leading-tight mb-1 font-extrabold text-black/90 dark:text-white px-4">
|
||||
Self-hosted{' '}
|
||||
@@ -27,7 +31,7 @@ function HomepageHeader() {
|
||||
solution<span className="block"></span>
|
||||
</p>
|
||||
|
||||
<p className="max-w-1/4 m-auto mt-4 px-4">
|
||||
<p className="max-w-1/4 m-auto mt-4 px-4 text-lg text-gray-700 dark:text-gray-100">
|
||||
Easily back up, organize, and manage your photos on your own server. Immich helps you
|
||||
<span className="sm:block"></span> browse, search and organize your photos and videos with ease, without
|
||||
sacrificing your privacy.
|
||||
@@ -35,27 +39,21 @@ function HomepageHeader() {
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row place-items-center place-content-center mt-9 gap-4 ">
|
||||
<Link
|
||||
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary dark:bg-immich-dark-primary rounded-xl no-underline hover:no-underline text-white hover:text-gray-50 dark:text-immich-dark-bg font-bold uppercase"
|
||||
to="docs/overview/introduction"
|
||||
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary dark:bg-immich-dark-primary rounded-xl no-underline hover:no-underline text-white hover:text-gray-50 dark:text-immich-dark-bg font-bold"
|
||||
to="docs/overview/quick-start"
|
||||
>
|
||||
Get started
|
||||
Get Started
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary/10 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold uppercase"
|
||||
className="flex place-items-center place-content-center py-3 px-8 border bg-white/90 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold"
|
||||
to="https://demo.immich.app/"
|
||||
>
|
||||
Demo
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
className="flex place-items-center place-content-center py-3 px-8 border bg-immich-primary/10 dark:bg-gray-300 rounded-xl hover:no-underline text-immich-primary dark:text-immich-dark-bg font-bold uppercase"
|
||||
to="https://immich.store"
|
||||
>
|
||||
Buy Merch
|
||||
Open Demo
|
||||
</Link>
|
||||
</div>
|
||||
<div className="my-12 flex gap-1 font-medium place-items-center place-content-center text-immich-primary dark:text-immich-dark-primary">
|
||||
|
||||
<div className="my-8 flex gap-1 font-medium place-items-center place-content-center text-immich-primary dark:text-immich-dark-primary">
|
||||
<Icon
|
||||
path={discordPath}
|
||||
viewBox={discordViewBox} /* viewBox may show an error in your IDE but it is normal. */
|
||||
@@ -88,11 +86,18 @@ function HomepageHeader() {
|
||||
<img className="h-24" alt="Get it on Google Play" src="/img/google-play-badge.png" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="h-24">
|
||||
<a href="https://apps.apple.com/sg/app/immich/id1613945652">
|
||||
<img className="h-24 sm:p-3.5 p-3" alt="Download on the App Store" src="/img/ios-app-store-badge.svg" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="h-24">
|
||||
<a href="https://github.com/immich-app/immich/releases/latest">
|
||||
<img className="h-24 sm:p-3.5 p-3" alt="Download APK" src="/img/download-apk-github.svg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<ThemedImage
|
||||
sources={{ dark: '/img/app-qr-code-dark.svg', light: '/img/app-qr-code-light.svg' }}
|
||||
@@ -111,7 +116,7 @@ export default function Home(): JSX.Element {
|
||||
<HomepageHeader />
|
||||
<div className="flex flex-col place-items-center text-center place-content-center dark:bg-immich-dark-bg py-8">
|
||||
<p>This project is available under GNU AGPL v3 license.</p>
|
||||
<p className="text-xs">Privacy should not be a luxury</p>
|
||||
<p className="text-sm">Privacy should not be a luxury</p>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import Link from '@docusaurus/Link';
|
||||
import Layout from '@theme/Layout';
|
||||
function HomepageHeader() {
|
||||
return (
|
||||
|
||||
@@ -76,6 +76,7 @@ import {
|
||||
mdiWeb,
|
||||
mdiDatabaseOutline,
|
||||
mdiLinkEdit,
|
||||
mdiTagFaces,
|
||||
mdiMovieOpenPlayOutline,
|
||||
} from '@mdi/js';
|
||||
import Layout from '@theme/Layout';
|
||||
@@ -83,6 +84,8 @@ import React from 'react';
|
||||
import { Item, Timeline } from '../components/timeline';
|
||||
|
||||
const releases = {
|
||||
'v1.130.0': new Date(2025, 2, 25),
|
||||
'v1.127.0': new Date(2025, 1, 26),
|
||||
'v1.122.0': new Date(2024, 11, 5),
|
||||
'v1.120.0': new Date(2024, 10, 6),
|
||||
'v1.114.0': new Date(2024, 8, 6),
|
||||
@@ -242,6 +245,13 @@ const roadmap: Item[] = [
|
||||
];
|
||||
|
||||
const milestones: Item[] = [
|
||||
withRelease({
|
||||
icon: mdiFolderMultiple,
|
||||
iconColor: 'brown',
|
||||
title: 'Folders view in the mobile app',
|
||||
description: 'Browse your photos and videos in their folder structure inside the mobile app',
|
||||
release: 'v1.130.0',
|
||||
}),
|
||||
{
|
||||
icon: mdiStar,
|
||||
iconColor: 'gold',
|
||||
@@ -249,6 +259,14 @@ const milestones: Item[] = [
|
||||
description: 'Reached 60K Stars on GitHub!',
|
||||
getDateLabel: withLanguage(new Date(2025, 2, 4)),
|
||||
},
|
||||
withRelease({
|
||||
icon: mdiTagFaces,
|
||||
iconColor: 'teal',
|
||||
title: 'Manual face tagging',
|
||||
description:
|
||||
'Manually tag or remove faces in photos and videos, even when automatic detection misses or misidentifies them.',
|
||||
release: 'v1.127.0',
|
||||
}),
|
||||
withRelease({
|
||||
icon: mdiLinkEdit,
|
||||
iconColor: 'crimson',
|
||||
@@ -266,8 +284,8 @@ const milestones: Item[] = [
|
||||
withRelease({
|
||||
icon: mdiDatabaseOutline,
|
||||
iconColor: 'brown',
|
||||
title: 'Automatic database backups',
|
||||
description: 'Database backups are now integrated into the Immich server',
|
||||
title: 'Automatic database dumps',
|
||||
description: 'Database dumps are now integrated into the Immich server',
|
||||
release: 'v1.120.0',
|
||||
}),
|
||||
{
|
||||
@@ -300,7 +318,7 @@ const milestones: Item[] = [
|
||||
withRelease({
|
||||
icon: mdiFolderMultiple,
|
||||
iconColor: 'brown',
|
||||
title: 'Folders',
|
||||
title: 'Folders view',
|
||||
description: 'Browse your photos and videos in their folder structure',
|
||||
release: 'v1.113.0',
|
||||
}),
|
||||
|
||||
5
docs/static/.well-known/security.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
Policy: https://github.com/immich-app/immich/blob/main/SECURITY.md
|
||||
Contact: mailto:security@immich.app
|
||||
Preferred-Languages: en
|
||||
Expires: 2026-05-01T23:59:00.000Z
|
||||
Canonical: https://immich.app/.well-known/security.txt
|
||||
1
docs/static/_redirects
vendored
@@ -30,3 +30,4 @@
|
||||
/docs/guides/api-album-sync /docs/community-projects 307
|
||||
/docs/guides/remove-offline-files /docs/community-projects 307
|
||||
/milestones /roadmap 307
|
||||
/docs/overview/introduction /docs/overview/welcome 307
|
||||
140
docs/static/archived-versions.json
vendored
@@ -1,15 +1,15 @@
|
||||
[
|
||||
{
|
||||
"label": "v1.130.2",
|
||||
"url": "https://v1.130.2.archive.immich.app"
|
||||
"label": "v1.132.3",
|
||||
"url": "https://v1.132.3.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.130.1",
|
||||
"url": "https://v1.130.1.archive.immich.app"
|
||||
"label": "v1.131.3",
|
||||
"url": "https://v1.131.3.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.130.0",
|
||||
"url": "https://v1.130.0.archive.immich.app"
|
||||
"label": "v1.130.3",
|
||||
"url": "https://v1.130.3.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.129.0",
|
||||
@@ -27,46 +27,14 @@
|
||||
"label": "v1.126.1",
|
||||
"url": "https://v1.126.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.126.0",
|
||||
"url": "https://v1.126.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.125.7",
|
||||
"url": "https://v1.125.7.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.125.6",
|
||||
"url": "https://v1.125.6.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.125.5",
|
||||
"url": "https://v1.125.5.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.125.3",
|
||||
"url": "https://v1.125.3.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.125.2",
|
||||
"url": "https://v1.125.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.125.1",
|
||||
"url": "https://v1.125.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.124.2",
|
||||
"url": "https://v1.124.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.124.1",
|
||||
"url": "https://v1.124.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.124.0",
|
||||
"url": "https://v1.124.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.123.0",
|
||||
"url": "https://v1.123.0.archive.immich.app"
|
||||
@@ -75,18 +43,6 @@
|
||||
"label": "v1.122.3",
|
||||
"url": "https://v1.122.3.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.122.2",
|
||||
"url": "https://v1.122.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.122.1",
|
||||
"url": "https://v1.122.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.122.0",
|
||||
"url": "https://v1.122.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.121.0",
|
||||
"url": "https://v1.121.0.archive.immich.app"
|
||||
@@ -95,34 +51,14 @@
|
||||
"label": "v1.120.2",
|
||||
"url": "https://v1.120.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.120.1",
|
||||
"url": "https://v1.120.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.120.0",
|
||||
"url": "https://v1.120.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.119.1",
|
||||
"url": "https://v1.119.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.119.0",
|
||||
"url": "https://v1.119.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.118.2",
|
||||
"url": "https://v1.118.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.118.1",
|
||||
"url": "https://v1.118.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.118.0",
|
||||
"url": "https://v1.118.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.117.0",
|
||||
"url": "https://v1.117.0.archive.immich.app"
|
||||
@@ -131,14 +67,6 @@
|
||||
"label": "v1.116.2",
|
||||
"url": "https://v1.116.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.116.1",
|
||||
"url": "https://v1.116.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.116.0",
|
||||
"url": "https://v1.116.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.115.0",
|
||||
"url": "https://v1.115.0.archive.immich.app"
|
||||
@@ -151,18 +79,10 @@
|
||||
"label": "v1.113.1",
|
||||
"url": "https://v1.113.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.113.0",
|
||||
"url": "https://v1.113.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.112.1",
|
||||
"url": "https://v1.112.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.112.0",
|
||||
"url": "https://v1.112.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.111.0",
|
||||
"url": "https://v1.111.0.archive.immich.app"
|
||||
@@ -175,14 +95,6 @@
|
||||
"label": "v1.109.2",
|
||||
"url": "https://v1.109.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.109.1",
|
||||
"url": "https://v1.109.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.109.0",
|
||||
"url": "https://v1.109.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.108.0",
|
||||
"url": "https://v1.108.0.archive.immich.app"
|
||||
@@ -191,38 +103,14 @@
|
||||
"label": "v1.107.2",
|
||||
"url": "https://v1.107.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.107.1",
|
||||
"url": "https://v1.107.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.107.0",
|
||||
"url": "https://v1.107.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.106.4",
|
||||
"url": "https://v1.106.4.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.106.3",
|
||||
"url": "https://v1.106.3.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.106.2",
|
||||
"url": "https://v1.106.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.106.1",
|
||||
"url": "https://v1.106.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.105.1",
|
||||
"url": "https://v1.105.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.105.0",
|
||||
"url": "https://v1.105.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.104.0",
|
||||
"url": "https://v1.104.0.archive.immich.app"
|
||||
@@ -231,26 +119,10 @@
|
||||
"label": "v1.103.1",
|
||||
"url": "https://v1.103.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.103.0",
|
||||
"url": "https://v1.103.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.102.3",
|
||||
"url": "https://v1.102.3.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.102.2",
|
||||
"url": "https://v1.102.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.102.1",
|
||||
"url": "https://v1.102.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.102.0",
|
||||
"url": "https://v1.102.0.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.101.0",
|
||||
"url": "https://v1.101.0.archive.immich.app"
|
||||
|
||||
BIN
docs/static/fonts/overpass/Overpass-Italic.ttf
vendored
Normal file
BIN
docs/static/fonts/overpass/Overpass.ttf
vendored
Normal file
BIN
docs/static/fonts/overpass/OverpassMono.ttf
vendored
Normal file
13
docs/static/img/download-apk-github.svg
vendored
Normal file
|
After Width: | Height: | Size: 14 KiB |
22
docs/static/img/logomark-dark-with-futo.svg
vendored
Normal file
|
After Width: | Height: | Size: 75 KiB |
21
docs/static/img/logomark-light-with-futo.svg
vendored
Normal file
|
After Width: | Height: | Size: 75 KiB |
@@ -1 +1 @@
|
||||
22.14.0
|
||||
22.15.0
|
||||
|
||||
@@ -34,7 +34,7 @@ services:
|
||||
- 2285:2285
|
||||
|
||||
redis:
|
||||
image: redis:6.2-alpine@sha256:148bb5411c184abd288d9aaed139c98123eeb8824c5d3fce03cf721db58066d8
|
||||
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
||||
|
||||
database:
|
||||
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52
|
||||
|
||||
@@ -1,39 +1,29 @@
|
||||
import { FlatCompat } from '@eslint/eslintrc';
|
||||
import js from '@eslint/js';
|
||||
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
||||
import globals from 'globals';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import typescriptEslint from 'typescript-eslint';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default [
|
||||
export default typescriptEslint.config([
|
||||
eslintPluginUnicorn.configs.recommended,
|
||||
eslintPluginPrettierRecommended,
|
||||
js.configs.recommended,
|
||||
typescriptEslint.configs.recommended,
|
||||
{
|
||||
ignores: ['eslint.config.mjs'],
|
||||
},
|
||||
...compat.extends(
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:unicorn/recommended',
|
||||
),
|
||||
{
|
||||
plugins: {
|
||||
'@typescript-eslint': typescriptEslint,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
|
||||
parser: tsParser,
|
||||
parser: typescriptEslint.parser,
|
||||
ecmaVersion: 5,
|
||||
sourceType: 'module',
|
||||
|
||||
@@ -62,4 +52,4 @@ export default [
|
||||
'object-shorthand': ['error', 'always'],
|
||||
},
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
4024
e2e/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "immich-e2e",
|
||||
"version": "1.130.2",
|
||||
"version": "1.132.3",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
@@ -25,18 +25,16 @@
|
||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/node": "^22.15.16",
|
||||
"@types/oidc-provider": "^8.5.1",
|
||||
"@types/pg": "^8.11.0",
|
||||
"@types/pg": "^8.15.1",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
||||
"@typescript-eslint/parser": "^8.15.0",
|
||||
"@vitest/coverage-v8": "^3.0.0",
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-unicorn": "^56.0.1",
|
||||
"eslint-plugin-unicorn": "^57.0.0",
|
||||
"exiftool-vendored": "^28.3.1",
|
||||
"globals": "^16.0.0",
|
||||
"jose": "^5.6.3",
|
||||
@@ -49,10 +47,11 @@
|
||||
"socket.io-client": "^4.7.4",
|
||||
"supertest": "^7.0.0",
|
||||
"typescript": "^5.3.3",
|
||||
"typescript-eslint": "^8.28.0",
|
||||
"utimes": "^5.2.1",
|
||||
"vitest": "^3.0.0"
|
||||
},
|
||||
"volta": {
|
||||
"node": "22.14.0"
|
||||
"node": "22.15.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,38 +46,6 @@ describe('/activities', () => {
|
||||
});
|
||||
|
||||
describe('GET /activities', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get('/activities');
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require an albumId', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/activities')
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toEqual(400);
|
||||
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
|
||||
});
|
||||
|
||||
it('should reject an invalid albumId', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/activities')
|
||||
.query({ albumId: uuidDto.invalid })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toEqual(400);
|
||||
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
|
||||
});
|
||||
|
||||
it('should reject an invalid assetId', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/activities')
|
||||
.query({ albumId: uuidDto.notFound, assetId: uuidDto.invalid })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toEqual(400);
|
||||
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['assetId must be a UUID'])));
|
||||
});
|
||||
|
||||
it('should start off empty', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/activities')
|
||||
@@ -192,30 +160,6 @@ describe('/activities', () => {
|
||||
});
|
||||
|
||||
describe('POST /activities', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).post('/activities');
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require an albumId', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/activities')
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||
.send({ albumId: uuidDto.invalid });
|
||||
expect(status).toEqual(400);
|
||||
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
|
||||
});
|
||||
|
||||
it('should require a comment when type is comment', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/activities')
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||
.send({ albumId: uuidDto.notFound, type: 'comment', comment: null });
|
||||
expect(status).toEqual(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['comment must be a string', 'comment should not be empty']));
|
||||
});
|
||||
|
||||
it('should add a comment to an album', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/activities')
|
||||
@@ -330,20 +274,6 @@ describe('/activities', () => {
|
||||
});
|
||||
|
||||
describe('DELETE /activities/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).delete(`/activities/${uuidDto.notFound}`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require a valid uuid', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/activities/${uuidDto.invalid}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||
});
|
||||
|
||||
it('should remove a comment from an album', async () => {
|
||||
const reaction = await createActivity({
|
||||
albumId: album.id,
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
LoginResponseDto,
|
||||
SharedLinkType,
|
||||
} from '@immich/sdk';
|
||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||
import { createUserDto } from 'src/fixtures';
|
||||
import { errorDto } from 'src/responses';
|
||||
import { app, asBearerAuth, utils } from 'src/utils';
|
||||
import request from 'supertest';
|
||||
@@ -128,28 +128,6 @@ describe('/albums', () => {
|
||||
});
|
||||
|
||||
describe('GET /albums', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get('/albums');
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should reject an invalid shared param', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/albums?shared=invalid')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toEqual(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['shared must be a boolean value']));
|
||||
});
|
||||
|
||||
it('should reject an invalid assetId param', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/albums?assetId=invalid')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toEqual(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['assetId must be a UUID']));
|
||||
});
|
||||
|
||||
it("should not show other users' favorites", async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
||||
@@ -323,12 +301,6 @@ describe('/albums', () => {
|
||||
});
|
||||
|
||||
describe('GET /albums/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get(`/albums/${user1Albums[0].id}`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should return album info for own album', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
|
||||
@@ -421,12 +393,6 @@ describe('/albums', () => {
|
||||
});
|
||||
|
||||
describe('GET /albums/statistics', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get('/albums/statistics');
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should return total count of albums the user has access to', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/albums/statistics')
|
||||
@@ -438,12 +404,6 @@ describe('/albums', () => {
|
||||
});
|
||||
|
||||
describe('POST /albums', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).post('/albums').send({ albumName: 'New album' });
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should create an album', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/albums')
|
||||
@@ -471,12 +431,6 @@ describe('/albums', () => {
|
||||
});
|
||||
|
||||
describe('PUT /albums/:id/assets', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/assets`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should be able to add own asset to own album', async () => {
|
||||
const asset = await utils.createAsset(user1.accessToken);
|
||||
const { status, body } = await request(app)
|
||||
@@ -526,14 +480,6 @@ describe('/albums', () => {
|
||||
});
|
||||
|
||||
describe('PATCH /albums/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.patch(`/albums/${uuidDto.notFound}`)
|
||||
.send({ albumName: 'New album name' });
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should update an album', async () => {
|
||||
const album = await utils.createAlbum(user1.accessToken, {
|
||||
albumName: 'New album',
|
||||
@@ -576,15 +522,6 @@ describe('/albums', () => {
|
||||
});
|
||||
|
||||
describe('DELETE /albums/:id/assets', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/albums/${user1Albums[0].id}/assets`)
|
||||
.send({ ids: [user1Asset1.id] });
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/albums/${user1Albums[1].id}/assets`)
|
||||
@@ -679,13 +616,6 @@ describe('/albums', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/users`).send({ sharedUserIds: [] });
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should be able to add user to own album', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/albums/${album.id}/users`)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LoginResponseDto, Permission, createApiKey } from '@immich/sdk';
|
||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||
import { createUserDto } from 'src/fixtures';
|
||||
import { errorDto } from 'src/responses';
|
||||
import { app, asBearerAuth, utils } from 'src/utils';
|
||||
import request from 'supertest';
|
||||
@@ -24,12 +24,6 @@ describe('/api-keys', () => {
|
||||
});
|
||||
|
||||
describe('POST /api-keys', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).post('/api-keys').send({ name: 'API Key' });
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should not work without permission', async () => {
|
||||
const { secret } = await create(user.accessToken, [Permission.ApiKeyRead]);
|
||||
const { status, body } = await request(app).post('/api-keys').set('x-api-key', secret).send({ name: 'API Key' });
|
||||
@@ -99,12 +93,6 @@ describe('/api-keys', () => {
|
||||
});
|
||||
|
||||
describe('GET /api-keys', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get('/api-keys');
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should start off empty', async () => {
|
||||
const { status, body } = await request(app).get('/api-keys').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(body).toEqual([]);
|
||||
@@ -125,12 +113,6 @@ describe('/api-keys', () => {
|
||||
});
|
||||
|
||||
describe('GET /api-keys/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get(`/api-keys/${uuidDto.notFound}`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||
const { status, body } = await request(app)
|
||||
@@ -140,14 +122,6 @@ describe('/api-keys', () => {
|
||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||
});
|
||||
|
||||
it('should require a valid uuid', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/api-keys/${uuidDto.invalid}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||
});
|
||||
|
||||
it('should get api key details', async () => {
|
||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||
const { status, body } = await request(app)
|
||||
@@ -165,12 +139,6 @@ describe('/api-keys', () => {
|
||||
});
|
||||
|
||||
describe('PUT /api-keys/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).put(`/api-keys/${uuidDto.notFound}`).send({ name: 'new name' });
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||
const { status, body } = await request(app)
|
||||
@@ -181,15 +149,6 @@ describe('/api-keys', () => {
|
||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||
});
|
||||
|
||||
it('should require a valid uuid', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/api-keys/${uuidDto.invalid}`)
|
||||
.send({ name: 'new name' })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||
});
|
||||
|
||||
it('should update api key details', async () => {
|
||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||
const { status, body } = await request(app)
|
||||
@@ -208,12 +167,6 @@ describe('/api-keys', () => {
|
||||
});
|
||||
|
||||
describe('DELETE /api-keys/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).delete(`/api-keys/${uuidDto.notFound}`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||
const { status, body } = await request(app)
|
||||
@@ -223,14 +176,6 @@ describe('/api-keys', () => {
|
||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||
});
|
||||
|
||||
it('should require a valid uuid', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/api-keys/${uuidDto.invalid}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||
});
|
||||
|
||||
it('should delete an api key', async () => {
|
||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||
const { status } = await request(app)
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
AssetMediaStatus,
|
||||
AssetResponseDto,
|
||||
AssetTypeEnum,
|
||||
AssetVisibility,
|
||||
getAssetInfo,
|
||||
getMyUser,
|
||||
LoginResponseDto,
|
||||
@@ -22,27 +23,9 @@ import { app, asBearerAuth, tempDir, TEN_TIMES, testAssetDir, utils } from 'src/
|
||||
import request from 'supertest';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
|
||||
const makeUploadDto = (options?: { omit: string }): Record<string, any> => {
|
||||
const dto: Record<string, any> = {
|
||||
deviceAssetId: 'example-image',
|
||||
deviceId: 'TEST',
|
||||
fileCreatedAt: new Date().toISOString(),
|
||||
fileModifiedAt: new Date().toISOString(),
|
||||
isFavorite: 'testing',
|
||||
duration: '0:00:00.000000',
|
||||
};
|
||||
|
||||
const omit = options?.omit;
|
||||
if (omit) {
|
||||
delete dto[omit];
|
||||
}
|
||||
|
||||
return dto;
|
||||
};
|
||||
|
||||
const locationAssetFilepath = `${testAssetDir}/metadata/gps-position/thompson-springs.jpg`;
|
||||
const ratingAssetFilepath = `${testAssetDir}/metadata/rating/mongolels.jpg`;
|
||||
const facesAssetFilepath = `${testAssetDir}/metadata/faces/portrait.jpg`;
|
||||
const facesAssetDir = `${testAssetDir}/metadata/faces`;
|
||||
|
||||
const readTags = async (bytes: Buffer, filename: string) => {
|
||||
const filepath = join(tempDir, filename);
|
||||
@@ -137,9 +120,9 @@ describe('/asset', () => {
|
||||
// stats
|
||||
utils.createAsset(statsUser.accessToken),
|
||||
utils.createAsset(statsUser.accessToken, { isFavorite: true }),
|
||||
utils.createAsset(statsUser.accessToken, { isArchived: true }),
|
||||
utils.createAsset(statsUser.accessToken, { visibility: AssetVisibility.Archive }),
|
||||
utils.createAsset(statsUser.accessToken, {
|
||||
isArchived: true,
|
||||
visibility: AssetVisibility.Archive,
|
||||
isFavorite: true,
|
||||
assetData: { filename: 'example.mp4' },
|
||||
}),
|
||||
@@ -160,13 +143,6 @@ describe('/asset', () => {
|
||||
});
|
||||
|
||||
describe('GET /assets/:id/original', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get(`/assets/${uuidDto.notFound}/original`);
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should download the file', async () => {
|
||||
const response = await request(app)
|
||||
.get(`/assets/${user1Assets[0].id}/original`)
|
||||
@@ -178,20 +154,6 @@ describe('/asset', () => {
|
||||
});
|
||||
|
||||
describe('GET /assets/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get(`/assets/${uuidDto.notFound}`);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
expect(status).toBe(401);
|
||||
});
|
||||
|
||||
it('should require a valid id', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/assets/${uuidDto.invalid}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||
});
|
||||
|
||||
it('should require access', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/assets/${user2Assets[0].id}`)
|
||||
@@ -224,31 +186,22 @@ describe('/asset', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should get the asset faces', async () => {
|
||||
const config = await utils.getSystemConfig(admin.accessToken);
|
||||
config.metadata.faces.import = true;
|
||||
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
||||
|
||||
// asset faces
|
||||
const facesAsset = await utils.createAsset(admin.accessToken, {
|
||||
assetData: {
|
||||
describe('faces', () => {
|
||||
const metadataFaceTests = [
|
||||
{
|
||||
description: 'without orientation',
|
||||
filename: 'portrait.jpg',
|
||||
bytes: await readFile(facesAssetFilepath),
|
||||
},
|
||||
});
|
||||
|
||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id });
|
||||
|
||||
const { status, body } = await request(app)
|
||||
.get(`/assets/${facesAsset.id}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(200);
|
||||
expect(body.id).toEqual(facesAsset.id);
|
||||
expect(body.people).toMatchObject([
|
||||
{
|
||||
description: 'adjusting face regions to orientation',
|
||||
filename: 'portrait-orientation-6.jpg',
|
||||
},
|
||||
];
|
||||
// should produce same resulting face region coordinates for any orientation
|
||||
const expectedFaces = [
|
||||
{
|
||||
name: 'Marie Curie',
|
||||
birthDate: null,
|
||||
thumbnailPath: '',
|
||||
isHidden: false,
|
||||
faces: [
|
||||
{
|
||||
@@ -265,7 +218,6 @@ describe('/asset', () => {
|
||||
{
|
||||
name: 'Pierre Curie',
|
||||
birthDate: null,
|
||||
thumbnailPath: '',
|
||||
isHidden: false,
|
||||
faces: [
|
||||
{
|
||||
@@ -279,7 +231,30 @@ describe('/asset', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
it.each(metadataFaceTests)('should get the asset faces from $filename $description', async ({ filename }) => {
|
||||
const config = await utils.getSystemConfig(admin.accessToken);
|
||||
config.metadata.faces.import = true;
|
||||
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
|
||||
|
||||
const facesAsset = await utils.createAsset(admin.accessToken, {
|
||||
assetData: {
|
||||
filename,
|
||||
bytes: await readFile(`${facesAssetDir}/${filename}`),
|
||||
},
|
||||
});
|
||||
|
||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: facesAsset.id });
|
||||
|
||||
const { status, body } = await request(app)
|
||||
.get(`/assets/${facesAsset.id}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body.id).toEqual(facesAsset.id);
|
||||
expect(body.people).toMatchObject(expectedFaces);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with a shared link', async () => {
|
||||
@@ -333,7 +308,7 @@ describe('/asset', () => {
|
||||
});
|
||||
|
||||
it('disallows viewing archived assets', async () => {
|
||||
const asset = await utils.createAsset(user1.accessToken, { isArchived: true });
|
||||
const asset = await utils.createAsset(user1.accessToken, { visibility: AssetVisibility.Archive });
|
||||
|
||||
const { status } = await request(app)
|
||||
.get(`/assets/${asset.id}`)
|
||||
@@ -354,13 +329,6 @@ describe('/asset', () => {
|
||||
});
|
||||
|
||||
describe('GET /assets/statistics', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get('/assets/statistics');
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should return stats of all assets', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/assets/statistics')
|
||||
@@ -384,7 +352,7 @@ describe('/asset', () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/assets/statistics')
|
||||
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
||||
.query({ isArchived: true });
|
||||
.query({ visibility: AssetVisibility.Archive });
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({ images: 1, videos: 1, total: 2 });
|
||||
@@ -394,7 +362,7 @@ describe('/asset', () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/assets/statistics')
|
||||
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
||||
.query({ isFavorite: true, isArchived: true });
|
||||
.query({ isFavorite: true, visibility: AssetVisibility.Archive });
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({ images: 0, videos: 1, total: 1 });
|
||||
@@ -404,7 +372,7 @@ describe('/asset', () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/assets/statistics')
|
||||
.set('Authorization', `Bearer ${statsUser.accessToken}`)
|
||||
.query({ isFavorite: false, isArchived: false });
|
||||
.query({ isFavorite: false, visibility: AssetVisibility.Timeline });
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({ images: 1, videos: 0, total: 1 });
|
||||
@@ -425,13 +393,6 @@ describe('/asset', () => {
|
||||
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get('/assets/random');
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it.each(TEN_TIMES)('should return 1 random assets', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/assets/random')
|
||||
@@ -467,31 +428,9 @@ describe('/asset', () => {
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual([expect.objectContaining({ id: user2Assets[0].id })]);
|
||||
});
|
||||
|
||||
it('should return error', async () => {
|
||||
const { status } = await request(app)
|
||||
.get('/assets/random?count=ABC')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
|
||||
expect(status).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /assets/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).put(`/assets/:${uuidDto.notFound}`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require a valid id', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/assets/${uuidDto.invalid}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
|
||||
});
|
||||
|
||||
it('should require access', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/assets/${user2Assets[0].id}`)
|
||||
@@ -519,7 +458,7 @@ describe('/asset', () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/assets/${user1Assets[0].id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ isArchived: true });
|
||||
.send({ visibility: AssetVisibility.Archive });
|
||||
expect(body).toMatchObject({ id: user1Assets[0].id, isArchived: true });
|
||||
expect(status).toEqual(200);
|
||||
});
|
||||
@@ -619,28 +558,6 @@ describe('/asset', () => {
|
||||
expect(status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should reject invalid gps coordinates', async () => {
|
||||
for (const test of [
|
||||
{ latitude: 12 },
|
||||
{ longitude: 12 },
|
||||
{ latitude: 12, longitude: 'abc' },
|
||||
{ latitude: 'abc', longitude: 12 },
|
||||
{ latitude: null, longitude: 12 },
|
||||
{ latitude: 12, longitude: null },
|
||||
{ latitude: 91, longitude: 12 },
|
||||
{ latitude: -91, longitude: 12 },
|
||||
{ latitude: 12, longitude: -181 },
|
||||
{ latitude: 12, longitude: 181 },
|
||||
]) {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/assets/${user1Assets[0].id}`)
|
||||
.send(test)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest());
|
||||
}
|
||||
});
|
||||
|
||||
it('should update gps data', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/assets/${user1Assets[0].id}`)
|
||||
@@ -712,17 +629,6 @@ describe('/asset', () => {
|
||||
expect(status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should reject invalid rating', async () => {
|
||||
for (const test of [{ rating: 7 }, { rating: 3.5 }, { rating: null }]) {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/assets/${user1Assets[0].id}`)
|
||||
.send(test)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest());
|
||||
}
|
||||
});
|
||||
|
||||
it('should return tagged people', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/assets/${user1Assets[0].id}`)
|
||||
@@ -746,25 +652,6 @@ describe('/asset', () => {
|
||||
});
|
||||
|
||||
describe('DELETE /assets', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/assets`)
|
||||
.send({ ids: [uuidDto.notFound] });
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require a valid uuid', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/assets`)
|
||||
.send({ ids: [uuidDto.invalid] })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['each value in ids must be a UUID']));
|
||||
});
|
||||
|
||||
it('should throw an error when the id is not found', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/assets`)
|
||||
@@ -877,13 +764,6 @@ describe('/asset', () => {
|
||||
});
|
||||
|
||||
describe('GET /assets/:id/thumbnail', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get(`/assets/${locationAsset.id}/thumbnail`);
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should not include gps data for webp thumbnails', async () => {
|
||||
await utils.waitForWebsocketEvent({
|
||||
event: 'assetUpload',
|
||||
@@ -919,13 +799,6 @@ describe('/asset', () => {
|
||||
});
|
||||
|
||||
describe('GET /assets/:id/original', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get(`/assets/${locationAsset.id}/original`);
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should download the original', async () => {
|
||||
const { status, body, type } = await request(app)
|
||||
.get(`/assets/${locationAsset.id}/original`)
|
||||
@@ -946,43 +819,9 @@ describe('/asset', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /assets', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).put('/assets');
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /assets', () => {
|
||||
beforeAll(setupTests, 30_000);
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).post(`/assets`);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
expect(status).toBe(401);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ should: 'require `deviceAssetId`', dto: { ...makeUploadDto({ omit: 'deviceAssetId' }) } },
|
||||
{ should: 'require `deviceId`', dto: { ...makeUploadDto({ omit: 'deviceId' }) } },
|
||||
{ should: 'require `fileCreatedAt`', dto: { ...makeUploadDto({ omit: 'fileCreatedAt' }) } },
|
||||
{ should: 'require `fileModifiedAt`', dto: { ...makeUploadDto({ omit: 'fileModifiedAt' }) } },
|
||||
{ should: 'require `duration`', dto: { ...makeUploadDto({ omit: 'duration' }) } },
|
||||
{ should: 'throw if `isFavorite` is not a boolean', dto: { ...makeUploadDto(), isFavorite: 'not-a-boolean' } },
|
||||
{ should: 'throw if `isVisible` is not a boolean', dto: { ...makeUploadDto(), isVisible: 'not-a-boolean' } },
|
||||
{ should: 'throw if `isArchived` is not a boolean', dto: { ...makeUploadDto(), isArchived: 'not-a-boolean' } },
|
||||
])('should $should', async ({ dto }) => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/assets')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.attach('assetData', makeRandomImage(), 'example.png')
|
||||
.field(dto);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest());
|
||||
});
|
||||
|
||||
const tests = [
|
||||
{
|
||||
input: 'formats/avif/8bit-sRGB.avif',
|
||||
@@ -1141,7 +980,7 @@ describe('/asset', () => {
|
||||
fNumber: 8,
|
||||
focalLength: 97,
|
||||
iso: 100,
|
||||
lensModel: 'E PZ 18-105mm F4 G OSS',
|
||||
lensModel: 'Sony E PZ 18-105mm F4 G OSS',
|
||||
fileSizeInByte: 25_001_984,
|
||||
dateTimeOriginal: '2016-09-27T10:51:44+00:00',
|
||||
orientation: '1',
|
||||
@@ -1163,7 +1002,7 @@ describe('/asset', () => {
|
||||
fNumber: 22,
|
||||
focalLength: 25,
|
||||
iso: 100,
|
||||
lensModel: 'E 25mm F2',
|
||||
lensModel: 'Zeiss Batis 25mm F2',
|
||||
fileSizeInByte: 49_512_448,
|
||||
dateTimeOriginal: '2016-01-08T14:08:01+00:00',
|
||||
orientation: '1',
|
||||
@@ -1234,7 +1073,7 @@ describe('/asset', () => {
|
||||
focalLength: 18.3,
|
||||
iso: 100,
|
||||
latitude: 36.613_24,
|
||||
lensModel: 'GR LENS 18.3mm F2.8',
|
||||
lensModel: '18.3mm F2.8',
|
||||
longitude: -121.897_85,
|
||||
make: 'RICOH IMAGING COMPANY, LTD.',
|
||||
model: 'RICOH GR III',
|
||||
@@ -1244,30 +1083,21 @@ describe('/asset', () => {
|
||||
},
|
||||
];
|
||||
|
||||
it(`should upload and generate a thumbnail for different file types`, async () => {
|
||||
// upload in parallel
|
||||
const assets = await Promise.all(
|
||||
tests.map(async ({ input }) => {
|
||||
const filepath = join(testAssetDir, input);
|
||||
return utils.createAsset(admin.accessToken, {
|
||||
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
||||
});
|
||||
}),
|
||||
);
|
||||
it.each(tests)(`should upload and generate a thumbnail for different file types`, async ({ input, expected }) => {
|
||||
const filepath = join(testAssetDir, input);
|
||||
const response = await utils.createAsset(admin.accessToken, {
|
||||
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
||||
});
|
||||
|
||||
for (const { id, status } of assets) {
|
||||
expect(status).toBe(AssetMediaStatus.Created);
|
||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id });
|
||||
}
|
||||
expect(response.status).toBe(AssetMediaStatus.Created);
|
||||
const id = response.id;
|
||||
// longer timeout as the thumbnail generation from full-size raw files can take a while
|
||||
await utils.waitForWebsocketEvent({ event: 'assetUpload', id });
|
||||
|
||||
for (const [i, { id }] of assets.entries()) {
|
||||
const { expected } = tests[i];
|
||||
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||
|
||||
expect(asset.exifInfo).toBeDefined();
|
||||
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
|
||||
expect(asset).toMatchObject(expected);
|
||||
}
|
||||
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||
expect(asset.exifInfo).toBeDefined();
|
||||
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
|
||||
expect(asset).toMatchObject(expected);
|
||||
});
|
||||
|
||||
it('should handle a duplicate', async () => {
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { deleteAssets, getAuditFiles, updateAsset, type LoginResponseDto } from '@immich/sdk';
|
||||
import { asBearerAuth, utils } from 'src/utils';
|
||||
import { beforeAll, describe, expect, it } from 'vitest';
|
||||
|
||||
describe('/audits', () => {
|
||||
let admin: LoginResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
await utils.resetDatabase();
|
||||
await utils.resetFilesystem();
|
||||
|
||||
admin = await utils.adminSetup();
|
||||
});
|
||||
|
||||
// TODO: Enable these tests again once #7436 is resolved as these were flaky
|
||||
describe.skip('GET :/file-report', () => {
|
||||
it('excludes assets without issues from report', async () => {
|
||||
const [trashedAsset, archivedAsset] = await Promise.all([
|
||||
utils.createAsset(admin.accessToken),
|
||||
utils.createAsset(admin.accessToken),
|
||||
utils.createAsset(admin.accessToken),
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
deleteAssets({ assetBulkDeleteDto: { ids: [trashedAsset.id] } }, { headers: asBearerAuth(admin.accessToken) }),
|
||||
updateAsset(
|
||||
{
|
||||
id: archivedAsset.id,
|
||||
updateAssetDto: { isArchived: true },
|
||||
},
|
||||
{ headers: asBearerAuth(admin.accessToken) },
|
||||
),
|
||||
]);
|
||||
|
||||
const body = await getAuditFiles({
|
||||
headers: asBearerAuth(admin.accessToken),
|
||||
});
|
||||
|
||||
expect(body.orphans).toHaveLength(0);
|
||||
expect(body.extras).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import { app, utils } from 'src/utils';
|
||||
import request from 'supertest';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
const { name, email, password } = signupDto.admin;
|
||||
const { email, password } = signupDto.admin;
|
||||
|
||||
describe(`/auth/admin-sign-up`, () => {
|
||||
beforeEach(async () => {
|
||||
@@ -13,58 +13,12 @@ describe(`/auth/admin-sign-up`, () => {
|
||||
});
|
||||
|
||||
describe('POST /auth/admin-sign-up', () => {
|
||||
const invalid = [
|
||||
{
|
||||
should: 'require an email address',
|
||||
data: { name, password },
|
||||
},
|
||||
{
|
||||
should: 'require a password',
|
||||
data: { name, email },
|
||||
},
|
||||
{
|
||||
should: 'require a name',
|
||||
data: { email, password },
|
||||
},
|
||||
{
|
||||
should: 'require a valid email',
|
||||
data: { name, email: 'immich', password },
|
||||
},
|
||||
];
|
||||
|
||||
for (const { should, data } of invalid) {
|
||||
it(`should ${should}`, async () => {
|
||||
const { status, body } = await request(app).post('/auth/admin-sign-up').send(data);
|
||||
expect(status).toEqual(400);
|
||||
expect(body).toEqual(errorDto.badRequest());
|
||||
});
|
||||
}
|
||||
|
||||
it(`should sign up the admin`, async () => {
|
||||
const { status, body } = await request(app).post('/auth/admin-sign-up').send(signupDto.admin);
|
||||
expect(status).toBe(201);
|
||||
expect(body).toEqual(signupResponseDto.admin);
|
||||
});
|
||||
|
||||
it('should sign up the admin with a local domain', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/auth/admin-sign-up')
|
||||
.send({ ...signupDto.admin, email: 'admin@local' });
|
||||
expect(status).toEqual(201);
|
||||
expect(body).toEqual({
|
||||
...signupResponseDto.admin,
|
||||
email: 'admin@local',
|
||||
});
|
||||
});
|
||||
|
||||
it('should transform email to lower case', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/auth/admin-sign-up')
|
||||
.send({ ...signupDto.admin, email: 'aDmIn@IMMICH.cloud' });
|
||||
expect(status).toEqual(201);
|
||||
expect(body).toEqual(signupResponseDto.admin);
|
||||
});
|
||||
|
||||
it('should not allow a second admin to sign up', async () => {
|
||||
await signUpAdmin({ signUpDto: signupDto.admin });
|
||||
|
||||
@@ -92,22 +46,6 @@ describe('/auth/*', () => {
|
||||
expect(body).toEqual(errorDto.incorrectLogin);
|
||||
});
|
||||
|
||||
for (const key of Object.keys(loginDto.admin)) {
|
||||
it(`should not allow null ${key}`, async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/auth/login')
|
||||
.send({ ...loginDto.admin, [key]: null });
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest());
|
||||
});
|
||||
|
||||
it('should reject an invalid email', async () => {
|
||||
const { status, body } = await request(app).post('/auth/login').send({ email: [], password });
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.invalidEmail);
|
||||
});
|
||||
}
|
||||
|
||||
it('should accept a correct password', async () => {
|
||||
const { status, body, headers } = await request(app).post('/auth/login').send({ email, password });
|
||||
expect(status).toBe(201);
|
||||
@@ -162,14 +100,6 @@ describe('/auth/*', () => {
|
||||
});
|
||||
|
||||
describe('POST /auth/change-password', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post(`/auth/change-password`)
|
||||
.send({ password, newPassword: 'Password1234' });
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should require the current password', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post(`/auth/change-password`)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { AssetMediaResponseDto, LoginResponseDto } from '@immich/sdk';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { errorDto } from 'src/responses';
|
||||
import { app, tempDir, utils } from 'src/utils';
|
||||
import request from 'supertest';
|
||||
import { beforeAll, describe, expect, it } from 'vitest';
|
||||
@@ -17,15 +16,6 @@ describe('/download', () => {
|
||||
});
|
||||
|
||||
describe('POST /download/info', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post(`/download/info`)
|
||||
.send({ assetIds: [asset1.id] });
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should download info', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/download/info')
|
||||
@@ -42,15 +32,6 @@ describe('/download', () => {
|
||||
});
|
||||
|
||||
describe('POST /download/archive', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post(`/download/archive`)
|
||||
.send({ assetIds: [asset1.id, asset2.id] });
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should download an archive', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/download/archive')
|
||||
|
||||
@@ -329,7 +329,7 @@ describe('/libraries', () => {
|
||||
const library = await utils.createLibrary(admin.accessToken, {
|
||||
ownerId: admin.userId,
|
||||
importPaths: [`${testAssetDirInternal}/temp`],
|
||||
exclusionPatterns: ['**/directoryA'],
|
||||
exclusionPatterns: ['**/directoryA/**'],
|
||||
});
|
||||
|
||||
await utils.scan(admin.accessToken, library.id);
|
||||
@@ -337,7 +337,82 @@ describe('/libraries', () => {
|
||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||
|
||||
expect(assets.count).toBe(1);
|
||||
expect(assets.items[0].originalPath.includes('directoryB'));
|
||||
|
||||
expect(assets.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ originalPath: expect.stringContaining('directoryB/assetB.png') }),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('should scan external library with multiple exclusion patterns', async () => {
|
||||
const library = await utils.createLibrary(admin.accessToken, {
|
||||
ownerId: admin.userId,
|
||||
importPaths: [`${testAssetDirInternal}/temp`],
|
||||
exclusionPatterns: ['**/directoryA/**', '**/directoryB/**'],
|
||||
});
|
||||
|
||||
await utils.scan(admin.accessToken, library.id);
|
||||
|
||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||
|
||||
expect(assets.count).toBe(0);
|
||||
|
||||
expect(assets.items).toEqual([]);
|
||||
});
|
||||
|
||||
it('should remove assets covered by a new exclusion pattern', async () => {
|
||||
const library = await utils.createLibrary(admin.accessToken, {
|
||||
ownerId: admin.userId,
|
||||
importPaths: [`${testAssetDirInternal}/temp`],
|
||||
});
|
||||
|
||||
await utils.scan(admin.accessToken, library.id);
|
||||
|
||||
{
|
||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||
|
||||
expect(assets.count).toBe(2);
|
||||
|
||||
expect(assets.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ originalPath: expect.stringContaining('directoryA/assetA.png') }),
|
||||
expect.objectContaining({ originalPath: expect.stringContaining('directoryB/assetB.png') }),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
await utils.updateLibrary(admin.accessToken, library.id, {
|
||||
exclusionPatterns: ['**/directoryA/**'],
|
||||
});
|
||||
|
||||
await utils.scan(admin.accessToken, library.id);
|
||||
|
||||
{
|
||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||
|
||||
expect(assets.count).toBe(1);
|
||||
|
||||
expect(assets.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ originalPath: expect.stringContaining('directoryB/assetB.png') }),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
await utils.updateLibrary(admin.accessToken, library.id, {
|
||||
exclusionPatterns: ['**/directoryA/**', '**/directoryB/**'],
|
||||
});
|
||||
|
||||
await utils.scan(admin.accessToken, library.id);
|
||||
|
||||
{
|
||||
const { assets } = await utils.searchAssets(admin.accessToken, { libraryId: library.id });
|
||||
|
||||
expect(assets.count).toBe(0);
|
||||
|
||||
expect(assets.items).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('should scan multiple import paths', async () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LoginResponseDto } from '@immich/sdk';
|
||||
import { AssetVisibility, LoginResponseDto } from '@immich/sdk';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { basename, join } from 'node:path';
|
||||
import { Socket } from 'socket.io-client';
|
||||
@@ -44,7 +44,7 @@ describe('/map', () => {
|
||||
it('should get map markers for all non-archived assets', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/map/markers')
|
||||
.query({ isArchived: false })
|
||||
.query({ visibility: AssetVisibility.Timeline })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
|
||||
expect(status).toBe(200);
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
startOAuth,
|
||||
updateConfig,
|
||||
} from '@immich/sdk';
|
||||
import { createHash, randomBytes } from 'node:crypto';
|
||||
import { errorDto } from 'src/responses';
|
||||
import { OAuthClient, OAuthUser } from 'src/setup/auth-server';
|
||||
import { app, asBearerAuth, baseUrl, utils } from 'src/utils';
|
||||
@@ -21,18 +22,30 @@ const mobileOverrideRedirectUri = 'https://photos.immich.app/oauth/mobile-redire
|
||||
|
||||
const redirect = async (url: string, cookies?: string[]) => {
|
||||
const { headers } = await request(url)
|
||||
.get('/')
|
||||
.get('')
|
||||
.set('Cookie', cookies || []);
|
||||
return { cookies: (headers['set-cookie'] as unknown as string[]) || [], location: headers.location };
|
||||
};
|
||||
|
||||
// Function to generate a code challenge from the verifier
|
||||
const generateCodeChallenge = async (codeVerifier: string): Promise<string> => {
|
||||
const hashed = createHash('sha256').update(codeVerifier).digest();
|
||||
return hashed.toString('base64url');
|
||||
};
|
||||
|
||||
const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => {
|
||||
const { url } = await startOAuth({ oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login` } });
|
||||
const state = randomBytes(16).toString('base64url');
|
||||
const codeVerifier = randomBytes(64).toString('base64url');
|
||||
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
||||
|
||||
const { url } = await startOAuth({
|
||||
oAuthConfigDto: { redirectUri: redirectUri ?? `${baseUrl}/auth/login`, state, codeChallenge },
|
||||
});
|
||||
|
||||
// login
|
||||
const response1 = await redirect(url.replace(authServer.internal, authServer.external));
|
||||
const response2 = await request(authServer.external + response1.location)
|
||||
.post('/')
|
||||
.post('')
|
||||
.set('Cookie', response1.cookies)
|
||||
.type('form')
|
||||
.send({ prompt: 'login', login: sub, password: 'password' });
|
||||
@@ -40,7 +53,7 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) =>
|
||||
// approve
|
||||
const response3 = await redirect(response2.header.location, response1.cookies);
|
||||
const response4 = await request(authServer.external + response3.location)
|
||||
.post('/')
|
||||
.post('')
|
||||
.type('form')
|
||||
.set('Cookie', response3.cookies)
|
||||
.send({ prompt: 'consent' });
|
||||
@@ -51,9 +64,9 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) =>
|
||||
expect(redirectUrl).toBeDefined();
|
||||
const params = new URL(redirectUrl).searchParams;
|
||||
expect(params.get('code')).toBeDefined();
|
||||
expect(params.get('state')).toBeDefined();
|
||||
expect(params.get('state')).toBe(state);
|
||||
|
||||
return redirectUrl;
|
||||
return { url: redirectUrl, state, codeVerifier };
|
||||
};
|
||||
|
||||
const setupOAuth = async (token: string, dto: Partial<SystemConfigOAuthDto>) => {
|
||||
@@ -119,9 +132,42 @@ describe(`/oauth`, () => {
|
||||
expect(body).toEqual(errorDto.badRequest(['url should not be empty']));
|
||||
});
|
||||
|
||||
it('should auto register the user by default', async () => {
|
||||
const url = await loginWithOAuth('oauth-auto-register');
|
||||
it(`should throw an error if the state is not provided`, async () => {
|
||||
const { url } = await loginWithOAuth('oauth-auto-register');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('OAuth state is missing'));
|
||||
});
|
||||
|
||||
it(`should throw an error if the state mismatches`, async () => {
|
||||
const callbackParams = await loginWithOAuth('oauth-auto-register');
|
||||
const { state } = await loginWithOAuth('oauth-auto-register');
|
||||
const { status } = await request(app)
|
||||
.post('/oauth/callback')
|
||||
.send({ ...callbackParams, state });
|
||||
expect(status).toBeGreaterThanOrEqual(400);
|
||||
});
|
||||
|
||||
it(`should throw an error if the codeVerifier is not provided`, async () => {
|
||||
const { url, state } = await loginWithOAuth('oauth-auto-register');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url, state });
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('OAuth code verifier is missing'));
|
||||
});
|
||||
|
||||
it(`should throw an error if the codeVerifier doesn't match the challenge`, async () => {
|
||||
const callbackParams = await loginWithOAuth('oauth-auto-register');
|
||||
const { codeVerifier } = await loginWithOAuth('oauth-auto-register');
|
||||
const { status, body } = await request(app)
|
||||
.post('/oauth/callback')
|
||||
.send({ ...callbackParams, codeVerifier });
|
||||
console.log(body);
|
||||
expect(status).toBeGreaterThanOrEqual(400);
|
||||
});
|
||||
|
||||
it('should auto register the user by default', async () => {
|
||||
const callbackParams = await loginWithOAuth('oauth-auto-register');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(201);
|
||||
expect(body).toMatchObject({
|
||||
accessToken: expect.any(String),
|
||||
@@ -132,16 +178,30 @@ describe(`/oauth`, () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow passing state and codeVerifier via cookies', async () => {
|
||||
const { url, state, codeVerifier } = await loginWithOAuth('oauth-auto-register');
|
||||
const { status, body } = await request(app)
|
||||
.post('/oauth/callback')
|
||||
.set('Cookie', [`immich_oauth_state=${state}`, `immich_oauth_code_verifier=${codeVerifier}`])
|
||||
.send({ url });
|
||||
expect(status).toBe(201);
|
||||
expect(body).toMatchObject({
|
||||
accessToken: expect.any(String),
|
||||
userId: expect.any(String),
|
||||
userEmail: 'oauth-auto-register@immich.app',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle a user without an email', async () => {
|
||||
const url = await loginWithOAuth(OAuthUser.NO_EMAIL);
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||
const callbackParams = await loginWithOAuth(OAuthUser.NO_EMAIL);
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('OAuth profile does not have an email address'));
|
||||
});
|
||||
|
||||
it('should set the quota from a claim', async () => {
|
||||
const url = await loginWithOAuth(OAuthUser.WITH_QUOTA);
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||
const callbackParams = await loginWithOAuth(OAuthUser.WITH_QUOTA);
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(201);
|
||||
expect(body).toMatchObject({
|
||||
accessToken: expect.any(String),
|
||||
@@ -154,8 +214,8 @@ describe(`/oauth`, () => {
|
||||
});
|
||||
|
||||
it('should set the storage label from a claim', async () => {
|
||||
const url = await loginWithOAuth(OAuthUser.WITH_USERNAME);
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||
const callbackParams = await loginWithOAuth(OAuthUser.WITH_USERNAME);
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(201);
|
||||
expect(body).toMatchObject({
|
||||
accessToken: expect.any(String),
|
||||
@@ -176,8 +236,8 @@ describe(`/oauth`, () => {
|
||||
buttonText: 'Login with Immich',
|
||||
signingAlgorithm: 'RS256',
|
||||
});
|
||||
const url = await loginWithOAuth('oauth-RS256-token');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||
const callbackParams = await loginWithOAuth('oauth-RS256-token');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(201);
|
||||
expect(body).toMatchObject({
|
||||
accessToken: expect.any(String),
|
||||
@@ -196,8 +256,8 @@ describe(`/oauth`, () => {
|
||||
buttonText: 'Login with Immich',
|
||||
profileSigningAlgorithm: 'RS256',
|
||||
});
|
||||
const url = await loginWithOAuth('oauth-signed-profile');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||
const callbackParams = await loginWithOAuth('oauth-signed-profile');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(201);
|
||||
expect(body).toMatchObject({
|
||||
userId: expect.any(String),
|
||||
@@ -213,8 +273,8 @@ describe(`/oauth`, () => {
|
||||
buttonText: 'Login with Immich',
|
||||
signingAlgorithm: 'something-that-does-not-work',
|
||||
});
|
||||
const url = await loginWithOAuth('oauth-signed-bad');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||
const callbackParams = await loginWithOAuth('oauth-signed-bad');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(500);
|
||||
expect(body).toMatchObject({
|
||||
error: 'Internal Server Error',
|
||||
@@ -235,8 +295,8 @@ describe(`/oauth`, () => {
|
||||
});
|
||||
|
||||
it('should not auto register the user', async () => {
|
||||
const url = await loginWithOAuth('oauth-no-auto-register');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||
const callbackParams = await loginWithOAuth('oauth-no-auto-register');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('User does not exist and auto registering is disabled.'));
|
||||
});
|
||||
@@ -247,8 +307,8 @@ describe(`/oauth`, () => {
|
||||
email: 'oauth-user3@immich.app',
|
||||
password: 'password',
|
||||
});
|
||||
const url = await loginWithOAuth('oauth-user3');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url });
|
||||
const callbackParams = await loginWithOAuth('oauth-user3');
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(201);
|
||||
expect(body).toMatchObject({
|
||||
userId,
|
||||
@@ -286,13 +346,15 @@ describe(`/oauth`, () => {
|
||||
});
|
||||
|
||||
it('should auto register the user by default', async () => {
|
||||
const url = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback');
|
||||
expect(url).toEqual(expect.stringContaining(mobileOverrideRedirectUri));
|
||||
const callbackParams = await loginWithOAuth('oauth-mobile-override', 'app.immich:///oauth-callback');
|
||||
expect(callbackParams.url).toEqual(expect.stringContaining(mobileOverrideRedirectUri));
|
||||
|
||||
// simulate redirecting back to mobile app
|
||||
const redirectUri = url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback');
|
||||
const url = callbackParams.url.replace(mobileOverrideRedirectUri, 'app.immich:///oauth-callback');
|
||||
|
||||
const { status, body } = await request(app).post('/oauth/callback').send({ url: redirectUri });
|
||||
const { status, body } = await request(app)
|
||||
.post('/oauth/callback')
|
||||
.send({ ...callbackParams, url });
|
||||
expect(status).toBe(201);
|
||||
expect(body).toMatchObject({
|
||||
accessToken: expect.any(String),
|
||||
|
||||
@@ -5,22 +5,6 @@ import { app, asBearerAuth, utils } from 'src/utils';
|
||||
import request from 'supertest';
|
||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
const invalidBirthday = [
|
||||
{
|
||||
birthDate: 'false',
|
||||
response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'],
|
||||
},
|
||||
{
|
||||
birthDate: '123567',
|
||||
response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'],
|
||||
},
|
||||
{
|
||||
birthDate: 123_567,
|
||||
response: ['birthDate must be a string in the format yyyy-MM-dd', 'Birth date cannot be in the future'],
|
||||
},
|
||||
{ birthDate: '9999-01-01', response: ['Birth date cannot be in the future'] },
|
||||
];
|
||||
|
||||
describe('/people', () => {
|
||||
let admin: LoginResponseDto;
|
||||
let visiblePerson: PersonResponseDto;
|
||||
@@ -58,14 +42,6 @@ describe('/people', () => {
|
||||
|
||||
describe('GET /people', () => {
|
||||
beforeEach(async () => {});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get('/people');
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should return all people (including hidden)', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/people')
|
||||
@@ -117,13 +93,6 @@ describe('/people', () => {
|
||||
});
|
||||
|
||||
describe('GET /people/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get(`/people/${uuidDto.notFound}`);
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should throw error if person with id does not exist', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/people/${uuidDto.notFound}`)
|
||||
@@ -144,13 +113,6 @@ describe('/people', () => {
|
||||
});
|
||||
|
||||
describe('GET /people/:id/statistics', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get(`/people/${multipleAssetsPerson.id}/statistics`);
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should throw error if person with id does not exist', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/people/${uuidDto.notFound}/statistics`)
|
||||
@@ -171,23 +133,6 @@ describe('/people', () => {
|
||||
});
|
||||
|
||||
describe('POST /people', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).post(`/people`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
for (const { birthDate, response } of invalidBirthday) {
|
||||
it(`should not accept an invalid birth date [${birthDate}]`, async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post(`/people`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||
.send({ birthDate });
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(response));
|
||||
});
|
||||
}
|
||||
|
||||
it('should create a person', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post(`/people`)
|
||||
@@ -223,39 +168,6 @@ describe('/people', () => {
|
||||
});
|
||||
|
||||
describe('PUT /people/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).put(`/people/${uuidDto.notFound}`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
for (const { key, type } of [
|
||||
{ key: 'name', type: 'string' },
|
||||
{ key: 'featureFaceAssetId', type: 'string' },
|
||||
{ key: 'isHidden', type: 'boolean value' },
|
||||
{ key: 'isFavorite', type: 'boolean value' },
|
||||
]) {
|
||||
it(`should not allow null ${key}`, async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/people/${visiblePerson.id}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||
.send({ [key]: null });
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest([`${key} must be a ${type}`]));
|
||||
});
|
||||
}
|
||||
|
||||
for (const { birthDate, response } of invalidBirthday) {
|
||||
it(`should not accept an invalid birth date [${birthDate}]`, async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/people/${visiblePerson.id}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||
.send({ birthDate });
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(response));
|
||||
});
|
||||
}
|
||||
|
||||
it('should update a date of birth', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.put(`/people/${visiblePerson.id}`)
|
||||
@@ -312,12 +224,6 @@ describe('/people', () => {
|
||||
});
|
||||
|
||||
describe('POST /people/:id/merge', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).post(`/people/${uuidDto.notFound}/merge`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should not supporting merging a person into themselves', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post(`/people/${visiblePerson.id}/merge`)
|
||||
|
||||