Skip to content

Commit 5719910

Browse files
authored
Merge branch 'awsdocs:main' into dotnet-updates
2 parents 60d9ef9 + 2ca2163 commit 5719910

40 files changed

+1617
-322
lines changed

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ python -m pip install -r requirements.txt
6262
python -m pip install -e .
6363
python -m mypy aws_doc_sdk_examples_tools
6464
python -m pytest -vv
65-
python -m black --check
65+
python -m black --check aws_doc_sdk_examples_tools
6666
```
6767

6868
## Validation Extensions
@@ -87,18 +87,19 @@ There are two stages, testing and deployment.
8787
5. **Open a Draft PR to main branch**: Do not publish for review. Wait for checks/tests to pass on the PR.
8888

8989
### 2. Deployment
90+
9091
1. **Run `stamp.sh --release` from the `main` branch to automatically perform the following actions**:
91-
- Update the `setup.py` version.
92-
- Create a tag in the -tools repository at the same SHA you identified earlier.
93-
- stamp.sh will create the next [stamp](https://blog.aspect.build/versioning-releases-from-a-monorepo) (which is valid [semver](https://packaging.python.org/en/latest/specifications/version-specifiers/#version-specifiers)) number as appropriate for the changes in this release. e.g. `2024.40.2`.
94-
- Push the new tag to `main`
92+
- Update the `setup.py` version.
93+
- Create a tag in the -tools repository at the same SHA you identified earlier.
94+
- stamp.sh will create the next [stamp](https://blog.aspect.build/versioning-releases-from-a-monorepo) (which is valid [semver](https://packaging.python.org/en/latest/specifications/version-specifiers/#version-specifiers)) number as appropriate for the changes in this release. e.g. `2024.40.2`.
95+
- Push the new tag to `main`
9596
1. **Update your testing PR branch**
96-
- Remove SHA and add tag to [validate-doc-metadata.yml](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/.github/workflows/validate-doc-metadata.yml)
97-
- Remove the SHA from [.doc_gen/validation.yaml](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/.doc_gen/validation.yaml)
98-
- This is easily accomplished in the Github UI.
97+
- Remove SHA and add tag to [validate-doc-metadata.yml](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/.github/workflows/validate-doc-metadata.yml)
98+
- Remove the SHA from [.doc_gen/validation.yaml](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/.doc_gen/validation.yaml)
99+
- This is easily accomplished in the Github UI.
99100
1. **Create a release**: Use the automated ["Create release from tag" button](https://github.com/awsdocs/aws-doc-sdk-examples-tools/releases/new) to create a new release with the new tag.
100101
1. **Perform internal update process**.
101-
- See `update.sh` script in internal package.
102+
- See `update.sh` script in internal package.
102103

103104
## Security
104105

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from __future__ import annotations
5+
6+
import re
7+
8+
from pathlib import Path
9+
from typing import Any, Callable, Dict, List, Optional
10+
from dataclasses import dataclass, field
11+
12+
from aws_doc_sdk_examples_tools import metadata_errors
13+
from .metadata_errors import (
14+
MetadataErrors,
15+
)
16+
17+
18+
def fake_gotmpl(tmpl: Optional[str], service: str, action: str):
19+
if not tmpl:
20+
return
21+
values = {
22+
".ServiceEntity.Short": service,
23+
".Action": action,
24+
}
25+
return re.sub(
26+
r"{{(?P<name>[.\w]+)}}",
27+
lambda x: values[
28+
x.groupdict()["name"]
29+
], # This will be a KeyError if the replacement isn't in the values dict
30+
tmpl,
31+
)
32+
33+
34+
@dataclass
35+
class TitleInfo:
36+
title: Optional[str] = field(default=None)
37+
title_abbrev: Optional[str] = field(default=None)
38+
synopsis: Optional[str] = field(default=None)
39+
title_suffixes: str | Dict[str, str] = field(default_factory=dict)
40+
41+
@classmethod
42+
def from_yaml(cls, yaml: Dict[str, str] | None) -> Optional[TitleInfo]:
43+
if yaml is None:
44+
return None
45+
46+
title = yaml.get("title")
47+
title_suffixes: str | Dict[str, str] = yaml.get("title_suffixes", {})
48+
title_abbrev = yaml.get("title_abbrev")
49+
synopsis = yaml.get("synopsis")
50+
51+
return cls(
52+
title=title,
53+
title_suffixes=title_suffixes,
54+
title_abbrev=title_abbrev,
55+
synopsis=synopsis,
56+
)
57+
58+
59+
@dataclass
60+
class Prefix:
61+
one: Optional[str]
62+
many: Optional[str]
63+
64+
@classmethod
65+
def from_yaml(cls, yaml: Optional[Dict[str, str]]) -> Optional[Prefix]:
66+
if yaml is None:
67+
return None
68+
69+
one = yaml.get("one")
70+
many = yaml.get("many")
71+
72+
return cls(
73+
one=one,
74+
many=many,
75+
)
76+
77+
78+
@dataclass
79+
class CategoryWithNoDisplayError(metadata_errors.MetadataError):
80+
def message(self):
81+
return "Category has no display value"
82+
83+
84+
empty_title_info = TitleInfo()
85+
86+
87+
@dataclass
88+
class Category:
89+
key: str
90+
display: str
91+
defaults: Optional[TitleInfo] = field(default=None)
92+
overrides: Optional[TitleInfo] = field(default=None)
93+
description: Optional[str] = field(default=None)
94+
synopsis_prefix: Optional[Prefix] = field(default=None)
95+
more_info: Optional[str] = field(default=None)
96+
97+
def evaluate(
98+
self,
99+
value: Optional[str],
100+
field: Callable[[TitleInfo], Optional[str]],
101+
service: str,
102+
action: str,
103+
):
104+
overrides = field(self.overrides or empty_title_info)
105+
if overrides:
106+
return fake_gotmpl(overrides, service, action)
107+
if value:
108+
return value
109+
defaults = field(self.defaults or empty_title_info)
110+
if defaults:
111+
return fake_gotmpl(defaults, service, action)
112+
return f"{service} {action}"
113+
114+
def validate(self, errors: MetadataErrors):
115+
if not self.display:
116+
errors.append(CategoryWithNoDisplayError(id=self.key))
117+
118+
@classmethod
119+
def from_yaml(
120+
cls, key: str, yaml: Dict[str, Any]
121+
) -> tuple[Category, MetadataErrors]:
122+
errors = MetadataErrors()
123+
display = str(yaml.get("display"))
124+
defaults = TitleInfo.from_yaml(yaml.get("defaults"))
125+
overrides = TitleInfo.from_yaml(yaml.get("overrides"))
126+
description = yaml.get("description")
127+
synopsis_prefix = Prefix.from_yaml(yaml.get("synopsis_prefix"))
128+
more_info = yaml.get("more_info")
129+
130+
return (
131+
cls(
132+
key=key,
133+
display=display,
134+
defaults=defaults,
135+
overrides=overrides,
136+
description=description,
137+
synopsis_prefix=synopsis_prefix,
138+
more_info=more_info,
139+
),
140+
errors,
141+
)
142+
143+
144+
def parse(
145+
file: Path, yaml: Dict[str, Any]
146+
) -> tuple[List[str], Dict[str, Category], MetadataErrors]:
147+
categories: Dict[str, Category] = {}
148+
errors = MetadataErrors()
149+
150+
standard_cats = yaml.get("standard_categories", [])
151+
# Work around inconsistency where some tools use 'Actions' and DocGen uses 'Api' to refer to single-action examples.
152+
for i in range(len(standard_cats)):
153+
if standard_cats[i] == "Actions":
154+
standard_cats[i] = "Api"
155+
for key, yaml_cat in yaml.get("categories", {}).items():
156+
if yaml_cat is None:
157+
errors.append(metadata_errors.MissingCategoryBody(id=key, file=file))
158+
else:
159+
category, cat_errs = Category.from_yaml(key, yaml_cat)
160+
categories[key] = category
161+
for error in cat_errs:
162+
error.file = file
163+
error.id = key
164+
errors.extend(cat_errs)
165+
166+
return standard_cats, categories, errors
167+
168+
169+
if __name__ == "__main__":
170+
from pprint import pp
171+
import yaml
172+
173+
path = Path(__file__).parent / "config" / "categories.yaml"
174+
with open(path) as file:
175+
meta = yaml.safe_load(file)
176+
standard_cats, cats, errs = parse(path, meta)
177+
pp(standard_cats)
178+
pp(cats)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from pathlib import Path
5+
from typing import Dict, List, Tuple
6+
import pytest
7+
import yaml
8+
9+
from aws_doc_sdk_examples_tools import metadata_errors
10+
from .categories import (
11+
parse,
12+
Category,
13+
TitleInfo,
14+
Prefix,
15+
)
16+
17+
18+
def load(
19+
path: str,
20+
) -> Tuple[List[str], Dict[str, Category], metadata_errors.MetadataErrors]:
21+
root = Path(__file__).parent
22+
filename = root / "test_resources" / path
23+
with open(filename) as file:
24+
meta = yaml.safe_load(file)
25+
return parse(filename, meta)
26+
27+
28+
def test_empty_categories():
29+
_, _, errs = load("empty_categories.yaml")
30+
assert [*errs] == [
31+
metadata_errors.MissingCategoryBody(
32+
file=Path(__file__).parent / "test_resources/empty_categories.yaml",
33+
id="EmptyCat",
34+
)
35+
]
36+
37+
38+
def test_categories():
39+
_, categories, _ = load("categories.yaml")
40+
assert categories == {
41+
"Actions": Category(
42+
key="Actions",
43+
display="Actions test",
44+
overrides=TitleInfo(
45+
title="Title override",
46+
title_suffixes={
47+
"cli": " with a CLI",
48+
"sdk": " with an &AWS; SDK",
49+
"sdk_cli": " with an &AWS; SDK or CLI",
50+
},
51+
title_abbrev="Title abbrev override",
52+
synopsis="synopsis test.",
53+
),
54+
description="test description.",
55+
),
56+
"Basics": Category(
57+
key="Basics",
58+
display="Basics",
59+
defaults=TitleInfo(
60+
title="Title default",
61+
title_abbrev="Title abbrev default",
62+
),
63+
description="default description.",
64+
),
65+
"TributaryLite": Category(
66+
key="TributaryLite",
67+
display="Tea light",
68+
description="light your way.",
69+
synopsis_prefix=Prefix(one="Tea light is", many="Tea lights are"),
70+
more_info="This is more tea light info.",
71+
),
72+
}
73+
74+
75+
if __name__ == "__main__":
76+
pytest.main([__file__, "-vv"])
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
standard_categories: ["Hello", "Basics", "Actions", "Scenarios"]
2+
categories:
3+
Hello:
4+
display: "Hello"
5+
overrides:
6+
title: "Hello {{.ServiceEntity.Short}}"
7+
title_abbrev: "Hello {{.ServiceEntity.Short}}"
8+
synopsis: "get started using {{.ServiceEntity.Short}}."
9+
Actions:
10+
display: "Actions"
11+
overrides:
12+
title: "Use <code>{{.Action}}</code>"
13+
title_suffixes:
14+
cli: " with a CLI"
15+
sdk: " with an &AWS; SDK"
16+
sdk_cli: " with an &AWS; SDK or CLI"
17+
title_abbrev: "<code>{{.Action}}</code>"
18+
synopsis: "use <code>{{.Action}}</code>."
19+
description: "are code excerpts from larger programs and must be run in context. While actions
20+
show you how to call individual service functions, you can see actions in context in their related scenarios."
21+
Basics:
22+
display: "Basics"
23+
defaults:
24+
title: "Learn the basics of {{.ServiceEntity.Short}} with an &AWS; SDK"
25+
title_abbrev: "Learn the basics"
26+
description: "are code examples that show you how to perform the essential operations within a service."
27+
Scenarios:
28+
display: "Scenarios"
29+
description: "are code examples that show you how to accomplish specific tasks by
30+
calling multiple functions within a service or combined with other &AWS-services;."
31+
TributaryLite:
32+
display: "&AWS; community contributions"
33+
description: "are examples that were created and are maintained by multiple teams across &AWS;.
34+
To provide feedback, use the mechanism provided in the linked repositories."

aws_doc_sdk_examples_tools/config/entities.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ expanded_override:
1414
"&kms-keys;": "KMS keys"
1515
"&kms-keys-long;": "AWS KMS keys"
1616
"&S3long;": "Amazon Simple Storage Service"
17+
"&S3only;": "S3"
1718
"&SLN;": "Amazon States Language"

0 commit comments

Comments
 (0)