mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-08-05 05:08:50 +00:00
Support regexp in command builder on the project name (#1419)
This commit is contained in:
@@ -58,6 +58,7 @@ const (
|
||||
DisableMarkdownFoldingFlag = "disable-markdown-folding"
|
||||
DisableRepoLockingFlag = "disable-repo-locking"
|
||||
EnablePolicyChecksFlag = "enable-policy-checks"
|
||||
EnableRegExpCmdFlag = "enable-regexp-cmd"
|
||||
GHHostnameFlag = "gh-hostname"
|
||||
GHTokenFlag = "gh-token"
|
||||
GHUserFlag = "gh-user"
|
||||
@@ -300,6 +301,10 @@ var boolFlags = map[string]boolFlag{
|
||||
description: "Enable atlantis to run user defined policy checks. This is explicitly disabled for TFE/TFC backends since plan files are inaccessible.",
|
||||
defaultValue: false,
|
||||
},
|
||||
EnableRegExpCmdFlag: {
|
||||
description: "Enable Atlantis to use regular expressions on plan/apply commands when \"-p\" flag is passed with it.",
|
||||
defaultValue: false,
|
||||
},
|
||||
AllowDraftPRs: {
|
||||
description: "Enable autoplan for Github Draft Pull Requests",
|
||||
defaultValue: false,
|
||||
|
||||
@@ -101,6 +101,7 @@ var testFlags = map[string]interface{}{
|
||||
WriteGitCredsFlag: true,
|
||||
DisableAutoplanFlag: true,
|
||||
EnablePolicyChecksFlag: false,
|
||||
EnableRegExpCmdFlag: false,
|
||||
}
|
||||
|
||||
func TestExecute_Defaults(t *testing.T) {
|
||||
|
||||
@@ -233,6 +233,17 @@ Values are chosen in this order:
|
||||
```
|
||||
Enables atlantis to run server side policies on the result of a terraform plan. Policies are defined in [server side repo config](https://www.runatlantis.io/docs/server-side-repo-config.html#reference).
|
||||
|
||||
* ### `--enable-regexp-cmd`
|
||||
```bash
|
||||
atlantis server --enable-regexp-cmd
|
||||
```
|
||||
Enable Atlantis to use regular expressions on plan/apply commands when \"-p\" flag is passed with it.
|
||||
|
||||
::: warning SECURITY WARNING
|
||||
It's not supposed to be used with `--disable-apply-all`.
|
||||
The command `atlantis apply -p .*` will bypass the restriction and run apply on every projects
|
||||
:::
|
||||
|
||||
* ### `--gh-hostname`
|
||||
```bash
|
||||
atlantis server --gh-hostname="my.github.enterprise.com"
|
||||
|
||||
@@ -38,6 +38,7 @@ func NewProjectCommandBuilder(
|
||||
pendingPlanFinder *DefaultPendingPlanFinder,
|
||||
commentBuilder CommentBuilder,
|
||||
skipCloneNoChanges bool,
|
||||
EnableRegExpCmd bool,
|
||||
) *DefaultProjectCommandBuilder {
|
||||
projectCommandBuilder := &DefaultProjectCommandBuilder{
|
||||
ParserValidator: parserValidator,
|
||||
@@ -48,6 +49,7 @@ func NewProjectCommandBuilder(
|
||||
GlobalCfg: globalCfg,
|
||||
PendingPlanFinder: pendingPlanFinder,
|
||||
SkipCloneNoChanges: skipCloneNoChanges,
|
||||
EnableRegExpCmd: EnableRegExpCmd,
|
||||
ProjectCommandContextBuilder: NewProjectCommandContextBulder(
|
||||
policyChecksSupported,
|
||||
commentBuilder,
|
||||
@@ -101,6 +103,7 @@ type DefaultProjectCommandBuilder struct {
|
||||
PendingPlanFinder *DefaultPendingPlanFinder
|
||||
ProjectCommandContextBuilder ProjectCommandContextBuilder
|
||||
SkipCloneNoChanges bool
|
||||
EnableRegExpCmd bool
|
||||
}
|
||||
|
||||
// See ProjectCommandBuilder.BuildAutoplanCommands.
|
||||
@@ -303,7 +306,7 @@ func (p *DefaultProjectCommandBuilder) buildProjectPlanCommand(ctx *CommandConte
|
||||
|
||||
// getCfg returns the atlantis.yaml config (if it exists) for this project. If
|
||||
// there is no config, then projectCfg and repoCfg will be nil.
|
||||
func (p *DefaultProjectCommandBuilder) getCfg(ctx *CommandContext, projectName string, dir string, workspace string, repoDir string) (projectCfg *valid.Project, repoCfg *valid.RepoCfg, err error) {
|
||||
func (p *DefaultProjectCommandBuilder) getCfg(ctx *CommandContext, projectName string, dir string, workspace string, repoDir string) (projectsCfg []valid.Project, repoCfg *valid.RepoCfg, err error) {
|
||||
hasConfigFile, err := p.ParserValidator.HasRepoCfg(repoDir)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "looking for %s file in %q", yaml.AtlantisYAMLFilename, repoDir)
|
||||
@@ -327,8 +330,14 @@ func (p *DefaultProjectCommandBuilder) getCfg(ctx *CommandContext, projectName s
|
||||
// If they've specified a project by name we look it up. Otherwise we
|
||||
// use the dir and workspace.
|
||||
if projectName != "" {
|
||||
projectCfg = repoCfg.FindProjectByName(projectName)
|
||||
if projectCfg == nil {
|
||||
if p.EnableRegExpCmd {
|
||||
projectsCfg = repoCfg.FindProjectsByName(projectName)
|
||||
} else {
|
||||
if p := repoCfg.FindProjectByName(projectName); p != nil {
|
||||
projectsCfg = append(projectsCfg, *p)
|
||||
}
|
||||
}
|
||||
if len(projectsCfg) == 0 {
|
||||
err = fmt.Errorf("no project with name %q is defined in %s", projectName, yaml.AtlantisYAMLFilename)
|
||||
return
|
||||
}
|
||||
@@ -343,7 +352,7 @@ func (p *DefaultProjectCommandBuilder) getCfg(ctx *CommandContext, projectName s
|
||||
err = fmt.Errorf("must specify project name: more than one project defined in %s matched dir: %q workspace: %q", yaml.AtlantisYAMLFilename, dir, workspace)
|
||||
return
|
||||
}
|
||||
projectCfg = &projCfgs[0]
|
||||
projectsCfg = projCfgs
|
||||
return
|
||||
}
|
||||
|
||||
@@ -418,7 +427,7 @@ func (p *DefaultProjectCommandBuilder) buildProjectApplyCommand(ctx *CommandCont
|
||||
)
|
||||
}
|
||||
|
||||
// buildProjectCommandCtx builds a context for a single project identified
|
||||
// buildProjectCommandCtx builds a context for a single or several projects identified
|
||||
// by the parameters.
|
||||
func (p *DefaultProjectCommandBuilder) buildProjectCommandCtx(ctx *CommandContext,
|
||||
cmd models.CommandName,
|
||||
@@ -429,27 +438,12 @@ func (p *DefaultProjectCommandBuilder) buildProjectCommandCtx(ctx *CommandContex
|
||||
workspace string,
|
||||
verbose bool) ([]models.ProjectCommandContext, error) {
|
||||
|
||||
projCfgPtr, repoCfgPtr, err := p.getCfg(ctx, projectName, repoRelDir, workspace, repoDir)
|
||||
matchingProjects, repoCfgPtr, err := p.getCfg(ctx, projectName, repoRelDir, workspace, repoDir)
|
||||
if err != nil {
|
||||
return []models.ProjectCommandContext{}, err
|
||||
}
|
||||
|
||||
var projCtxs []models.ProjectCommandContext
|
||||
var projCfg valid.MergedProjectCfg
|
||||
if projCfgPtr != nil {
|
||||
// Override any dir/workspace defined on the comment with what was
|
||||
// defined in config. This shouldn't matter since we don't allow comments
|
||||
// with both project name and dir/workspace.
|
||||
repoRelDir = projCfg.RepoRelDir
|
||||
workspace = projCfg.Workspace
|
||||
projCfg = p.GlobalCfg.MergeProjectCfg(ctx.Log, ctx.Pull.BaseRepo.ID(), *projCfgPtr, *repoCfgPtr)
|
||||
} else {
|
||||
projCfg = p.GlobalCfg.DefaultProjCfg(ctx.Log, ctx.Pull.BaseRepo.ID(), repoRelDir, workspace)
|
||||
}
|
||||
|
||||
if err := p.validateWorkspaceAllowed(repoCfgPtr, repoRelDir, workspace); err != nil {
|
||||
return []models.ProjectCommandContext{}, err
|
||||
}
|
||||
|
||||
automerge := DefaultAutomergeEnabled
|
||||
parallelApply := DefaultParallelApplyEnabled
|
||||
parallelPlan := DefaultParallelPlanEnabled
|
||||
@@ -459,17 +453,51 @@ func (p *DefaultProjectCommandBuilder) buildProjectCommandCtx(ctx *CommandContex
|
||||
parallelPlan = repoCfgPtr.ParallelPlan
|
||||
}
|
||||
|
||||
return p.ProjectCommandContextBuilder.BuildProjectContext(
|
||||
ctx,
|
||||
cmd,
|
||||
projCfg,
|
||||
commentFlags,
|
||||
repoDir,
|
||||
automerge,
|
||||
parallelApply,
|
||||
parallelPlan,
|
||||
verbose,
|
||||
), nil
|
||||
if len(matchingProjects) > 0 {
|
||||
// Override any dir/workspace defined on the comment with what was
|
||||
// defined in config. This shouldn't matter since we don't allow comments
|
||||
// with both project name and dir/workspace.
|
||||
repoRelDir = projCfg.RepoRelDir
|
||||
workspace = projCfg.Workspace
|
||||
for _, mp := range matchingProjects {
|
||||
ctx.Log.Debug("Merging config for project at dir: %q workspace: %q", mp.Dir, mp.Workspace)
|
||||
projCfg = p.GlobalCfg.MergeProjectCfg(ctx.Log, ctx.Pull.BaseRepo.ID(), mp, *repoCfgPtr)
|
||||
|
||||
projCtxs = append(projCtxs,
|
||||
p.ProjectCommandContextBuilder.BuildProjectContext(
|
||||
ctx,
|
||||
cmd,
|
||||
projCfg,
|
||||
commentFlags,
|
||||
repoDir,
|
||||
automerge,
|
||||
parallelApply,
|
||||
parallelPlan,
|
||||
verbose,
|
||||
)...)
|
||||
}
|
||||
} else {
|
||||
projCfg = p.GlobalCfg.DefaultProjCfg(ctx.Log, ctx.Pull.BaseRepo.ID(), repoRelDir, workspace)
|
||||
projCtxs = append(projCtxs,
|
||||
p.ProjectCommandContextBuilder.BuildProjectContext(
|
||||
ctx,
|
||||
cmd,
|
||||
projCfg,
|
||||
commentFlags,
|
||||
repoDir,
|
||||
automerge,
|
||||
parallelApply,
|
||||
parallelPlan,
|
||||
verbose,
|
||||
)...)
|
||||
}
|
||||
|
||||
if err := p.validateWorkspaceAllowed(repoCfgPtr, repoRelDir, workspace); err != nil {
|
||||
return []models.ProjectCommandContext{}, err
|
||||
}
|
||||
|
||||
return projCtxs, nil
|
||||
|
||||
}
|
||||
|
||||
// validateWorkspaceAllowed returns an error if repoCfg defines projects in
|
||||
|
||||
@@ -588,6 +588,7 @@ projects:
|
||||
&DefaultPendingPlanFinder{},
|
||||
&CommentParser{},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
// We run a test for each type of command.
|
||||
@@ -640,6 +641,191 @@ projects:
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildProjectCmdCtx_WithRegExpCmdEnabled(t *testing.T) {
|
||||
emptyPolicySets := valid.PolicySets{
|
||||
Version: nil,
|
||||
PolicySets: []valid.PolicySet{},
|
||||
}
|
||||
baseRepo := models.Repo{
|
||||
FullName: "owner/repo",
|
||||
VCSHost: models.VCSHost{
|
||||
Hostname: "github.com",
|
||||
},
|
||||
}
|
||||
pull := models.PullRequest{
|
||||
BaseRepo: baseRepo,
|
||||
}
|
||||
cases := map[string]struct {
|
||||
globalCfg string
|
||||
repoCfg string
|
||||
expErr string
|
||||
expCtx models.ProjectCommandContext
|
||||
expPlanSteps []string
|
||||
expApplySteps []string
|
||||
}{
|
||||
|
||||
// Test that if we've set global defaults, that they are used but the
|
||||
// allowed project config values also come through.
|
||||
"global defaults with repo cfg": {
|
||||
globalCfg: `
|
||||
repos:
|
||||
- id: /.*/
|
||||
workflow: default
|
||||
workflows:
|
||||
default:
|
||||
plan:
|
||||
steps:
|
||||
- init
|
||||
- plan
|
||||
apply:
|
||||
steps:
|
||||
- apply`,
|
||||
repoCfg: `
|
||||
version: 3
|
||||
automerge: true
|
||||
projects:
|
||||
- name: myproject_1
|
||||
dir: project1
|
||||
workspace: myworkspace
|
||||
autoplan:
|
||||
enabled: true
|
||||
when_modified: [../modules/**/*.tf]
|
||||
terraform_version: v10.0
|
||||
- name: myproject_2
|
||||
dir: project2
|
||||
workspace: myworkspace
|
||||
autoplan:
|
||||
enabled: true
|
||||
when_modified: [../modules/**/*.tf]
|
||||
terraform_version: v10.0
|
||||
- name: myproject_3
|
||||
dir: project3
|
||||
workspace: myworkspace
|
||||
autoplan:
|
||||
enabled: true
|
||||
when_modified: [../modules/**/*.tf]
|
||||
terraform_version: v10.0
|
||||
`,
|
||||
expCtx: models.ProjectCommandContext{
|
||||
ApplyCmd: "atlantis apply -p myproject_1",
|
||||
BaseRepo: baseRepo,
|
||||
EscapedCommentArgs: []string{`\f\l\a\g`},
|
||||
AutomergeEnabled: true,
|
||||
AutoplanEnabled: true,
|
||||
HeadRepo: models.Repo{},
|
||||
Log: nil,
|
||||
PullMergeable: true,
|
||||
Pull: pull,
|
||||
ProjectName: "myproject_1",
|
||||
ApplyRequirements: []string{},
|
||||
RepoConfigVersion: 3,
|
||||
RePlanCmd: "atlantis plan -p myproject_1 -- flag",
|
||||
RepoRelDir: "project1",
|
||||
TerraformVersion: mustVersion("10.0"),
|
||||
User: models.User{},
|
||||
Verbose: true,
|
||||
Workspace: "myworkspace",
|
||||
PolicySets: emptyPolicySets,
|
||||
},
|
||||
expPlanSteps: []string{"init", "plan"},
|
||||
expApplySteps: []string{"apply"},
|
||||
},
|
||||
}
|
||||
|
||||
for name, c := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
tmp, cleanup := DirStructure(t, map[string]interface{}{
|
||||
"project1": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
"modules": map[string]interface{}{
|
||||
"module": map[string]interface{}{
|
||||
"main.tf": nil,
|
||||
},
|
||||
},
|
||||
})
|
||||
defer cleanup()
|
||||
|
||||
workingDir := NewMockWorkingDir()
|
||||
When(workingDir.Clone(matchers.AnyPtrToLoggingSimpleLogger(), matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest(), AnyString())).ThenReturn(tmp, false, nil)
|
||||
vcsClient := vcsmocks.NewMockClient()
|
||||
When(vcsClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"modules/module/main.tf"}, nil)
|
||||
|
||||
// Write and parse the global config file.
|
||||
globalCfgPath := filepath.Join(tmp, "global.yaml")
|
||||
Ok(t, ioutil.WriteFile(globalCfgPath, []byte(c.globalCfg), 0600))
|
||||
parser := &yaml.ParserValidator{}
|
||||
globalCfg, err := parser.ParseGlobalCfg(globalCfgPath, valid.NewGlobalCfg(false, false, false))
|
||||
Ok(t, err)
|
||||
|
||||
if c.repoCfg != "" {
|
||||
Ok(t, ioutil.WriteFile(filepath.Join(tmp, "atlantis.yaml"), []byte(c.repoCfg), 0600))
|
||||
}
|
||||
|
||||
builder := NewProjectCommandBuilder(
|
||||
false,
|
||||
parser,
|
||||
&DefaultProjectFinder{},
|
||||
vcsClient,
|
||||
workingDir,
|
||||
NewDefaultWorkingDirLocker(),
|
||||
globalCfg,
|
||||
&DefaultPendingPlanFinder{},
|
||||
&CommentParser{},
|
||||
false,
|
||||
true,
|
||||
)
|
||||
|
||||
// We run a test for each type of command, again specific projects
|
||||
for _, cmd := range []models.CommandName{models.PlanCommand, models.ApplyCommand} {
|
||||
t.Run(cmd.String(), func(t *testing.T) {
|
||||
ctxs, err := builder.buildProjectCommandCtx(&CommandContext{
|
||||
Pull: models.PullRequest{
|
||||
BaseRepo: baseRepo,
|
||||
},
|
||||
PullMergeable: true,
|
||||
}, cmd, "myproject_[1-2]", []string{"flag"}, tmp, "project1", "myworkspace", true)
|
||||
|
||||
if c.expErr != "" {
|
||||
ErrEquals(t, c.expErr, err)
|
||||
return
|
||||
}
|
||||
ctx := ctxs[0]
|
||||
|
||||
Ok(t, err)
|
||||
|
||||
Equals(t, 2, len(ctxs))
|
||||
// Construct expected steps.
|
||||
var stepNames []string
|
||||
switch cmd {
|
||||
case models.PlanCommand:
|
||||
stepNames = c.expPlanSteps
|
||||
case models.ApplyCommand:
|
||||
stepNames = c.expApplySteps
|
||||
}
|
||||
var expSteps []valid.Step
|
||||
for _, stepName := range stepNames {
|
||||
expSteps = append(expSteps, valid.Step{
|
||||
StepName: stepName,
|
||||
})
|
||||
}
|
||||
|
||||
c.expCtx.CommandName = cmd
|
||||
// Init fields we couldn't in our cases map.
|
||||
c.expCtx.Steps = expSteps
|
||||
ctx.PolicySets = emptyPolicySets
|
||||
Equals(t, c.expCtx, ctx)
|
||||
// Equals() doesn't compare TF version properly so have to
|
||||
// use .String().
|
||||
if c.expCtx.TerraformVersion != nil {
|
||||
Equals(t, c.expCtx.TerraformVersion.String(), ctx.TerraformVersion.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildProjectCmdCtx_WithPolicCheckEnabled(t *testing.T) {
|
||||
emptyPolicySets := valid.PolicySets{
|
||||
Version: nil,
|
||||
@@ -790,6 +976,7 @@ workflows:
|
||||
&DefaultPendingPlanFinder{},
|
||||
&CommentParser{},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
cmd := models.PolicyCheckCommand
|
||||
|
||||
@@ -145,6 +145,7 @@ projects:
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
ctxs, err := builder.BuildAutoplanCommands(&events.CommandContext{
|
||||
@@ -370,6 +371,7 @@ projects:
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{},
|
||||
false,
|
||||
true,
|
||||
)
|
||||
|
||||
var actCtxs []models.ProjectCommandContext
|
||||
@@ -506,6 +508,7 @@ projects:
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
ctxs, err := builder.BuildPlanCommands(
|
||||
@@ -580,6 +583,7 @@ func TestDefaultProjectCommandBuilder_BuildMultiApply(t *testing.T) {
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
ctxs, err := builder.BuildApplyCommands(
|
||||
@@ -649,6 +653,7 @@ projects:
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
ctx := &events.CommandContext{
|
||||
@@ -713,6 +718,7 @@ func TestDefaultProjectCommandBuilder_EscapeArgs(t *testing.T) {
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
var actCtxs []models.ProjectCommandContext
|
||||
@@ -879,6 +885,7 @@ projects:
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
actCtxs, err := builder.BuildPlanCommands(
|
||||
@@ -929,6 +936,7 @@ projects:
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{},
|
||||
true,
|
||||
false,
|
||||
)
|
||||
|
||||
var actCtxs []models.ProjectCommandContext
|
||||
@@ -969,6 +977,7 @@ func TestDefaultProjectCommandBuilder_WithPolicyCheckEnabled_BuildAutoplanComman
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
&events.CommentParser{},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
ctxs, err := builder.BuildAutoplanCommands(&events.CommandContext{
|
||||
|
||||
@@ -4,6 +4,7 @@ package valid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
version "github.com/hashicorp/go-version"
|
||||
@@ -52,6 +53,20 @@ func (r RepoCfg) FindProjectByName(name string) *Project {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindProjectsByName returns all projects that match with name.
|
||||
func (r RepoCfg) FindProjectsByName(name string) []Project {
|
||||
var ps []Project
|
||||
sanitizedName := "^" + name + "$"
|
||||
for _, p := range r.Projects {
|
||||
if p.Name != nil {
|
||||
if match, _ := regexp.MatchString(sanitizedName, *p.Name); match {
|
||||
ps = append(ps, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// validateWorkspaceAllowed returns an error if repoCfg defines projects in
|
||||
// repoRelDir but none of them use workspace. We want this to be an error
|
||||
// because if users have gone to the trouble of defining projects in repoRelDir
|
||||
|
||||
@@ -640,6 +640,7 @@ func setupE2E(t *testing.T, repoDir string, policyChecksEnabled bool) (server.Ev
|
||||
&events.DefaultPendingPlanFinder{},
|
||||
commentParser,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
showStepRunner, err := runtime.NewShowStepRunner(terraformClient, defaultTFVersion)
|
||||
|
||||
@@ -412,6 +412,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
pendingPlanFinder,
|
||||
commentParser,
|
||||
userConfig.SkipCloneNoChanges,
|
||||
userConfig.EnableRegExpCmd,
|
||||
)
|
||||
|
||||
showStepRunner, err := runtime.NewShowStepRunner(terraformClient, defaultTfVersion)
|
||||
|
||||
@@ -28,6 +28,7 @@ type UserConfig struct {
|
||||
DisableMarkdownFolding bool `mapstructure:"disable-markdown-folding"`
|
||||
DisableRepoLocking bool `mapstructure:"disable-repo-locking"`
|
||||
EnablePolicyChecksFlag bool `mapstructure:"enable-policy-checks"`
|
||||
EnableRegExpCmd bool `mapstructure:"enable-regexp-cmd"`
|
||||
GithubHostname string `mapstructure:"gh-hostname"`
|
||||
GithubToken string `mapstructure:"gh-token"`
|
||||
GithubUser string `mapstructure:"gh-user"`
|
||||
|
||||
Reference in New Issue
Block a user