Repository: trstringer/manual-approval
Pull request #167: Adding support for issue-body with more than 65536 words, issue body to be created as an issue comment
Author: login: snskArora
Created: 2025-03-22T14:00:32Z | Merged: 2025-04-26T06:32:02Z

This has PR 2 major changes:
- The issueBody is split into chunks of less than 65536 characters.
https://github.com/snskArora/first_go_project/issues/9
This has an issue body of lorem ipsum of 1,00,000 words.
- Have static issue body and the input of issue body as comment/s 
The previous change with which the issueBody was added in the NOTE block makes any decent-sized approval body difficult to read.

> [!NOTE]
> Please perform a squash merge so as to not hinder ease of commit tracking as there are quite a few incoming commits.


Diff:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index dbcfa1a..c0d4e03 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -27,6 +27,7 @@ jobs:
         run: make build
         env:
           VERSION: latest
+
       - name: Test
         run: make test
       - name: Lint
diff --git a/Makefile b/Makefile
index 56ba8bc..54f20b9 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ tidy:
 
 .PHONY: build
 build:
-	@if [ -z "$$VERSION" ]; then \
+	@if [ -z "$(VERSION)" ]; then \
 		echo "VERSION is required"; \
 		exit 1; \
 	fi
@@ -15,11 +15,11 @@ build:
 
 .PHONY: push
 push:
-	@if [ -z "$$VERSION" ]; then \
+	@if [ -z "$(VERSION)" ]; then \
 		echo "VERSION is required"; \
 		exit 1; \
 	fi
-	docker push $(IMAGE_REPO):$$VERSION
+	docker push $(IMAGE_REPO):$(VERSION)
 
 .PHONY: test
 test:
diff --git a/approval.go b/approval.go
index 9516b64..0cadca0 100644
--- a/approval.go
+++ b/approval.go
@@ -85,9 +85,6 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error {
 		formatAcceptedWords(deniedWords),
 	)
 
-	if a.issueBody != "" {
-		issueBody = fmt.Sprintf(">%s\n>\n%s", a.issueBody, issueBody)
-	}
 	issueBody = fmt.Sprintf(">[!NOTE]\n%s", issueBody)
 
 	var err error
@@ -109,6 +106,16 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error {
 	}
 	a.approvalIssueNumber = a.approvalIssue.GetNumber()
 
+  bodyChunks := splitLongString(a.issueBody)
+  for _, chunk := range bodyChunks {
+      _, _, err = a.client.Issues.CreateComment(ctx, a.targetRepoOwner, a.targetRepoName, *a.approvalIssue.Number, &github.IssueComment{
+          Body: &chunk,
+      })
+      if err != nil {
+          return fmt.Errorf("failed to add comment chunk to issue: %w", err)
+      }
+  }
+
 	fmt.Printf("Issue created: %s\n", a.approvalIssue.GetHTMLURL())
 	return nil
 }
@@ -244,3 +251,76 @@ func formatAcceptedWords(words []string) string {
 
 	return strings.Join(quotedWords, ", ")
 }
+
+func splitLongLine(line string, maxL int) ([]string, bool) {
+	if len(line) <= maxL {
+		return []string{line}, false
+	}
+
+	words := strings.Fields(line)
+	var result []string
+	var currentLine string
+
+	for _, word := range words {
+		if len(currentLine)+len(word)+1 > maxL {
+			result = append(result, currentLine)
+			currentLine = word
+		} else {
+			if currentLine != "" {
+				currentLine += " "
+			}
+			currentLine += word
+		}
+	}
+	if currentLine != "" {
+		result = append(result, currentLine)
+	}
+	return result, true
+}
+
+func splitLongString(input string) []string {
+	maxLength := 65536
+	var result []string
+
+	lines := strings.Split(input, "\n")
+	currentChunk := strings.Builder{}
+	currentLength := 0
+
+	for i, line := range lines {
+    lineLength := len(line)
+		if i < len(lines)-1 {
+			lineLength++
+    }
+
+		if currentLength+lineLength > maxLength {
+			if currentChunk.Len() > 0 {
+				result = append(result, currentChunk.String())
+				currentChunk.Reset()
+				currentLength = 0
+			}
+		}
+
+		lineSplit, isLongLine := splitLongLine(line, maxLength)
+		if isLongLine {
+			if currentChunk.Len() > 0 {
+				result = append(result, currentChunk.String())
+				currentChunk.Reset()
+			}
+			result = append(result, lineSplit[:len(lineSplit)-1]...)
+			currentChunk.WriteString(lineSplit[len(lineSplit)-1])
+			currentLength = len(lineSplit[len(lineSplit)-1])
+		} else {
+			currentChunk.WriteString(line)
+			currentLength += lineLength
+		}
+
+		if i < len(lines)-1 {
+			currentChunk.WriteString("\n")
+		}
+	}
+	if currentChunk.Len() > 0 {
+		result = append(result, currentChunk.String())
+	}
+	return result
+}
+


Reviews:
user: login: lizziemac
body: good to merge, just update the PR name and make 65536 a constant/var
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-04-24T17:42:05Z
user: login: snskArora
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-26T06:26:18Z

Review comments:
diff_hunk: @@ -109,6 +106,16 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error {
 	}
 	a.approvalIssueNumber = a.approvalIssue.GetNumber()
 
+  bodyChunks := splitLongString(a.issueBody, 65536)
path: approval.go
user: login: lizziemac
body: make 65536 a constant, we should try to avoid magic numbers
created_at: 2025-04-24T17:40:49Z
author_association: COLLABORATOR
diff_hunk: @@ -109,6 +106,16 @@ func (a *approvalEnvironment) createApprovalIssue(ctx context.Context) error {
 	}
 	a.approvalIssueNumber = a.approvalIssue.GetNumber()
 
+  bodyChunks := splitLongString(a.issueBody, 65536)
path: approval.go
user: login: snskArora
body: updated
created_at: 2025-04-26T06:26:18Z
author_association: COLLABORATOR

PR comments:
user: login: snskArora
created_at: 2025-03-22T14:21:23Z
body: This would resolve #89 
author_association: COLLABORATOR
user: login: snskArora
created_at: 2025-04-05T19:33:57Z
body: @lizziemac @beverts312, please review this PR. It would be a nice feature to have within the new release.
author_association: COLLABORATOR
user: login: lizziemac
created_at: 2025-04-21T16:23:01Z
body: @snskArora this looks good, and it works, however there are couple issues and questions:
- the reported max is `characters`, not `words`
- some users can see character limits of 200k+, depending on the zipped size of the issue/comment https://github.com/orgs/community/discussions/41331?utm_source=chatgpt.com#discussioncomment-4366258
- we should limit to characters, not words, and have that limit be 65536, even though it seems sometimes (even in your real-life test) it works beyond that, to ensure this is supported for everyone
- was the issue body supposed to be in the issue message, then split from there? or are we just automatically making it a comment if it's detected to be too big? your example you provide only has the message in the comments. 
author_association: COLLABORATOR
user: login: snskArora
created_at: 2025-04-23T19:49:07Z
body: - The looping is done over words, but the limit threshold is in characters only. I am processing over words to avoid breaking a word between two comments. The only bottleneck it creates is that if a word is more than 65536 characters, it will fail.

- Earlier, it was in a single comment, but now, since we are going to have an issue comment, I feel it would be better to have the approval body as a separate block than instructions on working with the approval, for better clarity.
author_association: COLLABORATOR
user: login: lizziemac
created_at: 2025-04-24T17:40:15Z
body: > The looping is done over words, but the limit threshold is in characters only. I am processing over words to avoid breaking a word between two comments. The only bottleneck it creates is that if a word is more than 65536 characters, it will fail.

ah my bad, I think I was a little quick when reviewing. please update the PR name though

> Earlier, it was in a single comment, but now, since we are going to have an issue comment, I feel it would be better to have the approval body as a separate block than instructions on working with the approval, for better clarity.

noted. I think that's fine
author_association: COLLABORATOR