Skip to content

Commit a38230f

Browse files
authored
FIX broken links from cards on landing page (microsoft#1140)
1 parent f81f7db commit a38230f

6 files changed

Lines changed: 76 additions & 21 deletions

File tree

build_scripts/check_links.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,21 @@
2727
# Updated regex pattern to capture URLs from Markdown and HTML
2828
URL_PATTERN = re.compile(r'\[.*?\]\((.*?)\)|href="([^"]+)"|src="([^"]+)"')
2929

30+
# Pattern to capture :link: directives from MyST grid-item-cards
31+
GRID_LINK_PATTERN = re.compile(r"^:link:\s+(.+)$", re.MULTILINE)
32+
3033

3134
def extract_urls(file_path):
3235
with open(file_path, "r", encoding="utf-8") as file:
3336
content = file.read()
3437
matches = URL_PATTERN.findall(content)
3538
# Flatten the list of tuples and filter out empty strings
3639
urls = [strip_fragment(url) for match in matches for url in match if url]
40+
41+
# Extract :link: directives from MyST grid-item-cards
42+
grid_links = GRID_LINK_PATTERN.findall(content)
43+
urls.extend(grid_links)
44+
3745
return urls
3846

3947

@@ -47,7 +55,17 @@ def strip_fragment(url):
4755

4856
def resolve_relative_url(base_path, url):
4957
if not url.startswith(("http://", "https://", "mailto:", "attachment:")):
50-
return os.path.abspath(os.path.join(os.path.dirname(base_path), url))
58+
# Handle MyST doc references (e.g., setup/1b_install_docker)
59+
# These can be .md, .rst, or directory paths
60+
abs_path = os.path.abspath(os.path.join(os.path.dirname(base_path), url))
61+
62+
# Check various possible file extensions for doc links
63+
if not os.path.exists(abs_path):
64+
for ext in [".md", ".ipynb"]:
65+
if os.path.exists(abs_path + ext):
66+
return abs_path + ext
67+
68+
return abs_path
5169
return url
5270

5371

@@ -74,6 +92,10 @@ def check_url(url, retries=2, delay=2):
7492
):
7593
return url, True
7694

95+
# If it's not an HTTP URL at this point, it's likely a broken local file reference
96+
if not url.startswith(("http://", "https://")):
97+
return url, False
98+
7799
attempts = 0
78100
while attempts <= retries:
79101
try:
@@ -91,6 +113,9 @@ def check_url(url, retries=2, delay=2):
91113
return url, False
92114
time.sleep(delay)
93115

116+
# If we exit the loop without returning, the URL is broken
117+
return url, False
118+
94119

95120
def check_links_in_file(file_path):
96121
urls = extract_urls(file_path)

doc/_config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,5 @@ sphinx:
7272
- "https://scikit-learn.org/stable"
7373
- null
7474
pytorch:
75-
- "https://pytorch.org/docs/stable"
75+
- "https://docs.pytorch.org/docs/stable"
7676
- null

doc/contributing/11_release_process.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ links work properly. Note: There may not be any links to update, but it is
4949
good practice to check in case our README changes.
5050

5151
Replace all "main" links like
52-
"https://github.com/Azure/PyRIT/blob/main/doc/README.md" with "raw" links that have
52+
"https://github.com/Azure/PyRIT/blob/main/doc/index.md" with "raw" links that have
5353
the correct version number, i.e.,
54-
"https://raw.githubusercontent.com/Azure/PyRIT/releases/vx.y.z/doc/README.md".
54+
"https://raw.githubusercontent.com/Azure/PyRIT/releases/vx.y.z/doc/index.md".
5555

5656
For images, update using the "raw" link, e.g.,
5757
"https://raw.githubusercontent.com/Azure/PyRIT/releases/vx.y.z/assets/pyrit_architecture.png".

doc/index.md

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ PyRIT offers flexible installation options to suit different needs. Choose the p
2626
:gutter: 3
2727

2828
:::{grid-item-card} 🐋 Docker Installation
29-
:link: ./setup/1b_install_docker.md
29+
:link: setup/1b_install_docker
30+
:shadow: md
3031
:class-header: bg-light
32+
:link-type: doc
3133

3234
**For Users - Quick Start**
3335

@@ -47,8 +49,10 @@ Get started immediately with a pre-configured environment:
4749
:::
4850

4951
:::{grid-item-card} 🐍 Local Pip/Conda Installation
50-
:link: ./setup/1a_install_conda.md
52+
:link: setup/1a_install_conda
53+
:shadow: md
5154
:class-header: bg-light
55+
:link-type: doc
5256

5357
**For Users - Custom Setup**
5458

@@ -68,8 +72,10 @@ Install PyRIT directly on your machine:
6872
:::
6973

7074
:::{grid-item-card} 🐋 DevContainers in VS Code
71-
:link: ./contributing/1b_install_devcontainers.md
75+
:link: contributing/1b_install_devcontainers
76+
:shadow: md
7277
:class-header: bg-light
78+
:link-type: doc
7379

7480
**For Contributors**
7581

@@ -89,8 +95,10 @@ Standardized development environment:
8995
:::
9096

9197
:::{grid-item-card} 🐍 Local Conda Development
92-
:link: ./contributing/1a_install_conda.md#local-installation-with-condapython
98+
:link: contributing/1a_install_conda
99+
:shadow: md
93100
:class-header: bg-light
101+
:link-type: doc
94102

95103
**For Contributors - Custom Dev Setup**
96104

@@ -119,72 +127,90 @@ There is no single way to read the documentation, and it's perfectly fine to jum
119127
:gutter: 2
120128

121129
:::{grid-item-card} 1️⃣ Cookbooks
122-
:link: ./cookbooks/README.md
130+
:link: cookbooks/README
131+
:shadow: md
123132
:class-header: bg-light
133+
:link-type: doc
124134

125135
This provides an overview of PyRIT in action. A great place to see practical examples and get started quickly.
126136

127137
:::
128138

129139
:::{grid-item-card} 2️⃣ Architecture
130-
:link: ./code/architecture.md
140+
:link: code/architecture
141+
:shadow: md
131142
:class-header: bg-light
143+
:link-type: doc
132144

133145
High-level overview of all PyRIT components. Understanding any single component is easier with knowledge of the others.
134146

135147
:::
136148

137149
:::{grid-item-card} 3️⃣ Attacks
138-
:link: ./code/executor/0_executor.md
150+
:link: code/executor/0_executor
151+
:shadow: md
139152
:class-header: bg-light
153+
:link-type: doc
140154

141155
Top-level components implementing end-to-end attack techniques. Shows how all PyRIT components fit together.
142156

143157
:::
144158

145159
:::{grid-item-card} 4️⃣ Datasets
146-
:link: ./code/datasets/0_dataset.md
160+
:link: code/datasets/0_dataset
161+
:shadow: md
147162
:class-header: bg-light
163+
:link-type: doc
148164

149165
Building attacks using seed prompts and fetching datasets. The foundation for creating test scenarios.
150166

151167
:::
152168

153169
:::{grid-item-card} 5️⃣ Targets
154-
:link: ./code/targets/0_prompt_targets.md
170+
:link: code/targets/0_prompt_targets
171+
:shadow: md
155172
:class-header: bg-light
173+
:link-type: doc
156174

157175
Endpoints that PyRIT sends prompts to. Essential for nearly any PyRIT scenario - learn what targets are available.
158176

159177
:::
160178

161179
:::{grid-item-card} 6️⃣ Converters
162-
:link: ./code/converters/0_converters.ipynb
180+
:link: code/converters/0_converters
181+
:shadow: md
163182
:class-header: bg-light
183+
:link-type: doc
164184

165185
Transform prompts from one format to another. One of the most powerful capabilities within PyRIT.
166186

167187
:::
168188

169189
:::{grid-item-card} 7️⃣ Scorers
170-
:link: ./code/scoring/0_scoring.md
190+
:link: code/scoring/0_scoring
191+
:shadow: md
171192
:class-header: bg-light
193+
:link-type: doc
172194

173195
How PyRIT makes decisions and records output. Essential for evaluating AI system responses.
174196

175197
:::
176198

177199
:::{grid-item-card} 8️⃣ Memory
178-
:link: ./code/memory/0_memory.md
200+
:link: code/memory/0_memory
201+
:shadow: md
179202
:class-header: bg-light
203+
:link-type: doc
180204

181205
How PyRIT components communicate state information. Understand the data flow and storage mechanisms.
182206

183207
:::
184208

185209
:::{grid-item-card} 9️⃣ Auxiliary Attacks (Optional)
186-
:link: ./code/auxiliary_attacks/0_auxiliary_attacks.ipynb
210+
:link: code/auxiliary_attacks/0_auxiliary_attacks
211+
:shadow: md
187212
:class-header: bg-light
213+
:link-type: doc
188214

189215
Attacks and techniques that don't fit into core PyRIT functionality. Advanced and specialized methods.
190216

@@ -198,16 +224,20 @@ Attacks and techniques that don't fit into core PyRIT functionality. Advanced an
198224
:gutter: 2
199225

200226
:::{grid-item-card} 🚀 Deployment
201-
:link: ./deployment/README.md
227+
:link: deployment/README
228+
:shadow: md
202229
:class-header: bg-light
230+
:link-type: doc
203231

204232
Code to download, deploy, and score open-source models (such as Hugging Face) on Azure.
205233

206234
:::
207235

208236
:::{grid-item-card} 📰 Blog
209-
:link: ./blog/README.md
237+
:link: blog/README
238+
:shadow: md
210239
:class-header: bg-light
240+
:link-type: doc
211241

212242
Notable new changes and updates. Stay current with the latest PyRIT developments.
213243

pyrit/models/storage_io.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ async def read_file(self, path: Union[Path, str]) -> bytes:
215215
Asynchronously reads the content of a file (blob) from Azure Blob Storage.
216216
217217
If the provided `path` is a full URL
218-
(e.g., "https://<Azure STorage Account>.blob.core.windows.net/<container name>/dir1/dir2/sample.png"),
218+
(e.g., "https://account.blob.core.windows.net/container/dir1/dir2/sample.png"),
219219
it extracts the relative blob path (e.g., "dir1/dir2/sample.png") to correctly access the blob.
220220
If a relative path is provided, it will use it as-is.
221221

tests/unit/score/test_true_false_composite_scorer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,6 @@ async def test_composite_scorer_with_task(mock_request, true_scorer):
164164

165165

166166
def test_composite_scorer_empty_scorers_list():
167-
"""Test that CompositeScorer raises an exception when given an empty list of scorers."""
167+
"""Test that TrueFalseCompositeScorer raises an exception when given an empty list of scorers."""
168168
with pytest.raises(ValueError, match="At least one scorer must be provided"):
169169
TrueFalseCompositeScorer(aggregator=TrueFalseScoreAggregator.AND, scorers=[])

0 commit comments

Comments
 (0)