Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d0580c4fd | ||
|
|
ed3bd75367 | ||
|
|
ed0a282a52 | ||
|
|
e4e0a534ff | ||
|
|
e35ee2d71a | ||
|
|
1d9e505432 | ||
|
|
ddb0f17886 | ||
|
|
a8a8b743f3 | ||
|
|
a487bddb09 | ||
|
|
e3a37c95c1 | ||
|
|
9bb461e12f | ||
|
|
32193a0e6d | ||
|
|
155c4a204a | ||
|
|
491137f944 | ||
|
|
48cd33be22 | ||
|
|
2fae9bc723 | ||
|
|
bfc2ebec81 | ||
|
|
e01c39b4b8 | ||
|
|
aa93e894a6 |
@@ -1,9 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Use 'AM' or 'PM' (preceded by a space)."
|
|
||||||
link: "https://developers.google.com/style/word-list"
|
|
||||||
level: error
|
|
||||||
nonword: true
|
|
||||||
tokens:
|
|
||||||
- '\d{1,2}[AP]M\b'
|
|
||||||
- '\d{1,2} ?[ap]m\b'
|
|
||||||
- '\d{1,2} ?[aApP]\.[mM]\.'
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
extends: conditional
|
|
||||||
message: "Spell out '%s', if it's unfamiliar to the audience."
|
|
||||||
link: 'https://developers.google.com/style/abbreviations'
|
|
||||||
level: suggestion
|
|
||||||
ignorecase: false
|
|
||||||
# Ensures that the existence of 'first' implies the existence of 'second'.
|
|
||||||
first: '\b([A-Z]{3,5})\b'
|
|
||||||
second: '(?:\b[A-Z][a-z]+ )+\(([A-Z]{3,5})\)'
|
|
||||||
# ... with the exception of these:
|
|
||||||
exceptions:
|
|
||||||
- API
|
|
||||||
- ASP
|
|
||||||
- CLI
|
|
||||||
- CPU
|
|
||||||
- CSS
|
|
||||||
- CSV
|
|
||||||
- DEBUG
|
|
||||||
- DOM
|
|
||||||
- DPI
|
|
||||||
- FAQ
|
|
||||||
- GCC
|
|
||||||
- GDB
|
|
||||||
- GET
|
|
||||||
- GPU
|
|
||||||
- GTK
|
|
||||||
- GUI
|
|
||||||
- HTML
|
|
||||||
- HTTP
|
|
||||||
- HTTPS
|
|
||||||
- IDE
|
|
||||||
- JAR
|
|
||||||
- JSON
|
|
||||||
- JSX
|
|
||||||
- LESS
|
|
||||||
- LLDB
|
|
||||||
- NET
|
|
||||||
- NOTE
|
|
||||||
- NVDA
|
|
||||||
- OSS
|
|
||||||
- PATH
|
|
||||||
- PDF
|
|
||||||
- PHP
|
|
||||||
- POST
|
|
||||||
- RAM
|
|
||||||
- REPL
|
|
||||||
- RSA
|
|
||||||
- SCM
|
|
||||||
- SCSS
|
|
||||||
- SDK
|
|
||||||
- SQL
|
|
||||||
- SSH
|
|
||||||
- SSL
|
|
||||||
- SVG
|
|
||||||
- TBD
|
|
||||||
- TCP
|
|
||||||
- TODO
|
|
||||||
- URI
|
|
||||||
- URL
|
|
||||||
- USB
|
|
||||||
- UTF
|
|
||||||
- XML
|
|
||||||
- XSS
|
|
||||||
- YAML
|
|
||||||
- ZIP
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "'%s' should be in lowercase."
|
|
||||||
link: 'https://developers.google.com/style/colons'
|
|
||||||
nonword: true
|
|
||||||
level: warning
|
|
||||||
scope: sentence
|
|
||||||
tokens:
|
|
||||||
- '(?<!:[^ ]+?):\s[A-Z]'
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
extends: substitution
|
|
||||||
message: "Use '%s' instead of '%s'."
|
|
||||||
link: 'https://developers.google.com/style/contractions'
|
|
||||||
level: suggestion
|
|
||||||
ignorecase: true
|
|
||||||
action:
|
|
||||||
name: replace
|
|
||||||
swap:
|
|
||||||
are not: aren't
|
|
||||||
cannot: can't
|
|
||||||
could not: couldn't
|
|
||||||
did not: didn't
|
|
||||||
do not: don't
|
|
||||||
does not: doesn't
|
|
||||||
has not: hasn't
|
|
||||||
have not: haven't
|
|
||||||
how is: how's
|
|
||||||
is not: isn't
|
|
||||||
it is: it's
|
|
||||||
should not: shouldn't
|
|
||||||
that is: that's
|
|
||||||
they are: they're
|
|
||||||
was not: wasn't
|
|
||||||
we are: we're
|
|
||||||
we have: we've
|
|
||||||
were not: weren't
|
|
||||||
what is: what's
|
|
||||||
when is: when's
|
|
||||||
where is: where's
|
|
||||||
will not: won't
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Use 'July 31, 2016' format, not '%s'."
|
|
||||||
link: 'https://developers.google.com/style/dates-times'
|
|
||||||
ignorecase: true
|
|
||||||
level: error
|
|
||||||
nonword: true
|
|
||||||
tokens:
|
|
||||||
- '\d{1,2}(?:\.|/)\d{1,2}(?:\.|/)\d{4}'
|
|
||||||
- '\d{1,2} (?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)|May|Jun(?:e)|Jul(?:y)|Aug(?:ust)|Sep(?:tember)?|Oct(?:ober)|Nov(?:ember)?|Dec(?:ember)?) \d{4}'
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "In general, don't use an ellipsis."
|
|
||||||
link: 'https://developers.google.com/style/ellipses'
|
|
||||||
nonword: true
|
|
||||||
level: warning
|
|
||||||
action:
|
|
||||||
name: remove
|
|
||||||
tokens:
|
|
||||||
- '\.\.\.'
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Don't put a space before or after a dash."
|
|
||||||
link: "https://developers.google.com/style/dashes"
|
|
||||||
nonword: true
|
|
||||||
level: error
|
|
||||||
action:
|
|
||||||
name: edit
|
|
||||||
params:
|
|
||||||
- trim
|
|
||||||
- " "
|
|
||||||
tokens:
|
|
||||||
- '\s[—–]\s'
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Don't use exclamation points in text."
|
|
||||||
link: "https://developers.google.com/style/exclamation-points"
|
|
||||||
nonword: true
|
|
||||||
level: error
|
|
||||||
action:
|
|
||||||
name: edit
|
|
||||||
params:
|
|
||||||
- trim_right
|
|
||||||
- "!"
|
|
||||||
tokens:
|
|
||||||
- '\w+!(?:\s|$)'
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Avoid first-person pronouns such as '%s'."
|
|
||||||
link: 'https://developers.google.com/style/pronouns#personal-pronouns'
|
|
||||||
ignorecase: true
|
|
||||||
level: warning
|
|
||||||
nonword: true
|
|
||||||
tokens:
|
|
||||||
- (?:^|\s)I\s
|
|
||||||
- (?:^|\s)I,\s
|
|
||||||
- \bI'm\b
|
|
||||||
- \bme\b
|
|
||||||
- \bmy\b
|
|
||||||
- \bmine\b
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Don't use '%s' as a gender-neutral pronoun."
|
|
||||||
link: 'https://developers.google.com/style/pronouns#gender-neutral-pronouns'
|
|
||||||
level: error
|
|
||||||
ignorecase: true
|
|
||||||
tokens:
|
|
||||||
- he/she
|
|
||||||
- s/he
|
|
||||||
- \(s\)he
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
extends: substitution
|
|
||||||
message: "Consider using '%s' instead of '%s'."
|
|
||||||
ignorecase: true
|
|
||||||
link: "https://developers.google.com/style/inclusive-documentation"
|
|
||||||
level: error
|
|
||||||
action:
|
|
||||||
name: replace
|
|
||||||
swap:
|
|
||||||
(?:alumna|alumnus): graduate
|
|
||||||
(?:alumnae|alumni): graduates
|
|
||||||
air(?:m[ae]n|wom[ae]n): pilot(s)
|
|
||||||
anchor(?:m[ae]n|wom[ae]n): anchor(s)
|
|
||||||
authoress: author
|
|
||||||
camera(?:m[ae]n|wom[ae]n): camera operator(s)
|
|
||||||
door(?:m[ae]|wom[ae]n): concierge(s)
|
|
||||||
draft(?:m[ae]n|wom[ae]n): drafter(s)
|
|
||||||
fire(?:m[ae]n|wom[ae]n): firefighter(s)
|
|
||||||
fisher(?:m[ae]n|wom[ae]n): fisher(s)
|
|
||||||
fresh(?:m[ae]n|wom[ae]n): first-year student(s)
|
|
||||||
garbage(?:m[ae]n|wom[ae]n): waste collector(s)
|
|
||||||
lady lawyer: lawyer
|
|
||||||
ladylike: courteous
|
|
||||||
mail(?:m[ae]n|wom[ae]n): mail carriers
|
|
||||||
man and wife: husband and wife
|
|
||||||
man enough: strong enough
|
|
||||||
mankind: human kind|humanity
|
|
||||||
manmade: manufactured
|
|
||||||
manpower: personnel
|
|
||||||
middle(?:m[ae]n|wom[ae]n): intermediary
|
|
||||||
news(?:m[ae]n|wom[ae]n): journalist(s)
|
|
||||||
ombuds(?:man|woman): ombuds
|
|
||||||
oneupmanship: upstaging
|
|
||||||
poetess: poet
|
|
||||||
police(?:m[ae]n|wom[ae]n): police officer(s)
|
|
||||||
repair(?:m[ae]n|wom[ae]n): technician(s)
|
|
||||||
sales(?:m[ae]n|wom[ae]n): salesperson or sales people
|
|
||||||
service(?:m[ae]n|wom[ae]n): soldier(s)
|
|
||||||
steward(?:ess)?: flight attendant
|
|
||||||
tribes(?:m[ae]n|wom[ae]n): tribe member(s)
|
|
||||||
waitress: waiter
|
|
||||||
woman doctor: doctor
|
|
||||||
woman scientist[s]?: scientist(s)
|
|
||||||
work(?:m[ae]n|wom[ae]n): worker(s)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Don't put a period at the end of a heading."
|
|
||||||
link: "https://developers.google.com/style/capitalization#capitalization-in-titles-and-headings"
|
|
||||||
nonword: true
|
|
||||||
level: warning
|
|
||||||
scope: heading
|
|
||||||
action:
|
|
||||||
name: edit
|
|
||||||
params:
|
|
||||||
- trim_right
|
|
||||||
- "."
|
|
||||||
tokens:
|
|
||||||
- '[a-z0-9][.]\s*$'
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
extends: capitalization
|
|
||||||
message: "'%s' should use sentence-style capitalization."
|
|
||||||
link: "https://developers.google.com/style/capitalization#capitalization-in-titles-and-headings"
|
|
||||||
level: warning
|
|
||||||
scope: heading
|
|
||||||
match: $sentence
|
|
||||||
indicators:
|
|
||||||
- ":"
|
|
||||||
exceptions:
|
|
||||||
- Azure
|
|
||||||
- CLI
|
|
||||||
- Cosmos
|
|
||||||
- Docker
|
|
||||||
- Emmet
|
|
||||||
- gRPC
|
|
||||||
- I
|
|
||||||
- Kubernetes
|
|
||||||
- Linux
|
|
||||||
- macOS
|
|
||||||
- Marketplace
|
|
||||||
- MongoDB
|
|
||||||
- REPL
|
|
||||||
- Studio
|
|
||||||
- TypeScript
|
|
||||||
- URLs
|
|
||||||
- Visual
|
|
||||||
- VS
|
|
||||||
- Windows
|
|
||||||
- JSON
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
extends: substitution
|
|
||||||
message: "Use '%s' instead of '%s'."
|
|
||||||
link: 'https://developers.google.com/style/abbreviations'
|
|
||||||
ignorecase: true
|
|
||||||
level: error
|
|
||||||
nonword: true
|
|
||||||
action:
|
|
||||||
name: replace
|
|
||||||
swap:
|
|
||||||
'\b(?:eg|e\.g\.)(?=[\s,;])': for example
|
|
||||||
'\b(?:ie|i\.e\.)(?=[\s,;])': that is
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "'%s' doesn't need a hyphen."
|
|
||||||
link: "https://developers.google.com/style/hyphens"
|
|
||||||
level: error
|
|
||||||
ignorecase: false
|
|
||||||
nonword: true
|
|
||||||
action:
|
|
||||||
name: edit
|
|
||||||
params:
|
|
||||||
- regex
|
|
||||||
- "-"
|
|
||||||
- " "
|
|
||||||
tokens:
|
|
||||||
- '\b[^\s-]+ly-\w+\b'
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Don't use plurals in parentheses such as in '%s'."
|
|
||||||
link: "https://developers.google.com/style/plurals-parentheses"
|
|
||||||
level: error
|
|
||||||
nonword: true
|
|
||||||
action:
|
|
||||||
name: edit
|
|
||||||
params:
|
|
||||||
- trim_right
|
|
||||||
- "(s)"
|
|
||||||
tokens:
|
|
||||||
- '\b\w+\(s\)'
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Spell out all ordinal numbers ('%s') in text."
|
|
||||||
link: 'https://developers.google.com/style/numbers'
|
|
||||||
level: error
|
|
||||||
nonword: true
|
|
||||||
tokens:
|
|
||||||
- \d+(?:st|nd|rd|th)
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Use the Oxford comma in '%s'."
|
|
||||||
link: 'https://developers.google.com/style/commas'
|
|
||||||
scope: sentence
|
|
||||||
level: warning
|
|
||||||
tokens:
|
|
||||||
- '(?:[^,]+,){1,}\s\w+\s(?:and|or)'
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Use parentheses judiciously."
|
|
||||||
link: 'https://developers.google.com/style/parentheses'
|
|
||||||
nonword: true
|
|
||||||
level: suggestion
|
|
||||||
tokens:
|
|
||||||
- '\(.+\)'
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
link: 'https://developers.google.com/style/voice'
|
|
||||||
message: "In general, use active voice instead of passive voice ('%s')."
|
|
||||||
ignorecase: true
|
|
||||||
level: suggestion
|
|
||||||
raw:
|
|
||||||
- \b(am|are|were|being|is|been|was|be)\b\s*
|
|
||||||
tokens:
|
|
||||||
- '[\w]+ed'
|
|
||||||
- awoken
|
|
||||||
- beat
|
|
||||||
- become
|
|
||||||
- been
|
|
||||||
- begun
|
|
||||||
- bent
|
|
||||||
- beset
|
|
||||||
- bet
|
|
||||||
- bid
|
|
||||||
- bidden
|
|
||||||
- bitten
|
|
||||||
- bled
|
|
||||||
- blown
|
|
||||||
- born
|
|
||||||
- bought
|
|
||||||
- bound
|
|
||||||
- bred
|
|
||||||
- broadcast
|
|
||||||
- broken
|
|
||||||
- brought
|
|
||||||
- built
|
|
||||||
- burnt
|
|
||||||
- burst
|
|
||||||
- cast
|
|
||||||
- caught
|
|
||||||
- chosen
|
|
||||||
- clung
|
|
||||||
- come
|
|
||||||
- cost
|
|
||||||
- crept
|
|
||||||
- cut
|
|
||||||
- dealt
|
|
||||||
- dived
|
|
||||||
- done
|
|
||||||
- drawn
|
|
||||||
- dreamt
|
|
||||||
- driven
|
|
||||||
- drunk
|
|
||||||
- dug
|
|
||||||
- eaten
|
|
||||||
- fallen
|
|
||||||
- fed
|
|
||||||
- felt
|
|
||||||
- fit
|
|
||||||
- fled
|
|
||||||
- flown
|
|
||||||
- flung
|
|
||||||
- forbidden
|
|
||||||
- foregone
|
|
||||||
- forgiven
|
|
||||||
- forgotten
|
|
||||||
- forsaken
|
|
||||||
- fought
|
|
||||||
- found
|
|
||||||
- frozen
|
|
||||||
- given
|
|
||||||
- gone
|
|
||||||
- gotten
|
|
||||||
- ground
|
|
||||||
- grown
|
|
||||||
- heard
|
|
||||||
- held
|
|
||||||
- hidden
|
|
||||||
- hit
|
|
||||||
- hung
|
|
||||||
- hurt
|
|
||||||
- kept
|
|
||||||
- knelt
|
|
||||||
- knit
|
|
||||||
- known
|
|
||||||
- laid
|
|
||||||
- lain
|
|
||||||
- leapt
|
|
||||||
- learnt
|
|
||||||
- led
|
|
||||||
- left
|
|
||||||
- lent
|
|
||||||
- let
|
|
||||||
- lighted
|
|
||||||
- lost
|
|
||||||
- made
|
|
||||||
- meant
|
|
||||||
- met
|
|
||||||
- misspelt
|
|
||||||
- mistaken
|
|
||||||
- mown
|
|
||||||
- overcome
|
|
||||||
- overdone
|
|
||||||
- overtaken
|
|
||||||
- overthrown
|
|
||||||
- paid
|
|
||||||
- pled
|
|
||||||
- proven
|
|
||||||
- put
|
|
||||||
- quit
|
|
||||||
- read
|
|
||||||
- rid
|
|
||||||
- ridden
|
|
||||||
- risen
|
|
||||||
- run
|
|
||||||
- rung
|
|
||||||
- said
|
|
||||||
- sat
|
|
||||||
- sawn
|
|
||||||
- seen
|
|
||||||
- sent
|
|
||||||
- set
|
|
||||||
- sewn
|
|
||||||
- shaken
|
|
||||||
- shaven
|
|
||||||
- shed
|
|
||||||
- shod
|
|
||||||
- shone
|
|
||||||
- shorn
|
|
||||||
- shot
|
|
||||||
- shown
|
|
||||||
- shrunk
|
|
||||||
- shut
|
|
||||||
- slain
|
|
||||||
- slept
|
|
||||||
- slid
|
|
||||||
- slit
|
|
||||||
- slung
|
|
||||||
- smitten
|
|
||||||
- sold
|
|
||||||
- sought
|
|
||||||
- sown
|
|
||||||
- sped
|
|
||||||
- spent
|
|
||||||
- spilt
|
|
||||||
- spit
|
|
||||||
- split
|
|
||||||
- spoken
|
|
||||||
- spread
|
|
||||||
- sprung
|
|
||||||
- spun
|
|
||||||
- stolen
|
|
||||||
- stood
|
|
||||||
- stridden
|
|
||||||
- striven
|
|
||||||
- struck
|
|
||||||
- strung
|
|
||||||
- stuck
|
|
||||||
- stung
|
|
||||||
- stunk
|
|
||||||
- sung
|
|
||||||
- sunk
|
|
||||||
- swept
|
|
||||||
- swollen
|
|
||||||
- sworn
|
|
||||||
- swum
|
|
||||||
- swung
|
|
||||||
- taken
|
|
||||||
- taught
|
|
||||||
- thought
|
|
||||||
- thrived
|
|
||||||
- thrown
|
|
||||||
- thrust
|
|
||||||
- told
|
|
||||||
- torn
|
|
||||||
- trodden
|
|
||||||
- understood
|
|
||||||
- upheld
|
|
||||||
- upset
|
|
||||||
- wed
|
|
||||||
- wept
|
|
||||||
- withheld
|
|
||||||
- withstood
|
|
||||||
- woken
|
|
||||||
- won
|
|
||||||
- worn
|
|
||||||
- wound
|
|
||||||
- woven
|
|
||||||
- written
|
|
||||||
- wrung
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Don't use periods with acronyms or initialisms such as '%s'."
|
|
||||||
link: 'https://developers.google.com/style/abbreviations'
|
|
||||||
level: error
|
|
||||||
nonword: true
|
|
||||||
tokens:
|
|
||||||
- '\b(?:[A-Z]\.){3,}'
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Commas and periods go inside quotation marks."
|
|
||||||
link: 'https://developers.google.com/style/quotation-marks'
|
|
||||||
level: error
|
|
||||||
nonword: true
|
|
||||||
tokens:
|
|
||||||
- '"[^"]+"[.,?]'
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Don't add words such as 'from' or 'between' to describe a range of numbers."
|
|
||||||
link: 'https://developers.google.com/style/hyphens'
|
|
||||||
nonword: true
|
|
||||||
level: warning
|
|
||||||
tokens:
|
|
||||||
- '(?:from|between)\s\d+\s?-\s?\d+'
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Use semicolons judiciously."
|
|
||||||
link: 'https://developers.google.com/style/semicolons'
|
|
||||||
nonword: true
|
|
||||||
scope: sentence
|
|
||||||
level: suggestion
|
|
||||||
tokens:
|
|
||||||
- ';'
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Don't use internet slang abbreviations such as '%s'."
|
|
||||||
link: 'https://developers.google.com/style/abbreviations'
|
|
||||||
ignorecase: true
|
|
||||||
level: error
|
|
||||||
tokens:
|
|
||||||
- 'tl;dr'
|
|
||||||
- ymmv
|
|
||||||
- rtfm
|
|
||||||
- imo
|
|
||||||
- fwiw
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "'%s' should have one space."
|
|
||||||
link: 'https://developers.google.com/style/sentence-spacing'
|
|
||||||
level: error
|
|
||||||
nonword: true
|
|
||||||
action:
|
|
||||||
name: remove
|
|
||||||
tokens:
|
|
||||||
- '[a-z][.?!] {2,}[A-Z]'
|
|
||||||
- '[a-z][.?!][A-Z]'
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "In general, use American spelling instead of '%s'."
|
|
||||||
link: 'https://developers.google.com/style/spelling'
|
|
||||||
ignorecase: true
|
|
||||||
level: warning
|
|
||||||
tokens:
|
|
||||||
- '(?:\w+)nised?'
|
|
||||||
- 'colour'
|
|
||||||
- 'labour'
|
|
||||||
- 'centre'
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Put a nonbreaking space between the number and the unit in '%s'."
|
|
||||||
link: "https://developers.google.com/style/units-of-measure"
|
|
||||||
nonword: true
|
|
||||||
level: error
|
|
||||||
tokens:
|
|
||||||
- \b\d+(?:B|kB|MB|GB|TB)
|
|
||||||
- \b\d+(?:ns|ms|s|min|h|d)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Try to avoid using first-person plural like '%s'."
|
|
||||||
link: 'https://developers.google.com/style/pronouns#personal-pronouns'
|
|
||||||
level: warning
|
|
||||||
ignorecase: true
|
|
||||||
tokens:
|
|
||||||
- we
|
|
||||||
- we'(?:ve|re)
|
|
||||||
- ours?
|
|
||||||
- us
|
|
||||||
- let's
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
extends: existence
|
|
||||||
message: "Avoid using '%s'."
|
|
||||||
link: 'https://developers.google.com/style/tense'
|
|
||||||
ignorecase: true
|
|
||||||
level: warning
|
|
||||||
tokens:
|
|
||||||
- will
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
extends: substitution
|
|
||||||
message: "Use '%s' instead of '%s'."
|
|
||||||
link: "https://developers.google.com/style/word-list"
|
|
||||||
level: warning
|
|
||||||
ignorecase: false
|
|
||||||
action:
|
|
||||||
name: replace
|
|
||||||
swap:
|
|
||||||
"(?:API Console|dev|developer) key": API key
|
|
||||||
"(?:cell ?phone|smart ?phone)": phone|mobile phone
|
|
||||||
"(?:dev|developer|APIs) console": API console
|
|
||||||
"(?:e-mail|Email|E-mail)": email
|
|
||||||
"(?:file ?path|path ?name)": path
|
|
||||||
"(?:kill|terminate|abort)": stop|exit|cancel|end
|
|
||||||
"(?:OAuth ?2|Oauth)": OAuth 2.0
|
|
||||||
"(?:ok|Okay)": OK|okay
|
|
||||||
"(?:WiFi|wifi)": Wi-Fi
|
|
||||||
'[\.]+apk': APK
|
|
||||||
'3\-D': 3D
|
|
||||||
'Google (?:I\-O|IO)': Google I/O
|
|
||||||
"tap (?:&|and) hold": touch & hold
|
|
||||||
"un(?:check|select)": clear
|
|
||||||
above: preceding
|
|
||||||
account name: username
|
|
||||||
action bar: app bar
|
|
||||||
admin: administrator
|
|
||||||
Ajax: AJAX
|
|
||||||
a\.k\.a|aka: or|also known as
|
|
||||||
Android device: Android-powered device
|
|
||||||
android: Android
|
|
||||||
API explorer: APIs Explorer
|
|
||||||
application: app
|
|
||||||
approx\.: approximately
|
|
||||||
authN: authentication
|
|
||||||
authZ: authorization
|
|
||||||
autoupdate: automatically update
|
|
||||||
cellular data: mobile data
|
|
||||||
cellular network: mobile network
|
|
||||||
chapter: documents|pages|sections
|
|
||||||
check box: checkbox
|
|
||||||
CLI: command-line tool
|
|
||||||
click on: click|click in
|
|
||||||
Cloud: Google Cloud Platform|GCP
|
|
||||||
Container Engine: Kubernetes Engine
|
|
||||||
content type: media type
|
|
||||||
curated roles: predefined roles
|
|
||||||
data are: data is
|
|
||||||
Developers Console: Google API Console|API Console
|
|
||||||
disabled?: turn off|off
|
|
||||||
ephemeral IP address: ephemeral external IP address
|
|
||||||
fewer data: less data
|
|
||||||
file name: filename
|
|
||||||
firewalls: firewall rules
|
|
||||||
functionality: capability|feature
|
|
||||||
Google account: Google Account
|
|
||||||
Google accounts: Google Accounts
|
|
||||||
Googling: search with Google
|
|
||||||
grayed-out: unavailable
|
|
||||||
HTTPs: HTTPS
|
|
||||||
in order to: to
|
|
||||||
ingest: import|load
|
|
||||||
k8s: Kubernetes
|
|
||||||
long press: touch & hold
|
|
||||||
network IP address: internal IP address
|
|
||||||
omnibox: address bar
|
|
||||||
open-source: open source
|
|
||||||
overview screen: recents screen
|
|
||||||
regex: regular expression
|
|
||||||
SHA1: SHA-1|HAS-SHA1
|
|
||||||
sign into: sign in to
|
|
||||||
sign-?on: single sign-on
|
|
||||||
static IP address: static external IP address
|
|
||||||
stylesheet: style sheet
|
|
||||||
synch: sync
|
|
||||||
tablename: table name
|
|
||||||
tablet: device
|
|
||||||
touch: tap
|
|
||||||
url: URL
|
|
||||||
vs\.: versus
|
|
||||||
World Wide Web: web
|
|
||||||
@@ -2,6 +2,48 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [0.47.10] - 2026-08-05
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Unique molecule container names per CI runner
|
||||||
|
|
||||||
|
## [0.47.9] - 2026-08-03
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Unique molecule container names per CI runner
|
||||||
|
|
||||||
|
## [0.47.8] - 2026-08-03
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Increase CI_SCALE_FACTOR default from 4 to 6
|
||||||
|
|
||||||
|
## [0.47.7] - 2026-08-03
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Scale check_test_speed limits on CI runners
|
||||||
|
|
||||||
|
## [0.47.6] - 2026-08-03
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Configure git auth in setup_image for git+https deps
|
||||||
|
|
||||||
|
## [0.47.5] - 2026-08-03
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Push wiki to main branch instead of master
|
||||||
|
|
||||||
|
## [0.47.4] - 2026-08-03
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Add User-Agent header to _download in install_tools
|
||||||
|
|
||||||
## [0.47.3] - 2026-07-17
|
## [0.47.3] - 2026-07-17
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ quality badges.
|
|||||||
|
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||||
[](https://www.python.org/downloads/)
|
[](https://www.python.org/downloads/)
|
||||||
|
|
||||||
## Why devx?
|
## Why devx?
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ extra index and list devx in your dependencies:
|
|||||||
```toml
|
```toml
|
||||||
[project]
|
[project]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"devx>=0.47.3",
|
"devx>=0.47.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.pip]
|
[tool.pip]
|
||||||
@@ -101,8 +101,8 @@ pip install -e .
|
|||||||
```
|
```
|
||||||
|
|
||||||
> **Note:** If your project requires a specific devx version, pin it in
|
> **Note:** If your project requires a specific devx version, pin it in
|
||||||
> `dependencies` (for example, `"devx==0.47.3"`) or use a version constraint
|
> `dependencies` (for example, `"devx==0.47.10"`) or use a version constraint
|
||||||
> (for example, `"devx>=0.47.3,<0.48"`).
|
> (for example, `"devx>=0.47.10,<0.48"`).
|
||||||
|
|
||||||
### Optional extras
|
### Optional extras
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -12,12 +12,12 @@ project to be reusable across all oblachno-oss repositories.
|
|||||||
|
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
|
||||||
[](https://www.python.org/downloads/)
|
[](https://www.python.org/downloads/)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -74,14 +74,14 @@ Add devx to your `pyproject.toml` dependencies and configure the registry:
|
|||||||
```toml
|
```toml
|
||||||
[project]
|
[project]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"devx>=0.47.3",
|
"devx>=0.47.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.pip]
|
[tool.pip]
|
||||||
extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple"
|
extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple"
|
||||||
```
|
```
|
||||||
|
|
||||||
Pin a specific version if needed: `"devx==0.47.3"` or `"devx>=0.47.3,<0.48"`.
|
Pin a specific version if needed: `"devx==0.47.10"` or `"devx>=0.47.10,<0.48"`.
|
||||||
|
|
||||||
### Optional extras
|
### Optional extras
|
||||||
|
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ Add devx to your `pyproject.toml`:
|
|||||||
```toml
|
```toml
|
||||||
[project]
|
[project]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"devx>=0.47.3",
|
"devx>=0.47.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"devx>=0.47.3",
|
"devx>=0.47.10",
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
|
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
|
||||||
|
|
||||||
__version__ = "0.47.3"
|
__version__ = "0.47.10"
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ def commit_and_push(wiki_dir: Path, wiki_url: str, dry_run: bool) -> bool:
|
|||||||
|
|
||||||
# Push
|
# Push
|
||||||
result = subprocess.run( # nosec
|
result = subprocess.run( # nosec
|
||||||
["git", "push", "--force", wiki_url, "HEAD:master"],
|
["git", "push", "--force", wiki_url, "HEAD:main"],
|
||||||
cwd=wiki_dir,
|
cwd=wiki_dir,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
|||||||
@@ -125,6 +125,11 @@ def build_env_for_pair(pair: str, base_env: dict[str, str]) -> dict[str, str]:
|
|||||||
"""Build environment for a single molecule pair."""
|
"""Build environment for a single molecule pair."""
|
||||||
_role, _scenario, platform_name, platform_image, platform_command = parse_pair(pair)
|
_role, _scenario, platform_name, platform_image, platform_command = parse_pair(pair)
|
||||||
env = base_env.copy()
|
env = base_env.copy()
|
||||||
|
# Append runner index to platform name when running in CI matrix to avoid
|
||||||
|
# Docker container name conflicts when multiple runners share the same Docker host.
|
||||||
|
matrix_index = env.get("MATRIX_INDEX")
|
||||||
|
if matrix_index:
|
||||||
|
platform_name = f"{platform_name}-r{matrix_index}"
|
||||||
env["MOLECULE_PLATFORM_NAME"] = platform_name
|
env["MOLECULE_PLATFORM_NAME"] = platform_name
|
||||||
env["MOLECULE_PLATFORM_IMAGE"] = platform_image
|
env["MOLECULE_PLATFORM_IMAGE"] = platform_image
|
||||||
if platform_command:
|
if platform_command:
|
||||||
|
|||||||
@@ -11,6 +11,18 @@ Usage:
|
|||||||
The module runs ``make test-unit`` with ``PYTEST_ADDOPTS=--durations=0`` so
|
The module runs ``make test-unit`` with ``PYTEST_ADDOPTS=--durations=0`` so
|
||||||
that pytest emits per-test timing lines alongside the summary. Both the
|
that pytest emits per-test timing lines alongside the summary. Both the
|
||||||
total wall-clock time and individual test durations are parsed and validated.
|
total wall-clock time and individual test durations are parsed and validated.
|
||||||
|
|
||||||
|
CI runner scaling
|
||||||
|
-----------------
|
||||||
|
CI runners (Gitea Actions Docker containers) are typically 5-8x slower than
|
||||||
|
local development machines due to shared CPU, fewer cores, and container
|
||||||
|
overhead. When the ``CI`` environment variable is set (standard CI
|
||||||
|
convention), both the total and per-test limits are multiplied by
|
||||||
|
``CI_SCALE_FACTOR`` (default 6) to account for this. This keeps the local
|
||||||
|
budget strict while preventing false failures on slower CI runners.
|
||||||
|
|
||||||
|
The scale factor can be overridden via the ``DEVX_CI_SCALE_FACTOR``
|
||||||
|
environment variable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -27,6 +39,12 @@ DEFAULT_MAX_SECONDS = 10.0
|
|||||||
DEFAULT_MAX_SINGLE_SECONDS = 0.5
|
DEFAULT_MAX_SINGLE_SECONDS = 0.5
|
||||||
TEST_COMMAND = ["make", "test-unit"]
|
TEST_COMMAND = ["make", "test-unit"]
|
||||||
|
|
||||||
|
# CI runners are typically 5-8x slower than local machines (shared CPU,
|
||||||
|
# fewer cores, container overhead). Scale limits up when running on CI
|
||||||
|
# so the gate catches real regressions, not infrastructure slowness.
|
||||||
|
CI_SCALE_FACTOR = float(os.environ.get("DEVX_CI_SCALE_FACTOR", "6"))
|
||||||
|
_IS_CI = bool(os.environ.get("CI") or os.environ.get("GITEA_ACTIONS"))
|
||||||
|
|
||||||
# Matches pytest summary line: "234 passed in 0.70s"
|
# Matches pytest summary line: "234 passed in 0.70s"
|
||||||
_TIMING_RE = re.compile(r"(\d+) passed.* in ([0-9.]+)s")
|
_TIMING_RE = re.compile(r"(\d+) passed.* in ([0-9.]+)s")
|
||||||
|
|
||||||
@@ -38,6 +56,13 @@ _TIMING_RE = re.compile(r"(\d+) passed.* in ([0-9.]+)s")
|
|||||||
_DURATION_LINE_RE = re.compile(r"^(\d+\.?\d*)s\s+call\s+(.+)$")
|
_DURATION_LINE_RE = re.compile(r"^(\d+\.?\d*)s\s+call\s+(.+)$")
|
||||||
|
|
||||||
|
|
||||||
|
def _ci_scale_limit(limit: float) -> float:
|
||||||
|
"""Scale a time limit by the CI factor when running on CI."""
|
||||||
|
if _IS_CI:
|
||||||
|
return limit * CI_SCALE_FACTOR
|
||||||
|
return limit
|
||||||
|
|
||||||
|
|
||||||
def run_tests() -> tuple[str, str]:
|
def run_tests() -> tuple[str, str]:
|
||||||
"""Execute the unit-test suite and return (stdout, stderr).
|
"""Execute the unit-test suite and return (stdout, stderr).
|
||||||
|
|
||||||
@@ -123,21 +148,38 @@ def check_per_test_speed(
|
|||||||
|
|
||||||
def main(max_seconds: float, max_single_seconds: float) -> None:
|
def main(max_seconds: float, max_single_seconds: float) -> None:
|
||||||
"""Run tests, parse timings, and enforce both budgets."""
|
"""Run tests, parse timings, and enforce both budgets."""
|
||||||
|
# Scale limits for CI runners (slower CPU, fewer workers).
|
||||||
|
effective_max = _ci_scale_limit(max_seconds)
|
||||||
|
effective_single = _ci_scale_limit(max_single_seconds)
|
||||||
|
|
||||||
|
if _IS_CI:
|
||||||
|
click.echo(
|
||||||
|
_(
|
||||||
|
"[check-test-speed] CI environment detected — scaling limits by {factor}x "
|
||||||
|
"(total: {orig}s → {eff}s, per-test: {orig_s}s → {eff_s}s)",
|
||||||
|
factor=CI_SCALE_FACTOR,
|
||||||
|
orig=max_seconds,
|
||||||
|
eff=effective_max,
|
||||||
|
orig_s=max_single_seconds,
|
||||||
|
eff_s=effective_single,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
stdout, stderr = run_tests()
|
stdout, stderr = run_tests()
|
||||||
combined = stdout + "\n" + stderr
|
combined = stdout + "\n" + stderr
|
||||||
click.echo(combined, err=False)
|
click.echo(combined, err=False)
|
||||||
|
|
||||||
duration = parse_duration(combined)
|
duration = parse_duration(combined)
|
||||||
check_speed(duration, max_seconds)
|
check_speed(duration, effective_max)
|
||||||
|
|
||||||
if max_single_seconds > 0:
|
if effective_single > 0:
|
||||||
per_test = parse_per_test_durations(combined)
|
per_test = parse_per_test_durations(combined)
|
||||||
violations = check_per_test_speed(per_test, max_single_seconds)
|
violations = check_per_test_speed(per_test, effective_single)
|
||||||
if violations:
|
if violations:
|
||||||
msg = _(
|
msg = _(
|
||||||
"Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
|
"Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
|
||||||
count=len(violations),
|
count=len(violations),
|
||||||
limit=max_single_seconds,
|
limit=effective_single,
|
||||||
)
|
)
|
||||||
click.echo(f"\n{msg}", err=True)
|
click.echo(f"\n{msg}", err=True)
|
||||||
for v in violations:
|
for v in violations:
|
||||||
@@ -148,8 +190,8 @@ def main(max_seconds: float, max_single_seconds: float) -> None:
|
|||||||
_(
|
_(
|
||||||
"Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
|
"Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
|
||||||
duration=duration,
|
duration=duration,
|
||||||
max=max_seconds,
|
max=effective_max,
|
||||||
single=max_single_seconds,
|
single=effective_single,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -65,8 +65,13 @@ def _ensure_target_dir() -> Path:
|
|||||||
|
|
||||||
|
|
||||||
def _download(url: str, dest: Path) -> None:
|
def _download(url: str, dest: Path) -> None:
|
||||||
"""Download a file from ``url`` to ``dest`` with a 60s timeout."""
|
"""Download a file from ``url`` to ``dest`` with a 60s timeout.
|
||||||
with urllib.request.urlopen(url, timeout=60) as resp, open(dest, "wb") as f: # nosec B310
|
|
||||||
|
A User-Agent header is set because some CDNs (e.g. dl.gitea.com)
|
||||||
|
return 403 to requests with Python's default User-Agent.
|
||||||
|
"""
|
||||||
|
req = urllib.request.Request(url, headers={"User-Agent": "devx/install-tools"})
|
||||||
|
with urllib.request.urlopen(req, timeout=60) as resp, open(dest, "wb") as f: # nosec B310
|
||||||
shutil.copyfileobj(resp, f)
|
shutil.copyfileobj(resp, f)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ def _install_ansible_collections(bin_dir: str) -> None:
|
|||||||
if not requirements.exists():
|
if not requirements.exists():
|
||||||
click.echo(" ansible/requirements.yml not found — skipping collections.")
|
click.echo(" ansible/requirements.yml not found — skipping collections.")
|
||||||
return
|
return
|
||||||
_run([galaxy, "collection", "install", "-r", str(requirements)])
|
_run([galaxy, "collection", "install", "-r", str(requirements), "--no-cache"])
|
||||||
|
|
||||||
|
|
||||||
def _configure_tea_login() -> None:
|
def _configure_tea_login() -> None:
|
||||||
|
|||||||
@@ -81,6 +81,17 @@ def _install_in_image(
|
|||||||
username,
|
username,
|
||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
|
# Configure git URL rewrite so git+https dependencies can authenticate
|
||||||
|
subprocess.run( # nosec B603, B607
|
||||||
|
[
|
||||||
|
"git",
|
||||||
|
"config",
|
||||||
|
"--global",
|
||||||
|
f"url.https://{username}:{token}@{gitea_host}/.insteadOf",
|
||||||
|
f"https://{gitea_host}/",
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
click.echo(f"[setup-image] Linked {opt_venv}" + (f" with [{extras}]" if extras else "") + ".")
|
click.echo(f"[setup-image] Linked {opt_venv}" + (f" with [{extras}]" if extras else "") + ".")
|
||||||
subprocess.run(cmd, check=True, env=env) # nosec B603
|
subprocess.run(cmd, check=True, env=env) # nosec B603
|
||||||
|
|||||||
@@ -3822,5 +3822,13 @@
|
|||||||
"pl": "importlib.reload({mod}) called {n} time(s) in test '{test}' — odd count leaves module in modified state. Add a final reload to restore defaults or wrap in try/finally.",
|
"pl": "importlib.reload({mod}) called {n} time(s) in test '{test}' — odd count leaves module in modified state. Add a final reload to restore defaults or wrap in try/finally.",
|
||||||
"ru": "importlib.reload({mod}) called {n} time(s) in test '{test}' — odd count leaves module in modified state. Add a final reload to restore defaults or wrap in try/finally.",
|
"ru": "importlib.reload({mod}) called {n} time(s) in test '{test}' — odd count leaves module in modified state. Add a final reload to restore defaults or wrap in try/finally.",
|
||||||
"zh": "importlib.reload({mod}) called {n} time(s) in test '{test}' — odd count leaves module in modified state. Add a final reload to restore defaults or wrap in try/finally."
|
"zh": "importlib.reload({mod}) called {n} time(s) in test '{test}' — odd count leaves module in modified state. Add a final reload to restore defaults or wrap in try/finally."
|
||||||
|
},
|
||||||
|
"[check-test-speed] CI environment detected — scaling limits by {factor}x (total: {orig}s → {eff}s, per-test: {orig_s}s → {eff_s}s)": {
|
||||||
|
"en": "[check-test-speed] CI environment detected — scaling limits by {factor}x (total: {orig}s → {eff}s, per-test: {orig_s}s → {eff_s}s)",
|
||||||
|
"bg": "[check-test-speed] CI environment detected — scaling limits by {factor}x (total: {orig}s → {eff}s, per-test: {orig_s}s → {eff_s}s)",
|
||||||
|
"de": "[check-test-speed] CI environment detected — scaling limits by {factor}x (total: {orig}s → {eff}s, per-test: {orig_s}s → {eff_s}s)",
|
||||||
|
"pl": "[check-test-speed] CI environment detected — scaling limits by {factor}x (total: {orig}s → {eff}s, per-test: {orig_s}s → {eff_s}s)",
|
||||||
|
"ru": "[check-test-speed] CI environment detected — scaling limits by {factor}x (total: {orig}s → {eff}s, per-test: {orig_s}s → {eff_s}s)",
|
||||||
|
"zh": "[check-test-speed] CI environment detected — scaling limits by {factor}x (total: {orig}s → {eff}s, per-test: {orig_s}s → {eff_s}s)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ from devx.tools.check_test_speed import (
|
|||||||
DEFAULT_MAX_SECONDS,
|
DEFAULT_MAX_SECONDS,
|
||||||
DEFAULT_MAX_SINGLE_SECONDS,
|
DEFAULT_MAX_SINGLE_SECONDS,
|
||||||
TEST_COMMAND,
|
TEST_COMMAND,
|
||||||
|
_ci_scale_limit,
|
||||||
check_per_test_speed,
|
check_per_test_speed,
|
||||||
check_speed,
|
check_speed,
|
||||||
cli,
|
cli,
|
||||||
@@ -144,7 +145,26 @@ def test_main_module_block() -> None:
|
|||||||
mock_cli.assert_called_once_with([])
|
mock_cli.assert_called_once_with([])
|
||||||
|
|
||||||
|
|
||||||
|
class TestCiScaleLimit:
|
||||||
|
def test_no_scaling_when_not_ci(self) -> None:
|
||||||
|
with patch("devx.tools.check_test_speed._IS_CI", False):
|
||||||
|
assert _ci_scale_limit(10.0) == 10.0
|
||||||
|
assert _ci_scale_limit(0.5) == 0.5
|
||||||
|
|
||||||
|
def test_scales_when_ci(self) -> None:
|
||||||
|
with patch("devx.tools.check_test_speed._IS_CI", True):
|
||||||
|
with patch("devx.tools.check_test_speed.CI_SCALE_FACTOR", 4.0):
|
||||||
|
assert _ci_scale_limit(10.0) == 40.0
|
||||||
|
assert _ci_scale_limit(0.5) == 2.0
|
||||||
|
|
||||||
|
def test_custom_scale_factor(self) -> None:
|
||||||
|
with patch("devx.tools.check_test_speed._IS_CI", True):
|
||||||
|
with patch("devx.tools.check_test_speed.CI_SCALE_FACTOR", 2.5):
|
||||||
|
assert _ci_scale_limit(10.0) == 25.0
|
||||||
|
|
||||||
|
|
||||||
class TestMain:
|
class TestMain:
|
||||||
|
@patch("devx.tools.check_test_speed._IS_CI", False)
|
||||||
@patch("devx.tools.check_test_speed.run_tests")
|
@patch("devx.tools.check_test_speed.run_tests")
|
||||||
@patch("devx.tools.check_test_speed.parse_duration")
|
@patch("devx.tools.check_test_speed.parse_duration")
|
||||||
@patch("devx.tools.check_test_speed.check_speed")
|
@patch("devx.tools.check_test_speed.check_speed")
|
||||||
@@ -174,6 +194,7 @@ class TestMain:
|
|||||||
mock_parse_per.assert_called_once()
|
mock_parse_per.assert_called_once()
|
||||||
mock_check_per.assert_called_once_with([], DEFAULT_MAX_SINGLE_SECONDS)
|
mock_check_per.assert_called_once_with([], DEFAULT_MAX_SINGLE_SECONDS)
|
||||||
|
|
||||||
|
@patch("devx.tools.check_test_speed._IS_CI", False)
|
||||||
@patch("devx.tools.check_test_speed.run_tests")
|
@patch("devx.tools.check_test_speed.run_tests")
|
||||||
@patch("devx.tools.check_test_speed.parse_duration")
|
@patch("devx.tools.check_test_speed.parse_duration")
|
||||||
def test_slow_total_exits(
|
def test_slow_total_exits(
|
||||||
@@ -189,6 +210,7 @@ class TestMain:
|
|||||||
assert result.exit_code == 1
|
assert result.exit_code == 1
|
||||||
assert "too slow" in result.output.lower()
|
assert "too slow" in result.output.lower()
|
||||||
|
|
||||||
|
@patch("devx.tools.check_test_speed._IS_CI", False)
|
||||||
@patch("devx.tools.check_test_speed.run_tests")
|
@patch("devx.tools.check_test_speed.run_tests")
|
||||||
@patch("devx.tools.check_test_speed.parse_duration")
|
@patch("devx.tools.check_test_speed.parse_duration")
|
||||||
@patch("devx.tools.check_test_speed.check_speed")
|
@patch("devx.tools.check_test_speed.check_speed")
|
||||||
@@ -213,6 +235,7 @@ class TestMain:
|
|||||||
assert "Per-test speed check FAILED" in result.output
|
assert "Per-test speed check FAILED" in result.output
|
||||||
assert "test_slow" in result.output
|
assert "test_slow" in result.output
|
||||||
|
|
||||||
|
@patch("devx.tools.check_test_speed._IS_CI", False)
|
||||||
@patch("devx.tools.check_test_speed.run_tests")
|
@patch("devx.tools.check_test_speed.run_tests")
|
||||||
def test_parse_failure_exits(
|
def test_parse_failure_exits(
|
||||||
self,
|
self,
|
||||||
@@ -225,6 +248,7 @@ class TestMain:
|
|||||||
assert result.exit_code == 1
|
assert result.exit_code == 1
|
||||||
assert "Could not parse" in result.output
|
assert "Could not parse" in result.output
|
||||||
|
|
||||||
|
@patch("devx.tools.check_test_speed._IS_CI", False)
|
||||||
@patch("devx.tools.check_test_speed.run_tests")
|
@patch("devx.tools.check_test_speed.run_tests")
|
||||||
@patch("devx.tools.check_test_speed.parse_duration")
|
@patch("devx.tools.check_test_speed.parse_duration")
|
||||||
@patch("devx.tools.check_test_speed.check_speed")
|
@patch("devx.tools.check_test_speed.check_speed")
|
||||||
@@ -248,6 +272,7 @@ class TestMain:
|
|||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
mock_check.assert_called_once_with(0.5, 1.5)
|
mock_check.assert_called_once_with(0.5, 1.5)
|
||||||
|
|
||||||
|
@patch("devx.tools.check_test_speed._IS_CI", False)
|
||||||
@patch("devx.tools.check_test_speed.run_tests")
|
@patch("devx.tools.check_test_speed.run_tests")
|
||||||
@patch("devx.tools.check_test_speed.parse_duration")
|
@patch("devx.tools.check_test_speed.parse_duration")
|
||||||
@patch("devx.tools.check_test_speed.check_speed")
|
@patch("devx.tools.check_test_speed.check_speed")
|
||||||
@@ -270,6 +295,7 @@ class TestMain:
|
|||||||
mock_parse_per.assert_not_called()
|
mock_parse_per.assert_not_called()
|
||||||
mock_check_per.assert_not_called()
|
mock_check_per.assert_not_called()
|
||||||
|
|
||||||
|
@patch("devx.tools.check_test_speed._IS_CI", False)
|
||||||
@patch("devx.tools.check_test_speed.run_tests")
|
@patch("devx.tools.check_test_speed.run_tests")
|
||||||
@patch("devx.tools.check_test_speed.parse_duration")
|
@patch("devx.tools.check_test_speed.parse_duration")
|
||||||
@patch("devx.tools.check_test_speed.check_speed")
|
@patch("devx.tools.check_test_speed.check_speed")
|
||||||
@@ -292,3 +318,32 @@ class TestMain:
|
|||||||
result = runner.invoke(cli, ["--max-single-seconds", "1.0"])
|
result = runner.invoke(cli, ["--max-single-seconds", "1.0"])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
mock_check_per.assert_called_once_with([], 1.0)
|
mock_check_per.assert_called_once_with([], 1.0)
|
||||||
|
|
||||||
|
@patch("devx.tools.check_test_speed._IS_CI", True)
|
||||||
|
@patch("devx.tools.check_test_speed.CI_SCALE_FACTOR", 4.0)
|
||||||
|
@patch("devx.tools.check_test_speed.run_tests")
|
||||||
|
@patch("devx.tools.check_test_speed.parse_duration")
|
||||||
|
@patch("devx.tools.check_test_speed.check_speed")
|
||||||
|
@patch("devx.tools.check_test_speed.parse_per_test_durations")
|
||||||
|
@patch("devx.tools.check_test_speed.check_per_test_speed")
|
||||||
|
def test_ci_scales_limits(
|
||||||
|
self,
|
||||||
|
mock_check_per: MagicMock,
|
||||||
|
mock_parse_per: MagicMock,
|
||||||
|
mock_check: MagicMock,
|
||||||
|
mock_parse: MagicMock,
|
||||||
|
mock_run: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
mock_run.return_value = ("out\n", "err\n")
|
||||||
|
mock_parse.return_value = 30.0 # would fail local (10s) but pass CI (40s)
|
||||||
|
mock_parse_per.return_value = []
|
||||||
|
mock_check_per.return_value = []
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(cli, [])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "CI environment detected" in result.output
|
||||||
|
assert "scaling limits by 4.0x" in result.output
|
||||||
|
# check_speed called with scaled limit
|
||||||
|
mock_check.assert_called_once_with(30.0, 40.0)
|
||||||
|
mock_check_per.assert_called_once_with([], 2.0)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import platform
|
import platform
|
||||||
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
@@ -66,6 +67,10 @@ class TestDownload:
|
|||||||
with patch("urllib.request.urlopen", return_value=_FakeResponse()) as mock_urlopen:
|
with patch("urllib.request.urlopen", return_value=_FakeResponse()) as mock_urlopen:
|
||||||
install_tools._download("https://example.com/file", dest)
|
install_tools._download("https://example.com/file", dest)
|
||||||
mock_urlopen.assert_called_once()
|
mock_urlopen.assert_called_once()
|
||||||
|
call_args = mock_urlopen.call_args
|
||||||
|
req = call_args.args[0]
|
||||||
|
assert isinstance(req, urllib.request.Request)
|
||||||
|
assert req.get_header("User-agent") == "devx/install-tools"
|
||||||
assert dest.read_bytes() == b"data"
|
assert dest.read_bytes() == b"data"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,16 @@ class TestBuildEnvForPair:
|
|||||||
env = build_env_for_pair("default|ubuntu-2204|img:latest|", {"MOLECULE_HOME": "/custom/home"})
|
env = build_env_for_pair("default|ubuntu-2204|img:latest|", {"MOLECULE_HOME": "/custom/home"})
|
||||||
assert env["MOLECULE_HOME"] == "/custom/home"
|
assert env["MOLECULE_HOME"] == "/custom/home"
|
||||||
|
|
||||||
|
def test_appends_matrix_index_to_platform_name(self) -> None:
|
||||||
|
"""When MATRIX_INDEX is set, platform name gets a unique suffix."""
|
||||||
|
env = build_env_for_pair("default|ubuntu-2604|img:latest|sleep infinity", {"MATRIX_INDEX": "3"})
|
||||||
|
assert env["MOLECULE_PLATFORM_NAME"] == "ubuntu-2604-r3"
|
||||||
|
|
||||||
|
def test_no_matrix_index_keeps_platform_name(self) -> None:
|
||||||
|
"""Without MATRIX_INDEX, platform name is unchanged."""
|
||||||
|
env = build_env_for_pair("default|ubuntu-2604|img:latest|sleep infinity", {})
|
||||||
|
assert env["MOLECULE_PLATFORM_NAME"] == "ubuntu-2604"
|
||||||
|
|
||||||
|
|
||||||
class TestPollForOtherFailures:
|
class TestPollForOtherFailures:
|
||||||
def test_sets_failed_event_when_other_runner_fails(self) -> None:
|
def test_sets_failed_event_when_other_runner_fails(self) -> None:
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ class TestInstallAnsibleCollections:
|
|||||||
mock_path.return_value.__str__ = lambda _: str(req)
|
mock_path.return_value.__str__ = lambda _: str(req)
|
||||||
_install_ansible_collections(".venv/bin")
|
_install_ansible_collections(".venv/bin")
|
||||||
mock_run.assert_called_once()
|
mock_run.assert_called_once()
|
||||||
|
args = mock_run.call_args[0][0]
|
||||||
|
assert "--no-cache" in args, "ansible-galaxy must use --no-cache to avoid concurrent cache corruption"
|
||||||
|
|
||||||
@patch("devx.tools.setup._run")
|
@patch("devx.tools.setup._run")
|
||||||
def test_skips_when_no_requirements(self, mock_run: MagicMock) -> None:
|
def test_skips_when_no_requirements(self, mock_run: MagicMock) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user