@keinar/aac-cli 1.1.0 → 1.1.2

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.
Files changed (3) hide show
  1. package/README.md +53 -25
  2. package/dist/index.cjs +16 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,63 +1,91 @@
1
- # AAC CLI
1
+ # @keinar/aac-cli
2
2
 
3
- > **Agnostic Automation Center CLI** — Prepare any test automation repository to run seamlessly inside the AAC containerized platform.
3
+ > **Agnostic Automation Center CLI** — The deployment assistant that prepares and ships any test automation repository to the AAC platform, end-to-end.
4
4
 
5
5
  [![Release](https://github.com/keinar/aac-cli/actions/workflows/release.yml/badge.svg)](https://github.com/keinar/aac-cli/actions/workflows/release.yml)
6
- [![npm](https://img.shields.io/npm/v/aac-cli)](https://www.npmjs.com/package/aac-cli)
6
+ [![npm](https://img.shields.io/npm/v/@keinar/aac-cli)](https://www.npmjs.com/package/@keinar/aac-cli)
7
+
8
+ ## Quick Start
9
+
10
+ ```bash
11
+ npx @keinar/aac-cli@latest init
12
+ ```
13
+
14
+ > No installation required. This always runs the latest version.
7
15
 
8
16
  ## What It Does
9
17
 
10
- `aac-cli init` generates the integration files your test automation project needs to run inside the AAC platform:
18
+ `@keinar/aac-cli` handles the entire flow from raw test project to a deployed, platform-ready Docker image:
19
+
20
+ ### 1. Generates Integration Files
11
21
 
12
22
  | File | Purpose |
13
23
  |---|---|
14
- | `Dockerfile` | Builds your test suite image (no ENTRYPOINT/CMD the AAC Worker injects it) |
15
- | `entrypoint.sh` | Executed by the Worker at runtime: `/app/entrypoint.sh <folder>` |
24
+ | `Dockerfile` | Builds your test suite image no `ENTRYPOINT`/`CMD` (the AAC Worker injects it at runtime) |
25
+ | `entrypoint.sh` | Executed by the Worker: `/app/entrypoint.sh <folder>` |
16
26
  | `.dockerignore` | Prevents secrets (`.env`, `.git`) and bloat (`node_modules`, `__pycache__`) from entering the image |
17
27
 
28
+ ### 2. Detects Framework Versions
29
+
30
+ For **Playwright** projects, the CLI reads your `package.json`, extracts the `@playwright/test` version, and uses it to pin the exact Docker base image (`mcr.microsoft.com/playwright:v{version}-jammy`). No more mismatched browser binaries.
31
+
32
+ ### 3. Automates Docker Deployment
33
+
34
+ After generating files, the CLI offers to handle the full deployment:
35
+
36
+ - **Docker Hub login** — interactive authentication via `docker login`
37
+ - **Project name detection** — reads from `package.json`, with manual override
38
+ - **Multi-platform build** — `linux/amd64` + `linux/arm64` via Docker Buildx, ensuring compatibility with any AAC Worker architecture
39
+ - **Push to registry** — ships the image directly to Docker Hub
40
+
18
41
  ### Supported Frameworks
19
42
 
20
- - **Playwright** (TypeScript / Node.js)
43
+ - **Playwright** (TypeScript / Node.js) — auto-detects version
21
44
  - **Pytest** (Python)
22
45
 
23
- ## Quick Start
46
+ ## Prerequisites
24
47
 
25
- ```bash
26
- # No installation needed run directly with npx
27
- npx aac-cli init
28
- ```
48
+ - **Node.js 18+** — required to run the CLI
49
+ - **Docker Desktop**must be running for the automated build & push features
29
50
 
30
51
  ## Installation (optional)
31
52
 
32
53
  ```bash
33
- npm install -g aac-cli
54
+ npm install -g @keinar/aac-cli
34
55
  ```
35
56
 
36
57
  ## Usage
37
58
 
38
59
  ```bash
39
- # Initialize AAC integration in your project directory
40
- cd my-playwright-tests
41
- aac-cli init
60
+ # Run the full init + deploy flow
61
+ npx @keinar/aac-cli@latest init
42
62
 
43
63
  # Check version
44
- aac-cli --version
64
+ npx @keinar/aac-cli@latest --version
45
65
  ```
46
66
 
47
67
  ## How It Works
48
68
 
49
- 1. Run `aac-cli init` inside your test project.
50
- 2. Select your framework (Playwright or Pytest).
51
- 3. The CLI generates `Dockerfile`, `entrypoint.sh`, and `.dockerignore`.
52
- 4. Build & push your Docker image.
53
- 5. Enter the image name in the AAC Dashboard.
69
+ ```
70
+ ┌─────────────────────────────────────────────────┐
71
+ │ npx @keinar/aac-cli@latest init │
72
+ ├─────────────────────────────────────────────────┤
73
+ │ 1. Select framework (Playwright / Pytest) │
74
+ │ 2. Auto-detect version from package.json │
75
+ │ 3. Generate Dockerfile + entrypoint.sh │
76
+ │ 4. (Optional) Docker login │
77
+ │ 5. (Optional) Buildx multi-platform build │
78
+ │ 6. (Optional) Push image to Docker Hub │
79
+ │ 7. Enter image name in the AAC Dashboard │
80
+ └─────────────────────────────────────────────────┘
81
+ ```
54
82
 
55
83
  ## Development
56
84
 
57
85
  ```bash
58
86
  npm install
59
87
  npm run build
60
- node dist/index.js init
88
+ node dist/index.cjs init
61
89
  ```
62
90
 
63
91
  ## Release
@@ -65,8 +93,8 @@ node dist/index.js init
65
93
  Tag a new version to trigger the CI/CD pipeline:
66
94
 
67
95
  ```bash
68
- git tag v1.0.0
69
- git push origin v1.0.0
96
+ git tag v1.1.0
97
+ git push origin v1.1.0
70
98
  ```
71
99
 
72
100
  The workflow will build with tsup, publish to npm, and create a GitHub release.
package/dist/index.cjs CHANGED
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var N=Object.create;var b=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty;var G=(t,n,o,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of j(n))!_.call(t,i)&&i!==o&&b(t,i,{get:()=>n[i],enumerable:!(r=Y(n,i))||r.enumerable});return t};var f=(t,n,o)=>(o=t!=null?N(S(t)):{},G(n||!t||!t.__esModule?b(o,"default",{value:t,enumerable:!0}):o,t));var T=require("commander"),s=f(require("@clack/prompts"),1),y=f(require("picocolors"),1);var g=require("fs/promises"),D=require("path"),c=f(require("@clack/prompts"),1),p=f(require("picocolors"),1);var w="1.50.0";function O(t){return`FROM mcr.microsoft.com/playwright:v${t}-jammy
2
+ "use strict";var I=Object.create;var b=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var _=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var U=(t,n,o,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of Y(n))!j.call(t,i)&&i!==o&&b(t,i,{get:()=>n[i],enumerable:!(r=S(n,i))||r.enumerable});return t};var g=(t,n,o)=>(o=t!=null?I(_(t)):{},U(n||!t||!t.__esModule?b(o,"default",{value:t,enumerable:!0}):o,t));var N=require("commander"),s=g(require("@clack/prompts"),1),y=g(require("picocolors"),1);var f=require("fs/promises"),R=require("path"),c=g(require("@clack/prompts"),1),p=g(require("picocolors"),1);var w="1.50.0";function E(t){return`FROM mcr.microsoft.com/playwright:v${t}-jammy
3
3
 
4
4
  WORKDIR /app
5
5
 
6
6
  COPY package*.json ./
7
7
  RUN npm ci
8
+ RUN npm install -g allure-commandline
8
9
 
9
10
  COPY . .
10
11
 
11
12
  RUN chmod +x /app/entrypoint.sh
12
- `}var E=`#!/bin/sh
13
+ `}var O=`#!/bin/sh
13
14
 
14
15
  FOLDER=$1
15
16
 
@@ -18,13 +19,18 @@ if [ -f .env ]; then
18
19
  rm .env
19
20
  fi
20
21
 
22
+ echo "Running against BASE_URL: $BASE_URL"
23
+
21
24
  if [ -z "$FOLDER" ] || [ "$FOLDER" = "all" ]; then
22
25
  echo "Running ALL tests..."
23
- exec npx playwright test
26
+ npx playwright test
24
27
  else
25
28
  echo "Running tests in folder: $FOLDER"
26
- exec npx playwright test "$FOLDER"
29
+ npx playwright test "$FOLDER"
27
30
  fi
31
+
32
+ echo "Generating Allure Report..."
33
+ npx allure generate allure-results --clean -o allure-report
28
34
  `,P=`FROM python:3.11-slim
29
35
 
30
36
  WORKDIR /app
@@ -56,9 +62,9 @@ fi
56
62
  node_modules
57
63
  __pycache__
58
64
  .venv
59
- `;async function H(t){try{let n=await(0,g.readFile)((0,D.join)(t,"package.json"),"utf-8"),o=JSON.parse(n),r=o.devDependencies?.["@playwright/test"]??o.dependencies?.["@playwright/test"];return r?r.replace(/^[^\d]*/,"")||w:w}catch{return w}}async function U(t,n){let o=t==="playwright",r;if(o){let i=await H(n);c.log.info(`Detected Playwright version: ${p.default.cyan("v"+i)}`),r=O(i)}else r=P;return[{name:".dockerignore",content:$,mode:420},{name:"entrypoint.sh",content:o?E:C,mode:493},{name:"Dockerfile",content:r,mode:420}]}async function q(t){try{return await(0,g.stat)(t),!0}catch{return!1}}function B(t){return t.replace(/\r\n/g,`
60
- `)}async function F(t,n){let o=await U(t,n),r=new Set;for(let a of o){let d=(0,D.join)(n,a.name);if(await q(d)){let m=await c.confirm({message:`File ${p.default.yellow(a.name)} already exists. Overwrite?`});c.isCancel(m)&&(c.cancel("Operation cancelled."),process.exit(0)),m||r.add(a.name)}}if(r.size===o.length){c.log.warn("All files were skipped. No changes were made.");return}let i=[];for(let a of o){if(r.has(a.name))continue;let d=(0,D.join)(n,a.name),m=B(a.content);await(0,g.writeFile)(d,m,{mode:a.mode}),i.push(a.name)}for(let a of i)c.log.success(`Created ${p.default.green(a)}`);c.note([`${p.default.bold("1.")} docker build -t your-username/my-automation-tests:latest .`,`${p.default.bold("2.")} docker push your-username/my-automation-tests:latest`,`${p.default.bold("3.")} Enter this image name in the AAC Dashboard.`].join(`
61
- `),`${p.default.green("\u2705")} Next steps to connect your project to the AAC`)}var R=require("child_process"),v=require("fs/promises"),A=require("path"),e=f(require("@clack/prompts"),1),l=f(require("picocolors"),1);async function W(t){try{let n=await(0,v.readFile)((0,A.join)(t,"package.json"),"utf-8"),o=JSON.parse(n);return o.name?o.name.replace(/^@[^/]+\//,""):null}catch{return null}}async function L(t){let n=await e.confirm({message:"Do you have a Docker Hub account?"});if(e.isCancel(n)){e.cancel("Deployment cancelled.");return}n||(e.note(`Create a free account at ${l.default.cyan("https://hub.docker.com")}
62
- Press Enter when you're ready to continue.`,l.default.yellow("Docker Hub Account Required")),await e.text({message:"Press Enter to continue...",defaultValue:"",placeholder:""}));let o=await e.text({message:"What is your Docker Hub username?",validate:u=>{if(!u.trim())return"Username is required."}});if(e.isCancel(o)){e.cancel("Deployment cancelled.");return}let r=await W(t),i;if(r){let u=await e.confirm({message:`Detected project name "${l.default.cyan(r)}". Use this for the image?`});if(e.isCancel(u)){e.cancel("Deployment cancelled.");return}if(u)i=r;else{let h=await e.text({message:"Enter the image name:",validate:I=>{if(!I.trim())return"Image name is required."}});if(e.isCancel(h)){e.cancel("Deployment cancelled.");return}i=h.trim()}}else{let u=await e.text({message:"Enter the image name:",validate:h=>{if(!h.trim())return"Image name is required."}});if(e.isCancel(u)){e.cancel("Deployment cancelled.");return}i=u.trim()}let a=`${o.trim()}/${i}:latest`,d=await e.confirm({message:"I need to log you into Docker. Send a login request?"});if(e.isCancel(d)){e.cancel("Deployment cancelled.");return}if(d){e.log.step("Opening Docker login...");try{(0,R.execSync)("docker login",{stdio:"inherit",cwd:t}),e.log.success("Docker login successful.")}catch{e.log.error("Docker login failed. Please try again manually.");return}}let m=await e.confirm({message:`Build multi-platform image ${l.default.cyan(a)} and push to Docker Hub?`});if(e.isCancel(m)||!m){e.log.warn("Build skipped."),e.note([`${l.default.bold("1.")} docker buildx create --name aac-builder --use`,`${l.default.bold("2.")} docker buildx build --platform linux/amd64,linux/arm64 -t ${a} --push .`].join(`
63
- `),"You can build manually later");return}let k=e.spinner();try{k.start("Setting up Docker Buildx builder..."),(0,R.execSync)("docker buildx create --name aac-builder --use",{stdio:"pipe",cwd:t}),k.stop("Buildx builder ready.")}catch{k.stop("Using existing Buildx builder.")}e.log.step(`Building & pushing ${l.default.cyan(a)}...`);try{(0,R.execSync)(`docker buildx build --platform linux/amd64,linux/arm64 -t ${a} --push .`,{stdio:"inherit",cwd:t}),e.log.success(`Image ${l.default.green(a)} pushed successfully!`),e.note(`Go to the ${l.default.bold("AAC Dashboard")} and enter:
64
- ${l.default.cyan(a)}`,l.default.green("\u2705 Deployment Complete"))}catch{e.log.error("Docker build failed. Check the output above for details.")}}var x=new T.Command;x.name("aac-cli").description("Agnostic Automation Center CLI \u2014 Prepare any test automation repo for the AAC platform").version("1.1.0");x.command("init").description("Generate AAC integration files and optionally build & push your Docker image").action(async()=>{s.intro(y.default.bgCyan(y.default.black(" AAC CLI ")));let t=await s.select({message:"Select your automation project framework:",options:[{value:"playwright",label:"Playwright (TypeScript/Node.js)"},{value:"pytest",label:"Pytest (Python)"}]});s.isCancel(t)&&(s.cancel("Operation cancelled."),process.exit(0));let n=s.spinner();n.start("Generating AAC integration files..."),await new Promise(i=>setTimeout(i,300)),n.stop("Files ready.");let o=process.cwd();await F(t,o);let r=await s.confirm({message:"Do you want to build and push the image to Docker Hub right now?"});s.isCancel(r)&&(s.cancel("Operation cancelled."),process.exit(0)),r&&await L(o),s.outro(y.default.green("Done!")+" "+y.default.dim("Run `aac-cli init` again anytime."))});x.parse();
65
+ `;async function G(t){try{let n=await(0,f.readFile)((0,R.join)(t,"package.json"),"utf-8"),o=JSON.parse(n),r=o.devDependencies?.["@playwright/test"]??o.dependencies?.["@playwright/test"];return r?r.replace(/^[^\d]*/,"")||w:w}catch{return w}}async function B(t,n){let o=t==="playwright",r;if(o){let i=await G(n);c.log.info(`Detected Playwright version: ${p.default.cyan("v"+i)}`),r=E(i)}else r=P;return[{name:".dockerignore",content:$,mode:420},{name:"entrypoint.sh",content:o?O:C,mode:493},{name:"Dockerfile",content:r,mode:420}]}async function H(t){try{return await(0,f.stat)(t),!0}catch{return!1}}function q(t){return t.replace(/\r\n/g,`
66
+ `)}async function A(t,n){let o=await B(t,n),r=new Set;for(let a of o){let d=(0,R.join)(n,a.name);if(await H(d)){let m=await c.confirm({message:`File ${p.default.yellow(a.name)} already exists. Overwrite?`});c.isCancel(m)&&(c.cancel("Operation cancelled."),process.exit(0)),m||r.add(a.name)}}if(r.size===o.length){c.log.warn("All files were skipped. No changes were made.");return}let i=[];for(let a of o){if(r.has(a.name))continue;let d=(0,R.join)(n,a.name),m=q(a.content);await(0,f.writeFile)(d,m,{mode:a.mode}),i.push(a.name)}for(let a of i)c.log.success(`Created ${p.default.green(a)}`);c.note([`${p.default.bold("1.")} docker build -t your-username/my-automation-tests:latest .`,`${p.default.bold("2.")} docker push your-username/my-automation-tests:latest`,`${p.default.bold("3.")} Enter this image name in the AAC Dashboard.`].join(`
67
+ `),`${p.default.green("\u2705")} Next steps to connect your project to the AAC`)}var D=require("child_process"),F=require("fs/promises"),v=require("path"),e=g(require("@clack/prompts"),1),l=g(require("picocolors"),1);async function W(t){try{let n=await(0,F.readFile)((0,v.join)(t,"package.json"),"utf-8"),o=JSON.parse(n);return o.name?o.name.replace(/^@[^/]+\//,""):null}catch{return null}}async function L(t){let n=await e.confirm({message:"Do you have a Docker Hub account?"});if(e.isCancel(n)){e.cancel("Deployment cancelled.");return}n||(e.note(`Create a free account at ${l.default.cyan("https://hub.docker.com")}
68
+ Press Enter when you're ready to continue.`,l.default.yellow("Docker Hub Account Required")),await e.text({message:"Press Enter to continue...",defaultValue:"",placeholder:""}));let o=await e.text({message:"What is your Docker Hub username?",validate:u=>{if(!u.trim())return"Username is required."}});if(e.isCancel(o)){e.cancel("Deployment cancelled.");return}let r=await W(t),i;if(r){let u=await e.confirm({message:`Detected project name "${l.default.cyan(r)}". Use this for the image?`});if(e.isCancel(u)){e.cancel("Deployment cancelled.");return}if(u)i=r;else{let h=await e.text({message:"Enter the image name:",validate:T=>{if(!T.trim())return"Image name is required."}});if(e.isCancel(h)){e.cancel("Deployment cancelled.");return}i=h.trim()}}else{let u=await e.text({message:"Enter the image name:",validate:h=>{if(!h.trim())return"Image name is required."}});if(e.isCancel(u)){e.cancel("Deployment cancelled.");return}i=u.trim()}let a=`${o.trim()}/${i}:latest`,d=await e.confirm({message:"I need to log you into Docker. Send a login request?"});if(e.isCancel(d)){e.cancel("Deployment cancelled.");return}if(d){e.log.step("Opening Docker login...");try{(0,D.execSync)("docker login",{stdio:"inherit",cwd:t}),e.log.success("Docker login successful.")}catch{e.log.error("Docker login failed. Please try again manually.");return}}let m=await e.confirm({message:`Build multi-platform image ${l.default.cyan(a)} and push to Docker Hub?`});if(e.isCancel(m)||!m){e.log.warn("Build skipped."),e.note([`${l.default.bold("1.")} docker buildx create --name aac-builder --use`,`${l.default.bold("2.")} docker buildx build --platform linux/amd64,linux/arm64 -t ${a} --push .`].join(`
69
+ `),"You can build manually later");return}let k=e.spinner();try{k.start("Setting up Docker Buildx builder..."),(0,D.execSync)("docker buildx create --name aac-builder --use",{stdio:"pipe",cwd:t}),k.stop("Buildx builder ready.")}catch{k.stop("Using existing Buildx builder.")}e.log.step(`Building & pushing ${l.default.cyan(a)}...`);try{(0,D.execSync)(`docker buildx build --platform linux/amd64,linux/arm64 -t ${a} --push .`,{stdio:"inherit",cwd:t}),e.log.success(`Image ${l.default.green(a)} pushed successfully!`),e.note(`Go to the ${l.default.bold("AAC Dashboard")} and enter:
70
+ ${l.default.cyan(a)}`,l.default.green("\u2705 Deployment Complete"))}catch{e.log.error("Docker build failed. Check the output above for details.")}}var x=new N.Command;x.name("aac-cli").description("Agnostic Automation Center CLI \u2014 Prepare any test automation repo for the AAC platform").version("1.1.0");x.command("init").description("Generate AAC integration files and optionally build & push your Docker image").action(async()=>{s.intro(y.default.bgCyan(y.default.black(" AAC CLI ")));let t=await s.select({message:"Select your automation project framework:",options:[{value:"playwright",label:"Playwright (TypeScript/Node.js)"},{value:"pytest",label:"Pytest (Python)"}]});s.isCancel(t)&&(s.cancel("Operation cancelled."),process.exit(0));let n=s.spinner();n.start("Generating AAC integration files..."),await new Promise(i=>setTimeout(i,300)),n.stop("Files ready.");let o=process.cwd();await A(t,o);let r=await s.confirm({message:"Do you want to build and push the image to Docker Hub right now?"});s.isCancel(r)&&(s.cancel("Operation cancelled."),process.exit(0)),r&&await L(o),s.outro(y.default.green("Done!")+" "+y.default.dim("Run `aac-cli init` again anytime."))});x.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keinar/aac-cli",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Agnostic Automation Center CLI — Prepare any test automation repo for the AAC platform",
5
5
  "author": "keinar",
6
6
  "license": "MIT",