Skip to content

Commit 126a916

Browse files
authored
Merge pull request #156 from nanoapi-io/chore/windows-support
Add powershell install script for windows, verify windows support
2 parents da3191d + 1dd10a4 commit 126a916

File tree

9 files changed

+189
-91
lines changed

9 files changed

+189
-91
lines changed

README.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
![NanoAPI Banner](/media/github-banner.png)
22

3+
- [Documentation](https://docs.nanoapi.io)
4+
35
# napi - Better Software Architecture for the AI Age
46

57
`napi` is a versatile tool built by NanoAPI and designed to automatically
@@ -34,6 +36,9 @@ determinstic tool.
3436

3537
## Why `napi`?
3638

39+
- **Application Library**: `napi` is not just a CLI tool; it is a comprehensive
40+
application library of all projects and their interactions within your
41+
organization.
3742
- **Enables discovery into legacy systems**: indentify problematic code and
3843
potential improvements early.
3944
- **Modular Monoliths**: Simplifies the process of extracting functionality
@@ -65,24 +70,34 @@ For the latest updates, visit our [project board](/projects).
6570

6671
## Installation
6772

68-
`napi` works out of the box on both mac and linux systems.
73+
`napi` works out of the box on both mac, linux, and windows systems.
6974

70-
To use this tool on Windows, you will need to install and run the CLI commands
71-
from there.
75+
To install `napi`, you can use our installation script:
7276

73-
[WSL (Windows Subsystem for Linux)](https://learn.microsoft.com/en-us/windows/wsl/install)
77+
### Unix Systems (MacOS, Linux)
7478

75-
To install `napi`, you can use our installation script:
79+
Wyou to install napi using our convenience script:
7680

7781
```bash
7882
curl -fsSL https://raw.githubusercontent.com/nanoapi-io/napi/refs/heads/main/install_scripts/install.sh | bash
7983
```
8084

81-
Our download and install manually from:
85+
You can also download and install the latest release manually directly from our
86+
GitHub repository:
8287

8388
https://github.com/nanoapi-io/napi/releases/latest
8489

85-
This will download and install the latest version of `napi` to your system.
90+
### Windows
91+
92+
You can run napi using Windows Subsystem for Linux (WSL)
93+
https://learn.microsoft.com/en-us/windows/wsl/install
94+
95+
We are actively working on supporting windows natievely.
96+
97+
### Troubleshooting
98+
99+
If you encounter any issues during installation, please refer to our
100+
[Troubleshooting Guide](https://docs.nanoapi.io/default-guide/troubleshooting)
86101

87102
## CLI Usage
88103

@@ -97,6 +112,11 @@ napi --help
97112

98113
## Overview of all commands
99114

115+
### `napi login`
116+
117+
Authenticate with the NanoAPI service. This step is required to access the
118+
NanoAPI UI and to use certain features of `napi`.
119+
100120
### `napi init`
101121

102122
Initialize the project. This step is required before running any other command.
@@ -107,15 +127,7 @@ and settings necessary for further commands.
107127
### `napi manifest generate`
108128

109129
Generate a manifest of your codebase that captures its structure, dependencies,
110-
and relationships. This command analyzes your code and writes the manifest data
111-
to the configured `napi_out` directory in your project.
112-
113-
### `napi manifest view`
114-
115-
Open the NanoAPI UI in your default browser to visualize and explore the
116-
manifest generated by `napi manifest generate`. This provides an interactive
117-
view of your codebase's architecture, helping you identify areas for potential
118-
improvements and refactoring.
130+
and relationships and pushes it to your NanoAPI workspace in the app.
119131

120132
### `napi extract`
121133

install_scripts/install.ps1

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# PowerShell script to install NanoAPI CLI on Windows
2+
3+
$ErrorActionPreference = "Stop"
4+
5+
# Define platform-specific filename
6+
$FILENAME = "napi.exe"
7+
$RELEASE_URL = "https://github.com/nanoapi-io/napi/releases/latest/download/"
8+
$DOWNLOAD_URL = "$RELEASE_URL$FILENAME"
9+
10+
# Define install path (add this path to your system PATH if needed)
11+
$INSTALL_DIR = "$env:ProgramFiles\NanoAPI"
12+
$INSTALL_PATH = Join-Path $INSTALL_DIR "napi.exe"
13+
$BACKUP_PATH = "$INSTALL_PATH.bak"
14+
15+
# Create install directory if it doesn't exist
16+
if (-Not (Test-Path $INSTALL_DIR)) {
17+
New-Item -ItemType Directory -Path $INSTALL_DIR | Out-Null
18+
}
19+
20+
# Backup existing binary
21+
if (Test-Path $INSTALL_PATH) {
22+
Write-Output "Existing version found, creating a backup..."
23+
Move-Item -Force -Path $INSTALL_PATH -Destination $BACKUP_PATH
24+
} else {
25+
Write-Output "No existing version found, proceeding with installation."
26+
}
27+
28+
# Download the binary
29+
$TEMP_PATH = Join-Path $env:TEMP $FILENAME
30+
Write-Output "Downloading the latest version of $FILENAME..."
31+
Invoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $TEMP_PATH
32+
33+
# Install the binary
34+
Write-Output "Installing new version..."
35+
try {
36+
Move-Item -Force -Path $TEMP_PATH -Destination $INSTALL_PATH
37+
Write-Output "Installation/Update successful! You can now use your CLI by typing 'napi' if it's in your PATH."
38+
} catch {
39+
Write-Error "Update failed! Restoring the old version..."
40+
if (Test-Path $BACKUP_PATH) {
41+
Move-Item -Force -Path $BACKUP_PATH -Destination $INSTALL_PATH
42+
Write-Output "Restored the old version. Please try again later."
43+
} else {
44+
Write-Error "Backup not found. Manual intervention required."
45+
}
46+
}

src/cli/handlers/init/index.ts

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
createConfig,
44
getConfigFromWorkDir,
55
} from "../../middlewares/napiConfig.ts";
6-
import { join, normalize, relative } from "@std/path";
6+
import { join, normalize, relative, SEPARATOR } from "@std/path";
77
import type z from "zod";
88
import type { localConfigSchema } from "../../middlewares/napiConfig.ts";
99
import pythonStdlibList from "../../../scripts/generate_python_stdlib_list/output.json" with {
@@ -80,7 +80,7 @@ async function handler(
8080
if (confirmSave) {
8181
createConfig(napiConfig, argv.workdir);
8282
console.info("\n✅ Configuration saved successfully!");
83-
console.info(`📄 Created: ${argv.workdir}/.napirc`);
83+
console.info(`📄 Created: ${argv.workdir}${SEPARATOR}.napirc`);
8484
console.info("🎉 Your NanoAPI project is ready!");
8585
} else {
8686
console.info("❌ Configuration not saved");
@@ -238,8 +238,8 @@ async function collectIncludePatterns(
238238
Include patterns define which files NanoAPI will process and analyze.
239239
240240
Examples:
241-
- '**/*.py' for all Python files
242-
- 'src/**' for all files in src directory
241+
- '**${SEPARATOR}*.py' for all Python files
242+
- 'src${SEPARATOR}**' for all files in src directory
243243
- '*.py' for all Python files in the root directory
244244
`,
245245
);
@@ -281,7 +281,8 @@ Examples:
281281

282282
while (continueAdding) {
283283
const pattern = await input({
284-
message: "Enter glob pattern (e.g., '**/*.py', 'src/**')",
284+
message:
285+
`Enter glob pattern (e.g., '**${SEPARATOR}*.py', 'src${SEPARATOR}**')`,
285286
validate: (value) => {
286287
if (!value.trim()) return "Pattern cannot be empty";
287288
try {
@@ -363,9 +364,9 @@ async function collectExcludePatterns(
363364
Exclude patterns define which files NanoAPI will ignore during processing.
364365
365366
Examples:
366-
- 'node_modules/**' to exclude all node modules
367-
- '**/*.test.js' to exclude all JavaScript test files
368-
- '.git/**' to exclude git directory
367+
- 'node_modules${SEPARATOR}**' to exclude all node modules
368+
- '**${SEPARATOR}*.test.js' to exclude all JavaScript test files
369+
- '.git${SEPARATOR}**' to exclude git directory
369370
`,
370371
);
371372

@@ -410,7 +411,8 @@ Examples:
410411

411412
while (continueAdding) {
412413
const pattern = await input({
413-
message: "Enter glob pattern (e.g., 'node_modules/**', '**/*.test.js')",
414+
message:
415+
`Enter glob pattern (e.g., 'node_modules${SEPARATOR}**', '**${SEPARATOR}*.test.js')`,
414416
validate: (value) => {
415417
if (!value.trim()) return "Pattern cannot be empty";
416418
try {
@@ -488,45 +490,67 @@ function suggestIncludePatterns(
488490
// Language-specific suggestions
489491
if (language === pythonLanguage) {
490492
// Check for common Python project structures
491-
if (projectStructure.some((entry) => entry.includes("📂 src/"))) {
492-
suggestions.push("src/**/*.py");
493+
if (
494+
projectStructure.some((entry) => entry.includes(`📂 src${SEPARATOR}`))
495+
) {
496+
suggestions.push(`src${SEPARATOR}**${SEPARATOR}*.py`);
493497
}
494-
if (projectStructure.some((entry) => entry.includes("📂 lib/"))) {
495-
suggestions.push("lib/**/*.py");
498+
if (
499+
projectStructure.some((entry) => entry.includes(`📂 lib${SEPARATOR}`))
500+
) {
501+
suggestions.push(`lib${SEPARATOR}**${SEPARATOR}*.py`);
496502
}
497503
if (suggestions.length === 0) {
498-
if (projectStructure.some((entry) => entry.includes("📂 app/"))) {
499-
suggestions.push("app/**/*.py");
504+
if (
505+
projectStructure.some((entry) => entry.includes(`📂 app${SEPARATOR}`))
506+
) {
507+
suggestions.push(`app${SEPARATOR}**${SEPARATOR}*.py`);
500508
}
501509
}
502510

503511
// If no specific directories found, suggest all Python files
504512
if (suggestions.length === 0) {
505-
suggestions.push("**/*.py");
513+
suggestions.push(`**${SEPARATOR}*.py`);
506514
}
507515
} else if (language === csharpLanguage) {
508516
// Check for common C# project structures
509-
if (projectStructure.some((entry) => entry.includes("📂 src/"))) {
517+
if (
518+
projectStructure.some((entry) => entry.includes(`📂 src${SEPARATOR}`))
519+
) {
510520
suggestions.push("src/**/*.cs");
511521
}
512-
if (projectStructure.some((entry) => entry.includes("📂 lib/"))) {
513-
suggestions.push("lib/**/*.cs");
522+
if (
523+
projectStructure.some((entry) => entry.includes(`📂 lib${SEPARATOR}`))
524+
) {
525+
suggestions.push(`lib${SEPARATOR}**${SEPARATOR}*.cs`);
514526
}
515527
if (suggestions.length === 0) {
516-
if (projectStructure.some((entry) => entry.includes("📂 Controllers/"))) {
517-
suggestions.push("Controllers/**/*.cs");
528+
if (
529+
projectStructure.some((entry) =>
530+
entry.includes(`📂 Controllers${SEPARATOR}`)
531+
)
532+
) {
533+
suggestions.push(`Controllers${SEPARATOR}**${SEPARATOR}*.cs`);
518534
}
519-
if (projectStructure.some((entry) => entry.includes("📂 Models/"))) {
520-
suggestions.push("Models/**/*.cs");
535+
if (
536+
projectStructure.some((entry) =>
537+
entry.includes(`📂 Models${SEPARATOR}`)
538+
)
539+
) {
540+
suggestions.push(`Models${SEPARATOR}**${SEPARATOR}*.cs`);
521541
}
522-
if (projectStructure.some((entry) => entry.includes("📂 Services/"))) {
523-
suggestions.push("Services/**/*.cs");
542+
if (
543+
projectStructure.some((entry) =>
544+
entry.includes(`📂 Services${SEPARATOR}`)
545+
)
546+
) {
547+
suggestions.push(`Services${SEPARATOR}**${SEPARATOR}*.cs`);
524548
}
525549
}
526550

527551
// If no specific directories found, suggest all C# files
528552
if (suggestions.length === 0) {
529-
suggestions.push("**/*.cs");
553+
suggestions.push(`**${SEPARATOR}*.cs`);
530554
}
531555
}
532556

@@ -544,36 +568,36 @@ function suggestExcludePatterns(
544568
const suggestions: string[] = [];
545569

546570
// add outDir to the suggestions
547-
suggestions.push(`${outDir}/**`);
571+
suggestions.push(`${outDir}${SEPARATOR}**`);
548572

549573
// Common exclusions for all languages
550-
suggestions.push(".git/**");
551-
suggestions.push("**/dist/**");
552-
suggestions.push("**/build/**");
574+
suggestions.push(`.git${SEPARATOR}**`);
575+
suggestions.push(`**${SEPARATOR}dist${SEPARATOR}**`);
576+
suggestions.push(`**${SEPARATOR}build${SEPARATOR}**`);
553577

554578
// Language-specific suggestions
555579
if (language === pythonLanguage) {
556-
suggestions.push("**/__pycache__/**");
557-
suggestions.push("**/*.pyc");
558-
suggestions.push("**/.pytest_cache/**");
559-
suggestions.push("**/venv/**");
560-
suggestions.push("**/.env/**");
561-
suggestions.push("**/*.egg-info/**");
562-
suggestions.push("**/.tox/**");
563-
suggestions.push("**/.coverage");
564-
suggestions.push("**/htmlcov/**");
565-
suggestions.push("**/.mypy_cache/**");
580+
suggestions.push(`**${SEPARATOR}__pycache__${SEPARATOR}**`);
581+
suggestions.push(`**${SEPARATOR}*.pyc`);
582+
suggestions.push(`**${SEPARATOR}.pytest_cache${SEPARATOR}**`);
583+
suggestions.push(`**${SEPARATOR}venv${SEPARATOR}**`);
584+
suggestions.push(`**${SEPARATOR}.env${SEPARATOR}**`);
585+
suggestions.push(`**${SEPARATOR}*.egg-info${SEPARATOR}**`);
586+
suggestions.push(`**${SEPARATOR}.tox${SEPARATOR}**`);
587+
suggestions.push(`**${SEPARATOR}.coverage`);
588+
suggestions.push(`**${SEPARATOR}htmlcov${SEPARATOR}**`);
589+
suggestions.push(`**${SEPARATOR}.mypy_cache${SEPARATOR}**`);
566590
} else if (language === csharpLanguage) {
567-
suggestions.push("**/bin/**");
568-
suggestions.push("**/obj/**");
569-
suggestions.push("**/packages/**");
570-
suggestions.push("**/.vs/**");
571-
suggestions.push("**/TestResults/**");
572-
suggestions.push("**/*.user");
573-
suggestions.push("**/*.suo");
574-
suggestions.push("**/.nuget/**");
575-
suggestions.push("**/artifacts/**");
576-
suggestions.push("**/packages/**");
591+
suggestions.push(`**${SEPARATOR}bin${SEPARATOR}**`);
592+
suggestions.push(`**${SEPARATOR}obj${SEPARATOR}**`);
593+
suggestions.push(`**${SEPARATOR}packages${SEPARATOR}**`);
594+
suggestions.push(`**${SEPARATOR}.vs${SEPARATOR}**`);
595+
suggestions.push(`**${SEPARATOR}TestResults${SEPARATOR}**`);
596+
suggestions.push(`**${SEPARATOR}*.user`);
597+
suggestions.push(`**${SEPARATOR}*.suo`);
598+
suggestions.push(`**${SEPARATOR}.nuget${SEPARATOR}**`);
599+
suggestions.push(`**${SEPARATOR}artifacts${SEPARATOR}**`);
600+
suggestions.push(`**${SEPARATOR}packages${SEPARATOR}**`);
577601
}
578602

579603
return suggestions;

src/cli/handlers/manifest/generate.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ async function handler(
259259
Deno.exit(1);
260260
}
261261

262+
const responseBody = await response.json() as { id: number };
263+
262264
const duration = Date.now() - start;
263265
console.info(
264266
`✅ Manifest uploaded successfully for project id: ${projectId} in ${duration}ms`,
@@ -269,7 +271,7 @@ async function handler(
269271

270272
if (globalConfig.apiHost === defaultApiHost) {
271273
console.info(
272-
`\nView it here: https://app.nanoapi.io/projects/${projectId}/manifests`,
274+
`\nView it here: https://app.nanoapi.io/projects/${projectId}/manifests/${responseBody.id}`,
273275
);
274276
}
275277
} catch (error) {

0 commit comments

Comments
 (0)