Skip to content

Commit a6a2ff8

Browse files
committed
Complete Phase 4 polish and Phase 5 release packaging
1 parent 7584106 commit a6a2ff8

10 files changed

Lines changed: 544 additions & 75 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
build-and-release:
13+
name: Build & Publish Release
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout Repository
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Setup Go
23+
uses: actions/setup-go@v5
24+
with:
25+
go-version: '1.22'
26+
cache: true
27+
28+
- name: Run Unit Tests
29+
run: go test -v ./...
30+
31+
- name: Build Windows Executables
32+
run: |
33+
mkdir -p dist
34+
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o dist/phpvm-windows-amd64.exe .
35+
GOOS=windows GOARCH=arm64 go build -ldflags="-s -w" -o dist/phpvm-windows-arm64.exe .
36+
cp dist/phpvm-windows-amd64.exe dist/phpvm.exe
37+
38+
- name: Create Release
39+
uses: softprops/action-gh-release@v2
40+
with:
41+
files: |
42+
dist/phpvm.exe
43+
dist/phpvm-windows-amd64.exe
44+
dist/phpvm-windows-arm64.exe
45+
generate_release_notes: true
46+
env:
47+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

‎README.md‎

Lines changed: 65 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
> "Git for local PHP runtimes."
44
5-
phpvm is an open-source, Windows-first runtime orchestration tool that gives every PHP project its own isolated environment — the right PHP version, extensions, and services — without touching global machine state.
5+
`phpvm` is an open-source, Windows-first runtime orchestration tool that gives every PHP project its own isolated environment — the exact PHP version, extensions, web server (Nginx or PHP built-in), and database (MySQL) — without touching global machine state.
66

77
---
88

99
## The Problem It Solves
1010

11-
XAMPP assumes one machine = one PHP environment.
12-
Modern developers work across multiple projects with conflicting requirements.
11+
XAMPP assumes one machine = one PHP environment. Modern developers work across multiple projects with conflicting requirements:
1312

1413
| Project | Needs |
1514
|---|---|
@@ -18,108 +17,99 @@ Modern developers work across multiple projects with conflicting requirements.
1817
| Legacy WordPress | PHP 7.4 |
1918
| Experimental | PHP 8.4 |
2019

21-
Switching manually means: broken configs, DLL conflicts, Apache crashes, duplicated installs, and wasted hours.
22-
23-
phpvm eliminates this entirely.
20+
Switching manually means broken configs, DLL conflicts, web server crashes, duplicated installs, and wasted hours. `phpvm` eliminates this entirely.
2421

2522
---
2623

27-
## How It Works (30-second version)
24+
## Quick Start (30 Seconds)
25+
26+
### 1. Install `phpvm`
27+
```powershell
28+
irm https://raw.githubusercontent.com/Vikrez22/phpvm/main/install.ps1 | iex
29+
```
30+
31+
### 2. Initialize your project
32+
```powershell
33+
cd C:\my-project
34+
phpvm init
35+
```
36+
37+
### 3. Install required runtimes & start environment
38+
```powershell
39+
phpvm install
40+
phpvm up
41+
```
2842

29-
1. Drop a `runtime.yml` in your project root
30-
2. Run `phpvm up`
31-
3. The correct PHP version, extensions, and services activate — **scoped only to that project** — without mutating your machine globally
43+
The correct PHP version, enabled extensions, Nginx web server, and MySQL database activate — **scoped only to that project** — without mutating your global system PATH.
3244

3345
---
3446

35-
## Core Concepts
47+
## Command Reference
3648

37-
| Concept | What it means |
49+
| Command | Description |
3850
|---|---|
39-
| **Runtime Pack** | An immutable, isolated folder containing a specific PHP version + its extensions |
40-
| **Runtime Manifest** | A `runtime.yml` file in the project root declaring what the project needs |
41-
| **Environment Injection** | Temporarily scoping the correct PATH to the active terminal session only |
42-
| **Process Supervisor** | A lightweight daemon managing per-project Apache/Nginx, MySQL, queues |
51+
| `phpvm init` | Generate a `runtime.yml` manifest in the current directory |
52+
| `phpvm install [pack]` | Install a runtime pack (`php@8.3`, `nginx@1.30`, `mysql@8`) or all manifest requirements |
53+
| `phpvm up` | Start project web/database services & inject environment into shell |
54+
| `phpvm status` | Show PIDs, allocated ports, database connection details, and health |
55+
| `phpvm logs [service]` | View live log output for `web`, `mysql`, `nginx`, or `php-cgi` |
56+
| `phpvm down [--all]` | Stop running project services (or all projects using `--all`) |
57+
| `phpvm run <cmd>` | Execute a command using the project's isolated PHP runtime |
58+
| `phpvm shell` | Launch an interactive PowerShell session scoped to the project PHP |
59+
| `phpvm use <version>` | Switch local terminal session to a specific installed PHP version |
60+
| `phpvm list [--available]` | List installed (or available upstream) PHP and service packs |
61+
| `phpvm uninstall <pack>` | Remove an installed runtime or service pack |
62+
| `phpvm snapshot <name>` | Backup project runtime state to a `.tar.gz` archive |
63+
| `phpvm restore <name>` | Restore project runtime state from a backup archive |
64+
| `phpvm doctor` | Diagnose environment health, VC++ redistributables, & PATH conflicts |
65+
| `phpvm update` | Self-update `phpvm` binary and download fresh upstream registry metadata |
4366

4467
---
4568

46-
## Agent Navigation Guide
47-
48-
If you are an AI agent working on this codebase, read files in this order:
69+
## Core Concepts
4970

50-
```
51-
1. README.md ← you are here
52-
2. docs/architecture/OVERVIEW.md ← system architecture + data flow
53-
3. docs/architecture/RUNTIME_PACKS.md ← immutable runtime pack design
54-
4. docs/architecture/ENV_INJECTION.md ← how PATH scoping works on Windows
55-
5. docs/specs/MANIFEST_SPEC.md ← runtime.yml format + validation rules
56-
6. docs/specs/CLI_SPEC.md ← all CLI commands, flags, behavior
57-
7. docs/agent-guides/PHASE_1.md ← what to build first
58-
8. docs/agent-guides/PHASE_2.md ← core engine implementation
59-
9. docs/agent-guides/PHASE_3.md ← CLI implementation
60-
10. docs/agent-guides/PHASE_4.md ← process supervisor
61-
```
71+
| Concept | What it means |
72+
|---|---|
73+
| **Runtime Pack** | An immutable, isolated folder containing a specific PHP version + extensions (`%APPDATA%\phpvm\runtimes`) |
74+
| **Runtime Manifest** | A `runtime.yml` file in the project root declaring project dependencies |
75+
| **Environment Injection** | Temporarily scoping `PATH` and `PHP_INI_SCAN_DIR` to the active shell session only |
76+
| **Process Supervisor** | Lightweight manager supervising per-project Nginx, PHP-CGI, and MySQL processes |
6277

6378
---
6479

65-
## Repository Structure (Target)
80+
## Repository Structure
6681

6782
```
6883
phpvm/
69-
├── README.md
70-
├── runtime.yml.example
71-
├── go.mod
72-
├── go.sum
73-
├── main.go
74-
├── cmd/ ← CLI command handlers (Cobra)
75-
│ ├── root.go
76-
│ ├── up.go
77-
│ ├── use.go
78-
│ ├── install.go
79-
│ ├── init.go
80-
│ ├── snapshot.go
81-
│ └── status.go
84+
├── cmd/ ← Cobra CLI command handlers (up, down, status, logs, etc.)
8285
├── internal/
8386
│ ├── manifest/ ← runtime.yml parsing + validation
84-
│ ├── resolver/ ← matches manifest → installed runtime pack
85-
│ ├── injector/ ← scoped PATH injection (Windows)
86-
│ ├── downloader/ ← fetches PHP builds from registry
87-
│ ├── packs/ ← runtime pack management
88-
│ ├── supervisor/ ← process lifecycle (Apache, MySQL, etc.)
89-
│ └── proxy/ ← reverse proxy for .local domains
90-
├── registry/ ← known PHP versions + download URLs
91-
├── docs/
92-
│ ├── architecture/
93-
│ ├── specs/
94-
│ └── agent-guides/
95-
└── testdata/
87+
│ ├── resolver/ ← Matches manifest → installed runtime packs
88+
│ ├── injector/ ← Scoped PATH injection for Windows sessions
89+
│ ├── downloader/ ← Fetches PHP/Nginx/MySQL builds with SHA256/MD5 validation
90+
│ ├── packs/ ← Runtime & service pack management
91+
│ ├── supervisor/ ← Process supervision (Nginx, PHP-CGI, MySQL)
92+
│ ├── state/ ← Per-project state (.phpvm/state.json, port allocation)
93+
│ ├── ini/ ← Scoped php.ini generation
94+
│ └── paths/ ← Standardized directory paths (%APPDATA%\phpvm)
95+
├── registry/ ← Downstream registry index & URLs
96+
├── install.ps1 ← One-line PowerShell installation script
97+
└── .github/workflows/ ← CI/CD automated release pipeline
9698
```
9799

98100
---
99101

100-
## Tech Stack
101-
102-
| Layer | Choice | Reason |
103-
|---|---|---|
104-
| Core language | Go | Single binary, fast CLI, excellent process management, Windows support |
105-
| CLI framework | Cobra + Viper | Industry standard for Go CLIs |
106-
| Process management | os/exec + custom supervisor | Lightweight, no daemon overhead |
107-
| Config format | YAML | Familiar to PHP devs (docker-compose, Laravel) |
108-
| Distribution | Single `.exe` + install script | Zero-friction for Windows devs |
109-
110-
---
111-
112102
## Guiding Principles
113103

114-
1. **No global mutation** — phpvm never permanently changes your system PATH or overwrites global configs
115-
2. **Immutable runtime packs** — installed PHP versions are never modified after install
104+
1. **No global mutation** — `phpvm` never permanently changes your system PATH or overwrites global configs
105+
2. **Immutable runtime packs** — installed PHP versions are never modified after installation
116106
3. **Project-scoped** — every setting, every service, every port is tied to a project
117-
4. **Explicit over magic** — runtime.yml is always the source of truth
118-
5. **Fast** — `phpvm up` should take under 2 seconds on a warm cache
119-
6. **Windows-first** — solve the hardest platform properly; don't paper over it
107+
4. **Explicit over magic** — `runtime.yml` is always the source of truth
108+
5. **Fast** — `phpvm up` starts services under 2 seconds on a warm cache
109+
6. **Windows-first** — built natively for Windows environment isolation
120110

121111
---
122112

123113
## Status
124114

125-
🔴 Pre-development — architecture phase
115+
🟢 **Phase 5 — Packaging & Release Distribution** (Core engine, CLI surface, Nginx/PHP-CGI/MySQL supervision, and health diagnostics complete).

‎cmd/down.go‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"fmt"
55
"os"
6+
"path/filepath"
67

78
"github.com/Vikrez22/phpvm/internal/manifest"
89
"github.com/Vikrez22/phpvm/internal/resolver"
@@ -11,10 +12,15 @@ import (
1112
"github.com/spf13/cobra"
1213
)
1314

15+
var downAll bool
16+
1417
var downCmd = &cobra.Command{
1518
Use: "down",
1619
Short: "Stop all services for the current project",
1720
RunE: func(cmd *cobra.Command, args []string) error {
21+
if downAll {
22+
return runDownAll()
23+
}
1824
cwd, err := os.Getwd()
1925
if err != nil {
2026
return err
@@ -49,5 +55,58 @@ var downCmd = &cobra.Command{
4955
}
5056

5157
func init() {
58+
downCmd.Flags().BoolVar(&downAll, "all", false, "Stop all phpvm environments under the current directory")
5259
rootCmd.AddCommand(downCmd)
5360
}
61+
62+
func runDownAll() error {
63+
cwd, err := os.Getwd()
64+
if err != nil {
65+
return err
66+
}
67+
var stateFiles []string
68+
if err := filepath.WalkDir(cwd, func(path string, entry os.DirEntry, err error) error {
69+
if err != nil {
70+
return nil
71+
}
72+
if entry.IsDir() && entry.Name() == ".git" {
73+
return filepath.SkipDir
74+
}
75+
if !entry.IsDir() && filepath.Base(path) == "state.json" && filepath.Base(filepath.Dir(path)) == ".phpvm" {
76+
stateFiles = append(stateFiles, path)
77+
}
78+
return nil
79+
}); err != nil {
80+
return err
81+
}
82+
if len(stateFiles) == 0 {
83+
fmt.Println("No phpvm project states found.")
84+
return nil
85+
}
86+
for _, stateFile := range stateFiles {
87+
projectRoot := filepath.Dir(filepath.Dir(stateFile))
88+
ps, err := state.Load(projectRoot)
89+
if err != nil {
90+
fmt.Printf("%s: %v\n", projectRoot, err)
91+
continue
92+
}
93+
sup := supervisor.New(&manifest.Manifest{ProjectRoot: projectRoot}, nil, ps)
94+
statuses, err := sup.Down()
95+
if err != nil {
96+
fmt.Printf("%s: %v\n", projectRoot, err)
97+
continue
98+
}
99+
if len(statuses) == 0 {
100+
continue
101+
}
102+
fmt.Printf("%s\n", projectRoot)
103+
for _, status := range statuses {
104+
if status.Error != "" {
105+
fmt.Printf(" %s: %s\n", status.Name, status.Error)
106+
} else {
107+
fmt.Printf(" %s stopped\n", status.Name)
108+
}
109+
}
110+
}
111+
return nil
112+
}

0 commit comments

Comments
 (0)