mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-17 11:57:12 +03:00
🐛 Fix for e2e failures (#598)
* draft * fixes * linter * disable parallel * comments * commments * linter
This commit is contained in:
parent
9266f97ee9
commit
1829ee7600
@ -18,6 +18,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -27,7 +28,7 @@ import (
|
|||||||
"github.com/ossf/scorecard/clients"
|
"github.com/ossf/scorecard/clients"
|
||||||
)
|
)
|
||||||
|
|
||||||
const repoFilename = "./githubrepo.tar.gz"
|
const repoFilename = "githubrepo*.tar.gz"
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
repo *github.Repository
|
repo *github.Repository
|
||||||
@ -35,11 +36,13 @@ type Client struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
owner string
|
owner string
|
||||||
repoName string
|
repoName string
|
||||||
|
tarball string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) InitRepo(owner, repoName string) error {
|
func (client *Client) InitRepo(owner, repoName string) error {
|
||||||
client.owner = owner
|
client.owner = owner
|
||||||
client.repoName = repoName
|
client.repoName = repoName
|
||||||
|
|
||||||
repo, _, err := client.repoClient.Repositories.Get(client.ctx, client.owner, client.repoName)
|
repo, _, err := client.repoClient.Repositories.Get(client.ctx, client.owner, client.repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// nolint: wrapcheck
|
// nolint: wrapcheck
|
||||||
@ -52,31 +55,34 @@ func (client *Client) InitRepo(owner, repoName string) error {
|
|||||||
url = strings.Replace(url, "{/ref}", client.repo.GetDefaultBranch(), 1)
|
url = strings.Replace(url, "{/ref}", client.repo.GetDefaultBranch(), 1)
|
||||||
req, err := http.NewRequestWithContext(client.ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(client.ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error during http.NewRequestWithContext: %w", err)
|
return fmt.Errorf("http.NewRequestWithContext: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error during HTTP call: %w", err)
|
return fmt.Errorf("http.DefaultClient.Do: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
repoFile, err := os.OpenFile(repoFilename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
|
// Create a temp file. This automaticlly appends a random number to the name.
|
||||||
|
repoFile, err := ioutil.TempFile("", repoFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error opening file %s for write: %w", repoFilename, err)
|
return fmt.Errorf("ioutil.TempFile: %w", err)
|
||||||
}
|
}
|
||||||
|
defer repoFile.Close()
|
||||||
|
|
||||||
|
client.tarball = repoFile.Name()
|
||||||
|
|
||||||
if _, err := io.Copy(repoFile, resp.Body); err != nil {
|
if _, err := io.Copy(repoFile, resp.Body); err != nil {
|
||||||
return fmt.Errorf("error during io.Copy: %w", err)
|
return fmt.Errorf("io.Copy: %w", err)
|
||||||
}
|
|
||||||
if err := repoFile.Close(); err != nil {
|
|
||||||
return fmt.Errorf("error during file Close: %w", err)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) GetRepoArchiveReader() (io.ReadCloser, error) {
|
func (client *Client) GetRepoArchiveReader() (io.ReadCloser, error) {
|
||||||
archiveReader, err := os.OpenFile(repoFilename, os.O_RDONLY, 0o644)
|
archiveReader, err := os.OpenFile(client.tarball, os.O_RDONLY, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return archiveReader, fmt.Errorf("error opening file %s for read: %w", repoFilename, err)
|
return archiveReader, fmt.Errorf("os.OpenFile: %w", err)
|
||||||
}
|
}
|
||||||
return archiveReader, nil
|
return archiveReader, nil
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:Active", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "apache",
|
Owner: "apache",
|
||||||
Repo: "airflow",
|
Repo: "airflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -22,15 +22,21 @@ import (
|
|||||||
|
|
||||||
"github.com/ossf/scorecard/checker"
|
"github.com/ossf/scorecard/checker"
|
||||||
"github.com/ossf/scorecard/checks"
|
"github.com/ossf/scorecard/checks"
|
||||||
|
"github.com/ossf/scorecard/clients/githubrepo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("E2E TEST:Automatic-Dependency-Update", func() {
|
var _ = Describe("E2E TEST:Automatic-Dependency-Update", func() {
|
||||||
Context("E2E TEST:Validating dependencies are automatically updated", func() {
|
Context("E2E TEST:Validating dependencies are automatically updated", func() {
|
||||||
It("Should return deps are automatically updated for dependabot", func() {
|
It("Should return deps are automatically updated for dependabot", func() {
|
||||||
l := log{}
|
l := log{}
|
||||||
|
repoClient := githubrepo.CreateGithubRepoClient(context.Background(), ghClient)
|
||||||
|
err := repoClient.InitRepo("ossf", "scorecard")
|
||||||
|
Expect(err).Should(BeNil())
|
||||||
|
|
||||||
checker := checker.CheckRequest{
|
checker := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
|
RepoClient: repoClient,
|
||||||
Owner: "ossf",
|
Owner: "ossf",
|
||||||
Repo: "scorecard",
|
Repo: "scorecard",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
@ -42,9 +48,14 @@ var _ = Describe("E2E TEST:Automatic-Dependency-Update", func() {
|
|||||||
})
|
})
|
||||||
It("Should return deps are automatically updated for renovatebot", func() {
|
It("Should return deps are automatically updated for renovatebot", func() {
|
||||||
l := log{}
|
l := log{}
|
||||||
|
repoClient := githubrepo.CreateGithubRepoClient(context.Background(), ghClient)
|
||||||
|
err := repoClient.InitRepo("netlify", "netlify-cms")
|
||||||
|
Expect(err).Should(BeNil())
|
||||||
|
|
||||||
checker := checker.CheckRequest{
|
checker := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
|
RepoClient: repoClient,
|
||||||
Owner: "netlify",
|
Owner: "netlify",
|
||||||
Repo: "netlify-cms",
|
Repo: "netlify-cms",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:Branch Protection", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "apache",
|
Owner: "apache",
|
||||||
Repo: "airflow",
|
Repo: "airflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:CITests", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "apache",
|
Owner: "apache",
|
||||||
Repo: "airflow",
|
Repo: "airflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:CIIBestPractices", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "tensorflow",
|
Owner: "tensorflow",
|
||||||
Repo: "tensorflow",
|
Repo: "tensorflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:CodeReview", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "apache",
|
Owner: "apache",
|
||||||
Repo: "airflow",
|
Repo: "airflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -32,7 +32,8 @@ var _ = Describe("E2E TEST:CodeReview", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "ossf",
|
Owner: "ossf",
|
||||||
Repo: "scorecard",
|
Repo: "scorecard",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
@ -47,7 +48,8 @@ var _ = Describe("E2E TEST:CodeReview", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "apache",
|
Owner: "apache",
|
||||||
Repo: "airflow",
|
Repo: "airflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -33,7 +33,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
ghClient *github.Client
|
ghClient *github.Client
|
||||||
graphClient *githubv4.Client
|
graphClient *githubv4.Client
|
||||||
client *http.Client
|
httpClient *http.Client
|
||||||
)
|
)
|
||||||
|
|
||||||
type log struct {
|
type log struct {
|
||||||
@ -73,12 +73,12 @@ var _ = BeforeSuite(func() {
|
|||||||
|
|
||||||
rt := roundtripper.NewTransport(ctx, sugar)
|
rt := roundtripper.NewTransport(ctx, sugar)
|
||||||
|
|
||||||
client = &http.Client{
|
httpClient = &http.Client{
|
||||||
Transport: rt,
|
Transport: rt,
|
||||||
}
|
}
|
||||||
|
|
||||||
ghClient = github.NewClient(client)
|
ghClient = github.NewClient(httpClient)
|
||||||
graphClient = githubv4.NewClient(client)
|
graphClient = githubv4.NewClient(httpClient)
|
||||||
})
|
})
|
||||||
|
|
||||||
var _ = AfterSuite(func() {
|
var _ = AfterSuite(func() {
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//nolint:dupl
|
|
||||||
package e2e
|
package e2e
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -23,16 +22,22 @@ import (
|
|||||||
|
|
||||||
"github.com/ossf/scorecard/checker"
|
"github.com/ossf/scorecard/checker"
|
||||||
"github.com/ossf/scorecard/checks"
|
"github.com/ossf/scorecard/checks"
|
||||||
|
"github.com/ossf/scorecard/clients/githubrepo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("E2E TEST:FrozenDeps", func() {
|
var _ = Describe("E2E TEST:FrozenDeps", func() {
|
||||||
Context("E2E TEST:Validating deps are frozen", func() {
|
Context("E2E TEST:Validating deps are frozen", func() {
|
||||||
It("Should return deps are not frozen", func() {
|
It("Should return deps are not frozen", func() {
|
||||||
l := log{}
|
l := log{}
|
||||||
|
repoClient := githubrepo.CreateGithubRepoClient(context.Background(), ghClient)
|
||||||
|
err := repoClient.InitRepo("tensorflow", "tensorflow")
|
||||||
|
Expect(err).Should(BeNil())
|
||||||
|
|
||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: repoClient,
|
||||||
Owner: "tensorflow",
|
Owner: "tensorflow",
|
||||||
Repo: "tensorflow",
|
Repo: "tensorflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
@ -42,12 +47,17 @@ var _ = Describe("E2E TEST:FrozenDeps", func() {
|
|||||||
Expect(result.Error).Should(BeNil())
|
Expect(result.Error).Should(BeNil())
|
||||||
Expect(result.Pass).Should(BeFalse())
|
Expect(result.Pass).Should(BeFalse())
|
||||||
})
|
})
|
||||||
It("Should return deps are not frozen", func() {
|
It("Should return deps are frozen", func() {
|
||||||
l := log{}
|
l := log{}
|
||||||
|
repoClient := githubrepo.CreateGithubRepoClient(context.Background(), ghClient)
|
||||||
|
err := repoClient.InitRepo("ossf", "scorecard")
|
||||||
|
Expect(err).Should(BeNil())
|
||||||
|
|
||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: repoClient,
|
||||||
Owner: "ossf",
|
Owner: "ossf",
|
||||||
Repo: "scorecard",
|
Repo: "scorecard",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:Fuzzing", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "tensorflow",
|
Owner: "tensorflow",
|
||||||
Repo: "tensorflow",
|
Repo: "tensorflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -32,7 +32,8 @@ var _ = Describe("E2E TEST:Packaging", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "apache",
|
Owner: "apache",
|
||||||
Repo: "orc",
|
Repo: "orc",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
@ -47,7 +48,8 @@ var _ = Describe("E2E TEST:Packaging", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "ossf",
|
Owner: "ossf",
|
||||||
Repo: "scorecard",
|
Repo: "scorecard",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:PullRequests", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "apache",
|
Owner: "apache",
|
||||||
Repo: "airflow",
|
Repo: "airflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:SAST", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "apache",
|
Owner: "apache",
|
||||||
Repo: "airflow",
|
Repo: "airflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -22,16 +22,22 @@ import (
|
|||||||
|
|
||||||
"github.com/ossf/scorecard/checker"
|
"github.com/ossf/scorecard/checker"
|
||||||
"github.com/ossf/scorecard/checks"
|
"github.com/ossf/scorecard/checks"
|
||||||
|
"github.com/ossf/scorecard/clients/githubrepo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("E2E TEST:SecurityPolicy", func() {
|
var _ = Describe("E2E TEST:SecurityPolicy", func() {
|
||||||
Context("E2E TEST:Validating security policy", func() {
|
Context("E2E TEST:Validating security policy", func() {
|
||||||
It("Should return valid security policy", func() {
|
It("Should return valid security policy", func() {
|
||||||
l := log{}
|
l := log{}
|
||||||
|
repoClient := githubrepo.CreateGithubRepoClient(context.Background(), ghClient)
|
||||||
|
err := repoClient.InitRepo("tensorflow", "tensorflow")
|
||||||
|
Expect(err).Should(BeNil())
|
||||||
|
|
||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: repoClient,
|
||||||
Owner: "tensorflow",
|
Owner: "tensorflow",
|
||||||
Repo: "tensorflow",
|
Repo: "tensorflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:Signedreleases", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "apache",
|
Owner: "apache",
|
||||||
Repo: "airflow",
|
Repo: "airflow",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -31,7 +31,8 @@ var _ = Describe("E2E TEST:Signedtags", func() {
|
|||||||
checkRequest := checker.CheckRequest{
|
checkRequest := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Client: ghClient,
|
Client: ghClient,
|
||||||
HTTPClient: client,
|
HTTPClient: httpClient,
|
||||||
|
RepoClient: nil,
|
||||||
Owner: "bitcoin",
|
Owner: "bitcoin",
|
||||||
Repo: "bitcoin",
|
Repo: "bitcoin",
|
||||||
GraphClient: graphClient,
|
GraphClient: graphClient,
|
||||||
|
@ -85,6 +85,7 @@ func RunScorecards(ctx context.Context,
|
|||||||
if err := repoClient.InitRepo(repo.Owner, repo.Repo); err != nil {
|
if err := repoClient.InitRepo(repo.Owner, repo.Repo); err != nil {
|
||||||
return repos.RepoResult{}, fmt.Errorf("error during InitRepo for %s: %w", repo.URL(), err)
|
return repos.RepoResult{}, fmt.Errorf("error during InitRepo for %s: %w", repo.URL(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := repos.RepoResult{
|
ret := repos.RepoResult{
|
||||||
Repo: repo.URL(),
|
Repo: repo.URL(),
|
||||||
Date: time.Now().Format("2006-01-02"),
|
Date: time.Now().Format("2006-01-02"),
|
||||||
|
Loading…
Reference in New Issue
Block a user