Repository: go-swagger/go-swagger (go)

Issue:
Wrong import path generated for an operation called "client"
## Problem statement
I have a set of operations under a tag called "client". When I go to generate my server code in an output folder called `gen/server`, the file at `gen/server/restapi/operations/<app_name>_api.go` has all the correct operation imports, EXCEPT for the one called client.

The imports for all the others look like:

```go
	"github.com/<org name>/<app name>/gen/server/restapi/operations/budgets"
```

But the import for the client operations looks like:
```go
	"github.com/<org name>/<app name>/gen/server/client"
```

The `restapi/operations` part of the path is missing.

If I rename the tag to `client2`, this behavior is not reproduced.

Please remove the sections that don't apply

## Swagger specification
N/A

## Steps to reproduce
Explained above

## Environment
swagger version: v0.28.0 and v0.29.0 have same behavior  
go version: go1.17.5
OS: mac osx monterey


Pull request description:
fix(codegen): fixed import conflict when tag is "client"
* fixes #2730

This PR deconflicts imports when the package for an operation group is named "client". This affects both server and client generation.

1. Removed the systematic addition of the client package to default imports: this is only need for CLI generation
2. Removed the systematic import of the operations package: this is used for server
3. Check for conflicts with the target client package and uses a deconflicted alias if needed.

Other fixes:
* updated the list of golang OS names to check for special file suffixes during file name mangling.
* similar conflicts may be constructed with the CLI target package

Patch:
diff --git a/fixtures/bugs/2730/2730.yaml b/fixtures/bugs/2730/2730.yaml
new file mode 100644
index 00000000..d8b7ee23
--- /dev/null
+++ b/fixtures/bugs/2730/2730.yaml
@@ -0,0 +1,635 @@
+swagger: "2.0"
+info:
+    contact:
+        email: example@example.com
+    description: 'fixture tag named "client"'
+    license:
+        name: Apache 2.0
+        url: http://www.apache.org/licenses/LICENSE-2.0.html
+    termsOfService: http://swagger.io/terms/
+    title: Swagger Petstore
+    version: 1.0.0
+basePath: /
+tags:
+    - description: Everything about your Pets
+      externalDocs:
+        description: Find out more
+        url: http://swagger.io
+      name: pet
+    - description: Access to Petstore orders
+      name: client
+    - description: Operations about user
+      externalDocs:
+        description: Find out more about our store
+        url: http://swagger.io
+      name: user
+paths:
+    /pet:
+        post:
+            consumes:
+                - application/json
+                - application/xml
+            operationId: addPet
+            parameters:
+                - description: Pet object that needs to be added to the store
+                  in: body
+                  name: body
+                  required: true
+                  schema:
+                    $ref: '#/definitions/Pet'
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "405":
+                    description: Invalid input
+            summary: Add a new pet to the store
+            tags:
+                - pet
+        put:
+            consumes:
+                - application/json
+                - application/xml
+            operationId: updatePet
+            parameters:
+                - description: Pet object that needs to be added to the store
+                  in: body
+                  name: body
+                  required: true
+                  schema:
+                    $ref: '#/definitions/Pet'
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "400":
+                    description: Invalid ID supplied
+                "404":
+                    description: Pet not found
+                "405":
+                    description: Validation exception
+            summary: Update an existing pet
+            tags:
+                - pet
+    /pet/findByStatus:
+        get:
+            description: Multiple status values can be provided with comma separated strings
+            operationId: findPetsByStatus
+            parameters:
+                - collectionFormat: multi
+                  description: Status values that need to be considered for filter
+                  in: query
+                  items:
+                    default: available
+                    enum:
+                        - available
+                        - pending
+                        - sold
+                    type: string
+                  name: status
+                  required: true
+                  type: array
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "200":
+                    description: successful operation
+                    schema:
+                        items:
+                            $ref: '#/definitions/Pet'
+                        type: array
+                "400":
+                    description: Invalid status value
+            summary: Finds Pets by status
+            tags:
+                - pet
+    /pet/findByTags:
+        get:
+            deprecated: true
+            description: Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
+            operationId: findPetsByTags
+            parameters:
+                - collectionFormat: multi
+                  description: Tags to filter by
+                  in: query
+                  items:
+                    type: string
+                  name: tags
+                  required: true
+                  type: array
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "200":
+                    description: successful operation
+                    schema:
+                        items:
+                            $ref: '#/definitions/Pet'
+                        type: array
+                "400":
+                    description: Invalid tag value
+            summary: Finds Pets by tags
+            tags:
+                - pet
+    /pet/{petId}:
+        delete:
+            operationId: deletePet
+            parameters:
+                - in: header
+                  name: api_key
+                  type: string
+                - description: Pet id to delete
+                  format: int64
+                  in: path
+                  name: petId
+                  required: true
+                  type: integer
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "400":
+                    description: Invalid ID supplied
+                "404":
+                    description: Pet not found
+            summary: Deletes a pet
+            tags:
+                - pet
+        get:
+            description: Returns a single pet
+            operationId: getPetById
+            parameters:
+                - description: ID of pet to return
+                  format: int64
+                  in: path
+                  name: petId
+                  required: true
+                  type: integer
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "200":
+                    description: successful operation
+                    schema:
+                        $ref: '#/definitions/Pet'
+                "400":
+                    description: Invalid ID supplied
+                "404":
+                    description: Pet not found
+            summary: Find pet by ID
+            tags:
+                - pet
+        post:
+            consumes:
+                - application/x-www-form-urlencoded
+            operationId: updatePetWithForm
+            parameters:
+                - description: ID of pet that needs to be updated
+                  format: int64
+                  in: path
+                  name: petId
+                  required: true
+                  type: integer
+                - description: Updated name of the pet
+                  in: formData
+                  name: name
+                  type: string
+                - description: Updated status of the pet
+                  in: formData
+                  name: status
+                  type: string
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "405":
+                    description: Invalid input
+            summary: Updates a pet in the store with form data
+            tags:
+                - pet
+    /pet/{petId}/uploadImage:
+        post:
+            consumes:
+                - multipart/form-data
+            operationId: uploadFile
+            parameters:
+                - description: ID of pet to update
+                  format: int64
+                  in: path
+                  name: petId
+                  required: true
+                  type: integer
+                - description: Additional data to pass to server
+                  in: formData
+                  name: additionalMetadata
+                  type: string
+                - description: file to upload
+                  in: formData
+                  name: file
+                  type: file
+            produces:
+                - application/json
+            responses:
+                "200":
+                    description: successful operation
+                    schema:
+                        $ref: '#/definitions/ApiResponse'
+            security:
+                - petstore_auth:
+                    - write:pets
+                    - read:pets
+            summary: uploads an image
+            tags:
+                - pet
+    /store/inventory:
+        get:
+            description: Returns a map of status codes to quantities
+            operationId: getInventory
+            produces:
+                - application/json
+            responses:
+                "200":
+                    description: successful operation
+                    schema:
+                        additionalProperties:
+                            format: int32
+                            type: integer
+                        type: object
+            summary: Returns pet inventories by status
+            tags:
+                - client
+    /store/order:
+        post:
+            operationId: placeOrder
+            parameters:
+                - description: order placed for purchasing the pet
+                  in: body
+                  name: body
+                  required: true
+                  schema:
+                    $ref: '#/definitions/Order'
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "200":
+                    description: successful operation
+                    schema:
+                        $ref: '#/definitions/Order'
+                "400":
+                    description: Invalid Order
+            summary: Place an order for a pet
+            tags:
+                - client
+    /store/order/{orderId}:
+        delete:
+            description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors
+            operationId: deleteOrder
+            parameters:
+                - description: ID of the order that needs to be deleted
+                  format: int64
+                  in: path
+                  minimum: !!float 1
+                  name: orderId
+                  required: true
+                  type: integer
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "400":
+                    description: Invalid ID supplied
+                "404":
+                    description: Order not found
+            summary: Delete purchase order by ID
+            tags:
+                - client
+        get:
+            description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions
+            operationId: getOrderById
+            parameters:
+                - description: ID of pet that needs to be fetched
+                  format: int64
+                  in: path
+                  maximum: !!float 10
+                  minimum: !!float 1
+                  name: orderId
+                  required: true
+                  type: integer
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "200":
+                    description: successful operation
+                    schema:
+                        $ref: '#/definitions/Order'
+                "400":
+                    description: Invalid ID supplied
+                "404":
+                    description: Order not found
+            summary: Find purchase order by ID
+            tags:
+                - client
+    /user:
+        post:
+            description: This can only be done by the logged in user.
+            operationId: createUser
+            parameters:
+                - description: Created user object
+                  in: body
+                  name: body
+                  required: true
+                  schema:
+                    $ref: '#/definitions/User'
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                default:
+                    description: successful operation
+            summary: Create user
+            tags:
+                - user
+    /user/createWithArray:
+        post:
+            operationId: createUsersWithArrayInput
+            parameters:
+                - description: List of user object
+                  in: body
+                  name: body
+                  required: true
+                  schema:
+                    items:
+                        $ref: '#/definitions/User'
+                    type: array
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                default:
+                    description: successful operation
+            summary: Creates list of users with given input array
+            tags:
+                - user
+    /user/createWithList:
+        post:
+            operationId: createUsersWithListInput
+            parameters:
+                - description: List of user object
+                  in: body
+                  name: body
+                  required: true
+                  schema:
+                    items:
+                        $ref: '#/definitions/User'
+                    type: array
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                default:
+                    description: successful operation
+            summary: Creates list of users with given input array
+            tags:
+                - user
+    /user/login:
+        get:
+            operationId: loginUser
+            parameters:
+                - description: The user name for login
+                  in: query
+                  name: username
+                  required: true
+                  type: string
+                - description: The password for login in clear text
+                  in: query
+                  name: password
+                  required: true
+                  type: string
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "200":
+                    description: successful operation
+                    headers:
+                        X-Expires-After:
+                            description: date in UTC when token expires
+                            format: date-time
+                            type: string
+                        X-Rate-Limit:
+                            description: calls per hour allowed by the user
+                            format: int32
+                            type: integer
+                    schema:
+                        type: string
+                "400":
+                    description: Invalid username/password supplied
+            summary: Logs user into the system
+            tags:
+                - user
+    /user/logout:
+        get:
+            operationId: logoutUser
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                default:
+                    description: successful operation
+            summary: Logs out current logged in user session
+            tags:
+                - user
+    /user/{username}:
+        delete:
+            description: This can only be done by the logged in user.
+            operationId: deleteUser
+            parameters:
+                - description: The name that needs to be deleted
+                  in: path
+                  name: username
+                  required: true
+                  type: string
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "400":
+                    description: Invalid username supplied
+                "404":
+                    description: User not found
+            summary: Delete user
+            tags:
+                - user
+        get:
+            operationId: getUserByName
+            parameters:
+                - description: 'The name that needs to be fetched. Use user1 for testing. '
+                  in: path
+                  name: username
+                  required: true
+                  type: string
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "200":
+                    description: successful operation
+                    schema:
+                        $ref: '#/definitions/User'
+                "400":
+                    description: Invalid username supplied
+                "404":
+                    description: User not found
+            summary: Get user by user name
+            tags:
+                - user
+        put:
+            description: This can only be done by the logged in user.
+            operationId: updateUser
+            parameters:
+                - description: name that need to be updated
+                  in: path
+                  name: username
+                  required: true
+                  type: string
+                - description: Updated user object
+                  in: body
+                  name: body
+                  required: true
+                  schema:
+                    $ref: '#/definitions/User'
+            produces:
+                - application/xml
+                - application/json
+            responses:
+                "400":
+                    description: Invalid user supplied
+                "404":
+                    description: User not found
+            summary: Updated user
+            tags:
+                - user
+definitions:
+    ApiResponse:
+        properties:
+            code:
+                format: int32
+                type: integer
+            message:
+                type: string
+            type:
+                type: string
+        type: object
+    Category:
+        properties:
+            id:
+                format: int64
+                type: integer
+            name:
+                type: string
+        type: object
+        xml:
+            name: Category
+    Order:
+        properties:
+            complete:
+                default: false
+                type: boolean
+            id:
+                format: int64
+                type: integer
+            petId:
+                format: int64
+                type: integer
+            quantity:
+                format: int32
+                type: integer
+            shipDate:
+                format: date-time
+                type: string
+            status:
+                description: Order Status
+                enum:
+                    - placed
+                    - approved
+                    - delivered
+                type: string
+        type: object
+        xml:
+            name: Order
+    Pet:
+        properties:
+            category:
+                $ref: '#/definitions/Category'
+            id:
+                format: int64
+                type: integer
+            name:
+                example: doggie
+                type: string
+            photoUrls:
+                items:
+                    type: string
+                type: array
+                xml:
+                    name: photoUrl
+                    wrapped: true
+            status:
+                description: pet status in the store
+                enum:
+                    - available
+                    - pending
+                    - sold
+                type: string
+            tags:
+                items:
+                    $ref: '#/definitions/Tag'
+                type: array
+                xml:
+                    name: tag
+                    wrapped: true
+        required:
+            - name
+            - photoUrls
+        type: object
+        xml:
+            name: Pet
+    Tag:
+        properties:
+            id:
+                format: int64
+                type: integer
+            name:
+                type: string
+        type: object
+        xml:
+            name: Tag
+    User:
+        properties:
+            email:
+                type: string
+            firstName:
+                type: string
+            id:
+                format: int64
+                type: integer
+            lastName:
+                type: string
+            password:
+                type: string
+            phone:
+                type: string
+            userStatus:
+                description: User Status
+                format: int32
+                type: integer
+            username:
+                type: string
+        type: object
+        xml:
+            name: User
diff --git a/generator/language.go b/generator/language.go
index 37c9219d..68e91166 100644
--- a/generator/language.go
+++ b/generator/language.go
@@ -154,6 +154,7 @@ func GoLangOpts() *LanguageOpts {
 		"freebsd":   true,
 		"hurd":      true,
 		"illumos":   true,
+		"ios":       true,
 		"js":        true,
 		"linux":     true,
 		"nacl":      true,
@@ -172,6 +173,7 @@ func GoLangOpts() *LanguageOpts {
 		"armbe":       true,
 		"arm64":       true,
 		"arm64be":     true,
+		"loong64":     true,
 		"mips":        true,
 		"mipsle":      true,
 		"mips64":      true,
diff --git a/generator/support.go b/generator/support.go
index a785f70b..cccc4603 100644
--- a/generator/support.go
+++ b/generator/support.go
@@ -220,11 +220,13 @@ func (a *appGenerator) GenerateSupport(ap *GenApp) error {
 	app.DefaultImports[pkgAlias] = serverPath
 	app.ServerPackageAlias = pkgAlias
 
-	// add client import for cli generation
-	clientPath := path.Join(baseImport,
-		a.GenOpts.LanguageOpts.ManglePackagePath(a.ClientPackage, defaultClientTarget))
-	clientPkgAlias := importAlias(clientPath)
-	app.DefaultImports[clientPkgAlias] = clientPath
+	if a.GenOpts.IncludeCLi { // no need to add this import when there is no CLI
+		// add client import for cli generation
+		clientPath := path.Join(baseImport,
+			a.GenOpts.LanguageOpts.ManglePackagePath(a.ClientPackage, defaultClientTarget))
+		clientPkgAlias := importAlias(clientPath)
+		app.DefaultImports[clientPkgAlias] = clientPath
+	}
 
 	return a.GenOpts.renderApplication(app)
 }
@@ -265,9 +267,11 @@ func (a *appGenerator) makeCodegenApp() (GenApp, error) {
 
 	imports := make(map[string]string, 50)
 	alias := deconflictPkg(a.GenOpts.LanguageOpts.ManglePackageName(a.OperationsPackage, defaultOperationsTarget), renameAPIPackage)
-	imports[alias] = path.Join(
-		baseImport,
-		a.GenOpts.LanguageOpts.ManglePackagePath(a.OperationsPackage, defaultOperationsTarget))
+	if !a.GenOpts.IsClient { // we don't want to inject this import for clients
+		imports[alias] = path.Join(
+			baseImport,
+			a.GenOpts.LanguageOpts.ManglePackagePath(a.OperationsPackage, defaultOperationsTarget))
+	}
 
 	implAlias := ""
 	if a.GenOpts.ImplementationPackage != "" {
@@ -358,7 +362,6 @@ func (a *appGenerator) makeCodegenApp() (GenApp, error) {
 
 		op.ReceiverName = receiver
 		op.Tags = tags // ordered tags for this operation, possibly filtered by CLI params
-		genOps = append(genOps, op)
 
 		if !a.GenOpts.SkipTagPackages && tag != "" {
 			importPath := filepath.ToSlash(
@@ -367,8 +370,19 @@ func (a *appGenerator) makeCodegenApp() (GenApp, error) {
 					a.GenOpts.LanguageOpts.ManglePackagePath(a.OperationsPackage, defaultOperationsTarget),
 					a.GenOpts.LanguageOpts.ManglePackageName(bldr.APIPackage, defaultOperationsTarget),
 				))
+
+			// check for possible conflicts that requires import aliasing
+			pth, aliasUsed := defaultImports[bldr.APIPackageAlias]
+			if (a.GenOpts.IsClient && bldr.APIPackageAlias == a.GenOpts.ClientPackage) || // we don't want import to shadow the current package
+				(a.GenOpts.IncludeCLi && bldr.APIPackageAlias == a.GenOpts.CliPackage) ||
+				(aliasUsed && pth != importPath) { // was already imported with a different target
+				op.PackageAlias = renameOperationPackage(tags, bldr.APIPackageAlias)
+				bldr.APIPackageAlias = op.PackageAlias
+			}
 			defaultImports[bldr.APIPackageAlias] = importPath
 		}
+
+		genOps = append(genOps, op)
 	}
 	sort.Sort(genOps)
 
@@ -381,7 +395,7 @@ func (a *appGenerator) makeCodegenApp() (GenApp, error) {
 
 	opGroups := make(GenOperationGroups, 0, len(opsGroupedByPackage))
 	for k, v := range opsGroupedByPackage {
-		log.Printf("operations for package packages %q (found: %d)", k, len(v))
+		log.Printf("operations for package %q (found: %d)", k, len(v))
 		sort.Sort(v)
 		// trim duplicate extra schemas within the same package
 		vv := make(GenOperations, 0, len(v))


Test patch:
diff --git a/generator/generate_test.go b/generator/generate_test.go
index 2d4a5128..fc0dd8dd 100644
--- a/generator/generate_test.go
+++ b/generator/generate_test.go
@@ -536,6 +536,23 @@ func generateFixtures(_ testing.TB) map[string]generateFixture {
 				)
 			},
 		},
+		"conflict_name_server_issue_2730": {
+			spec:   "../fixtures/bugs/2730/2730.yaml",
+			target: "server-2730",
+			prepare: func(_ *testing.T, opts *GenOpts) {
+				opts.MainPackage = "nrcodegen-server"
+				opts.IncludeMain = true
+				opts.ValidateSpec = true
+			},
+			verify: func(t *testing.T, target string) {
+				location := filepath.Join(target, "cmd", "nrcodegen-server")
+				require.True(t, fileExists("", location))
+
+				t.Run("building generated server",
+					goExecInDir(location, "build"),
+				)
+			},
+		},
 	}
 }
 
@@ -590,6 +607,19 @@ func generateClientFixtures(_ testing.TB) map[string]generateFixture {
 				)
 			},
 		},
+		"conflict_name_client_issue_2730": {
+			spec:    "../fixtures/bugs/2730/2730.yaml",
+			target:  "server-2730",
+			prepare: func(_ *testing.T, opts *GenOpts) {},
+			verify: func(t *testing.T, target string) {
+				location := filepath.Join(target, "client")
+				require.True(t, fileExists("", location))
+
+				t.Run("building generated client",
+					goExecInDir(location, "build"),
+				)
+			},
+		},
 	}
 }