v0.253.0 — errors carry the key of the sentence they are (R-557 slice 2 release B)
gates / gates (push) Successful in 24s
gates / gates (push) Successful in 24s
179 Hungarian sentences were built deep inside a package with fmt.Errorf and printed by whoever caught them: too late to translate where they are shown, too early where they are made. Every one now carries its key across that gap. ZERO Hungarian error literals remain. util.MsgError does three things at once, each earned: - Error() is the Hungarian, byte for byte, so every un-converted printer is unchanged; - errors.Is answers for the kind AND for a wrapped cause (KindErrorf dropped the cause); - an error ARGUMENT renders recursively, so "formázás sikertelen: %w" translates whole. A foreign error — restic, docker, ssh, the stdlib — prints verbatim. It is not ours. 76 display sites go through errText, and TestNoErrErrorInPageOutput convicts any that do not. memoryVerdict returns an error rather than a sentence, so the deploy's 409 and the household's language come from one value; UpdateRefusal gained a Cause to carry it. Plurals, one rule, stated once: a key with .one/.other takes its COUNT first. Not a per-call-site flag — the producer somebody forgot would read "3 app is not running". The guard caught a real key collision (alert.deadapp.one) the day the rule landed. TWO DEFECTS FOUND IN MY OWN TOOLING, recorded rather than quietly fixed. The bulk converter silently dropped multi-line concatenations, damaging 7 producers — and the parity gate could not see it, because every surviving fragment WAS a real base literal while the CALL had lost text; two behaviour tests caught it. And the counting script was case-sensitive, so it said "0 left" while five remained. MinAgent: 0.131.0 (unchanged). No hub release needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -23,6 +23,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/util"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
@@ -163,7 +164,7 @@ func (m *Manager) acquireMigrating() error {
|
||||
m.migrateMu.Lock()
|
||||
defer m.migrateMu.Unlock()
|
||||
if m.migrating {
|
||||
return fmt.Errorf("migráció már folyamatban")
|
||||
return util.MsgError("err.stacks.migracio_mar_folyamatban")
|
||||
}
|
||||
m.migrating = true
|
||||
return nil
|
||||
@@ -242,12 +243,12 @@ func (m *Manager) startMigration(scope, sourcePath, appName, targetPath string,
|
||||
case "app":
|
||||
cfg := m.LoadAppConfigByName(appName)
|
||||
if cfg == nil {
|
||||
return "", fmt.Errorf("alkalmazás nem található: %s", appName)
|
||||
return "", util.MsgError("err.stacks.alkalmazas_nem_talalhato", appName)
|
||||
}
|
||||
j.Source, j.SourceNS = m.appSourceNS(cfg)
|
||||
apps = []string{appName}
|
||||
default:
|
||||
return "", fmt.Errorf("ismeretlen migrációs hatókör: %s", scope)
|
||||
return "", util.MsgError("err.stacks.ismeretlen_migracios_hatokor", scope)
|
||||
}
|
||||
|
||||
j.TargetNS = appbackup.NamespaceRoot(j.Target, m.inGuest(j.Target))
|
||||
@@ -266,7 +267,7 @@ func (m *Manager) startMigration(scope, sourcePath, appName, targetPath string,
|
||||
j.UpdatedAt = time.Now().UTC()
|
||||
m.setJob(j)
|
||||
if err := m.writeJournal(j); err != nil {
|
||||
return "", fmt.Errorf("migrációs napló írása: %w", err)
|
||||
return "", util.MsgError("err.stacks.migracios_naplo_irasa", err)
|
||||
}
|
||||
|
||||
launched = true
|
||||
@@ -359,18 +360,18 @@ func (m *Manager) appsOnDrive(sourcePath string) []string {
|
||||
// migValidate runs the pre-flight checks. Any failure aborts before any side-effect.
|
||||
func (m *Manager) migValidate(j *MigrationJob) error {
|
||||
if m.backupRunning != nil && m.backupRunning() {
|
||||
return fmt.Errorf("biztonsági mentés folyamatban, próbáld újra")
|
||||
return util.MsgError("err.stacks.biztonsagi_mentes_folyamatban_probald_ujra")
|
||||
}
|
||||
if m.testSeams == nil { // real rsync is only required when not using injected copy/verify seams
|
||||
if _, err := exec.LookPath("rsync"); err != nil {
|
||||
return fmt.Errorf("az rsync nem érhető el a rendszeren")
|
||||
return util.MsgError("err.stacks.az_rsync_nem_erheto_el_a")
|
||||
}
|
||||
}
|
||||
if j.Target == j.Source {
|
||||
return fmt.Errorf("a cél és a forrás tároló megegyezik")
|
||||
return util.MsgError("err.stacks.a_cel_es_a_forras_tarolo")
|
||||
}
|
||||
if m.settings == nil || !m.settings.IsStoragePathSchedulable(j.Target) {
|
||||
return fmt.Errorf("a céltároló nem elérhető vagy nem választható")
|
||||
return util.MsgError("err.stacks.a_celtarolo_nem_elerheto_vagy_nem")
|
||||
}
|
||||
// App-dir collision: refuse if any of the app's resolved appdata dir(s) already exists at target.
|
||||
var collide []string
|
||||
@@ -383,7 +384,7 @@ func (m *Manager) migValidate(j *MigrationJob) error {
|
||||
}
|
||||
}
|
||||
if len(collide) > 0 {
|
||||
return fmt.Errorf("ütközés a céltárolón — már létezik ezeknek az alkalmazásoknak az adata: %s", strings.Join(collide, ", "))
|
||||
return util.MsgError("err.stacks.utkozes_a_celtarolon_mar_letezik_ezeknek", strings.Join(collide, ", "))
|
||||
}
|
||||
// Free-space check (best-effort; GetDiskUsage is nil on non-linux).
|
||||
need := m.migSourceSize(j)
|
||||
@@ -391,7 +392,7 @@ func (m *Manager) migValidate(j *MigrationJob) error {
|
||||
if du := system.GetDiskUsage(j.Target); du != nil {
|
||||
avail := int64(du.AvailGB * 1e9)
|
||||
if need > avail {
|
||||
return fmt.Errorf("nincs elég hely a céltárolón (kb. %d GB szükséges, %.1f GB szabad)", need/1_000_000_000, du.AvailGB)
|
||||
return util.MsgError("err.stacks.nincs_eleg_hely_a_celtarolon_kb", need/1_000_000_000, du.AvailGB)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -451,7 +452,7 @@ func (m *Manager) runMigration(ctx context.Context, j *MigrationJob) {
|
||||
case PhaseDone, PhaseAborted:
|
||||
return
|
||||
default:
|
||||
err = fmt.Errorf("ismeretlen migrációs fázis: %q", j.Phase)
|
||||
err = util.MsgError("err.stacks.ismeretlen_migracios_fazis", j.Phase)
|
||||
}
|
||||
if err != nil {
|
||||
m.migAbort(j, err)
|
||||
@@ -484,7 +485,7 @@ func (m *Manager) migAbort(j *MigrationJob, cause error) {
|
||||
func (m *Manager) migStop(j *MigrationJob) error {
|
||||
for _, app := range j.Apps {
|
||||
if err := m.doStop(app); err != nil {
|
||||
return fmt.Errorf("alkalmazás leállítása sikertelen (%s): %w", app, err)
|
||||
return util.MsgError("err.stacks.alkalmazas_leallitasa_sikertelen", app, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -513,13 +514,13 @@ func (m *Manager) migCopy(ctx context.Context, j *MigrationJob) error {
|
||||
}
|
||||
if err := m.copySubtree(ctx, j, src, appbackup.AppDataDir(j.TargetNS, name)); err != nil {
|
||||
u.Error = err.Error()
|
||||
return fmt.Errorf("másolás sikertelen (%s appdata): %w", app, err)
|
||||
return util.MsgError("err.stacks.masolas_sikertelen_appdata", app, err)
|
||||
}
|
||||
}
|
||||
// the app's recovery unit (db-dumps + volume-dumps + compose + manifest)
|
||||
if err := m.copySubtree(ctx, j, appbackup.RecoveryUnitPath(j.SourceNS, app), appbackup.RecoveryUnitPath(j.TargetNS, app)); err != nil {
|
||||
u.Error = err.Error()
|
||||
return fmt.Errorf("másolás sikertelen (%s mentés): %w", app, err)
|
||||
return util.MsgError("err.stacks.masolas_sikertelen_mentes", app, err)
|
||||
}
|
||||
u.State = UnitCopied
|
||||
_ = m.persistJob(j)
|
||||
@@ -530,7 +531,7 @@ func (m *Manager) migCopy(ctx context.Context, j *MigrationJob) error {
|
||||
j.CurrentApp = ""
|
||||
if err := walkMerge(m.logger, j.SourceNS, j.TargetNS, m.appDataSkipSet(j), false, func(b int64) { j.BytesDone += b }); err != nil {
|
||||
u.Error = err.Error()
|
||||
return fmt.Errorf("ügyfél-adatok összefésülése sikertelen: %w", err)
|
||||
return util.MsgError("err.stacks.ugyfel_adatok_osszefesulese_sikertelen", err)
|
||||
}
|
||||
u.State = UnitCopied
|
||||
_ = m.persistJob(j)
|
||||
@@ -557,12 +558,12 @@ func (m *Manager) migVerify(ctx context.Context, j *MigrationJob) error {
|
||||
for _, name := range m.ResolveAppDataDirNames(app) {
|
||||
if err := m.verifySubtree(ctx, appbackup.AppDataDir(j.SourceNS, name), appbackup.AppDataDir(j.TargetNS, name)); err != nil {
|
||||
u.Error = err.Error()
|
||||
return fmt.Errorf("ellenőrzés sikertelen (%s appdata): %w", app, err)
|
||||
return util.MsgError("err.stacks.ellenorzes_sikertelen_appdata", app, err)
|
||||
}
|
||||
}
|
||||
if err := m.verifySubtree(ctx, appbackup.RecoveryUnitPath(j.SourceNS, app), appbackup.RecoveryUnitPath(j.TargetNS, app)); err != nil {
|
||||
u.Error = err.Error()
|
||||
return fmt.Errorf("ellenőrzés sikertelen (%s mentés): %w", app, err)
|
||||
return util.MsgError("err.stacks.ellenorzes_sikertelen_mentes", app, err)
|
||||
}
|
||||
u.State = UnitVerified
|
||||
_ = m.persistJob(j)
|
||||
@@ -573,7 +574,7 @@ func (m *Manager) migVerify(ctx context.Context, j *MigrationJob) error {
|
||||
// assert-only merge walk: every source file has a content-identical counterpart at target.
|
||||
if err := walkMerge(m.logger, j.SourceNS, j.TargetNS, m.appDataSkipSet(j), true, nil); err != nil {
|
||||
u.Error = err.Error()
|
||||
return fmt.Errorf("ügyfél-adatok ellenőrzése sikertelen: %w", err)
|
||||
return util.MsgError("err.stacks.ugyfel_adatok_ellenorzese_sikertelen", err)
|
||||
}
|
||||
u.State = UnitVerified
|
||||
_ = m.persistJob(j)
|
||||
@@ -602,7 +603,7 @@ func (m *Manager) migFlipRedeploy(j *MigrationJob) error {
|
||||
_ = m.persistJob(j)
|
||||
if err := m.doFlipRedeploy(app, j.Target); err != nil {
|
||||
u.Error = err.Error()
|
||||
return fmt.Errorf("újratelepítés sikertelen (%s): %w", app, err)
|
||||
return util.MsgError("err.stacks.ujratelepites_sikertelen", app, err)
|
||||
}
|
||||
u.State = UnitRedeployed
|
||||
_ = m.persistJob(j)
|
||||
@@ -623,11 +624,11 @@ func (m *Manager) migCleanup(j *MigrationJob) error {
|
||||
}
|
||||
for _, name := range m.ResolveAppDataDirNames(app) {
|
||||
if err := os.RemoveAll(appbackup.AppDataDir(j.SourceNS, name)); err != nil {
|
||||
return fmt.Errorf("forrás törlése sikertelen (%s appdata): %w", app, err)
|
||||
return util.MsgError("err.stacks.forras_torlese_sikertelen_appdata", app, err)
|
||||
}
|
||||
}
|
||||
if err := os.RemoveAll(appbackup.RecoveryUnitPath(j.SourceNS, app)); err != nil {
|
||||
return fmt.Errorf("forrás törlése sikertelen (%s mentés): %w", app, err)
|
||||
return util.MsgError("err.stacks.forras_torlese_sikertelen_mentes", app, err)
|
||||
}
|
||||
u.State = UnitCleaned
|
||||
_ = m.persistJob(j)
|
||||
@@ -638,11 +639,11 @@ func (m *Manager) migCleanup(j *MigrationJob) error {
|
||||
// Remove every remaining child of the source namespace (the non-app/customer content).
|
||||
entries, err := os.ReadDir(j.SourceNS)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("forrás névtér olvasása sikertelen: %w", err)
|
||||
return util.MsgError("err.stacks.forras_nevter_olvasasa_sikertelen", err)
|
||||
}
|
||||
for _, e := range entries {
|
||||
if err := os.RemoveAll(filepath.Join(j.SourceNS, e.Name())); err != nil {
|
||||
return fmt.Errorf("forrás törlése sikertelen (%s): %w", e.Name(), err)
|
||||
return util.MsgError("err.stacks.forras_torlese_sikertelen", e.Name(), err)
|
||||
}
|
||||
}
|
||||
u.State = UnitCleaned
|
||||
@@ -657,12 +658,12 @@ func (m *Manager) migCleanup(j *MigrationJob) error {
|
||||
func (m *Manager) migCleanupAllowed(j *MigrationJob) error {
|
||||
for key, u := range j.Units {
|
||||
if stateRank(u.State) < stateRank(UnitVerified) {
|
||||
return fmt.Errorf("cleanup gate: a(z) %q egység nincs ellenőrizve (állapot=%s)", key, u.State)
|
||||
return util.MsgError("err.stacks.cleanup_gate_a_z_egyseg_nincs", key, u.State)
|
||||
}
|
||||
}
|
||||
for _, app := range j.Apps {
|
||||
if stateRank(j.Units[app].State) < stateRank(UnitRedeployed) {
|
||||
return fmt.Errorf("cleanup gate: a(z) %q alkalmazás nincs újratelepítve (állapot=%s)", app, j.Units[app].State)
|
||||
return util.MsgError("err.stacks.cleanup_gate_a_z_alkalmazas_nincs", app, j.Units[app].State)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -749,7 +750,7 @@ func (m *Manager) doFlipRedeploy(name, target string) error {
|
||||
return err
|
||||
}
|
||||
if !m.waitHealthy(name) {
|
||||
return fmt.Errorf("az alkalmazás nem indult el az új tárhelyen")
|
||||
return util.MsgError("err.stacks.az_alkalmazas_nem_indult_el_az")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -879,7 +880,7 @@ func rsyncVerify(ctx context.Context, src, dst string) error {
|
||||
}
|
||||
}
|
||||
if len(pending) > 0 {
|
||||
return fmt.Errorf("%d függőben lévő átvitel maradt, pl. %q", len(pending), pending[0])
|
||||
return util.MsgError("err.stacks.fuggoben_levo_atvitel_maradt_pl", len(pending), pending[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -979,7 +980,7 @@ func walkMerge(lg *log.Logger, srcNS, dstNS string, skip map[string]bool, assert
|
||||
return nil // dedup / already present
|
||||
}
|
||||
if assertOnly {
|
||||
return fmt.Errorf("a forrásfájlnak nincs azonos másolata a célon: %s", rel)
|
||||
return util.MsgError("err.stacks.a_forrasfajlnak_nincs_azonos_masolata_a", rel)
|
||||
}
|
||||
out := dst
|
||||
if pathExists(dst) {
|
||||
|
||||
Reference in New Issue
Block a user