Phase docs are targetPath + phaseRepo + phase.DocumentEntrypoint
Change-Id: I1d5ea75d1c19eb4ebaf37b28918ccde771bcef85 Relates-To: #356
This commit is contained in:
parent
0dc4ab7491
commit
dc68640389
@ -1,2 +1,2 @@
|
|||||||
phase:
|
phase:
|
||||||
path: airshipctl/manifests/phases
|
path: manifests/phases
|
||||||
|
@ -7,7 +7,7 @@ config:
|
|||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: ImageConfiguration
|
kind: ImageConfiguration
|
||||||
name: isogen
|
name: isogen
|
||||||
documentEntryPoint: airshipctl/manifests/site/test-site/ephemeral/bootstrap
|
documentEntryPoint: manifests/site/test-site/ephemeral/bootstrap
|
||||||
---
|
---
|
||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: Phase
|
kind: Phase
|
||||||
@ -19,7 +19,7 @@ config:
|
|||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: KubernetesApply
|
kind: KubernetesApply
|
||||||
name: kubernetes-apply
|
name: kubernetes-apply
|
||||||
documentEntryPoint: airshipctl/manifests/site/test-site/ephemeral/initinfra
|
documentEntryPoint: manifests/site/test-site/ephemeral/initinfra
|
||||||
---
|
---
|
||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: Phase
|
kind: Phase
|
||||||
@ -31,7 +31,7 @@ config:
|
|||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: KubernetesApply
|
kind: KubernetesApply
|
||||||
name: kubernetes-apply
|
name: kubernetes-apply
|
||||||
documentEntryPoint: airshipctl/manifests/site/test-site/ephemeral/controlplane
|
documentEntryPoint: manifests/site/test-site/ephemeral/controlplane
|
||||||
---
|
---
|
||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: Phase
|
kind: Phase
|
||||||
@ -44,7 +44,7 @@ config:
|
|||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: KubernetesApply
|
kind: KubernetesApply
|
||||||
name: kubernetes-apply
|
name: kubernetes-apply
|
||||||
documentEntryPoint: airshipctl/manifests/site/test-site/target/initinfra
|
documentEntryPoint: manifests/site/test-site/target/initinfra
|
||||||
---
|
---
|
||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: Phase
|
kind: Phase
|
||||||
@ -57,7 +57,7 @@ config:
|
|||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: KubernetesApply
|
kind: KubernetesApply
|
||||||
name: kubernetes-apply
|
name: kubernetes-apply
|
||||||
documentEntryPoint: airshipctl/manifests/site/test-site/target/workers
|
documentEntryPoint: manifests/site/test-site/target/workers
|
||||||
---
|
---
|
||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: Phase
|
kind: Phase
|
||||||
@ -102,4 +102,4 @@ config:
|
|||||||
apiVersion: airshipit.org/v1alpha1
|
apiVersion: airshipit.org/v1alpha1
|
||||||
kind: KubernetesApply
|
kind: KubernetesApply
|
||||||
name: kubernetes-apply
|
name: kubernetes-apply
|
||||||
documentEntryPoint: airshipctl/manifests/site/test-site/target/workload
|
documentEntryPoint: manifests/site/test-site/target/workload
|
||||||
|
@ -222,6 +222,7 @@ func makeDefaultHelper(t *testing.T) ifc.Helper {
|
|||||||
cfg := config.NewConfig()
|
cfg := config.NewConfig()
|
||||||
cfg.Manifests[config.AirshipDefaultManifest].TargetPath = "./testdata"
|
cfg.Manifests[config.AirshipDefaultManifest].TargetPath = "./testdata"
|
||||||
cfg.Manifests[config.AirshipDefaultManifest].MetadataPath = "metadata.yaml"
|
cfg.Manifests[config.AirshipDefaultManifest].MetadataPath = "metadata.yaml"
|
||||||
|
cfg.Manifests[config.AirshipDefaultManifest].Repositories[config.DefaultTestPhaseRepo].URLString = ""
|
||||||
cfg.SetLoadedConfigPath(".")
|
cfg.SetLoadedConfigPath(".")
|
||||||
helper, err := phase.NewHelper(cfg)
|
helper, err := phase.NewHelper(cfg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -358,6 +358,20 @@ func (c *Config) CurrentContextTargetPath() (string, error) {
|
|||||||
return ccm.TargetPath, nil
|
return ccm.TargetPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentContextPhaseRepositoryDir returns phase repository directory from current context's manifest
|
||||||
|
// E.g. let repository url be "http://dummy.org/phaserepo.git" then repo directory under targetPath is "phaserepo"
|
||||||
|
func (c *Config) CurrentContextPhaseRepositoryDir() (string, error) {
|
||||||
|
ccm, err := c.CurrentContextManifest()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
repo, exist := ccm.Repositories[ccm.PhaseRepositoryName]
|
||||||
|
if !exist {
|
||||||
|
return "", ErrMissingRepositoryName{}
|
||||||
|
}
|
||||||
|
return util.GitDirNameFromURL(repo.URL()), nil
|
||||||
|
}
|
||||||
|
|
||||||
// CurrentContextClusterType returns cluster type of current context
|
// CurrentContextClusterType returns cluster type of current context
|
||||||
func (c *Config) CurrentContextClusterType() (string, error) {
|
func (c *Config) CurrentContextClusterType() (string, error) {
|
||||||
context, err := c.GetCurrentContext()
|
context, err := c.GetCurrentContext()
|
||||||
@ -568,12 +582,16 @@ func (c *Config) CurrentContextManifestMetadata() (*Metadata, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
phaseRepoDir, err := c.CurrentContextPhaseRepositoryDir()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
meta := &Metadata{
|
meta := &Metadata{
|
||||||
// Populate with empty values to avoid nil pointers
|
// Populate with empty values to avoid nil pointers
|
||||||
Inventory: &InventoryMeta{},
|
Inventory: &InventoryMeta{},
|
||||||
PhaseMeta: &PhaseMeta{},
|
PhaseMeta: &PhaseMeta{},
|
||||||
}
|
}
|
||||||
err = util.ReadYAMLFile(filepath.Join(manifest.TargetPath, manifest.MetadataPath), meta)
|
err = util.ReadYAMLFile(filepath.Join(manifest.TargetPath, phaseRepoDir, manifest.MetadataPath), meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"opendev.org/airship/airshipctl/pkg/util"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@ -315,6 +317,26 @@ func TestCurrentTargetPath(t *testing.T) {
|
|||||||
assert.Equal(t, conf.Manifests[defaultString].TargetPath, targetPath)
|
assert.Equal(t, conf.Manifests[defaultString].TargetPath, targetPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCurrentPhaseRepositoryDir(t *testing.T) {
|
||||||
|
conf, cleanup := testutil.InitConfig(t)
|
||||||
|
defer cleanup(t)
|
||||||
|
|
||||||
|
conf.CurrentContext = currentContextName
|
||||||
|
conf.Contexts[currentContextName].Manifest = defaultString
|
||||||
|
|
||||||
|
phaseRepoDir, err := conf.CurrentContextPhaseRepositoryDir()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, util.GitDirNameFromURL(
|
||||||
|
conf.Manifests[defaultString].Repositories[conf.Manifests[defaultString].PhaseRepositoryName].URL()),
|
||||||
|
phaseRepoDir)
|
||||||
|
|
||||||
|
conf.Manifests[defaultString].PhaseRepositoryName = "nonexisting"
|
||||||
|
phaseRepoDir, err = conf.CurrentContextPhaseRepositoryDir()
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Equal(t, config.ErrMissingRepositoryName{}, err)
|
||||||
|
assert.Equal(t, "", phaseRepoDir)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCurrentContextEntryPoint(t *testing.T) {
|
func TestCurrentContextEntryPoint(t *testing.T) {
|
||||||
conf, cleanup := testutil.InitConfig(t)
|
conf, cleanup := testutil.InitConfig(t)
|
||||||
defer cleanup(t)
|
defer cleanup(t)
|
||||||
@ -414,9 +436,16 @@ func TestCurrentContextManifestMetadata(t *testing.T) {
|
|||||||
context := &config.Context{
|
context := &config.Context{
|
||||||
Manifest: "testManifest",
|
Manifest: "testManifest",
|
||||||
}
|
}
|
||||||
|
repos := map[string]*config.Repository{
|
||||||
|
config.DefaultTestPhaseRepo: {
|
||||||
|
URLString: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
manifest := &config.Manifest{
|
manifest := &config.Manifest{
|
||||||
MetadataPath: tt.metaPath,
|
MetadataPath: tt.metaPath,
|
||||||
TargetPath: "testdata",
|
TargetPath: "testdata",
|
||||||
|
PhaseRepositoryName: config.DefaultTestPhaseRepo,
|
||||||
|
Repositories: repos,
|
||||||
}
|
}
|
||||||
conf.Manifests = map[string]*config.Manifest{
|
conf.Manifests = map[string]*config.Manifest{
|
||||||
"testManifest": manifest,
|
"testManifest": manifest,
|
||||||
|
2
pkg/config/testdata/config-string.yaml
vendored
2
pkg/config/testdata/config-string.yaml
vendored
@ -17,7 +17,7 @@ managementConfiguration:
|
|||||||
type: redfish
|
type: redfish
|
||||||
manifests:
|
manifests:
|
||||||
dummy_manifest:
|
dummy_manifest:
|
||||||
metadataPath: manifests/site/test-site/metadata.yaml
|
metadataPath: metadata.yaml
|
||||||
phaseRepositoryName: primary
|
phaseRepositoryName: primary
|
||||||
repositories:
|
repositories:
|
||||||
primary:
|
primary:
|
||||||
|
2
pkg/config/testdata/manifest-string.yaml
vendored
2
pkg/config/testdata/manifest-string.yaml
vendored
@ -1,4 +1,4 @@
|
|||||||
metadataPath: manifests/site/test-site/metadata.yaml
|
metadataPath: metadata.yaml
|
||||||
phaseRepositoryName: primary
|
phaseRepositoryName: primary
|
||||||
repositories:
|
repositories:
|
||||||
primary:
|
primary:
|
||||||
|
@ -52,6 +52,7 @@ func NewConfig() *Config {
|
|||||||
TargetPath: "/tmp/" + AirshipDefaultManifest,
|
TargetPath: "/tmp/" + AirshipDefaultManifest,
|
||||||
PhaseRepositoryName: DefaultTestPhaseRepo,
|
PhaseRepositoryName: DefaultTestPhaseRepo,
|
||||||
SubPath: AirshipDefaultManifestRepo + "/manifests/site",
|
SubPath: AirshipDefaultManifestRepo + "/manifests/site",
|
||||||
|
MetadataPath: DefaultManifestMetadataFile,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -231,6 +231,12 @@ func makeDefaultHelper(t *testing.T) ifc.Helper {
|
|||||||
"default-manifest": {
|
"default-manifest": {
|
||||||
MetadataPath: "metadata.yaml",
|
MetadataPath: "metadata.yaml",
|
||||||
TargetPath: "testdata",
|
TargetPath: "testdata",
|
||||||
|
PhaseRepositoryName: config.DefaultTestPhaseRepo,
|
||||||
|
Repositories: map[string]*config.Repository{
|
||||||
|
config.DefaultTestPhaseRepo: {
|
||||||
|
URLString: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,8 @@ func (p *phase) DocumentRoot() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetPath := p.helper.TargetPath()
|
targetPath := p.helper.TargetPath()
|
||||||
return filepath.Join(targetPath, relativePath), nil
|
phaseRepoDir := p.helper.PhaseRepoDir()
|
||||||
|
return filepath.Join(targetPath, phaseRepoDir, relativePath), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Details returns description of the phase
|
// Details returns description of the phase
|
||||||
|
@ -63,6 +63,12 @@ func TestRunCommand(t *testing.T) {
|
|||||||
"manifest": {
|
"manifest": {
|
||||||
MetadataPath: "broken_metadata.yaml",
|
MetadataPath: "broken_metadata.yaml",
|
||||||
TargetPath: "testdata",
|
TargetPath: "testdata",
|
||||||
|
PhaseRepositoryName: config.DefaultTestPhaseRepo,
|
||||||
|
Repositories: map[string]*config.Repository{
|
||||||
|
config.DefaultTestPhaseRepo: {
|
||||||
|
URLString: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
conf.CurrentContext = "context"
|
conf.CurrentContext = "context"
|
||||||
@ -127,6 +133,12 @@ func TestPlanCommand(t *testing.T) {
|
|||||||
"manifest": {
|
"manifest": {
|
||||||
MetadataPath: "broken_metadata.yaml",
|
MetadataPath: "broken_metadata.yaml",
|
||||||
TargetPath: "testdata",
|
TargetPath: "testdata",
|
||||||
|
PhaseRepositoryName: config.DefaultTestPhaseRepo,
|
||||||
|
Repositories: map[string]*config.Repository{
|
||||||
|
config.DefaultTestPhaseRepo: {
|
||||||
|
URLString: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
conf.CurrentContext = "context"
|
conf.CurrentContext = "context"
|
||||||
|
@ -34,7 +34,7 @@ type Helper struct {
|
|||||||
phaseRoot string
|
phaseRoot string
|
||||||
inventoryRoot string
|
inventoryRoot string
|
||||||
targetPath string
|
targetPath string
|
||||||
|
phaseRepoDir string
|
||||||
metadata *config.Metadata
|
metadata *config.Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,12 +47,16 @@ func NewHelper(cfg *config.Config) (ifc.Helper, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
helper.phaseRepoDir, err = cfg.CurrentContextPhaseRepositoryDir()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
helper.metadata, err = cfg.CurrentContextManifestMetadata()
|
helper.metadata, err = cfg.CurrentContextManifestMetadata()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
helper.phaseRoot = filepath.Join(helper.targetPath, helper.metadata.PhaseMeta.Path)
|
helper.phaseRoot = filepath.Join(helper.targetPath, helper.phaseRepoDir, helper.metadata.PhaseMeta.Path)
|
||||||
helper.inventoryRoot = filepath.Join(helper.targetPath, helper.metadata.Inventory.Path)
|
helper.inventoryRoot = filepath.Join(helper.targetPath, helper.phaseRepoDir, helper.metadata.Inventory.Path)
|
||||||
return helper, nil
|
return helper, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,6 +201,12 @@ func (helper *Helper) TargetPath() string {
|
|||||||
return helper.targetPath
|
return helper.targetPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PhaseRepoDir returns the last part of the repo url
|
||||||
|
// E.g. http://dummy.org/reponame.git -> reponame
|
||||||
|
func (helper *Helper) PhaseRepoDir() string {
|
||||||
|
return helper.phaseRepoDir
|
||||||
|
}
|
||||||
|
|
||||||
// PhaseRoot returns path to document root with phase documents
|
// PhaseRoot returns path to document root with phase documents
|
||||||
func (helper *Helper) PhaseRoot() string {
|
func (helper *Helper) PhaseRoot() string {
|
||||||
return helper.phaseRoot
|
return helper.phaseRoot
|
||||||
|
@ -449,8 +449,11 @@ manifests:
|
|||||||
phaseRepositoryName: primary
|
phaseRepositoryName: primary
|
||||||
targetPath: testdata
|
targetPath: testdata
|
||||||
metadataPath: valid_site/metadata.yaml
|
metadataPath: valid_site/metadata.yaml
|
||||||
subPath: valid_site`
|
subPath: valid_site
|
||||||
|
repositories:
|
||||||
|
primary:
|
||||||
|
url: "empty/filename/"
|
||||||
|
`
|
||||||
conf := &config.Config{}
|
conf := &config.Config{}
|
||||||
err := yaml.Unmarshal([]byte(confString), conf)
|
err := yaml.Unmarshal([]byte(confString), conf)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
// Helper is a phase helper that provides phases with additional config related information
|
// Helper is a phase helper that provides phases with additional config related information
|
||||||
type Helper interface {
|
type Helper interface {
|
||||||
TargetPath() string
|
TargetPath() string
|
||||||
|
PhaseRepoDir() string
|
||||||
WorkDir() (string, error)
|
WorkDir() (string, error)
|
||||||
Phase(phaseID ID) (*v1alpha1.Phase, error)
|
Phase(phaseID ID) (*v1alpha1.Phase, error)
|
||||||
Plan() (*v1alpha1.PhasePlan, error)
|
Plan() (*v1alpha1.PhasePlan, error)
|
||||||
|
@ -33,6 +33,12 @@ func TestRender(t *testing.T) {
|
|||||||
rs := testutil.DummyConfig()
|
rs := testutil.DummyConfig()
|
||||||
dummyManifest := rs.Manifests["dummy_manifest"]
|
dummyManifest := rs.Manifests["dummy_manifest"]
|
||||||
dummyManifest.TargetPath = "testdata"
|
dummyManifest.TargetPath = "testdata"
|
||||||
|
dummyManifest.PhaseRepositoryName = config.DefaultTestPhaseRepo
|
||||||
|
dummyManifest.Repositories = map[string]*config.Repository{
|
||||||
|
config.DefaultTestPhaseRepo: {
|
||||||
|
URLString: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
dummyManifest.SubPath = ""
|
dummyManifest.SubPath = ""
|
||||||
dummyManifest.MetadataPath = "metadata.yaml"
|
dummyManifest.MetadataPath = "metadata.yaml"
|
||||||
fixturePath := "phase"
|
fixturePath := "phase"
|
||||||
|
@ -56,8 +56,11 @@ func withTestDataPath(path string) Configuration {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Unable to initialize management tests. Current Context error %q", err))
|
panic(fmt.Sprintf("Unable to initialize management tests. Current Context error %q", err))
|
||||||
}
|
}
|
||||||
manifest.TargetPath = fmt.Sprintf("testdata/%s", path)
|
manifest.TargetPath = "testdata"
|
||||||
manifest.MetadataPath = "metadata.yaml"
|
manifest.MetadataPath = "metadata.yaml"
|
||||||
|
dummyRepo := testutil.DummyRepository()
|
||||||
|
dummyRepo.URLString = fmt.Sprintf("http://dummy.url.com/%s.git", path)
|
||||||
|
manifest.Repositories = map[string]*config.Repository{manifest.PhaseRepositoryName: dummyRepo}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ func DummyManifest() *config.Manifest {
|
|||||||
// Repositories is the map of repository addressable by a name
|
// Repositories is the map of repository addressable by a name
|
||||||
m.Repositories = map[string]*config.Repository{"primary": DummyRepository()}
|
m.Repositories = map[string]*config.Repository{"primary": DummyRepository()}
|
||||||
m.PhaseRepositoryName = "primary"
|
m.PhaseRepositoryName = "primary"
|
||||||
|
m.MetadataPath = "metadata.yaml"
|
||||||
m.TargetPath = "/var/tmp/"
|
m.TargetPath = "/var/tmp/"
|
||||||
m.SubPath = "manifests/site/test-site"
|
m.SubPath = "manifests/site/test-site"
|
||||||
return m
|
return m
|
||||||
|
@ -30,7 +30,7 @@ export REMOTE_PROXY=false
|
|||||||
export AIRSHIP_CONFIG_ISO_SERVE_HOST=${HOST:-"localhost"}
|
export AIRSHIP_CONFIG_ISO_SERVE_HOST=${HOST:-"localhost"}
|
||||||
export AIRSHIP_CONFIG_ISO_PORT=${SERVE_PORT}
|
export AIRSHIP_CONFIG_ISO_PORT=${SERVE_PORT}
|
||||||
export AIRSHIP_CONFIG_ISO_NAME=${ISO_NAME:-"ubuntu-focal.iso"}
|
export AIRSHIP_CONFIG_ISO_NAME=${ISO_NAME:-"ubuntu-focal.iso"}
|
||||||
export AIRSHIP_CONFIG_METADATA_PATH=${AIRSHIP_CONFIG_METADATA_PATH:-"airshipctl/manifests/metadata.yaml"}
|
export AIRSHIP_CONFIG_METADATA_PATH=${AIRSHIP_CONFIG_METADATA_PATH:-"manifests/metadata.yaml"}
|
||||||
export SYSTEM_ACTION_RETRIES=30
|
export SYSTEM_ACTION_RETRIES=30
|
||||||
export SYSTEM_REBOOT_DELAY=30
|
export SYSTEM_REBOOT_DELAY=30
|
||||||
export AIRSHIP_CONFIG_PHASE_REPO_BRANCH=${BRANCH:-"master"}
|
export AIRSHIP_CONFIG_PHASE_REPO_BRANCH=${BRANCH:-"master"}
|
||||||
|
Loading…
Reference in New Issue
Block a user