Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .cloudbees/testing/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ name: 'Checkout'
description: 'Checkout a Git repository at a particular version'
inputs:
provider:
description: 'SCM provider that is hosting the repository. For example github, bitbucket, gitlab or custom'
description: |
SCM provider that is hosting the repository. For example github, bitbucket, gitlab or custom.
When specifying an SSH URL within the repository input, the provider must be set to 'custom'.
default: "${{ cloudbees.scm.provider }}"
repository:
description: 'Repository name with owner. For example, actions/checkout. Alternatively if provider is custom then this is the clone URL of the repository'
description: |
Repository URL or name with owner.
For example, cloudbees-io/checkout or https://github.com/cloudbees-io/checkout.
default: "${{ cloudbees.scm.repository }}"
ref:
description: >
Expand Down
14 changes: 9 additions & 5 deletions .cloudbees/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,17 @@ jobs:
env:
RUNNER_DEBUG: "1"
- name: Verify that the repo was checked out
uses: docker://golang:1.24.6
uses: docker://golang:1.24-alpine3.22
run: |
set -x
[ -d .git ]
[ -f Dockerfile ]
go build .
echo Repository URL = ${{ steps.runaction.outputs.repository-url }}
echo Commit ID = ${{ steps.runaction.outputs.commit }}
echo Commit URL = ${{ steps.runaction.outputs.commit-url }}
echo Ref = ${{ steps.runaction.outputs.ref }}
echo Action outputs:
echo ' Repository URL = ${{ steps.runaction.outputs.repository-url }}'
echo ' Commit = ${{ steps.runaction.outputs.commit }}'
echo ' Commit URL = ${{ steps.runaction.outputs.commit-url }}'
echo ' Ref = ${{ steps.runaction.outputs.ref }}'
[ "${{ steps.runaction.outputs.repository-url }}" = "https://github.com/cloudbees-io/checkout.git" ]
[ "${{ steps.runaction.outputs.commit }}" = "${{ cloudbees.scm.sha }}" ]
[ "${{ steps.runaction.outputs.commit-url }}" = "https://github.com/cloudbees-io/checkout/commit/${{ cloudbees.scm.sha }}" ]
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
build/
vendor
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ verify: format sync test ## Verifies that the committed code is formatted, all f
echo "$(ANSI_BOLD)✅ Git workspace is clean$(ANSI_RESET)" ; \
else \
echo "$(ANSI_BOLD)❌ Git workspace is dirty$(ANSI_RESET)" ; \
git status --porcelain ; \
exit 1 ; \
fi

Expand Down
5 changes: 4 additions & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Default is `1`.
| No

| The SCM provider hosting the repository. For example GitHub, BitBucket, or custom. The default is `${{ cloudbees.scm.provider }}`.
When specifying an SSH URL within the repository input, the provider must be set to 'custom'.

| `ref`
| String
Expand All @@ -74,7 +75,9 @@ The action uses the default branch, except when checking out the triggering work
| `repository`
| String
| No
| The repository name with owner. For example, `actions/checkout`. Default value is `${{ cloudbees.scm.repository }}`.
| Repository URL or name with owner.
For example, https://github.com/cloudbees-io/checkout or cloudbees-io/checkout.
Default value is `${{ cloudbees.scm.repository }}`.

| `set-safe-directory`
| Boolean
Expand Down
8 changes: 6 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ name: 'Checkout'
description: 'Checkout a Git repository at a particular version'
inputs:
provider:
description: 'SCM provider that is hosting the repository. For example github, bitbucket, gitlab or custom'
description: |
SCM provider that is hosting the repository. For example github, bitbucket, gitlab or custom.
When specifying an SSH URL within the repository input, the provider must be set to 'custom'.
default: "${{ cloudbees.scm.provider }}"
repository:
description: 'Repository name with owner. For example, actions/checkout. Alternatively if provider is custom then this is the clone URL of the repository'
description: |
Repository URL or name with owner.
For example, cloudbees-io/checkout or https://github.com/cloudbees-io/checkout.
default: "${{ cloudbees.scm.repository }}"
ref:
description: >
Expand Down
105 changes: 85 additions & 20 deletions internal/checkout/source_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
type Config struct {
Provider string
Repository string
repositoryCloneURL string
Ref string
CloudBeesApiToken string
CloudBeesApiURL string
Expand Down Expand Up @@ -85,12 +86,14 @@ func (cfg *Config) validate() error {
core.Debug("providerURL = %s", cfg.providerURL)
core.Debug("token auth type = %s", cfg.TokenAuthtype)

// Repository
if cfg.Provider != auth.CustomProvider {
splitRepository := strings.Split(cfg.Repository, "/")
if len(splitRepository) != 2 || splitRepository[0] == "" || splitRepository[1] == "" {
return fmt.Errorf("invalid repository '%s', expected format {owner}/{repo}", cfg.Repository)
}
err = cfg.initDefaultProviderURL()
if err != nil {
return err
}

err = cfg.normalizeRepositoryURL()
if err != nil {
return err
}

// Repository Path
Expand Down Expand Up @@ -172,7 +175,11 @@ func (cfg *Config) validate() error {
cfg.githubWorkflowOrganizationId, _ = getStringFromMap(owner, "id")
}

// Determine the provider URL that the repository is being hosted from
return nil
}

// initDefaultProviderURL sets the default provider-specific serverURL if not specified
func (cfg *Config) initDefaultProviderURL() error {
switch cfg.Provider {
case auth.GitHubProvider:
if cfg.GithubServerURL == "" {
Expand Down Expand Up @@ -220,14 +227,69 @@ func (cfg *Config) validate() error {
return nil
}

func (cfg *Config) normalizeRepositoryURL() error {
cfg.repositoryCloneURL = cfg.Repository

if isSSHURL(cfg.Repository) {
// Handle SSH URL
if cfg.SSHKey == "" {
return errors.New("must also specify the ssh-key input when specifying an SSH URL as repository input")
}
if cfg.Provider != auth.CustomProvider {
return errors.New("provider input must be set to 'custom' when specifying an SSH URL within the repository input")
}
} else {
// Handle HTTP URL
repoURL, err := url.Parse(cfg.Repository)
if err != nil {
return fmt.Errorf("invalid repository %q: %w", cfg.Repository, err)
}

if repoURL.IsAbs() && repoURL.Host != "" {
serverURL := cfg.serverURL()

if !strings.HasPrefix(cfg.Repository, serverURL+"/") {
return fmt.Errorf("repository url (%s) must start with the server URL (%s) of the provider (%s)", cfg.Repository, serverURL, cfg.Provider)
}

// Add .git suffix to repository URL in case of a known SCM provider.
// This is to align with the old logic implemented in fetchURL().
// We cannot add the .git suffix to every clone URL since some SCM providers don't support it (e.g. Azure DevOps).
switch cfg.Provider {
case auth.GitHubProvider,
auth.BitbucketProvider,
auth.BitbucketDatacenterProvider,
auth.GitLabProvider:
if !strings.HasSuffix(cfg.Repository, ".git") {
cfg.repositoryCloneURL = fmt.Sprintf("%s.git", cfg.Repository)
}
}
} else {
if cfg.Provider == auth.CustomProvider {
return errors.New("short form repository URL provided but an absolute URL is required when using a custom SCM provider")
}

splitRepository := strings.Split(repoURL.Path, "/")
if len(splitRepository) != 2 || splitRepository[0] == "" || splitRepository[1] == "" {
return fmt.Errorf("invalid repository '%s', expected format {owner}/{repo} or {serverURL}/{repoPath}", cfg.Repository)
}

// Absolutize the short form URL
cfg.repositoryCloneURL, err = cfg.fetchURL(cfg.SSHKey != "")
if err != nil {
return fmt.Errorf("absolutize repository url: %w", err)
}
}
}

return nil
}

func (cfg *Config) writeActionOutputs(cli *git.GitCLI) error {
//Output commit details
outDir := os.Getenv("CLOUDBEES_OUTPUTS")
fullUrl, err := cfg.fetchURL(cfg.SSHKey != "")
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(outDir, "repository-url"), []byte(fullUrl), 0640)
fullUrl := cfg.repositoryCloneURL
err := os.WriteFile(filepath.Join(outDir, "repository-url"), []byte(fullUrl), 0640)
if err != nil {
return err
}
Expand Down Expand Up @@ -260,9 +322,13 @@ func (cfg *Config) writeActionOutputs(cli *git.GitCLI) error {
case auth.BitbucketProvider:
fullCommitUrl = fullUrl + "/commits/" + commitId
case auth.BitbucketDatacenterProvider:
name := strings.Split(cfg.Repository, "/")
if len(name) == 2 {
fullCommitUrl = cfg.providerURL + "projects/" + name[0] + "/repos/" + name[1] + "/commits/" + commitId
repo := cfg.Repository
if strings.HasSuffix(repo, ".git") {
repo = repo[:len(repo)-len(".git")]
}
name := strings.Split(repo, "/")
if len(name) >= 2 {
fullCommitUrl = cfg.BitbucketServerURL + "/projects/" + name[len(name)-2] + "/repos/" + name[len(name)-1] + "/commits/" + commitId
}
}
}
Expand All @@ -289,7 +355,7 @@ func (cfg *Config) ensureScmPathForBitbucketDatacenterUrl() error {

p, err := url.Parse(cfg.BitbucketServerURL)
if err != nil {
return err
return fmt.Errorf("bitbucket-server-url: %w", err)
}

if strings.HasSuffix(p.Path, "/scm") || strings.HasSuffix(p.Path, "/scm/") {
Expand Down Expand Up @@ -327,10 +393,8 @@ func (cfg *Config) Run(ctx context.Context) (retErr error) {
return err
}

repositoryURL, err := cfg.fetchURL(useSSH)
if err != nil {
return err
}
repositoryURL := cfg.repositoryCloneURL

fmt.Printf("Syncing Repository: %s\n", repositoryURL)

// Remove conflicting file path
Expand Down Expand Up @@ -859,6 +923,7 @@ func (cfg *Config) isWorkflowRepository(eventContext map[string]interface{}) boo
core.Debug("ctx.repository = %s", ctxRepository)
core.Debug("cfg.provider = %s", cfg.Provider)
core.Debug("cfg.repository = %s", cfg.Repository)
core.Debug("cfg.repositoryCloneURL = %s", cfg.repositoryCloneURL)

return haveP && cfg.Provider == ctxProvider && haveR && cfg.Repository == ctxRepository
}
Expand Down
Loading