Skip to content

Commit 8c3bd66

Browse files
committed
Add 7.1.0 post blog announcement.
1 parent 9cace08 commit 8c3bd66

17 files changed

+667
-4
lines changed

sites/highlight.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,25 @@ def shell_to_html(snippet: str) -> str:
161161
output = output.replace("\x1b[1m", '<span class="bold">')
162162
output = output.replace("\x1b[0m", "</span>")
163163
output = output.replace("\x1b[31m", '<span class="red">')
164+
output = output.replace("\x1b[0;31m", '<span class="red">')
164165
output = output.replace("\x1b[32m", '<span class="green">')
166+
output = output.replace("\x1b[0;32m", '<span class="green">')
167+
output = output.replace("\x1b[33m", '<span class="yellow">')
168+
output = output.replace("\x1b[0;33m", '<span class="yellow">')
165169
output = output.replace("\x1b[34m", '<span class="blue">')
170+
output = output.replace("\x1b[0;34m", '<span class="blue">')
166171
output = output.replace("\x1b[35m", '<span class="magenta">')
172+
output = output.replace("\x1b[0;35m", '<span class="magenta">')
173+
output = output.replace("\x1b[36m", '<span class="cyan">')
174+
output = output.replace("\x1b[0;36m", '<span class="cyan">')
167175
output = output.replace("\x1b[90m", '<span class="gray">')
168-
output = output.replace("\x1b[1;36m", '<span class="bright-cyan">')
176+
output = output.replace("\x1b[1;36m", '<span class="bold bright-cyan">')
169177
output = output.replace("\x1b[1;31m", '<span class="bright-red">')
170178
output = output.replace("\x1b[1;32m", '<span class="bright-green">')
171179
output = output.replace("\x1b[1;34m", '<span class="bright-blue">')
172180
output = output.replace("\x1b[1;35m", '<span class="bright-magenta">')
173181
output = output.replace("\x1b[1;36m", '<span class="bright-cyan">')
182+
output = output.replace("\x1b[1;39m", '<span class="bold">')
174183
return output
175184

176185

sites/hurl.dev/_posts/2025-07-30-announcing-hurl-7.0.0.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ __That's all for today!__
218218
There are a lot of other improvements with Hurl 7.0.0 and also a lot of bug fixes, you can check the complete list of
219219
enhancements and bug fixes [in our release note].
220220

221-
If you like Hurl, don't hesitate to [support us with a star on GitHub] and share it on [𝕏] and [Bluesky]!
221+
If you like Hurl, don't hesitate to [support us with a star on GitHub] and share it on [𝕏 / Twitter] and [Bluesky]!
222222

223223
We'll be happy to hear from you, either for enhancement requests or for sharing your success story using Hurl!
224224

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
---
2+
title: Hurl 7.1.0, the Pretty Edition
3+
layout: blog
4+
section: Blog
5+
permalink: /blog/:year/:month/:day/:title.html
6+
---
7+
8+
# {{ page.title }}
9+
10+
<div class="blog-post-date">{{ page.date | date: "%b. %d, %Y" }}</div>
11+
12+
<p>
13+
<picture>
14+
<source srcset="{{ '/assets/img/curl-plus-jq.avif' | prepend:site.baseurl }}" type="image/avif">
15+
<source srcset="{{ '/assets/img/curl-plus-jq.webp' | prepend:site.baseurl }}" type="image/webp">
16+
<source srcset="{{ '/assets/img/curl-plus-jq.png' | prepend:site.baseurl }}" type="image/png">
17+
<img class="u-drop-shadow u-border" src="{{ '/assets/img/curl-plus-jq.png' | prepend:site.baseurl }}" width="100%" alt="Hurl 7.1.0, the pretty edition"/>
18+
</picture>
19+
</p>
20+
21+
The Hurl team is thrilled to announce [Hurl 7.1.0] <picture><source srcset="{{ '/assets/img/emoji-rocket.avif' | prepend:site.baseurl }}" type="image/avif"><source srcset="{{ '/assets/img/emoji-rocket.webp' | prepend:site.baseurl }}" type="image/webp"><source srcset="{{ '/assets/img/emoji-rocket.png' | prepend:site.baseurl }}" type="image/png"><img class="emoji" src="{{ '/assets/img/emoji-rocket.png' | prepend:site.baseurl }}" width="20" height="20" alt="Partying face emoji"></picture> !
22+
23+
[Hurl] is a command line tool powered by [curl], that runs HTTP requests defined in a simple plain text format:
24+
25+
```hurl
26+
GET https://example.org/api/tests/4567
27+
HTTP 200
28+
[Asserts]
29+
jsonpath "$.status" == "RUNNING" # Check the status code
30+
jsonpath "$.tests" count == 25 # Check the number of items
31+
jsonpath "$.id" matches /\d{4}/ # Check the format of the id
32+
33+
# Some tests on the HTTP layer:
34+
GET https://example.org
35+
HTTP 200
36+
[Asserts]
37+
header "x-foo" contains "bar"
38+
certificate "Expire-Date" daysAfterNow > 15
39+
ip == "2001:0db8:85a3:0000:0000:8a2e:0370:733"
40+
```
41+
42+
## What’s New in This Release
43+
44+
- [JSON Response Automatic prettifying](#json-response-automatic-prettifying)
45+
- [New Predicates `isObject`, `isList`](#new-predicates-isobject-islist)
46+
- [New Supported curl options](#new-supported-curl-options)
47+
- [New Filters `utf8Decode`, `utf8Encode`](#new-filters-utf8decode-utf8encode)
48+
49+
## JSON Response Automatic prettifying
50+
51+
Hurl supports two running modes:
52+
53+
- the "default" one
54+
55+
hurl books.hurl
56+
57+
- the test-oriented one
58+
59+
hurl --test books.hurl
60+
61+
In both cases, asserts and captures are run, the difference between these two modes is about what is written on standard output and standard error.
62+
63+
With default mode, the last HTTP response is written on standard output, as received from the network. You can use this mode
64+
when you want to get data from a server, and you need a kind of workflow to get it. It's like curl, but it's easier to chain requests and pass
65+
data from response to request (like [OAuth], [CSRF] etc...).
66+
67+
With test mode, no response is written on standard output, but the display is tweaked for a test run, with a succinct summary:
68+
69+
```
70+
$ hurl --test *.hurl
71+
...
72+
--------------------------------------------------------------------------------
73+
Executed files: 100
74+
Executed requests: 100 (1612.9/s)
75+
Succeeded files: 100 (100.0%)
76+
Failed files: 0 (0.0%)
77+
Duration: 62 ms (0h:0m:0s:62ms)
78+
```
79+
80+
Starting with Hurl 7.1.0, we've improved the default mode, when response is displayed on standard output.
81+
If the response is a JSON, we're displaying now a pretty, colored, indented version of it:
82+
83+
84+
```shell
85+
$ hurl books.hurl
86+
{
87+
"store": {
88+
"book": [
89+
{
90+
"category": "reference",
91+
"author": "Nigel Rees",
92+
"title": "Sayings of the Century",
93+
"price": 8.95
94+
},
95+
{
96+
"category": "fiction",
97+
"author": "J. R. R. Tolkien",
98+
"title": "The Lord of the Rings",
99+
"isbn": "0-395-19395-8",
100+
"price": 22.99
101+
}
102+
],
103+
"bicycle": {
104+
"color": "red",
105+
"price": 399
106+
}
107+
}
108+
}
109+
```
110+
111+
Before Hurl 7.1.0, you can achieve the same result with piping to [jq](https://jqlang.org):
112+
113+
```shell
114+
$ hurl foo.hurl | jq
115+
```
116+
117+
Now, you can just run:
118+
119+
```shell
120+
$ hurl foo.hurl
121+
```
122+
123+
and the response will be pretty printed. This improvement adresses one of our top-voted issues
124+
since 2023, so we're very happy to have implemented it in this release!
125+
126+
<p>
127+
<picture>
128+
<source srcset="{{ '/assets/img/top-voted-issue.avif' | prepend:site.baseurl }}" type="image/avif">
129+
<source srcset="{{ '/assets/img/top-voted-issue.webp' | prepend:site.baseurl }}" type="image/webp">
130+
<source srcset="{{ '/assets/img/top-voted-issue.png' | prepend:site.baseurl }}" type="image/png">
131+
<img class="u-drop-shadow u-border" src="{{ '/assets/img/top-voted-issue.png' | prepend:site.baseurl }}" width="100%" alt="Top-voted issue"/>
132+
</picture>
133+
</p>
134+
135+
### Under the hood
136+
137+
Prettifying JSON response is optimized to consume the fewest possible ressources, so there is no performance
138+
hits for a normal usage, even with big responses.
139+
140+
Some implementation details worth noting:
141+
142+
#### No Unicode escape transformation
143+
144+
In JSON, characters can be written directly using UTF-8 or using Unicode escapes. For instance, a string containing an emoji can be written like this:
145+
146+
```json
147+
{
148+
"an emoji with UTF-8": "🏝️",
149+
"an emoji with Unicode escapes": "\uD83C\uDFDD"
150+
}
151+
```
152+
153+
The two strings represent exactly the same Unicode char. With this input, different program will prettify
154+
JSON differently. Let's take [jq](https://jqlang.org), the de facto standard to manipulate JSON data, and [HTTPie](https://httpie.io),
155+
one of the best HTTP client.
156+
157+
jq, by default, will normalize Unicode escapes, rendering Unicode escapes to their UTF-8 representation:
158+
159+
```shell
160+
$ cat island.json | jq
161+
{
162+
"an emoji with UTF-8": "🏝️",
163+
"an emoji with Unicode escapes": "🏝"
164+
}
165+
```
166+
167+
HTTPie renders also Unicode escapes:
168+
169+
```shell
170+
$ http http://localhost:8000/island.json
171+
HTTP/1.0 200 OK
172+
Content-Length: 83
173+
...
174+
175+
{
176+
"an emoji with UTF-8": "🏝️",
177+
"an emoji with Unicode escapes": "🏝"
178+
}
179+
```
180+
181+
Contrary to jq and HTTPie, Hurl will minimally prettify JSON, just coloring Unicode escapes:
182+
183+
```shell
184+
$ echo 'GET http://localhost:8000/island.json' | hurl
185+
{
186+
"an emoji with UTF-8": "🏝️",
187+
"an emoji with Unicode escapes": "\uD83C\uDFDD"
188+
}
189+
```
190+
191+
The idea is to add colors and indentations, but leave the input source "unchanged" otherwise.
192+
193+
### Numbers are left untouched
194+
195+
In JSON, float numbers can be represented in different ways, for instance 1,234 can be written `1234`, `1.234e3` or
196+
even `1.234E+3`.
197+
198+
Given this input:
199+
200+
```
201+
{
202+
"scientific_notation_positive": 1.23e4,
203+
"scientific_notation_negative": 6.02e-3,
204+
"scientific_uppercase_E": 9.81E+2
205+
}
206+
```
207+
208+
jq normalizes numbers, keeping fields order: `1.23e4` becomes `1.23E+4`, `6.02e-3` becomes `0.00602` and `9.81E+2`
209+
becomes `981`.
210+
211+
```shell
212+
$ cat numbers.json | jq
213+
{
214+
"scientific_notation_positive": 1.23E+4,
215+
"scientific_notation_negative": 0.00602,
216+
"scientific_uppercase_E": 981
217+
}
218+
```
219+
220+
HTTPie normalizes numbers differently from jq, and also re-orders field (`scientific_notation_negative` is now before
221+
`scientific_notation_positive`):
222+
223+
```shell
224+
$ http http://localhost:8000/numbers.json
225+
HTTP/1.0 200 OK
226+
Content-Length: 83
227+
...
228+
229+
{
230+
"scientific_notation_negative": 0.00602,
231+
"scientific_notation_positive": 12300.0,
232+
"scientific_uppercase_E": 981.0
233+
}
234+
```
235+
236+
With Hurl, once again, we've chosen not to normalize anything and just augment user input with colors and spacing:
237+
238+
```shell
239+
$ echo 'GET http://localhost:8000/numbers.json'
240+
{
241+
"scientific_notation_positive": 1.23e4,
242+
"scientific_notation_negative": 6.02e-3,
243+
"scientific_uppercase_E": 9.81E+2
244+
}
245+
```
246+
247+
Which is exactly the JSON response, minus color and spaces:
248+
249+
```
250+
{
251+
"scientific_notation_positive": 1.23e4,
252+
"scientific_notation_negative": 6.02e-3,
253+
"scientific_uppercase_E": 9.81E+2
254+
}
255+
```
256+
257+
258+
If Hurl pretty printing is too minimalist for you, you can still pipe Hurl output through `jq` for instance and it will work.
259+
When Hurl's output is redirected to a file or through a pipe, all pretty printing is disable, so tools that expect a plain
260+
JSON response will work as usual.
261+
262+
## New Predicates isObject, isList
263+
264+
[Predicates]({% link _docs/asserting-response.md %}#predicates) are used to check HTTP responses:
265+
266+
```hurl
267+
GET http://httpbin.org/json
268+
HTTP 200
269+
[Asserts]
270+
jsonpath "$.slideshow.author" startsWith "Yours Truly"
271+
jsonpath "$.slideshow.slides[0].title" contains "Wonder"
272+
jsonpath "$.slideshow.slides" count == 2
273+
jsonpath "$.slideshow.date" != null
274+
```
275+
276+
Two new predicates are introduced with 7.1.0:
277+
278+
- `isObject`: check is a value is an object (when working with JSONPath for instance)
279+
- `isList`: check if a value is an array
280+
281+
```hurl
282+
GET https://example.org/order
283+
HTTP 200
284+
[Asserts]
285+
jsonpath "$.userInfo" isObject
286+
jsonpath "$.userInfo.books" isList
287+
```
288+
289+
## New Supported curl options
290+
291+
Introduced in [Hurl 7.0.0], [`--ntlm`]({% link _docs/manual.md %}#ntlm) and [`--negotiate`]({% link _docs/manual.md %}#negotiate)
292+
curl options can now be set per request:
293+
294+
```hurl
295+
GET http://petfactory.com/sharepoint
296+
[Options]
297+
user: alice:1234
298+
ntlm: true
299+
HTTP 200
300+
```
301+
302+
## New Filters utf8Decode, utf8Encode
303+
304+
[Filters]({% link _docs/filters.md %}) allow to transform data extracted from HTTP responses. In the following sample, `replaceRegex`, `split`,
305+
`count` and `nth` are filters that process input; they can be chained to transform values in asserts and captures:
306+
307+
```hurl
308+
GET https://example.org/api
309+
HTTP 200
310+
[Captures]
311+
name: jsonpath "$.user.id" replaceRegex /\d/ "x"
312+
[Asserts]
313+
header "x-servers" split "," count == 2
314+
header "x-servers" split "," nth 0 == "rec1"
315+
header "x-servers" split "," nth 1 == "rec3"
316+
jsonpath "$.books" count == 12
317+
```
318+
319+
In Hurl 7.1.0, we've added new filters `utf8Decode` and `utf8Encode` to encode and decode from bytes to string. In the next
320+
example, we get bytes from a Base64 encoded string, then decode these bytes to a string using UTF-8 encoding:
321+
322+
```hurl
323+
GET https://example.org/messages
324+
HTTP 200
325+
[Asserts]
326+
# From a Base64 string to UTF-8 bytes to final string
327+
jsonpath "$.bytesInBase64" base64Decode utf8Decode == "Hello World"
328+
```
329+
330+
## __That's all for today!__
331+
332+
There are a lot of other improvements with Hurl 7.1.0 and also a lot of bug fixes. Among other things, we have added
333+
the following features to 7.1.0:
334+
335+
- new ways to add [secrets]({% link _docs/templates.md %}#secrets)
336+
- by setting environment variables `HURL_SECRET_my_secret`
337+
- using secrets files with [`--secrets-file`]({% link _docs/manual.md %}#secrets-file)
338+
- improve `--test` progress bar to display retry status
339+
- small improvments to HTML report
340+
341+
You can check the complete list of enhancements and bug fixes [in our release note].
342+
343+
If you like Hurl, don't hesitate to [support us with a star on GitHub] and share it on [𝕏] and [Bluesky]!
344+
345+
We'll be happy to hear from you, either for enhancement requests or for sharing your success story using Hurl!
346+
347+
348+
[Hurl]: https://hurl.dev
349+
[Hurl 7.1.0]: https://github.com/Orange-OpenSource/hurl/releases/tag/7.1.0
350+
[curl]: https://curl.se
351+
[in our release note]: https://github.com/Orange-OpenSource/hurl/releases/tag/7.1.0
352+
[Bluesky]: https://bsky.app/profile/hurldev.bsky.social
353+
[support us with a star on GitHub]: https://github.com/Orange-OpenSource/hurl/stargazers
354+
[OAuth]: https://en.wikipedia.org/wiki/OAuth
355+
[CSRF]: https://en.wikipedia.org/wiki/Cross-site_request_forgery
356+
[Hurl 7.0.0]: https://hurl.dev/blog/2025/07/30/announcing-hurl-7.0.0.html
357+
[𝕏]: https://x.com/HurlDev
358+

0 commit comments

Comments
 (0)