Skip to content

Commit 62bdfa7

Browse files
authored
Merge pull request #683 from linode/dev
Release v5.55.0
2 parents 8bc7bbe + 5bab33a commit 62bdfa7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1905
-401
lines changed

.github/dependabot.yml

+4
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ updates:
99
directory: "/" # Location of package manifests
1010
schedule:
1111
interval: "daily"
12+
- package-ecosystem: "github-actions"
13+
directory: "/"
14+
schedule:
15+
interval: "monthly"

.github/workflows/e2e-suite-windows.yml

+11-10
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@ on:
22
pull_request:
33
workflow_dispatch:
44
inputs:
5-
test_path:
6-
description: "The path from 'test/integration' to the target to be tested, e.g. 'cli'"
5+
module:
6+
description: "The module from 'test/integration' to the target to be tested, e.g. 'cli, domains, events, etc'"
77
required: false
8+
run_long_tests:
9+
description: "Select True to run long tests, e.g. database, rebuild, etc"
10+
required: false
11+
type: choice
12+
options:
13+
- "True"
14+
- "False"
15+
default: "False"
816
sha:
917
description: 'The hash value of the commit.'
1018
required: true
@@ -21,13 +29,6 @@ jobs:
2129
github.event_name == 'workflow_dispatch' && inputs.sha != ''
2230

2331
steps:
24-
- uses: actions-ecosystem/action-regex-match@v2
25-
id: validate-tests
26-
with:
27-
text: ${{ inputs.test_path }}
28-
regex: '[^a-z0-9-:.\/_]' # Tests validation
29-
flags: gi
30-
3132
# Check out merge commit
3233
- name: Checkout PR
3334
uses: actions/checkout@v4
@@ -72,7 +73,7 @@ jobs:
7273
env:
7374
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
7475

75-
- run: make INTEGRATION_TEST_PATH="${{ inputs.test_path }}" testint
76+
- run: make MODULE="${{ inputs.module }}" RUN_LONG_TESTS="${{ inputs.run_long_tests }}" testint
7677
env:
7778
LINODE_CLI_TOKEN: ${{ secrets.LINODE_TOKEN_2 }}
7879

.github/workflows/e2e-suite.yml

+33-4
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,47 @@ on:
1010
module:
1111
description: "The module from 'test/integration' to the target to be tested, e.g. 'cli, domains, events, etc'"
1212
required: false
13+
run_long_tests:
14+
description: "Select True to run long tests, e.g. database, rebuild, etc"
15+
required: false
16+
type: choice
17+
options:
18+
- "True"
19+
- "False"
20+
default: "False"
1321
sha:
1422
description: 'The hash value of the commit.'
1523
required: true
1624
default: ''
1725
pull_request_number:
1826
description: 'The number of the PR. Ensure sha value is provided'
1927
required: false
28+
openapi_spec_url:
29+
description: 'URL of the OpenAPI spec to use for the tests'
30+
required: false
31+
default: ''
32+
python-version:
33+
description: 'Specify Python version to use'
34+
required: false
35+
run-eol-python-version:
36+
description: 'Run EOL python version?'
37+
required: false
38+
default: 'false'
39+
type: choice
40+
options:
41+
- 'true'
42+
- 'false'
43+
2044
push:
2145
branches:
2246
- main
2347
- dev
2448

49+
env:
50+
DEFAULT_PYTHON_VERSION: "3.10"
51+
EOL_PYTHON_VERSION: "3.8"
52+
EXIT_STATUS: 0
53+
2554
jobs:
2655
integration_tests:
2756
name: Run integration tests on Ubuntu
@@ -74,7 +103,7 @@ jobs:
74103
- name: Setup Python
75104
uses: actions/setup-python@v4
76105
with:
77-
python-version: '3.x'
106+
python-version: ${{ inputs.run-eol-python-version == 'true' && env.EOL_PYTHON_VERSION || inputs.python-version || env.DEFAULT_PYTHON_VERSION }}
78107

79108
- name: Install Python dependencies and update cert
80109
run: |
@@ -83,7 +112,7 @@ jobs:
83112
pip install .[obj,dev]
84113
85114
- name: Install Package
86-
run: make install
115+
run: make install SPEC="${{ inputs.OPENAPI_SPEC_URL }}"
87116
env:
88117
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
89118

@@ -95,7 +124,7 @@ jobs:
95124
run: |
96125
timestamp=$(date +'%Y%m%d%H%M')
97126
report_filename="${timestamp}_cli_test_report.xml"
98-
make testint TEST_ARGS="--junitxml=${report_filename}" MODULE="${{ inputs.module }}"
127+
make testint TEST_ARGS="--junitxml=${report_filename}" MODULE="${{ inputs.module }}" RUN_LONG_TESTS="${{ inputs.run_long_tests }}"
99128
env:
100129
LINODE_CLI_TOKEN: ${{ env.LINODE_CLI_TOKEN }}
101130

@@ -272,4 +301,4 @@ jobs:
272301
]
273302
}
274303
env:
275-
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
304+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

.github/workflows/labeler.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
uses: actions/checkout@v4
2222
-
2323
name: Run Labeler
24-
uses: crazy-max/ghaction-github-labeler@de749cf181958193cb7debf1a9c5bb28922f3e1b
24+
uses: crazy-max/ghaction-github-labeler@b54af0c25861143e7c8813d7cbbf46d2c341680c
2525
with:
2626
github-token: ${{ secrets.GITHUB_TOKEN }}
2727
yaml-file: .github/labels.yml

.github/workflows/publish-oci.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ jobs:
2020
run: make requirements
2121

2222
- name: Set up QEMU
23-
uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # pin@v2.2.0
23+
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # pin@v3.2.0
2424

2525
- name: Set up Docker Buildx
2626
uses: docker/setup-buildx-action@ecf95283f03858871ff00b787d79c419715afc34 # [email protected]
2727

2828
- name: Login to Docker Hub
29-
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # pin@v2.2.0
29+
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # pin@v3.3.0
3030
with:
3131
username: ${{ secrets.DOCKERHUB_USERNAME }}
3232
password: ${{ secrets.DOCKERHUB_TOKEN }}

.github/workflows/remote-release-trigger.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
steps:
1010
- name: Generate App Installation Token
1111
id: generate_token
12-
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 # pin@v1
12+
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # pin@v1
1313
with:
1414
app_id: ${{ secrets.CLI_RELEASE_APP_ID }}
1515
private_key: ${{ secrets.CLI_RELEASE_PRIVATE_KEY }}
@@ -23,7 +23,7 @@ jobs:
2323

2424
- name: Get previous tag
2525
id: previoustag
26-
uses: WyriHaximus/github-action-get-previous-tag@385a2a0b6abf6c2efeb95adfac83d96d6f968e0c # pin@v1
26+
uses: WyriHaximus/github-action-get-previous-tag@04e8485ecb6487243907e330d522ff60f02283ce # pin@v1
2727
env:
2828
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2929

.github/workflows/unit-tests.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
jobs:
77
unit-tests-on-ubuntu:
88
runs-on: ubuntu-latest
9+
strategy:
10+
matrix:
11+
python-version: [ '3.9','3.10','3.11', '3.12' ]
912
steps:
1013
- name: Clone Repository
1114
uses: actions/checkout@v3
@@ -14,9 +17,9 @@ jobs:
1417
run: sudo apt-get update -y
1518

1619
- name: Setup Python
17-
uses: actions/setup-python@v4
20+
uses: actions/setup-python@v5
1821
with:
19-
python-version: '3.x'
22+
python-version: ${{ matrix.python-version }}
2023

2124
- name: Install Python wheel
2225
run: pip install wheel boto3

Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ testunit:
7272
@orig_xdg_config_home=$${XDG_CONFIG_HOME:-}; \
7373
export LINODE_CLI_TEST_MODE=1 XDG_CONFIG_HOME=/tmp/linode/.config; \
7474
pytest -v tests/unit; \
75-
export XDG_CONFIG_HOME=$$orig_xdg_config_home
75+
exit_code=$$?; \
76+
export XDG_CONFIG_HOME=$$orig_xdg_config_home; \
77+
exit $$exit_code
7678

7779
.PHONY: testint
7880
testint:

linodecli/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,5 @@ def main(): # pylint: disable=too-many-branches,too-many-statements
230230
if parsed.help:
231231
print_help_action(cli, parsed.command, parsed.action)
232232
sys.exit(ExitCodes.SUCCESS)
233+
233234
cli.handle_command(parsed.command, parsed.action, args)

linodecli/api_request.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,13 @@ def _build_request_body(ctx, operation, parsed_args) -> Optional[str]:
259259
if ctx.defaults:
260260
parsed_args = ctx.config.update(parsed_args, operation.allowed_defaults)
261261

262+
param_names = {param.name for param in operation.params}
263+
262264
expanded_json = {}
263265

264266
# expand paths
265267
for k, v in vars(parsed_args).items():
266-
if v is None:
268+
if v is None or k in param_names:
267269
continue
268270

269271
cur = expanded_json

linodecli/baked/operation.py

+28-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import openapi3.paths
1818
from openapi3.paths import Operation, Parameter
1919

20+
from linodecli.baked.parsing import simplify_description
2021
from linodecli.baked.request import OpenAPIFilteringRequest, OpenAPIRequest
2122
from linodecli.baked.response import OpenAPIResponse
2223
from linodecli.exit_codes import ExitCodes
@@ -356,8 +357,12 @@ def __init__(
356357
self.action_aliases = {}
357358
self.action = action
358359

359-
self.summary = operation.summary
360-
self.description = operation.description.split(".")[0]
360+
# Ensure the summary has punctuation
361+
self.summary = operation.summary.rstrip(".") + "."
362+
363+
self.description_rich, self.description = simplify_description(
364+
operation.description or ""
365+
)
361366

362367
# The apiVersion attribute should not be specified as a positional argument
363368
self.params = [
@@ -366,6 +371,20 @@ def __init__(
366371
if param.name not in {"apiVersion"}
367372
]
368373

374+
# Validation to ensure no conflicting arguments & param names are found.
375+
# This is necessary because arguments and parameters are both parsed into the
376+
# same result namespace by argparse.
377+
if self.request is not None and hasattr(self.request, "attrs"):
378+
param_names = {param.name for param in self.params}
379+
380+
for attr in self.request.attrs:
381+
if attr not in param_names:
382+
continue
383+
384+
raise ValueError(
385+
f"Attribute {attr.name} conflicts with parameter of the same name"
386+
)
387+
369388
(
370389
self.url_base,
371390
self.url_path,
@@ -447,7 +466,13 @@ def _resolve_api_version(
447466
None,
448467
)
449468
if version_param is not None:
450-
return version_param.schema.default
469+
schema = version_param.schema
470+
471+
if schema.default:
472+
return schema.default
473+
474+
if schema.enum and len(schema.enum) > 0:
475+
return schema.enum[0]
451476

452477
return None
453478

linodecli/baked/parsing.py

+31-23
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
import functools
66
import re
77
from html import unescape
8-
from typing import List, Tuple
8+
from typing import List, Optional, Tuple
99

1010
# Sentence delimiter, split on a period followed by any type of
1111
# whitespace (space, new line, tab, etc.)
12-
REGEX_SENTENCE_DELIMITER = re.compile(r"\W(?:\s|$)")
12+
REGEX_SENTENCE_DELIMITER = re.compile(r"\.(?:\s|$)", flags=re.M)
1313

1414
# Matches on pattern __prefix__ at the beginning of a description
1515
# or after a comma
16-
REGEX_TECHDOCS_PREFIX = re.compile(r"(?:, |\A)__([\w-]+)__")
16+
REGEX_TECHDOCS_PREFIX = re.compile(r"(?:, |\A)__([^_]+)__")
1717

1818
# Matches on pattern [link title](https://.../)
1919
REGEX_MARKDOWN_LINK = re.compile(r"\[(?P<text>.*?)]\((?P<link>.*?)\)")
@@ -121,23 +121,35 @@ def get_short_description(description: str) -> str:
121121
:rtype: set
122122
"""
123123

124-
target_lines = description.splitlines()
125-
relevant_lines = None
126-
127-
for i, line in enumerate(target_lines):
124+
def __simplify(sentence: str) -> Optional[str]:
128125
# Edge case for descriptions starting with a note
129-
if line.lower().startswith("__note__"):
130-
continue
126+
if sentence.lower().startswith("__note__"):
127+
return None
128+
129+
sentence = strip_techdocs_prefixes(sentence)
131130

132-
relevant_lines = target_lines[i:]
133-
break
131+
# Check that the sentence still has content after stripping prefixes
132+
if len(sentence) < 2:
133+
return None
134134

135-
if relevant_lines is None:
135+
return sentence + "."
136+
137+
# Find the first relevant sentence
138+
result = next(
139+
simplified
140+
for simplified in iter(
141+
__simplify(sentence)
142+
for sentence in REGEX_SENTENCE_DELIMITER.split(description)
143+
)
144+
if simplified is not None
145+
)
146+
147+
if result is None:
136148
raise ValueError(
137149
f"description does not contain any relevant lines: {description}",
138150
)
139151

140-
return REGEX_SENTENCE_DELIMITER.split("\n".join(relevant_lines), 1)[0] + "."
152+
return result
141153

142154

143155
def strip_techdocs_prefixes(description: str) -> str:
@@ -150,14 +162,10 @@ def strip_techdocs_prefixes(description: str) -> str:
150162
:returns: The stripped description
151163
:rtype: str
152164
"""
153-
result_description = REGEX_TECHDOCS_PREFIX.sub(
154-
"", description.lstrip()
155-
).lstrip()
156-
157-
return result_description
165+
return REGEX_TECHDOCS_PREFIX.sub("", description.lstrip()).lstrip()
158166

159167

160-
def process_arg_description(description: str) -> Tuple[str, str]:
168+
def simplify_description(description: str) -> Tuple[str, str]:
161169
"""
162170
Processes the given raw request argument description into one suitable
163171
for help pages, etc.
@@ -173,12 +181,12 @@ def process_arg_description(description: str) -> Tuple[str, str]:
173181
return "", ""
174182

175183
result = get_short_description(description)
176-
result = strip_techdocs_prefixes(result)
177184
result = result.replace("\n", " ").replace("\r", " ")
178185

179-
description, links = extract_markdown_links(result)
186+
# NOTE: Links should only be separated from Rich Markdown links
187+
result_no_links, links = extract_markdown_links(result)
180188

181189
if len(links) > 0:
182-
description += f" See: {'; '.join(links)}"
190+
result_no_links += f" See: {'; '.join(links)}"
183191

184-
return unescape(markdown_to_rich_markup(description)), unescape(description)
192+
return unescape(markdown_to_rich_markup(result_no_links)), unescape(result)

0 commit comments

Comments
 (0)