From 0f35ae1fb99bc2f4db741e5f7b16273662459880 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Mon, 31 Mar 2025 15:14:11 +0800 Subject: [PATCH] Per-repo merge request sequences --- git_hooks_handle_linux.go | 13 +++++++------ git_hooks_handle_other.go | 13 +++++++------ http_handle_repo_contrib_index.go | 2 +- http_handle_repo_contrib_one.go | 4 ++-- sql/schema.sql | 75 +++++++++++++++++++++++++++++++++++++++++++---------- diff --git a/git_hooks_handle_linux.go b/git_hooks_handle_linux.go index 686d560cbf00abdec813dbed15238b3f9d5170d2..812a429bac120fcf588bcec50894b91b20afa796 100644 --- a/git_hooks_handle_linux.go +++ b/git_hooks_handle_linux.go @@ -229,25 +229,26 @@ if strings.HasPrefix(refName, "refs/heads/contrib/") { if allZero(oldOID) { // New branch fmt.Fprintln(sshStderr, ansiec.Blue+"POK"+ansiec.Reset, refName) - var newMRID int + var newMRLocalID int if packPass.userID != 0 { err = database.QueryRow(ctx, - "INSERT INTO merge_requests (repo_id, creator, source_ref, status) VALUES ($1, $2, $3, 'open') RETURNING id", + "INSERT INTO merge_requests (repo_id, creator, source_ref, status) VALUES ($1, $2, $3, 'open') RETURNING repo_local_id", packPass.repoID, packPass.userID, strings.TrimPrefix(refName, "refs/heads/"), - ).Scan(&newMRID) + ).Scan(&newMRLocalID) } else { err = database.QueryRow(ctx, - "INSERT INTO merge_requests (repo_id, source_ref, status) VALUES ($1, $2, 'open') RETURNING id", + "INSERT INTO merge_requests (repo_id, source_ref, status) VALUES ($1, $2, 'open') RETURNING repo_local_id", packPass.repoID, strings.TrimPrefix(refName, "refs/heads/"), - ).Scan(&newMRID) + ).Scan(&newMRLocalID) } if err != nil { writeRedError(sshStderr, "Error creating merge request: %v", err) return 1 } - mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRID) + mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRLocalID) fmt.Fprintln(sshStderr, ansiec.Blue+"Created merge request at", mergeRequestWebURL+ansiec.Reset) + select { case ircSendBuffered <- "PRIVMSG #chat :New merge request at " + mergeRequestWebURL: default: diff --git a/git_hooks_handle_other.go b/git_hooks_handle_other.go index 0d197a39386e1eb01852c2a59e9bff89e0868bcf..f1b2e04808fa86e31e4bd74ebb973e739ef23691 100644 --- a/git_hooks_handle_other.go +++ b/git_hooks_handle_other.go @@ -207,25 +207,26 @@ if strings.HasPrefix(refName, "refs/heads/contrib/") { if allZero(oldOID) { // New branch fmt.Fprintln(sshStderr, ansiec.Blue+"POK"+ansiec.Reset, refName) - var newMRID int + var newMRLocalID int if packPass.userID != 0 { err = database.QueryRow(ctx, - "INSERT INTO merge_requests (repo_id, creator, source_ref, status) VALUES ($1, $2, $3, 'open') RETURNING id", + "INSERT INTO merge_requests (repo_id, creator, source_ref, status) VALUES ($1, $2, $3, 'open') RETURNING repo_local_id", packPass.repoID, packPass.userID, strings.TrimPrefix(refName, "refs/heads/"), - ).Scan(&newMRID) + ).Scan(&newMRLocalID) } else { err = database.QueryRow(ctx, - "INSERT INTO merge_requests (repo_id, source_ref, status) VALUES ($1, $2, 'open') RETURNING id", + "INSERT INTO merge_requests (repo_id, source_ref, status) VALUES ($1, $2, 'open') RETURNING repo_local_id", packPass.repoID, strings.TrimPrefix(refName, "refs/heads/"), - ).Scan(&newMRID) + ).Scan(&newMRLocalID) } if err != nil { writeRedError(sshStderr, "Error creating merge request: %v", err) return 1 } - mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRID) + mergeRequestWebURL := fmt.Sprintf("%s/contrib/%d/", genHTTPRemoteURL(packPass.groupPath, packPass.repoName), newMRLocalID) fmt.Fprintln(sshStderr, ansiec.Blue+"Created merge request at", mergeRequestWebURL+ansiec.Reset) + select { case ircSendBuffered <- "PRIVMSG #chat :New merge request at " + mergeRequestWebURL: default: diff --git a/http_handle_repo_contrib_index.go b/http_handle_repo_contrib_index.go index 4f54836692f0afb9e717f1f758b00ce7d6aec038..c199bfa997bf1f2ff5ae5edd22d03b638b73076e 100644 --- a/http_handle_repo_contrib_index.go +++ b/http_handle_repo_contrib_index.go @@ -21,7 +21,7 @@ var result []idTitleStatus var err error if rows, err = database.Query(request.Context(), - "SELECT id, COALESCE(title, 'Untitled'), status FROM merge_requests WHERE repo_id = $1", + "SELECT repo_local_id, COALESCE(title, 'Untitled'), status FROM merge_requests WHERE repo_id = $1", params["repo_id"], ); err != nil { errorPage500(writer, params, "Error querying merge requests: "+err.Error()) diff --git a/http_handle_repo_contrib_one.go b/http_handle_repo_contrib_one.go index 485362d4f51868f1c94a78dbeace1d4861a104f3..0fc56fae73ef758a8a1fc26b188599d35f3e26e3 100644 --- a/http_handle_repo_contrib_one.go +++ b/http_handle_repo_contrib_one.go @@ -32,8 +32,8 @@ } mrIDInt = int(mrIDInt64) if err = database.QueryRow(request.Context(), - "SELECT COALESCE(title, ''), status, source_ref, COALESCE(destination_branch, '') FROM merge_requests WHERE id = $1", - mrIDInt, + "SELECT COALESCE(title, ''), status, source_ref, COALESCE(destination_branch, '') FROM merge_requests WHERE repo_id = $1 AND repo_local_id = $2", + params["repo_id"], mrIDInt, ).Scan(&title, &status, &srcRefStr, &dstBranchStr); err != nil { errorPage500(writer, params, "Error querying merge request: "+err.Error()) return diff --git a/sql/schema.sql b/sql/schema.sql index 0213cea84f06432e84d98137ee01db38f8f3e6c7..a6efc39b95d0d6fe881e289c9a25195fc05ae657 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -56,19 +56,6 @@ session_id TEXT PRIMARY KEY NOT NULL, UNIQUE(user_id, session_id) ); --- TODO: -CREATE TABLE merge_requests ( - id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - title TEXT, - repo_id INTEGER NOT NULL REFERENCES repos(id) ON DELETE CASCADE, - creator INTEGER REFERENCES users(id) ON DELETE SET NULL, - source_ref TEXT NOT NULL, - destination_branch TEXT, - status TEXT NOT NULL CHECK (status IN ('open', 'merged', 'closed')), - UNIQUE (repo_id, source_ref, destination_branch), - UNIQUE (repo_id, id) -); - CREATE TABLE user_group_roles ( group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, @@ -82,6 +69,7 @@ remote_username TEXT NOT NULL, PRIMARY KEY(user_id, service) ); +-- Ticket tracking CREATE TABLE ticket_trackers ( id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, @@ -144,3 +132,64 @@ CREATE TRIGGER before_insert_ticket BEFORE INSERT ON tickets FOR EACH ROW EXECUTE FUNCTION assign_tracker_local_id(); + +-- Merge requests + +CREATE TABLE merge_requests ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + repo_id INTEGER NOT NULL REFERENCES repos(id) ON DELETE CASCADE, + repo_local_id INTEGER NOT NULL, + title TEXT, + creator INTEGER REFERENCES users(id) ON DELETE SET NULL, + source_ref TEXT NOT NULL, + destination_branch TEXT, + status TEXT NOT NULL CHECK (status IN ('open', 'merged', 'closed')), + UNIQUE (repo_id, repo_local_id), + UNIQUE (repo_id, source_ref, destination_branch) +); + +CREATE OR REPLACE FUNCTION create_repo_mr_sequence() +RETURNS TRIGGER AS $$ +DECLARE + seq_name TEXT := 'repo_mr_seq_' || NEW.id; +BEGIN + EXECUTE format('CREATE SEQUENCE %I', seq_name); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; +CREATE TRIGGER after_insert_repo +AFTER INSERT ON repos +FOR EACH ROW +EXECUTE FUNCTION create_repo_mr_sequence(); + +CREATE OR REPLACE FUNCTION drop_repo_mr_sequence() +RETURNS TRIGGER AS $$ +DECLARE + seq_name TEXT := 'repo_mr_seq_' || OLD.id; +BEGIN + EXECUTE format('DROP SEQUENCE IF EXISTS %I', seq_name); + RETURN OLD; +END; +$$ LANGUAGE plpgsql; +CREATE TRIGGER before_delete_repo +BEFORE DELETE ON repos +FOR EACH ROW +EXECUTE FUNCTION drop_repo_mr_sequence(); + + +CREATE OR REPLACE FUNCTION assign_repo_local_id() +RETURNS TRIGGER AS $$ +DECLARE + seq_name TEXT := 'repo_mr_seq_' || NEW.repo_id; +BEGIN + IF NEW.repo_local_id IS NULL THEN + EXECUTE format('SELECT nextval(%L)', seq_name) + INTO NEW.repo_local_id; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; +CREATE TRIGGER before_insert_merge_request +BEFORE INSERT ON merge_requests +FOR EACH ROW +EXECUTE FUNCTION assign_repo_local_id(); -- 2.48.1