Merge pull request 'fix(backend): Publish repariert - spawn git ENOENT und kaputter Image-Build' (#5) from fix/publish-git-enoent into main
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
+2
-1
@@ -39,8 +39,9 @@ RUN npm ci --omit=dev || npm install --production && \
|
||||
RUN apk del python3 make g++ vips-dev
|
||||
|
||||
# Copy built files from builder
|
||||
# Hinweis: Drizzle-Migrationen werden nicht kopiert - das Schema wird zur
|
||||
# Laufzeit von initDatabase() angelegt, src/db/migrations existiert nicht.
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/src/db/migrations ./dist/db/migrations
|
||||
|
||||
# Copy migration script and migrated images
|
||||
COPY --from=builder /app/migrate-production.js ./migrate-production.js
|
||||
|
||||
@@ -1,43 +1,84 @@
|
||||
import simpleGit, { SimpleGit } from 'simple-git';
|
||||
import { mkdir, rm } from 'fs/promises';
|
||||
import { mkdir, rm, cp } from 'fs/promises';
|
||||
import { existsSync } from 'fs';
|
||||
import path from 'path';
|
||||
import { env } from '../config/env.js';
|
||||
|
||||
export class GitService {
|
||||
private git: SimpleGit;
|
||||
private workspaceDir: string;
|
||||
private parentDir: string;
|
||||
private repoUrl: string;
|
||||
private token: string;
|
||||
|
||||
constructor() {
|
||||
this.workspaceDir = env.GIT_WORKSPACE_DIR;
|
||||
this.parentDir = path.dirname(this.workspaceDir);
|
||||
this.repoUrl = env.GIT_REPO_URL;
|
||||
this.token = env.GIT_TOKEN;
|
||||
this.git = simpleGit();
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
// Ensure workspace directory exists
|
||||
await mkdir(this.workspaceDir, { recursive: true });
|
||||
// Elternverzeichnis muss existieren - simple-git startet git von dort aus
|
||||
await mkdir(this.parentDir, { recursive: true });
|
||||
|
||||
// Add token to repo URL for authentication
|
||||
const authenticatedUrl = this.repoUrl.replace(
|
||||
'https://',
|
||||
`https://oauth2:${this.token}@`
|
||||
`https://oauth2:${encodeURIComponent(this.token)}@`
|
||||
);
|
||||
|
||||
let usable = false;
|
||||
|
||||
if (existsSync(path.join(this.workspaceDir, '.git'))) {
|
||||
try {
|
||||
// Check if repo already exists
|
||||
await this.git.cwd(this.workspaceDir);
|
||||
this.git = simpleGit(this.workspaceDir);
|
||||
await this.git.status();
|
||||
console.log('Repository already exists, pulling latest...');
|
||||
await this.git.pull();
|
||||
} catch {
|
||||
// Clone if doesn't exist
|
||||
usable = true;
|
||||
} catch (error) {
|
||||
// Token aus der Meldung entfernen - git gibt die Remote-URL mit aus
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
console.warn(
|
||||
'Existing workspace unusable, re-cloning:',
|
||||
msg.replace(/\/\/[^@\s/]*@/g, '//***@')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!usable) {
|
||||
console.log('Cloning repository...');
|
||||
|
||||
// Hochgeladene Bilder liegen im Workspace und wuerden beim Loeschen
|
||||
// verschwinden - vorher wegsichern, nach dem Clone zurueckspielen
|
||||
const imagesDir = path.join(this.workspaceDir, 'public', 'images');
|
||||
const backupDir = path.join(this.parentDir, '.workspace-images-backup');
|
||||
const hasImages = existsSync(imagesDir);
|
||||
|
||||
await rm(backupDir, { recursive: true, force: true });
|
||||
if (hasImages) {
|
||||
await cp(imagesDir, backupDir, { recursive: true });
|
||||
}
|
||||
|
||||
await rm(this.workspaceDir, { recursive: true, force: true });
|
||||
|
||||
// Aus dem existierenden Elternverzeichnis klonen, nicht aus dem
|
||||
// soeben geloeschten Zielverzeichnis (sonst: spawn git ENOENT)
|
||||
this.git = simpleGit(this.parentDir);
|
||||
await this.git.clone(authenticatedUrl, this.workspaceDir);
|
||||
await this.git.cwd(this.workspaceDir);
|
||||
this.git = simpleGit(this.workspaceDir);
|
||||
|
||||
if (hasImages) {
|
||||
// force: false -> was schon im Repo liegt, bleibt unangetastet
|
||||
await cp(backupDir, imagesDir, {
|
||||
recursive: true,
|
||||
force: false,
|
||||
errorOnExist: false,
|
||||
});
|
||||
await rm(backupDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
// Configure git user
|
||||
@@ -47,6 +88,14 @@ export class GitService {
|
||||
|
||||
async commitAndPush(message: string): Promise<string> {
|
||||
await this.git.add('.');
|
||||
|
||||
const status = await this.git.status();
|
||||
if (status.isClean()) {
|
||||
// Nichts zu committen - aktuellen Stand zurueckgeben statt zu scheitern
|
||||
const current = await this.git.log({ maxCount: 1 });
|
||||
return current.latest?.hash || '';
|
||||
}
|
||||
|
||||
await this.git.commit(message);
|
||||
await this.git.push('origin', 'main');
|
||||
|
||||
@@ -59,7 +108,15 @@ export class GitService {
|
||||
}
|
||||
|
||||
async reset() {
|
||||
await this.git.reset(['--hard', 'HEAD']);
|
||||
await this.git.clean('f', ['-d']);
|
||||
// Nur zuruecksetzen wenn wirklich ein Repo da ist - sonst laeuft git
|
||||
// im Prozess-Arbeitsverzeichnis und raeumt dort auf
|
||||
if (!existsSync(path.join(this.workspaceDir, '.git'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const git = simpleGit(this.workspaceDir);
|
||||
await git.reset(['--hard', 'HEAD']);
|
||||
// Uploads ausnehmen - die sind noch nicht committed und waeren sonst weg
|
||||
await git.clean('f', ['-d', '-e', 'public/images']);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user