-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #79 from opentofu/issue_workflow
Provider/Module submission workflow
- Loading branch information
Showing
7 changed files
with
431 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
name: Submit new Module | ||
description: Submit a new OpenTofu Module | ||
title: "Module: " | ||
labels: ["module", "submission"] | ||
body: | ||
- type: input | ||
id: module_repository | ||
attributes: | ||
label: Module Repository | ||
description: Path to a public GitHub repository following the pattern {owner}/terraform-{target}-{name}, ex. GoogleCloudPlatform/terraform-google-secured-data-warehouse | ||
validations: | ||
required: true | ||
- type: checkboxes | ||
id: dco | ||
attributes: | ||
label: DCO | ||
options: | ||
- label: I sign this project's [DCO](https://developercertificate.org/) | ||
required: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
name: Submit new Provider | ||
description: Submit a new OpenTofu Provider | ||
title: "Provider: " | ||
labels: ["provider", "submission"] | ||
body: | ||
- type: input | ||
id: repository | ||
attributes: | ||
label: Provider Repository | ||
description: Path to a public GitHub repository following the pattern {owner}/terraform-provider-{name}, ex. opentofu/terraform-provider-aws | ||
validations: | ||
required: true | ||
- type: checkboxes | ||
id: dco | ||
attributes: | ||
label: DCO | ||
options: | ||
- label: I sign this project's [DCO](https://developercertificate.org/) | ||
required: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
name: Issue Submission to Pull Request | ||
on: | ||
issues: | ||
types: | ||
[opened, edited] | ||
|
||
jobs: | ||
submit-provider: | ||
if: contains(github.event.issue.labels.*.name, 'provider') && contains(github.event.issue.labels.*.name, 'submission') | ||
runs-on: ubuntu-latest | ||
permissions: | ||
issues: write | ||
contents: write | ||
pull-requests: write | ||
steps: | ||
- name: Checkout Repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version-file: './src/go.mod' | ||
|
||
- name: Validate Provider and Create PR | ||
env: | ||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
GH_REPO: ${{ github.repository }} | ||
NUMBER: ${{ github.event.issue.number }} | ||
URL: ${{ github.event.issue.url }} | ||
TITLE: ${{ github.event.issue.title }} | ||
BODY: ${{ github.event.issue.body }} | ||
working-directory: ./src | ||
run: | | ||
set +e | ||
echo "$BODY" | grep "\- \[[xX]\] I sign this project's \[DCO\](https://developercertificate.org/)" | ||
if [[ "$?" != 0 ]]; then | ||
gh issue comment $NUMBER -b "DCO must be signed to submit this repository" | ||
exit 1 | ||
fi | ||
set -e | ||
repository=$(echo "$BODY" | grep "### Provider Repository" -A2 | tail -n1 | sed -e 's/[\r\n]//g') | ||
set +e | ||
go run ./cmd/add-provider -repository="$repository" -output=./output.json | ||
if [[ "$?" != 0 ]]; then | ||
gh issue comment $NUMBER -b "$(cat ./output.json | jq -r '.validation')" | ||
exit 1 | ||
fi | ||
set -e | ||
namespace=$(cat ./output.json | jq -r '.namespace') | ||
name=$(cat ./output.json | jq -r '.name') | ||
jsonfile=$(cat ./output.json | jq -r '.file') | ||
# Create Branch | ||
branch=provider-submission_${namespace}_${name} | ||
set +e | ||
git checkout -b $branch | ||
if [[ "$?" != 0 ]]; then | ||
gh issue comment $NUMBER -b "Failed validation: A branch already exists for this provider '$branch'" | ||
exit 1 | ||
fi | ||
set -e | ||
# Add result | ||
git add $jsonfile | ||
# Commit and push result | ||
git config --global user.email "no-reply@opentofu.org" | ||
git config --global user.name "OpenTofu Automation" | ||
git commit -s -m "Create provider $namespace/$name" | ||
git push -u origin $branch | ||
# Create pull request and update issue | ||
pr=$(gh pr create --title "$TITLE" --body "Created $(echo $jsonfile | sed -e 's/../src/') for provider $namespace/$name. See issue #$NUMBER for details.") #--assignee opentofu/core-engineers) | ||
gh issue comment $NUMBER -b "Your submission has been validated and has moved on to the pull request phase ($pr). This issue has been locked." | ||
gh issue lock $NUMBER -r resolved | ||
submit-module: | ||
if: contains(github.event.issue.labels.*.name, 'module') && contains(github.event.issue.labels.*.name, 'submission') | ||
runs-on: ubuntu-latest | ||
permissions: | ||
issues: write | ||
contents: write | ||
pull-requests: write | ||
steps: | ||
- name: Checkout Repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version-file: './src/go.mod' | ||
|
||
- name: Validate Module and Create PR | ||
env: | ||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
GH_REPO: ${{ github.repository }} | ||
NUMBER: ${{ github.event.issue.number }} | ||
URL: ${{ github.event.issue.url }} | ||
TITLE: ${{ github.event.issue.title }} | ||
BODY: ${{ github.event.issue.body }} | ||
working-directory: ./src | ||
run: | | ||
set +e | ||
echo "$BODY" | grep "\- \[[xX]\] I sign this project's \[DCO\](https://developercertificate.org/)" | ||
if [[ "$?" != 0 ]]; then | ||
gh issue comment $NUMBER -b "DCO must be signed to submit this repository" | ||
exit 1 | ||
fi | ||
set -e | ||
repository=$(echo "$BODY" | grep "### Module Repository" -A2 | tail -n1 | sed -e 's/[\r\n]//g') | ||
set +e | ||
go run ./cmd/add-module -repository="$repository" -output=./output.json | ||
if [[ "$?" != 0 ]]; then | ||
gh issue comment $NUMBER -b "$(cat ./output.json | jq -r '.validation')" | ||
exit 1 | ||
fi | ||
set -e | ||
namespace=$(cat ./output.json | jq -r '.namespace') | ||
name=$(cat ./output.json | jq -r '.name') | ||
target=$(cat ./output.json | jq -r '.target') | ||
jsonfile=$(cat ./output.json | jq -r '.file') | ||
# Create Branch | ||
branch=module-submission_${namespace}_${name}_${target} | ||
set +e | ||
git checkout -b $branch | ||
if [[ "$?" != 0 ]]; then | ||
gh issue comment $NUMBER -b "Failed validation: A branch already exists for this module '$branch'" | ||
exit 1 | ||
fi | ||
set -e | ||
# Add result | ||
git add $jsonfile | ||
# Commit and push result | ||
git config --global user.email "no-reply@opentofu.org" | ||
git config --global user.name "OpenTofu Automation" | ||
git commit -s -m "Create module $namespace/$name/$target" | ||
git push -u origin $branch | ||
# Create pull request and update issue | ||
pr=$(gh pr create --title "$TITLE" --body "Created $(echo $jsonfile | sed -e 's/../src/') for module $namespace/$name/$target. See issue #$NUMBER for details.") #--assignee opentofu/core-engineers) | ||
gh issue comment $NUMBER -b "Your submission has been validated and has moved on to the pull request phase ($pr). This issue has been locked." | ||
gh issue lock $NUMBER -r resolved |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"flag" | ||
"fmt" | ||
"log/slog" | ||
"os" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/opentofu/registry-stable/internal/files" | ||
"github.com/opentofu/registry-stable/internal/github" | ||
"github.com/opentofu/registry-stable/internal/module" | ||
|
||
regaddr "github.com/opentofu/registry-address" | ||
) | ||
|
||
type Output struct { | ||
File string `json:"file"` | ||
Namespace string `json:"namespace"` | ||
Name string `json:"name"` | ||
Target string `json:"target"` | ||
Validation string `json:"validation"` | ||
} | ||
|
||
func main() { | ||
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) | ||
|
||
repository := flag.String("repository", "", "The module repository to add") | ||
outputFile := flag.String("output", "", "Path to write JSON result to") | ||
moduleDataDir := flag.String("module-data", "../modules", "Directory containing the module data") | ||
|
||
flag.Parse() | ||
|
||
ctx := context.Background() | ||
token, err := github.EnvAuthToken() | ||
if err != nil { | ||
logger.Error("Initialization Error", slog.Any("err", err)) | ||
os.Exit(1) | ||
} | ||
ghClient := github.NewClient(ctx, logger, token) | ||
|
||
output := Output{} | ||
|
||
err = func() error { | ||
// Lower case input | ||
re := regexp.MustCompile("(?P<Namespace>[a-zA-Z0-9]+)/terraform-(?P<Target>[a-zA-Z0-9]*)-(?P<Name>[a-zA-Z0-9-]*)") | ||
match := re.FindStringSubmatch(*repository) | ||
if match == nil { | ||
return fmt.Errorf("Invalid repository name: %s", *repository) | ||
} | ||
|
||
submitted := module.Module{ | ||
Namespace: match[re.SubexpIndex("Namespace")], | ||
Name: match[re.SubexpIndex("Name")], | ||
TargetSystem: match[re.SubexpIndex("Target")], | ||
Directory: *moduleDataDir, | ||
Logger: logger, | ||
Github: ghClient, | ||
} | ||
|
||
_, err = regaddr.ParseModuleSource(fmt.Sprintf("%s/%s/%s", submitted.Namespace, submitted.Name, submitted.TargetSystem)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
modules, err := module.ListModules(*moduleDataDir, "", logger, ghClient) | ||
if err != nil { | ||
return err | ||
} | ||
for _, p := range modules { | ||
if strings.ToLower(p.RepositoryURL()) == strings.ToLower(submitted.RepositoryURL()) { | ||
return fmt.Errorf("Repository already exists in the registry, %s", p.RepositoryURL()) | ||
} | ||
} | ||
|
||
err = submitted.WriteMetadata(module.Metadata{}) | ||
if err != nil { | ||
return fmt.Errorf("An unexpected error occured: %w", err) | ||
} | ||
|
||
err = submitted.UpdateMetadataFile() | ||
if err != nil { | ||
return fmt.Errorf("An unexpected error occured: %w", err) | ||
} | ||
|
||
meta, err := submitted.ReadMetadata() | ||
if err != nil { | ||
return fmt.Errorf("An unexpected error occured: %w", err) | ||
} | ||
if len(meta.Versions) == 0 { | ||
return fmt.Errorf("No versions detected for repository %s", submitted.RepositoryURL()) | ||
} | ||
|
||
output.Namespace = submitted.Namespace | ||
output.Name = submitted.Name | ||
output.Target = submitted.TargetSystem | ||
output.File = submitted.MetadataPath() | ||
return nil | ||
}() | ||
|
||
if err != nil { | ||
logger.Error("Unable to add module", slog.Any("err", err)) | ||
output.Validation = err.Error() | ||
// Don't exit yet, still need to write the json. | ||
} | ||
|
||
jsonErr := files.SafeWriteObjectToJSONFile(*outputFile, output) | ||
if jsonErr != nil { | ||
// This really should not happen | ||
panic(jsonErr) | ||
} | ||
|
||
if err != nil { | ||
os.Exit(1) | ||
} | ||
} |
Oops, something went wrong.