nate2-scripts 1.0.1__tar.gz → 2.0.0__tar.gz
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.
- {nate2_scripts-1.0.1 → nate2_scripts-2.0.0}/.gitignore +2 -1
- {nate2_scripts-1.0.1 → nate2_scripts-2.0.0}/PKG-INFO +6 -2
- nate2_scripts-2.0.0/README.md +204 -0
- nate2_scripts-2.0.0/i-want-to-refactor-fizzy-feather.md +173 -0
- nate2_scripts-2.0.0/plan.md +16 -0
- nate2_scripts-2.0.0/pyproject.toml +39 -0
- nate2_scripts-1.0.1/src/nate2_scripts/bing_wrapper.py → nate2_scripts-2.0.0/src/nate2_scripts/bing_wrapper/__init__.py +44 -1
- nate2_scripts-2.0.0/src/nate2_scripts/bing_wrapper/autocomplete.sh +30 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/__init__.py +207 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/boto3_session.py +51 -0
- nate2_scripts-1.0.1/src/nate2_scripts/console/hashGrabber.py → nate2_scripts-2.0.0/src/nate2_scripts/console/hash_grabber.py +27 -12
- nate2_scripts-2.0.0/src/nate2_scripts/console/local_refresh.py +0 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/__init__.py +44 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/apigateway.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/artifact.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/athena.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/batch.py +52 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/bedrock.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/cloudformation.py +28 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/cloudfront.py +12 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/cloudhsm.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/cloudtrail.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/cognito.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/config.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/controltower.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/cost.py +13 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/dynamo.py +27 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/ec2.py +118 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/ecr.py +16 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/ecs.py +61 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/efs.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/eks.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/elasticbeanstalk.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/elb.py +35 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/eventbridge.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/fsx.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/glue.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/guardduty.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/iam.py +43 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/inspector.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/kms.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/lambda_.py +45 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/lex.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/lightsail.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/logs.py +81 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/marketplace.py +24 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/polly.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/quick.py +15 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/rds.py +34 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/route53.py +16 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/s3.py +66 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/sagemaker.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/secretsmanager.py +31 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/sns.py +29 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/sqs.py +28 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/ssm.py +18 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/stepfunctions.py +37 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/storagegateway.py +11 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/support.py +14 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/resolvers/vpc.py +93 -0
- nate2_scripts-1.0.1/src/nate2_scripts/console/sessionCache.py → nate2_scripts-2.0.0/src/nate2_scripts/console/session_cache.py +8 -22
- nate2_scripts-2.0.0/src/nate2_scripts/console/text_resolver.py +193 -0
- nate2_scripts-2.0.0/src/nate2_scripts/console/types.py +59 -0
- {nate2_scripts-1.0.1/src/nate2_scripts → nate2_scripts-2.0.0}/test.py +4 -2
- nate2_scripts-2.0.0/tests/__init__.py +0 -0
- nate2_scripts-2.0.0/tests/console/__init__.py +0 -0
- nate2_scripts-2.0.0/tests/console/resolvers/__init__.py +29 -0
- nate2_scripts-2.0.0/tests/console/resolvers/apigateway.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/artifact.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/athena.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/batch.py +50 -0
- nate2_scripts-2.0.0/tests/console/resolvers/bedrock.py +36 -0
- nate2_scripts-2.0.0/tests/console/resolvers/cloudformation.py +132 -0
- nate2_scripts-2.0.0/tests/console/resolvers/cloudfront.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/cloudhsm.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/cloudtrail.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/cognito.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/config.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/conftest.py +40 -0
- nate2_scripts-2.0.0/tests/console/resolvers/controltower.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/cost.py +22 -0
- nate2_scripts-2.0.0/tests/console/resolvers/dynamo.py +47 -0
- nate2_scripts-2.0.0/tests/console/resolvers/ec2.py +269 -0
- nate2_scripts-2.0.0/tests/console/resolvers/ecr.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/ecs.py +60 -0
- nate2_scripts-2.0.0/tests/console/resolvers/efs.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/eks.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/elasticbeanstalk.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/elb.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/eventbridge.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/fsx.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/glue.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/guardduty.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/iam.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/inspector.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/kms.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/lambda_.py +202 -0
- nate2_scripts-2.0.0/tests/console/resolvers/lex.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/lightsail.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/logs.py +258 -0
- nate2_scripts-2.0.0/tests/console/resolvers/marketplace.py +60 -0
- nate2_scripts-2.0.0/tests/console/resolvers/polly.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/rds.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/route53.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/s3.py +305 -0
- nate2_scripts-2.0.0/tests/console/resolvers/sagemaker.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/secretsmanager.py +47 -0
- nate2_scripts-2.0.0/tests/console/resolvers/sns.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/sqs.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/ssm.py +89 -0
- nate2_scripts-2.0.0/tests/console/resolvers/stepfunctions.py +21 -0
- nate2_scripts-2.0.0/tests/console/resolvers/storagegateway.py +34 -0
- nate2_scripts-2.0.0/tests/console/resolvers/support.py +36 -0
- nate2_scripts-2.0.0/tests/console/resolvers/vpc.py +58 -0
- nate2_scripts-2.0.0/tests/console/test_aws/config +35 -0
- nate2_scripts-2.0.0/tests/console/test_aws/credentials +11 -0
- nate2_scripts-1.0.1/README.md +0 -90
- nate2_scripts-1.0.1/pyproject.toml +0 -23
- nate2_scripts-1.0.1/src/nate2_scripts/console/__init__.py +0 -174
- nate2_scripts-1.0.1/src/nate2_scripts/console/arnResolver.py +0 -514
- nate2_scripts-1.0.1/src/nate2_scripts/console/destitationResolver.py +0 -113
- nate2_scripts-1.0.1/src/nate2_scripts/console/nameResolver.py +0 -99
- nate2_scripts-1.0.1/src/nate2_scripts/console/regionCache.py +0 -50
- nate2_scripts-1.0.1/src/nate2_scripts/console/resourceIdResolver.py +0 -174
- {nate2_scripts-1.0.1 → nate2_scripts-2.0.0}/.github/workflows/publish.yml +0 -0
- {nate2_scripts-1.0.1 → nate2_scripts-2.0.0}/CONSOLE-NOTES.md +0 -0
- {nate2_scripts-1.0.1 → nate2_scripts-2.0.0}/LICENSE +0 -0
- {nate2_scripts-1.0.1 → nate2_scripts-2.0.0}/src/nate2_scripts/__init__.py +0 -0
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: nate2-scripts
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Utility scripts for AWS development on macOS
|
|
5
5
|
Project-URL: Homepage, https://github.com/tennex-nwestfall/nate2-scripts
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Requires-Python: >=3.11
|
|
8
8
|
Requires-Dist: argcomplete
|
|
9
9
|
Requires-Dist: boto3
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: boto3-stubs[essential]; extra == 'dev'
|
|
12
|
+
Requires-Dist: moto[all]; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
[](https://pypi.org/project/nate2-scripts/)
|
|
2
|
+
[](https://github.com/tennex-nwestfall/nate2-scripts/blob/main/LICENSE)
|
|
3
|
+
|
|
4
|
+
# Nate2's Scripts
|
|
5
|
+
|
|
6
|
+
Utility scripts for AWS development on macOS.
|
|
7
|
+
|
|
8
|
+
To install run:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install nate2-scripts
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
To update run:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install --upgrade nate2-scripts
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Scripts
|
|
21
|
+
|
|
22
|
+
### `console`
|
|
23
|
+
|
|
24
|
+
Opens the AWS Management Console in your browser for a specific service, ARN, resource id, or resource name, using federated sign-in from your current AWS credentials. Supports multi-session via searching browser history sqlite databases.
|
|
25
|
+
|
|
26
|
+
**Requirements:**
|
|
27
|
+
|
|
28
|
+
- `AWS_PROFILE` set (e.g. via `asp`)
|
|
29
|
+
- `git config user.email` set — it is used as the `role_session_name` for every assume-role profile
|
|
30
|
+
|
|
31
|
+
**Example Alias:**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# noglob is so that one can paste in logs arns that end with :*
|
|
35
|
+
# otherwise zsh tries to see the log arn as a glob and fails
|
|
36
|
+
alias co="noglob console"
|
|
37
|
+
# needed for autocomplete
|
|
38
|
+
eval "$(register-python-argcomplete console)"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Usage:**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
console [service | arn | id | name] [search ...] [-r REGION] [-f]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# change to an account/role
|
|
49
|
+
asp my-aws-role
|
|
50
|
+
|
|
51
|
+
# open main page of the role/account
|
|
52
|
+
console
|
|
53
|
+
# open ec2 page of the role/account
|
|
54
|
+
console ec2
|
|
55
|
+
# open lambda page of the role/account in us-west-2
|
|
56
|
+
console lambda -r us-west-2
|
|
57
|
+
# open a service page filtered by search terms (multiple words are joined)
|
|
58
|
+
console lambda my-function
|
|
59
|
+
console logs /aws/lambda/my
|
|
60
|
+
console cf my stack
|
|
61
|
+
# open instance id page of the role/account in
|
|
62
|
+
# whatever region that instance is in
|
|
63
|
+
console i-0123456789abcdef
|
|
64
|
+
# open an S3 bucket, prefix, or object
|
|
65
|
+
console s3://my-bucket/some/prefix/
|
|
66
|
+
# open a resource by name (log group, S3 bucket, EC2 Name tag,
|
|
67
|
+
# CloudFormation stack, or Lambda function)
|
|
68
|
+
console my-function
|
|
69
|
+
# open the console page for the resource the ARN points to
|
|
70
|
+
console arn:aws:ec2:us-east-1:012345678967:instance/i-0123456789abcdef
|
|
71
|
+
# force a new federated sign-in (resets the session cache)
|
|
72
|
+
console -f
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Options:**
|
|
76
|
+
|
|
77
|
+
| Flag | Description |
|
|
78
|
+
| ----------------------- | ----------------------------------------------------------------- |
|
|
79
|
+
| `-r`, `--region <name>` | Region to open the console in; also added to the searched regions |
|
|
80
|
+
| `-f`, `--force` | Reset the session cache and force a new federated sign-in |
|
|
81
|
+
|
|
82
|
+
**Resolution order:**
|
|
83
|
+
|
|
84
|
+
1. **ARN** — if the first argument parses as an ARN
|
|
85
|
+
2. **Service** — if the first argument is a known service name or alias
|
|
86
|
+
3. **Resource id** — if the first argument matches a known id pattern
|
|
87
|
+
4. **Resource name** — looked up in parallel across all name-resolving services; the first match in priority order wins: CloudWatch log group → S3 bucket → EC2 `Name` tag → CloudFormation stack → Lambda function
|
|
88
|
+
|
|
89
|
+
**Supported services** (aliases after the `/`):
|
|
90
|
+
|
|
91
|
+
| Service | Names | Search terms |
|
|
92
|
+
| ---------------------- | ----------------------------------------- | ------------ |
|
|
93
|
+
| API Gateway | `apigateway` / `api` | |
|
|
94
|
+
| Artifact | `artifact` | |
|
|
95
|
+
| Athena | `athena` | |
|
|
96
|
+
| Batch | `batch` / `job` / `jobs` | |
|
|
97
|
+
| Bedrock | `bedrock` / `br` | |
|
|
98
|
+
| CloudFormation | `cloudformation` / `cf` | ✓ |
|
|
99
|
+
| CloudFront | `cloudfront` / `front` | |
|
|
100
|
+
| CloudHSM | `cloudhsm` / `hsm` | |
|
|
101
|
+
| CloudTrail | `cloudtrail` / `ct` | |
|
|
102
|
+
| CloudWatch | `cloudwatch` / `cw` | |
|
|
103
|
+
| CloudWatch Logs | `logs` | ✓ |
|
|
104
|
+
| Cognito | `cognito` / `cn` | |
|
|
105
|
+
| Config | `config` / `cfg` | |
|
|
106
|
+
| Control Tower | `controltower` | |
|
|
107
|
+
| Cost Management | `cost` | |
|
|
108
|
+
| DynamoDB | `dynamodb` / `dynamo` / `dyn` | |
|
|
109
|
+
| EC2 instances | `ec2` | ✓ |
|
|
110
|
+
| EC2 AMIs (owned by me) | `ami` | ✓ |
|
|
111
|
+
| ECR | `ecr` / `docker` | |
|
|
112
|
+
| ECS | `ecs` / `cluster` / `task` / `tasks` | |
|
|
113
|
+
| EFS | `efs` | |
|
|
114
|
+
| EKS | `eks` | |
|
|
115
|
+
| Elastic Beanstalk | `elasticbeanstalk` / `bean` | |
|
|
116
|
+
| Elastic Load Balancing | `elb` / `alb` | |
|
|
117
|
+
| EventBridge | `eventbridge` / `eb` | |
|
|
118
|
+
| FSx | `fsx` | |
|
|
119
|
+
| Glue | `glue` | |
|
|
120
|
+
| GuardDuty | `guardduty` / `guard` | |
|
|
121
|
+
| IAM | `iam` | |
|
|
122
|
+
| Inspector | `inspector` | |
|
|
123
|
+
| KMS | `kms` | |
|
|
124
|
+
| Lambda | `lambda` | ✓ |
|
|
125
|
+
| Lex | `lex` | |
|
|
126
|
+
| Lightsail | `lightsail` / `ls` | |
|
|
127
|
+
| Marketplace | `marketplace` / `market` / `tennex` | ✓ |
|
|
128
|
+
| Polly | `polly` | |
|
|
129
|
+
| RDS | `rds` | |
|
|
130
|
+
| Route 53 | `route53` / `53` | |
|
|
131
|
+
| S3 | `s3` | |
|
|
132
|
+
| SageMaker | `sagemaker` / `sage` | |
|
|
133
|
+
| Secrets Manager | `secretsmanager` / `secretmanager` / `sm` | |
|
|
134
|
+
| Security groups | `sg` | ✓ |
|
|
135
|
+
| SNS | `sns` | |
|
|
136
|
+
| SQS | `sqs` | |
|
|
137
|
+
| Step Functions | `step` | |
|
|
138
|
+
| Storage Gateway | `storagegateway` / `sgw` | |
|
|
139
|
+
| Support | `support` | |
|
|
140
|
+
| Systems Manager | `ssm` | |
|
|
141
|
+
| SSM Parameter Store | `parameter` / `ps` | ✓ |
|
|
142
|
+
| VPC | `vpc` | |
|
|
143
|
+
| VPC subnets | `subnet` | |
|
|
144
|
+
| VPC network ACLs | `acl` / `nacl` | |
|
|
145
|
+
| VPC Elastic IPs | `elasticip` / `eni` | |
|
|
146
|
+
| VPC route tables | `routetable` / `routes` | |
|
|
147
|
+
| VPC internet gateways | `internetgateway` / `internet` / `igw` | |
|
|
148
|
+
| VPC NAT gateways | `nat` | |
|
|
149
|
+
| VPC endpoints | `endpoints` / `end` | |
|
|
150
|
+
| VPC transit gateways | `transitgateway` / `transit` / `tgw` | |
|
|
151
|
+
|
|
152
|
+
`tennex` opens a Marketplace search for "tennex". Cost Management and Marketplace always open in `us-east-1`.
|
|
153
|
+
|
|
154
|
+
**Supported resource ids:** `i-*` (EC2 instance), `sg-*` (security group), `ami-*`, `lt-*` (launch template), `vol-*` (EBS volume), `snap-*` (EBS snapshot), `eni-*` (network interface), `s3://bucket[/key]`
|
|
155
|
+
|
|
156
|
+
**Supported ARNs:** `ec2` (instance), `lambda` (function), `logs` (log group, log stream), `s3` (bucket, prefix, object)
|
|
157
|
+
|
|
158
|
+
**Cross-account ARNs:** if an ARN's account differs from the current one, `console` switches to the first profile in your AWS config whose `role_arn` is in that account.
|
|
159
|
+
|
|
160
|
+
**Regions:** ids and names are searched across `us-east-1`, `us-east-2`, `us-west-1`, and `us-west-2`, plus the region passed with `-r`.
|
|
161
|
+
|
|
162
|
+
**Session cache:** federated session hashes are cached per profile for 59 minutes in `$XDG_STATE_HOME/console-sessions.json` (default `~/.local/state/console-sessions.json`). Use `-f` to reset it.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### `bing-wrapper`
|
|
167
|
+
|
|
168
|
+
A PTY wrapper that runs any command in a pseudo-terminal and plays a macOS system sound when a configurable output pattern is detected, and/or when the command finishes. Useful for long-running CLI tools (e.g. CDK deployments) that prompt for confirmation or take a while to complete.
|
|
169
|
+
|
|
170
|
+
**Requirements:** None
|
|
171
|
+
|
|
172
|
+
**Example Alias Usage:**
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# wraps cdk to play Frog.aiff when done and play Glass.aiff (default) on "(y/n)" prompts
|
|
176
|
+
alias cdk="bing-wrapper --end --pattern \"(y/n)\" --sound-end /System/Library/Sounds/Frog.aiff -- cdk"
|
|
177
|
+
|
|
178
|
+
# wraps nextflow to play a sound when done
|
|
179
|
+
alias nextflow="bing-wrapper --end -- nextflow"
|
|
180
|
+
|
|
181
|
+
# needed for autocomplete of bing-wrapper and the inner command
|
|
182
|
+
eval "$(bing-wrapper --autocomplete)"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Options:**
|
|
186
|
+
|
|
187
|
+
| Flag | Description | Default |
|
|
188
|
+
| ------------------------ | --------------------------------------------------------- | ------------ |
|
|
189
|
+
| `--end` | Play a sound when the command exits | off |
|
|
190
|
+
| `--pattern <text>` | Play a sound when `<text>` appears in output (repeatable) | none |
|
|
191
|
+
| `--sound-end <path>` | Sound file to play on exit | `Glass.aiff` |
|
|
192
|
+
| `--sound-pattern <path>` | Sound file to play on pattern match | `Glass.aiff` |
|
|
193
|
+
| `--sound-while <path>` | Sound file to play on loop while the command runs | none |
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Development
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
pip install -e ".[dev]"
|
|
201
|
+
pytest
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Resolver tests live in `tests/console/resolvers/`, one module per service, and use `moto` to mock AWS.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Refactor console resolvers into per-service `Resolver` classes
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
The `console` command (entry point `nate2_scripts.console:main`) turns a user argument
|
|
6
|
+
into an AWS console URL and opens it. Today the resolution logic lives in **three
|
|
7
|
+
monolithic resolvers** plus a static service map, each of which handles *every* AWS
|
|
8
|
+
service internally via large `match`/`if` ladders:
|
|
9
|
+
|
|
10
|
+
- [destitationResolver.py](src/nate2_scripts/console/destitationResolver.py) — orchestrator + `_get_services()` static keyword→URL map
|
|
11
|
+
- [arnResolver.py](src/nate2_scripts/console/arnResolver.py) — one class, ~15 `parse_*` methods for every ARN service
|
|
12
|
+
- [nameResolver.py](src/nate2_scripts/console/nameResolver.py) — one class, parallel cross-service/region name lookup
|
|
13
|
+
- [resourceIdResolver.py](src/nate2_scripts/console/resourceIdResolver.py) — one class, `match` over every id prefix
|
|
14
|
+
|
|
15
|
+
The new target shape already exists in [resolvers/s3.py](src/nate2_scripts/console/resolvers/s3.py):
|
|
16
|
+
a `Resolver` ABC ([resolvers/__init__.py](src/nate2_scripts/console/resolvers/__init__.py))
|
|
17
|
+
with four dimensions — `try_resolve_name`, `try_resolve_arn`, `try_resolve_service`,
|
|
18
|
+
`try_resolve_id` — and **one class per service** (`S3Resolver`) implementing all four,
|
|
19
|
+
each returning `None` when the input isn't theirs.
|
|
20
|
+
|
|
21
|
+
Goal: move all existing resolution logic into per-service `Resolver` subclasses following
|
|
22
|
+
the `S3Resolver` pattern, wire a dispatcher that iterates them, and delete the three old
|
|
23
|
+
monolith files. **Functionality stays the same** (same URLs, same parallel name search,
|
|
24
|
+
same priority tie-breaking).
|
|
25
|
+
|
|
26
|
+
## Key design points (settled)
|
|
27
|
+
|
|
28
|
+
- **Granularity:** one class per service, splitting the ec2 namespace into a separate
|
|
29
|
+
`Ec2Resolver` (instances, AMIs, volumes, snapshots, launch templates, ENIs, security
|
|
30
|
+
groups) and `VpcResolver` (vpc, subnet, route table, nat gateway, acl).
|
|
31
|
+
- **Static-only services** (cost, support, athena, ecr, bedrock, config, controltower,
|
|
32
|
+
cognito, cloudfront, route53, kms-link, storagegateway, fsx, ssm, parameter, cloudtrail,
|
|
33
|
+
tennex): each gets its **own** `Resolver` subclass. **No shared mixin/base** — every class
|
|
34
|
+
implements all four methods explicitly, with the three unused ones written out as
|
|
35
|
+
`return None`. `sns`, `sqs`, `ecs`, `eks` (and every other service) stay their own separate
|
|
36
|
+
classes; none are combined.
|
|
37
|
+
- **Name lookup stays parallel + priority-ordered:** logs > s3 > ec2 > cloudformation >
|
|
38
|
+
lambda (existing order in [nameResolver.py:24](src/nate2_scripts/console/nameResolver.py#L24)).
|
|
39
|
+
- **URL wrapping:** resolvers return a **bare host+path** (e.g. `console.aws.amazon.com/s3/...`
|
|
40
|
+
for global, `{region}.console.aws.amazon.com/...` for regional), exactly as `S3Resolver`
|
|
41
|
+
does. The **dispatcher** prepends `https://{mdn}`. `mdn` (multisession domain name) stays
|
|
42
|
+
out of `Context` — it's the dispatcher's concern.
|
|
43
|
+
|
|
44
|
+
## Context object
|
|
45
|
+
|
|
46
|
+
Extend `Context` in [resolvers/__init__.py](src/nate2_scripts/console/resolvers/__init__.py)
|
|
47
|
+
so resolvers have what the monoliths used to pull from constructor args:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
class Context:
|
|
51
|
+
session: boto3.Session # built from assumed creds in main()
|
|
52
|
+
default_region: str
|
|
53
|
+
regions: list[str] # NEW — from RegionCache, for multi-region name/id search
|
|
54
|
+
current_account: str # for arn.account access checks
|
|
55
|
+
profile: str
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`session` replaces the bare `boto3.client(...)` calls in the old resolvers — every
|
|
59
|
+
resolver uses `context.session.client(svc, region_name=region)`. In
|
|
60
|
+
[console/__init__.py](src/nate2_scripts/console/__init__.py) `main()`, build the session
|
|
61
|
+
from the `creds` already obtained via `get_credentials()` and populate `Context`.
|
|
62
|
+
|
|
63
|
+
## New files (under `resolvers/`)
|
|
64
|
+
|
|
65
|
+
Resource-resolving services (port the matching `parse_*` / `_resolve_*` bodies verbatim,
|
|
66
|
+
swapping `boto3.client` → `context.session.client` and dropping the `https://{mdn}` prefix
|
|
67
|
+
from returned strings so the dispatcher adds it):
|
|
68
|
+
|
|
69
|
+
| File | Class | Dimensions ported from |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `ec2.py` | `Ec2Resolver` | arn `instance`; ids `i-,sg-,ami-,lt-,vol-,snap-,eni-`; name tag:Name; service `ec2`,`sg`,`ami`,`elb`* |
|
|
72
|
+
| `vpc.py` | `VpcResolver` | arn `subnet`,`natgateway`; ids `vpc-,subnet-,rtb-,nat-`; service `vpc`,`subnet`,`acl` |
|
|
73
|
+
| `iam.py` | `IamResolver` | arn `role`,`user`; ids `role/`,`usr/`; service `iam` (global, us-east-1) |
|
|
74
|
+
| `lambda.py` | `LambdaResolver` | arn `function`; name function; service `lambda` |
|
|
75
|
+
| `logs.py` | `LogsResolver` | arn `log-group`; name log-group; service `logs`,`cw`,`cloudwatch` |
|
|
76
|
+
| `cloudformation.py` | `CloudFormationResolver` | name stack; service `cf`,`cloudformation` |
|
|
77
|
+
| `rds.py` | `RdsResolver` | arn `db`,`cluster`; service `rds` |
|
|
78
|
+
| `secretsmanager.py` | `SecretsManagerResolver` | arn `secret`; service `sm`,`secretsmanager`,`secretmanager` |
|
|
79
|
+
| `ecs.py` | `EcsResolver` | arn `cluster`,`service`,`task`,`task-definition`; service `ecs` |
|
|
80
|
+
| `eks.py` | `EksResolver` | arn `cluster`; service `eks` |
|
|
81
|
+
| `sns.py` | `SnsResolver` | arn topic; service `sns` |
|
|
82
|
+
| `sqs.py` | `SqsResolver` | arn queue; service `sqs` |
|
|
83
|
+
| `batch.py` | `BatchResolver` | arn `job-queue`,`compute-environment`,`job-definition`,`job`; service `batch` |
|
|
84
|
+
| `dynamodb.py` | `DynamoDbResolver` | arn `table`; service `dynamo` |
|
|
85
|
+
| `stepfunctions.py` | `StepFunctionsResolver` | arn `stateMachine`,`execution`; service `step` |
|
|
86
|
+
| `elb.py` | `ElbResolver` | arn `loadbalancer`,`targetgroup`; service `elb` |
|
|
87
|
+
|
|
88
|
+
*`elb`/`acl`/`ami`/`sg` service keywords are static list-view links — keep them on the
|
|
89
|
+
resolver that owns that resource where natural, or in a static resolver; either is fine.
|
|
90
|
+
|
|
91
|
+
Static-only resolvers (one class each): `cost.py`, `support.py`, `athena.py`, `ecr.py`,
|
|
92
|
+
`bedrock.py`, `config.py`, `controltower.py`, `cognito.py`, `cloudfront.py`, `route53.py`,
|
|
93
|
+
`kms.py`, `storagegateway.py`, `fsx.py`, `ssm.py`, `cloudtrail.py`, `tennex.py` — seeded
|
|
94
|
+
from the remaining entries of `_get_services()` in
|
|
95
|
+
[destitationResolver.py:60](src/nate2_scripts/console/destitationResolver.py#L60).
|
|
96
|
+
**No shared base/mixin.** Each class implements `try_resolve_service` (matching its own
|
|
97
|
+
keyword aliases) and writes out `try_resolve_name`, `try_resolve_arn`, `try_resolve_id` as
|
|
98
|
+
explicit `return None`.
|
|
99
|
+
|
|
100
|
+
## Dispatcher
|
|
101
|
+
|
|
102
|
+
Rewrite [destitationResolver.py](src/nate2_scripts/console/destitationResolver.py)
|
|
103
|
+
`parse_destination` (keep `DestinationResolver` name + `get_service_list()` so
|
|
104
|
+
[console/__init__.py](src/nate2_scripts/console/__init__.py) callers are untouched) to hold
|
|
105
|
+
an ordered `list[Resolver]` and dispatch:
|
|
106
|
+
|
|
107
|
+
1. Empty arg → `https://{mdn}{default_region}.console.aws.amazon.com/`.
|
|
108
|
+
2. `Arn.try_parse(arg)` succeeds → **ARN input is terminal.** Try each resolver's
|
|
109
|
+
`try_resolve_arn`; return the first non-None. If none resolve it, print an
|
|
110
|
+
ARN-specific error to stderr and `sys.exit(1)` — **do not** fall through to the
|
|
111
|
+
service/id/name steps below.
|
|
112
|
+
3. `try_resolve_service(ctx, arg, search)` across resolvers → first non-None.
|
|
113
|
+
4. Else `try_resolve_id(ctx, arg)` across resolvers → first non-None.
|
|
114
|
+
5. Else **name** search: run each name-capable resolver's `try_resolve_name` concurrently
|
|
115
|
+
(`ThreadPoolExecutor`, reuse `MAX_THREADS`), collect by each resolver's priority index,
|
|
116
|
+
return the lowest-priority winner — preserving [nameResolver.py:33-48](src/nate2_scripts/console/nameResolver.py#L33-L48).
|
|
117
|
+
Each regional resolver loops `context.regions` internally.
|
|
118
|
+
6. None matched → same stderr error + `sys.exit(1)`.
|
|
119
|
+
|
|
120
|
+
Every returned bare path is wrapped as `https://{mdn}{path}` before returning.
|
|
121
|
+
`get_service_list()` returns the union of every resolver's known service keywords (for
|
|
122
|
+
argcomplete in [console/__init__.py:129](src/nate2_scripts/console/__init__.py#L129)).
|
|
123
|
+
|
|
124
|
+
- **`search` is a real second positional CLI arg.** Add a `search` positional (`nargs="?"`,
|
|
125
|
+
default `""`) to `parse_args` in
|
|
126
|
+
[console/__init__.py:115](src/nate2_scripts/console/__init__.py#L115), pass it into
|
|
127
|
+
`parse_destination(service, region, search)`, and thread it through to
|
|
128
|
+
`try_resolve_service` and `try_resolve_name`. Usage becomes `console <service> <search>`
|
|
129
|
+
(e.g. `console ec2 <filter>`).
|
|
130
|
+
- **Apply `search` to the URL when given and the target console supports it.** When
|
|
131
|
+
`search` is non-empty and the resolver's console view accepts a filter/search query
|
|
132
|
+
param, the resolver appends it (URL-encoded) so the console opens pre-filtered — e.g.
|
|
133
|
+
EC2 instances `...#Instances:search=<term>`, Lambda `...#/functions?fo=and&k0=...&v0=<term>`,
|
|
134
|
+
CloudWatch log-groups list, S3 object/bucket search, etc. Where a console view has no such
|
|
135
|
+
param (as noted in the `S3Resolver` comments), the resolver ignores `search` and returns
|
|
136
|
+
the plain URL. Each ported resolver decides per-view; an empty `search` always yields the
|
|
137
|
+
current (unfiltered) URL, preserving today's behavior. The exact query-param syntax per
|
|
138
|
+
service is verified against the live console during implementation.
|
|
139
|
+
- `try_resolve_*` methods **return `None`** on any failure (matching `S3Resolver`), rather
|
|
140
|
+
than `sys.exit` mid-resolve as the old monoliths did. Net effect: the final "not found"
|
|
141
|
+
error is emitted once by the dispatcher. This drops a few service-specific error strings
|
|
142
|
+
(e.g. "current account X cannot access Y") in favor of the unified message — a deliberate
|
|
143
|
+
simplification consistent with the target pattern.
|
|
144
|
+
|
|
145
|
+
## Cleanup
|
|
146
|
+
|
|
147
|
+
Delete [arnResolver.py](src/nate2_scripts/console/arnResolver.py),
|
|
148
|
+
[nameResolver.py](src/nate2_scripts/console/nameResolver.py),
|
|
149
|
+
[resourceIdResolver.py](src/nate2_scripts/console/resourceIdResolver.py) once their logic
|
|
150
|
+
is ported. Move the `split()` helper from arnResolver into `resolvers/__init__.py` (shared
|
|
151
|
+
by several resolvers). Keep `Arn` in `resolvers/__init__.py` as the single ARN parser
|
|
152
|
+
(replaces `ParsedArn` + `split`-based parsing).
|
|
153
|
+
|
|
154
|
+
## Verification
|
|
155
|
+
|
|
156
|
+
No test suite exists, so verify by running the CLI against a real profile:
|
|
157
|
+
|
|
158
|
+
1. `asp <profile>` then reinstall/link: `pip install -e .` (or `uv pip install -e .`).
|
|
159
|
+
2. Exercise each dimension and confirm the same URL opens as before the refactor:
|
|
160
|
+
- **service keyword:** `console s3`, `console ec2`, `console cost`, `console logs`, `console sm`
|
|
161
|
+
- **arn:** `console arn:aws:s3:::<bucket>`, `console arn:aws:lambda:us-east-1:<acct>:function:<fn>`,
|
|
162
|
+
`console arn:aws:iam::<acct>:role/<role>`
|
|
163
|
+
- **resource id:** `console i-<id>`, `console vpc-<id>`, `console subnet-<id>`
|
|
164
|
+
- **name:** `console <bucket-name>`, `console <stack-name>`, `console <lambda-name>`,
|
|
165
|
+
`console <ec2-Name-tag>` — confirm priority tie-break still favors logs > s3 > ec2 > cfn > lambda.
|
|
166
|
+
- **search arg:** `console ec2 <term>` — confirm the second positional is accepted and
|
|
167
|
+
threaded through (no crash; ignored by resolvers that don't use it yet).
|
|
168
|
+
- **empty:** `console` → region landing page.
|
|
169
|
+
- **unknown:** `console zzzzz` → stderr "Unknown service" + exit 1.
|
|
170
|
+
3. Confirm argcomplete still lists services: `console <TAB>` (or inspect `get_service_list()`).
|
|
171
|
+
4. Spot-check that the multisession domain prefix (`{mdn}`) still appears in generated URLs
|
|
172
|
+
when a multi-session hash is cached (the `-f` path vs cached path in
|
|
173
|
+
[console/__init__.py:174-185](src/nate2_scripts/console/__init__.py#L174-L185)).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# plan
|
|
2
|
+
|
|
3
|
+
I want:
|
|
4
|
+
|
|
5
|
+
- resolve arns
|
|
6
|
+
- tries to switch to account if -s or --switch is specified
|
|
7
|
+
- resolve resource names in
|
|
8
|
+
- resolve service with search parameter
|
|
9
|
+
search parameter is the second arg
|
|
10
|
+
- resolve ids
|
|
11
|
+
|
|
12
|
+
- p or --profile
|
|
13
|
+
- takes a profile glob
|
|
14
|
+
|
|
15
|
+
- f or --force
|
|
16
|
+
- refreshes mdn cache
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nate2-scripts"
|
|
7
|
+
version = "2.0.0"
|
|
8
|
+
description = "Utility scripts for AWS development on macOS"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"argcomplete",
|
|
12
|
+
"boto3",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
console = "nate2_scripts.console:main"
|
|
17
|
+
bing-wrapper = "nate2_scripts.bing_wrapper:main"
|
|
18
|
+
|
|
19
|
+
[tool.hatch.build.targets.wheel]
|
|
20
|
+
packages = ["src/nate2_scripts"]
|
|
21
|
+
|
|
22
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
23
|
+
"src/nate2_scripts/bing_wrapper/autocomplete.sh" = "nate2_scripts/bing_wrapper/autocomplete.sh"
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = [
|
|
27
|
+
"boto3-stubs[essential]",
|
|
28
|
+
"moto[all]",
|
|
29
|
+
"pytest",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[tool.pytest.ini_options]
|
|
33
|
+
testpaths = ["tests"]
|
|
34
|
+
# Resolver test modules are named after the service they cover (s3.py, logs.py,
|
|
35
|
+
# ...) rather than test_*.py, so widen collection to any module under tests/.
|
|
36
|
+
python_files = ["*.py"]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/tennex-nwestfall/nate2-scripts"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""PTY wrapper for cdk that plays a sound when (y/n) prompts appear."""
|
|
2
2
|
|
|
3
|
+
import argparse
|
|
3
4
|
import fcntl
|
|
4
5
|
import os
|
|
5
6
|
import select
|
|
@@ -8,8 +9,8 @@ import struct
|
|
|
8
9
|
import subprocess
|
|
9
10
|
import sys
|
|
10
11
|
import termios
|
|
12
|
+
import threading
|
|
11
13
|
import tty
|
|
12
|
-
import argparse
|
|
13
14
|
|
|
14
15
|
# _PATTERN = b"(y/n)"
|
|
15
16
|
# _WINDOW = len(_PATTERN) * 2
|
|
@@ -55,6 +56,28 @@ def run(args: argparse.Namespace) -> None:
|
|
|
55
56
|
# ---- parent ----
|
|
56
57
|
os.close(slave_fd)
|
|
57
58
|
|
|
59
|
+
# Loop sound-while for the lifetime of the child process.
|
|
60
|
+
_stop_sound_while = threading.Event()
|
|
61
|
+
_sound_while_thread = None
|
|
62
|
+
|
|
63
|
+
def _play_sound_while_loop() -> None:
|
|
64
|
+
while not _stop_sound_while.is_set():
|
|
65
|
+
proc = subprocess.Popen(["afplay", args.sound_while])
|
|
66
|
+
while proc.poll() is None:
|
|
67
|
+
if _stop_sound_while.wait(timeout=0.05):
|
|
68
|
+
proc.terminate()
|
|
69
|
+
try:
|
|
70
|
+
proc.wait(timeout=1)
|
|
71
|
+
except subprocess.TimeoutExpired:
|
|
72
|
+
proc.kill()
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
if args.sound_while:
|
|
76
|
+
_sound_while_thread = threading.Thread(
|
|
77
|
+
target=_play_sound_while_loop, daemon=True
|
|
78
|
+
)
|
|
79
|
+
_sound_while_thread.start()
|
|
80
|
+
|
|
58
81
|
# Propagate terminal resizes to the child (also sends SIGWINCH to it).
|
|
59
82
|
def _sigwinch(signum: int, frame) -> None:
|
|
60
83
|
try:
|
|
@@ -119,6 +142,10 @@ def run(args: argparse.Namespace) -> None:
|
|
|
119
142
|
|
|
120
143
|
_, status = os.waitpid(pid, 0)
|
|
121
144
|
|
|
145
|
+
_stop_sound_while.set()
|
|
146
|
+
if _sound_while_thread is not None:
|
|
147
|
+
_sound_while_thread.join(timeout=2)
|
|
148
|
+
|
|
122
149
|
if args.end:
|
|
123
150
|
subprocess.Popen(["afplay", args.sound_end])
|
|
124
151
|
if os.WIFEXITED(status):
|
|
@@ -156,9 +183,25 @@ def main() -> None:
|
|
|
156
183
|
type=str,
|
|
157
184
|
default="/System/Library/Sounds/Glass.aiff",
|
|
158
185
|
)
|
|
186
|
+
parser.add_argument(
|
|
187
|
+
"--sound-while",
|
|
188
|
+
help="play sound while command is running",
|
|
189
|
+
type=str,
|
|
190
|
+
default="",
|
|
191
|
+
)
|
|
192
|
+
parser.add_argument(
|
|
193
|
+
"--autocomplete",
|
|
194
|
+
help="return autocomplete scripts",
|
|
195
|
+
action="store_true",
|
|
196
|
+
)
|
|
159
197
|
|
|
160
198
|
args = parser.parse_args()
|
|
161
199
|
|
|
200
|
+
if args.autocomplete:
|
|
201
|
+
script = os.path.join(os.path.dirname(__file__), "autocomplete.sh")
|
|
202
|
+
print(open(script).read())
|
|
203
|
+
sys.exit(0)
|
|
204
|
+
|
|
162
205
|
if not args.command:
|
|
163
206
|
parser.print_help()
|
|
164
207
|
sys.exit(1)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
_bing_wrapper() {
|
|
2
|
+
local -a bing_args
|
|
3
|
+
local -i double_dash_pos=0
|
|
4
|
+
|
|
5
|
+
# Find position of "--"
|
|
6
|
+
for (( i=1; i <= $#words; i++ )); do
|
|
7
|
+
if [[ "${words[$i]}" == "--" ]]; then
|
|
8
|
+
double_dash_pos=$i
|
|
9
|
+
break
|
|
10
|
+
fi
|
|
11
|
+
done
|
|
12
|
+
|
|
13
|
+
if (( double_dash_pos > 0 && CURRENT > double_dash_pos )); then
|
|
14
|
+
# Delegate to the wrapped command's completion
|
|
15
|
+
words=("${words[@]:$double_dash_pos}")
|
|
16
|
+
CURRENT=$(( CURRENT - double_dash_pos ))
|
|
17
|
+
_normal # let zsh look up the right completion for inner_cmd
|
|
18
|
+
else
|
|
19
|
+
# Complete bing-wrapper's own options
|
|
20
|
+
_arguments \
|
|
21
|
+
'--end[play sound at end]' \
|
|
22
|
+
'*--pattern[pattern to listen for]:pattern:' \
|
|
23
|
+
'--sound-end[sound file at end]:file:_files' \
|
|
24
|
+
'--sound-pattern[sound file for pattern]:file:_files' \
|
|
25
|
+
'--sound-while[sound file for duration of command]:file:_files' \
|
|
26
|
+
'--[inner command]:command:_normal' \
|
|
27
|
+
fi
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
compdef _bing_wrapper bing-wrapper
|