From ab25618819345fa0b8f0a72123158a08db58cd56 Mon Sep 17 00:00:00 2001 From: Haolan Date: Thu, 10 Apr 2025 16:45:31 +0800 Subject: [PATCH 01/22] chore: stop uploading zip to assets --- internal/actions/api.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/actions/api.go b/internal/actions/api.go index 7905dcd..86385d1 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -552,12 +552,8 @@ func (d *DefaultClient) Release() { err = file.Zip(tempDir, zipFilePath) must(err) - release := d.getReleaseByTag(version) - - // upload file to release - err = d.uploadFileToRelease(zipFilePath, release) - must(err) + // upload to artifacts in GitHub Action } // CreateBranchFromLabel creates release branch based on label format From 8554ee322954d180f8ce17b08c1575866f393250 Mon Sep 17 00:00:00 2001 From: Haolan Date: Thu, 10 Apr 2025 17:30:40 +0800 Subject: [PATCH 02/22] feat: download all artifacts to release --- internal/actions/actions.go | 11 +++++ internal/actions/api.go | 87 +++++++++++++++++++++++++++++++------ 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 8f0deaf..9363511 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -10,6 +10,7 @@ import ( "runtime" "slices" "sort" + "strconv" "strings" "sync" @@ -272,3 +273,13 @@ func LatestCommitSHA() string { } return sha } + +func WorkflowID() int64 { + runId := os.Getenv("GITHUB_RUN_ID") + if runId == "" { + panic("no GITHUB_RUN_ID found") + } + id, err := strconv.ParseInt(runId, 10, 64) + must(err) + return id +} diff --git a/internal/actions/api.go b/internal/actions/api.go index 86385d1..daddbb7 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -4,11 +4,14 @@ package actions import ( "context" "fmt" + "io" "net/http" "os" + "path" "path/filepath" "regexp" "strings" + "sync" "time" "github.com/google/go-github/v69/github" @@ -347,31 +350,76 @@ func (d *DefaultClient) createReleaseByTag(tag string) *github.RepositoryRelease return release } -func (d *DefaultClient) getReleaseByTag(tag string) *github.RepositoryRelease { +func (d *DefaultClient) uploadFileToRelease(wg *sync.WaitGroup, fs *os.File, release *github.RepositoryRelease) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) + defer wg.Done() defer cancel() + defer fs.Close() - release, _, err := d.client.Repositories.GetReleaseByTag(ctx, d.owner, d.repo, tag) + _, _, err := d.client.Repositories.UploadReleaseAsset( + ctx, d.owner, d.repo, release.GetID(), + &github.UploadOptions{ + Name: filepath.Base(fs.Name()), + }, fs) must(err) - // ok we get the relase entry - return release } -func (d *DefaultClient) uploadFileToRelease(fileName string, release *github.RepositoryRelease) error { +func (d *DefaultClient) downloadArtifactTo(fileCh chan *os.File, dir string, artifactID int64) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() - fs, err := os.Open(fileName) + url, _, err := d.client.Actions.DownloadArtifact(ctx, d.owner, d.repo, + artifactID, 0) + must(err) - defer fs.Close() - _, _, err = d.client.Repositories.UploadReleaseAsset( - ctx, d.owner, d.repo, release.GetID(), - &github.UploadOptions{ - Name: filepath.Base(fs.Name()), - }, fs) + httpClient := &http.Client{Timeout: 30 * time.Second} - return err + resp, err := httpClient.Get(url.Path) + must(err) + defer resp.Body.Close() + + filePath := filepath.Join(dir, path.Base(url.Path)) + + localFile, err := os.Create(filePath) + must(err) + + fmt.Printf("Download %s to %s\n", url.Path, filePath) + + _, err = io.Copy(localFile, resp.Request.Body) + must(err) + + fileCh <- localFile +} + +func (d *DefaultClient) downloadArtifacts() (files []*os.File) { + ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) + defer cancel() + + artifacts, _, err := d.client.Actions.ListWorkflowRunArtifacts(ctx, d.owner, d.repo, + WorkflowID(), &github.ListOptions{}) + + must(err) + + if artifacts.GetTotalCount() == 0 { + panic("no artifact found") + } + + tempDir, err := os.MkdirTemp("", "artfacts") + must(err) + + fileCh := make(chan *os.File, len(artifacts.Artifacts)) + + for _, artifact := range artifacts.Artifacts { + go d.downloadArtifactTo(fileCh, tempDir, artifact.GetID()) + } + + for range artifacts.Artifacts { + file := <-fileCh + + files = append(files, file) + } + return } // removeBranch deletes a branch from the repository @@ -501,7 +549,18 @@ func (d *DefaultClient) Postprocessing() { } // create a release - d.createReleaseByTag(version) + release := d.createReleaseByTag(version) + + files := d.downloadArtifacts() + + var wg sync.WaitGroup + wg.Add(len(files)) + + for _, file := range files { + go d.uploadFileToRelease(&wg, file, release) + } + + wg.Wait() // we have finished tagging the commit, safe to remove the branch if branchName, isLegacy := d.isLegacyVersion(); isLegacy { From 9132172e074f1123e328d98329242ddd97755627 Mon Sep 17 00:00:00 2001 From: Haolan Date: Thu, 10 Apr 2025 17:33:30 +0800 Subject: [PATCH 03/22] feat: output zipFilePath to env for uploading artifact --- internal/actions/api.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/actions/api.go b/internal/actions/api.go index daddbb7..371c6b7 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -613,6 +613,9 @@ func (d *DefaultClient) Release() { must(err) // upload to artifacts in GitHub Action + Setenv(map[string]string{ + "BIN_PATH": zipFilePath, + }) } // CreateBranchFromLabel creates release branch based on label format From f7dd0c8625faf2b885da3946e06c3333dae92076 Mon Sep 17 00:00:00 2001 From: Meteor Date: Fri, 11 Apr 2025 05:21:33 +0000 Subject: [PATCH 04/22] feat: upload concurrently --- internal/actions/api.go | 73 +++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 43 deletions(-) diff --git a/internal/actions/api.go b/internal/actions/api.go index 371c6b7..a126d48 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -5,9 +5,9 @@ import ( "context" "fmt" "io" + "mime" "net/http" "os" - "path" "path/filepath" "regexp" "strings" @@ -350,22 +350,23 @@ func (d *DefaultClient) createReleaseByTag(tag string) *github.RepositoryRelease return release } -func (d *DefaultClient) uploadFileToRelease(wg *sync.WaitGroup, fs *os.File, release *github.RepositoryRelease) { +func (d *DefaultClient) uploadToRelease(fileName string, size int64, reader io.Reader, release *github.RepositoryRelease) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) - defer wg.Done() defer cancel() - defer fs.Close() - _, _, err := d.client.Repositories.UploadReleaseAsset( - ctx, d.owner, d.repo, release.GetID(), - &github.UploadOptions{ - Name: filepath.Base(fs.Name()), - }, fs) + url := fmt.Sprintf("repos/%s/%s/releases/%d/assets?name=%s", d.owner, d.repo, release.GetID(), fileName) + + req, err := d.client.NewUploadRequest(url, reader, size, "application/zip") + must(err) + + asset := new(github.ReleaseAsset) + _, err = d.client.Do(ctx, req, asset) must(err) } -func (d *DefaultClient) downloadArtifactTo(fileCh chan *os.File, dir string, artifactID int64) { +func (d *DefaultClient) uploadArtifact(wg *sync.WaitGroup, artifactID int64, release *github.RepositoryRelease) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) + defer wg.Done() defer cancel() url, _, err := d.client.Actions.DownloadArtifact(ctx, d.owner, d.repo, @@ -375,24 +376,25 @@ func (d *DefaultClient) downloadArtifactTo(fileCh chan *os.File, dir string, art httpClient := &http.Client{Timeout: 30 * time.Second} - resp, err := httpClient.Get(url.Path) + resp, err := httpClient.Get(url.String()) must(err) defer resp.Body.Close() - filePath := filepath.Join(dir, path.Base(url.Path)) - - localFile, err := os.Create(filePath) + disposition := resp.Header.Get("Content-Disposition") + _, params, err := mime.ParseMediaType(disposition) must(err) - fmt.Printf("Download %s to %s\n", url.Path, filePath) + fileName, ok := params["filename"] + if !ok { + panic("no filename found in Content-Disposition") + } - _, err = io.Copy(localFile, resp.Request.Body) - must(err) + fmt.Printf("Upload %s to %s\n", fileName, release.GetName()) - fileCh <- localFile + d.uploadToRelease(fileName, resp.ContentLength, resp.Body, release) } -func (d *DefaultClient) downloadArtifacts() (files []*os.File) { +func (d *DefaultClient) uploadArtifactsToRelease(release *github.RepositoryRelease) (files []*os.File) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -405,20 +407,12 @@ func (d *DefaultClient) downloadArtifacts() (files []*os.File) { panic("no artifact found") } - tempDir, err := os.MkdirTemp("", "artfacts") - must(err) - - fileCh := make(chan *os.File, len(artifacts.Artifacts)) - + var wg sync.WaitGroup + wg.Add(len(artifacts.Artifacts)) for _, artifact := range artifacts.Artifacts { - go d.downloadArtifactTo(fileCh, tempDir, artifact.GetID()) - } - - for range artifacts.Artifacts { - file := <-fileCh - - files = append(files, file) + go d.uploadArtifact(&wg, artifact.GetID(), release) } + wg.Wait() return } @@ -551,16 +545,7 @@ func (d *DefaultClient) Postprocessing() { // create a release release := d.createReleaseByTag(version) - files := d.downloadArtifacts() - - var wg sync.WaitGroup - wg.Add(len(files)) - - for _, file := range files { - go d.uploadFileToRelease(&wg, file, release) - } - - wg.Wait() + d.uploadArtifactsToRelease(release) // we have finished tagging the commit, safe to remove the branch if branchName, isLegacy := d.isLegacyVersion(); isLegacy { @@ -607,14 +592,16 @@ func (d *DefaultClient) Release() { file.RemovePattern(filepath.Join(tempDir, "*.pc")) file.RemovePattern(filepath.Join(tempDir, "*.sh")) - zipFilePath, _ := filepath.Abs(binaryZip(uc.Pkg.Name)) + zipFilename := binaryZip(uc.Pkg.Name) + zipFilePath, _ := filepath.Abs(zipFilename) err = file.Zip(tempDir, zipFilePath) must(err) // upload to artifacts in GitHub Action Setenv(map[string]string{ - "BIN_PATH": zipFilePath, + "BIN_PATH": zipFilePath, + "BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"), }) } From cdc6e1214c66ad45c05341d24bbd084e3eb89bfb Mon Sep 17 00:00:00 2001 From: Meteor Date: Fri, 11 Apr 2025 07:18:38 +0000 Subject: [PATCH 05/22] refactor: decouple mappedversion and tag --- internal/actions/actions.go | 46 +++------------- internal/actions/api.go | 53 +++++++++---------- internal/actions/api_test.go | 17 ------ .../actions/mappedversion/mappedversion.go | 42 +++++++++++++++ internal/actions/tag/tag.go | 38 +++++++++++++ internal/actions/tag/tag_test.go | 31 +++++++++++ 6 files changed, 145 insertions(+), 82 deletions(-) create mode 100644 internal/actions/mappedversion/mappedversion.go create mode 100644 internal/actions/tag/tag.go create mode 100644 internal/actions/tag/tag_test.go diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 9363511..593773e 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -5,7 +5,6 @@ import ( "fmt" "log" "os" - "os/exec" "path/filepath" "runtime" "slices" @@ -80,48 +79,11 @@ func IssueEvent() map[string]any { return issue } -// tagRef constructs full Git tag reference string (e.g. "refs/tags/v1.0.0") -func tagRef(tag string) string { - return "refs/tags/" + strings.TrimSpace(tag) -} - // branchRef generates full Git branch reference string (e.g. "refs/heads/main") func branchRef(branchName string) string { return "refs/heads/" + strings.TrimSpace(branchName) } -// hasTag checks if specified Git tag exists in repository -func hasTag(tag string) bool { - _, err := exec.Command("git", "rev-parse", tagRef(tag)).CombinedOutput() - return err == nil -} - -// shaFromTag retrieves commit SHA for given Git tag -// Panics if tag doesn't exist -func shaFromTag(tag string) string { - ret, err := exec.Command("git", "rev-list", "-n", "1", tag).CombinedOutput() - if err != nil { - log.Fatalf("cannot find a tag: %s %s", tag, string(ret)) - } - return strings.TrimSpace(string(ret)) -} - -// parseMappedVersion splits the mapped version string into library name and version. -// Input format: "clib/semver" where semver starts with 'v' -// Panics if input format is invalid or version isn't valid semantic version -func parseMappedVersion(version string) (clib, mappedVersion string) { - arr := strings.Split(version, "/") - if len(arr) != 2 { - panic("invalid mapped version format") - } - clib, mappedVersion = arr[0], arr[1] - - if !semver.IsValid(mappedVersion) { - panic("invalid mapped version format: mappedVersion is not a semver") - } - return -} - // isValidLLPkg checks if directory contains both llpkg.cfg and llcppg.cfg func isValidLLPkg(files []os.DirEntry) bool { fileMap := make(map[string]struct{}, len(files)) @@ -134,6 +96,14 @@ func isValidLLPkg(files []os.DirEntry) bool { return hasLLCppg && hasLLPkg } +func mustTrimPrefix(s, prefix string) string { + result := strings.TrimPrefix(s, prefix) + if result == s { + log.Fatalf("invalid format: %s", result) + } + return result +} + // checkLegacyVersion validates versioning strategy for legacy package submissions // Ensures semantic versioning compliance and proper branch maintenance strategy func checkLegacyVersion(ver *versions.Versions, cfg config.LLPkgConfig, mappedVersion string, isLegacy bool) { diff --git a/internal/actions/api.go b/internal/actions/api.go index a126d48..521e2a0 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -16,6 +16,8 @@ import ( "github.com/google/go-github/v69/github" "github.com/goplus/llpkgstore/config" + "github.com/goplus/llpkgstore/internal/actions/mappedversion" + "github.com/goplus/llpkgstore/internal/actions/tag" "github.com/goplus/llpkgstore/internal/actions/versions" "github.com/goplus/llpkgstore/internal/file" "github.com/goplus/llpkgstore/internal/pc" @@ -217,22 +219,25 @@ func (d *DefaultClient) removeLabel(labelName string) { // Panics: // // If no valid version found in PR commits -func (d *DefaultClient) checkMappedVersion(packageName string) (mappedVersion string) { +func (d *DefaultClient) checkMappedVersion(packageName string) mappedversion.MappedVersion { matchMappedVersion := regex(packageName) + var rawMappedVersion string + for _, commit := range d.currentPRCommit() { message := commit.GetCommit().GetMessage() - if mappedVersion = matchMappedVersion.FindString(message); mappedVersion != "" { + if rawMappedVersion = matchMappedVersion.FindString(message); rawMappedVersion != "" { // remove space, of course - mappedVersion = strings.TrimSpace(mappedVersion) + rawMappedVersion = strings.TrimSpace(rawMappedVersion) break } } - if mappedVersion == "" { + if rawMappedVersion == "" { panic("no MappedVersion found in the PR") } - return + + return mappedversion.From(rawMappedVersion) } // commitMessage retrieves commit details by SHA @@ -270,10 +275,8 @@ func (d *DefaultClient) mappedVersion() string { if mappedVersion == "" { return "" } - version := strings.TrimPrefix(mappedVersion, MappedVersionPrefix) - if version == mappedVersion { - panic("invalid format") - } + version := mustTrimPrefix(mappedVersion, MappedVersionPrefix) + return strings.TrimSpace(version) } @@ -286,12 +289,12 @@ func (d *DefaultClient) mappedVersion() string { // Returns: // // error: Error during tag creation -func (d *DefaultClient) createTag(tag, sha string) error { +func (d *DefaultClient) createTag(versionTag tag.Tag, sha string) error { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() // tag the commit - tagRefName := tagRef(tag) + tagRefName := versionTag.Ref() _, _, err := d.client.Git.CreateRef(ctx, d.owner, d.repo, &github.Reference{ Ref: &tagRefName, Object: &github.GitObject{ @@ -441,8 +444,7 @@ func (d *DefaultClient) removeBranch(branchName string) error { func (d *DefaultClient) checkVersion(ver *versions.Versions, cfg config.LLPkgConfig) { // 4. Check MappedVersion version := d.checkMappedVersion(cfg.Upstream.Package.Name) - _, mappedVersion := parseMappedVersion(version) - + _, mappedVersion := version.MustParse() // 5. Check version is valid _, isLegacy := d.isLegacyVersion() checkLegacyVersion(ver, cfg, mappedVersion, isLegacy) @@ -524,7 +526,7 @@ func (d *DefaultClient) Postprocessing() { panic("no mapped version found in the commit message") } - clib, mappedVersion := parseMappedVersion(version) + clib, mappedVersion := mappedversion.From(version).MustParse() // the pr has merged, so we can read it. cfg, err := config.ParseLLPkgConfig(filepath.Join(clib, "llpkg.cfg")) @@ -534,11 +536,13 @@ func (d *DefaultClient) Postprocessing() { ver := versions.Read("llpkgstore.json") ver.Write(clib, cfg.Upstream.Package.Version, mappedVersion) - if hasTag(version) { + versionTag := tag.From(version) + + if versionTag.Exist() { panic("tag has already existed") } - if err := d.createTag(version, sha); err != nil { + if err := d.createTag(versionTag, sha); err != nil { panic(err) } @@ -561,7 +565,7 @@ func (d *DefaultClient) Release() { panic("no mapped version found in the commit message") } - clib, _ := parseMappedVersion(version) + clib, _ := mappedversion.From(version).MustParse() // the pr has merged, so we can read it. cfg, err := config.ParseLLPkgConfig(filepath.Join(clib, "llpkg.cfg")) must(err) @@ -609,20 +613,15 @@ func (d *DefaultClient) Release() { // Follows naming convention: release-branch./ func (d *DefaultClient) CreateBranchFromLabel(labelName string) { // design: branch:release-branch.{CLibraryName}/{MappedVersion} - branchName := strings.TrimPrefix(strings.TrimSpace(labelName), LabelPrefix) - if branchName == labelName { - panic("invalid label name format") - } + branchName := mustTrimPrefix(strings.TrimSpace(labelName), LabelPrefix) // fast-path: branch exists, can skip. if d.hasBranch(branchName) { return } - version := strings.TrimPrefix(branchName, BranchPrefix) - if version == branchName { - panic("invalid label name format") - } - clib, _ := parseMappedVersion(version) + version := mustTrimPrefix(branchName, BranchPrefix) + + clib, _ := mappedversion.From(version).MustParse() // slow-path: check the condition if we can create a branch // // create a branch only when this version is legacy. @@ -640,7 +639,7 @@ func (d *DefaultClient) CreateBranchFromLabel(labelName string) { panic("c version dones't follow semver, skip maintaining.") } - err := d.createBranch(branchName, shaFromTag(version)) + err := d.createBranch(branchName, tag.From(version).SHA()) must(err) } diff --git a/internal/actions/api_test.go b/internal/actions/api_test.go index 3ecef0b..e586cf9 100644 --- a/internal/actions/api_test.go +++ b/internal/actions/api_test.go @@ -2,7 +2,6 @@ package actions import ( "os" - "os/exec" "strings" "testing" @@ -10,22 +9,6 @@ import ( "github.com/goplus/llpkgstore/internal/actions/versions" ) -func TestHasTag(t *testing.T) { - if hasTag("aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9") { - t.Error("unexpected tag") - } - exec.Command("git", "tag", "aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9").Run() - if !hasTag("aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9") { - t.Error("tag doesn't exist") - } - ret, _ := exec.Command("git", "tag").CombinedOutput() - t.Log(string(ret)) - exec.Command("git", "tag", "-d", "aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9").Run() - if hasTag("aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9") { - t.Error("unexpected tag") - } -} - func recoverFn(branchName string, fn func(legacy bool)) (ret any) { defer func() { ret = recover() diff --git a/internal/actions/mappedversion/mappedversion.go b/internal/actions/mappedversion/mappedversion.go new file mode 100644 index 0000000..65c85dd --- /dev/null +++ b/internal/actions/mappedversion/mappedversion.go @@ -0,0 +1,42 @@ +package mappedversion + +import ( + "errors" + "strings" + + "golang.org/x/mod/semver" +) + +var ( + ErrVersionFormat = errors.New("invalid mapped version format: mappedVersion is not a semver") +) + +type MappedVersion string + +func From(version string) MappedVersion { + return MappedVersion(version) +} + +// Parse splits the mapped version string into library name and version. +// Input format: "clib/semver" where semver starts with 'v' +// Panics if input format is invalid or version isn't valid semantic version +func (m MappedVersion) Parse() (clib, mappedVersion string, err error) { + arr := strings.Split(string(m), "/") + if len(arr) != 2 { + panic("invalid mapped version format") + } + clib, mappedVersion = arr[0], arr[1] + + if !semver.IsValid(mappedVersion) { + err = ErrVersionFormat + } + return +} + +func (m MappedVersion) MustParse() (clib, mappedVersion string) { + clib, mappedVersion, err := m.Parse() + if err != nil { + panic(err) + } + return +} diff --git a/internal/actions/tag/tag.go b/internal/actions/tag/tag.go new file mode 100644 index 0000000..99fe3b2 --- /dev/null +++ b/internal/actions/tag/tag.go @@ -0,0 +1,38 @@ +package tag + +import ( + "log" + "os/exec" + "strings" +) + +type Tag string + +func From(tag string) Tag { + return Tag(tag) +} + +// SHA retrieves commit SHA for given Git tag +// Panics if tag doesn't exist +func (t Tag) SHA() string { + ret, err := exec.Command("git", "rev-list", "-n", "1", t.String()).CombinedOutput() + if err != nil { + log.Fatalf("cannot find a tag: %s %s", t, string(ret)) + } + return strings.TrimSpace(string(ret)) +} + +// Exist checks if specified Git tag exists in repository +func (t Tag) Exist() bool { + _, err := exec.Command("git", "rev-parse", t.Ref()).CombinedOutput() + return err == nil +} + +// tagRef constructs full Git tag reference string (e.g. "refs/tags/v1.0.0") +func (t Tag) Ref() string { + return "refs/tags/" + strings.TrimSpace(t.String()) +} + +func (t Tag) String() string { + return string(t) +} diff --git a/internal/actions/tag/tag_test.go b/internal/actions/tag/tag_test.go new file mode 100644 index 0000000..adccf6b --- /dev/null +++ b/internal/actions/tag/tag_test.go @@ -0,0 +1,31 @@ +package tag + +import ( + "os/exec" + "strings" + "testing" +) + +func TestTag(t *testing.T) { + tg := From("aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9") + if tg.Exist() { + t.Error("unexpected tag") + } + if tg.Ref() != "refs/tags/aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9" { + t.Errorf("unexpected tag ref: want %s got %s", "aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9", tg.Ref()) + } + exec.Command("git", "tag", "aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9").Run() + if !tg.Exist() { + t.Error("tag doesn't exist") + } + ret, _ := exec.Command("git", "rev-parse", "HEAD").CombinedOutput() + if strings.TrimSpace(string(ret)) != tg.SHA() { + t.Errorf("unexpected tag SHA: want %s got %s", string(ret), tg.SHA()) + } + ret, _ = exec.Command("git", "tag").CombinedOutput() + t.Log(string(ret)) + exec.Command("git", "tag", "-d", "aaaaaaaaaaa1.1.4.5.1.4.1.9.1.9").Run() + if tg.Exist() { + t.Error("unexpected tag") + } +} From fed8e6bee2ab43a5ccff5adb61441a95a1ce6108 Mon Sep 17 00:00:00 2001 From: Meteor Date: Fri, 11 Apr 2025 07:57:00 +0000 Subject: [PATCH 06/22] chore: add mappedversion test --- .../actions/mappedversion/mappedversion.go | 6 ++-- .../mappedversion/mappedversion_test.go | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 internal/actions/mappedversion/mappedversion_test.go diff --git a/internal/actions/mappedversion/mappedversion.go b/internal/actions/mappedversion/mappedversion.go index 65c85dd..4ff39d1 100644 --- a/internal/actions/mappedversion/mappedversion.go +++ b/internal/actions/mappedversion/mappedversion.go @@ -8,7 +8,8 @@ import ( ) var ( - ErrVersionFormat = errors.New("invalid mapped version format: mappedVersion is not a semver") + ErrVersionFormat = errors.New("invalid mapped version format") + ErrMappedVersionFormat = errors.New("invalid mapped version format: mappedVersion is not a semver") ) type MappedVersion string @@ -23,7 +24,8 @@ func From(version string) MappedVersion { func (m MappedVersion) Parse() (clib, mappedVersion string, err error) { arr := strings.Split(string(m), "/") if len(arr) != 2 { - panic("invalid mapped version format") + err = ErrVersionFormat + return } clib, mappedVersion = arr[0], arr[1] diff --git a/internal/actions/mappedversion/mappedversion_test.go b/internal/actions/mappedversion/mappedversion_test.go new file mode 100644 index 0000000..a4ac363 --- /dev/null +++ b/internal/actions/mappedversion/mappedversion_test.go @@ -0,0 +1,31 @@ +package mappedversion + +import "testing" + +func TestMappedVersion(t *testing.T) { + t.Run("invalid-1", func(t *testing.T) { + _, _, err := From("cjson").Parse() + if err == nil { + t.Errorf("unpexted behavior: no error") + } + }) + t.Run("invalid-2", func(t *testing.T) { + _, _, err := From("cjson/").Parse() + if err == nil { + t.Errorf("unpexted behavior: no error") + } + }) + + t.Run("valid", func(t *testing.T) { + clib, version, err := From("cjson/v1.0.0").Parse() + if err != nil { + t.Errorf("unpexted error: %v", err) + } + if clib != "cjson" { + t.Errorf("unpexted clib: want %s got %s", "cjson", clib) + } + if version != "v1.0.0" { + t.Errorf("unpexted clib: want %s got %s", "v1.0.0", version) + } + }) +} From 8fcc443ef95e2bfed0ce56ceabaf871014c7b054 Mon Sep 17 00:00:00 2001 From: Meteor Date: Fri, 11 Apr 2025 07:57:47 +0000 Subject: [PATCH 07/22] chore: add mappedversion test --- internal/actions/mappedversion/mappedversion_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/actions/mappedversion/mappedversion_test.go b/internal/actions/mappedversion/mappedversion_test.go index a4ac363..559a4e7 100644 --- a/internal/actions/mappedversion/mappedversion_test.go +++ b/internal/actions/mappedversion/mappedversion_test.go @@ -16,6 +16,13 @@ func TestMappedVersion(t *testing.T) { } }) + t.Run("invalid-3", func(t *testing.T) { + _, _, err := From("cjson/1.7.18").Parse() + if err == nil { + t.Errorf("unpexted behavior: no error") + } + }) + t.Run("valid", func(t *testing.T) { clib, version, err := From("cjson/v1.0.0").Parse() if err != nil { From e4f5fda7cf3d983481b6bf490027888623ea5036 Mon Sep 17 00:00:00 2001 From: Meteor Date: Fri, 11 Apr 2025 08:54:33 +0000 Subject: [PATCH 08/22] chore: adjust non-export global vars --- internal/actions/actions.go | 16 +++++++++++----- internal/actions/api.go | 12 ++++-------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 593773e..6d5d522 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -18,12 +18,14 @@ import ( "golang.org/x/mod/semver" ) -// GitHubEvent caches parsed GitHub event data from GITHUB_EVENT_PATH -var GitHubEvent = sync.OnceValue(parseGitHubEvent) +var ( + // GitHubEvent caches parsed GitHub event data from GITHUB_EVENT_PATH + GitHubEvent = sync.OnceValue(parseGitHubEvent) -// In our previous design, each platform should generate *_{OS}_{Arch}.go file -// Feb 12th, this design revoked, still keep the code. -var currentSuffix = runtime.GOOS + "_" + runtime.GOARCH + // In our previous design, each platform should generate *_{OS}_{Arch}.go file + // Feb 12th, this design revoked, still keep the code. + _currentSuffix = runtime.GOOS + "_" + runtime.GOARCH +) // must panics if the error is non-nil, halting execution func must(err error) { @@ -32,6 +34,10 @@ func must(err error) { } } +func binaryZip(packageName string) string { + return fmt.Sprintf("%s_%s.zip", packageName, _currentSuffix) +} + // envToString converts environment variables map to newline-separated key=value pairs for GitHub Actions func envToString(envm map[string]string) string { var env []string diff --git a/internal/actions/api.go b/internal/actions/api.go index 521e2a0..4dddb51 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -28,8 +28,8 @@ const ( BranchPrefix = "release-branch." MappedVersionPrefix = "Release-as: " - defaultReleaseBranch = "main" - regexString = `Release-as:\s%s/v(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?` + _defaultReleaseBranch = "main" + _regexString = `Release-as:\s%s/v(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?` ) // regex compiles a regular expression pattern to detect "Release-as" directives in commit messages @@ -43,11 +43,7 @@ const ( func regex(packageName string) *regexp.Regexp { // format: Release-as: clib/semver(with v prefix) // Must have one space in the end of Release-as: - return regexp.MustCompile(fmt.Sprintf(regexString, packageName)) -} - -func binaryZip(packageName string) string { - return fmt.Sprintf("%s_%s.zip", packageName, currentSuffix) + return regexp.MustCompile(fmt.Sprintf(_regexString, packageName)) } // DefaultClient provides GitHub API client capabilities with authentication for Actions workflows @@ -333,7 +329,7 @@ func (d *DefaultClient) createReleaseByTag(tag string) *github.RepositoryRelease ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() - branch := defaultReleaseBranch + branch := _defaultReleaseBranch makeLatest := "true" if _, isLegacy := d.isLegacyVersion(); isLegacy { From 72ddfcf8b6a83b8fb854d3d12148340e588170f9 Mon Sep 17 00:00:00 2001 From: Meteor Date: Fri, 11 Apr 2025 09:05:35 +0000 Subject: [PATCH 09/22] chore: move env related code --- cmd/llpkgstore/internal/verification.go | 2 +- internal/actions/actions.go | 86 ---------------------- internal/actions/api.go | 2 +- internal/actions/env.go | 95 +++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 88 deletions(-) create mode 100644 internal/actions/env.go diff --git a/cmd/llpkgstore/internal/verification.go b/cmd/llpkgstore/internal/verification.go index 10b86c3..42c1049 100644 --- a/cmd/llpkgstore/internal/verification.go +++ b/cmd/llpkgstore/internal/verification.go @@ -61,7 +61,7 @@ func runLLCppgVerification(_ *cobra.Command, _ []string) { } // output parsed path to Github Env for demotest b, _ := json.Marshal(&paths) - actions.Setenv(map[string]string{ + actions.Setenv(actions.Env{ "LLPKG_PATH": string(b), }) } diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 6d5d522..5d17264 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -9,7 +9,6 @@ import ( "runtime" "slices" "sort" - "strconv" "strings" "sync" @@ -38,16 +37,6 @@ func binaryZip(packageName string) string { return fmt.Sprintf("%s_%s.zip", packageName, _currentSuffix) } -// envToString converts environment variables map to newline-separated key=value pairs for GitHub Actions -func envToString(envm map[string]string) string { - var env []string - - for name, value := range envm { - env = append(env, fmt.Sprintf("%s=%s", name, value)) - } - return strings.Join(env, "\n") -} - // parseGitHubEvent parses the GitHub event payload from GITHUB_EVENT_PATH into a map func parseGitHubEvent() map[string]any { eventFileName := os.Getenv("GITHUB_EVENT_PATH") @@ -184,78 +173,3 @@ func checkLegacyVersion(ver *versions.Versions, cfg config.LLPkgConfig, mappedVe panic("mapped version should not less than the legacy one.") } } - -// Setenv writes environment variables to GITHUB_ENV for GitHub Actions consumption -func Setenv(envm map[string]string) { - env, err := os.OpenFile(os.Getenv("GITHUB_ENV"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) - // should never happen, - // it means current runtime is not Github actions if there's any errors - must(err) - - env.WriteString(envToString(envm)) - - // make sure we write it to the GITHUB_ENV - env.Close() -} - -// SetOutput writes workflow outputs to GITHUB_OUTPUT for GitHub Actions -func SetOutput(envm map[string]string) { - env, err := os.OpenFile(os.Getenv("GITHUB_OUTPUT"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) - must(err) - - env.WriteString(envToString(envm)) - - env.Close() -} - -// Changes returns the changed files in current PR, -// which depends on ALL_CHANGED_FILES generated by tj-actions/changed-files action, -// if there's no content in ALL_CHANGED_FILES, it panic. -func Changes() []string { - changes := os.Getenv("ALL_CHANGED_FILES") - if changes == "" { - panic("cannot find changes file!") - } - return strings.Fields(changes) -} - -// Repository returns owner and repository name for the current repository -// -// Example: goplus/llpkg, owner: goplus, repo: llpkg -// Repository extracts GitHub repository owner and name from GITHUB_REPOSITORY -func Repository() (owner, repo string) { - thisRepo := os.Getenv("GITHUB_REPOSITORY") - if thisRepo == "" { - panic("no github repo") - } - current := strings.Split(thisRepo, "/") - return current[0], current[1] -} - -// Token returns Github Token for current runner -func Token() string { - token := os.Getenv("GITHUB_TOKEN") - if token == "" { - panic("no GITHUB_TOKEN") - } - return token -} - -// LatestCommitSHA returns the current commit SHA from GITHUB_SHA environment variable -func LatestCommitSHA() string { - sha := os.Getenv("GITHUB_SHA") - if sha == "" { - panic("no GITHUB_SHA found") - } - return sha -} - -func WorkflowID() int64 { - runId := os.Getenv("GITHUB_RUN_ID") - if runId == "" { - panic("no GITHUB_RUN_ID found") - } - id, err := strconv.ParseInt(runId, 10, 64) - must(err) - return id -} diff --git a/internal/actions/api.go b/internal/actions/api.go index 4dddb51..957cf2a 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -599,7 +599,7 @@ func (d *DefaultClient) Release() { must(err) // upload to artifacts in GitHub Action - Setenv(map[string]string{ + Setenv(Env{ "BIN_PATH": zipFilePath, "BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"), }) diff --git a/internal/actions/env.go b/internal/actions/env.go new file mode 100644 index 0000000..4e0f2ad --- /dev/null +++ b/internal/actions/env.go @@ -0,0 +1,95 @@ +package actions + +import ( + "fmt" + "os" + "strconv" + "strings" +) + +type Env map[string]string + +// String converts environment variables map to newline-separated key=value pairs for GitHub Actions +func (e Env) String() string { + var env []string + + for name, value := range e { + env = append(env, fmt.Sprintf("%s=%s", name, value)) + } + return strings.Join(env, "\n") +} + +// Setenv writes environment variables to GITHUB_ENV for GitHub Actions consumption +func Setenv(envm Env) { + env, err := os.OpenFile(os.Getenv("GITHUB_ENV"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + // should never happen, + // it means current runtime is not Github actions if there's any errors + must(err) + + env.WriteString(envm.String()) + + // make sure we write it to the GITHUB_ENV + env.Close() +} + +// SetOutput writes workflow outputs to GITHUB_OUTPUT for GitHub Actions +func SetOutput(envm Env) { + env, err := os.OpenFile(os.Getenv("GITHUB_OUTPUT"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + must(err) + + env.WriteString(envm.String()) + + env.Close() +} + +// Changes returns the changed files in current PR, +// which depends on ALL_CHANGED_FILES generated by tj-actions/changed-files action, +// if there's no content in ALL_CHANGED_FILES, it panic. +func Changes() []string { + changes := os.Getenv("ALL_CHANGED_FILES") + if changes == "" { + panic("cannot find changes file!") + } + return strings.Fields(changes) +} + +// Repository returns owner and repository name for the current repository +// +// Example: goplus/llpkg, owner: goplus, repo: llpkg +// Repository extracts GitHub repository owner and name from GITHUB_REPOSITORY +func Repository() (owner, repo string) { + thisRepo := os.Getenv("GITHUB_REPOSITORY") + if thisRepo == "" { + panic("no github repo") + } + current := strings.Split(thisRepo, "/") + return current[0], current[1] +} + +// Token returns Github Token for current runner +func Token() string { + token := os.Getenv("GITHUB_TOKEN") + if token == "" { + panic("no GITHUB_TOKEN") + } + return token +} + +// LatestCommitSHA returns the current commit SHA from GITHUB_SHA environment variable +func LatestCommitSHA() string { + sha := os.Getenv("GITHUB_SHA") + if sha == "" { + panic("no GITHUB_SHA found") + } + return sha +} + +func WorkflowID() int64 { + runId := os.Getenv("GITHUB_RUN_ID") + if runId == "" { + panic("no GITHUB_RUN_ID found") + } + id, err := strconv.ParseInt(runId, 10, 64) + must(err) + return id +} From 630863092142767f5d47c9d0928b67b18816d921 Mon Sep 17 00:00:00 2001 From: Meteor Date: Fri, 11 Apr 2025 09:14:44 +0000 Subject: [PATCH 10/22] chore: add env test --- internal/actions/env.go | 3 +++ internal/actions/env_test.go | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 internal/actions/env_test.go diff --git a/internal/actions/env.go b/internal/actions/env.go index 4e0f2ad..d4121df 100644 --- a/internal/actions/env.go +++ b/internal/actions/env.go @@ -3,6 +3,7 @@ package actions import ( "fmt" "os" + "sort" "strconv" "strings" ) @@ -16,6 +17,8 @@ func (e Env) String() string { for name, value := range e { env = append(env, fmt.Sprintf("%s=%s", name, value)) } + + sort.Strings(env) return strings.Join(env, "\n") } diff --git a/internal/actions/env_test.go b/internal/actions/env_test.go new file mode 100644 index 0000000..988921f --- /dev/null +++ b/internal/actions/env_test.go @@ -0,0 +1,17 @@ +package actions + +import "testing" + +func TestEnv(t *testing.T) { + env := Env{ + "TEST_VAR1": "114514", + "TEST_VAR2": "123", + "TEST_VAR3": "456", + } + + expectedContent := "TEST_VAR1=114514\nTEST_VAR2=123\nTEST_VAR3=456" + + if env.String() != expectedContent { + t.Errorf("unexpected content: want %s got %s", expectedContent, env.String()) + } +} From b4b17d282e458f98c0a7baca45000149f78d0564 Mon Sep 17 00:00:00 2001 From: Meteor Date: Fri, 11 Apr 2025 09:18:12 +0000 Subject: [PATCH 11/22] chore: move env --- cmd/llpkgstore/internal/verification.go | 3 ++- internal/actions/api.go | 17 +++++++++-------- internal/actions/{ => env}/env.go | 9 ++++++++- internal/actions/{ => env}/env_test.go | 2 +- 4 files changed, 20 insertions(+), 11 deletions(-) rename internal/actions/{ => env}/env.go (94%) rename internal/actions/{ => env}/env_test.go (95%) diff --git a/cmd/llpkgstore/internal/verification.go b/cmd/llpkgstore/internal/verification.go index 42c1049..53f233c 100644 --- a/cmd/llpkgstore/internal/verification.go +++ b/cmd/llpkgstore/internal/verification.go @@ -9,6 +9,7 @@ import ( "github.com/goplus/llpkgstore/config" "github.com/goplus/llpkgstore/internal/actions" + "github.com/goplus/llpkgstore/internal/actions/env" "github.com/goplus/llpkgstore/internal/actions/generator/llcppg" "github.com/spf13/cobra" ) @@ -61,7 +62,7 @@ func runLLCppgVerification(_ *cobra.Command, _ []string) { } // output parsed path to Github Env for demotest b, _ := json.Marshal(&paths) - actions.Setenv(actions.Env{ + env.Setenv(env.Env{ "LLPKG_PATH": string(b), }) } diff --git a/internal/actions/api.go b/internal/actions/api.go index 957cf2a..a12fedd 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -16,6 +16,7 @@ import ( "github.com/google/go-github/v69/github" "github.com/goplus/llpkgstore/config" + "github.com/goplus/llpkgstore/internal/actions/env" "github.com/goplus/llpkgstore/internal/actions/mappedversion" "github.com/goplus/llpkgstore/internal/actions/tag" "github.com/goplus/llpkgstore/internal/actions/versions" @@ -66,9 +67,9 @@ type DefaultClient struct { // *DefaultClient: Configured client instance func NewDefaultClient() *DefaultClient { dc := &DefaultClient{ - client: github.NewClient(nil).WithAuthToken(Token()), + client: github.NewClient(nil).WithAuthToken(env.Token()), } - dc.owner, dc.repo = Repository() + dc.owner, dc.repo = env.Repository() return dc } @@ -138,7 +139,7 @@ func (d *DefaultClient) isLegacyVersion() (branchName string, legacy bool) { var refName string if !ok { // if this actions is not triggered by pull request, fallback to call API. - pulls := d.associatedWithPullRequest(LatestCommitSHA()) + pulls := d.associatedWithPullRequest(env.LatestCommitSHA()) if len(pulls) == 0 { panic("this commit is not associated with a pull request, this should not happen") } @@ -263,7 +264,7 @@ func (d *DefaultClient) commitMessage(sha string) *github.RepositoryCommit { // If version format is invalid func (d *DefaultClient) mappedVersion() string { // get message - message := d.commitMessage(LatestCommitSHA()).GetCommit().GetMessage() + message := d.commitMessage(env.LatestCommitSHA()).GetCommit().GetMessage() // parse the mapped version mappedVersion := regex(".*").FindString(message) @@ -398,7 +399,7 @@ func (d *DefaultClient) uploadArtifactsToRelease(release *github.RepositoryRelea defer cancel() artifacts, _, err := d.client.Actions.ListWorkflowRunArtifacts(ctx, d.owner, d.repo, - WorkflowID(), &github.ListOptions{}) + env.WorkflowID(), &github.ListOptions{}) must(err) @@ -453,7 +454,7 @@ func (d *DefaultClient) checkVersion(ver *versions.Versions, cfg config.LLPkgCon func (d *DefaultClient) CheckPR() []string { // build a file path map pathMap := map[string][]string{} - for _, path := range Changes() { + for _, path := range env.Changes() { dir := filepath.Dir(path) // initialize the dir pathMap[dir] = nil @@ -509,7 +510,7 @@ func (d *DefaultClient) CheckPR() []string { // Creates Git tags, updates version records, and cleans up legacy branches func (d *DefaultClient) Postprocessing() { // https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push - sha := LatestCommitSHA() + sha := env.LatestCommitSHA() // check it's associated with a pr if !d.isAssociatedWithPullRequest(sha) { // not a merge commit, skip it. @@ -599,7 +600,7 @@ func (d *DefaultClient) Release() { must(err) // upload to artifacts in GitHub Action - Setenv(Env{ + env.Setenv(env.Env{ "BIN_PATH": zipFilePath, "BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"), }) diff --git a/internal/actions/env.go b/internal/actions/env/env.go similarity index 94% rename from internal/actions/env.go rename to internal/actions/env/env.go index d4121df..0ea29a1 100644 --- a/internal/actions/env.go +++ b/internal/actions/env/env.go @@ -1,4 +1,4 @@ -package actions +package env import ( "fmt" @@ -8,6 +8,13 @@ import ( "strings" ) +// must panics if the error is non-nil, halting execution +func must(err error) { + if err != nil { + panic(err) + } +} + type Env map[string]string // String converts environment variables map to newline-separated key=value pairs for GitHub Actions diff --git a/internal/actions/env_test.go b/internal/actions/env/env_test.go similarity index 95% rename from internal/actions/env_test.go rename to internal/actions/env/env_test.go index 988921f..f488d70 100644 --- a/internal/actions/env_test.go +++ b/internal/actions/env/env_test.go @@ -1,4 +1,4 @@ -package actions +package env import "testing" From d566820f8663eaccbb67d493fc83a014606ba6b3 Mon Sep 17 00:00:00 2001 From: Meteor Date: Sat, 12 Apr 2025 09:30:48 +0000 Subject: [PATCH 12/22] feat: use parser to standardize trim prefix logic --- internal/actions/actions.go | 61 ++++---- internal/actions/api.go | 142 ++---------------- internal/actions/api_test.go | 3 +- .../mappedversion/mappedversion.go | 6 +- .../mappedversion/mappedversion_test.go | 0 internal/actions/parser/parser.go | 11 ++ internal/actions/parser/prefix/prefix.go | 58 +++++++ internal/actions/text.go | 15 ++ 8 files changed, 130 insertions(+), 166 deletions(-) rename internal/actions/{ => parser}/mappedversion/mappedversion.go (90%) rename internal/actions/{ => parser}/mappedversion/mappedversion_test.go (100%) create mode 100644 internal/actions/parser/parser.go create mode 100644 internal/actions/parser/prefix/prefix.go create mode 100644 internal/actions/text.go diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 5d17264..0ab517a 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -3,7 +3,6 @@ package actions import ( "encoding/json" "fmt" - "log" "os" "path/filepath" "runtime" @@ -13,6 +12,7 @@ import ( "sync" "github.com/goplus/llpkgstore/config" + "github.com/goplus/llpkgstore/internal/actions/parser/prefix" "github.com/goplus/llpkgstore/internal/actions/versions" "golang.org/x/mod/semver" ) @@ -26,17 +26,6 @@ var ( _currentSuffix = runtime.GOOS + "_" + runtime.GOARCH ) -// must panics if the error is non-nil, halting execution -func must(err error) { - if err != nil { - panic(err) - } -} - -func binaryZip(packageName string) string { - return fmt.Sprintf("%s_%s.zip", packageName, _currentSuffix) -} - // parseGitHubEvent parses the GitHub event payload from GITHUB_EVENT_PATH into a map func parseGitHubEvent() map[string]any { eventFileName := os.Getenv("GITHUB_EVENT_PATH") @@ -44,9 +33,8 @@ func parseGitHubEvent() map[string]any { panic("cannot get GITHUB_EVENT_PATH") } event, err := os.ReadFile(eventFileName) - if err != nil { - panic(err) - } + must(err) + var m map[string]any json.Unmarshal([]byte(event), &m) @@ -56,22 +44,15 @@ func parseGitHubEvent() map[string]any { return m } -// PullRequestEvent extracts pull request details from the parsed GitHub event data -func PullRequestEvent() map[string]any { - pullRequest, ok := GitHubEvent()["pull_request"].(map[string]any) - if !ok { - panic("cannot parse GITHUB_EVENT_PATH pull_request") +// must panics if the error is non-nil, halting execution +func must(err error) { + if err != nil { + panic(err) } - return pullRequest } -// IssueEvent retrieves issue-related information from the GitHub event payload -func IssueEvent() map[string]any { - issue, ok := GitHubEvent()["issue"].(map[string]any) - if !ok { - panic("cannot parse GITHUB_EVENT_PATH pull_request") - } - return issue +func binaryZip(packageName string) string { + return fmt.Sprintf("%s_%s.zip", packageName, _currentSuffix) } // branchRef generates full Git branch reference string (e.g. "refs/heads/main") @@ -79,6 +60,10 @@ func branchRef(branchName string) string { return "refs/heads/" + strings.TrimSpace(branchName) } +func isLegacyBranch(branchName string) bool { + return strings.HasPrefix(branchName, prefix.BranchPrefix) +} + // isValidLLPkg checks if directory contains both llpkg.cfg and llcppg.cfg func isValidLLPkg(files []os.DirEntry) bool { fileMap := make(map[string]struct{}, len(files)) @@ -91,12 +76,22 @@ func isValidLLPkg(files []os.DirEntry) bool { return hasLLCppg && hasLLPkg } -func mustTrimPrefix(s, prefix string) string { - result := strings.TrimPrefix(s, prefix) - if result == s { - log.Fatalf("invalid format: %s", result) +// PullRequestEvent extracts pull request details from the parsed GitHub event data +func PullRequestEvent() map[string]any { + pullRequest, ok := GitHubEvent()["pull_request"].(map[string]any) + if !ok { + panic("cannot parse GITHUB_EVENT_PATH pull_request") + } + return pullRequest +} + +// IssueEvent retrieves issue-related information from the GitHub event payload +func IssueEvent() map[string]any { + issue, ok := GitHubEvent()["issue"].(map[string]any) + if !ok { + panic("cannot parse GITHUB_EVENT_PATH pull_request") } - return result + return issue } // checkLegacyVersion validates versioning strategy for legacy package submissions diff --git a/internal/actions/api.go b/internal/actions/api.go index a12fedd..4ffc13e 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -17,35 +17,15 @@ import ( "github.com/google/go-github/v69/github" "github.com/goplus/llpkgstore/config" "github.com/goplus/llpkgstore/internal/actions/env" - "github.com/goplus/llpkgstore/internal/actions/mappedversion" + "github.com/goplus/llpkgstore/internal/actions/parser/mappedversion" + "github.com/goplus/llpkgstore/internal/actions/parser/prefix" "github.com/goplus/llpkgstore/internal/actions/tag" "github.com/goplus/llpkgstore/internal/actions/versions" "github.com/goplus/llpkgstore/internal/file" "github.com/goplus/llpkgstore/internal/pc" ) -const ( - LabelPrefix = "branch:" - BranchPrefix = "release-branch." - MappedVersionPrefix = "Release-as: " - - _defaultReleaseBranch = "main" - _regexString = `Release-as:\s%s/v(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?` -) - -// regex compiles a regular expression pattern to detect "Release-as" directives in commit messages -// Parameters: -// -// packageName: Name of the package to format into the regex pattern -// -// Returns: -// -// *regexp.Regexp: Compiled regular expression for version parsing -func regex(packageName string) *regexp.Regexp { - // format: Release-as: clib/semver(with v prefix) - // Must have one space in the end of Release-as: - return regexp.MustCompile(fmt.Sprintf(_regexString, packageName)) -} +const _defaultReleaseBranch = "main" // DefaultClient provides GitHub API client capabilities with authentication for Actions workflows type DefaultClient struct { @@ -58,13 +38,6 @@ type DefaultClient struct { } // NewDefaultClient initializes a new GitHub API client with authentication and repository configuration -// Uses: -// - GitHub token from environment -// - Repository info from GITHUB_REPOSITORY context -// -// Returns: -// -// *DefaultClient: Configured client instance func NewDefaultClient() *DefaultClient { dc := &DefaultClient{ client: github.NewClient(nil).WithAuthToken(env.Token()), @@ -74,13 +47,6 @@ func NewDefaultClient() *DefaultClient { } // hasBranch checks existence of a specific branch in the repository -// Parameters: -// -// branchName: Name of the branch to check -// -// Returns: -// -// bool: True if branch exists func (d *DefaultClient) hasBranch(branchName string) bool { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -94,13 +60,6 @@ func (d *DefaultClient) hasBranch(branchName string) bool { } // associatedWithPullRequest finds all pull requests containing the specified commit -// Parameters: -// -// sha: Commit hash to search for -// -// Returns: -// -// []*github.PullRequest: List of associated pull requests func (d *DefaultClient) associatedWithPullRequest(sha string) []*github.PullRequest { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -113,13 +72,6 @@ func (d *DefaultClient) associatedWithPullRequest(sha string) []*github.PullRequ } // isAssociatedWithPullRequest checks if commit belongs to a closed pull request -// Parameters: -// -// sha: Commit hash to check -// -// Returns: -// -// bool: True if part of closed PR func (d *DefaultClient) isAssociatedWithPullRequest(sha string) bool { pulls := d.associatedWithPullRequest(sha) // don't use GetMerge, because GetMerge may be a mistake. @@ -130,35 +82,26 @@ func (d *DefaultClient) isAssociatedWithPullRequest(sha string) bool { } // isLegacyVersion determines if PR targets a legacy branch -// Returns: -// -// branchName: Base branch name -// legacy: True if branch starts with "release-branch." func (d *DefaultClient) isLegacyVersion() (branchName string, legacy bool) { pullRequest, ok := GitHubEvent()["pull_request"].(map[string]any) - var refName string if !ok { // if this actions is not triggered by pull request, fallback to call API. pulls := d.associatedWithPullRequest(env.LatestCommitSHA()) if len(pulls) == 0 { panic("this commit is not associated with a pull request, this should not happen") } - refName = pulls[0].GetBase().GetRef() + branchName = pulls[0].GetBase().GetRef() } else { // unnecessary to check type, because currentPRCommit has been checked. base := pullRequest["base"].(map[string]any) - refName = base["ref"].(string) + branchName = base["ref"].(string) } - legacy = strings.HasPrefix(refName, BranchPrefix) - branchName = refName + legacy = isLegacyBranch(branchName) return } // currentPRCommit retrieves all commits in the current pull request -// Returns: -// -// []*github.RepositoryCommit: List of PR commits func (d *DefaultClient) currentPRCommit() []*github.RepositoryCommit { pullRequest := PullRequestEvent() prNumber := int(pullRequest["number"].(float64)) @@ -175,9 +118,6 @@ func (d *DefaultClient) currentPRCommit() []*github.RepositoryCommit { } // allCommits retrieves all repository commits -// Returns: -// -// []*github.RepositoryCommit: List of all commits func (d *DefaultClient) allCommits() []*github.RepositoryCommit { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -191,9 +131,6 @@ func (d *DefaultClient) allCommits() []*github.RepositoryCommit { } // removeLabel deletes a label from the repository -// Parameters: -// -// labelName: Name of the label to remove func (d *DefaultClient) removeLabel(labelName string) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -205,17 +142,6 @@ func (d *DefaultClient) removeLabel(labelName string) { } // checkMappedVersion validates PR contains valid "Release-as" version declaration -// Parameters: -// -// packageName: Target package name for version mapping -// -// Returns: -// -// string: Validated mapped version string -// -// Panics: -// -// If no valid version found in PR commits func (d *DefaultClient) checkMappedVersion(packageName string) mappedversion.MappedVersion { matchMappedVersion := regex(packageName) @@ -238,13 +164,6 @@ func (d *DefaultClient) checkMappedVersion(packageName string) mappedversion.Map } // commitMessage retrieves commit details by SHA -// Parameters: -// -// sha: Commit hash to retrieve -// -// Returns: -// -// *github.RepositoryCommit: Commit details object func (d *DefaultClient) commitMessage(sha string) *github.RepositoryCommit { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -255,37 +174,22 @@ func (d *DefaultClient) commitMessage(sha string) *github.RepositoryCommit { } // mappedVersion parses the latest commit's mapped version from "Release-as" directive -// Returns: -// -// string: Parsed version string or empty if not found -// -// Panics: -// -// If version format is invalid func (d *DefaultClient) mappedVersion() string { // get message message := d.commitMessage(env.LatestCommitSHA()).GetCommit().GetMessage() // parse the mapped version - mappedVersion := regex(".*").FindString(message) + commitVersion := regex(".*").FindString(message) // mapped version not found, a normal commit? - if mappedVersion == "" { + if commitVersion == "" { return "" } - version := mustTrimPrefix(mappedVersion, MappedVersionPrefix) + version := prefix.NewCommitVersionParser(commitVersion).MustParse() return strings.TrimSpace(version) } // createTag creates a new Git tag pointing to specific commit -// Parameters: -// -// tag: Tag name (e.g. "v1.2.3") -// sha: Target commit hash -// -// Returns: -// -// error: Error during tag creation func (d *DefaultClient) createTag(versionTag tag.Tag, sha string) error { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -303,14 +207,6 @@ func (d *DefaultClient) createTag(versionTag tag.Tag, sha string) error { } // createBranch creates a new branch pointing to specific commit -// Parameters: -// -// branchName: New branch name -// sha: Target commit hash -// -// Returns: -// -// error: Error during branch creation func (d *DefaultClient) createBranch(branchName, sha string) error { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -417,13 +313,6 @@ func (d *DefaultClient) uploadArtifactsToRelease(release *github.RepositoryRelea } // removeBranch deletes a branch from the repository -// Parameters: -// -// branchName: Name of the branch to delete -// -// Returns: -// -// error: Error during branch deletion func (d *DefaultClient) removeBranch(branchName string) error { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -434,10 +323,6 @@ func (d *DefaultClient) removeBranch(branchName string) error { } // checkVersion performs version validation and configuration checks -// Parameters: -// -// ver: Version store object -// cfg: Package configuration func (d *DefaultClient) checkVersion(ver *versions.Versions, cfg config.LLPkgConfig) { // 4. Check MappedVersion version := d.checkMappedVersion(cfg.Upstream.Package.Name) @@ -448,9 +333,6 @@ func (d *DefaultClient) checkVersion(ver *versions.Versions, cfg config.LLPkgCon } // CheckPR validates PR changes and returns affected packages -// Returns: -// -// []string: List of affected package paths func (d *DefaultClient) CheckPR() []string { // build a file path map pathMap := map[string][]string{} @@ -610,13 +492,13 @@ func (d *DefaultClient) Release() { // Follows naming convention: release-branch./ func (d *DefaultClient) CreateBranchFromLabel(labelName string) { // design: branch:release-branch.{CLibraryName}/{MappedVersion} - branchName := mustTrimPrefix(strings.TrimSpace(labelName), LabelPrefix) + branchName := prefix.NewLabelParser(labelName).MustParse() // fast-path: branch exists, can skip. if d.hasBranch(branchName) { return } - version := mustTrimPrefix(branchName, BranchPrefix) + version := prefix.NewBranchParser(branchName).MustParse() clib, _ := mappedversion.From(version).MustParse() // slow-path: check the condition if we can create a branch @@ -672,7 +554,7 @@ func (d *DefaultClient) CleanResource() { for _, labels := range issueEvent["labels"].([]map[string]any) { label := labels["name"].(string) - if strings.HasPrefix(label, BranchPrefix) { + if strings.HasPrefix(label, prefix.BranchPrefix) { labelName = label break } diff --git a/internal/actions/api_test.go b/internal/actions/api_test.go index e586cf9..15e7305 100644 --- a/internal/actions/api_test.go +++ b/internal/actions/api_test.go @@ -2,7 +2,6 @@ package actions import ( "os" - "strings" "testing" "github.com/goplus/llpkgstore/config" @@ -13,7 +12,7 @@ func recoverFn(branchName string, fn func(legacy bool)) (ret any) { defer func() { ret = recover() }() - fn(strings.HasPrefix(branchName, BranchPrefix)) + fn(isLegacyBranch(branchName)) return } diff --git a/internal/actions/mappedversion/mappedversion.go b/internal/actions/parser/mappedversion/mappedversion.go similarity index 90% rename from internal/actions/mappedversion/mappedversion.go rename to internal/actions/parser/mappedversion/mappedversion.go index 4ff39d1..888c3bf 100644 --- a/internal/actions/mappedversion/mappedversion.go +++ b/internal/actions/parser/mappedversion/mappedversion.go @@ -22,7 +22,7 @@ func From(version string) MappedVersion { // Input format: "clib/semver" where semver starts with 'v' // Panics if input format is invalid or version isn't valid semantic version func (m MappedVersion) Parse() (clib, mappedVersion string, err error) { - arr := strings.Split(string(m), "/") + arr := strings.Split(m.String(), "/") if len(arr) != 2 { err = ErrVersionFormat return @@ -42,3 +42,7 @@ func (m MappedVersion) MustParse() (clib, mappedVersion string) { } return } + +func (m MappedVersion) String() string { + return string(m) +} diff --git a/internal/actions/mappedversion/mappedversion_test.go b/internal/actions/parser/mappedversion/mappedversion_test.go similarity index 100% rename from internal/actions/mappedversion/mappedversion_test.go rename to internal/actions/parser/mappedversion/mappedversion_test.go diff --git a/internal/actions/parser/parser.go b/internal/actions/parser/parser.go new file mode 100644 index 0000000..4826efd --- /dev/null +++ b/internal/actions/parser/parser.go @@ -0,0 +1,11 @@ +package parser + +import "errors" + +var ErrInvalidFormat = errors.New("parse error: invalid format") + +type Parser interface { + Parse() (content string, err error) + MustParse() (content string) + String() string +} diff --git a/internal/actions/parser/prefix/prefix.go b/internal/actions/parser/prefix/prefix.go new file mode 100644 index 0000000..275bafb --- /dev/null +++ b/internal/actions/parser/prefix/prefix.go @@ -0,0 +1,58 @@ +package prefix + +import ( + "strings" + + "github.com/goplus/llpkgstore/internal/actions/parser" +) + +const ( + LabelPrefix = "branch:" + BranchPrefix = "release-branch." + MappedVersionPrefix = "Release-as: " +) + +var _ parser.Parser = (*prefixParser)(nil) + +type prefixParser struct { + s string + prefix string +} + +func newPrefixParser(s, prefix string) parser.Parser { + return &prefixParser{s: strings.TrimSpace(s), prefix: prefix} +} + +func (l *prefixParser) Parse() (content string, err error) { + result := strings.TrimPrefix(l.String(), l.prefix) + if result == l.String() { + err = parser.ErrInvalidFormat + return + } + content = result + return +} + +func (l *prefixParser) MustParse() (content string) { + content, err := l.Parse() + if err != nil { + panic(err) + } + return +} + +func (l *prefixParser) String() string { + return l.s +} + +func NewLabelParser(content string) parser.Parser { + return newPrefixParser(content, LabelPrefix) +} + +func NewBranchParser(content string) parser.Parser { + return newPrefixParser(content, BranchPrefix) +} + +func NewCommitVersionParser(content string) parser.Parser { + return newPrefixParser(content, MappedVersionPrefix) +} diff --git a/internal/actions/text.go b/internal/actions/text.go new file mode 100644 index 0000000..12eb476 --- /dev/null +++ b/internal/actions/text.go @@ -0,0 +1,15 @@ +package actions + +import ( + "fmt" + "regexp" +) + +const _regexString = `Release-as:\s%s/v(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?` + +// regex compiles a regular expression pattern to detect "Release-as" directives in commit messages +func regex(packageName string) *regexp.Regexp { + // format: Release-as: clib/semver(with v prefix) + // Must have one space in the end of Release-as: + return regexp.MustCompile(fmt.Sprintf(_regexString, packageName)) +} From 12aaf86cb5e327c881f509c8ec2f4c4d30505630 Mon Sep 17 00:00:00 2001 From: Meteor Date: Sat, 12 Apr 2025 09:39:53 +0000 Subject: [PATCH 13/22] chore: adjust function order --- internal/actions/actions.go | 3 +- internal/actions/api.go | 475 ++++++++++++++++++------------------ 2 files changed, 239 insertions(+), 239 deletions(-) diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 0ab517a..9734e36 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -3,6 +3,7 @@ package actions import ( "encoding/json" "fmt" + "log" "os" "path/filepath" "runtime" @@ -98,7 +99,7 @@ func IssueEvent() map[string]any { // Ensures semantic versioning compliance and proper branch maintenance strategy func checkLegacyVersion(ver *versions.Versions, cfg config.LLPkgConfig, mappedVersion string, isLegacy bool) { if slices.Contains(ver.GoVersions(cfg.Upstream.Package.Name), mappedVersion) { - panic("repeat semver") + log.Fatalf("repeat semver: %s", mappedVersion) } vers := ver.CVersions(cfg.Upstream.Package.Name) currentVersion := versions.ToSemVer(cfg.Upstream.Package.Version) diff --git a/internal/actions/api.go b/internal/actions/api.go index 4ffc13e..81acdf4 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -1,4 +1,3 @@ -// Package actions contains GitHub Actions helper functions for version management and repository operations. package actions import ( @@ -46,6 +45,241 @@ func NewDefaultClient() *DefaultClient { return dc } +// CheckPR validates PR changes and returns affected packages +func (d *DefaultClient) CheckPR() []string { + // build a file path map + pathMap := map[string][]string{} + for _, path := range env.Changes() { + dir := filepath.Dir(path) + // initialize the dir + pathMap[dir] = nil + } + + var allPaths []string + + ver := versions.Read("llpkgstore.json") + + for path := range pathMap { + // don't retrieve files from pr changes, consider about maintenance case + files, _ := os.ReadDir(path) + + if !isValidLLPkg(files) { + delete(pathMap, path) + continue + } + // 3. Check directory name + llpkgFile := filepath.Join(path, "llpkg.cfg") + cfg, err := config.ParseLLPkgConfig(llpkgFile) + if err != nil { + panic(err) + } + // in our design, directory name should equal to the package name, + // which means it's not required to be equal. + // + // However, at the current stage, if this is not equal, conan may panic, + // to aovid unexpected behavior, we assert it's equal temporarily. + // this logic may be changed in the future. + packageName := strings.TrimSpace(cfg.Upstream.Package.Name) + if packageName != path { + panic("directory name is not equal to package name in llpkg.cfg") + } + d.checkVersion(ver, cfg) + + allPaths = append(allPaths, path) + } + + // 1. Check there's only one directory in PR + if len(pathMap) > 1 { + panic("too many to-be-converted directory") + } + + // 2. Check config files(llpkg.cfg and llcppg.cfg) + if len(pathMap) == 0 { + panic("no valid config files, llpkg.cfg and llcppg.cfg must exist") + } + + return allPaths +} + +// Postprocessing handles version tagging and record updates after PR merge +// Creates Git tags, updates version records, and cleans up legacy branches +func (d *DefaultClient) Postprocessing() { + // https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push + sha := env.LatestCommitSHA() + // check it's associated with a pr + if !d.isAssociatedWithPullRequest(sha) { + // not a merge commit, skip it. + panic("not a merge request commit") + } + + version := d.mappedVersion() + // skip it when no mapped version is found + if version == "" { + panic("no mapped version found in the commit message") + } + + clib, mappedVersion := mappedversion.From(version).MustParse() + + // the pr has merged, so we can read it. + cfg, err := config.ParseLLPkgConfig(filepath.Join(clib, "llpkg.cfg")) + must(err) + + // write it to llpkgstore.json + ver := versions.Read("llpkgstore.json") + ver.Write(clib, cfg.Upstream.Package.Version, mappedVersion) + + versionTag := tag.From(version) + + if versionTag.Exist() { + panic("tag has already existed") + } + + if err := d.createTag(versionTag, sha); err != nil { + panic(err) + } + + // create a release + release := d.createReleaseByTag(version) + + d.uploadArtifactsToRelease(release) + + // we have finished tagging the commit, safe to remove the branch + if branchName, isLegacy := d.isLegacyVersion(); isLegacy { + d.removeBranch(branchName) + } + // move to website in Github Action... +} + +func (d *DefaultClient) Release() { + version := d.mappedVersion() + // skip it when no mapped version is found + if version == "" { + panic("no mapped version found in the commit message") + } + + clib, _ := mappedversion.From(version).MustParse() + // the pr has merged, so we can read it. + cfg, err := config.ParseLLPkgConfig(filepath.Join(clib, "llpkg.cfg")) + must(err) + + uc, err := config.NewUpstreamFromConfig(cfg.Upstream) + must(err) + + tempDir, _ := os.MkdirTemp("", "llpkg-tool") + + deps, err := uc.Installer.Install(uc.Pkg, tempDir) + must(err) + + pkgConfigDir := filepath.Join(tempDir, "lib", "pkgconfig") + // clear exist .pc + os.RemoveAll(pkgConfigDir) + + err = os.Mkdir(pkgConfigDir, 0777) + must(err) + + for _, pcName := range deps { + pcFile := filepath.Join(tempDir, pcName+".pc") + // generate pc template to lib/pkgconfig + err = pc.GenerateTemplateFromPC(pcFile, pkgConfigDir, deps) + must(err) + } + + // okay, safe to remove old pc + file.RemovePattern(filepath.Join(tempDir, "*.pc")) + file.RemovePattern(filepath.Join(tempDir, "*.sh")) + + zipFilename := binaryZip(uc.Pkg.Name) + zipFilePath, _ := filepath.Abs(zipFilename) + + err = file.Zip(tempDir, zipFilePath) + must(err) + + // upload to artifacts in GitHub Action + env.Setenv(env.Env{ + "BIN_PATH": zipFilePath, + "BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"), + }) +} + +// CreateBranchFromLabel creates release branch based on label format +// Follows naming convention: release-branch./ +func (d *DefaultClient) CreateBranchFromLabel(labelName string) { + // design: branch:release-branch.{CLibraryName}/{MappedVersion} + branchName := prefix.NewLabelParser(labelName).MustParse() + + // fast-path: branch exists, can skip. + if d.hasBranch(branchName) { + return + } + version := prefix.NewBranchParser(branchName).MustParse() + + clib, _ := mappedversion.From(version).MustParse() + // slow-path: check the condition if we can create a branch + // + // create a branch only when this version is legacy. + // according to branch maintenance strategy + + // get latest version of the clib + ver := versions.Read("llpkgstore.json") + + cversions := ver.CVersions(clib) + if len(cversions) == 0 { + panic("no clib found") + } + + if !versions.IsSemver(cversions) { + panic("c version dones't follow semver, skip maintaining.") + } + + err := d.createBranch(branchName, tag.From(version).SHA()) + must(err) +} + +// CleanResource removes labels and resources after issue resolution +// Verifies issue closure via PR merge before deletion +func (d *DefaultClient) CleanResource() { + issueEvent := IssueEvent() + + issueNumber := int(issueEvent["number"].(float64)) + regex := regexp.MustCompile(fmt.Sprintf(`(f|F)ix.*#%d`, issueNumber)) + + // 1. check this issue is closed by a PR + // In Github, close a issue with a commit whose message follows this format + // fix/Fix* #{IssueNumber} + found := false + for _, commit := range d.allCommits() { + message := commit.Commit.GetMessage() + + if regex.MatchString(message) && + d.isAssociatedWithPullRequest(commit.GetSHA()) { + found = true + break + } + } + + if !found { + panic("current issue isn't closed by merged PR.") + } + + var labelName string + + // 2. find out the branch name from the label + for _, labels := range issueEvent["labels"].([]map[string]any) { + label := labels["name"].(string) + + if strings.HasPrefix(label, prefix.BranchPrefix) { + labelName = label + break + } + } + + if labelName == "" { + panic("current issue hasn't labelled, this should not happen") + } + + d.removeLabel(labelName) +} + // hasBranch checks existence of a specific branch in the repository func (d *DefaultClient) hasBranch(branchName string) bool { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) @@ -260,7 +494,7 @@ func (d *DefaultClient) uploadToRelease(fileName string, size int64, reader io.R must(err) } -func (d *DefaultClient) uploadArtifact(wg *sync.WaitGroup, artifactID int64, release *github.RepositoryRelease) { +func (d *DefaultClient) uploadArtifactToRelease(wg *sync.WaitGroup, artifactID int64, release *github.RepositoryRelease) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer wg.Done() defer cancel() @@ -306,7 +540,7 @@ func (d *DefaultClient) uploadArtifactsToRelease(release *github.RepositoryRelea var wg sync.WaitGroup wg.Add(len(artifacts.Artifacts)) for _, artifact := range artifacts.Artifacts { - go d.uploadArtifact(&wg, artifact.GetID(), release) + go d.uploadArtifactToRelease(&wg, artifact.GetID(), release) } wg.Wait() return @@ -331,238 +565,3 @@ func (d *DefaultClient) checkVersion(ver *versions.Versions, cfg config.LLPkgCon _, isLegacy := d.isLegacyVersion() checkLegacyVersion(ver, cfg, mappedVersion, isLegacy) } - -// CheckPR validates PR changes and returns affected packages -func (d *DefaultClient) CheckPR() []string { - // build a file path map - pathMap := map[string][]string{} - for _, path := range env.Changes() { - dir := filepath.Dir(path) - // initialize the dir - pathMap[dir] = nil - } - - var allPaths []string - - ver := versions.Read("llpkgstore.json") - - for path := range pathMap { - // don't retrieve files from pr changes, consider about maintenance case - files, _ := os.ReadDir(path) - - if !isValidLLPkg(files) { - delete(pathMap, path) - continue - } - // 3. Check directory name - llpkgFile := filepath.Join(path, "llpkg.cfg") - cfg, err := config.ParseLLPkgConfig(llpkgFile) - if err != nil { - panic(err) - } - // in our design, directory name should equal to the package name, - // which means it's not required to be equal. - // - // However, at the current stage, if this is not equal, conan may panic, - // to aovid unexpected behavior, we assert it's equal temporarily. - // this logic may be changed in the future. - packageName := strings.TrimSpace(cfg.Upstream.Package.Name) - if packageName != path { - panic("directory name is not equal to package name in llpkg.cfg") - } - d.checkVersion(ver, cfg) - - allPaths = append(allPaths, path) - } - - // 1. Check there's only one directory in PR - if len(pathMap) > 1 { - panic("too many to-be-converted directory") - } - - // 2. Check config files(llpkg.cfg and llcppg.cfg) - if len(pathMap) == 0 { - panic("no valid config files, llpkg.cfg and llcppg.cfg must exist") - } - - return allPaths -} - -// Postprocessing handles version tagging and record updates after PR merge -// Creates Git tags, updates version records, and cleans up legacy branches -func (d *DefaultClient) Postprocessing() { - // https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push - sha := env.LatestCommitSHA() - // check it's associated with a pr - if !d.isAssociatedWithPullRequest(sha) { - // not a merge commit, skip it. - panic("not a merge request commit") - } - - version := d.mappedVersion() - // skip it when no mapped version is found - if version == "" { - panic("no mapped version found in the commit message") - } - - clib, mappedVersion := mappedversion.From(version).MustParse() - - // the pr has merged, so we can read it. - cfg, err := config.ParseLLPkgConfig(filepath.Join(clib, "llpkg.cfg")) - must(err) - - // write it to llpkgstore.json - ver := versions.Read("llpkgstore.json") - ver.Write(clib, cfg.Upstream.Package.Version, mappedVersion) - - versionTag := tag.From(version) - - if versionTag.Exist() { - panic("tag has already existed") - } - - if err := d.createTag(versionTag, sha); err != nil { - panic(err) - } - - // create a release - release := d.createReleaseByTag(version) - - d.uploadArtifactsToRelease(release) - - // we have finished tagging the commit, safe to remove the branch - if branchName, isLegacy := d.isLegacyVersion(); isLegacy { - d.removeBranch(branchName) - } - // move to website in Github Action... -} - -func (d *DefaultClient) Release() { - version := d.mappedVersion() - // skip it when no mapped version is found - if version == "" { - panic("no mapped version found in the commit message") - } - - clib, _ := mappedversion.From(version).MustParse() - // the pr has merged, so we can read it. - cfg, err := config.ParseLLPkgConfig(filepath.Join(clib, "llpkg.cfg")) - must(err) - - uc, err := config.NewUpstreamFromConfig(cfg.Upstream) - must(err) - - tempDir, _ := os.MkdirTemp("", "llpkg-tool") - - deps, err := uc.Installer.Install(uc.Pkg, tempDir) - must(err) - - pkgConfigDir := filepath.Join(tempDir, "lib", "pkgconfig") - // clear exist .pc - os.RemoveAll(pkgConfigDir) - - err = os.Mkdir(pkgConfigDir, 0777) - must(err) - - for _, pcName := range deps { - pcFile := filepath.Join(tempDir, pcName+".pc") - // generate pc template to lib/pkgconfig - err = pc.GenerateTemplateFromPC(pcFile, pkgConfigDir, deps) - must(err) - } - - // okay, safe to remove old pc - file.RemovePattern(filepath.Join(tempDir, "*.pc")) - file.RemovePattern(filepath.Join(tempDir, "*.sh")) - - zipFilename := binaryZip(uc.Pkg.Name) - zipFilePath, _ := filepath.Abs(zipFilename) - - err = file.Zip(tempDir, zipFilePath) - must(err) - - // upload to artifacts in GitHub Action - env.Setenv(env.Env{ - "BIN_PATH": zipFilePath, - "BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"), - }) -} - -// CreateBranchFromLabel creates release branch based on label format -// Follows naming convention: release-branch./ -func (d *DefaultClient) CreateBranchFromLabel(labelName string) { - // design: branch:release-branch.{CLibraryName}/{MappedVersion} - branchName := prefix.NewLabelParser(labelName).MustParse() - - // fast-path: branch exists, can skip. - if d.hasBranch(branchName) { - return - } - version := prefix.NewBranchParser(branchName).MustParse() - - clib, _ := mappedversion.From(version).MustParse() - // slow-path: check the condition if we can create a branch - // - // create a branch only when this version is legacy. - // according to branch maintenance strategy - - // get latest version of the clib - ver := versions.Read("llpkgstore.json") - - cversions := ver.CVersions(clib) - if len(cversions) == 0 { - panic("no clib found") - } - - if !versions.IsSemver(cversions) { - panic("c version dones't follow semver, skip maintaining.") - } - - err := d.createBranch(branchName, tag.From(version).SHA()) - must(err) -} - -// CleanResource removes labels and resources after issue resolution -// Verifies issue closure via PR merge before deletion -func (d *DefaultClient) CleanResource() { - issueEvent := IssueEvent() - - issueNumber := int(issueEvent["number"].(float64)) - regex := regexp.MustCompile(fmt.Sprintf(`(f|F)ix.*#%d`, issueNumber)) - - // 1. check this issue is closed by a PR - // In Github, close a issue with a commit whose message follows this format - // fix/Fix* #{IssueNumber} - found := false - for _, commit := range d.allCommits() { - message := commit.Commit.GetMessage() - - if regex.MatchString(message) && - d.isAssociatedWithPullRequest(commit.GetSHA()) { - found = true - break - } - } - - if !found { - panic("current issue isn't closed by merged PR.") - } - - var labelName string - - // 2. find out the branch name from the label - for _, labels := range issueEvent["labels"].([]map[string]any) { - label := labels["name"].(string) - - if strings.HasPrefix(label, prefix.BranchPrefix) { - labelName = label - break - } - } - - if labelName == "" { - panic("current issue hasn't labelled, this should not happen") - } - - d.removeLabel(labelName) -} From 49ba21a7a0cc6a8220f447c754285e1d2f9285a4 Mon Sep 17 00:00:00 2001 From: Meteor Date: Sat, 12 Apr 2025 09:48:04 +0000 Subject: [PATCH 14/22] fix: trim space for tag by default --- internal/actions/tag/tag.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/actions/tag/tag.go b/internal/actions/tag/tag.go index 99fe3b2..dcbb13c 100644 --- a/internal/actions/tag/tag.go +++ b/internal/actions/tag/tag.go @@ -9,7 +9,7 @@ import ( type Tag string func From(tag string) Tag { - return Tag(tag) + return Tag(strings.TrimSpace(tag)) } // SHA retrieves commit SHA for given Git tag @@ -30,7 +30,7 @@ func (t Tag) Exist() bool { // tagRef constructs full Git tag reference string (e.g. "refs/tags/v1.0.0") func (t Tag) Ref() string { - return "refs/tags/" + strings.TrimSpace(t.String()) + return "refs/tags/" + t.String() } func (t Tag) String() string { From 3a3abd47566f0cf248c9fef6e783a2d8079ff54c Mon Sep 17 00:00:00 2001 From: Meteor Date: Sat, 12 Apr 2025 10:05:47 +0000 Subject: [PATCH 15/22] refactor: actions version --- internal/actions/versions/versions.go | 62 ++++++++++++---------- internal/actions/versions/versions_test.go | 2 +- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/internal/actions/versions/versions.go b/internal/actions/versions/versions.go index 395046f..dd0126b 100644 --- a/internal/actions/versions/versions.go +++ b/internal/actions/versions/versions.go @@ -62,16 +62,6 @@ func Read(fileName string) *Versions { } } -// cVersions retrieves the version mappings for a specific C library. -// It returns a map where keys are C library versions and values are supported Go versions. -func (v *Versions) cVersions(clib string) map[metadata.CVersion][]metadata.GoVersion { - versions := v.MetadataMap[clib] - if versions == nil { - return nil - } - return versions.Versions -} - // CVersions returns all available versions of the specified C library. // The versions are returned as semantic version strings. func (v *Versions) CVersions(clib string) (ret []string) { @@ -99,14 +89,7 @@ func (v *Versions) GoVersions(clib string) (ret []string) { // LatestGoVersionForCVersion finds the latest Go version compatible with a specific C library version. func (v *Versions) LatestGoVersionForCVersion(clib, cver string) string { - version := v.MetadataMap[clib] - if version == nil { - return "" - } - goVersions := version.Versions[cver] - if len(goVersions) == 0 { - return "" - } + goVersions := v.goVersion(clib, cver) semver.Sort(goVersions) return goVersions[len(goVersions)-1] } @@ -141,18 +124,13 @@ func (v *Versions) LatestGoVersion(clib string) string { // // It appends the Go version to the existing list for the C library version and saves the updated metadata. func (v *Versions) Write(clib, clibVersion, mappedVersion string) { - clibVersions := v.MetadataMap[clib] - if clibVersions == nil { - clibVersions = &metadata.Metadata{ - Versions: map[metadata.CVersion][]metadata.GoVersion{}, - } - v.MetadataMap[clib] = clibVersions - } - versions := clibVersions.Versions[clibVersion] + v.initCVersion(clib) - versions = appendVersion(versions, mappedVersion) + versions := v.goVersion(clib, clibVersion) + + // TODO(ghl): rewrite llpkgstore.json + v.MetadataMap[clib].Versions[clibVersion] = appendVersion(versions, mappedVersion) - clibVersions.Versions[clibVersion] = versions // sync to disk b, _ := json.Marshal(&v.MetadataMap) @@ -164,3 +142,31 @@ func (v *Versions) String() string { b, _ := json.Marshal(&v.MetadataMap) return string(b) } + +func (v *Versions) initCVersion(clib string) { + clibVersions := v.MetadataMap[clib] + if clibVersions == nil { + clibVersions = &metadata.Metadata{ + Versions: map[metadata.CVersion][]metadata.GoVersion{}, + } + v.MetadataMap[clib] = clibVersions + } +} + +func (v *Versions) goVersion(clib, clibVersion string) []metadata.GoVersion { + clibVersions := v.MetadataMap[clib] + if clibVersions == nil { + return nil + } + return append([]metadata.GoVersion{}, clibVersions.Versions[clibVersion]...) +} + +// cVersions retrieves the version mappings for a specific C library. +// It returns a map where keys are C library versions and values are supported Go versions. +func (v *Versions) cVersions(clib string) map[metadata.CVersion][]metadata.GoVersion { + versions := v.MetadataMap[clib] + if versions == nil { + return nil + } + return versions.Versions +} diff --git a/internal/actions/versions/versions_test.go b/internal/actions/versions/versions_test.go index 43f4ca8..8fd15c9 100644 --- a/internal/actions/versions/versions_test.go +++ b/internal/actions/versions/versions_test.go @@ -97,6 +97,6 @@ func TestAppend(t *testing.T) { b, _ := os.ReadFile("llpkgstore.json") if !bytes.Equal(b, []byte(`{"cjson":{"versions":{"1.7.18":["v1.0.0","v1.0.1"],"1.7.19":["v1.0.2"]}},"libxml":{"versions":{"1.45.1.4":["v1.0.0"],"1.45.1.5":["v1.0.1"]}}}`)) { - t.Error("unexpected append result") + t.Errorf("unexpected append result: got %s", string(b)) } } From e74533e4be9164a1dbf577f29d30c382f6697f04 Mon Sep 17 00:00:00 2001 From: Meteor Date: Sat, 12 Apr 2025 10:07:45 +0000 Subject: [PATCH 16/22] fix: add len check --- internal/actions/versions/versions.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/actions/versions/versions.go b/internal/actions/versions/versions.go index dd0126b..ea5d5dc 100644 --- a/internal/actions/versions/versions.go +++ b/internal/actions/versions/versions.go @@ -90,6 +90,9 @@ func (v *Versions) GoVersions(clib string) (ret []string) { // LatestGoVersionForCVersion finds the latest Go version compatible with a specific C library version. func (v *Versions) LatestGoVersionForCVersion(clib, cver string) string { goVersions := v.goVersion(clib, cver) + if len(goVersions) == 0 { + return "" + } semver.Sort(goVersions) return goVersions[len(goVersions)-1] } From f42f17d66b4f2e28dcc1d64f60d6043526bf2554 Mon Sep 17 00:00:00 2001 From: Meteor Date: Sat, 12 Apr 2025 10:11:14 +0000 Subject: [PATCH 17/22] chore: hide MetaMap --- internal/actions/versions/versions.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/actions/versions/versions.go b/internal/actions/versions/versions.go index ea5d5dc..255ef96 100644 --- a/internal/actions/versions/versions.go +++ b/internal/actions/versions/versions.go @@ -14,7 +14,7 @@ import ( // Versions is a mapping table implement for Github Action only. // It's recommend to use another implement in llgo for common usage. type Versions struct { - metadata.MetadataMap + m metadata.MetadataMap fileName string } @@ -57,15 +57,15 @@ func Read(fileName string) *Versions { } return &Versions{ - MetadataMap: m, - fileName: f.Name(), + m: m, + fileName: f.Name(), } } // CVersions returns all available versions of the specified C library. // The versions are returned as semantic version strings. func (v *Versions) CVersions(clib string) (ret []string) { - versions := v.MetadataMap[clib] + versions := v.m[clib] if versions == nil { return } @@ -77,7 +77,7 @@ func (v *Versions) CVersions(clib string) (ret []string) { // GoVersions lists all Go versions associated with the given C library. func (v *Versions) GoVersions(clib string) (ret []string) { - versions := v.MetadataMap[clib] + versions := v.m[clib] if versions == nil { return } @@ -132,32 +132,32 @@ func (v *Versions) Write(clib, clibVersion, mappedVersion string) { versions := v.goVersion(clib, clibVersion) // TODO(ghl): rewrite llpkgstore.json - v.MetadataMap[clib].Versions[clibVersion] = appendVersion(versions, mappedVersion) + v.m[clib].Versions[clibVersion] = appendVersion(versions, mappedVersion) // sync to disk - b, _ := json.Marshal(&v.MetadataMap) + b, _ := json.Marshal(&v.m) os.WriteFile(v.fileName, []byte(b), 0644) } // String returns the JSON representation of the Versions metadata. func (v *Versions) String() string { - b, _ := json.Marshal(&v.MetadataMap) + b, _ := json.Marshal(&v.m) return string(b) } func (v *Versions) initCVersion(clib string) { - clibVersions := v.MetadataMap[clib] + clibVersions := v.m[clib] if clibVersions == nil { clibVersions = &metadata.Metadata{ Versions: map[metadata.CVersion][]metadata.GoVersion{}, } - v.MetadataMap[clib] = clibVersions + v.m[clib] = clibVersions } } func (v *Versions) goVersion(clib, clibVersion string) []metadata.GoVersion { - clibVersions := v.MetadataMap[clib] + clibVersions := v.m[clib] if clibVersions == nil { return nil } @@ -167,7 +167,7 @@ func (v *Versions) goVersion(clib, clibVersion string) []metadata.GoVersion { // cVersions retrieves the version mappings for a specific C library. // It returns a map where keys are C library versions and values are supported Go versions. func (v *Versions) cVersions(clib string) map[metadata.CVersion][]metadata.GoVersion { - versions := v.MetadataMap[clib] + versions := v.m[clib] if versions == nil { return nil } From 440e31b97b8cb7c4708ec1d07e5073bd4f0c0572 Mon Sep 17 00:00:00 2001 From: Meteor Date: Sat, 12 Apr 2025 10:39:55 +0000 Subject: [PATCH 18/22] chore: add prefix test --- internal/actions/parser/prefix/prefix_test.go | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 internal/actions/parser/prefix/prefix_test.go diff --git a/internal/actions/parser/prefix/prefix_test.go b/internal/actions/parser/prefix/prefix_test.go new file mode 100644 index 0000000..37e3f80 --- /dev/null +++ b/internal/actions/parser/prefix/prefix_test.go @@ -0,0 +1,48 @@ +package prefix + +import "testing" + +func TestLabelPrefix(t *testing.T) { + t.Run("label-valid", func(t *testing.T) { + p := NewLabelParser("branch:release-branch.").MustParse() + if p != "release-branch." { + t.Errorf("unexpected result: want: %s got %s", "release-branch.", p) + } + }) + t.Run("inabel-valid", func(t *testing.T) { + _, err := NewLabelParser("release-branch.").Parse() + if err == nil { + t.Error("unexpected behavior: no error") + } + }) +} + +func TestBranchPrefix(t *testing.T) { + t.Run("branch-valid", func(t *testing.T) { + p := NewBranchParser("release-branch.cjson/v1.0.0").MustParse() + if p != "cjson/v1.0.0" { + t.Errorf("unexpected result: want: %s got %s", "cjson/v1.0.0", p) + } + }) + t.Run("branch-invalid", func(t *testing.T) { + _, err := NewBranchParser("release-branch").Parse() + if err == nil { + t.Error("unexpected behavior: no error") + } + }) +} + +func TestCommitVersionPrefix(t *testing.T) { + t.Run("commitversion-valid", func(t *testing.T) { + p := NewCommitVersionParser("Release-as: cjson/v1.0.0").MustParse() + if p != "cjson/v1.0.0" { + t.Errorf("unexpected result: want: %s got %s", "cjson/v1.0.0", p) + } + }) + t.Run("commitversion-invalid", func(t *testing.T) { + _, err := NewCommitVersionParser("Release: cjson/v1.0.0").Parse() + if err == nil { + t.Error("unexpected behavior: no error") + } + }) +} From dfda0d8bdc4f79618752a27c97353f29f941f41a Mon Sep 17 00:00:00 2001 From: Meteor Date: Mon, 14 Apr 2025 07:20:40 +0000 Subject: [PATCH 19/22] chore: fix some var names --- internal/actions/actions.go | 9 ++-- internal/actions/api.go | 49 +++++++++---------- internal/actions/api_test.go | 12 ++--- .../{versions => mappingtable}/versions.go | 13 ++--- .../versions_test.go | 2 +- internal/actions/text.go | 6 +-- .../actions/{versions => version}/semver.go | 2 +- upstream/installer/conan/conan.go | 14 +++--- 8 files changed, 54 insertions(+), 53 deletions(-) rename internal/actions/{versions => mappingtable}/versions.go (94%) rename internal/actions/{versions => mappingtable}/versions_test.go (99%) rename internal/actions/{versions => version}/semver.go (98%) diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 9734e36..86f06d9 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -13,8 +13,9 @@ import ( "sync" "github.com/goplus/llpkgstore/config" + "github.com/goplus/llpkgstore/internal/actions/mappingtable" "github.com/goplus/llpkgstore/internal/actions/parser/prefix" - "github.com/goplus/llpkgstore/internal/actions/versions" + "github.com/goplus/llpkgstore/internal/actions/version" "golang.org/x/mod/semver" ) @@ -97,19 +98,19 @@ func IssueEvent() map[string]any { // checkLegacyVersion validates versioning strategy for legacy package submissions // Ensures semantic versioning compliance and proper branch maintenance strategy -func checkLegacyVersion(ver *versions.Versions, cfg config.LLPkgConfig, mappedVersion string, isLegacy bool) { +func checkLegacyVersion(ver *mappingtable.Versions, cfg config.LLPkgConfig, mappedVersion string, isLegacy bool) { if slices.Contains(ver.GoVersions(cfg.Upstream.Package.Name), mappedVersion) { log.Fatalf("repeat semver: %s", mappedVersion) } vers := ver.CVersions(cfg.Upstream.Package.Name) - currentVersion := versions.ToSemVer(cfg.Upstream.Package.Version) + currentVersion := version.ToSemVer(cfg.Upstream.Package.Version) // skip when we're the only latest version or C version doesn't follow semver. if len(vers) == 0 || !semver.IsValid(currentVersion) { return } - sort.Sort(versions.ByVersionDescending(vers)) + sort.Sort(version.ByVersionDescending(vers)) latestVersion := vers[0] diff --git a/internal/actions/api.go b/internal/actions/api.go index 81acdf4..2392f04 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -16,10 +16,11 @@ import ( "github.com/google/go-github/v69/github" "github.com/goplus/llpkgstore/config" "github.com/goplus/llpkgstore/internal/actions/env" + "github.com/goplus/llpkgstore/internal/actions/mappingtable" "github.com/goplus/llpkgstore/internal/actions/parser/mappedversion" "github.com/goplus/llpkgstore/internal/actions/parser/prefix" "github.com/goplus/llpkgstore/internal/actions/tag" - "github.com/goplus/llpkgstore/internal/actions/versions" + "github.com/goplus/llpkgstore/internal/actions/version" "github.com/goplus/llpkgstore/internal/file" "github.com/goplus/llpkgstore/internal/pc" ) @@ -57,7 +58,7 @@ func (d *DefaultClient) CheckPR() []string { var allPaths []string - ver := versions.Read("llpkgstore.json") + ver := mappingtable.Read("llpkgstore.json") for path := range pathMap { // don't retrieve files from pr changes, consider about maintenance case @@ -70,9 +71,7 @@ func (d *DefaultClient) CheckPR() []string { // 3. Check directory name llpkgFile := filepath.Join(path, "llpkg.cfg") cfg, err := config.ParseLLPkgConfig(llpkgFile) - if err != nil { - panic(err) - } + must(err) // in our design, directory name should equal to the package name, // which means it's not required to be equal. // @@ -112,23 +111,23 @@ func (d *DefaultClient) Postprocessing() { panic("not a merge request commit") } - version := d.mappedVersion() + rawMappedVersion := d.retrieveMappedVersion() // skip it when no mapped version is found - if version == "" { + if rawMappedVersion == "" { panic("no mapped version found in the commit message") } - clib, mappedVersion := mappedversion.From(version).MustParse() + clib, mappedVersion := mappedversion.From(rawMappedVersion).MustParse() // the pr has merged, so we can read it. cfg, err := config.ParseLLPkgConfig(filepath.Join(clib, "llpkg.cfg")) must(err) // write it to llpkgstore.json - ver := versions.Read("llpkgstore.json") + ver := mappingtable.Read("llpkgstore.json") ver.Write(clib, cfg.Upstream.Package.Version, mappedVersion) - versionTag := tag.From(version) + versionTag := tag.From(rawMappedVersion) if versionTag.Exist() { panic("tag has already existed") @@ -139,7 +138,7 @@ func (d *DefaultClient) Postprocessing() { } // create a release - release := d.createReleaseByTag(version) + release := d.createReleaseByTag(rawMappedVersion) d.uploadArtifactsToRelease(release) @@ -151,7 +150,7 @@ func (d *DefaultClient) Postprocessing() { } func (d *DefaultClient) Release() { - version := d.mappedVersion() + version := d.retrieveMappedVersion() // skip it when no mapped version is found if version == "" { panic("no mapped version found in the commit message") @@ -167,7 +166,7 @@ func (d *DefaultClient) Release() { tempDir, _ := os.MkdirTemp("", "llpkg-tool") - deps, err := uc.Installer.Install(uc.Pkg, tempDir) + pkgConfigNames, err := uc.Installer.Install(uc.Pkg, tempDir) must(err) pkgConfigDir := filepath.Join(tempDir, "lib", "pkgconfig") @@ -177,10 +176,10 @@ func (d *DefaultClient) Release() { err = os.Mkdir(pkgConfigDir, 0777) must(err) - for _, pcName := range deps { + for _, pcName := range pkgConfigNames { pcFile := filepath.Join(tempDir, pcName+".pc") // generate pc template to lib/pkgconfig - err = pc.GenerateTemplateFromPC(pcFile, pkgConfigDir, deps) + err = pc.GenerateTemplateFromPC(pcFile, pkgConfigDir, pkgConfigNames) must(err) } @@ -211,27 +210,27 @@ func (d *DefaultClient) CreateBranchFromLabel(labelName string) { if d.hasBranch(branchName) { return } - version := prefix.NewBranchParser(branchName).MustParse() + rawMappedVersion := prefix.NewBranchParser(branchName).MustParse() - clib, _ := mappedversion.From(version).MustParse() + clib, _ := mappedversion.From(rawMappedVersion).MustParse() // slow-path: check the condition if we can create a branch // // create a branch only when this version is legacy. // according to branch maintenance strategy // get latest version of the clib - ver := versions.Read("llpkgstore.json") + ver := mappingtable.Read("llpkgstore.json") cversions := ver.CVersions(clib) if len(cversions) == 0 { panic("no clib found") } - if !versions.IsSemver(cversions) { + if !version.IsSemver(cversions) { panic("c version dones't follow semver, skip maintaining.") } - err := d.createBranch(branchName, tag.From(version).SHA()) + err := d.createBranch(branchName, tag.From(rawMappedVersion).SHA()) must(err) } @@ -377,13 +376,13 @@ func (d *DefaultClient) removeLabel(labelName string) { // checkMappedVersion validates PR contains valid "Release-as" version declaration func (d *DefaultClient) checkMappedVersion(packageName string) mappedversion.MappedVersion { - matchMappedVersion := regex(packageName) + mappedVersionRegex := compileCommitVersionRegexByName(packageName) var rawMappedVersion string for _, commit := range d.currentPRCommit() { message := commit.GetCommit().GetMessage() - if rawMappedVersion = matchMappedVersion.FindString(message); rawMappedVersion != "" { + if rawMappedVersion = mappedVersionRegex.FindString(message); rawMappedVersion != "" { // remove space, of course rawMappedVersion = strings.TrimSpace(rawMappedVersion) break @@ -408,12 +407,12 @@ func (d *DefaultClient) commitMessage(sha string) *github.RepositoryCommit { } // mappedVersion parses the latest commit's mapped version from "Release-as" directive -func (d *DefaultClient) mappedVersion() string { +func (d *DefaultClient) retrieveMappedVersion() string { // get message message := d.commitMessage(env.LatestCommitSHA()).GetCommit().GetMessage() // parse the mapped version - commitVersion := regex(".*").FindString(message) + commitVersion := compileCommitVersionRegexByName(".*").FindString(message) // mapped version not found, a normal commit? if commitVersion == "" { return "" @@ -557,7 +556,7 @@ func (d *DefaultClient) removeBranch(branchName string) error { } // checkVersion performs version validation and configuration checks -func (d *DefaultClient) checkVersion(ver *versions.Versions, cfg config.LLPkgConfig) { +func (d *DefaultClient) checkVersion(ver *mappingtable.Versions, cfg config.LLPkgConfig) { // 4. Check MappedVersion version := d.checkMappedVersion(cfg.Upstream.Package.Name) _, mappedVersion := version.MustParse() diff --git a/internal/actions/api_test.go b/internal/actions/api_test.go index 15e7305..c977fc9 100644 --- a/internal/actions/api_test.go +++ b/internal/actions/api_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/goplus/llpkgstore/config" - "github.com/goplus/llpkgstore/internal/actions/versions" + "github.com/goplus/llpkgstore/internal/actions/mappingtable" ) func recoverFn(branchName string, fn func(legacy bool)) (ret any) { @@ -42,7 +42,7 @@ func TestLegacyVersion1(t *testing.T) { defer os.Remove(".llpkgstore.json") cfg, _ := config.ParseLLPkgConfig(".llpkg.cfg") - ver := versions.Read(".llpkgstore.json") + ver := mappingtable.Read(".llpkgstore.json") err := recoverFn("main", func(legacy bool) { checkLegacyVersion(ver, cfg, "v0.1.1", legacy) @@ -83,7 +83,7 @@ func TestLegacyVersion2(t *testing.T) { defer os.Remove(".llpkgstore.json") cfg, _ := config.ParseLLPkgConfig(".llpkg.cfg") - ver := versions.Read(".llpkgstore.json") + ver := mappingtable.Read(".llpkgstore.json") err := recoverFn("release-branch.cjson/v0.1.1", func(legacy bool) { checkLegacyVersion(ver, cfg, "v0.1.2", legacy) @@ -122,7 +122,7 @@ func TestLegacyVersion3(t *testing.T) { defer os.Remove(".llpkgstore.json") cfg, _ := config.ParseLLPkgConfig(".llpkg.cfg") - ver := versions.Read(".llpkgstore.json") + ver := mappingtable.Read(".llpkgstore.json") err := recoverFn("main", func(legacy bool) { checkLegacyVersion(ver, cfg, "v0.3.0", legacy) @@ -161,7 +161,7 @@ func TestLegacyVersion4(t *testing.T) { defer os.Remove(".llpkgstore.json") cfg, _ := config.ParseLLPkgConfig(".llpkg.cfg") - ver := versions.Read(".llpkgstore.json") + ver := mappingtable.Read(".llpkgstore.json") err := recoverFn("main", func(legacy bool) { checkLegacyVersion(ver, cfg, "v0.0.1", legacy) @@ -201,7 +201,7 @@ func TestLegacyVersion5(t *testing.T) { defer os.Remove(".llpkgstore.json") cfg, _ := config.ParseLLPkgConfig(".llpkg.cfg") - ver := versions.Read(".llpkgstore.json") + ver := mappingtable.Read(".llpkgstore.json") err := recoverFn("main", func(legacy bool) { checkLegacyVersion(ver, cfg, "v0.1.1", legacy) diff --git a/internal/actions/versions/versions.go b/internal/actions/mappingtable/versions.go similarity index 94% rename from internal/actions/versions/versions.go rename to internal/actions/mappingtable/versions.go index 255ef96..ce65072 100644 --- a/internal/actions/versions/versions.go +++ b/internal/actions/mappingtable/versions.go @@ -1,4 +1,4 @@ -package versions +package mappingtable import ( "encoding/json" @@ -7,6 +7,7 @@ import ( "os" "slices" + "github.com/goplus/llpkgstore/internal/actions/version" "github.com/goplus/llpkgstore/metadata" "golang.org/x/mod/semver" ) @@ -69,8 +70,8 @@ func (v *Versions) CVersions(clib string) (ret []string) { if versions == nil { return } - for version := range versions.Versions { - ret = append(ret, ToSemVer(version)) + for cversion := range versions.Versions { + ret = append(ret, version.ToSemVer(cversion)) } return } @@ -99,9 +100,9 @@ func (v *Versions) LatestGoVersionForCVersion(clib, cver string) string { // SearchBySemVer looks up a C library version by its semantic version string. func (v *Versions) SearchBySemVer(clib, semver string) string { - for version := range v.cVersions(clib) { - if ToSemVer(version) == semver { - return version + for cversion := range v.cVersions(clib) { + if version.ToSemVer(cversion) == semver { + return cversion } } return "" diff --git a/internal/actions/versions/versions_test.go b/internal/actions/mappingtable/versions_test.go similarity index 99% rename from internal/actions/versions/versions_test.go rename to internal/actions/mappingtable/versions_test.go index 8fd15c9..cc0fdcc 100644 --- a/internal/actions/versions/versions_test.go +++ b/internal/actions/mappingtable/versions_test.go @@ -1,4 +1,4 @@ -package versions +package mappingtable import ( "bytes" diff --git a/internal/actions/text.go b/internal/actions/text.go index 12eb476..c155dd4 100644 --- a/internal/actions/text.go +++ b/internal/actions/text.go @@ -5,11 +5,11 @@ import ( "regexp" ) -const _regexString = `Release-as:\s%s/v(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?` +const _unspecificMappedVersionRegex = `Release-as:\s%s/v(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?` // regex compiles a regular expression pattern to detect "Release-as" directives in commit messages -func regex(packageName string) *regexp.Regexp { +func compileCommitVersionRegexByName(packageName string) *regexp.Regexp { // format: Release-as: clib/semver(with v prefix) // Must have one space in the end of Release-as: - return regexp.MustCompile(fmt.Sprintf(_regexString, packageName)) + return regexp.MustCompile(fmt.Sprintf(_unspecificMappedVersionRegex, packageName)) } diff --git a/internal/actions/versions/semver.go b/internal/actions/version/semver.go similarity index 98% rename from internal/actions/versions/semver.go rename to internal/actions/version/semver.go index e22f725..3052017 100644 --- a/internal/actions/versions/semver.go +++ b/internal/actions/version/semver.go @@ -1,4 +1,4 @@ -package versions +package version import "golang.org/x/mod/semver" diff --git a/upstream/installer/conan/conan.go b/upstream/installer/conan/conan.go index f264e68..ae4b711 100644 --- a/upstream/installer/conan/conan.go +++ b/upstream/installer/conan/conan.go @@ -51,7 +51,7 @@ func (c *conanInstaller) findBinaryPathFromPC( installOutput []byte, ) ( binaryDir string, - pcName []string, + pcNames []string, err error, ) { var m conanOutput @@ -68,7 +68,7 @@ func (c *conanInstaller) findBinaryPathFromPC( // default to package name, // first element is the real pkg-config name of this package // use append here to avoid resizing slice again. - pcName = append(pcName, pkg.Name) + pcNames = append(pcNames, pkg.Name) for _, packageInfo := range m.Graph.Nodes { if packageInfo.Name != pkg.Name { @@ -82,12 +82,12 @@ func (c *conanInstaller) findBinaryPathFromPC( } if root.Properties.PkgName != "" { // root is the real pkg config name, replace instead. - pcName[0] = root.Properties.PkgName + pcNames[0] = root.Properties.PkgName } - pcName = append(pcName, retrievePC(packageInfo.CppInfo)...) + pcNames = append(pcNames, retrievePC(packageInfo.CppInfo)...) } - pcFile, err := os.ReadFile(filepath.Join(dir, pcName[0]+".pc")) + pcFile, err := os.ReadFile(filepath.Join(dir, pcNames[0]+".pc")) if err != nil { return } @@ -165,7 +165,7 @@ func (c *conanInstaller) Install(pkg upstream.Package, outputDir string) ([]stri // fmt.Println(string(out)) return nil, err } - binaryDir, pkgConfigName, err := c.findBinaryPathFromPC(pkg, outputDir, ret) + binaryDir, pkgConfigNames, err := c.findBinaryPathFromPC(pkg, outputDir, ret) if err != nil { return nil, err } @@ -175,7 +175,7 @@ func (c *conanInstaller) Install(pkg upstream.Package, outputDir string) ([]stri return nil, err } - return pkgConfigName, nil + return pkgConfigNames, nil } // Search checks Conan remote repository for the specified package availability. From f99780c195464eb799a2d112d9b394d33a2fdb76 Mon Sep 17 00:00:00 2001 From: Haolan Date: Mon, 14 Apr 2025 17:04:08 +0800 Subject: [PATCH 20/22] chore: add comment --- internal/actions/api.go | 24 ++++++++----- .../parser/mappedversion/mappedversion.go | 34 ++++++++++--------- internal/actions/parser/prefix/prefix.go | 16 +++++++-- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/internal/actions/api.go b/internal/actions/api.go index 2392f04..5f4aaa5 100644 --- a/internal/actions/api.go +++ b/internal/actions/api.go @@ -100,8 +100,8 @@ func (d *DefaultClient) CheckPR() []string { return allPaths } -// Postprocessing handles version tagging and record updates after PR merge -// Creates Git tags, updates version records, and cleans up legacy branches +// Postprocessing handles version tagging and record updates after PR merge. This MUST be called after the Release function. +// Functions include creating Git tags, updating version records in llpkgstore.json, and cleaning up legacy branches. func (d *DefaultClient) Postprocessing() { // https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push sha := env.LatestCommitSHA() @@ -111,7 +111,7 @@ func (d *DefaultClient) Postprocessing() { panic("not a merge request commit") } - rawMappedVersion := d.retrieveMappedVersion() + rawMappedVersion := d.findRawMappedVersion() // skip it when no mapped version is found if rawMappedVersion == "" { panic("no mapped version found in the commit message") @@ -149,14 +149,16 @@ func (d *DefaultClient) Postprocessing() { // move to website in Github Action... } +// Release prepares and uploads package artifacts for distribution. THIS FUNCTION MUST BE CALLED BEFORE Postprocessing. +// It generates package configuration files, creates a ZIP artifact, and sets environment variables for subsequent steps. func (d *DefaultClient) Release() { - version := d.retrieveMappedVersion() + rawMappedVersion := d.findRawMappedVersion() // skip it when no mapped version is found - if version == "" { + if rawMappedVersion == "" { panic("no mapped version found in the commit message") } - clib, _ := mappedversion.From(version).MustParse() + clib, _ := mappedversion.From(rawMappedVersion).MustParse() // the pr has merged, so we can read it. cfg, err := config.ParseLLPkgConfig(filepath.Join(clib, "llpkg.cfg")) must(err) @@ -406,8 +408,8 @@ func (d *DefaultClient) commitMessage(sha string) *github.RepositoryCommit { return commit } -// mappedVersion parses the latest commit's mapped version from "Release-as" directive -func (d *DefaultClient) retrieveMappedVersion() string { +// mappedVersion finds raw mapped version from the latest commit +func (d *DefaultClient) findRawMappedVersion() string { // get message message := d.commitMessage(env.LatestCommitSHA()).GetCommit().GetMessage() @@ -455,6 +457,9 @@ func (d *DefaultClient) createBranch(branchName, sha string) error { return err } +// createReleaseByTag creates a GitHub release using the specified tag. +// It uses the default branch (_defaultReleaseBranch) as the target commitish. +// The 'makeLatest' flag is set to "true" for non-legacy versions and "legacy" otherwise. func (d *DefaultClient) createReleaseByTag(tag string) *github.RepositoryRelease { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -479,6 +484,7 @@ func (d *DefaultClient) createReleaseByTag(tag string) *github.RepositoryRelease return release } +// uploadToRelease uploads a file to a GitHub release. func (d *DefaultClient) uploadToRelease(fileName string, size int64, reader io.Reader, release *github.RepositoryRelease) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() @@ -493,6 +499,7 @@ func (d *DefaultClient) uploadToRelease(fileName string, size int64, reader io.R must(err) } +// uploadArtifactToRelease uploads a single artifact to a GitHub release in a goroutine. func (d *DefaultClient) uploadArtifactToRelease(wg *sync.WaitGroup, artifactID int64, release *github.RepositoryRelease) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer wg.Done() @@ -523,6 +530,7 @@ func (d *DefaultClient) uploadArtifactToRelease(wg *sync.WaitGroup, artifactID i d.uploadToRelease(fileName, resp.ContentLength, resp.Body, release) } +// uploadArtifactsToRelease uploads all available workflow artifacts to the specified GitHub release. func (d *DefaultClient) uploadArtifactsToRelease(release *github.RepositoryRelease) (files []*os.File) { ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) defer cancel() diff --git a/internal/actions/parser/mappedversion/mappedversion.go b/internal/actions/parser/mappedversion/mappedversion.go index 888c3bf..5e97d16 100644 --- a/internal/actions/parser/mappedversion/mappedversion.go +++ b/internal/actions/parser/mappedversion/mappedversion.go @@ -7,13 +7,15 @@ import ( "golang.org/x/mod/semver" ) -var ( - ErrVersionFormat = errors.New("invalid mapped version format") - ErrMappedVersionFormat = errors.New("invalid mapped version format: mappedVersion is not a semver") -) +// ErrVersionFormat invalid version format error +var ErrVersionFormat = errors.New("invalid mapped version format") + +// ErrMappedVersionFormat invalid semantic version error +var ErrMappedVersionFormat = errors.New("invalid semantic version format") type MappedVersion string +// From creates a MappedVersion from string func From(version string) MappedVersion { return MappedVersion(version) } @@ -21,28 +23,28 @@ func From(version string) MappedVersion { // Parse splits the mapped version string into library name and version. // Input format: "clib/semver" where semver starts with 'v' // Panics if input format is invalid or version isn't valid semantic version -func (m MappedVersion) Parse() (clib, mappedVersion string, err error) { - arr := strings.Split(m.String(), "/") - if len(arr) != 2 { - err = ErrVersionFormat - return +func (m MappedVersion) Parse() (clib, version string, err error) { + parts := strings.Split(string(m), "/") + if len(parts) != 2 { + return "", "", ErrVersionFormat } - clib, mappedVersion = arr[0], arr[1] - - if !semver.IsValid(mappedVersion) { - err = ErrVersionFormat + clib, version = parts[0], parts[1] + if !semver.IsValid(version) { + return "", "", ErrMappedVersionFormat } return } -func (m MappedVersion) MustParse() (clib, mappedVersion string) { - clib, mappedVersion, err := m.Parse() +// MustParse parses version or panics +func (m MappedVersion) MustParse() (string, string) { + clib, ver, err := m.Parse() if err != nil { panic(err) } - return + return clib, ver } +// String returns the version string representation func (m MappedVersion) String() string { return string(m) } diff --git a/internal/actions/parser/prefix/prefix.go b/internal/actions/parser/prefix/prefix.go index 275bafb..6d6cd39 100644 --- a/internal/actions/parser/prefix/prefix.go +++ b/internal/actions/parser/prefix/prefix.go @@ -7,22 +7,29 @@ import ( ) const ( - LabelPrefix = "branch:" - BranchPrefix = "release-branch." + // LabelPrefix is the prefix identifier for label strings + LabelPrefix = "branch:" + // BranchPrefix denotes the prefix format for release branches + BranchPrefix = "release-branch." + // MappedVersionPrefix indicates the prefix used for commit version mappings MappedVersionPrefix = "Release-as: " ) +// Interface guard to ensure prefixParser implements parser.Parser var _ parser.Parser = (*prefixParser)(nil) +// prefixParser represents a parser that trims a specific prefix from a string type prefixParser struct { s string prefix string } +// newPrefixParser creates a new prefixParser instance with the provided string and prefix func newPrefixParser(s, prefix string) parser.Parser { return &prefixParser{s: strings.TrimSpace(s), prefix: prefix} } +// Parse trims the configured prefix from the input string and returns the result func (l *prefixParser) Parse() (content string, err error) { result := strings.TrimPrefix(l.String(), l.prefix) if result == l.String() { @@ -33,6 +40,7 @@ func (l *prefixParser) Parse() (content string, err error) { return } +// MustParse parses the string and panics if the prefix is not found func (l *prefixParser) MustParse() (content string) { content, err := l.Parse() if err != nil { @@ -41,18 +49,22 @@ func (l *prefixParser) MustParse() (content string) { return } +// String returns the original input string being parsed func (l *prefixParser) String() string { return l.s } +// NewLabelParser creates a parser for label strings with the LabelPrefix func NewLabelParser(content string) parser.Parser { return newPrefixParser(content, LabelPrefix) } +// NewBranchParser creates a parser for branch names with the BranchPrefix func NewBranchParser(content string) parser.Parser { return newPrefixParser(content, BranchPrefix) } +// NewCommitVersionParser creates a parser for commit version strings with the MappedVersionPrefix func NewCommitVersionParser(content string) parser.Parser { return newPrefixParser(content, MappedVersionPrefix) } From fa8505c6a98afe4c7e8ccdfd168ce535f91ac9fb Mon Sep 17 00:00:00 2001 From: Haolan Date: Mon, 14 Apr 2025 17:11:00 +0800 Subject: [PATCH 21/22] chore: add comment --- internal/actions/mappingtable/versions.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/internal/actions/mappingtable/versions.go b/internal/actions/mappingtable/versions.go index ce65072..df6c8d7 100644 --- a/internal/actions/mappingtable/versions.go +++ b/internal/actions/mappingtable/versions.go @@ -147,6 +147,10 @@ func (v *Versions) String() string { return string(b) } +// initCVersion initializes the C library version entry in metadata if it doesn't exist +// Parameters: +// +// clib: The name of the C library func (v *Versions) initCVersion(clib string) { clibVersions := v.m[clib] if clibVersions == nil { @@ -157,12 +161,21 @@ func (v *Versions) initCVersion(clib string) { } } +// goVersion retrieves Go versions associated with specific C library version +// Parameters: +// +// clib: The C library name +// clibVersion: The C library version +// +// Returns: +// +// Slice of Go versions compatible with given C library version func (v *Versions) goVersion(clib, clibVersion string) []metadata.GoVersion { clibVersions := v.m[clib] if clibVersions == nil { return nil } - return append([]metadata.GoVersion{}, clibVersions.Versions[clibVersion]...) + return slices.Clone(clibVersions.Versions[clibVersion]) } // cVersions retrieves the version mappings for a specific C library. From a5372d2cc5732cb0b5d72ff697f5d521042978f7 Mon Sep 17 00:00:00 2001 From: Haolan Date: Mon, 14 Apr 2025 17:18:39 +0800 Subject: [PATCH 22/22] fix: replace Fatal to Panic --- internal/actions/actions.go | 2 +- internal/actions/mappingtable/versions.go | 2 +- internal/actions/tag/tag.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 86f06d9..2c3981d 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -100,7 +100,7 @@ func IssueEvent() map[string]any { // Ensures semantic versioning compliance and proper branch maintenance strategy func checkLegacyVersion(ver *mappingtable.Versions, cfg config.LLPkgConfig, mappedVersion string, isLegacy bool) { if slices.Contains(ver.GoVersions(cfg.Upstream.Package.Name), mappedVersion) { - log.Fatalf("repeat semver: %s", mappedVersion) + log.Panicf("repeat semver: %s", mappedVersion) } vers := ver.CVersions(cfg.Upstream.Package.Name) currentVersion := version.ToSemVer(cfg.Upstream.Package.Version) diff --git a/internal/actions/mappingtable/versions.go b/internal/actions/mappingtable/versions.go index df6c8d7..971e38a 100644 --- a/internal/actions/mappingtable/versions.go +++ b/internal/actions/mappingtable/versions.go @@ -28,7 +28,7 @@ type Versions struct { // elem: Version to append func appendVersion(arr []string, elem string) []string { if slices.Contains(arr, elem) { - log.Fatalf("version %s has already existed", elem) + log.Panicf("version %s has already existed", elem) } return append(arr, elem) } diff --git a/internal/actions/tag/tag.go b/internal/actions/tag/tag.go index dcbb13c..aef5336 100644 --- a/internal/actions/tag/tag.go +++ b/internal/actions/tag/tag.go @@ -17,7 +17,7 @@ func From(tag string) Tag { func (t Tag) SHA() string { ret, err := exec.Command("git", "rev-list", "-n", "1", t.String()).CombinedOutput() if err != nil { - log.Fatalf("cannot find a tag: %s %s", t, string(ret)) + log.Panicf("cannot find a tag: %s %s", t, string(ret)) } return strings.TrimSpace(string(ret)) }