release: v0.16.0

See CHANGELOG.md for the full v0.16.0 entry.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ralf <rohlenbo79@gmail.com>
Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
This commit is contained in:
Ralksta
2026-09-22 14:12:13 +02:00
committed by GitHub
parent c3f92ca4ec
commit a3a6319c29
120 changed files with 6818 additions and 975 deletions

View File

@@ -1,4 +1,4 @@
import { describe, it, expect } from 'vitest';
import { describe, it, expect, vi } from 'vitest';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
@@ -14,6 +14,7 @@ import {
findUnwritable,
formatReport,
frontmatterPassword,
gatherFindings,
reportWritable,
loadDotEnv,
resolveCredentials,
@@ -425,3 +426,67 @@ describe('shouldUseColor', () => {
expect(shouldUseColor({ FORCE_COLOR: '0' }, false)).toBe(false);
});
});
/**
* `if (albums.length)` used to skip the album check entirely when Immich
* answered with an empty list — an API key regenerated under a different
* account, say — so every connection check passed while every album page
* was silently empty (#629). gatherFindings is the whole pipeline; these
* cover it end to end rather than just the pure checkAlbumIds function.
*/
describe('gatherFindings — album checks (#629)', () => {
const ALBUM_ID = '11111111-1111-1111-1111-111111111111';
function envFor(dir: string): EnvLike {
return {
INSTALL_CONTENT_DIR: dir,
IMMICH_API_URL: 'https://immich.example',
IMMICH_API_KEY: 'key',
AUTH_SECRET: 'x'.repeat(32),
};
}
function stubEmptyAlbumsFetch() {
const fetchMock = vi.fn(async (url: string) => {
if (url.includes('/albums')) {
return { ok: true, json: async () => [] } as Response;
}
return { ok: true, json: async () => ({}) } as Response;
});
vi.stubGlobal('fetch', fetchMock);
}
it('reports every configured album as missing when Immich returns an empty list', async () => {
const dir = tmpdir();
try {
fs.writeFileSync(path.join(dir, 'gallery.yaml'), `albums:\n - ${ALBUM_ID}\n`);
stubEmptyAlbumsFetch();
const findings = await gatherFindings(dir, envFor(dir));
const albumFinding = findings.find((f) => f.id === 'album-ids');
expect(albumFinding?.level).toBe('error');
expect(albumFinding?.title).toContain('1 of 1');
} finally {
vi.unstubAllGlobals();
fs.rmSync(dir, { recursive: true, force: true });
}
});
it('still warns "no albums configured" when none are configured at all', async () => {
const dir = tmpdir();
try {
fs.writeFileSync(path.join(dir, 'gallery.yaml'), 'albums: []\n');
stubEmptyAlbumsFetch();
const findings = await gatherFindings(dir, envFor(dir));
const albumFinding = findings.find((f) => f.id === 'album-ids');
expect(albumFinding?.level).toBe('warn');
expect(albumFinding?.title).toContain('No albums configured');
} finally {
vi.unstubAllGlobals();
fs.rmSync(dir, { recursive: true, force: true });
}
});
});

View File

@@ -523,7 +523,7 @@ function reportProxyHops(env: EnvLike): CliFinding {
};
}
async function gatherFindings(cwd: string, env: EnvLike): Promise<CliFinding[]> {
export async function gatherFindings(cwd: string, env: EnvLike): Promise<CliFinding[]> {
const contentDir = env.INSTALL_CONTENT_DIR || path.join(cwd, 'content');
const findings: CliFinding[] = [];
const credentials = resolveCredentials(contentDir, env);
@@ -652,12 +652,20 @@ async function gatherFindings(cwd: string, env: EnvLike): Promise<CliFinding[]>
}
findings.push(checkImmichCalls(calls));
// Whenever albums are configured, run the check even if Immich answered
// with an empty list — an API key regenerated under a different account,
// say. `if (albums.length)` used to skip this entirely, so a `200 []`
// response passed every connection check and reported nothing about
// albums at all, while every album page was silently empty (#629).
// checkAlbumIds treats every configured ID as missing when none resolve.
if (configuredAlbums.length) {
findings.push(checkAlbumIds(configuredAlbums, albums));
findings.push(checkAlbumsShared(configuredAlbums, albums));
}
}
if (albums.length) {
findings.push(checkAlbumIds(configuredAlbums, albums));
findings.push(checkAlbumsShared(configuredAlbums, albums));
} else if (galleryExists && !gallery.error && !configuredAlbums.length) {
if (!configuredAlbums.length && galleryExists && !gallery.error) {
findings.push({
id: 'album-ids',
level: 'warn',