Repository: logto-io/logto
File: packages/cli/src/commands/database/alteration/index.ts (TypeScript)

Code before:
      if (beforeDown) {
        await beforeDown(pool);
      }

      await pool.transaction(async (connection) => {
        await down(connection);

        const newTimestamp = timestamp - 1;

        if (newTimestamp > 0) {
          await updateDatabaseTimestamp(connection, newTimestamp);
        }
      });
    }
  } catch (error: unknown) {
    consoleLog.error(error);

    await pool.end();
    const hasNonTransactionalSteps =
      // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
      (action === 'up' && beforeUp) || (action === 'down' && beforeDown);

    if (hasNonTransactionalSteps) {
      consoleLog.fatal(
        `Error occurred while running alteration ${chalk.blue(filename)}.\n\n` +
          '  Non-transactional steps may have been applied before the failure.\n' +
          '  Inspect the database state, fix the issue, then rerun the alteration to reconcile state and timestamp.'
      );
    } else {
      consoleLog.fatal(
        `Error occurred while running alteration ${chalk.blue(filename)}.\n\n` +
          "  This alteration didn't change anything since it was in a transaction.\n" +
          '  Fix the error and deploy again.'
      );
    }
  }

  consoleLog.info(`Run alteration ${filename} \`${action}()\` function succeeded`);
};

const revertAlterations = async (alterations: AlterationFile[], pool: DatabasePool) => {
  consoleLog.info(
    `Found ${alterations.length} alteration${conditionalString(
      alterations.length > 1 && 's'
    )} to revert`
  );

  // The await inside the loop is intended, alterations should run in order
  // eslint-disable-next-line @silverhand/fp/no-mutating-methods
  for (const alteration of alterations.slice().reverse()) {
    // eslint-disable-next-line no-await-in-loop

Review comment (bug):
```suggestion
          '  Non-transactional steps (`beforeUp`/`beforeDown`) may have been applied before the failure.\n' +
```

Code after:
      if (beforeDown) {
        await beforeDown(pool);
      }

      await pool.transaction(async (connection) => {
        await down(connection);

        const newTimestamp = timestamp - 1;

        if (newTimestamp > 0) {
          await updateDatabaseTimestamp(connection, newTimestamp);
        }
      });
    }
  } catch (error: unknown) {
    consoleLog.error(error);

    await pool.end();
    const hasNonTransactionalSteps =
      // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
      (action === 'up' && beforeUp) || (action === 'down' && beforeDown);

    if (hasNonTransactionalSteps) {
      consoleLog.fatal(
        `Error occurred while running alteration ${chalk.blue(filename)}.\n\n` +
          '  Non-transactional steps (`beforeUp`/`beforeDown`) may have been applied before the failure.\n' +
          '  Inspect the database state, fix the issue, then rerun the alteration to reconcile state and timestamp.'
      );
    } else {
      consoleLog.fatal(
        `Error occurred while running alteration ${chalk.blue(filename)}.\n\n` +
          "  This alteration didn't change anything since it was in a transaction.\n" +
          '  Fix the error and deploy again.'
      );
    }
  }

  consoleLog.info(`Run alteration ${filename} \`${action}()\` function succeeded`);
};

const revertAlterations = async (alterations: AlterationFile[], pool: DatabasePool) => {
  consoleLog.info(
    `Found ${alterations.length} alteration${conditionalString(
      alterations.length > 1 && 's'
    )} to revert`
  );

  // The await inside the loop is intended, alterations should run in order
  // eslint-disable-next-line @silverhand/fp/no-mutating-methods
  for (const alteration of alterations.slice().reverse()) {
    // eslint-disable-next-line no-await-in-loop