1
0
mirror of https://github.com/arduino/library-registry.git synced 2025-07-07 14:41:10 +03:00

Move submission parser tool to dedicated repository

This commit is contained in:
per1234
2021-03-24 09:11:14 -07:00
parent cb6c0a2bb5
commit b0eccfe5b6
8 changed files with 10 additions and 751 deletions

View File

@ -1,45 +0,0 @@
name: Check Go code
on:
push:
paths:
- ".github/workflows/check-go.yml"
- "Taskfile.yml"
- "manager/**"
pull_request:
paths:
- ".github/workflows/check-go.yml"
- "Taskfile.yml"
- "manager/**"
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#workflow_dispatch
workflow_dispatch:
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#repository_dispatch
repository_dispatch:
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout local repository
uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: "1.14"
- name: Install Taskfile
uses: arduino/actions/setup-taskfile@master
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Lint
run: task go:lint
- name: Run tests
run: task go:test
- name: Check formatting
run: task go:check-formatting

View File

@ -1,5 +1,8 @@
name: Manage PRs
env:
SUBMISSION_PARSER_VERSION: 1.0.0-rc2 # See: https://github.com/arduino/library-manager-submission-parser/releases
on:
# pull_request_target trigger is used instead of pull_request so the token will have the write permissions needed to comment and merge.
# Note that this means the version of the workflow from the PR base ref will be used as opposed to the head ref, as is the case with pull_request triggered workflows.
@ -25,7 +28,6 @@ jobs:
contains(github.event.comment.body, 'ArduinoBot')
)
runs-on: ubuntu-latest
steps:
- name: Dummy step to make job valid
run: ""
@ -77,27 +79,15 @@ jobs:
index-entry: ${{ steps.parse-request.outputs.index-entry }}
steps:
- name: Set environment variables
run: |
# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
echo "MANAGER_PATH=${{ runner.temp }}/manager" >> "$GITHUB_ENV"
- name: Checkout local repository
uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
- name: Install Taskfile
uses: arduino/actions/setup-taskfile@master
- name: Download submission parser
id: download-parser
uses: carlosperate/download-file-action@v1.0.3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x
- name: Build manager
env:
GO_BUILD_FLAGS: -o $MANAGER_PATH
run: task go:build
file-url: https://github.com/arduino/library-registry-submission-parser/releases/download/${{ env.SUBMISSION_PARSER_VERSION }}/parser
location: ${{ runner.temp }}
- name: Download diff
uses: actions/download-artifact@v2
@ -108,7 +98,8 @@ jobs:
- name: Parse request
id: parse-request
run: |
REQUEST="$("$MANAGER_PATH" --diffpath="${{ needs.diff.outputs.path }}/${{ needs.diff.outputs.filename }}" --repopath="${{ github.workspace }}" --listname="repositories.txt")"
chmod u+x "${{ steps.download-parser.outputs.file-path }}"
REQUEST="$("${{ steps.download-parser.outputs.file-path }}" --diffpath="${{ needs.diff.outputs.path }}/${{ needs.diff.outputs.filename }}" --repopath="${{ github.workspace }}" --listname="repositories.txt")"
# Due to limitations of the GitHub Actions workflow system, dedicated outputs must be created for use in certain workflow fields.
echo "::set-output name=type::$(echo "$REQUEST" | jq -r -c '.type')"
echo "::set-output name=submissions::$(echo "$REQUEST" | jq -c '.submissions')"

13
.gitignore vendored
View File

@ -1,13 +0,0 @@
# Build artifacts
/manager/manager
/manager/manager.exe
# Test artifacts
coverage_unit.txt
# IDEs
.idea/
.vscode/
*.bak
*.code-workspace
*.sublime-workspace

View File

@ -1,59 +1,9 @@
version: "3"
vars:
DEFAULT_GO_PACKAGES:
sh: echo `cd manager && go list ./... | tr '\n' ' '`
DEFAULT_GO_PATHS:
sh: echo '`cd manager && go list -f '{{"{{"}}.Dir{{"}}"}}' ./...`'
PRETTIER: prettier@2.1.2
tasks:
go:build:
desc: Build the project
dir: manager
cmds:
- go build -v {{.GO_BUILD_FLAGS}}
check:
desc: Test, lint, and check formatting of everything
deps:
- task: go:check
- task: docs:check
- task: config:check
go:check:
desc: Test, lint, and check formatting of Go code
deps:
- task: go:lint
- task: go:test
- task: go:check-formatting
go:lint:
desc: Lint Go code
dir: manager
cmds:
- go vet {{default .DEFAULT_GO_PACKAGES .GO_PACKAGES}}
- go get golang.org/x/lint/golint
- |
GOLINT_PATH="$(go list -f '{{"{{"}}.Target{{"}}"}}' golang.org/x/lint/golint || echo "false")"
"$GOLINT_PATH" {{default "-min_confidence 0.8 -set_exit_status" .GO_LINT_FLAGS}} "{{default .DEFAULT_GO_PACKAGES .GO_PACKAGES}}"
go:test:
desc: Run unit tests
dir: manager
cmds:
- go test -v -short -run '{{default ".*" .GO_TEST_REGEX}}' {{default "-timeout 10m -coverpkg=./... -covermode=atomic" .GO_TEST_FLAGS}} -coverprofile=coverage_unit.txt {{default .DEFAULT_GO_PACKAGES .GO_PACKAGES}}
go:check-formatting:
desc: Check Go code formatting
dir: manager
cmds:
- |
RESULTS="$(gofmt -l {{default .DEFAULT_GO_PATHS .GO_PATHS}})"
echo "$RESULTS"
test -z "$RESULTS"
docs:check:
desc: Lint and check formatting of documentation files
deps:
@ -119,18 +69,6 @@ tasks:
cmds:
- npx {{.PRETTIER}} --check "**/*.{yml,yaml,json}"
format:
desc: Format all files
deps:
- task: go:format
- task: docs:format
- task: config:format
go:format:
desc: Format Go code
cmds:
- gofmt -l -w {{default .DEFAULT_GO_PATHS .GO_PATHS}}
docs:format:
desc: Format documentation files
cmds:

View File

@ -1,10 +0,0 @@
module github.com/arduino/library-manager-list/manager
go 1.14
require (
github.com/arduino/go-paths-helper v1.5.0
github.com/arduino/go-properties-orderedmap v1.4.0
github.com/sourcegraph/go-diff v0.6.1
github.com/stretchr/testify v1.3.0
)

View File

@ -1,22 +0,0 @@
github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck=
github.com/arduino/go-paths-helper v1.5.0 h1:RVo189hD+GhUS1rQ3gixwK1nSbvVR8MGIGa7Gxv2bdM=
github.com/arduino/go-paths-helper v1.5.0/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU=
github.com/arduino/go-properties-orderedmap v1.4.0 h1:YEbbzPqm1gXWDM/Jaq8tlvmh09z2qeHPJTUw9/VA4Dk=
github.com/arduino/go-properties-orderedmap v1.4.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0HZqLQ=
github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -1,386 +0,0 @@
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"os/exec"
"strings"
"github.com/sourcegraph/go-diff/diff"
"github.com/arduino/go-paths-helper"
properties "github.com/arduino/go-properties-orderedmap"
)
// Git hosts that are supported for library repositories.
var supportedHosts []string = []string{
"bitbucket.org",
"github.com",
"gitlab.com",
}
// Libraries under these organizations will have the "Arduino" type and be linted with Arduino Lint in the "official" setting.
var officialOrganizations []string = []string{
"github.com/arduino",
"github.com/arduino-libraries",
"github.com/bcmi-labs",
"github.com/vidor-libraries",
}
// Libraries under these organizations will have the "Partner" type.
var partnerOrganizations []string = []string{
"github.com/Azure",
"github.com/ms-iot",
"github.com/ameltech",
}
// Libraries under these organizations will have the "Recommended" type.
var recommendedOrganizations []string = []string{
"github.com/adafruit",
}
// requestType is the type of the request data.
type requestType struct {
Type string `json:"type"` // Request type.
Submissions []submissionType `json:"submissions"` // Data for submitted libraries.
IndexEntry string `json:"indexEntry"` // Entry that will be made to the Library Manager index source file when the submission is accepted.
}
// submissionType is the type of the data for each individual library submitted in the request.
type submissionType struct {
SubmissionURL string `json:"submissionURL"` // Library repository URL as submitted by user. Used to identify the submission to the user.
NormalizedURL string `json:"normalizedURL"` // Submission URL in the standardized format that will be used in the index entry.
Name string `json:"name"` // Library name.
Official bool `json:"official"` // Whether the library is official.
Tag string `json:"tag"` // Name of the submission repository's latest tag, which is used as the basis for the index entry and validation.
Error string `json:"error"` // Error message.
}
// Command line flags.
var diffPathArgument = flag.String("diffpath", "", "")
var repoPathArgument = flag.String("repopath", "", "")
var listNameArgument = flag.String("listname", "", "")
func main() {
// Validate flag input.
flag.Parse()
if *diffPathArgument == "" {
errorExit("--diffpath flag is required")
}
if *repoPathArgument == "" {
errorExit("--repopath flag is required")
}
if *listNameArgument == "" {
errorExit("--listname flag is required")
}
diffPath := paths.New(*diffPathArgument)
exist, err := diffPath.ExistCheck()
if !exist {
errorExit("diff file not found")
}
listPath := paths.New(*repoPathArgument, *listNameArgument)
exist, err = listPath.ExistCheck()
if !exist {
errorExit(fmt.Sprintf("list file %s not found", listPath))
}
// Parse the PR diff.
rawDiff, err := diffPath.ReadFile()
if err != nil {
panic(err)
}
var request requestType
var submissionURLs []string
request.Type, submissionURLs = parseDiff(rawDiff, *listNameArgument)
// Process the submissions.
var indexEntries []string
for _, submissionURL := range submissionURLs {
submission, indexEntry := populateSubmission(submissionURL, listPath)
request.Submissions = append(request.Submissions, submission)
indexEntries = append(indexEntries, indexEntry)
}
// Assemble the index entry for the submissions.
request.IndexEntry = strings.Join(indexEntries, "%0A")
// Marshal the request data into a JSON document.
var marshalledRequest bytes.Buffer
jsonEncoder := json.NewEncoder(io.Writer(&marshalledRequest))
// By default, the json package HTML-sanitizes strings during marshalling (https://golang.org/pkg/encoding/json/#Marshal)
// It's not possible to change this behavior when using the simple json.MarshalIndent() approach.
jsonEncoder.SetEscapeHTML(false)
jsonEncoder.SetIndent("", "") // Single line.
err = jsonEncoder.Encode(request)
if err != nil {
panic(err)
}
fmt.Println(marshalledRequest.String())
}
// errorExit prints the error message in a standardized format and exits with status 1.
func errorExit(message string) {
fmt.Printf("ERROR: %s\n", message)
os.Exit(1)
}
// parseDiff parses the request diff and returns the request type and list of submission URLs.
func parseDiff(rawDiff []byte, listName string) (string, []string) {
var submissionURLs []string
diffs, err := diff.ParseMultiFileDiff(rawDiff)
if err != nil {
panic(err)
}
if (len(diffs) != 1) || (diffs[0].OrigName[2:] != listName) || (diffs[0].OrigName[2:] != diffs[0].NewName[2:]) { // Git diffs have a a/ or b/ prefix on file names.
// This is not a Library Manager submission.
return "other", nil
}
var addedCount int
var deletedCount int
// Get the added URLs from the diff
for _, hunk := range diffs[0].Hunks {
hunkBody := string(hunk.Body)
for _, rawDiffLine := range strings.Split(hunkBody, "\n") {
diffLine := strings.TrimRight(rawDiffLine, " \t")
if len(diffLine) < 2 {
continue // Ignore blank lines.
}
switch diffLine[0] {
case '+':
addedCount++
submissionURLs = append(submissionURLs, strings.TrimSpace(diffLine[1:]))
case '-':
deletedCount++
default:
continue
}
}
}
var requestType string
if addedCount > 0 && deletedCount == 0 {
requestType = "submission"
} else if addedCount == 0 && deletedCount > 0 {
requestType = "removal"
} else {
requestType = "modification"
}
return requestType, submissionURLs
}
// populateSubmission does the checks on the submission that aren't provided by Arduino Lint and gathers the necessary data on it.
func populateSubmission(submissionURL string, listPath *paths.Path) (submissionType, string) {
indexSourceSeparator := "|"
var submission submissionType
submission.SubmissionURL = submissionURL
// Normalize and validate submission URL.
submissionURLObject, err := url.Parse(submission.SubmissionURL)
if err != nil {
submission.Error = fmt.Sprintf("Invalid submission URL (%s)", err)
return submission, ""
}
// Check if URL is accessible.
httpResponse, err := http.Get(submissionURLObject.String())
if err != nil {
submission.Error = fmt.Sprintf("Unable to load submission URL: %s", err)
return submission, ""
}
if httpResponse.StatusCode != http.StatusOK {
submission.Error = "Unable to load submission URL. Is the repository public?"
return submission, ""
}
// Resolve redirects and normalize.
normalizedURLObject := normalizeURL(httpResponse.Request.URL)
submission.NormalizedURL = normalizedURLObject.String()
// Check if URL is from a supported Git host.
if !uRLIsUnder(normalizedURLObject, supportedHosts) {
submission.Error = normalizedURLObject.Host + " is not currently supported as a Git hosting website for Library Manager.%0ASee: https://github.com/arduino/Arduino/wiki/Library-Manager-FAQ#how-can-i-add-my-library-to-library-manager"
return submission, ""
}
// Check if URL is a Git repository
err = exec.Command("git", "ls-remote", normalizedURLObject.String()).Run()
if err != nil {
if _, ok := err.(*exec.ExitError); ok {
submission.Error = "Submission URL is not a Git clone URL (e.g., https://github.com/arduino-libraries/Servo)."
return submission, ""
}
panic(err)
}
// Check if the URL is already in the index.
listLines, err := listPath.ReadFileAsLines()
occurrences := 0
for _, listURL := range listLines {
listURLObject, err := url.Parse(strings.TrimSpace(listURL))
if err != nil {
panic(err) // All list items have already passed parsing so something is broken if this happens.
}
normalizedListURLObject := normalizeURL(listURLObject)
if normalizedListURLObject.String() == normalizedURLObject.String() {
occurrences++
if occurrences > 1 {
submission.Error = "Submission URL is already in the Library Manager index."
return submission, ""
}
}
}
// Determine the library types attributes.
submission.Official = uRLIsUnder(normalizedURLObject, officialOrganizations)
var types []string
if submission.Official {
types = append(types, "Arduino")
}
if uRLIsUnder(normalizedURLObject, partnerOrganizations) {
types = append(types, "Partner")
}
if uRLIsUnder(normalizedURLObject, recommendedOrganizations) {
types = append(types, "Recommended")
}
if types == nil {
types = append(types, "Contributed")
}
submissionClonePath, err := paths.MkTempDir("", "")
if err != nil {
panic(err)
}
err = exec.Command("git", "clone", "--depth", "1", normalizedURLObject.String(), submissionClonePath.String()).Run()
if err != nil {
panic(err)
}
// Determine latest tag name in submission repo
err = os.Chdir(submissionClonePath.String())
if err != nil {
panic(err)
}
err = exec.Command("git", "fetch", "--tags").Run()
if err != nil {
panic(err)
}
tagList, err := exec.Command("git", "rev-list", "--tags", "--max-count=1").Output()
if err != nil {
panic(err)
}
if string(tagList) == "" {
submission.Error = "The repository has no tags. You need to create a [release](https://docs.github.com/en/github/administering-a-repository/managing-releases-in-a-repository) or [tag](https://git-scm.com/docs/git-tag) that matches the `version` value in the library's library.properties file."
return submission, ""
}
latestTag, err := exec.Command("git", "describe", "--tags", strings.TrimSpace(string(tagList))).Output()
if err != nil {
panic(err)
}
submission.Tag = strings.TrimSpace(string(latestTag))
// Checkout latest tag.
err = exec.Command("git", "checkout", submission.Tag).Run()
if err != nil {
panic(err)
}
// Get submission library name. It is necessary to record this in the index source entry because the library is locked to this name.
libraryPropertiesPath := submissionClonePath.Join("library.properties")
if !libraryPropertiesPath.Exist() {
submission.Error = "Library is missing a library.properties metadata file."
return submission, ""
}
libraryProperties, err := properties.LoadFromPath(libraryPropertiesPath)
if err != nil {
submission.Error = fmt.Sprintf("Invalid library.properties file (%s)", err)
return submission, ""
}
var ok bool
submission.Name, ok = libraryProperties.GetOk("name")
if !ok {
submission.Error = "library.properties is missing a name field"
return submission, ""
}
// Assemble Library Manager index source entry string
indexEntry := strings.Join(
[]string{
submission.NormalizedURL,
strings.Join(types, ","),
submission.Name,
},
indexSourceSeparator,
)
return submission, indexEntry
}
// normalizeURL converts the URL into the standardized format used in the index.
func normalizeURL(rawURL *url.URL) url.URL {
normalizedPath := strings.TrimRight(rawURL.Path, "/")
if !strings.HasSuffix(normalizedPath, ".git") {
normalizedPath += ".git"
}
return url.URL{
Scheme: "https",
Host: rawURL.Host,
Path: normalizedPath,
}
}
func uRLIsUnder(childURL url.URL, parentCandidates []string) bool {
for _, parentCandidate := range parentCandidates {
if !strings.HasSuffix(parentCandidate, "/") {
parentCandidate += "/"
}
parentCandidateURL, err := url.Parse("https://" + parentCandidate)
if err != nil {
panic(err)
}
isUnderPath, err := paths.New(childURL.Path).IsInsideDir(paths.New(parentCandidateURL.Path))
if err != nil {
panic(err)
}
if (childURL.Host == parentCandidateURL.Host) && isUnderPath {
return true
}
}
return false
}

View File

@ -1,194 +0,0 @@
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.
package main
import (
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_parseDiff(t *testing.T) {
testName := "Multiple files"
diff := []byte(`
diff --git a/README.md b/README.md
index d4edde0..807b76d 100644
--- a/README.md
+++ b/README.md
@@ -1,0 +2 @@
+hello
diff --git a/repositories.txt b/repositories.txt
index cff484d..e14c179 100644
--- a/repositories.txt
+++ b/repositories.txt
@@ -8,0 +9 @@ https://github.com/arduino-libraries/Ethernet
+https://github.com/foo/bar
`)
requestType, submissionURLs := parseDiff(diff, "repositories.txt")
assert.Equal(t, "other", requestType, testName)
assert.Nil(t, submissionURLs, testName)
testName = "Not list"
diff = []byte(`
diff --git a/README.md b/README.md
index d4edde0..807b76d 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
# Arduino Library Manager list
+hello
`)
requestType, submissionURLs = parseDiff(diff, "repositories.txt")
assert.Equal(t, "other", requestType, testName)
assert.Nil(t, submissionURLs, testName)
testName = "List filename change"
diff = []byte(`
diff --git a/repositories.txt b/foobar.txt
similarity index 99%
rename from repositories.txt
rename to foobar.txt
index cff484d..e14c179 100644
--- a/repositories.txt
+++ b/foobar.txt
@@ -8,0 +9 @@ https://github.com/arduino-libraries/Ethernet
+https://github.com/foo/bar
`)
requestType, submissionURLs = parseDiff(diff, "repositories.txt")
assert.Equal(t, "other", requestType, testName)
assert.Nil(t, submissionURLs, testName)
testName = "Submission"
diff = []byte(`
diff --git a/repositories.txt b/repositories.txt
index cff484d..9f67763 100644
--- a/repositories.txt
+++ b/repositories.txt
@@ -8,0 +9,2 @@ https://github.com/arduino-libraries/Ethernet
+https://github.com/foo/bar
+https://github.com/foo/baz
`)
requestType, submissionURLs = parseDiff(diff, "repositories.txt")
assert.Equal(t, "submission", requestType, testName)
assert.ElementsMatch(t, submissionURLs, []string{"https://github.com/foo/bar", "https://github.com/foo/baz"}, testName)
testName = "Submission w/ no newline at end of file"
diff = []byte(`
diff --git a/repositories.txt b/repositories.txt
index cff484d..1b0b80b 100644
--- a/repositories.txt
+++ b/repositories.txt
@@ -3391,0 +3392 @@ https://github.com/lbernstone/plotutils
+https://github.com/foo/bar
\ No newline at end of file
`)
requestType, submissionURLs = parseDiff(diff, "repositories.txt")
assert.Equal(t, "submission", requestType, testName)
assert.ElementsMatch(t, submissionURLs, []string{"https://github.com/foo/bar"}, testName)
testName = "Submission w/ blank line"
diff = []byte(`
diff --git a/repositories.txt b/repositories.txt
index cff484d..1b0b80b 100644
--- a/repositories.txt
+++ b/repositories.txt
@@ -3391,0 +3392 @@ https://github.com/lbernstone/plotutils
+https://github.com/foo/bar
\ No newline at end of file
`)
requestType, submissionURLs = parseDiff(diff, "repositories.txt")
assert.Equal(t, "submission", requestType, testName)
assert.ElementsMatch(t, submissionURLs, []string{"https://github.com/foo/bar"}, testName)
testName = "Removal"
diff = []byte(`
diff --git a/repositories.txt b/repositories.txt
index cff484d..38e11d8 100644
--- a/repositories.txt
+++ b/repositories.txt
@@ -8 +7,0 @@ https://github.com/firmata/arduino
-https://github.com/arduino-libraries/Ethernet
`)
requestType, submissionURLs = parseDiff(diff, "repositories.txt")
assert.Equal(t, "removal", requestType, testName)
assert.Nil(t, submissionURLs, testName)
testName = "Modification"
diff = []byte(`
diff --git a/repositories.txt b/repositories.txt
index cff484d..8b401a1 100644
--- a/repositories.txt
+++ b/repositories.txt
@@ -8 +8 @@ https://github.com/firmata/arduino
-https://github.com/arduino-libraries/Ethernet
+https://github.com/foo/bar
`)
requestType, submissionURLs = parseDiff(diff, "repositories.txt")
assert.Equal(t, "modification", requestType, testName)
assert.Equal(t, submissionURLs, []string{"https://github.com/foo/bar"}, testName)
}
func Test_normalizeURL(t *testing.T) {
testTables := []struct {
testName string
rawURL string
expectedNormalizedURL string
}{
{"Trailing slash", "https://github.com/foo/bar/", "https://github.com/foo/bar.git"},
{".git suffix", "https://github.com/foo/bar.git", "https://github.com/foo/bar.git"},
{"http://", "http://github.com/foo/bar", "https://github.com/foo/bar.git"},
{"git://", "git://github.com/foo/bar", "https://github.com/foo/bar.git"},
}
for _, testTable := range testTables {
rawURL, err := url.Parse(testTable.rawURL)
require.Nil(t, err)
expectedNormalizedURL, err := url.Parse(testTable.expectedNormalizedURL)
require.Nil(t, err)
assert.Equal(t, *expectedNormalizedURL, normalizeURL(rawURL), testTable.testName)
}
}
func Test_uRLIsUnder(t *testing.T) {
testTables := []struct {
testName string
childURL string
parentCandidates []string
assertion assert.BoolAssertionFunc
}{
{"Match, root path", "https://github.com/foo/bar", []string{"example.com", "github.com"}, assert.True},
{"Mismatch, root path", "https://github.com/foo/bar", []string{"example.com", "example.org"}, assert.False},
{"Match, subfolder", "https://github.com/foo/bar", []string{"example.com/foo", "github.com/foo"}, assert.True},
{"Mismatch, subfolder", "https://github.com/foo/bar", []string{"example.com/foo", "github.org/bar"}, assert.False},
}
for _, testTable := range testTables {
childURL, err := url.Parse(testTable.childURL)
require.Nil(t, err)
t.Run(testTable.testName, func(t *testing.T) {
testTable.assertion(t, uRLIsUnder(*childURL, testTable.parentCandidates))
})
}
}