aws-lambda-layer-cli 2.1.3 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -7
- package/completion/aws-lambda-layer-completion.bash +1 -1
- package/completion/aws-lambda-layer-completion.zsh +1 -1
- package/package.json +1 -1
- package/scripts/aws-lambda-layer-cli +161 -78
- package/scripts/build_pypi.sh +2 -1
- package/scripts/create_python_layer.sh +20 -33
- package/scripts/create_wheel_layer.sh +272 -35
- package/scripts/install.ps1 +1 -1
- package/scripts/uninstall.ps1 +0 -0
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@ A command-line tool for creating and publishing AWS Lambda layers for Node.js an
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- Create and publish
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
7
|
+
- **Effortless Publishing**: Create and publish Node.js and Python layers in a single command
|
|
8
|
+
- **Smart Compatibility**: Auto-selects the right Linux binaries for Amazon Linux 2 or 2023
|
|
9
|
+
- **Cross-Architecture**: Native support for `x86_64` and `arm64` builds
|
|
10
|
+
- **Auto-Versioning**: Automatically handles layer naming and version increments
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
@@ -46,12 +46,12 @@ aws-lambda-layer-cli <command> [options]
|
|
|
46
46
|
|--------|-------------|
|
|
47
47
|
| `--nodejs, -n <pkgs>` | Create Node.js layer (comma-separated packages) |
|
|
48
48
|
| `--python, -p <pkgs>` | Create Python layer (comma-separated packages) |
|
|
49
|
+
| `--wheel, -w <file>` | Use with `--python` to create layer from `.whl` file |
|
|
49
50
|
| `--name` | Custom layer name |
|
|
50
51
|
| `--description` | Layer description (publish only) |
|
|
51
52
|
| `--profile` | AWS CLI profile (publish only) |
|
|
52
53
|
| `--region` | AWS region (publish only) |
|
|
53
54
|
| `--architecture, -a` | Target architecture (`x86_64` or `arm64`) |
|
|
54
|
-
| `--platform` | Target platform tag (Python only, e.g. `manylinux_2_28_aarch64`) |
|
|
55
55
|
| `--node-version` | Node.js version (default: 24) |
|
|
56
56
|
| `--python-version` | Python version (default: 3.14) |
|
|
57
57
|
| `-v, --version` | Show version |
|
|
@@ -72,9 +72,23 @@ aws-lambda-layer-cli publish --nodejs lodash --profile prod --region us-east-1 -
|
|
|
72
72
|
# Create local zip with specific python version and architecture
|
|
73
73
|
aws-lambda-layer-cli zip --python numpy==1.26.0,pandas --python-version 3.12 --architecture arm64
|
|
74
74
|
|
|
75
|
-
# Publish to AWS
|
|
76
|
-
aws-lambda-layer-cli publish --python requests --name web-layer --
|
|
75
|
+
# Publish to AWS for ARM64 architecture
|
|
76
|
+
aws-lambda-layer-cli publish --python requests --name web-layer --architecture arm64
|
|
77
77
|
```
|
|
78
|
+
> **Note**: This tool automatically selects the optimal platform based on the Python version:
|
|
79
|
+
> - **Python 3.12+ (Amazon Linux 2023)**: Targets `manylinux_2_28` (GLIBC 2.28+)
|
|
80
|
+
> - **Python 3.11- (Amazon Linux 2)**: Targets `manylinux2014` (GLIBC 2.17+)
|
|
81
|
+
|
|
82
|
+
### Wheel File
|
|
83
|
+
The tool auto-detects Python version and architecture from the wheel filename.
|
|
84
|
+
```bash
|
|
85
|
+
# Create local zip from wheel (preferred syntax)
|
|
86
|
+
aws-lambda-layer-cli zip --python --wheel numpy-2.4.1-cp313-cp313-manylinux.whl
|
|
87
|
+
|
|
88
|
+
# Publish directly from wheel
|
|
89
|
+
aws-lambda-layer-cli publish --python --wheel pandas-2.1.0-cp311-...-x86_64.whl
|
|
90
|
+
```
|
|
91
|
+
> **Note**: For wheels, arguments like `--python-version` or `--architecture` are checked against the wheel metadata. If they conflict, the tool will error to prevent incompatibility.
|
|
78
92
|
|
|
79
93
|
## Shell Completion
|
|
80
94
|
|
|
@@ -13,7 +13,7 @@ _aws_lambda_layer_cli() {
|
|
|
13
13
|
local common_opts="--name -h --help"
|
|
14
14
|
local publish_opts="--description --layer-name --profile --region"
|
|
15
15
|
local node_opts="--node-version"
|
|
16
|
-
local python_opts="--python-version --
|
|
16
|
+
local python_opts="--python-version --platform"
|
|
17
17
|
|
|
18
18
|
case ${cword} in
|
|
19
19
|
1)
|
package/package.json
CHANGED
|
@@ -41,13 +41,19 @@ else
|
|
|
41
41
|
PYTHON_SCRIPT="$INSTALL_DIR/create_python_layer.sh"
|
|
42
42
|
fi
|
|
43
43
|
|
|
44
|
+
if [ -f "$SCRIPT_DIR/create_wheel_layer.sh" ]; then
|
|
45
|
+
WHEEL_SCRIPT="$SCRIPT_DIR/create_wheel_layer.sh"
|
|
46
|
+
else
|
|
47
|
+
WHEEL_SCRIPT="$INSTALL_DIR/create_wheel_layer.sh"
|
|
48
|
+
fi
|
|
49
|
+
|
|
44
50
|
BIN_DIR="/usr/local/bin"
|
|
45
51
|
COMPLETION_DIR="/etc/bash_completion.d"
|
|
46
52
|
|
|
47
53
|
# Show help
|
|
48
54
|
show_help() {
|
|
49
55
|
local version_file="$SCRIPT_DIR/VERSION.txt"
|
|
50
|
-
local version="2.
|
|
56
|
+
local version="2.2.0"
|
|
51
57
|
if [ -f "$version_file" ]; then
|
|
52
58
|
version=$(cat "$version_file")
|
|
53
59
|
fi
|
|
@@ -56,8 +62,10 @@ show_help() {
|
|
|
56
62
|
printf "${BLUE}Usage:${NC}\n"
|
|
57
63
|
printf " aws-lambda-layer-cli ${GREEN}zip${NC} ${YELLOW}--nodejs${NC} <packages> [options]\n"
|
|
58
64
|
printf " aws-lambda-layer-cli ${GREEN}zip${NC} ${YELLOW}--python${NC} <packages> [options]\n"
|
|
65
|
+
printf " aws-lambda-layer-cli ${GREEN}zip${NC} ${YELLOW}--python --wheel${NC} <wheel_file> [options]\n"
|
|
59
66
|
printf " aws-lambda-layer-cli ${GREEN}publish${NC} ${YELLOW}--nodejs${NC} <packages> [options]\n"
|
|
60
67
|
printf " aws-lambda-layer-cli ${GREEN}publish${NC} ${YELLOW}--python${NC} <packages> [options]\n"
|
|
68
|
+
printf " aws-lambda-layer-cli ${GREEN}publish${NC} ${YELLOW}--python --wheel${NC} <wheel_file> [options]\n"
|
|
61
69
|
printf " aws-lambda-layer-cli ${GREEN}help${NC}\n"
|
|
62
70
|
printf " aws-lambda-layer-cli [options]\n\n"
|
|
63
71
|
|
|
@@ -72,8 +80,11 @@ show_help() {
|
|
|
72
80
|
printf " ${YELLOW}--nodejs, --node, -n${NC} Create a Node.js Lambda layer\n"
|
|
73
81
|
printf " ${YELLOW}--python, --py, -p${NC} Create a Python Lambda layer\n"
|
|
74
82
|
printf " ${YELLOW}--runtime=RUNTIME${NC} Specify runtime (nodejs or python)\n"
|
|
83
|
+
printf "${BLUE}Source Options (Python only):${NC}\n"
|
|
84
|
+
printf " ${YELLOW}--wheel, -w${NC} Create layer from existing Wheel file\n"
|
|
75
85
|
printf "${BLUE}Arguments:${NC}\n"
|
|
76
|
-
printf " <packages> Comma-separated list of packages
|
|
86
|
+
printf " <packages> Comma-separated list of packages\n"
|
|
87
|
+
printf " <wheel_file> Path to a local .whl file (use with --python --wheel)\n"
|
|
77
88
|
|
|
78
89
|
printf "${BLUE}Common Options:${NC}\n"
|
|
79
90
|
printf " ${YELLOW}--name${NC} Name for the output zip file / layer name\n"
|
|
@@ -85,8 +96,7 @@ show_help() {
|
|
|
85
96
|
printf " ${YELLOW}--version, -v${NC} Show tool version information\n"
|
|
86
97
|
printf " ${YELLOW}--node-version${NC} Node.js version (default: 24)\n"
|
|
87
98
|
printf " ${YELLOW}--python-version${NC} Python version (default: 3.14)\n"
|
|
88
|
-
printf " ${YELLOW}--architecture, -a${NC} Target architecture (x86_64, arm64)\n"
|
|
89
|
-
printf " ${YELLOW}--platform${NC} Target platform tag (Python only)\n\n"
|
|
99
|
+
printf " ${YELLOW}--architecture, -a${NC} Target architecture (x86_64, arm64)\n\n"
|
|
90
100
|
|
|
91
101
|
printf "${MAGENTA}${UNDERLINE}Package Version Examples:${NC}\n"
|
|
92
102
|
printf " Node.js: express@^4.0.0, lodash@~4.17.0, axios@>=1.6.0\n"
|
|
@@ -114,7 +124,7 @@ show_version() {
|
|
|
114
124
|
echo "v$version"
|
|
115
125
|
else
|
|
116
126
|
# Fallback if VERSION file is missing (e.g. during development or if moved)
|
|
117
|
-
echo "v2.
|
|
127
|
+
echo "v2.2.0"
|
|
118
128
|
fi
|
|
119
129
|
}
|
|
120
130
|
|
|
@@ -249,25 +259,6 @@ get_aws_account_info() {
|
|
|
249
259
|
esac
|
|
250
260
|
}
|
|
251
261
|
|
|
252
|
-
# Determine compatible runtimes for AWS
|
|
253
|
-
get_compatible_runtimes() {
|
|
254
|
-
local runtime="$1"
|
|
255
|
-
local version="2.1.3"
|
|
256
|
-
|
|
257
|
-
case "$runtime" in
|
|
258
|
-
nodejs)
|
|
259
|
-
echo "nodejs${version}.x"
|
|
260
|
-
;;
|
|
261
|
-
python)
|
|
262
|
-
# AWS Lambda uses format like python3.14, not python3.14.x
|
|
263
|
-
echo "python${version}"
|
|
264
|
-
;;
|
|
265
|
-
*)
|
|
266
|
-
echo ""
|
|
267
|
-
;;
|
|
268
|
-
esac
|
|
269
|
-
}
|
|
270
|
-
|
|
271
262
|
# Check dependencies
|
|
272
263
|
check_dependencies() {
|
|
273
264
|
local runtime="$1"
|
|
@@ -317,6 +308,7 @@ check_dependencies() {
|
|
|
317
308
|
# Zip command handler - creates local zip files
|
|
318
309
|
handle_zip() {
|
|
319
310
|
local runtime=""
|
|
311
|
+
local is_wheel=false
|
|
320
312
|
local packages=""
|
|
321
313
|
|
|
322
314
|
# Parse runtime flag
|
|
@@ -328,6 +320,10 @@ handle_zip() {
|
|
|
328
320
|
--python|--py|-p)
|
|
329
321
|
runtime="python"
|
|
330
322
|
shift
|
|
323
|
+
if [[ "${1:-}" == "--wheel" || "${1:-}" == "-w" || "${1:-}" == "--whl" ]]; then
|
|
324
|
+
is_wheel=true
|
|
325
|
+
shift
|
|
326
|
+
fi
|
|
331
327
|
;;
|
|
332
328
|
--runtime=*)
|
|
333
329
|
runtime="${1#*=}"
|
|
@@ -396,7 +392,7 @@ handle_zip() {
|
|
|
396
392
|
local current_dir=$(pwd)
|
|
397
393
|
cd "$output_dir"
|
|
398
394
|
|
|
399
|
-
# Pass arguments to the appropriate script
|
|
395
|
+
# Pass arguments to the appropriate script
|
|
400
396
|
if [ "$runtime" = "nodejs" ]; then
|
|
401
397
|
if [ ! -f "$NODE_SCRIPT" ]; then
|
|
402
398
|
cd "$current_dir"
|
|
@@ -410,23 +406,37 @@ handle_zip() {
|
|
|
410
406
|
cd "$current_dir"
|
|
411
407
|
exit $exit_code
|
|
412
408
|
elif [ "$runtime" = "python" ]; then
|
|
413
|
-
if [
|
|
409
|
+
if [ "$is_wheel" = true ]; then
|
|
410
|
+
if [ ! -f "$WHEEL_SCRIPT" ]; then
|
|
411
|
+
cd "$current_dir"
|
|
412
|
+
printf "${RED}Error: Wheel script not found at $WHEEL_SCRIPT${NC}\n"
|
|
413
|
+
exit 1
|
|
414
|
+
fi
|
|
415
|
+
printf "${BLUE}Creating Lambda layer from Wheel...${NC}\n"
|
|
416
|
+
bash "$WHEEL_SCRIPT" -w "$packages" "$@"
|
|
417
|
+
local exit_code=$?
|
|
414
418
|
cd "$current_dir"
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
419
|
+
exit $exit_code
|
|
420
|
+
else
|
|
421
|
+
if [ ! -f "$PYTHON_SCRIPT" ]; then
|
|
422
|
+
cd "$current_dir"
|
|
423
|
+
printf "${RED}Error: Python script not found at $PYTHON_SCRIPT${NC}\n"
|
|
424
|
+
printf "Please run scripts/install.sh first\n"
|
|
425
|
+
exit 1
|
|
426
|
+
fi
|
|
427
|
+
printf "${BLUE}Creating Python Lambda layer (local zip)...${NC}\n"
|
|
428
|
+
bash "$PYTHON_SCRIPT" -i "$packages" "$@"
|
|
429
|
+
local exit_code=$?
|
|
430
|
+
cd "$current_dir"
|
|
431
|
+
exit $exit_code
|
|
418
432
|
fi
|
|
419
|
-
printf "${BLUE}Creating Python Lambda layer (local zip)...${NC}\n"
|
|
420
|
-
bash "$PYTHON_SCRIPT" -i "$packages" "$@"
|
|
421
|
-
local exit_code=$?
|
|
422
|
-
cd "$current_dir"
|
|
423
|
-
exit $exit_code
|
|
424
433
|
fi
|
|
425
434
|
}
|
|
426
435
|
|
|
427
436
|
# Publish command handler - publishes layer directly to AWS
|
|
428
437
|
handle_publish() {
|
|
429
438
|
local runtime=""
|
|
439
|
+
local is_wheel=false
|
|
430
440
|
local description=""
|
|
431
441
|
local layer_name=""
|
|
432
442
|
local packages=""
|
|
@@ -512,6 +522,11 @@ handle_publish() {
|
|
|
512
522
|
--python|--py|-p)
|
|
513
523
|
runtime="python"
|
|
514
524
|
shift
|
|
525
|
+
# Check if the next argument is --wheel
|
|
526
|
+
if [[ "${1:-}" == "--wheel" || "${1:-}" == "-w" || "${1:-}" == "--whl" ]]; then
|
|
527
|
+
is_wheel=true
|
|
528
|
+
shift
|
|
529
|
+
fi
|
|
515
530
|
;;
|
|
516
531
|
--runtime=*)
|
|
517
532
|
runtime="${1#*=}"
|
|
@@ -544,14 +559,14 @@ handle_publish() {
|
|
|
544
559
|
esac
|
|
545
560
|
else
|
|
546
561
|
printf "${RED}Error: --runtime requires an argument${NC}\n"
|
|
547
|
-
printf "Example: --runtime=nodejs
|
|
562
|
+
printf "Example: --runtime=nodejs, --runtime python\n"
|
|
548
563
|
exit 1
|
|
549
564
|
fi
|
|
550
565
|
;;
|
|
551
566
|
*)
|
|
552
567
|
printf "${RED}Error: Missing or invalid runtime specification${NC}\n"
|
|
553
|
-
printf "Use --nodejs
|
|
554
|
-
printf "Or use --runtime=nodejs
|
|
568
|
+
printf "Use --nodejs (-n) or --python (-p)\n"
|
|
569
|
+
printf "Or use --runtime=nodejs, --runtime=python\n"
|
|
555
570
|
exit 1
|
|
556
571
|
;;
|
|
557
572
|
esac
|
|
@@ -561,7 +576,7 @@ handle_publish() {
|
|
|
561
576
|
packages="$1"
|
|
562
577
|
shift
|
|
563
578
|
else
|
|
564
|
-
printf "${RED}Error: Missing packages
|
|
579
|
+
printf "${RED}Error: Missing argument (packages or wheel file)${NC}\n"
|
|
565
580
|
printf "Usage: aws-lambda-layer publish --nodejs <packages> [options]\n"
|
|
566
581
|
printf "Example: aws-lambda-layer publish --nodejs express,axios --description \"My layer\"\n"
|
|
567
582
|
exit 1
|
|
@@ -591,7 +606,13 @@ handle_publish() {
|
|
|
591
606
|
printf "${BLUE}Building Lambda layer in output directory...${NC}\n"
|
|
592
607
|
|
|
593
608
|
# Build the layer using the appropriate script
|
|
594
|
-
local build_args=(
|
|
609
|
+
local build_args=()
|
|
610
|
+
if [ "$is_wheel" = true ]; then
|
|
611
|
+
build_args+=("-w" "$packages")
|
|
612
|
+
else
|
|
613
|
+
build_args+=("-i" "$packages")
|
|
614
|
+
fi
|
|
615
|
+
|
|
595
616
|
if [ -n "$layer_name" ]; then
|
|
596
617
|
build_args+=("--name" "${layer_name}.zip")
|
|
597
618
|
fi
|
|
@@ -635,37 +656,75 @@ handle_publish() {
|
|
|
635
656
|
local compatible_runtimes="nodejs${node_version}.x"
|
|
636
657
|
|
|
637
658
|
elif [ "$runtime" = "python" ]; then
|
|
638
|
-
if [
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
659
|
+
if [ "$is_wheel" = true ]; then
|
|
660
|
+
if [ ! -f "$WHEEL_SCRIPT" ]; then
|
|
661
|
+
cd "$current_dir"
|
|
662
|
+
printf "${RED}Error: Wheel script not found${NC}\n"
|
|
663
|
+
exit 1
|
|
664
|
+
fi
|
|
665
|
+
bash "$WHEEL_SCRIPT" "${build_args[@]}" 2>&1 | tee build.log
|
|
666
|
+
|
|
667
|
+
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
668
|
+
cd "$current_dir"
|
|
669
|
+
printf "${RED}Error: Wheel layer build failed${NC}\n"
|
|
670
|
+
printf "Check build log: $output_dir/build.log\n"
|
|
671
|
+
exit 1
|
|
672
|
+
fi
|
|
673
|
+
|
|
674
|
+
zip_file=$(grep -o "File: .*\.zip" "build.log" | cut -d' ' -f2 | tail -1)
|
|
675
|
+
if [ -n "$zip_file" ]; then
|
|
676
|
+
zip_file=$(basename "$zip_file")
|
|
677
|
+
else
|
|
678
|
+
zip_file=$(find . -maxdepth 1 -name "*.zip" -type f | head -1 | sed 's|^\./||')
|
|
679
|
+
fi
|
|
680
|
+
|
|
681
|
+
local python_version=$(grep "Detected Python: " "build.log" | awk -F': ' '{print $2}' | tail -1)
|
|
682
|
+
if [ -z "$python_version" ]; then python_version="3.12"; fi
|
|
683
|
+
compatible_runtimes="python${python_version}"
|
|
684
|
+
|
|
685
|
+
local detected_arch=$(grep "Detected Architecture: " "build.log" | awk -F': ' '{print $2}' | tail -1)
|
|
686
|
+
if [ -n "$detected_arch" ]; then
|
|
687
|
+
if [ "$detected_arch" = "aarch64" ]; then compatible_architectures="arm64";
|
|
688
|
+
elif [ "$detected_arch" = "x86_64" ]; then compatible_architectures="x86_64";
|
|
689
|
+
fi
|
|
690
|
+
fi
|
|
691
|
+
|
|
692
|
+
if [ -n "${compatible_architectures:-}" ]; then
|
|
693
|
+
printf "${CYAN}Info: Using detected architecture: $compatible_architectures${NC}\n"
|
|
694
|
+
fi
|
|
657
695
|
else
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
696
|
+
if [ ! -f "$PYTHON_SCRIPT" ]; then
|
|
697
|
+
cd "$current_dir"
|
|
698
|
+
printf "${RED}Error: Python script not found${NC}\n"
|
|
699
|
+
exit 1
|
|
700
|
+
fi
|
|
701
|
+
bash "$PYTHON_SCRIPT" "${build_args[@]}" 2>&1 | tee build.log
|
|
702
|
+
|
|
703
|
+
# Check if build script succeeded
|
|
704
|
+
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
|
705
|
+
cd "$current_dir"
|
|
706
|
+
printf "${RED}Error: Python layer build failed${NC}\n"
|
|
707
|
+
printf "Check build log: $output_dir/build.log\n"
|
|
708
|
+
exit 1
|
|
709
|
+
fi
|
|
710
|
+
|
|
711
|
+
# Extract zip file name from build output (just the filename, not full path)
|
|
712
|
+
zip_file=$(grep -o "File: .*\.zip" "build.log" | cut -d' ' -f2 | tail -1)
|
|
713
|
+
if [ -n "$zip_file" ]; then
|
|
714
|
+
zip_file=$(basename "$zip_file")
|
|
715
|
+
else
|
|
716
|
+
# Try to find zip file in current directory
|
|
717
|
+
zip_file=$(find . -maxdepth 1 -name "*.zip" -type f | head -1 | sed 's|^\./||')
|
|
718
|
+
fi
|
|
719
|
+
|
|
720
|
+
# Extract Python version for compatible runtimes
|
|
721
|
+
# Pattern matches "Python version: X.Y" or "Python Version: X.Y"
|
|
722
|
+
local python_version=$(grep -i "Python.*version: [0-9.]*" "build.log" | awk '{print $NF}' | tail -1)
|
|
723
|
+
if [ -z "$python_version" ]; then
|
|
724
|
+
python_version="3.14"
|
|
725
|
+
fi
|
|
726
|
+
local compatible_runtimes="python${python_version}"
|
|
667
727
|
fi
|
|
668
|
-
local compatible_runtimes="python${python_version}"
|
|
669
728
|
fi
|
|
670
729
|
|
|
671
730
|
# Check if zip file was created
|
|
@@ -681,7 +740,11 @@ handle_publish() {
|
|
|
681
740
|
if [ "$runtime" = "nodejs" ]; then
|
|
682
741
|
packages_info=$(grep -o "Installed packages: .*" "build.log" | cut -d' ' -f3- | tail -1)
|
|
683
742
|
elif [ "$runtime" = "python" ]; then
|
|
684
|
-
|
|
743
|
+
if [ "$is_wheel" = true ]; then
|
|
744
|
+
packages_info=$(basename "$packages")
|
|
745
|
+
else
|
|
746
|
+
packages_info=$(grep -o "Installed packages: .*" "build.log" | cut -d' ' -f3- | tail -1)
|
|
747
|
+
fi
|
|
685
748
|
fi
|
|
686
749
|
|
|
687
750
|
# Determine layer name - use just the first package name if not specified
|
|
@@ -694,7 +757,11 @@ handle_publish() {
|
|
|
694
757
|
layer_name=$(echo "$first_package" | sed 's/@[0-9^~<>=].*$//' | sed 's/^@//' | tr '/' '-')
|
|
695
758
|
else
|
|
696
759
|
# For Python: remove ==version, >=version, etc.
|
|
697
|
-
|
|
760
|
+
if [ "$is_wheel" = true ]; then
|
|
761
|
+
layer_name=$(basename "$first_package" | cut -d'-' -f1)
|
|
762
|
+
else
|
|
763
|
+
layer_name=$(echo "$first_package" | sed 's/[=<>~!].*$//')
|
|
764
|
+
fi
|
|
698
765
|
fi
|
|
699
766
|
fi
|
|
700
767
|
|
|
@@ -750,16 +817,32 @@ handle_publish() {
|
|
|
750
817
|
printf " --layer-name \"$layer_name\"\n"
|
|
751
818
|
printf " --description \"$final_description\"\n"
|
|
752
819
|
printf " --zip-file \"fileb://$(convert_path "$current_dir/$output_dir/$zip_file")\"\n"
|
|
753
|
-
printf " --compatible-runtimes \"$compatible_runtimes\"\n
|
|
820
|
+
printf " --compatible-runtimes \"$compatible_runtimes\"\n"
|
|
821
|
+
if [ -n "${compatible_architectures:-}" ]; then
|
|
822
|
+
printf " --compatible-architectures \"$compatible_architectures\"\n"
|
|
823
|
+
fi
|
|
824
|
+
printf "\n"
|
|
754
825
|
|
|
826
|
+
# Build AWS CLI command array
|
|
827
|
+
local publish_cmd=(aws)
|
|
828
|
+
if [ ${#publish_aws_opts[@]} -gt 0 ]; then
|
|
829
|
+
publish_cmd+=("${publish_aws_opts[@]}")
|
|
830
|
+
fi
|
|
831
|
+
publish_cmd+=(lambda publish-layer-version)
|
|
832
|
+
publish_cmd+=(--layer-name "$layer_name")
|
|
833
|
+
publish_cmd+=(--description "$final_description")
|
|
834
|
+
publish_cmd+=(--zip-file "fileb://$(convert_path "$current_dir/$output_dir/$zip_file")")
|
|
835
|
+
publish_cmd+=(--compatible-runtimes "$compatible_runtimes")
|
|
836
|
+
|
|
837
|
+
if [ -n "${compatible_architectures:-}" ]; then
|
|
838
|
+
publish_cmd+=(--compatible-architectures "$compatible_architectures")
|
|
839
|
+
fi
|
|
840
|
+
|
|
841
|
+
publish_cmd+=(--query '[LayerVersionArn, Version, Description]')
|
|
842
|
+
publish_cmd+=(--output table)
|
|
843
|
+
|
|
755
844
|
# Run AWS CLI command and capture output and exit code
|
|
756
|
-
|
|
757
|
-
--layer-name "$layer_name" \
|
|
758
|
-
--description "$final_description" \
|
|
759
|
-
--zip-file "fileb://$(convert_path "$current_dir/$output_dir/$zip_file")" \
|
|
760
|
-
--compatible-runtimes "$compatible_runtimes" \
|
|
761
|
-
--query '[LayerVersionArn, Version, Description]' \
|
|
762
|
-
--output table 2>&1 | tee publish.log
|
|
845
|
+
"${publish_cmd[@]}" 2>&1 | tee publish.log
|
|
763
846
|
|
|
764
847
|
local aws_exit_code=${PIPESTATUS[0]}
|
|
765
848
|
|
package/scripts/build_pypi.sh
CHANGED
|
@@ -25,6 +25,7 @@ cp "$BASE_DIR/VERSION.txt" "$BUILD_DIR/"
|
|
|
25
25
|
cp "$BASE_DIR/scripts/aws-lambda-layer-cli" "$ASSETS_DIR/"
|
|
26
26
|
cp "$BASE_DIR/scripts/create_nodejs_layer.sh" "$ASSETS_DIR/"
|
|
27
27
|
cp "$BASE_DIR/scripts/create_python_layer.sh" "$ASSETS_DIR/"
|
|
28
|
+
cp "$BASE_DIR/scripts/create_wheel_layer.sh" "$ASSETS_DIR/"
|
|
28
29
|
cp "$BASE_DIR/scripts/uninstall.sh" "$ASSETS_DIR/"
|
|
29
30
|
|
|
30
31
|
# Copy completion files
|
|
@@ -40,6 +41,6 @@ cd "$BASE_DIR"
|
|
|
40
41
|
python3 -m build
|
|
41
42
|
|
|
42
43
|
echo "Cleaning up temporary package files..."
|
|
43
|
-
|
|
44
|
+
rm -rf "$BUILD_DIR" "$BASE_DIR/aws_lambda_layer_cli.egg-info" "$BASE_DIR/build"
|
|
44
45
|
|
|
45
46
|
echo "Build complete! Artifacts are in dist/"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
# Python Lambda Layer Creator with
|
|
3
|
+
# Python Lambda Layer Creator with version specification
|
|
4
4
|
# Usage:
|
|
5
5
|
# ./create_python_layer.sh -i numpy==1.26.0,pandas==2.1.3
|
|
6
6
|
# ./create_python_layer.sh -i numpy==1.26.0,pandas,boto3==1.34.0 -n my-layer.zip
|
|
7
|
-
# ./create_python_layer.sh --packages=requests==2.31.0,boto3
|
|
7
|
+
# ./create_python_layer.sh --packages=requests==2.31.0,boto3
|
|
8
8
|
|
|
9
9
|
set -e # Exit on error
|
|
10
10
|
set -u # Treat unset variables as errors
|
|
@@ -140,20 +140,6 @@ while [[ $# -gt 0 ]]; do
|
|
|
140
140
|
validate_python_version "$PYTHON_VERSION"
|
|
141
141
|
shift
|
|
142
142
|
;;
|
|
143
|
-
--platform)
|
|
144
|
-
if [[ -n "${2:-}" && "${2:-}" != -* ]]; then
|
|
145
|
-
PLATFORM="$2"
|
|
146
|
-
shift 2
|
|
147
|
-
else
|
|
148
|
-
printf "${RED}Error: $1 requires an argument${NC}\n"
|
|
149
|
-
printf "Example: $1 manylinux_2_28_x86_64\n"
|
|
150
|
-
exit 1
|
|
151
|
-
fi
|
|
152
|
-
;;
|
|
153
|
-
--platform=*)
|
|
154
|
-
PLATFORM="${1#*=}"
|
|
155
|
-
shift
|
|
156
|
-
;;
|
|
157
143
|
-a|--architecture)
|
|
158
144
|
if [[ -n "${2:-}" && "${2:-}" != -* ]]; then
|
|
159
145
|
ARCHITECTURE="$2"
|
|
@@ -180,18 +166,9 @@ Options:
|
|
|
180
166
|
-i, --packages Comma-separated list of Python packages (with optional versions)
|
|
181
167
|
-n, --name Name of the output zip file
|
|
182
168
|
--python-version Python version (default: 3.14)
|
|
183
|
-
--platform Target platform tag (optional, overrides architecture)
|
|
184
169
|
-a, --architecture Target architecture (x86_64 or arm64, default: x86_64)
|
|
185
170
|
-h, --help Show this help message
|
|
186
171
|
|
|
187
|
-
Supported Platforms (optional):
|
|
188
|
-
manylinux2014_x86_64 # Amazon Linux 2, RHEL 7+ (older)
|
|
189
|
-
manylinux2014_aarch64 # ARM64 architecture
|
|
190
|
-
manylinux_2_28_x86_64 # Amazon Linux 2023, RHEL 8+ (newer)
|
|
191
|
-
manylinux_2_28_aarch64 # ARM64 with newer glibc
|
|
192
|
-
linux_x86_64 # Generic Linux
|
|
193
|
-
linux_aarch64 # Generic ARM64 Linux
|
|
194
|
-
|
|
195
172
|
Version Specification:
|
|
196
173
|
Package versions can be specified using standard Python version specifiers:
|
|
197
174
|
numpy==1.26.0 # Exact version
|
|
@@ -356,12 +333,18 @@ if [ -z "$PLATFORM" ]; then
|
|
|
356
333
|
PY_VER_MAJOR=$(echo "$PYTHON_VERSION" | cut -d. -f1)
|
|
357
334
|
PY_VER_MINOR=$(echo "$PYTHON_VERSION" | cut -d. -f2)
|
|
358
335
|
|
|
359
|
-
#
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
336
|
+
# Platform selection based on AWS Lambda Runtime
|
|
337
|
+
if [ "$PY_VER_MAJOR" -eq 3 ] && [ "$PY_VER_MINOR" -ge 12 ]; then
|
|
338
|
+
# Python 3.12+ runs on Amazon Linux 2023 (GLIBC 2.34)
|
|
339
|
+
# We use manylinux_2_28 (GLIBC 2.28) which is well-supported
|
|
340
|
+
PLATFORM_PREFIX="manylinux_2_28"
|
|
341
|
+
printf " Targeting Amazon Linux 2023 (Python $PYTHON_VERSION)\n"
|
|
342
|
+
else
|
|
343
|
+
# Python 3.11- runs on Amazon Linux 2 (GLIBC 2.26)
|
|
344
|
+
# We use manylinux2014 (GLIBC 2.17) for max compatibility
|
|
345
|
+
PLATFORM_PREFIX="manylinux2014"
|
|
346
|
+
printf " Targeting Amazon Linux 2 (Python $PYTHON_VERSION)\n"
|
|
347
|
+
fi
|
|
365
348
|
|
|
366
349
|
PLATFORM="${PLATFORM_PREFIX}_${ARCHITECTURE}"
|
|
367
350
|
printf "Auto-detected platform: $PLATFORM (Python $PYTHON_VERSION, Arch $ARCHITECTURE)\n"
|
|
@@ -393,9 +376,13 @@ if [ ${#INSTALL_OPTS[@]} -gt 0 ]; then
|
|
|
393
376
|
# We use the site-packages directory of the current venv
|
|
394
377
|
SITE_PACKAGES=$(python -c "import site; print(site.getsitepackages()[0])")
|
|
395
378
|
printf " Targeting site-packages: $SITE_PACKAGES\n"
|
|
396
|
-
pip install "${PKG_ARRAY[@]}" "${INSTALL_OPTS[@]}" --target "$SITE_PACKAGES"
|
|
379
|
+
CMD=(pip install "${PKG_ARRAY[@]}" "${INSTALL_OPTS[@]}" --target "$SITE_PACKAGES")
|
|
380
|
+
echo " Running: ${CMD[*]}"
|
|
381
|
+
"${CMD[@]}"
|
|
397
382
|
else
|
|
398
|
-
pip install "${PKG_ARRAY[@]}"
|
|
383
|
+
CMD=(pip install "${PKG_ARRAY[@]}")
|
|
384
|
+
echo " Running: ${CMD[*]}"
|
|
385
|
+
"${CMD[@]}"
|
|
399
386
|
fi
|
|
400
387
|
|
|
401
388
|
# Count packages from command argument
|
|
@@ -12,10 +12,19 @@ set -u
|
|
|
12
12
|
WHEEL_FILE=""
|
|
13
13
|
PACKAGES=""
|
|
14
14
|
LAYER_NAME=""
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
# We now track user provided values separate from defaults
|
|
16
|
+
USER_PYTHON_VERSION=""
|
|
17
|
+
USER_ARCHITECTURE=""
|
|
18
|
+
USER_PLATFORM=""
|
|
19
|
+
|
|
20
|
+
DEFAULT_PYTHON_VERSION="3.12"
|
|
21
|
+
DEFAULT_ARCHITECTURE="x86_64"
|
|
22
|
+
|
|
23
|
+
PYTHON_VERSION="$DEFAULT_PYTHON_VERSION"
|
|
24
|
+
ARCHITECTURE="$DEFAULT_ARCHITECTURE"
|
|
25
|
+
PLATFORM=""
|
|
17
26
|
IMPLEMENTATION="cp"
|
|
18
|
-
ABI="
|
|
27
|
+
ABI="" # Will be calculated
|
|
19
28
|
|
|
20
29
|
# Colors
|
|
21
30
|
RED='\033[0;31m'
|
|
@@ -67,6 +76,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
67
76
|
;;
|
|
68
77
|
--python-version)
|
|
69
78
|
if [[ -n "${2:-}" && "${2:-}" != -* ]]; then
|
|
79
|
+
USER_PYTHON_VERSION="$2"
|
|
70
80
|
PYTHON_VERSION="$2"
|
|
71
81
|
shift 2
|
|
72
82
|
else
|
|
@@ -75,20 +85,23 @@ while [[ $# -gt 0 ]]; do
|
|
|
75
85
|
fi
|
|
76
86
|
;;
|
|
77
87
|
--python-version=*)
|
|
88
|
+
USER_PYTHON_VERSION="${1#*=}"
|
|
78
89
|
PYTHON_VERSION="${1#*=}"
|
|
79
90
|
shift
|
|
80
91
|
;;
|
|
81
|
-
--
|
|
92
|
+
--architecture|-a)
|
|
82
93
|
if [[ -n "${2:-}" && "${2:-}" != -* ]]; then
|
|
83
|
-
|
|
94
|
+
USER_ARCHITECTURE="$2"
|
|
95
|
+
ARCHITECTURE="$2"
|
|
84
96
|
shift 2
|
|
85
97
|
else
|
|
86
98
|
printf "${RED}Error: $1 requires an argument${NC}\n"
|
|
87
99
|
exit 1
|
|
88
100
|
fi
|
|
89
101
|
;;
|
|
90
|
-
--
|
|
91
|
-
|
|
102
|
+
--architecture=*)
|
|
103
|
+
USER_ARCHITECTURE="${1#*=}"
|
|
104
|
+
ARCHITECTURE="${1#*=}"
|
|
92
105
|
shift
|
|
93
106
|
;;
|
|
94
107
|
-h|--help)
|
|
@@ -100,28 +113,18 @@ Options:
|
|
|
100
113
|
-i, --packages Additional packages (comma or space separated)
|
|
101
114
|
-n, --name Output zip filename
|
|
102
115
|
--python-version Target Python version (default: 3.12)
|
|
103
|
-
--
|
|
104
|
-
|
|
105
|
-
Supported Platforms:
|
|
106
|
-
manylinux2014_x86_64 # Amazon Linux 2, RHEL 7+ (older)
|
|
107
|
-
manylinux2014_aarch64 # ARM64 architecture
|
|
108
|
-
manylinux_2_28_x86_64 # Amazon Linux 2023, RHEL 8+ (newer)
|
|
109
|
-
manylinux_2_28_aarch64 # ARM64 with newer glibc
|
|
110
|
-
linux_x86_64 # Generic Linux
|
|
111
|
-
linux_aarch64 # Generic ARM64 Linux
|
|
116
|
+
-a, --architecture Target architecture (x86_64, arm64)
|
|
112
117
|
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
Supported Architectures:
|
|
119
|
+
x86_64 (amd64) # Standard Intel/AMD 64-bit
|
|
120
|
+
arm64 (aarch64) # AWS Graviton (ARM 64-bit)
|
|
115
121
|
|
|
116
122
|
Examples:
|
|
117
|
-
# Build for Amazon Linux 2 (Python 3.12)
|
|
118
|
-
./create_wheel_layer.sh -w mypackage.whl --python-version=3.12
|
|
123
|
+
# Build for Amazon Linux 2 (Python 3.12, x86_64)
|
|
124
|
+
./create_wheel_layer.sh -w mypackage.whl --python-version=3.12
|
|
119
125
|
|
|
120
|
-
# Build for
|
|
121
|
-
./create_wheel_layer.sh -w mypackage.whl
|
|
122
|
-
|
|
123
|
-
# Build with additional packages for ARM64
|
|
124
|
-
./create_wheel_layer.sh -w mypackage.whl -i "numpy,pandas" --platform=manylinux_2_28_aarch64
|
|
126
|
+
# Build for ARM64
|
|
127
|
+
./create_wheel_layer.sh -w mypackage.whl -a arm64
|
|
125
128
|
EOF
|
|
126
129
|
exit 0
|
|
127
130
|
;;
|
|
@@ -137,15 +140,233 @@ if [ -z "$WHEEL_FILE" ]; then
|
|
|
137
140
|
printf "${RED}Error: Wheel file is required (-w)${NC}\n"
|
|
138
141
|
exit 1
|
|
139
142
|
fi
|
|
140
|
-
|
|
143
|
+
# Detect pip
|
|
144
|
+
PIP_EXE=""
|
|
145
|
+
if command -v pip &> /dev/null; then
|
|
146
|
+
PIP_EXE="pip"
|
|
147
|
+
elif command -v pip3 &> /dev/null; then
|
|
148
|
+
PIP_EXE="pip3"
|
|
149
|
+
else
|
|
150
|
+
printf "${RED}Error: pip or pip3 not found. Please install Python and pip.${NC}\n"
|
|
151
|
+
exit 1
|
|
152
|
+
fi
|
|
141
153
|
if [ ! -f "$WHEEL_FILE" ]; then
|
|
142
154
|
printf "${RED}Error: File $WHEEL_FILE not found${NC}\n"
|
|
143
155
|
exit 1
|
|
144
156
|
fi
|
|
145
157
|
|
|
146
|
-
#
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
# ------------------------------------------------------------------
|
|
159
|
+
# WHEEL METADATA AUTO-DETECTION
|
|
160
|
+
# ------------------------------------------------------------------
|
|
161
|
+
# Extract metadata directly from filename/wheel to enforce strictness
|
|
162
|
+
# Format: Name-Ver-PyTag-AbiTag-PlatTag.whl
|
|
163
|
+
|
|
164
|
+
DETECTED_PY="any"
|
|
165
|
+
DETECTED_ABI="none"
|
|
166
|
+
DETECTED_PLAT="any"
|
|
167
|
+
DETECTED_ARCH="any"
|
|
168
|
+
|
|
169
|
+
# Helper python script to parse filename strictly
|
|
170
|
+
META_OUT=$(python3 -c "
|
|
171
|
+
import sys
|
|
172
|
+
import os
|
|
173
|
+
|
|
174
|
+
filename = os.path.basename(sys.argv[1])
|
|
175
|
+
if filename.endswith('.whl'):
|
|
176
|
+
filename = filename[:-4]
|
|
177
|
+
|
|
178
|
+
parts = filename.split('-')
|
|
179
|
+
# Minimal check: Name-Ver-Py-Abi-Plat
|
|
180
|
+
if len(parts) >= 5:
|
|
181
|
+
plat = parts[-1]
|
|
182
|
+
abi = parts[-2]
|
|
183
|
+
py = parts[-3]
|
|
184
|
+
|
|
185
|
+
# Arch mapping
|
|
186
|
+
arch = 'any'
|
|
187
|
+
if 'x86_64' in plat or 'amd64' in plat:
|
|
188
|
+
arch = 'x86_64'
|
|
189
|
+
elif 'aarch64' in plat or 'arm64' in plat:
|
|
190
|
+
arch = 'aarch64'
|
|
191
|
+
|
|
192
|
+
# Py Ver extraction (cp312->3.12)
|
|
193
|
+
py_ver = 'any'
|
|
194
|
+
if py.startswith('cp') and len(py) > 2 and py[2:].isdigit():
|
|
195
|
+
pv_raw = py[2:]
|
|
196
|
+
if len(pv_raw) == 2: # 39
|
|
197
|
+
py_ver = f'{pv_raw[0]}.{pv_raw[1]}'
|
|
198
|
+
elif len(pv_raw) >= 3: # 312
|
|
199
|
+
major = pv_raw[0]
|
|
200
|
+
minor = pv_raw[1:]
|
|
201
|
+
py_ver = f'{major}.{minor}'
|
|
202
|
+
|
|
203
|
+
print(f'{py_ver}|{arch}|{plat}|{abi}')
|
|
204
|
+
else:
|
|
205
|
+
print('any|any|any|none')
|
|
206
|
+
" "$WHEEL_FILE")
|
|
207
|
+
|
|
208
|
+
IFS='|' read -r DET_PY DET_ARCH DET_PLAT DET_ABI <<< "$META_OUT"
|
|
209
|
+
|
|
210
|
+
# STRICT MODE ENFORCEMENT
|
|
211
|
+
# If the wheel is binary (specific architecture or python), we ENFORCE it.
|
|
212
|
+
# If the wheel is 'any', we allow defaults or user args.
|
|
213
|
+
|
|
214
|
+
if [ "$DET_PY" != "any" ]; then
|
|
215
|
+
if [ -n "$USER_PYTHON_VERSION" ] && [ "$USER_PYTHON_VERSION" != "$DET_PY" ]; then
|
|
216
|
+
printf "${RED}Error: Wheel is for Python $DET_PY, but you requested $USER_PYTHON_VERSION.${NC}\n"
|
|
217
|
+
printf "Please remove the argument or use matching version.\n"
|
|
218
|
+
exit 1
|
|
219
|
+
fi
|
|
220
|
+
# Auto-set
|
|
221
|
+
PYTHON_VERSION="$DET_PY"
|
|
222
|
+
printf "Detected Python: $PYTHON_VERSION\n"
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
if [ "$DET_ARCH" != "any" ]; then
|
|
226
|
+
if [ -n "$USER_ARCHITECTURE" ]; then
|
|
227
|
+
# Normalize user input for comparison
|
|
228
|
+
NORM_USER_ARCH="$USER_ARCHITECTURE"
|
|
229
|
+
if [ "$USER_ARCHITECTURE" == "arm64" ]; then NORM_USER_ARCH="aarch64"; fi
|
|
230
|
+
if [ "$USER_ARCHITECTURE" == "amd64" ]; then NORM_USER_ARCH="x86_64"; fi
|
|
231
|
+
|
|
232
|
+
if [ "$NORM_USER_ARCH" != "$DET_ARCH" ]; then
|
|
233
|
+
printf "${RED}Error: Wheel is for $DET_ARCH, but you requested $USER_ARCHITECTURE.${NC}\n"
|
|
234
|
+
exit 1
|
|
235
|
+
fi
|
|
236
|
+
fi
|
|
237
|
+
# Auto-set
|
|
238
|
+
ARCHITECTURE="$DET_ARCH"
|
|
239
|
+
printf "Detected Architecture: $ARCHITECTURE\n"
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
if [ "$DET_PLAT" != "any" ] && [ -z "$USER_PLATFORM" ]; then
|
|
243
|
+
# Only override platform if user didn't specify one (pip might need specific one)
|
|
244
|
+
# But usually filename platform is correct for install
|
|
245
|
+
PLATFORM="$DET_PLAT"
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
if [ "$DET_ABI" != "none" ]; then
|
|
249
|
+
ABI="$DET_ABI"
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
# Normalize Architecture
|
|
253
|
+
AWS_ARCH="$ARCHITECTURE"
|
|
254
|
+
if [ "$ARCHITECTURE" = "arm64" ] || [ "$ARCHITECTURE" = "aarch64" ]; then
|
|
255
|
+
ARCHITECTURE="aarch64"
|
|
256
|
+
AWS_ARCH="arm64"
|
|
257
|
+
elif [ "$ARCHITECTURE" = "amd64" ] || [ "$ARCHITECTURE" = "x86_64" ]; then
|
|
258
|
+
ARCHITECTURE="x86_64"
|
|
259
|
+
AWS_ARCH="x86_64"
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
# Determine Platform if still empty
|
|
263
|
+
if [ -z "$PLATFORM" ]; then
|
|
264
|
+
# Default to manylinux2014 as it is safe for both AL2 and AL2023
|
|
265
|
+
PLATFORM="manylinux2014_${ARCHITECTURE}"
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# Validate Wheel Suitability for Lambda (Linux) using metadata
|
|
270
|
+
printf "Validating wheel compatibility...\n"
|
|
271
|
+
|
|
272
|
+
# Only run python validation if python is available (it should be, given pip is used)
|
|
273
|
+
PYTHON_EXE=""
|
|
274
|
+
if command -v python3 &> /dev/null; then
|
|
275
|
+
PYTHON_EXE="python3"
|
|
276
|
+
elif command -v python &> /dev/null; then
|
|
277
|
+
PYTHON_EXE="python"
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
if [ -n "$PYTHON_EXE" ]; then
|
|
281
|
+
# Use Python to inspect the WHEEL metadata for accurate tags
|
|
282
|
+
$PYTHON_EXE -c "
|
|
283
|
+
import sys, zipfile, os
|
|
284
|
+
|
|
285
|
+
try:
|
|
286
|
+
wheel_path = sys.argv[1]
|
|
287
|
+
target_arch = sys.argv[2]
|
|
288
|
+
|
|
289
|
+
# Define compatibility
|
|
290
|
+
compatible_os = ['manylinux', 'linux', 'any']
|
|
291
|
+
|
|
292
|
+
arch_map = {
|
|
293
|
+
'x86_64': ['x86_64', 'amd64', 'any'],
|
|
294
|
+
'arm64': ['aarch64', 'arm64', 'any'],
|
|
295
|
+
'aarch64': ['aarch64', 'arm64', 'any']
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
with zipfile.ZipFile(wheel_path, 'r') as z:
|
|
299
|
+
# Find .dist-info/WHEEL
|
|
300
|
+
wheel_files = [f for f in z.namelist() if f.endswith('.dist-info/WHEEL')]
|
|
301
|
+
if not wheel_files:
|
|
302
|
+
# Fallback for old wheels without WHEEL metadata (rare)
|
|
303
|
+
print('Warning: No .dist-info/WHEEL found, skipping strict validation.')
|
|
304
|
+
sys.exit(0)
|
|
305
|
+
|
|
306
|
+
content = z.read(wheel_files[0]).decode('utf-8')
|
|
307
|
+
tags = []
|
|
308
|
+
for line in content.splitlines():
|
|
309
|
+
if line.startswith('Tag:'):
|
|
310
|
+
tags.append(line.split(':', 1)[1].strip())
|
|
311
|
+
|
|
312
|
+
has_linux = False
|
|
313
|
+
has_arch = False
|
|
314
|
+
detected_plats = set()
|
|
315
|
+
|
|
316
|
+
for tag in tags:
|
|
317
|
+
parts = tag.split('-')
|
|
318
|
+
if len(parts) >= 3:
|
|
319
|
+
plat = parts[2]
|
|
320
|
+
detected_plats.add(plat)
|
|
321
|
+
|
|
322
|
+
# Check OS
|
|
323
|
+
if any(x in plat for x in compatible_os):
|
|
324
|
+
has_linux = True
|
|
325
|
+
|
|
326
|
+
# Check Arch
|
|
327
|
+
target_valid_archs = arch_map.get(target_arch, [])
|
|
328
|
+
|
|
329
|
+
# Special handling for 'any' to avoid matching 'manylinux'
|
|
330
|
+
if plat == 'any' and 'any' in target_valid_archs:
|
|
331
|
+
has_arch = True
|
|
332
|
+
else:
|
|
333
|
+
# Filter out 'any' from search strings for substring check
|
|
334
|
+
search_archs = [x for x in target_valid_archs if x != 'any']
|
|
335
|
+
if any(x in plat for x in search_archs):
|
|
336
|
+
has_arch = True
|
|
337
|
+
|
|
338
|
+
if not has_linux:
|
|
339
|
+
print(f'Error: Wheel is not compatible with Linux.\nDetected platforms: {', '.join(sorted(detected_plats))}')
|
|
340
|
+
sys.exit(1)
|
|
341
|
+
|
|
342
|
+
if not has_arch:
|
|
343
|
+
print(f'Error: Wheel architecture mismatch.\nTarget: {target_arch}\nDetected platforms: {', '.join(sorted(detected_plats))}')
|
|
344
|
+
sys.exit(1)
|
|
345
|
+
|
|
346
|
+
except Exception as e:
|
|
347
|
+
print(f'Warning: Could not validate wheel metadata: {e}')
|
|
348
|
+
sys.exit(0) # Don't block build if validation script itself crashes
|
|
349
|
+
" "$WHEEL_FILE" "$ARCHITECTURE"
|
|
350
|
+
|
|
351
|
+
# Check exit code of python script
|
|
352
|
+
if [ $? -ne 0 ]; then
|
|
353
|
+
exit 1
|
|
354
|
+
fi
|
|
355
|
+
else
|
|
356
|
+
# Fallback to simple filename check if python not found (unlikely)
|
|
357
|
+
WHEEL_BASENAME=$(basename "$WHEEL_FILE")
|
|
358
|
+
if [[ "$WHEEL_BASENAME" == *"macosx"* ]] || [[ "$WHEEL_BASENAME" == *"win32"* ]]; then
|
|
359
|
+
printf "${YELLOW}Warning: Filename suggests non-Linux wheel ($WHEEL_BASENAME)${NC}\n"
|
|
360
|
+
fi
|
|
361
|
+
fi
|
|
362
|
+
|
|
363
|
+
# Determine ABI tag (If not auto-detected)
|
|
364
|
+
# e.g., 3.12 -> cp312, 3.10 -> cp310
|
|
365
|
+
if [ -z "$ABI" ] || [ "$ABI" == "none" ]; then
|
|
366
|
+
PY_MAJOR=$(echo "$PYTHON_VERSION" | cut -d. -f1)
|
|
367
|
+
PY_MINOR=$(echo "$PYTHON_VERSION" | cut -d. -f2)
|
|
368
|
+
ABI="cp${PY_MAJOR}${PY_MINOR}"
|
|
369
|
+
fi
|
|
149
370
|
|
|
150
371
|
if [ -z "$LAYER_NAME" ]; then
|
|
151
372
|
BASENAME=$(basename "$WHEEL_FILE" .whl)
|
|
@@ -166,14 +387,15 @@ printf "Wheel: $WHEEL_FILE\n"
|
|
|
166
387
|
if [ -n "$PACKAGES" ]; then
|
|
167
388
|
printf "Extra Packages: $PACKAGES\n"
|
|
168
389
|
fi
|
|
169
|
-
printf "
|
|
170
|
-
printf "
|
|
390
|
+
printf "Target Architecture: $AWS_ARCH\n"
|
|
391
|
+
printf "Platform Tag: $PLATFORM\n"
|
|
392
|
+
printf "Python: $PYTHON_VERSION (ABI: $ABI)\n"
|
|
171
393
|
|
|
172
394
|
mkdir -p "$LAYER_DIR/python"
|
|
173
395
|
|
|
174
396
|
# Install
|
|
175
397
|
printf "${GREEN}Installing packages...${NC}\n"
|
|
176
|
-
CMD=("
|
|
398
|
+
CMD=("$PIP_EXE" "install" "$WHEEL_FILE")
|
|
177
399
|
|
|
178
400
|
if [ -n "$PACKAGES" ]; then
|
|
179
401
|
# Replace commas with spaces
|
|
@@ -184,7 +406,14 @@ if [ -n "$PACKAGES" ]; then
|
|
|
184
406
|
fi
|
|
185
407
|
|
|
186
408
|
CMD+=("--target" "$LAYER_DIR/python")
|
|
187
|
-
|
|
409
|
+
|
|
410
|
+
# Handle multiple platform tags (e.g. manylinux1_x86_64.linux_x86_64)
|
|
411
|
+
# Split by dot and add each as separate --platform argument
|
|
412
|
+
IFS='.' read -ra PLAT_TAGS <<< "$PLATFORM"
|
|
413
|
+
for tag in "${PLAT_TAGS[@]}"; do
|
|
414
|
+
CMD+=("--platform" "$tag")
|
|
415
|
+
done
|
|
416
|
+
|
|
188
417
|
CMD+=("--implementation" "$IMPLEMENTATION")
|
|
189
418
|
CMD+=("--python-version" "$PYTHON_VERSION")
|
|
190
419
|
CMD+=("--abi" "$ABI")
|
|
@@ -208,10 +437,18 @@ find "$LAYER_DIR" -type d -name "*.dist-info" -exec rm -rf {} +
|
|
|
208
437
|
# Zip
|
|
209
438
|
printf "${GREEN}Zipping to $LAYER_NAME...${NC}\n"
|
|
210
439
|
cd "$LAYER_DIR"
|
|
211
|
-
|
|
440
|
+
|
|
441
|
+
# Handle absolute vs relative path for LAYER_NAME
|
|
442
|
+
ZIP_DEST="$LAYER_NAME"
|
|
443
|
+
if [[ "$LAYER_NAME" != /* ]]; then
|
|
444
|
+
ZIP_DEST="$ORIGINAL_DIR/$LAYER_NAME"
|
|
445
|
+
fi
|
|
446
|
+
|
|
447
|
+
if zip -r "$ZIP_DEST" python > /dev/null; then
|
|
212
448
|
printf "${GREEN}✅ Done! Created $LAYER_NAME${NC}\n"
|
|
449
|
+
printf "File: $(basename "$LAYER_NAME")\n"
|
|
213
450
|
else
|
|
214
|
-
printf "${RED}Error creating zip file${NC}\n"
|
|
451
|
+
printf "${RED}Error creating zip file at $ZIP_DEST${NC}\n"
|
|
215
452
|
cd "$ORIGINAL_DIR"
|
|
216
453
|
rm -rf "$LAYER_DIR"
|
|
217
454
|
exit 1
|
package/scripts/install.ps1
CHANGED
|
@@ -44,7 +44,7 @@ if ([string]::IsNullOrEmpty($InstallDir)) {
|
|
|
44
44
|
# Configuration
|
|
45
45
|
$RepoUrl = "https://github.com/yukcw/aws-lambda-layer-cli"
|
|
46
46
|
$ToolName = "aws-lambda-layer-cli"
|
|
47
|
-
$Version = "2.
|
|
47
|
+
$Version = "2.2.0" # Fallback version
|
|
48
48
|
|
|
49
49
|
# Colors for output
|
|
50
50
|
$Green = "Green"
|
package/scripts/uninstall.ps1
CHANGED
|
File without changes
|