diff --git a/README.md b/README.md index 4f94cfb..57d226d 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ If you find a problem with a particular version of Confluence, please ### Plugin setup -There are four mandatory variables and two optional variables to configure, as either: +There are three mandatory variables and three optional variables to configure, as either: 1. environment variables @@ -92,34 +92,27 @@ There are four mandatory variables and two optional variables to configure, as e [properties file](https://docs.gauge.org/configuration.html#local-configuration-of-gauge-default-properties), e.g. `/env/default/anythingyoulike.properties` -The four mandatory variables to configure are: +The three mandatory variables to configure are: `CONFLUENCE_BASE_URL` e.g. `https://example.com/path-to-your-confluence-wiki` for Confluence Server, or `https://example.atlassian.net` for Confluence Cloud `CONFLUENCE_USERNAME` -`CONFLUENCE_TOKEN` +`CONFLUENCE_TOKEN` (This can either be a token or the password for the given Confluence username) -`CONFLUENCE_SPACE_KEY` - -Use a dedicated, empty [Confluence Space](https://support.atlassian.com/confluence-cloud/docs/use-spaces-to-organize-your-work/) -that will contain just the Gauge specifications and nothing else. - -NB You can use [Confluence's include macro](https://confluence.atlassian.com/doc/include-page-macro-139514.html) -to include the [page tree](https://confluence.atlassian.com/conf59/page-tree-macro-792499177.html) of Gauge Specs -(that gets created by this plugin) in as many of your existing spaces as you like. - -The two optional variables to configure are: +The three optional variables to configure are: `GAUGE_LOG_LEVEL` `DRY_RUN` +`CONFLUENCE_SPACE_KEY` + +___ The `GAUGE_LOG_LEVEL` variable can be set to `debug` or `info` (default is `info`). It controls the logging level both for the log files which are generated, _and_ what is logged to the console. NB the command line flag `--log-level` does not have any effect on the logging for this plugin. - - +___ **Setting the `DRY_RUN` variable to `true` means that running the plugin does not publish specs to Confluence.** Instead the plugin just checks that the specs are in a valid publishable state (e.g. that there are no duplicate @@ -127,6 +120,17 @@ spec headings). This is very useful e.g. **in a CI/CD pipeline the plugin can run in dry run mode on feature branches and pull requests.** This ensures that the Gauge specs are always in good shape to be automatically published by the CI/CD pipeline upon any push to the trunk branch (e.g. upon a successful pull request merge). +___ +If the `CONFLUENCE_SPACE_KEY` is not provided, the plugin will derive the Space key to be used based on the remote Git repository URL. This convention ensures that each Git repository has its own unique Confluence space key derived from it, i.e. a one to one mapping between each Git repository and its associated one to one space. + +The recommended way to run the plugin is not to provide the `CONFLUENCE_SPACE_KEY` variable, and instead to rely on the plugin to set it. This is particularly useful in CI/CD for instance, as it removes the need to set the Space key manually before being able to run the plugin. + +One use case for setting the `CONFLUENCE_SPACE_KEY` is if for whatever reason you are unable to specify a Confluence user who has permission to create Confluence Spaces. By setting the `CONFLUENCE_SPACE_KEY` to be an existing Space which someone (e.g. a Confluence admin) has created for you, you will still be able to run the plugin even without Confluence create space permissions. In this case use a dedicated, empty [Confluence Space](https://support.atlassian.com/confluence-cloud/docs/use-spaces-to-organize-your-work/) that will contain just the Gauge specifications and nothing else. + +NB You can use [Confluence's include macro](https://confluence.atlassian.com/doc/include-page-macro-139514.html) +to include the [page tree](https://confluence.atlassian.com/conf59/page-tree-macro-792499177.html) of Gauge Specs +(that gets created by this plugin) in as many of your existing spaces as you like. +___ ### Running the plugin (i.e. publishing specs to Confluence) diff --git a/functional-tests/specs/check_config_vars.spec b/functional-tests/specs/check_config_vars.spec index 168c312..d195781 100644 --- a/functional-tests/specs/check_config_vars.spec +++ b/functional-tests/specs/check_config_vars.spec @@ -6,7 +6,6 @@ Tags: create-space-manually |CONFLUENCE_BASE_URL | |CONFLUENCE_USERNAME | |CONFLUENCE_TOKEN | - |CONFLUENCE_SPACE_KEY| ## The plugin fails if required configuration variables are not set diff --git a/functional-tests/specs/dry_run.spec b/functional-tests/specs/dry_run.spec index d4bb677..4744abb 100644 --- a/functional-tests/specs/dry_run.spec +++ b/functional-tests/specs/dry_run.spec @@ -41,16 +41,12 @@ Tags: create-space-manually The absence of the "create-space-manually" tag means the Confluence Space does not exist for this scenario -* Space does not exist - * Activate dry run mode * Publish "1" specs to Confluence * Output contains "Dry run finished successfully" -* Space does not exist - __________________________________________________________________________________________ diff --git a/functional-tests/specs/space_creation.spec b/functional-tests/specs/space_creation.spec index 75e6793..7f59706 100644 --- a/functional-tests/specs/space_creation.spec +++ b/functional-tests/specs/space_creation.spec @@ -2,14 +2,16 @@ ## If the Space does not already exist, the plugin will create it -* Space does not exist - * Publish "1" specs to Confluence * Specs "did" get published +* Space has key "GITHUBCOMEXAMPLEUSEREXAMPLEREPO" + * Space has name "Gauge specs for example-user/example-repo" +* Output contains "Success: published 2 specs and directory pages to Confluence Space named: Gauge specs for example-user/example-repo" + The `example-user/example-repo` comes from the [dummy Git remote URL config in the test framework code][1]. When users run the plugin the Space name will be taken from the Git remote URL of the Git repository that the plugin is executed on. diff --git a/functional-tests/src/test/java/com/thoughtworks/gauge/test/common/GaugeProject.java b/functional-tests/src/test/java/com/thoughtworks/gauge/test/common/GaugeProject.java index a6af2f3..3636215 100644 --- a/functional-tests/src/test/java/com/thoughtworks/gauge/test/common/GaugeProject.java +++ b/functional-tests/src/test/java/com/thoughtworks/gauge/test/common/GaugeProject.java @@ -5,7 +5,6 @@ import com.thoughtworks.gauge.datastore.ScenarioDataStore; import com.thoughtworks.gauge.test.StepImpl; import com.thoughtworks.gauge.test.confluence.Confluence; -import com.thoughtworks.gauge.test.git.Config.GitConfig; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.ArrayUtils; @@ -333,9 +332,9 @@ public void deleteSpec(String specName) { getSpecFile(specName).delete(); } - public void addGitConfig(GitConfig gitConfig) throws Exception { + public void addGitConfig(String remoteOriginURL) throws Exception { executeGitCommand("init"); - executeGitCommand("remote", "add", "origin", gitConfig.remoteOriginURL()); + executeGitCommand("remote", "add", "origin", remoteOriginURL); } public void simulateGitDetachedHead() throws IOException { diff --git a/functional-tests/src/test/java/com/thoughtworks/gauge/test/common/builders/ProjectBuilder.java b/functional-tests/src/test/java/com/thoughtworks/gauge/test/common/builders/ProjectBuilder.java index 4cf91be..b75cda6 100644 --- a/functional-tests/src/test/java/com/thoughtworks/gauge/test/common/builders/ProjectBuilder.java +++ b/functional-tests/src/test/java/com/thoughtworks/gauge/test/common/builders/ProjectBuilder.java @@ -4,6 +4,7 @@ import com.thoughtworks.gauge.test.common.GaugeProject; import com.thoughtworks.gauge.test.common.Util; import com.thoughtworks.gauge.test.git.Config.GitConfig; +import static com.thoughtworks.gauge.test.confluence.Confluence.getGitRemoteURLFromScenarioDataStore; public class ProjectBuilder { @@ -11,7 +12,7 @@ public class ProjectBuilder { private String projName; private boolean deleteExampleSpec; private boolean remoteTemplate; - private boolean gitConfig; + private boolean addGitConfig; public ProjectBuilder() { this.remoteTemplate = false; @@ -28,12 +29,12 @@ public ProjectBuilder withProjectName(String projName) { } public ProjectBuilder withGitConfig() { - this.gitConfig = true; + this.addGitConfig = true; return this; } public ProjectBuilder withoutGitConfig() { - this.gitConfig = false; + this.addGitConfig = false; return this; } @@ -49,8 +50,13 @@ public GaugeProject build(boolean expectFailure) throws Exception { + currentProject.getLastProcessStderr() + "\n\nSTDOUT:\n\n" + currentProject.getLastProcessStdout()); - if (this.gitConfig) - currentProject.addGitConfig(GitConfig.HTTPS); + if (this.addGitConfig) { + if (getGitRemoteURLFromScenarioDataStore() != null) { + currentProject.addGitConfig(getGitRemoteURLFromScenarioDataStore()); + } else { + currentProject.addGitConfig(GitConfig.HTTPS.remoteOriginURL()); + } + } if (this.deleteExampleSpec) currentProject.deleteSpec(Util.combinePath("specs", "example")); diff --git a/functional-tests/src/test/java/com/thoughtworks/gauge/test/confluence/Confluence.java b/functional-tests/src/test/java/com/thoughtworks/gauge/test/confluence/Confluence.java index 592b8cd..e9a3596 100644 --- a/functional-tests/src/test/java/com/thoughtworks/gauge/test/confluence/Confluence.java +++ b/functional-tests/src/test/java/com/thoughtworks/gauge/test/confluence/Confluence.java @@ -1,20 +1,22 @@ package com.thoughtworks.gauge.test.confluence; -import com.thoughtworks.gauge.BeforeScenario; -import com.thoughtworks.gauge.Step; -import com.thoughtworks.gauge.Table; -import com.thoughtworks.gauge.TableRow; -import com.thoughtworks.gauge.AfterScenario; -import com.thoughtworks.gauge.datastore.ScenarioDataStore; -import com.thoughtworks.gauge.test.implementation.Console; - +import static com.thoughtworks.gauge.test.confluence.ConfluenceClient.deleteSpace; +import static com.thoughtworks.gauge.test.confluence.ConfluenceClient.doesSpaceExist; import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; import java.time.LocalTime; -import java.util.concurrent.TimeUnit; import java.util.Objects; import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import com.thoughtworks.gauge.AfterScenario; +import com.thoughtworks.gauge.BeforeScenario; +import com.thoughtworks.gauge.Step; +import com.thoughtworks.gauge.Table; +import com.thoughtworks.gauge.TableRow; +import com.thoughtworks.gauge.datastore.ScenarioDataStore; +import com.thoughtworks.gauge.test.implementation.Console; public class Confluence { @@ -24,6 +26,7 @@ public class Confluence { private static final String DRY_RUN_MODE = "dry-run-mode"; private static final String CONFLUENCE_USERNAME = "confluence-username"; private static final String CONFLUENCE_TOKEN = "confluence-token"; + private static final String GIT_REMOTE_URL_KEY_NAME = "git-remote-url"; public static String getScenarioSpaceKey() { return Objects.toString(ScenarioDataStore.get(SCENARIO_SPACE_KEY_NAME), ""); @@ -45,31 +48,33 @@ public static String getConfluenceTokenFromScenarioDataStore() { return (String) ScenarioDataStore.get(CONFLUENCE_TOKEN); } - @BeforeScenario - public void setDryRunModeOff() { - ScenarioDataStore.put(DRY_RUN_MODE, false); + public static String getGitRemoteURLFromScenarioDataStore() { + return (String) ScenarioDataStore.get(GIT_REMOTE_URL_KEY_NAME); } @BeforeScenario - public void setSpaceKeyName() { - ScenarioDataStore.put(SCENARIO_SPACE_KEY_NAME, generateUniqueSpaceKeyName()); + public void setDryRunModeOff() { + ScenarioDataStore.put(DRY_RUN_MODE, false); } @BeforeScenario(tags = {"create-space-manually"}) public void beforeScenario() { + ScenarioDataStore.put(SCENARIO_SPACE_KEY_NAME, generateUniqueSpaceKeyName()); String spaceHomepageID = ConfluenceClient.createSpace(getScenarioSpaceKey(), SCENARIO_SPACE_NAME); ScenarioDataStore.put(SCENARIO_SPACE_HOMEPAGE_ID_KEY_NAME, spaceHomepageID); } - @AfterScenario - public void setConfluenceUsernameAndTokenFromEnvVar() { + @AfterScenario() + public void afterScenario() { + if ((!getScenarioSpaceKey().isEmpty()) && doesSpaceExist(getScenarioSpaceKey())) { + deleteSpace(getScenarioSpaceKey()); + } ScenarioDataStore.remove(CONFLUENCE_USERNAME); ScenarioDataStore.remove(CONFLUENCE_TOKEN); } - @AfterScenario(tags = {"create-space-manually"}) - public void afterScenario() { - ConfluenceClient.deleteSpace(getScenarioSpaceKey()); + public void setScenarioSpaceKeyName(String keyName) { + ScenarioDataStore.put(SCENARIO_SPACE_KEY_NAME, keyName); } @Step("Activate dry run mode") @@ -94,6 +99,13 @@ public void assertSpaceHasName(String name) { assertThat(space.getName()).isEqualTo(name); } + @Step("Space has key ") + public void assertSpaceHasKey(String key) { + setScenarioSpaceKeyName(key); + Space space = new Space(getScenarioSpaceKey()); + assertThat(space.getKey()).isEqualTo(key); + } + @Step("Space has description ") public void assertSpaceHasDescription(String description) { Space space = new Space(getScenarioSpaceKey()); diff --git a/functional-tests/src/test/java/com/thoughtworks/gauge/test/confluence/Space.java b/functional-tests/src/test/java/com/thoughtworks/gauge/test/confluence/Space.java index c5d817c..09e979b 100644 --- a/functional-tests/src/test/java/com/thoughtworks/gauge/test/confluence/Space.java +++ b/functional-tests/src/test/java/com/thoughtworks/gauge/test/confluence/Space.java @@ -10,6 +10,10 @@ public Space(String key) { this.jsonSpace = ConfluenceClient.getSpace(key); } + public String getKey() { + return jsonSpace.getString("key"); + } + public String getName() { return jsonSpace.getString("name"); } diff --git a/functional-tests/src/test/java/com/thoughtworks/gauge/test/git/Config.java b/functional-tests/src/test/java/com/thoughtworks/gauge/test/git/Config.java index f10c90f..8f645b6 100644 --- a/functional-tests/src/test/java/com/thoughtworks/gauge/test/git/Config.java +++ b/functional-tests/src/test/java/com/thoughtworks/gauge/test/git/Config.java @@ -27,7 +27,7 @@ public String remoteOriginURL() { @Step("Add Git config to project") public void addGitConfigToProject(String gitConfig) throws Exception { - getCurrentProject().addGitConfig(GitConfig.valueOf(gitConfig)); + getCurrentProject().addGitConfig(GitConfig.valueOf(gitConfig).remoteOriginURL()); } @Step("Simulate Git detached HEAD") diff --git a/internal/confluence/publisher.go b/internal/confluence/publisher.go index 291a719..d47ba84 100644 --- a/internal/confluence/publisher.go +++ b/internal/confluence/publisher.go @@ -25,10 +25,9 @@ type Publisher struct { // NewPublisher instantiates a new Publisher. func NewPublisher(m *gauge_messages.SpecDetails) Publisher { - spaceKey := env.GetRequired("CONFLUENCE_SPACE_KEY") apiClient := api.NewClient() - return Publisher{apiClient: apiClient, space: newSpace(spaceKey, apiClient), specs: makeSpecsMap(m), + return Publisher{apiClient: apiClient, space: newSpace(apiClient), specs: makeSpecsMap(m), dryRunPages: make(map[string]page)} } @@ -91,7 +90,13 @@ func (p *Publisher) Publish(specPaths []string) (err error) { return err } - logger.Infof(true, "Success: published %d specs and directory pages to Confluence", len(p.space.publishedPages)) + spaceName, err := p.space.name() + if err != nil { + return err + } + + logger.Infof(true, "Success: published %d specs and directory pages to Confluence Space named: %s", + len(p.space.publishedPages), spaceName) return nil } diff --git a/internal/confluence/space.go b/internal/confluence/space.go index 2921f58..669272f 100644 --- a/internal/confluence/space.go +++ b/internal/confluence/space.go @@ -2,7 +2,10 @@ package confluence import ( "fmt" + "net/url" + "os" "path/filepath" + "strings" "github.com/agilepathway/gauge-confluence/internal/confluence/api" "github.com/agilepathway/gauge-confluence/internal/confluence/api/http" @@ -10,6 +13,7 @@ import ( "github.com/agilepathway/gauge-confluence/internal/env" "github.com/agilepathway/gauge-confluence/internal/git" "github.com/agilepathway/gauge-confluence/internal/logger" + str "github.com/agilepathway/gauge-confluence/internal/strings" ) type space struct { @@ -23,12 +27,44 @@ type space struct { } // newSpace initialises a new space. -func newSpace(key string, apiClient api.Client) space { - return space{key: key, publishedPages: make(map[string]page), apiClient: apiClient} +func newSpace(apiClient api.Client) space { + return space{publishedPages: make(map[string]page), apiClient: apiClient} } -func (s *space) setup() error { - err := s.createIfDoesNotAlreadyExist() +func retrieveOrGenerateKey() (string, error) { + retrievedKey := os.Getenv("CONFLUENCE_SPACE_KEY") + if retrievedKey != "" { + return retrievedKey, nil + } + + return generateKey() +} + +func generateKey() (string, error) { + gitWebURL, err := git.WebURL() + if err != nil { + return "", err + } + + return keyFmt(gitWebURL), nil +} + +func keyFmt(u *url.URL) string { + hostAndPath := u.Host + u.Path + alphanumeric := str.StripNonAlphaNumeric(hostAndPath) + + return strings.ToUpper(alphanumeric) +} + +func (s *space) setup() error { // nolint:funlen + key, err := retrieveOrGenerateKey() + if err != nil { + return err + } + + s.key = key + + err = s.createIfDoesNotAlreadyExist() if err != nil { return err } diff --git a/internal/confluence/space_test.go b/internal/confluence/space_test.go new file mode 100644 index 0000000..0aa27a2 --- /dev/null +++ b/internal/confluence/space_test.go @@ -0,0 +1,36 @@ +package confluence + +import ( + "net/url" + "testing" +) + +var keyFmtTests = []struct { //nolint:gochecknoglobals + input string + expected string +}{ + {"https://github.com/example-user/example-repo", "GITHUBCOMEXAMPLEUSEREXAMPLEREPO"}, + {"http://github.com/example-user/example-repo", "GITHUBCOMEXAMPLEUSEREXAMPLEREPO"}, + {"http://github.com:8080/example-user/example-repo", "GITHUBCOM8080EXAMPLEUSEREXAMPLEREPO"}, + {"http://example.com/example-user/example-repo", "EXAMPLECOMEXAMPLEUSEREXAMPLEREPO"}, + {"https://example.com/example-user/example-repo", "EXAMPLECOMEXAMPLEUSEREXAMPLEREPO"}, + {"https://example.com/example-user/example-repo/nested", "EXAMPLECOMEXAMPLEUSEREXAMPLEREPONESTED"}, +} + +//nolint:errcheck,gosec +func TestKeyFmt(t *testing.T) { + for _, tt := range keyFmtTests { + expected := tt.expected + inputURL, _ := url.Parse(tt.input) + actual := keyFmt(inputURL) + + if expected != actual { + t.Fatalf(` + Expected + %s + + but got: + %s`, expected, actual) + } + } +} diff --git a/internal/git/git_url.go b/internal/git/git_url.go index d61248e..17c74fe 100644 --- a/internal/git/git_url.go +++ b/internal/git/git_url.go @@ -11,11 +11,11 @@ import ( var scpLikeURIRegExp = regexp.MustCompile(`^(?:(?P[^@]+)@)?(?P[^:\s]+):(?:(?P[0-9]{1,5})(?:\/|:))?(?P[^\\].*\/[^\\].*)$`) //nolint:lll // WebURL provides the web URL of the remote Git repository. -func WebURL() (string, error) { +func WebURL() (*url.URL, error) { remoteGitURL, err := discoverRemoteGitURL() if err != nil { - return "", err + return nil, err } return buildGitWebURL(remoteGitURL) @@ -51,36 +51,31 @@ func SpecGitURL(absoluteSpecPath, projectRoot string) string { return "" } - return gitWebURL + "/blob/" + branch + toURLFormat(relativeSpecPath) + return gitWebURL.String() + "/blob/" + branch + toURLFormat(relativeSpecPath) } -func parseURLPath(s string) (string, error) { - u, err := url.Parse(s) - if err != nil { - return "", err - } - +func parseURLPath(u *url.URL) (string, error) { return strings.TrimPrefix(u.Path, "/"), nil } // buildGitWebURL constructs the publicly accessible Git web URL from a Git remote URL. -func buildGitWebURL(remoteGitURI string) (string, error) { +func buildGitWebURL(remoteGitURI string) (*url.URL, error) { url, err := url.Parse(remoteGitURI) isStandardURL := err == nil && url != nil if isStandardURL { webURL := gitWebURLScheme(url.Scheme) + "://" + url.Host + strings.TrimSuffix(url.Path, ".git") - return webURL, nil + return url.Parse(webURL) } if isSCPStyleURI(remoteGitURI) { _, host, port, path := findScpLikeComponents(remoteGitURI) webURL := "https://" + hostAndPort(host, port) + "/" + strings.TrimSuffix(path, ".git") - return webURL, nil + return url.Parse(webURL) } - return "", fmt.Errorf("could not parse Git URL %s", remoteGitURI) + return nil, fmt.Errorf("could not parse Git URL %s", remoteGitURI) } func hostAndPort(host, port string) string { diff --git a/internal/git/git_url_test.go b/internal/git/git_url_test.go index 4690fbf..b451b7a 100644 --- a/internal/git/git_url_test.go +++ b/internal/git/git_url_test.go @@ -1,6 +1,7 @@ package git import ( + "net/url" "testing" ) @@ -50,7 +51,7 @@ var buildGitWebURLTests = []struct { //nolint:gochecknoglobals func TestBuildGitWebURL(t *testing.T) { for _, tt := range buildGitWebURLTests { actual, _ := buildGitWebURL(tt.input) - if tt.expected != actual { + if tt.expected != actual.String() { t.Errorf("buildGitWebURL(%s): expected %s, actual %s", tt.input, tt.expected, actual) } } @@ -69,7 +70,9 @@ var parseURLPathTests = []struct { //nolint:gochecknoglobals func TestParsePathFromURL(t *testing.T) { for _, tt := range parseURLPathTests { - actual, _ := parseURLPath(tt.input) + inputURL, _ := url.Parse(tt.input) + actual, _ := parseURLPath(inputURL) + if tt.expected != actual { t.Errorf("parsePathFromURL(%s): expected %s, actual %s", tt.input, tt.expected, actual) } diff --git a/internal/strings/alphanumeric.go b/internal/strings/alphanumeric.go new file mode 100644 index 0000000..84a0ad8 --- /dev/null +++ b/internal/strings/alphanumeric.go @@ -0,0 +1,10 @@ +// Package strings provides utility functions to manipulate strings. +package strings + +import "regexp" + +// StripNonAlphaNumeric returns a string with all non alphanumeric characters stripped. +func StripNonAlphaNumeric(input string) string { + reg := regexp.MustCompile("[^a-zA-Z0-9]+") + return reg.ReplaceAllString(input, "") +} diff --git a/internal/strings/alphanumeric_test.go b/internal/strings/alphanumeric_test.go new file mode 100644 index 0000000..56e62e2 --- /dev/null +++ b/internal/strings/alphanumeric_test.go @@ -0,0 +1,31 @@ +package strings + +import ( + "testing" +) + +var stripNonAlphaNumericTests = []struct { //nolint:gochecknoglobals + input string + expected string +}{ + {"github.com/example-user/example-repo", "githubcomexampleuserexamplerepo"}, + {"abc*12?3def", "abc123def"}, + {"ABC*12?3DEF1", "ABC123DEF1"}, +} + +//nolint:errcheck,gosec +func TestStripNonAlphaNumeric(t *testing.T) { + for _, tt := range stripNonAlphaNumericTests { + expected := tt.expected + actual := StripNonAlphaNumeric(tt.input) + + if expected != actual { + t.Fatalf(` + Expected + %s + + but got: + %s`, expected, actual) + } + } +} diff --git a/main.go b/main.go index 0c83c44..eb8e2a7 100644 --- a/main.go +++ b/main.go @@ -69,7 +69,6 @@ func checkRequiredConfigVars() { env.GetRequired("CONFLUENCE_BASE_URL") env.GetRequired("CONFLUENCE_USERNAME") env.GetRequired("CONFLUENCE_TOKEN") - env.GetRequired("CONFLUENCE_SPACE_KEY") } // providedSpecsPaths returns the specs paths passed in diff --git a/plugin.json b/plugin.json index d12143b..41202e3 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "id": "confluence", - "version": "0.15.1", + "version": "0.16.0", "name": "Confluence", "description": "Publishes Gauge specifications to Confluence", "install": {