@@ -290,62 +290,95 @@ func startMySQL() error {
290290 log .Println ("stopping any existing mysql service" )
291291 _ = exec .Command ("sudo" , "service" , "mysql" , "stop" ).Run ()
292292 _ = exec .Command ("sudo" , "mysqladmin" , "shutdown" ).Run ()
293+ // Give MySQL time to fully shut down
294+ time .Sleep (2 * time .Second )
295+
296+ if err := ensureMySQLDirs (); err != nil {
297+ return err
298+ }
293299
294300 // Check if data directory already exists and has been initialized
301+ needsPasswordReset := false
295302 if mysqlInitialized () {
296303 log .Println ("mysql data directory already initialized, skipping initialization" )
304+ // Existing data dir may have an unknown root password (e.g. pre-installed
305+ // MySQL on GitHub Actions). We'll need to use --skip-grant-tables to reset it.
306+ needsPasswordReset = true
297307 } else {
298308 log .Println ("initializing mysql data directory" )
299309 if err := run ("sudo" , "mysqld" , "--initialize-insecure" , "--user=mysql" ); err != nil {
300310 return fmt .Errorf ("mysqld --initialize-insecure: %w" , err )
301311 }
302312 }
303313
304- // Ensure the run directory exists for the socket/pid file
314+ if needsPasswordReset {
315+ // Start with --skip-grant-tables to reset the unknown root password.
316+ if err := startMySQLDaemon ("--skip-grant-tables" ); err != nil {
317+ return err
318+ }
319+
320+ log .Println ("resetting root password via --skip-grant-tables" )
321+ resetSQL := "FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'mysecretpassword';"
322+ if err := run ("mysql" , "-u" , "root" , "-e" , resetSQL ); err != nil {
323+ return fmt .Errorf ("resetting mysql root password: %w" , err )
324+ }
325+
326+ // Restart without --skip-grant-tables
327+ log .Println ("restarting mysql normally" )
328+ if err := run ("sudo" , "mysqladmin" , "-u" , "root" , "-pmysecretpassword" , "shutdown" ); err != nil {
329+ // If mysqladmin fails, try killing the process directly
330+ _ = run ("sudo" , "pkill" , "-f" , "mysqld" )
331+ }
332+ time .Sleep (2 * time .Second )
333+
334+ if err := startMySQLDaemon (); err != nil {
335+ return err
336+ }
337+ } else {
338+ // Fresh initialization — start normally and set password
339+ if err := startMySQLDaemon (); err != nil {
340+ return err
341+ }
342+
343+ log .Println ("setting mysql root password" )
344+ alterSQL := "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'mysecretpassword'; FLUSH PRIVILEGES;"
345+ if err := run ("mysql" , "-u" , "root" , "-e" , alterSQL ); err != nil {
346+ return fmt .Errorf ("setting mysql root password: %w" , err )
347+ }
348+ }
349+
350+ return verifyMySQL ()
351+ }
352+
353+ // ensureMySQLDirs creates the directories MySQL needs at runtime.
354+ func ensureMySQLDirs () error {
305355 if err := run ("sudo" , "mkdir" , "-p" , "/var/run/mysqld" ); err != nil {
306356 return fmt .Errorf ("creating /var/run/mysqld: %w" , err )
307357 }
308358 if err := run ("sudo" , "chown" , "mysql:mysql" , "/var/run/mysqld" ); err != nil {
309359 return fmt .Errorf ("chowning /var/run/mysqld: %w" , err )
310360 }
361+ return nil
362+ }
311363
312- log .Println ("starting mysql via mysqld_safe" )
313- // mysqld_safe runs in the foreground, so we launch it in the background
314- cmd := exec .Command ("sudo" , "mysqld_safe" , "--user=mysql" )
364+ // startMySQLDaemon starts mysqld_safe in the background and waits for it to
365+ // accept connections. Extra args (e.g. "--skip-grant-tables") are appended.
366+ func startMySQLDaemon (extraArgs ... string ) error {
367+ args := append ([]string {"mysqld_safe" , "--user=mysql" }, extraArgs ... )
368+ log .Printf ("starting mysql via mysqld_safe %v" , extraArgs )
369+ cmd := exec .Command ("sudo" , args ... )
315370 cmd .Stdout = os .Stderr
316371 cmd .Stderr = os .Stderr
317372 if err := cmd .Start (); err != nil {
318373 return fmt .Errorf ("starting mysqld_safe: %w" , err )
319374 }
320375
321- // Wait for MySQL to become ready
322376 log .Println ("waiting for mysql to accept connections" )
323377 if err := waitForMySQL (30 * time .Second ); err != nil {
324378 return fmt .Errorf ("mysql did not start in time: %w" , err )
325379 }
326380 log .Println ("mysql is accepting connections" )
327-
328- // Set root password.
329- // The debconf-based install may configure auth_socket plugin which only
330- // works via Unix socket. We need caching_sha2_password for TCP access.
331- log .Println ("configuring mysql root password for TCP access" )
332- if err := run ("mysql" , "-h" , "127.0.0.1" , "-u" , "root" , "-pmysecretpassword" , "-e" , "SELECT 1;" ); err == nil {
333- log .Println ("mysql root password already set to expected value, skipping" )
334- } else {
335- log .Println ("setting mysql root password with caching_sha2_password plugin" )
336- alterSQL := "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'mysecretpassword'; FLUSH PRIVILEGES;"
337- // Try via socket without sudo (works when password is blank)
338- if err := run ("mysql" , "-u" , "root" , "-e" , alterSQL ); err != nil {
339- // Try via socket with sudo (needed when auth_socket plugin is
340- // active, since it requires the OS user to match the MySQL user)
341- log .Println ("retrying with sudo (auth_socket requires OS root user)" )
342- if err := run ("sudo" , "mysql" , "-u" , "root" , "-e" , alterSQL ); err != nil {
343- return fmt .Errorf ("setting mysql root password: %w" , err )
344- }
345- }
346- }
347-
348- return verifyMySQL ()
381+ return nil
349382}
350383
351384// mysqlReady checks if MySQL is running and accepting connections with the expected password.
0 commit comments