Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
21a8120
build(deps): bump docker/setup-qemu-action from 3.3.0 to 3.6.0
dependabot[bot] Mar 1, 2025
952d844
build(deps): bump docker/setup-buildx-action from 3.8.0 to 3.10.0
dependabot[bot] Mar 1, 2025
7d7ddcf
build(deps): bump crazy-max/ghaction-github-labeler from 5.1.0 to 5.2…
dependabot[bot] Mar 3, 2025
3587441
SSH login by domain (#723)
jrouzierinverse Mar 3, 2025
2cd1160
build(deps): bump docker/build-push-action from 6.13.0 to 6.15.0 (#741)
dependabot[bot] Mar 5, 2025
9146e20
Update image view integration test (#738)
yec-akamai Mar 5, 2025
986ce6e
Merge pull request #740 from linode/dependabot/github_actions/docker/…
jriddle-linode Mar 11, 2025
69f3c69
Merge pull request #739 from linode/dependabot/github_actions/docker/…
jriddle-linode Mar 11, 2025
e0d17e3
Print OBJ plugin errors to stderr (#737)
ezilber-akamai Mar 11, 2025
0ed9d73
Added integration tests for suspend and resume (#743)
ezilber-akamai Mar 11, 2025
6ff1828
Add test summary to slack notification (#744)
ykim-akamai Mar 14, 2025
679561f
Improve maintainability of arg_helpers.py (#745)
ezilber-akamai Mar 17, 2025
533e9ce
Improve maintainability of api_request.py (#748)
ezilber-akamai Mar 18, 2025
7ad1df4
Add conditions to notification job in e2e workflow (#747)
ykim-akamai Mar 18, 2025
a939ff5
Remove deprecated warning and update license format to latest format …
ykim-akamai Mar 21, 2025
6583a5f
Configure Python `logging` level when debug flag is specified; add de…
lgarber-akamai Mar 31, 2025
2fd248f
Add openapi.json to .gitignore; drop from repository (#752)
lgarber-akamai Mar 31, 2025
7463089
build(deps): bump crazy-max/ghaction-github-labeler from 5.2.0 to 5.3…
dependabot[bot] Apr 1, 2025
fe7d229
build(deps): bump docker/login-action from 3.3.0 to 3.4.0 (#754)
dependabot[bot] Apr 2, 2025
5612fe9
Add object-storage integration tests (#750)
ykim-akamai Apr 2, 2025
740353d
Error on missing x-linode-cli-command extension; log when operationId…
lgarber-akamai Apr 11, 2025
723c441
fix the failing test for Linode CLI (#759)
vshanthe Apr 17, 2025
3f7f52b
Add support for custom aliases for commands (#755)
ezilber-akamai Apr 22, 2025
257cb72
Update LA Disk Encryption Capability (#760)
vshanthe Apr 24, 2025
f6c9fe2
Add LKE tier integration tests (#758)
ykim-akamai Apr 28, 2025
f96193e
Fixed domain test failures caused by `--target-command` argument (#764)
ezilber-akamai Apr 29, 2025
af0da8c
Rename integration test workflows; cleanup an unnecessary step (#762)
zliang-akamai Apr 30, 2025
91678ed
Trusted publisher for PyPI (#763)
zliang-akamai Apr 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .github/workflows/e2e-suite-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ on:
description: 'The number of the PR.'
required: false

name: PR E2E Tests
name: Integration Tests on Windows

jobs:
integration-fork-windows:
Expand Down Expand Up @@ -65,9 +65,6 @@ jobs:
with:
python-version: '3.x'

- name: Install Python deps
run: pip install .[obj,dev]

- name: Install the CLI
run: make install
env:
Expand Down
34 changes: 28 additions & 6 deletions .github/workflows/e2e-suite.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Integration Tests
name: Integration Tests on Linux

on:
workflow_dispatch:
Expand Down Expand Up @@ -97,9 +97,6 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Update system packages
run: sudo apt-get update -y

- name: Setup Python
uses: actions/setup-python@v5
with:
Expand Down Expand Up @@ -235,6 +232,8 @@ jobs:
runs-on: ubuntu-latest
needs: [integration_tests]
if: always() && github.repository == 'linode/linode-cli' # Run even if integration tests fail and only on main repository
outputs:
summary: ${{ steps.set-test-summary.outputs.summary }}

steps:
- name: Checkout code
Expand Down Expand Up @@ -275,14 +274,26 @@ jobs:
LINODE_CLI_OBJ_ACCESS_KEY: ${{ secrets.LINODE_CLI_OBJ_ACCESS_KEY }}
LINODE_CLI_OBJ_SECRET_KEY: ${{ secrets.LINODE_CLI_OBJ_SECRET_KEY }}

- name: Generate test summary and save to output
id: set-test-summary
run: |
filename=$(ls | grep -E '^[0-9]{12}_cli_test_report\.xml$')
test_output=$(python3 e2e_scripts/tod_scripts/generate_test_summary.py "${filename}")
{
echo 'summary<<EOF'
echo "$test_output"
echo EOF
} >> "$GITHUB_OUTPUT"


notify-slack:
runs-on: ubuntu-latest
needs: [integration_tests]
needs: [integration_tests, process-upload-report]
if: ${{ (success() || failure()) && github.repository == 'linode/linode-cli' }} # Run even if integration tests fail and only on main repository

steps:
- name: Notify Slack
id: main_message
uses: slackapi/[email protected]
with:
method: chat.postMessage
Expand All @@ -293,7 +304,7 @@ jobs:
- type: section
text:
type: mrkdwn
text: ":rocket: *${{ github.workflow }} Completed in: ${{ github.repository }}* :white_check_mark:"
text: ":rocket: *${{ github.workflow }} Completed in: ${{ github.repository }}* ${{ needs.integration_tests.result == 'success' && ':white_check_mark:' || ':failed:' }}"
- type: divider
- type: section
fields:
Expand All @@ -312,3 +323,14 @@ jobs:
elements:
- type: mrkdwn
text: "Triggered by: :bust_in_silhouette: `${{ github.actor }}`"

- name: Test summary thread
if: success()
uses: slackapi/[email protected]
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: ${{ secrets.SLACK_CHANNEL_ID }}
thread_ts: "${{ steps.main_message.outputs.ts }}"
text: "${{ needs.process-upload-report.outputs.summary }}"
2 changes: 1 addition & 1 deletion .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
uses: actions/checkout@v4
-
name: Run Labeler
uses: crazy-max/ghaction-github-labeler@b54af0c25861143e7c8813d7cbbf46d2c341680c
uses: crazy-max/ghaction-github-labeler@24d110aa46a59976b8a7f35518cb7f14f434c916
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
yaml-file: .github/labels.yml
Expand Down
14 changes: 8 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ jobs:
run: make requirements

- name: Set up QEMU
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # pin@v3.3.0
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # pin@v3.6.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # pin@v3.8.0
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # pin@v3.10.0

- name: Login to Docker Hub
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # pin@v3.3.0
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # pin@v3.4.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Expand All @@ -67,7 +67,7 @@ jobs:
result-encoding: string

- name: Build and push to DockerHub
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # pin@v6.13.0
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # pin@v6.15.0
with:
context: .
file: Dockerfile
Expand All @@ -79,7 +79,11 @@ jobs:
github_token=${{ secrets.GITHUB_TOKEN }}

pypi-release:
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
runs-on: ubuntu-latest
environment: pypi-release
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -103,5 +107,3 @@ jobs:

- name: Publish the release artifacts to PyPI
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # pin@release/v1.12.4
with:
password: ${{ secrets.PYPI_API_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ test/.env
MANIFEST
venv
openapi*.yaml
openapi*.json
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ VERSION_FILE := ./linodecli/version.py
VERSION_MODULE_DOCSTRING ?= \"\"\"\nThe version of the Linode CLI.\n\"\"\"\n\n
LINODE_CLI_VERSION ?= "0.0.0.dev"

BAKE_FLAGS := --debug

.PHONY: install
install: check-prerequisites requirements build
pip3 install --force dist/*.whl
Expand All @@ -21,7 +23,7 @@ bake: clean
ifeq ($(SKIP_BAKE), 1)
@echo Skipping bake stage
else
python3 -m linodecli bake ${SPEC} --skip-config
python3 -m linodecli bake ${SPEC} --skip-config $(BAKE_FLAGS)
cp data-3 linodecli/
endif

Expand Down
74 changes: 61 additions & 13 deletions linodecli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""

import argparse
import logging
import os
import sys
from importlib.metadata import version
Expand Down Expand Up @@ -38,12 +39,14 @@

TEST_MODE = os.getenv("LINODE_CLI_TEST_MODE") == "1"

# Configure the `logging` package log level depending on the --debug flag.
logging.basicConfig(
level=logging.DEBUG if "--debug" in argv else logging.WARNING,
)

# if any of these arguments are given, we don't need to prompt for configuration
skip_config = (
any(
c in argv
for c in ["--skip-config", "--help", "--version", "completion"]
)
any(c in argv for c in ["--skip-config", "--version", "completion"])
or TEST_MODE
)

Expand Down Expand Up @@ -104,6 +107,43 @@ def main(): # pylint: disable=too-many-branches,too-many-statements
# if not spec was found and we weren't baking, we're doomed
sys.exit(ExitCodes.ARGUMENT_ERROR)

if parsed.command in ("set-custom-alias", "remove-custom-alias"):
if not parsed.alias_command or not parsed.alias:
print(
"Both --alias-command and --alias must be provided.",
file=sys.stderr,
)
sys.exit(ExitCodes.ARGUMENT_ERROR)

command = parsed.alias_command
alias = parsed.alias

if command not in cli.ops:
print(
f"Error: '{command}' is not a valid command.", file=sys.stderr
)
sys.exit(ExitCodes.ARGUMENT_ERROR)

if parsed.command == "set-custom-alias":
if (alias, command) not in cli.config.get_custom_aliases().items():
cli.config.set_custom_alias(alias, command)
print(f"Custom alias '{alias}' set for command '{command}'")
else:
print(
f"Custom alias '{alias}' already set for command '{command}'"
)

if parsed.command == "remove-custom-alias":
if (alias, command) in cli.config.get_custom_aliases().items():
cli.config.remove_custom_alias(alias, command)
print(f"Custom alias '{alias}' removed for command '{command}'")
else:
print(
f"Custom alias '{alias}' does not exist for command '{command}'"
)

sys.exit(ExitCodes.SUCCESS)

if parsed.command == "register-plugin":
if parsed.action is None:
print("register-plugin requires a module name!", file=sys.stderr)
Expand Down Expand Up @@ -207,28 +247,36 @@ def main(): # pylint: disable=too-many-branches,too-many-statements
plugin_args.remove(parsed.command) # don't include the plugin name
plugins.invoke(parsed.command, plugin_args, context)
sys.exit(ExitCodes.SUCCESS)

# unknown commands
if (
parsed.command not in cli.ops
and parsed.command not in plugins.available(cli.config)
and parsed.command not in HELP_TOPICS
and parsed.command not in cli.config.get_custom_aliases().keys()
):
print(f"Unrecognized command {parsed.command}", file=sys.stderr)
sys.exit(ExitCodes.UNRECOGNIZED_COMMAND)

# handle a help for a command - either --help or no action triggers this
if (
parsed.command is not None
and parsed.action is None
and parsed.command in cli.ops
):
print_help_command_actions(cli.ops, parsed.command)
sys.exit(ExitCodes.SUCCESS)
if parsed.command is not None and parsed.action is None:
if parsed.command in cli.ops:
print_help_command_actions(cli.ops, parsed.command)
sys.exit(ExitCodes.SUCCESS)
if parsed.command in cli.config.get_custom_aliases().keys():
print_help_command_actions(
cli.ops, cli.config.get_custom_aliases()[parsed.command]
)

if parsed.command is not None and parsed.action is not None:
if parsed.help:
print_help_action(cli, parsed.command, parsed.action)
if parsed.command in cli.config.get_custom_aliases().keys():
print_help_action(
cli,
cli.config.get_custom_aliases()[parsed.command],
parsed.action,
)
else:
print_help_action(cli, parsed.command, parsed.action)
sys.exit(ExitCodes.SUCCESS)

cli.handle_command(parsed.command, parsed.action, args)
Loading
Loading