apply-lobster-execution-quality-gate.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. const mysql = require('mysql2/promise')
  2. const DB_CFG = {
  3. host: process.env.DB_HOST || 'cq-cdb-8fjmemkb.sql.tencentcdb.com',
  4. port: Number(process.env.DB_PORT || 27220),
  5. user: process.env.DB_USER || 'root',
  6. password: process.env.DB_PASSWORD || 'Ylrz_1q2w3e4r5t6y',
  7. multipleStatements: true
  8. }
  9. const MASTER_DB = process.env.MASTER_DB || 'ylrz_saas'
  10. const ALTER_SQL = `
  11. SET @col_exists := (SELECT COUNT(*) FROM information_schema.COLUMNS
  12. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lobster_execution_config' AND COLUMN_NAME = 'quality_gate_enabled');
  13. SET @ddl := IF(@col_exists = 0,
  14. 'ALTER TABLE lobster_execution_config ADD COLUMN quality_gate_enabled tinyint NOT NULL DEFAULT 1 COMMENT ''quality gate enabled'' AFTER auto_start_instance_on_tag',
  15. 'SELECT 1');
  16. PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  17. SET @col_exists := (SELECT COUNT(*) FROM information_schema.COLUMNS
  18. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lobster_execution_config' AND COLUMN_NAME = 'quality_pass_score_percent');
  19. SET @ddl := IF(@col_exists = 0,
  20. 'ALTER TABLE lobster_execution_config ADD COLUMN quality_pass_score_percent int NOT NULL DEFAULT 75 COMMENT ''runtime pass score percent'' AFTER quality_gate_enabled',
  21. 'SELECT 1');
  22. PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  23. SET @col_exists := (SELECT COUNT(*) FROM information_schema.COLUMNS
  24. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lobster_execution_config' AND COLUMN_NAME = 'quality_target_score_percent');
  25. SET @ddl := IF(@col_exists = 0,
  26. 'ALTER TABLE lobster_execution_config ADD COLUMN quality_target_score_percent int NOT NULL DEFAULT 75 COMMENT ''e2e target score percent'' AFTER quality_pass_score_percent',
  27. 'SELECT 1');
  28. PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  29. SET @col_exists := (SELECT COUNT(*) FROM information_schema.COLUMNS
  30. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lobster_execution_config' AND COLUMN_NAME = 'quality_min_node_score_percent');
  31. SET @ddl := IF(@col_exists = 0,
  32. 'ALTER TABLE lobster_execution_config ADD COLUMN quality_min_node_score_percent int NOT NULL DEFAULT 60 COMMENT ''e2e min node score percent'' AFTER quality_target_score_percent',
  33. 'SELECT 1');
  34. PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  35. SET @col_exists := (SELECT COUNT(*) FROM information_schema.COLUMNS
  36. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lobster_execution_config' AND COLUMN_NAME = 'quality_min_e2e_avg_score_percent');
  37. SET @ddl := IF(@col_exists = 0,
  38. 'ALTER TABLE lobster_execution_config ADD COLUMN quality_min_e2e_avg_score_percent int NOT NULL DEFAULT 75 COMMENT ''e2e min avg score percent'' AFTER quality_min_node_score_percent',
  39. 'SELECT 1');
  40. PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  41. SET @col_exists := (SELECT COUNT(*) FROM information_schema.COLUMNS
  42. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lobster_execution_config' AND COLUMN_NAME = 'quality_multiturn_min_avg_score_percent');
  43. SET @ddl := IF(@col_exists = 0,
  44. 'ALTER TABLE lobster_execution_config ADD COLUMN quality_multiturn_min_avg_score_percent int NOT NULL DEFAULT 45 COMMENT ''multiturn min avg score percent'' AFTER quality_min_e2e_avg_score_percent',
  45. 'SELECT 1');
  46. PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
  47. `
  48. function dbNameFromUrl(url) {
  49. if (!url) return null
  50. const m = String(url).match(/\/([^/?]+)(?:\?|$)/)
  51. return m ? m[1] : null
  52. }
  53. async function columnExists(conn, column) {
  54. const [rows] = await conn.query(
  55. `SELECT COUNT(*) AS c FROM information_schema.COLUMNS
  56. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'lobster_execution_config' AND COLUMN_NAME = ?`,
  57. [column]
  58. )
  59. return rows[0].c > 0
  60. }
  61. async function applyOnDb(conn, label) {
  62. const [tables] = await conn.query("SHOW TABLES LIKE 'lobster_execution_config'")
  63. if (!tables.length) {
  64. console.log(`${label}: skip (no lobster_execution_config table)`)
  65. return
  66. }
  67. await conn.query(ALTER_SQL)
  68. const ok = await columnExists(conn, 'quality_gate_enabled')
  69. console.log(`${label}: quality_gate_enabled=${ok ? 'OK' : 'MISSING'}`)
  70. }
  71. async function main() {
  72. const dbs = new Set(process.argv.slice(2))
  73. const masterConn = await mysql.createConnection({ ...DB_CFG, database: MASTER_DB })
  74. if (!dbs.size) {
  75. try {
  76. const [tenants] = await masterConn.query(
  77. 'SELECT db_url FROM tenant_info WHERE db_url IS NOT NULL AND db_url != \'\''
  78. )
  79. for (const t of tenants) {
  80. const name = dbNameFromUrl(t.db_url)
  81. if (name) dbs.add(name)
  82. }
  83. } catch (e) {
  84. console.warn('tenant_info skipped:', e.message)
  85. }
  86. }
  87. await masterConn.end()
  88. for (const db of dbs) {
  89. const conn = await mysql.createConnection({ ...DB_CFG, database: db })
  90. try {
  91. await applyOnDb(conn, db)
  92. } catch (e) {
  93. console.error(`${db}: ${e.message}`)
  94. } finally {
  95. await conn.end()
  96. }
  97. }
  98. }
  99. main().catch(err => {
  100. console.error(err)
  101. process.exit(1)
  102. })