!93 add DevStar模板 in /repo/create
* 减少不必要更改,恢复GenerateGitContent * 清理一些不必要的格式差异 * add DevStar模板 in /repo/create
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -169,13 +170,14 @@ func Create(ctx *context.Context) {
|
|||||||
ctx.Data["private"] = getRepoPrivate(ctx)
|
ctx.Data["private"] = getRepoPrivate(ctx)
|
||||||
ctx.Data["default_branch"] = setting.Repository.DefaultBranch
|
ctx.Data["default_branch"] = setting.Repository.DefaultBranch
|
||||||
ctx.Data["repo_template_name"] = ctx.Tr("repo.template_select")
|
ctx.Data["repo_template_name"] = ctx.Tr("repo.template_select")
|
||||||
|
ctx.Data["devstar_template_name"] = ctx.Tr("repo.template_select")
|
||||||
templateID := ctx.FormInt64("template_id")
|
templateID := ctx.FormInt64("template_id")
|
||||||
if templateID > 0 {
|
if templateID > 0 {
|
||||||
templateRepo, err := repo_model.GetRepositoryByID(ctx, templateID)
|
templateRepo, err := repo_model.GetRepositoryByID(ctx, templateID)
|
||||||
if err == nil && access_model.CheckRepoUnitUser(ctx, templateRepo, ctxUser, unit.TypeCode) {
|
if err == nil && access_model.CheckRepoUnitUser(ctx, templateRepo, ctxUser, unit.TypeCode) {
|
||||||
ctx.Data["repo_template"] = templateID
|
ctx.Data["repo_template"] = templateID
|
||||||
ctx.Data["repo_template_name"] = templateRepo.Name
|
ctx.Data["repo_template_name"] = templateRepo.FullName
|
||||||
|
ctx.Data["devstar_template_name"] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +216,79 @@ func handleCreateError(ctx *context.Context, owner *user_model.User, err error,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isValidCommitUrl checks whether a given URL is a valid Git URL with a commit reference.
|
||||||
|
func isValidCommitUrl(url string) bool {
|
||||||
|
// 正则表达式用于匹配包含 commit 的 Git URL
|
||||||
|
regex := `^https?:\/\/([a-zA-Z0-9.-]+)\/([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)(\/commit\/([a-f0-9]{40}))?$`
|
||||||
|
|
||||||
|
// 编译正则表达式
|
||||||
|
pattern, err := regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Invalid regex for commit URL:", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 URL 是否匹配正则表达式
|
||||||
|
return pattern.MatchString(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidGitPath checks whether a given URL is a valid Git URL ending with .git.
|
||||||
|
func isValidGitPath(url string) bool {
|
||||||
|
// 正则表达式用于匹配以 .git 结尾的 Git URL
|
||||||
|
regexWithGit := `^https?:\/\/([a-zA-Z0-9.-]+)\/([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)\.git$`
|
||||||
|
|
||||||
|
// 编译正则表达式
|
||||||
|
pattern, err := regexp.Compile(regexWithGit)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Invalid regex for .git URL:", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 URL 是否匹配正则表达式
|
||||||
|
return pattern.MatchString(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractCommitID 从给定的 URL 中提取 commitID
|
||||||
|
func ExtractCommitID(url string) (string, error) {
|
||||||
|
// 正则表达式匹配 commit URL 中的 commitID
|
||||||
|
regex := `^https?:\/\/[a-zA-Z0-9.-]+\/[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+\/commit\/([a-f0-9]{40})$`
|
||||||
|
|
||||||
|
// 编译正则表达式
|
||||||
|
commitPattern, err := regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试匹配 URL
|
||||||
|
if commitPattern.MatchString(url) {
|
||||||
|
// 提取 commitID
|
||||||
|
matches := commitPattern.FindStringSubmatch(url)
|
||||||
|
if len(matches) >= 2 {
|
||||||
|
return matches[1], nil // 返回 commitID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果匹配失败,返回错误
|
||||||
|
return "", fmt.Errorf("URL does not match expected format: %s", url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceCommitWithGit 将 commit URL 中的 /commit/<commitID> 替换为 .git
|
||||||
|
func ReplaceCommitWithGit(url string) (string, error) {
|
||||||
|
// 定义正则表达式匹配 URL 中的 /commit/<commitID> 部分
|
||||||
|
regex := `(/commit/[a-f0-9]{40})$`
|
||||||
|
|
||||||
|
// 编译正则表达式
|
||||||
|
commitPattern, err := regexp.Compile(regex)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 替换为 .git
|
||||||
|
replacedURL := commitPattern.ReplaceAllString(url, ".git")
|
||||||
|
|
||||||
|
return replacedURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreatePost response for creating repository
|
// CreatePost response for creating repository
|
||||||
func CreatePost(ctx *context.Context) {
|
func CreatePost(ctx *context.Context) {
|
||||||
createCommon(ctx)
|
createCommon(ctx)
|
||||||
@@ -292,6 +367,54 @@ func CreatePost(ctx *context.Context) {
|
|||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
|
log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
|
||||||
|
|
||||||
|
if strings.TrimSpace(repo.DefaultBranch) == "" {
|
||||||
|
repo.DefaultBranch = setting.Repository.DefaultBranch
|
||||||
|
}
|
||||||
|
|
||||||
|
if isValidGitPath(form.DevstarTemplate) {
|
||||||
|
log.Info("init repo from %s", form.DevstarTemplate)
|
||||||
|
repo, err = repo_service.InitRepoFromGitURL(ctx, ctx.Doer, ctxUser, form.DevstarTemplate, "", repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error initializing repo from DevstarTemplate: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if repo == nil {
|
||||||
|
log.Error("repo is nil after initializing from DevstarTemplate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isValidGitPath(form.GitUrlTemplate) {
|
||||||
|
log.Info("init repo from %s", form.GitUrlTemplate)
|
||||||
|
repo, err = repo_service.InitRepoFromGitURL(ctx, ctx.Doer, ctxUser, form.GitUrlTemplate, "", repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error initializing repo from GitUrlTemplate: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if repo == nil {
|
||||||
|
log.Error("repo is nil after initializing from GitUrlTemplate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isValidCommitUrl(form.GitUrlTemplate) {
|
||||||
|
log.Info("init repo from %s", form.GitUrlTemplate)
|
||||||
|
commitID, err := ExtractCommitID(form.GitUrlTemplate)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error:", err)
|
||||||
|
}
|
||||||
|
gitURL, err := ReplaceCommitWithGit(form.GitUrlTemplate)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error:", err)
|
||||||
|
}
|
||||||
|
repo, err = repo_service.InitRepoFromGitURL(ctx, ctx.Doer, ctxUser, gitURL, commitID, repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error initializing repo from GitUrlTemplate (commit): %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if repo == nil {
|
||||||
|
log.Error("repo is nil after initializing from GitUrlTemplate (commit)")
|
||||||
|
}
|
||||||
|
}
|
||||||
ctx.Redirect(repo.Link())
|
ctx.Redirect(repo.Link())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ type CreateRepoForm struct {
|
|||||||
Readme string
|
Readme string
|
||||||
Template bool
|
Template bool
|
||||||
|
|
||||||
|
DevstarTemplate string
|
||||||
|
GitUrlTemplate string
|
||||||
RepoTemplate int64
|
RepoTemplate int64
|
||||||
GitContent bool
|
GitContent bool
|
||||||
Topics bool
|
Topics bool
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply changes and commit.
|
// Apply changes and commit.
|
||||||
if err = initRepoCommit(ctx, tmpDir, repo, u, opts.DefaultBranch); err != nil {
|
if err = initRepoCommit(ctx, tmpDir, repo, u, opts.DefaultBranch, "Initial commit"); err != nil {
|
||||||
return fmt.Errorf("initRepoCommit: %w", err)
|
return fmt.Errorf("initRepoCommit: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -250,7 +251,7 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
|
|||||||
defaultBranch = templateRepo.DefaultBranch
|
defaultBranch = templateRepo.DefaultBranch
|
||||||
}
|
}
|
||||||
|
|
||||||
return initRepoCommit(ctx, tmpDir, repo, repo.Owner, defaultBranch)
|
return initRepoCommit(ctx, tmpDir, repo, repo.Owner, defaultBranch, "Initial commit")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateGitContent generates git content from a template repository
|
// GenerateGitContent generates git content from a template repository
|
||||||
@@ -292,6 +293,81 @@ func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *repo_mo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateGitContent generates git content from a Git URL
|
||||||
|
func GenerateGitContentFromGitURL(ctx context.Context, gitURL, commitID string, repo *repo_model.Repository) error {
|
||||||
|
tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to create temp dir for repository %s: %w", gitURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := util.RemoveAll(tmpDir); err != nil {
|
||||||
|
log.Error("RemoveAll: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// generateRepoCommit
|
||||||
|
commitTimeStr := time.Now().Format(time.RFC3339)
|
||||||
|
authorSig := repo.Owner.NewGitSig()
|
||||||
|
|
||||||
|
// Because this may call hooks we should pass in the environment
|
||||||
|
env := append(os.Environ(),
|
||||||
|
"GIT_AUTHOR_NAME="+authorSig.Name,
|
||||||
|
"GIT_AUTHOR_EMAIL="+authorSig.Email,
|
||||||
|
"GIT_AUTHOR_DATE="+commitTimeStr,
|
||||||
|
"GIT_COMMITTER_NAME="+authorSig.Name,
|
||||||
|
"GIT_COMMITTER_EMAIL="+authorSig.Email,
|
||||||
|
"GIT_COMMITTER_DATE="+commitTimeStr,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Clone to temporary path with a specified depth.
|
||||||
|
if err := git.Clone(ctx, gitURL, tmpDir, git.CloneRepoOptions{
|
||||||
|
Depth: 1, // 仍然可以保持深度克隆以优化性能
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("git clone: %w", err)
|
||||||
|
}
|
||||||
|
// Change to the specified commit ID after cloning
|
||||||
|
if commitID != "" {
|
||||||
|
if err := git.NewCommand("checkout").AddDynamicArguments(commitID).Run(ctx, &git.RunOpts{Dir: tmpDir}); err != nil {
|
||||||
|
return fmt.Errorf("git checkout %s: %w", commitID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get the current SHA1 version of the working directory
|
||||||
|
var sha1Buffer bytes.Buffer
|
||||||
|
if err := git.NewCommand("rev-parse", "HEAD").Run(ctx, &git.RunOpts{
|
||||||
|
Dir: tmpDir,
|
||||||
|
Stdout: &sha1Buffer, // 使用 bytes.Buffer
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("git rev-parse HEAD: %w", err)
|
||||||
|
}
|
||||||
|
// 获取SHA1字符串并修整空白
|
||||||
|
sha1 := strings.TrimSpace(sha1Buffer.String())
|
||||||
|
|
||||||
|
if err := util.RemoveAll(path.Join(tmpDir, ".git")); err != nil {
|
||||||
|
return fmt.Errorf("remove git dir: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.InitRepository(ctx, tmpDir, false, repo.ObjectFormatName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repoPath := repo.RepoPath()
|
||||||
|
if stdout, _, err := git.NewCommand("remote", "add", "origin").AddDynamicArguments(repoPath).
|
||||||
|
//SetDescription(fmt.Sprintf("generateRepoCommit (git remote add): %s to %s", gitURL, tmpDir)).
|
||||||
|
RunStdString(ctx, &git.RunOpts{Dir: tmpDir, Env: env}); err != nil {
|
||||||
|
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)
|
||||||
|
return fmt.Errorf("git remote add: %w", err)
|
||||||
|
}
|
||||||
|
commitMessage := "Initial commit from " + gitURL + " ( " + sha1 + " ) "
|
||||||
|
initRepoCommit(ctx, tmpDir, repo, repo.Owner, repo.DefaultBranch, commitMessage)
|
||||||
|
|
||||||
|
if err := repo_module.UpdateRepoSize(ctx, repo); err != nil {
|
||||||
|
return fmt.Errorf("failed to update size for repository: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("GenerateGitContentFromGitURL init repo from %s : %s", gitURL, commitID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GenerateRepoOptions contains the template units to generate
|
// GenerateRepoOptions contains the template units to generate
|
||||||
type GenerateRepoOptions struct {
|
type GenerateRepoOptions struct {
|
||||||
Name string
|
Name string
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// initRepoCommit temporarily changes with work directory.
|
// initRepoCommit temporarily changes with work directory.
|
||||||
func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Repository, u *user_model.User, defaultBranch string) (err error) {
|
func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Repository, u *user_model.User, defaultBranch string, commitMessage string) (err error) {
|
||||||
commitTimeStr := time.Now().Format(time.RFC3339)
|
commitTimeStr := time.Now().Format(time.RFC3339)
|
||||||
|
commitMsg := "--message=" + commitMessage
|
||||||
sig := u.NewGitSig()
|
sig := u.NewGitSig()
|
||||||
// Because this may call hooks we should pass in the environment
|
// Because this may call hooks we should pass in the environment
|
||||||
env := append(os.Environ(),
|
env := append(os.Environ(),
|
||||||
@@ -39,7 +39,7 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
|
|||||||
return fmt.Errorf("git add --all: %w", err)
|
return fmt.Errorf("git add --all: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := git.NewCommand("commit", "--message=Initial commit").
|
cmd := git.NewCommand("commit").AddOptionFormat(commitMsg).
|
||||||
AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email)
|
AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email)
|
||||||
|
|
||||||
sign, key, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
|
sign, key, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
|
||||||
|
|||||||
@@ -192,3 +192,17 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ
|
|||||||
|
|
||||||
return generateRepo, nil
|
return generateRepo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitRepoFromGitURL for a repository from a Git URL
|
||||||
|
func InitRepoFromGitURL(ctx context.Context, doer, owner *user_model.User, gitURL, commitID string, repo *repo_model.Repository) (_ *repo_model.Repository, err error) {
|
||||||
|
if !doer.IsAdmin {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = GenerateGitContentFromGitURL(ctx, gitURL, commitID, repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,17 +61,31 @@
|
|||||||
<label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
|
<label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
|
||||||
<textarea id="description" rows="2" name="description" placeholder="{{ctx.Locale.Tr "repo.repo_desc_helper"}}" maxlength="2048">{{.description}}</textarea>
|
<textarea id="description" rows="2" name="description" placeholder="{{ctx.Locale.Tr "repo.repo_desc_helper"}}" maxlength="2048">{{.description}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{if .devstar_template_name}}
|
||||||
|
<div id="devstar_template_area" class="inline field">
|
||||||
|
<label>{{ctx.Locale.Tr "repo.devstar_template"}}</label>
|
||||||
|
<div id="devstar_template_search" class="ui search selection dropdown">
|
||||||
|
<input type="hidden" id="devstar_template" name="devstar_template" value="">
|
||||||
|
<div id="devstar_template_name" class="default text">{{.devstar_template_name}}</div>
|
||||||
|
<div class="menu"></div>
|
||||||
|
</div>
|
||||||
|
<span class="help">{{ctx.Locale.Tr "repo.devstar_template_desc" "https://DevStar.cn/"}}</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<div id="repo_template_area">
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<label>{{ctx.Locale.Tr "repo.template"}}</label>
|
<label>{{ctx.Locale.Tr "repo.template"}}</label>
|
||||||
<div id="repo_template_search" class="ui search selection dropdown">
|
<div id="repo_template_search" class="ui search selection dropdown">
|
||||||
<input type="hidden" id="repo_template" name="repo_template" value="{{or .repo_template ""}}">
|
<input type="hidden" id="repo_template" name="repo_template" value="{{or .repo_template ""}}">
|
||||||
<div class="default text">{{.repo_template_name}}</div>
|
<div id="repo_template_name" class="default text">{{.repo_template_name}}</div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="template_units" class="tw-hidden">
|
<div id="template_units" {{if not .repo_template}}class="tw-hidden"{{end}}>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<label>{{ctx.Locale.Tr "repo.template.items"}}</label>
|
<label>{{ctx.Locale.Tr "repo.template.items"}}</label>
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
@@ -113,8 +127,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="non_template">
|
<div id="non_template" {{if .repo_template}}class="tw-hidden"{{end}}>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<label>{{ctx.Locale.Tr "repo.issue_labels"}}</label>
|
<label>{{ctx.Locale.Tr "repo.issue_labels"}}</label>
|
||||||
<div class="ui search selection dropdown">
|
<div class="ui search selection dropdown">
|
||||||
|
|||||||
187
web_src/js/features/repo-template.ts
Normal file
187
web_src/js/features/repo-template.ts
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
import $ from 'jquery';
|
||||||
|
|
||||||
|
import { htmlEscape } from '../utils/html.ts';
|
||||||
|
import { hideElem, showElem } from '../utils/dom.ts';
|
||||||
|
|
||||||
|
const {appSubUrl} = window.config;
|
||||||
|
|
||||||
|
export function initRepoTemplateSearch() {
|
||||||
|
|
||||||
|
function isValidGitUrl(url) {
|
||||||
|
// 正则表达式支持两种格式的 Git URL
|
||||||
|
const regex = /^https:\/\/([a-zA-Z0-9.-]+)\/([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)(\/commit\/([a-f0-9]{40}))?$/;
|
||||||
|
// 或者,以 .git 结尾
|
||||||
|
const regexWithGit = /^https:\/\/([a-zA-Z0-9.-]+)\/([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)\.git$/;
|
||||||
|
return regex.test(url) || regexWithGit.test(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRepoNameFromGitUrl(url) {
|
||||||
|
// 正则表达式支持两种格式的 Git URL
|
||||||
|
const regexWithCommit = /^https:\/\/([a-zA-Z0-9.-]+)\/([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)\/commit\/([a-f0-9]{40})$/; // 针对 commit ID 的 URL
|
||||||
|
const regexWithoutCommit = /^https:\/\/([a-zA-Z0-9.-]+)\/([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)\.git$/; // 针对 .git 结尾的 URL
|
||||||
|
// 尝试匹配 commit URL
|
||||||
|
let match = url.match(regexWithCommit);
|
||||||
|
if (match) {
|
||||||
|
return match[3]; // 返回 repo-name
|
||||||
|
}
|
||||||
|
// 尝试匹配 .git URL
|
||||||
|
match = url.match(regexWithoutCommit);
|
||||||
|
if (match) {
|
||||||
|
return match[3]; // 返回 repo-name
|
||||||
|
}
|
||||||
|
return null; // 如果没有匹配,返回 null
|
||||||
|
}
|
||||||
|
|
||||||
|
const $repoTemplate = $('#repo_template');
|
||||||
|
const $devstarTemplate = $('#devstar_template');
|
||||||
|
const $gitURLTemplate = $('#git_url_template');
|
||||||
|
const $templateUnits = $('#template_units');
|
||||||
|
const $nonTemplate = $('#non_template');
|
||||||
|
const $repoTemplateArea = $('#repo_template_area');
|
||||||
|
const $gitURLTemplateArea = $('#git_url_template_area');
|
||||||
|
const $devstarTemplateArea = $('#devstar_template_area');
|
||||||
|
|
||||||
|
const isDevstar = /^(https?:\/\/)?(www\.)?(devstar\.cn)(:\d+)?(\/.*)?$/i.test(document.URL);
|
||||||
|
if (isDevstar) {
|
||||||
|
hideElem($devstarTemplateArea);
|
||||||
|
$devstarTemplateArea.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const GitURL = document.URL.match(/[?&]template_url=([^&]*)/);
|
||||||
|
if (GitURL) {
|
||||||
|
if (isValidGitUrl(GitURL[1])) {
|
||||||
|
$gitURLTemplate.val(GitURL[1]);
|
||||||
|
}
|
||||||
|
$devstarTemplateArea.val('');
|
||||||
|
hideElem($devstarTemplateArea);
|
||||||
|
hideElem($repoTemplateArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
const template_id = document.URL.match(/[?&]template_id=([^&]*)/);
|
||||||
|
if (template_id) {
|
||||||
|
$devstarTemplateArea.val('');
|
||||||
|
hideElem($devstarTemplateArea);
|
||||||
|
$gitURLTemplate.val("");
|
||||||
|
hideElem($gitURLTemplateArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkTemplate = function () {
|
||||||
|
if ($repoTemplate.val() !== '' && $repoTemplate.val() !== '0') {
|
||||||
|
showElem($templateUnits);
|
||||||
|
hideElem($nonTemplate);
|
||||||
|
hideElem($gitURLTemplateArea);
|
||||||
|
$devstarTemplateArea.val('');
|
||||||
|
hideElem($devstarTemplateArea);
|
||||||
|
} else {
|
||||||
|
showElem($devstarTemplateArea);
|
||||||
|
hideElem($templateUnits);
|
||||||
|
showElem($nonTemplate);
|
||||||
|
showElem($gitURLTemplateArea);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$repoTemplate.on('change', checkTemplate);
|
||||||
|
checkTemplate();
|
||||||
|
|
||||||
|
const checkGitURLTemplate = function () {
|
||||||
|
if ($gitURLTemplate.val() !== '' && $gitURLTemplate.val() !== '0') {
|
||||||
|
if ($('#repo_name').val() == '') {
|
||||||
|
$('#repo_name').val(getRepoNameFromGitUrl($gitURLTemplate.val()));
|
||||||
|
}
|
||||||
|
if ($('#description').val() == '') {
|
||||||
|
$('#description').val("init repo from " + $gitURLTemplate.val());
|
||||||
|
}
|
||||||
|
hideElem($repoTemplateArea);
|
||||||
|
hideElem($nonTemplate);
|
||||||
|
$devstarTemplateArea.val('');
|
||||||
|
hideElem($devstarTemplateArea);
|
||||||
|
} else {
|
||||||
|
showElem($devstarTemplateArea);
|
||||||
|
showElem($repoTemplateArea);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if($gitURLTemplate.length){
|
||||||
|
$gitURLTemplate.on('change', checkGitURLTemplate);
|
||||||
|
checkGitURLTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkDevStarTemplate = function () {
|
||||||
|
if ($devstarTemplate.val() !== '' && $devstarTemplate.val() !== '0') {
|
||||||
|
if ($('#repo_name').val() == '') {
|
||||||
|
$('#repo_name').val(getRepoNameFromGitUrl($devstarTemplate.val()));
|
||||||
|
}
|
||||||
|
if ($('#description').val() == '') {
|
||||||
|
$('#description').val("init repo from " + $devstarTemplate.val());
|
||||||
|
}
|
||||||
|
$repoTemplate.val('');
|
||||||
|
hideElem($gitURLTemplateArea);
|
||||||
|
hideElem($repoTemplateArea);
|
||||||
|
hideElem($nonTemplate);
|
||||||
|
} else {
|
||||||
|
showElem($repoTemplateArea);
|
||||||
|
showElem($gitURLTemplateArea);
|
||||||
|
hideElem($templateUnits);
|
||||||
|
showElem($nonTemplate);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if($devstarTemplate.length){
|
||||||
|
$devstarTemplate.on('change', checkDevStarTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
const initDevStarTemplateSearch = function () {
|
||||||
|
$('#devstar_template_search')
|
||||||
|
.dropdown({
|
||||||
|
apiSettings: {
|
||||||
|
url: `https://devstar.cn/api/v1/repos/search?q={query}&template=true`,
|
||||||
|
onResponse(response) {
|
||||||
|
console.log(response.data);
|
||||||
|
const filteredResponse = { success: true, results: [] };
|
||||||
|
filteredResponse.results.push({
|
||||||
|
name: '-----',
|
||||||
|
value: '',
|
||||||
|
});
|
||||||
|
// Parse the response from the api to work with our dropdown
|
||||||
|
$.each(response.data, (_r, repo) => {
|
||||||
|
filteredResponse.results.push({
|
||||||
|
name: htmlEscape("DevStar.cn/" + repo.full_name),
|
||||||
|
value: repo.clone_url,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return filteredResponse;
|
||||||
|
},
|
||||||
|
cache: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
fullTextSearch: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
initDevStarTemplateSearch();
|
||||||
|
|
||||||
|
const changeOwner = function () {
|
||||||
|
$('#repo_template_search')
|
||||||
|
.dropdown({
|
||||||
|
apiSettings: {
|
||||||
|
url: `${appSubUrl}/repo/search?q={query}&template=true&priority_owner_id=${$('#uid').val()}`,
|
||||||
|
onResponse(response) {
|
||||||
|
const filteredResponse = { success: true, results: [] };
|
||||||
|
filteredResponse.results.push({
|
||||||
|
name: '-----',
|
||||||
|
value: '',
|
||||||
|
});
|
||||||
|
// Parse the response from the api to work with our dropdown
|
||||||
|
$.each(response.data, (_r, repo) => {
|
||||||
|
filteredResponse.results.push({
|
||||||
|
name: htmlEscape(repo.repository.full_name),
|
||||||
|
value: repo.repository.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return filteredResponse;
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
fullTextSearch: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$('#uid').on('change', changeOwner);
|
||||||
|
changeOwner();
|
||||||
|
}
|
||||||
@@ -66,6 +66,7 @@ import {initGlobalButtonClickOnEnter, initGlobalButtons, initGlobalDeleteButton}
|
|||||||
import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
|
import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
|
||||||
import {callInitFunctions} from './modules/init.ts';
|
import {callInitFunctions} from './modules/init.ts';
|
||||||
import {initRepoViewFileTree} from './features/repo-view-file-tree.ts';
|
import {initRepoViewFileTree} from './features/repo-view-file-tree.ts';
|
||||||
|
import {initRepoTemplateSearch} from './features/repo-template.ts';
|
||||||
|
|
||||||
const initStartTime = performance.now();
|
const initStartTime = performance.now();
|
||||||
const initPerformanceTracer = callInitFunctions([
|
const initPerformanceTracer = callInitFunctions([
|
||||||
@@ -164,6 +165,7 @@ const initPerformanceTracer = callInitFunctions([
|
|||||||
initOAuth2SettingsDisableCheckbox,
|
initOAuth2SettingsDisableCheckbox,
|
||||||
|
|
||||||
initRepoFileView,
|
initRepoFileView,
|
||||||
|
initRepoTemplateSearch,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// it must be the last one, then the "querySelectorAll" only needs to be executed once for global init functions.
|
// it must be the last one, then the "querySelectorAll" only needs to be executed once for global init functions.
|
||||||
|
|||||||
Reference in New Issue
Block a user