Repository: goplus/llpkgstore
Pull request #50: feat: publish GitHub Release when assets are ready
Author: login: MeteorsLiu
Created: 2025-04-11T05:22:28Z | Merged: 2025-04-15T03:49:39Z

This PR is a part of https://github.com/goplus/llpkg/issues/45
Test: 
https://github.com/MeteorsLiu/llpkg/actions/runs/14396114262
https://github.com/MeteorsLiu/llpkg/releases/tag/zlib%2Fv1.0.0


Diff:
diff --git a/internal/actions/actions.go b/internal/actions/actions.go
index 8f0deaf..8b82164 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 WorkflowRunID() 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 7905dcd..52b6d63 100644
--- a/internal/actions/api.go
+++ b/internal/actions/api.go
@@ -4,11 +4,14 @@ package actions
 import (
 	"context"
 	"fmt"
+	"io"
+	"mime"
 	"net/http"
 	"os"
 	"path/filepath"
 	"regexp"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/google/go-github/v69/github"
@@ -347,31 +350,74 @@ func (d *DefaultClient) createReleaseByTag(tag string) *github.RepositoryRelease
 	return release
 }
 
-func (d *DefaultClient) getReleaseByTag(tag string) *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 cancel()
 
-	release, _, err := d.client.Repositories.GetReleaseByTag(ctx, d.owner, d.repo, tag)
+	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)
-	// ok we get the relase entry
-	return release
 }
 
-func (d *DefaultClient) uploadFileToRelease(fileName string, release *github.RepositoryRelease) error {
+func (d *DefaultClient) uploadArtifact(artifactID int64, release *github.RepositoryRelease) {
 	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.String())
+	must(err)
+	defer resp.Body.Close()
+
+	disposition := resp.Header.Get("Content-Disposition")
+	_, params, err := mime.ParseMediaType(disposition)
+	must(err)
+
+	fileName, ok := params["filename"]
+	if !ok {
+		panic("no filename found in Content-Disposition")
+	}
+
+	fmt.Printf("Upload %s to %s\n", fileName, release.GetName())
+
+	d.uploadToRelease(fileName, resp.ContentLength, resp.Body, release)
+}
+
+func (d *DefaultClient) uploadArtifactsToRelease(release *github.RepositoryRelease) (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,
+		WorkflowRunID(), &github.ListOptions{})
+
+	must(err)
+
+	if artifacts.GetTotalCount() == 0 {
+		panic("no artifact found")
+	}
+
+	var wg sync.WaitGroup
+	wg.Add(len(artifacts.Artifacts))
+	for _, artifact := range artifacts.Artifacts {
+		// make a copy to avoid for loop bug
+		artifactID := artifact.GetID()
+		go func() {
+			d.uploadArtifact(artifactID, release)
+			wg.Done()
+		}()
+	}
+	wg.Wait()
+	return
 }
 
 // removeBranch deletes a branch from the repository
@@ -501,7 +547,9 @@ func (d *DefaultClient) Postprocessing() {
 	}
 
 	// create a release
-	d.createReleaseByTag(version)
+	release := d.createReleaseByTag(version)
+
+	d.uploadArtifactsToRelease(release)
 
 	// we have finished tagging the commit, safe to remove the branch
 	if branchName, isLegacy := d.isLegacyVersion(); isLegacy {
@@ -510,6 +558,7 @@ func (d *DefaultClient) Postprocessing() {
 	// move to website in Github Action...
 }
 
+// Release must be called before Postprocessing
 func (d *DefaultClient) Release() {
 	version := d.mappedVersion()
 	// skip it when no mapped version is found
@@ -548,16 +597,19 @@ func (d *DefaultClient) Release() {
 	file.RemovePattern(filepath.Join(tempDir, "*.pc"))
 	file.RemovePattern(filepath.Join(tempDir, "*.sh"))
 
-	zipFilePath, _ := filepath.Abs(binaryZip(uc.Pkg.Name))
-
-	err = file.Zip(tempDir, zipFilePath)
+	zipFilename := binaryZip(uc.Pkg.Name)
+	zipFilePath, err := filepath.Abs(zipFilename)
 	must(err)
-	release := d.getReleaseByTag(version)
 
-	// upload file to release
-	err = d.uploadFileToRelease(zipFilePath, release)
+	err = file.Zip(tempDir, zipFilePath)
 	must(err)
 
+	// upload to artifacts in GitHub Action
+	// https://github.com/goplus/llpkg/pull/50/files#diff-95373be0ab51a56a2200c8c07981d82e81569f2cd1e4e2946e2002bb66de766fR56-R60
+	Setenv(map[string]string{
+		"BIN_PATH":     zipFilePath,
+		"BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"),
+	})
 }
 
 // CreateBranchFromLabel creates release branch based on label format


Reviews:
user: login: luoliwoshang
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-14T07:30:20Z
user: login: MeteorsLiu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T07:32:35Z
user: login: luoliwoshang
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-14T07:49:39Z
user: login: MeteorsLiu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T08:00:35Z
user: login: aofei
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-15T02:57:31Z
user: login: MeteorsLiu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T03:10:04Z
user: login: MeteorsLiu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T03:10:09Z
user: login: MeteorsLiu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T03:10:14Z
user: login: aofei
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-15T03:49:19Z

Review comments:
diff_hunk: @@ -548,16 +592,17 @@ 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)
-	release := d.getReleaseByTag(version)
-
-	// upload file to release
-	err = d.uploadFileToRelease(zipFilePath, release)
-	must(err)
 
+	// upload to artifacts in GitHub Action
+	Setenv(map[string]string{
+		"BIN_PATH":     zipFilePath,
+		"BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"),
+	})
path: internal/actions/api.go
user: login: luoliwoshang
body: we can't upload this artifacts through github api?
created_at: 2025-04-14T07:30:14Z
author_association: MEMBER
diff_hunk: @@ -548,16 +592,17 @@ 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)
-	release := d.getReleaseByTag(version)
-
-	// upload file to release
-	err = d.uploadFileToRelease(zipFilePath, release)
-	must(err)
 
+	// upload to artifacts in GitHub Action
+	Setenv(map[string]string{
+		"BIN_PATH":     zipFilePath,
+		"BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"),
+	})
path: internal/actions/api.go
user: login: MeteorsLiu
body: Yes, there's no `uploadArtifact` method in [`Github Artifact API`](https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#about-artifacts-in-github-actions)
created_at: 2025-04-14T07:32:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -548,16 +592,17 @@ 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)
-	release := d.getReleaseByTag(version)
-
-	// upload file to release
-	err = d.uploadFileToRelease(zipFilePath, release)
-	must(err)
 
+	// upload to artifacts in GitHub Action
+	Setenv(map[string]string{
+		"BIN_PATH":     zipFilePath,
+		"BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"),
+	})
path: internal/actions/api.go
user: login: luoliwoshang
body: current update of this pr,we modify the order of the Release binary & Create Tag(Postprocessing), And This EnvSet need User to collect this asstets. We need patch comment to determine these order & how to use it.
created_at: 2025-04-14T07:49:36Z
author_association: MEMBER
diff_hunk: @@ -548,16 +592,17 @@ 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)
-	release := d.getReleaseByTag(version)
-
-	// upload file to release
-	err = d.uploadFileToRelease(zipFilePath, release)
-	must(err)
 
+	// upload to artifacts in GitHub Action
+	Setenv(map[string]string{
+		"BIN_PATH":     zipFilePath,
+		"BIN_FILENAME": strings.TrimSuffix(zipFilename, ".zip"),
+	})
path: internal/actions/api.go
user: login: MeteorsLiu
body: Done in [chore: add comment](https://github.com/goplus/llpkgstore/pull/50/commits/aebd087444f4839c8fc77487e31d5ff707a03e08)
created_at: 2025-04-14T08:00:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -272,3 +273,13 @@ func LatestCommitSHA() string {
 	}
 	return sha
 }
+
+func WorkflowID() int64 {
path: internal/actions/actions.go
user: login: aofei
body: The term `workflow ID` doesn't really exist (it's `workflow name`). Naming it `WorkflowRunID` makes more sense, as `workflow run` is a widely used term.
created_at: 2025-04-15T02:51:37Z
author_association: MEMBER
diff_hunk: @@ -347,31 +350,70 @@ func (d *DefaultClient) createReleaseByTag(tag string) *github.RepositoryRelease
 	return release
 }
 
-func (d *DefaultClient) getReleaseByTag(tag string) *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 cancel()
 
-	release, _, err := d.client.Repositories.GetReleaseByTag(ctx, d.owner, d.repo, tag)
+	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)
-	// ok we get the relase entry
-	return release
 }
 
-func (d *DefaultClient) uploadFileToRelease(fileName string, release *github.RepositoryRelease) error {
+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()
 
-	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.String())
+	must(err)
+	defer resp.Body.Close()
+
+	disposition := resp.Header.Get("Content-Disposition")
+	_, params, err := mime.ParseMediaType(disposition)
+	must(err)
+
+	fileName, ok := params["filename"]
+	if !ok {
+		panic("no filename found in Content-Disposition")
+	}
+
+	fmt.Printf("Upload %s to %s\n", fileName, release.GetName())
+
+	d.uploadToRelease(fileName, resp.ContentLength, resp.Body, release)
+}
+
+func (d *DefaultClient) uploadArtifactsToRelease(release *github.RepositoryRelease) (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")
+	}
+
+	var wg sync.WaitGroup
+	wg.Add(len(artifacts.Artifacts))
+	for _, artifact := range artifacts.Artifacts {
+		go d.uploadArtifact(&wg, artifact.GetID(), release)
path: internal/actions/api.go
user: login: aofei
body: Avoid passing `sync.WaitGroup` to subcalls, as it can make the code harder to reason about.

```go
go func() {
  d.uploadArtifact(artifact.GetID(), release)
  wg.Done()
}()
```
created_at: 2025-04-15T02:55:36Z
author_association: MEMBER
diff_hunk: @@ -548,16 +593,18 @@ 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)
path: internal/actions/api.go
user: login: aofei
body: The potential error returned by `filepath.Abs` should be handled.
created_at: 2025-04-15T02:57:14Z
author_association: MEMBER
diff_hunk: @@ -548,16 +593,18 @@ 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)
path: internal/actions/api.go
user: login: MeteorsLiu
body: Done in [fix: use closure function instead of passing args](https://github.com/goplus/llpkgstore/pull/50/commits/47f85e54a0fe77ccba1e7687aa743ed2684195fd)
created_at: 2025-04-15T03:10:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -347,31 +350,70 @@ func (d *DefaultClient) createReleaseByTag(tag string) *github.RepositoryRelease
 	return release
 }
 
-func (d *DefaultClient) getReleaseByTag(tag string) *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 cancel()
 
-	release, _, err := d.client.Repositories.GetReleaseByTag(ctx, d.owner, d.repo, tag)
+	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)
-	// ok we get the relase entry
-	return release
 }
 
-func (d *DefaultClient) uploadFileToRelease(fileName string, release *github.RepositoryRelease) error {
+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()
 
-	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.String())
+	must(err)
+	defer resp.Body.Close()
+
+	disposition := resp.Header.Get("Content-Disposition")
+	_, params, err := mime.ParseMediaType(disposition)
+	must(err)
+
+	fileName, ok := params["filename"]
+	if !ok {
+		panic("no filename found in Content-Disposition")
+	}
+
+	fmt.Printf("Upload %s to %s\n", fileName, release.GetName())
+
+	d.uploadToRelease(fileName, resp.ContentLength, resp.Body, release)
+}
+
+func (d *DefaultClient) uploadArtifactsToRelease(release *github.RepositoryRelease) (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")
+	}
+
+	var wg sync.WaitGroup
+	wg.Add(len(artifacts.Artifacts))
+	for _, artifact := range artifacts.Artifacts {
+		go d.uploadArtifact(&wg, artifact.GetID(), release)
path: internal/actions/api.go
user: login: MeteorsLiu
body: Done in [fix: use closure function instead of passing args](https://github.com/goplus/llpkgstore/pull/50/commits/47f85e54a0fe77ccba1e7687aa743ed2684195fd)

created_at: 2025-04-15T03:10:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -272,3 +273,13 @@ func LatestCommitSHA() string {
 	}
 	return sha
 }
+
+func WorkflowID() int64 {
path: internal/actions/actions.go
user: login: MeteorsLiu
body: Done in [fix: use closure function instead of passing args](https://github.com/goplus/llpkgstore/pull/50/commits/47f85e54a0fe77ccba1e7687aa743ed2684195fd)
created_at: 2025-04-15T03:10:13Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-04-11T05:25:34Z
body: ## [Codecov](https://app.codecov.io/gh/goplus/llpkgstore/pull/50?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) Report
Attention: Patch coverage is `0%` with `68 lines` in your changes missing coverage. Please review.
> Project coverage is 39.74%. Comparing base [(`4420420`)](https://app.codecov.io/gh/goplus/llpkgstore/commit/44204206133d8c8397e631ed6d91b6216b741dc6?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) to head [(`47f85e5`)](https://app.codecov.io/gh/goplus/llpkgstore/commit/47f85e54a0fe77ccba1e7687aa743ed2684195fd?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).
> Report is 1 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/goplus/llpkgstore/pull/50?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) | Patch % | Lines |
|---|---|---|
| [internal/actions/api.go](https://app.codecov.io/gh/goplus/llpkgstore/pull/50?src=pr&el=tree&filepath=internal%2Factions%2Fapi.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus#diff-aW50ZXJuYWwvYWN0aW9ucy9hcGkuZ28=) | 0.00% | [61 Missing :warning: ](https://app.codecov.io/gh/goplus/llpkgstore/pull/50?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) |
| [internal/actions/actions.go](https://app.codecov.io/gh/goplus/llpkgstore/pull/50?src=pr&el=tree&filepath=internal%2Factions%2Factions.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus#diff-aW50ZXJuYWwvYWN0aW9ucy9hY3Rpb25zLmdv) | 0.00% | [7 Missing :warning: ](https://app.codecov.io/gh/goplus/llpkgstore/pull/50?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main      #50      +/-   ##
==========================================
- Coverage   40.85%   39.74%   -1.12%     
==========================================
  Files          33       33              
  Lines        1745     1794      +49     
==========================================
  Hits          713      713              
- Misses        947      996      +49     
  Partials       85       85              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/goplus/llpkgstore/pull/50?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: qiniu-x[bot]
created_at: 2025-04-14T08:00:22Z
body: **[Git-flow]** Hi @MeteorsLiu, There are some suggestions for your information:

---


> 
### Rebase suggestions
* Following commits seems generated via `git merge` 
 
  > Merge branch 'main' of https://github.com/goplus/llpkgstore into release-when-ready

 Which seems insignificant, recommend to use **`git rebase`** command to reorganize your PR. 



For other `git-flow` instructions, recommend refer to [these examples](https://github.com/qiniu/reviewbot/blob/master/docs/engineering-practice/git-flow-instructions_zh.md).


<details>

If you have any questions about this comment, feel free to raise an issue here:

- **https://github.com/qiniu/reviewbot**

</details>

author_association: CONTRIBUTOR