Compare commits

...

3 Commits

Author SHA1 Message Date
iratusmachina 98268ffe0a Merge pull request 'Adding a new flag' (#2) from feature/adding_new_flag into main
Reviewed-on: #2
2024-04-20 10:54:25 +00:00
iratusmachina 1ff8fde838 fixing errcheck errors
ci/woodpecker/pr/pr/1 Pipeline was successful Details
ci/woodpecker/pr/pr/2 Pipeline was successful Details
2024-04-20 06:45:34 -04:00
iratusmachina 555dbc2e0d Adding a new flag
ci/woodpecker/pr/pr/1 Pipeline failed Details
ci/woodpecker/pr/pr/2 Pipeline failed Details
2024-04-19 17:23:37 -04:00
5 changed files with 184 additions and 48 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.env .env
bin bin
*.txt *.txt
artifacts
bin

View File

@ -7,13 +7,22 @@ steps:
image: golang:1.20-alpine image: golang:1.20-alpine
commands: commands:
- apk update - apk update
- apk add --no-cache make alpine-sdk g++ - apk add --no-cache make alpine-sdk g++ bash
- make build - make build
- echo "$${CI_COMMIT_TAG}" - echo "$${CI_COMMIT_TAG}"
- make release - make release
generate-changelog:
image: python:3.11-alpine
commands:
- apk update
- apk add --no-cache make alpine-sdk g++ bash
- make changelog-draft
release: release:
image: woodpeckerci/plugin-gitea-release image: woodpeckerci/plugin-gitea-release
when:
event: tag
settings: settings:
base_url: https://git.iratusmachina.com base_url: https://git.iratusmachina.com
files: files:
@ -25,3 +34,4 @@ steps:
skip_verify: true skip_verify: true
target: main target: main
checksum: sha256 checksum: sha256
note: "./draft_notes.md"

View File

@ -42,6 +42,14 @@ release:
rm -rf ${BIN_DIR}/tmp rm -rf ${BIN_DIR}/tmp
cd ${CURRENT_DIR} cd ${CURRENT_DIR}
.PHONY: changelog-full
changelog-full:
python3 generate_notes.py --full
.PHONY: changelog-draft
changelog-draft:
python3 generate_notes.py --draft
.PHONY: lint-all .PHONY: lint-all
lint-all: lint-all:
golangci-lint run --enable-all golangci-lint run --enable-all

75
generate_notes.py Normal file
View File

@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""
This module generates a draft_notes.md to be attached to a Gitea release and
a CHANGELOG.md to be committed into the repo.
It is the python version of this https://stackoverflow.com/a/46033999
"""
import subprocess
import argparse
def full():
with open("CHANGELOG.md", "w+") as fw:
try:
# Get all the tags sorted in decreasing order (the -creatordate is what sorts in decreasing order)
tags = subprocess.check_output(["git", "tag", "--sort=-creatordate"], text=True)
tags = [tag for tag in tags.split("\n") if tag]
# Get the remote url
remote_url = subprocess.check_output(["git", "remote", "get-url", "origin"], text=True)
# Remove the first occurence of the word git starting from the end of the string
remote_url = remote_url[0:remote_url.rfind(".git")]
previous_tag = ""
for tag in tags:
if previous_tag:
# Extract the date of the commit
tag_date = subprocess.check_output(["git", "log", "-1", f"--pretty=format:'%ad'", "--date=short", f"{tag}"], text=True)
tag_date = tag_date.replace("'", "")
# Get each commit of a tag formatted
formatted_lines = subprocess.check_output(["git", "log", f"{tag}...{previous_tag}", f'--pretty=format:"* %s [View]({remote_url}/commits/%H)"'], text=True)
if formatted_lines:
fw.write(f"## {tag} ({tag_date})\n\n")
# Remove merge commits or Changelog commits
lines = "\n".join([line.replace("\"", "") for line in formatted_lines.split("\n") if all(["merge" not in line.lower(), "changelog.md" not in line.lower()])])
fw.write(lines)
fw.write("\n\n")
previous_tag = tag
except subprocess.CalledProcessError as e:
print(f"Command failed with return code {e.returncode}")
def draft():
with open("draft_notes.md", "w+") as fw:
try:
# Get the remote url
remote_url = subprocess.check_output(["git", "remote", "get-url", "origin"], text=True)
# Remove the first occurence of the word git starting from the end of the string
remote_url = remote_url[0:remote_url.rfind(".git")]
# Get the current and previous tags
tags = subprocess.check_output(["git", "tag", "--sort=creatordate"], text=True)
tags = [tag for tag in tags.split("\n") if tag]
tags.reverse()
current_tag, previous_tag, *_ = tags
formatted_lines = subprocess.check_output(["git", "log", f"{current_tag}...{previous_tag}", f'--pretty=format:"* %s"'], text=True)
lines = "\n".join([line.replace("\"", "") for line in formatted_lines.split("\n") if all(["merge" not in line.lower(), "changelog.md" not in line.lower()])])
fw.write(lines)
fw.write("\n\n")
fw.write(f"Compare between recent changes: [{previous_tag[1:]}...{current_tag[1:]}]({remote_url}/compare/{previous_tag}...{current_tag})")
except subprocess.CalledProcessError as e:
print(f"Command failed with return code {e.returncode}")
def run():
parser = argparse.ArgumentParser(description='Generate changelogs....')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--full", help="Generate full changelog", action="store_true")
group.add_argument("--draft", help="Generate notes with tag", action="store_true")
args = parser.parse_args()
if args.full:
full()
elif args.draft:
draft()
if __name__ == "__main__":
run()

View File

@ -2,6 +2,7 @@ package cmdline
import ( import (
"bytes" "bytes"
"errors"
"flag" "flag"
"fmt" "fmt"
"log" "log"
@ -15,6 +16,8 @@ import (
var ( var (
printDebug bool printDebug bool
useGenius bool
useGoogle bool
outputFile string outputFile string
errorLog *log.Logger errorLog *log.Logger
mainLog *log.Logger mainLog *log.Logger
@ -29,69 +32,78 @@ func recurseNodes(top *html.Node, sb *strings.Builder) {
} }
} }
func searchGoogle(song string) error { func searchGoogle(song string) (err error) {
runOption := &playwright.RunOptions{ runOption := &playwright.RunOptions{
SkipInstallBrowsers: true, SkipInstallBrowsers: true,
} }
err := playwright.Install(runOption) tempErr := playwright.Install(runOption)
if err != nil { if tempErr != nil {
return fmt.Errorf("could not install playwright dependencies: %v", err) err = fmt.Errorf("could not install playwright dependencies: %v", tempErr)
return err
} }
pw, err := playwright.Run() pw, tempErr := playwright.Run()
if err != nil { if tempErr != nil {
return fmt.Errorf("could not start playwright: %v", err) err = fmt.Errorf("could not start playwright: %v", tempErr)
return err
} }
defer func(pw *playwright.Playwright) error { defer func(pw *playwright.Playwright) {
err := pw.Stop() tempErr := pw.Stop()
if err != nil { if tempErr != nil {
return fmt.Errorf("could not stop Playwright: %v", err) e := fmt.Errorf("could not stop Playwright: %v", tempErr)
err = errors.Join(err, e)
} }
return nil
}(pw) }(pw)
option := playwright.BrowserTypeLaunchOptions{ option := playwright.BrowserTypeLaunchOptions{
Channel: playwright.String("chrome"), Channel: playwright.String("chrome"),
Headless: playwright.Bool(false), Headless: playwright.Bool(false),
} }
browser, err := pw.Chromium.Launch(option) browser, tempErr := pw.Chromium.Launch(option)
if err != nil { if tempErr != nil {
return fmt.Errorf("could not launch browser: %v", err) err = fmt.Errorf("could not launch browser: %v", tempErr)
return err
} }
defer func(browser playwright.Browser) error { defer func(browser playwright.Browser) {
err = browser.Close() tempErr = browser.Close()
if err != nil { if tempErr != nil {
return fmt.Errorf("could not close browser: %v", err) e := fmt.Errorf("could not close browser: %v", tempErr)
err = errors.Join(err, e)
} }
return nil
}(browser) }(browser)
page, err := browser.NewPage() page, tempErr := browser.NewPage()
if err != nil { if tempErr != nil {
return fmt.Errorf("could not create page: %v", err) err = fmt.Errorf("could not create page: %v", tempErr)
return err
} }
if _, err := page.Goto(fmt.Sprintf("https://www.google.com/search?q=%ss+lyrics", song), if _, tempErr := page.Goto(fmt.Sprintf("https://www.google.com/search?q=%ss+lyrics", song),
playwright.PageGotoOptions{ playwright.PageGotoOptions{
WaitUntil: playwright.WaitUntilStateLoad, WaitUntil: playwright.WaitUntilStateLoad,
}); err != nil { }); tempErr != nil {
return fmt.Errorf("could not goto: %v", err) err = fmt.Errorf("could not goto: %v", tempErr)
return err
} }
err = page.Locator("body").WaitFor(playwright.LocatorWaitForOptions{ tempErr = page.Locator("body").WaitFor(playwright.LocatorWaitForOptions{
State: playwright.WaitForSelectorStateVisible, State: playwright.WaitForSelectorStateVisible,
}) })
if err != nil { if tempErr != nil {
return fmt.Errorf("could not wait for body: %v", err) err = fmt.Errorf("could not wait for body: %v", tempErr)
return err
} }
html, err := page.Locator("html").InnerHTML() html, tempErr := page.Locator("html").InnerHTML()
if err != nil { if tempErr != nil {
return fmt.Errorf("could not get innerHtml: %v", err) err = fmt.Errorf("could not get innerHtml: %v", tempErr)
return err
} }
doc, err := htmlquery.Parse(bytes.NewReader([]byte(html))) doc, tempErr := htmlquery.Parse(bytes.NewReader([]byte(html)))
if err != nil { if err != nil {
return fmt.Errorf("could not parse the innerHtml: %v", err) err = fmt.Errorf("could not parse the innerHtml: %v", tempErr)
return err
} }
nodes, err := htmlquery.QueryAll(doc, "//div[@data-lyricid]/div") nodes, tempErr := htmlquery.QueryAll(doc, "//div[@data-lyricid]/div")
if err != nil { if err != nil {
return fmt.Errorf("could not get the nodes: %v", err) err = fmt.Errorf("could not get the nodes: %v", tempErr)
return err
} }
var sb strings.Builder var sb strings.Builder
@ -103,9 +115,10 @@ func searchGoogle(song string) error {
mainLog.Println("Writing lyrics from Google...") mainLog.Println("Writing lyrics from Google...")
} }
filename := fmt.Sprintf("%s_google.txt", outputFile) filename := fmt.Sprintf("%s_google.txt", outputFile)
err = os.WriteFile(filename, []byte(sb.String()), os.ModePerm) tempErr = os.WriteFile(filename, []byte(sb.String()), os.ModePerm)
if err != nil { if tempErr != nil {
return fmt.Errorf("could not write to %s: %v", filename, err) err = fmt.Errorf("could not write to %s: %v", filename, err)
return err
} }
} else { } else {
mainLog.Println("Lyrics cannot be found...") mainLog.Println("Lyrics cannot be found...")
@ -128,11 +141,13 @@ func Main() int {
out := flags.Output() out := flags.Output()
fmt.Fprintf(out, "Usage: %v \n\n", programName) fmt.Fprintf(out, "Usage: %v \n\n", programName)
fmt.Fprint(out, " This program is used to download lyrics for a song\n") fmt.Fprint(out, " This program is used to download lyrics for a song\n")
fmt.Fprint(out, " from the internet. The steps of operation are shown here: \n\n") fmt.Fprint(out, " from the internet. There are two modes of operation.\n\n")
fmt.Fprint(out, " (a) It first opens a chrome window, searches for the lyrics \n") fmt.Fprint(out, " The first mode is scraping the lyrics from Google. So the operation is this: \n")
fmt.Fprint(out, " It first opens a chrome window, searches for the lyrics \n")
fmt.Fprint(out, " , and copies the lyrics returned by Google Search to a \n") fmt.Fprint(out, " , and copies the lyrics returned by Google Search to a \n")
fmt.Fprint(out, " file defined by you.\n\n") fmt.Fprint(out, " file defined by you.\n\n")
fmt.Fprint(out, " (b) It then tries to get search for the same song using the \n") fmt.Fprint(out, " The second mode is getting the lyrics from Genius API. So the operation is this: \n")
fmt.Fprint(out, " It then tries to get search for the same song using the \n")
fmt.Fprint(out, " Genius API. It then tries to compare the lyrics with the \n") fmt.Fprint(out, " Genius API. It then tries to compare the lyrics with the \n")
fmt.Fprint(out, " Genius one.\n\n") fmt.Fprint(out, " Genius one.\n\n")
flags.PrintDefaults() flags.PrintDefaults()
@ -142,8 +157,13 @@ func Main() int {
verboseFlag := flags.Bool("verbose", false, "Optional. Turn on debug. Default is false.") verboseFlag := flags.Bool("verbose", false, "Optional. Turn on debug. Default is false.")
searchFlag := flags.String("search", "", "Required. Name of song to search. If the name of the song is not a single word, put in quotes\"\"") searchFlag := flags.String("search", "", "Required. Name of song to search. If the name of the song is not a single word, put in quotes\"\"")
helpFlag := flags.Bool("help", false, "Optional. Print Usage") helpFlag := flags.Bool("help", false, "Optional. Print Usage")
useGoogleFlag := flags.Bool("google", false, "Optional. Use google.")
useGeniusFlag := flags.Bool("genius", false, "Optional. Use genius")
flags.Parse(os.Args[1:]) err := flags.Parse(os.Args[1:])
if err != nil {
return 1
}
if len(flags.Args()) > 1 { if len(flags.Args()) > 1 {
errorLog.Println("Error: too many command-line arguments") errorLog.Println("Error: too many command-line arguments")
@ -159,6 +179,18 @@ func Main() int {
return 1 return 1
} }
if !allSetFlags["google"] && !allSetFlags["genius"] {
errorLog.Println("Error: One of -google or -genius must be set")
flags.Usage()
return 1
}
if allSetFlags["google"] && allSetFlags["genius"] {
errorLog.Println("Error: if -google is set, -genius must remain unset and vice versa")
flags.Usage()
return 1
}
if *helpFlag { if *helpFlag {
flags.Usage() flags.Usage()
return 0 return 0
@ -184,10 +216,19 @@ func Main() int {
mainLog.Printf("Output flag: %s, Debug flag: %t, Search flag: %s\n", outputFile, printDebug, songToSearch) mainLog.Printf("Output flag: %s, Debug flag: %t, Search flag: %s\n", outputFile, printDebug, songToSearch)
} }
err := searchGoogle(songToSearch) useGenius = *useGeniusFlag
if err != nil { useGoogle = *useGoogleFlag
errorLog.Printf("Err: %+v", err)
return 1 if useGoogle {
err := searchGoogle(songToSearch)
if err != nil {
errorLog.Printf("Err: %+v", err)
return 1
}
}
if useGenius {
fmt.Println("No op")
} }
return 0 return 0