package main import ( "bytes" "encoding/json" "errors" "flag" "fmt" "io" "net/http" "net/url" "os" "os/exec" "path/filepath" "strconv" "strings" "time" ) const ( versionFile = "../../internal/version/version.txt" changelogFile = "../../../docs/src/content/docs/changelog.mdx" defaultReleaseBranch = "v3-alpha" defaultReleaseTitle = "Wails %s" defaultReleaseTarget = "v3-alpha" githubDefaultAPI = "https://api.github.com" githubAPIVersion = "2022-11-28" ) var ( unreleasedChangelogFile = "../../UNRELEASED_CHANGELOG.md" ) type releaseOptions struct { version string dryRun bool branch string target string } var errNoUnreleasedContent = errors.New("No unreleased changelog content found.") func checkError(err error) { if err != nil { println(err.Error()) os.Exit(1) } } // getUnreleasedChangelogTemplate returns the template content for UNRELEASED_CHANGELOG.md func getUnreleasedChangelogTemplate() string { return `# Unreleased Changes ## Added ## Changed ## Fixed ## Deprecated ## Removed ## Security --- ### Example Entries: **Added:** - Add support for custom window icons in application options - Add new ` + "`SetWindowIcon()`" + ` method to runtime API (#1234) **Changed:** - Update minimum Go version requirement to 1.21 - Improve error messages for invalid configuration files **Fixed:** - Fix memory leak in event system during window close operations (#5678) - Fix crash when using context menus on Linux with Wayland **Security:** - Update dependencies to address CVE-2024-12345 in third-party library ` } // clearUnreleasedChangelog clears the UNRELEASED_CHANGELOG.md file and resets it with the template func clearUnreleasedChangelog() error { template := getUnreleasedChangelogTemplate() // Write the template back to the file err := os.WriteFile(unreleasedChangelogFile, []byte(template), 0o644) if err != nil { return fmt.Errorf("failed to reset UNRELEASED_CHANGELOG.md: %w", err) } fmt.Printf("Successfully reset %s with template content\n", unreleasedChangelogFile) return nil } // extractChangelogContent extracts the actual changelog content from UNRELEASED_CHANGELOG.md // It returns the content between the section headers and the example section func extractChangelogContent() (string, error) { content, err := os.ReadFile(unreleasedChangelogFile) if err != nil { return "", fmt.Errorf("failed to read %s: %w", unreleasedChangelogFile, err) } contentStr := string(content) lines := strings.Split(contentStr, "\n") var result []string var inExampleSection bool var inCommentBlock bool var hasActualContent bool var currentSection string for i, line := range lines { trimmedLine := strings.TrimSpace(line) // Track comment blocks (handle multi-line comments) if strings.Contains(line, "") { inCommentBlock = false } continue } if inCommentBlock { if strings.Contains(line, "-->") { inCommentBlock = false } continue } // Skip the main title if strings.HasPrefix(trimmedLine, "# Unreleased Changes") { continue } // Check if we're entering the example section if strings.HasPrefix(trimmedLine, "---") || strings.HasPrefix(trimmedLine, "### Example Entries") { inExampleSection = true continue } // Skip example section content if inExampleSection { continue } // Handle section headers if strings.HasPrefix(trimmedLine, "##") { currentSection = trimmedLine // Only include section headers that have content after them // We'll add it later if we find content continue } // Handle bullet points if strings.HasPrefix(trimmedLine, "-") || strings.HasPrefix(trimmedLine, "*") { // Check if this is actual content (not empty) content := strings.TrimSpace(trimmedLine[1:]) if content != "" { // If this is the first content in a section, add the section header first if currentSection != "" { // Only add empty line if this isn't the first section if len(result) > 0 { result = append(result, "") } result = append(result, currentSection) currentSection = "" // Reset so we don't add it again } result = append(result, line) hasActualContent = true } } else if trimmedLine != "" && !strings.HasPrefix(trimmedLine, "