Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
426 changes: 426 additions & 0 deletions .devcontainer/.devcontainer.postcreate.sh

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .devcontainer/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.14.1
96 changes: 96 additions & 0 deletions .devcontainer/devcontainer-functions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env bash

# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

log_info() {
echo -e "${CYAN}[INFO]${NC} $1"
}

log_success() {
echo -e "${GREEN}[DONE]${NC} $1"
}

log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
WARNINGS+=("$1")
}

log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}

exit_with_error() {
log_error "$1"
exit 1
}

asdf_plugin_installed() {
asdf plugin list | grep -q "^$1$"
}

install_asdf_plugin() {
local plugin=$1
if asdf_plugin_installed "$plugin"; then
log_info "Plugin '${plugin}' already installed"
else
log_info "Installing asdf plugin: ${plugin}"
if ! asdf plugin add "${plugin}"; then
log_warn "❌ Failed to add asdf plugin: ${plugin}"
return 1
fi
fi
}

install_with_pipx() {
local package="$1"
local container_user="${CONTAINER_USER:?CONTAINER_USER must be set}"
if is_wsl; then
# WSL compatibility: Do not use sudo -u in WSL as it fails
if ! python -m pipx install "${package}"; then
exit_with_error "Failed to install ${package} with pipx in WSL environment"
fi
else
# Non-WSL: Use sudo -u to ensure correct user environment
if ! sudo -u "${container_user}" bash -c "export PATH=\"\$PATH:/home/${container_user}/.local/bin\" && source /home/${container_user}/.asdf/asdf.sh && python -m pipx install '${package}'"; then
exit_with_error "Failed to install ${package} with pipx in non-WSL environment"
fi
fi
}

is_wsl() {
uname -r | grep -i microsoft > /dev/null
}

write_file_with_wsl_compat() {
local file_path="$1"
local content="$2"
local permissions="${3:-}"

if is_wsl; then
echo "$content" | sudo tee "$file_path" > /dev/null
if [ -n "$permissions" ]; then
sudo chmod "$permissions" "$file_path"
fi
else
echo "$content" > "$file_path"
if [ -n "$permissions" ]; then
chmod "$permissions" "$file_path"
fi
fi
}

append_to_file_with_wsl_compat() {
local file_path="$1"
local content="$2"

if is_wsl; then
echo "$content" | sudo tee -a "$file_path" > /dev/null
else
echo "$content" >> "$file_path"
fi
}
99 changes: 99 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
{
"name": "caylent-devcontainer",
"remoteUser": "vscode",
"image": "mcr.microsoft.com/devcontainers/base:noble",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"features": {
"ghcr.io/devcontainers/features/aws-cli:1": {
"version": "latest"
},
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {
"version": "latest"
},
"ghcr.io/devcontainers/features/common-utils:2": {
"version": "latest"
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "latest"
}
},
"customizations": {
"vscode": {
"extensions": [
"amazonwebservices.amazon-q-vscode",
"aws-scripting-guy.cform",
"amazonwebservices.aws-toolkit-vscode",
"cschlosser.doxdocgen",
"eamodio.gitlens",
"GitHub.copilot",
"GitHub.copilot-chat",
"GitHub.vscode-pull-request-github",
"GitHub.vscode-github-actions",
"ms-azuretools.vscode-docker",
"ms-python.debugpy",
"charliermarsh.ruff",
"ms-python.python",
"ms-python.vscode-pylance",
"ms-vscode.makefile-tools",
"redhat.vscode-yaml",
"streetsidesoftware.code-spell-checker",
"wayou.vscode-todo-highlight",
"wholroyd.jinja"
],
"settings": {
"terminal.integrated.defaultProfile.linux": "zsh",
"gitlens.currentLine.enabled": false,
"gitlens.codeLens.enabled": false,
"gitlens.hovers.enabled": false,
"gitlens.statusBar.enabled": false,
"gitlens.blame.line.enabled": false,
"gitlens.blame.file.enabled": false,
"python.defaultInterpreterPath": "/home/vscode/.asdf/installs/python/${env:DEFAULT_PYTHON_VERSION}/bin/python",
"python.linting.enabled": true,
"python.defaultFormatter": "charliermarsh.ruff",
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
}
},
"editor.formatOnSave": true,
"files.associations": {
"*.yml": "yaml",
"*.yaml": "yaml",
"Dockerfile*": "dockerfile",
"*.sql": "sql"
},
"makefile.extensionOutputFolder": ".vscode",
"docker.attachShellCommand.linuxContainer": "/bin/bash"
}
}
},
"forwardPorts": [
5678
],
"portsAttributes": {
"5678": {
"label": "Python Debug (debugpy)"
}
},
"userEnvProbe": "loginInteractiveShell",
"containerEnv": {
"AWS_CONFIG_ENABLED": "${localEnv:AWS_CONFIG_ENABLED}",
"CICD": "${localEnv:CICD}",
"DEFAULT_GIT_BRANCH": "${localEnv:DEFAULT_GIT_BRANCH}",
"DEFAULT_PYTHON_VERSION": "${localEnv:DEFAULT_PYTHON_VERSION}",
"DEVELOPER_NAME": "${localEnv:DEVELOPER_NAME}",
"DEVCONTAINER": "true",
"EXTRA_APT_PACKAGES": "${localEnv:EXTRA_APT_PACKAGES}",
"GIT_PROVIDER_URL": "${localEnv:GIT_PROVIDER_URL}",
"GIT_TOKEN": "${localEnv:GIT_TOKEN}",
"GIT_USER": "${localEnv:GIT_USER}",
"GIT_USER_EMAIL": "${localEnv:GIT_USER_EMAIL}",
"PAGER": "${localEnv:PAGER}",
"AWS_DEFAULT_OUTPUT": "${localEnv:AWS_DEFAULT_OUTPUT}",
"BASH_ENV": "${containerWorkspaceFolder}/shell.env"
},
"postCreateCommand": "bash -c 'exec > >(tee /tmp/devcontainer-setup.log) 2>&1; if uname -r | grep -i microsoft > /dev/null; then sudo apt-get update && sudo apt-get install -y gettext-base jq python3 && find .devcontainer -type f -exec sed -i \"s/\\r$//\" {} + && python3 .devcontainer/fix-line-endings.py && sudo apt-get remove -y python3 && sudo apt-get autoremove -y && source shell.env && exec bash .devcontainer/.devcontainer.postcreate.sh vscode; else sudo apt-get update && sudo apt-get install -y gettext-base jq && sudo bash .devcontainer/.devcontainer.postcreate.sh vscode; fi && echo \"Setup complete. View logs: cat /tmp/devcontainer-setup.log\" && exit 0'"
}
112 changes: 112 additions & 0 deletions .devcontainer/fix-line-endings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""
Convert Windows line endings (CRLF) to Unix line endings (LF) for all text files.
Excludes binary files and processes files in parallel for performance.
"""

import os
import sys
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
from threading import Lock

# Thread-safe counters
stats_lock = Lock()
converted_count = 0
skipped_count = 0
error_count = 0

def convert_file(file_path):
"""Convert a single file from CRLF to LF if it's a text file."""
global converted_count, skipped_count, error_count

try:
with open(file_path, 'rb') as f:
content = f.read()

if not content:
with stats_lock:
skipped_count += 1
return False

# Skip binary files (contain null bytes)
if b'\x00' in content:
with stats_lock:
skipped_count += 1
return False

# Skip if no CRLF to convert
if b'\r\n' not in content:
with stats_lock:
skipped_count += 1
return False

# Convert CRLF to LF
converted_content = content.replace(b'\r\n', b'\n')

with open(file_path, 'wb') as f:
f.write(converted_content)

with stats_lock:
converted_count += 1
return True

except (OSError, IOError) as e:
with stats_lock:
error_count += 1
print(f"Error processing {file_path}: {e}", file=sys.stderr)
return False

def main():
"""Main function to process all files in the workspace."""
global converted_count, skipped_count, error_count
workspace_path = Path(os.getcwd())

if not workspace_path.exists():
print(f"Error: Workspace path {workspace_path} does not exist", file=sys.stderr)
sys.exit(1)

print(f"Converting line endings in: {workspace_path}")

# Find all files
files_to_process = []
for root, dirs, files in os.walk(workspace_path):
# Skip .git directory for performance
if '.git' in dirs:
dirs.remove('.git')

for file in files:
file_path = Path(root) / file
files_to_process.append(file_path)

if not files_to_process:
print("No files found to process")
return

print(f"Processing {len(files_to_process)} files...")

# Process files in parallel
max_workers = os.cpu_count() or 1
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(convert_file, file_path): file_path
for file_path in files_to_process}

for future in as_completed(futures):
try:
future.result()
except Exception as e:
file_path = futures[future]
print(f"Unexpected error processing {file_path}: {e}", file=sys.stderr)
with stats_lock:
error_count += 1

# Print summary
print(f"Line ending conversion complete:")
print(f" Converted: {converted_count} files")
print(f" Skipped: {skipped_count} files")
if error_count > 0:
print(f" Errors: {error_count} files")
sys.exit(1)

if __name__ == "__main__":
main()
10 changes: 10 additions & 0 deletions .devcontainer/project-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

set -euo pipefail

# Source shared functions
source "$(dirname "$0")/devcontainer-functions.sh"

make install

log_info "Project-specific setup complete"
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ exclude =
.venv,
venv,
.tox,
.devcontainer/fix-line-endings.py,
9 changes: 9 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

set -e

echo "Running pre-push checks..."

make pre-commit

echo "✓ All pre-push checks passed!"
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*.log
*.pyc
__pycache__
build
/dist
.repopickle_*
/repoc
Expand All @@ -11,3 +12,9 @@ __pycache__

# PyCharm related
/.idea/

# Environment files
shell.env
devcontainer-environment-variables.json
.devcontainer/aws-profile-map.json
.coverage
2 changes: 1 addition & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ force_sort_within_sections = true
order_by_type = false

# Ignore generated files.
extend_skip_glob = *_pb2.py
extend_skip_glob = *_pb2.py,.devcontainer/fix-line-endings.py

# Allow importing multiple classes on a single line from these modules.
# https://google.github.io/styleguide/pyguide#s2.2-imports
Expand Down
2 changes: 2 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pre-commit 4.3.0
python 3.12.9
Loading