

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 程序包组定义语法和匹配行为
<a name="package-group-definition-syntax-matching-behavior"></a>

本主题包含有关定义程序包组、模式匹配行为、程序包关联强度和程序包组层次结构的信息。

**Contents**
+ [程序包组定义语法和示例](#package-group-definition-syntax-examples)
  + [程序包组定义和规范化](#package-group-definition-syntax-examples-normalization)
  + [程序包组定义中的命名空间](#package-group-definition-syntax-examples-namespaces)
+ [程序包组层次结构和模式特异性](#package-group-hierarchy-pattern-specificity)
+ [单词、单词边界和前缀匹配](#package-group-word-boundary-prefix)
+ [区分大小写](#package-group-case-sensitivity)
+ [强匹配和弱匹配](#package-group-strong-and-weak-match)
+ [其他变体](#package-group-additional-variations)

## 程序包组定义语法和示例
<a name="package-group-definition-syntax-examples"></a>

 定义程序包组的模式语法严格遵循程序包路径的格式。程序包路径是根据程序包的坐标组件（格式、命名空间和名称）创建的，方法是在开头添加正斜杠，然后用正斜杠分隔每个组件。例如，命名空间 *space* 中名为 *anycompany-ui-components* 的 npm 程序包的程序包路径为 */npm/space/anycompany-ui-components*。

程序包组模式遵循与程序包路径相同的结构，不同之处在于省略了未指定为组定义一部分的组件，并且该模式以一个后缀结尾。包含的后缀决定了模式的匹配行为，如下所示：
+ `$` 后缀将与完整的程序包坐标匹配。
+ `~` 后缀将与一个前缀匹配。
+ `*` 后缀将与先前定义的组件的所有值匹配。

以下是每种允许组合的示例模式：

1. 所有程序包格式：`/*`

1. 特定程序包格式：`/npm/*`

1. 程序包格式和命名空间前缀：`/maven/com.anycompany~`

1. 程序包格式和命名空间：`/npm/space/*`

1. 程序包格式、命名空间和名称前缀：`/npm/space/anycompany-ui~`

1. 程序包格式、命名空间和名称：`/maven/org.apache.logging.log4j/log4j-core$`

如以上示例所示，`~` 后缀添加到命名空间或名称的末尾以表示前缀匹配，当用于匹配路径中下一个组件的所有值（所有格式、所有命名空间或所有名称）时，`*` 位于正斜杠之后。

### 程序包组定义和规范化
<a name="package-group-definition-syntax-examples-normalization"></a>

CodeArtifact 对 NuGet、Python 和 Swift 程序包名称进行规范化，并对 Swift 程序包命名空间进行规范化，然后存储它们。CodeArtifact 在将程序包与程序包组定义进行匹配时使用这些规范化名称。因此，包含采用这些格式的命名空间或名称的程序包组必须使用规范化的命名空间和名称。有关如何规范程序包名称和命名空间的更多信息，请参阅 [NuGet](nuget-name-normalization.md)、[Python](python-name-normalization.md) 和 [Swift](swift-name-normalization.md) 名称规范化文档。

### 程序包组定义中的命名空间
<a name="package-group-definition-syntax-examples-namespaces"></a>

对于没有命名空间的程序包或程序包格式（Python 和 NuGet），程序包组不得包含命名空间。这些程序包组的程序包组定义包含一个空白的命名空间部分。例如，名为 *requests* 的 Python 程序包的路径为 */python//requests*。

对于带有命名空间的程序包或程序包格式（Maven、通用和 Swift），如果包含程序包名称，则必须包含命名空间。对于 Swift 程序包格式，将使用规范化的程序包命名空间。有关如何规范 Swift 程序包命名空间的更多信息，请参阅 [Swift 程序包名称和命名空间规范化](swift-name-normalization.md)。

## 程序包组层次结构和模式特异性
<a name="package-group-hierarchy-pattern-specificity"></a>

“属于”某个程序包组或与之“关联”的程序包是指其程序包路径与该组的模式匹配，但与更具体组的模式不匹配的程序包。例如，给定程序包组 `/npm/*` 和 `/npm/space/*`，程序包路径 */npm//react* 与第一个组（`/npm/*`）关联，而 */npm/space/aui.components* 和 */npm/space/amplify-ui-core* 与第二个组（`/npm/space/*`）关联。尽管一个程序包可能与多个组匹配，但每个程序包仅与一个组关联（即最具体的匹配），只有这个组的配置才适用于该程序包。

当一个程序包路径与多个模式匹配时，可以将“更具体”的模式视为最长的匹配模式。或者，更具体的模式是与程序包（该程序包与不太具体的模式匹配）的适当子集匹配的模式。在之前的示例中，每个与 `/npm/space/*` 匹配的程序包也都与 `/npm/*` 匹配，但反之则不成立，这使得 `/npm/space/*` 模式更具体，因为它是 `/npm/*` 的一个适当子集。由于一个组是另一个组的子集，因此它会创建一个层次结构，其中 `/npm/space/*` 是父组 `/npm/*` 的子组。

尽管只有最具体的程序包组的配置适用于程序包，但可以将该组配置为继承其父组的配置。

## 单词、单词边界和前缀匹配
<a name="package-group-word-boundary-prefix"></a>

讨论前缀匹配之前，我们先定义一些关键术语：
+ *单词*：一个字母或数字，后跟零个或多个字母、数字或标记字符（例如重音符号、变音符号等）。
+ 当到达非单词字符时，**单词边界位于单词的末尾。非单词字符是标点符号，例如 `.`、`-` 和 `_`。

具体而言，单词的正则表达式模式是 `[\p{L}\p{N}][\p{L}\p{N}\p{M}]*`，可以分解如下：
+ `\p{L}` 代表任何字母。
+ `\p{N}` 代表任意数字。
+ `\p{M}` 代表任何标记字符，例如重音符号、变音符号等。

因此，`[\p{L}\p{N}]` 代表一个数字或字母，`[\p{L}\p{N}\p{M}]*` 代表零个或多个字母、数字或标记字符，而单词边界位于此正则表达式模式的每个匹配项的末尾。

**注意**  
单词边界匹配基于“单词”的这一定义，而不基于字典中定义的单词，也不基于 CameCase。例如，`oneword` 或 `OneWord` 中没有单词边界。

定义了单词和单词边界后，我们可以用它们来描述 CodeArtifact 中的前缀匹配。为了表示单词边界上的前缀匹配，在单词字符后面使用匹配字符（`~`）。例如，模式 `/npm/space/foo~` 与程序包路径 `/npm/space/foo` 和 `/npm/space/foo-bar` 匹配，但与 `/npm/space/food` 或 `/npm/space/foot` 不匹配。

非单词字符后面需要使用通配符（`*`），而不是 `~`，例如在模式 `/npm/*` 中。

## 区分大小写
<a name="package-group-case-sensitivity"></a>

程序包组定义区分大小写，这意味着仅在大小写方面有所不同的模式可作为单独的程序包组存在。例如，用户可以为 npm 公有注册表上存在的三个不同的程序包（*AsyncStorage*、*asyncStorage* 和 *asyncstorage*，它们仅在大小写方面有所不同）创建单独的程序包组，其模式分别为 `/npm//AsyncStorage$`、`/npm//asyncStorage$` 和 `/npm//asyncstorage$`。

虽然大小写很重要，但如果程序包的模式变体只是大小写不同，CodeArtifact 仍会将程序包与程序包组关联。如果用户创建了 `/npm//AsyncStorage$` 程序包组，而没有创建上面显示的其他两个组，则名称 *AsyncStorage* 的所有大小写变体（包括 *asyncStorage* 和 *asyncstorage*）都将与该程序包组关联。但是，如下一节[强匹配和弱匹配](#package-group-strong-and-weak-match)所述，这些变体的处理方式将与 *AsyncStorage* 不同，后者与模式完全匹配。

## 强匹配和弱匹配
<a name="package-group-strong-and-weak-match"></a>

上一节[区分大小写](#package-group-case-sensitivity)中的信息说明程序包组区分大小写，接着又解释它们不区分大小写。这是因为 CodeArtifact 中的程序包组定义具有强匹配（或精确匹配）和弱匹配（或变体匹配）的概念。强匹配是指程序包与模式完全匹配，无任何变体。弱匹配是指程序包与模式的变体（例如不同的字母大小写）匹配。弱匹配行为可防止作为程序包组模式变体的程序包汇总到更通用的程序包组。当某个程序包是最具体匹配组模式的变体（弱匹配）时，此程序包便与该组关联，但此程序包会被阻止，而不会应用该组的来源控制配置，从而防止此程序包的任何新版本从上游提取或发布。这种行为降低了由于名称几乎相同的程序包的依赖项混淆而导致供应链攻击的风险。

为了说明弱匹配行为，假设程序包组 `/npm/*` 允许摄取但阻止发布。更具体的程序包组 `/npm//anycompany-spicy-client$` 被配置为阻止摄取但允许发布。名为 *anycompany-spicy-client* 的程序包是程序包组的强匹配，允许发布程序包版本但阻止摄取程序包版本。允许发布的程序包名称的唯一大小写格式为 *anycompany-spicy-client*，因为它是程序包定义模式的强匹配。另一种大小写变体（例如 *AnyCompany-spicy-client*）由于是弱匹配，因此无法发布。更重要的是，程序包组会阻止摄取所有大小写变体，而不仅仅是模式中使用的小写名称，从而降低了依赖项混淆攻击的风险。

## 其他变体
<a name="package-group-additional-variations"></a>

除了大小写差异外，弱匹配还会忽略破折号 `-`、点 `.`、下划线 `_` 和易混淆字符（例如来自不同字母表的外观相似的字符）序列方面的差异。在用于弱匹配的规范化期间，CodeArtifact 会执行大小写折叠（类似于转换为小写形式），用单个点替换短划线、点和下划线字符序列，并规范易混淆的字符。

弱匹配将破折号、点和下划线视为等效，但不会完全忽略它们。这意味着 *foo-bar*、*foo.bar*、*foo..bar* 和 *foo\$1bar* 都是弱匹配的等效形式，但 *foobar* 不是。尽管一些公有存储库实施了相应措施来防止出现这些类型的变体，但公有存储库提供的保护并没有使程序包组的这一功能变得不必要。例如，诸如 npm 公有注册表之类的公有存储库只有在 *my-package* 已发布到名为 *my-package* 的程序包时，才会阻止该程序包的新变体。如果 *my-package* 是内部程序包，并且您已创建允许发布但阻止摄取的程序包组 `/npm//my-package$`，那么您可能不想将 *my-package* 发布到 npm 公有注册表，以防止允许诸如 *my.package* 之类的变体。

虽然某些程序包格式（例如 Maven）对这些字符的处理方式不同（Maven 将 `.` 而不是 `-` 或 `_` 视为命名空间层次结构分隔符），但像 *com.act-on* 这样的程序包仍可能与 *com.act.on* 发生混淆。

**注意**  
请注意，每当有多个变体与一个程序包组关联时，管理员都可以为特定变体创建一个新的程序包组，以便为该变体配置不同的行为。