Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c388fff
chore(go): add Go module scaffolding
rinatkhaziev Aug 20, 2026
cd591c0
build(go): add Makefile and Windows build script
rinatkhaziev Aug 20, 2026
b549d7e
feat(go): output, TUI and exit-code primitives
rinatkhaziev Aug 20, 2026
b7f848e
feat(go): GraphQL schema and operations
rinatkhaziev Aug 20, 2026
426a1de
feat(go): generated GraphQL bindings
rinatkhaziev Aug 20, 2026
42b8c4b
feat(go): GraphQL client, transport and retry
rinatkhaziev Aug 20, 2026
4566ffa
feat(go): auth, keychain and step-up rechallenge
rinatkhaziev Aug 20, 2026
650460d
feat(go): app and environment context resolution
rinatkhaziev Aug 20, 2026
e730eed
feat(go): read-only platform APIs
rinatkhaziev Aug 20, 2026
a22e44c
feat(go): SQL export, validation and site import
rinatkhaziev Aug 20, 2026
f9934a5
feat(go): media import, upload and file validation
rinatkhaziev Aug 20, 2026
d7aa5ae
feat(go): backup, custom deploy and environment sync
rinatkhaziev Aug 20, 2026
d041077
feat(go): wp-cli shell, SSH and stream transport
rinatkhaziev Aug 20, 2026
3319bd7
feat(devenv): container model and Docker integration
rinatkhaziev Aug 20, 2026
da6f394
feat(devenv): lifecycle, proxy and host operations
rinatkhaziev Aug 20, 2026
8ce571e
feat(devenv): environment model and lifecycle operations
rinatkhaziev Aug 20, 2026
fed4271
feat(devenv): data import and SQL sync
rinatkhaziev Aug 20, 2026
7ac3f5c
feat(cli): root command, login/logout and global flags
rinatkhaziev Aug 20, 2026
ecabcd7
feat(cli): app, whoami, logs and slowlogs commands
rinatkhaziev Aug 20, 2026
7593b75
feat(cli): config, defensive-mode, cache and db commands
rinatkhaziev Aug 20, 2026
ad4ae8a
feat(cli): import commands
rinatkhaziev Aug 20, 2026
3514cd6
feat(cli): export, backup, sync and wp commands
rinatkhaziev Aug 20, 2026
0538292
feat(cli): dev-env command surface
rinatkhaziev Aug 20, 2026
9e5119d
test(go): Node-vs-Go parity harness
rinatkhaziev Aug 20, 2026
8665a36
test(go): parity recordings and fixtures
rinatkhaziev Aug 20, 2026
3600b2e
ci: add Go build, test and parity workflow
rinatkhaziev Aug 20, 2026
ac401cc
ci: add Buildkite signing and notarization pipeline
rinatkhaziev Aug 20, 2026
f25bf97
docs: document the Go CLI build, release and cutover
rinatkhaziev Aug 20, 2026
eee9db0
ci: keep prettier and eslint out of the Go tree
rinatkhaziev Aug 20, 2026
6033a08
test(parity): normalize the keychain fallback notice out of stderr
rinatkhaziev Aug 21, 2026
d9b9bde
test(parity): report what diverged on a drift-signature mismatch
rinatkhaziev Aug 21, 2026
b49c267
test(parity): make the enquirer prompt scenarios platform-stable
rinatkhaziev Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
43 changes: 43 additions & 0 deletions .buildkite/build-linux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -euo pipefail
# Build the Linux vip-next binaries + checksums on a Buildkite Linux agent.
# Linux has no OS-enforced executable signature; we publish checksums (a detached
# GPG/cosign signature is optional — see the bottom of this file).

[ -f .buildkite/shared-pipeline-vars ] && . .buildkite/shared-pipeline-vars
: "${BIN_BASE:=vip-next}"

command -v go >/dev/null 2>&1 || { echo "go not found; agent must provide Go ${GO_VERSION:-1.27}+" >&2; exit 1; }
go version

VERSION="$(git describe --tags --always --dirty 2>/dev/null || echo dev)"
COMMIT="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"

build() {
local goarch="$1" out="dist/${BIN_BASE}-linux-${goarch}"
echo "--- :go: build linux/${goarch}"
CGO_ENABLED=0 GOOS=linux GOARCH="${goarch}" \
go build -buildvcs=false -trimpath \
-ldflags="-s -w -X github.com/Automattic/vip/internal/version.Version=${VERSION} -X github.com/Automattic/vip/internal/version.Commit=${COMMIT}" \
-o "${out}" ./cmd/vip-next
shasum -a 256 "${out}" > "${out}.sha256"
}

mkdir -p dist
build amd64
build arm64

# Smoke-test only the arch matching this agent (a cross-built slice won't run here).
case "$(uname -m)" in
x86_64|amd64) native=amd64 ;;
aarch64|arm64) native=arm64 ;;
*) native="" ;;
esac
if [ -n "${native}" ]; then
echo "--- :test_tube: smoke linux/${native}"
"dist/${BIN_BASE}-linux-${native}" --version
"dist/${BIN_BASE}-linux-${native}" whoami --help
fi

# Optional detached signature (needs an infra-owned key); checksums-only by default.
# gpg --armor --detach-sign "dist/${BIN_BASE}-linux-amd64" # ← infra: enable if desired
97 changes: 97 additions & 0 deletions .buildkite/build-macos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
set -euo pipefail
# Build, and on tag builds sign + notarize, the macOS vip-next artifacts, on a
# Buildkite macOS agent (queue: mac):
# - two bare per-arch binaries: codesigned + notarized (online-verified; a bare
# Mach-O can't be stapled)
# - one universal .pkg installer: codesigned + productsigned + notarized + STAPLED
# (offline-verified)
# Checksums are written AFTER signing (signing changes the bytes).

[ -f .buildkite/shared-pipeline-vars ] && . .buildkite/shared-pipeline-vars
: "${BIN_BASE:=vip-next}"

echo "--- :ruby: install gems"
if command -v install_gems >/dev/null 2>&1; then install_gems; else bundle install; fi

echo "--- :go: toolchain"
command -v go >/dev/null 2>&1 || { echo "go not found; agent must provide Go ${GO_VERSION:-1.27}+" >&2; exit 1; }
go version

VERSION="$(git describe --tags --always --dirty 2>/dev/null || echo dev)"
COMMIT="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"

build() {
local goarch="$1" out="dist/${BIN_BASE}-darwin-${goarch}"
echo "--- :go: build darwin/${goarch}"
CGO_ENABLED=0 GOOS=darwin GOARCH="${goarch}" \
go build -buildvcs=false -trimpath \
-ldflags="-s -w -X github.com/Automattic/vip/internal/version.Version=${VERSION} -X github.com/Automattic/vip/internal/version.Commit=${COMMIT}" \
-o "${out}" ./cmd/vip-next
}

checksum() { shasum -a 256 "$1" > "$1.sha256"; }

mkdir -p dist
build arm64
build amd64

# Smoke-test the native arch (a cross-built slice may not run without Rosetta).
case "$(uname -m)" in
arm64) native=arm64 ;;
x86_64) native=amd64 ;;
*) native="" ;;
esac
if [ -n "${native}" ]; then
echo "--- :test_tube: smoke darwin/${native}"
"dist/${BIN_BASE}-darwin-${native}" --version
"dist/${BIN_BASE}-darwin-${native}" whoami --help
fi

if [ -z "${BUILDKITE_TAG:-}" ]; then
echo "--- not a tag build; skipping sign/notarize (unsigned checksums only)"
checksum "dist/${BIN_BASE}-darwin-arm64"
checksum "dist/${BIN_BASE}-darwin-amd64"
exit 0
fi

echo "--- :closed_lock_with_key: fetch signing certs (fastlane match)"
bundle exec fastlane configure_code_signing

# Build the universal binary from the UNSIGNED arches, then sign all three once.
uni="dist/${BIN_BASE}-darwin-universal"
lipo -create -output "${uni}" "dist/${BIN_BASE}-darwin-arm64" "dist/${BIN_BASE}-darwin-amd64"

for bin in "dist/${BIN_BASE}-darwin-arm64" "dist/${BIN_BASE}-darwin-amd64" "${uni}"; do
echo "--- :closed_lock_with_key: codesign ${bin}"
codesign --remove-signature "${bin}" 2>/dev/null || true # drop Go's ad-hoc sig
codesign --sign "${MACOS_SIGN_IDENTITY}" --options runtime --timestamp --force "${bin}"
codesign --verify --strict --verbose=2 "${bin}"
done

# Bare binaries: notarize (no staple — nothing to hold the ticket), then checksum.
for arch in arm64 amd64; do
bin="dist/${BIN_BASE}-darwin-${arch}"
echo "--- :cloud: notarize ${arch} (no staple)"
ditto -c -k --keepParent "${bin}" "${bin}.zip"
bundle exec fastlane notarize_artifact path:"${bin}.zip"
rm -f "${bin}.zip"
checksum "${bin}"
done

# Universal .pkg: package the signed universal binary → sign the pkg → notarize + staple.
echo "--- :package: build + sign universal .pkg"
pkgroot="$(mktemp -d)"
cp "${uni}" "${pkgroot}/${BIN_BASE}"
pkg="dist/${BIN_BASE}-darwin-universal.pkg"
pkgbuild --root "${pkgroot}" --identifier com.automattic.vip-cli --version "${VERSION}" \
--install-location /usr/local/bin "${pkg}.unsigned"
productsign --sign "${MACOS_INSTALLER_IDENTITY}" "${pkg}.unsigned" "${pkg}"
rm -f "${pkg}.unsigned"
rm -rf "${pkgroot}"

echo "--- :cloud: notarize + staple .pkg"
bundle exec fastlane notarize_artifact path:"${pkg}" skip_stapling:false
xcrun stapler validate "${pkg}"
checksum "${uni}"
checksum "${pkg}"
53 changes: 53 additions & 0 deletions .buildkite/build-windows.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#Requires -Version 5.1
# Build vip-next.exe and (on tag builds) Authenticode-sign it, on a Buildkite
# Windows agent. Checksum is computed AFTER signing (signing changes the bytes).
$ErrorActionPreference = 'Stop'

$binBase = if ($env:BIN_BASE) { $env:BIN_BASE } else { 'vip-next' }

function Get-GitOr($cmd, $fallback) {
try { $v = & git @cmd 2>$null; if ($LASTEXITCODE -eq 0 -and $v) { return $v.Trim() } } catch {}
return $fallback
}
$version = Get-GitOr @('describe','--tags','--always','--dirty') 'dev'
$commit = Get-GitOr @('rev-parse','--short','HEAD') 'unknown'

New-Item -ItemType Directory -Force -Path dist | Out-Null
$out = "dist/$binBase-windows-amd64.exe"

Write-Host "--- :go: build windows/amd64"
$env:CGO_ENABLED = '0'; $env:GOOS = 'windows'; $env:GOARCH = 'amd64'
$ldflags = "-s -w -X github.com/Automattic/vip/internal/version.Version=$version -X github.com/Automattic/vip/internal/version.Commit=$commit"
go build -buildvcs=false -trimpath -ldflags="$ldflags" -o $out ./cmd/vip-next
if ($LASTEXITCODE -ne 0) { throw 'go build failed' }

Write-Host "--- :test_tube: smoke"
& $out --version
& $out whoami --help

if ($env:BUILDKITE_TAG) {
Write-Host "--- :closed_lock_with_key: Authenticode sign"
# ← infra: confirm the Windows cert mechanism. Draft = PFX-from-base64-secret,
# mirroring the current GitHub Actions workflow. EV certs can NOT use a plain
# PFX (FIPS-hardware since June 2023) — if you use Azure Trusted Signing, swap
# the two signtool lines for `signtool sign /fd SHA256 /tr <url> /td SHA256 /dlib <dll> /dmdf <metadata> $out`.
$pfxB64 = $env:WINDOWS_CERTIFICATE_PFX_BASE64
$pfxPw = $env:WINDOWS_CERTIFICATE_PASSWORD
$ts = if ($env:WINDOWS_TIMESTAMP_URL) { $env:WINDOWS_TIMESTAMP_URL } else { 'http://timestamp.digicert.com' }
if (-not $pfxB64 -or -not $pfxPw) { throw 'tag build but WINDOWS_CERTIFICATE_PFX_BASE64 / _PASSWORD not set' }

$pfx = Join-Path $env:TEMP 'vip-codesign.pfx'
[IO.File]::WriteAllBytes($pfx, [Convert]::FromBase64String($pfxB64))
try {
signtool sign /fd SHA256 /td SHA256 /tr $ts /f $pfx /p $pfxPw $out
if ($LASTEXITCODE -ne 0) { throw 'signtool sign failed' }
signtool verify /pa /v $out
if ($LASTEXITCODE -ne 0) { throw 'signtool verify failed' }
} finally {
Remove-Item $pfx -Force -ErrorAction SilentlyContinue
}
}

Write-Host "--- checksum"
$hash = (Get-FileHash -Algorithm SHA256 $out).Hash.ToLower()
"$hash *$(Split-Path $out -Leaf)" | Set-Content "$out.sha256" -NoNewline
44 changes: 44 additions & 0 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/buildkite/pipeline-schema/main/schema.json
---
# Shared vars (CI_TOOLKIT_PLUGIN, GO_VERSION, BIN_BASE, signing identities) come
# from .buildkite/shared-pipeline-vars, which our setup source's before
# `buildkite-agent pipeline upload` interpolates this file.
#
# Every commit builds + smoke-tests all three platforms. Signing + notarization
# run only on TAG builds — gated on $BUILDKITE_TAG inside each build script.

env:
DO_NOT_TRACK: '1'

steps:
- label: ':macos: Build & sign (macOS)'
command: .buildkite/build-macos.sh
plugins:
- $CI_TOOLKIT_PLUGIN
agents:
queue: mac
notify:
- github_commit_status:
context: 'Build & sign (macOS)'
artifact_paths:
- 'dist/vip-next-darwin-*'

- label: ':windows: Build & sign (Windows)'
command: powershell -NoProfile -ExecutionPolicy Bypass -File .buildkite/build-windows.ps1
agents:
queue: windows # ← infra: confirm Windows agent queue name
notify:
- github_commit_status:
context: 'Build & sign (Windows)'
artifact_paths:
- 'dist/vip-next-windows-amd64.exe*'

- label: ':linux: Build (Linux)'
command: .buildkite/build-linux.sh
agents:
queue: default # ← infra: confirm Linux agent queue name
notify:
- github_commit_status:
context: 'Build (Linux)'
artifact_paths:
- 'dist/vip-next-linux-*'
21 changes: 21 additions & 0 deletions .buildkite/shared-pipeline-vars
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/sh
# This file is `source`'d before `buildkite-agent pipeline upload`, so the
# variables below get interpolated into pipeline.yml before it is uploaded.
# (Same mechanism as Automattic/download's .buildkite/shared-pipeline-vars.)

# a8c CI toolkit Buildkite plugin — provides install_gems, secret injection, and
# GitHub status helpers on the macOS agent.
export CI_TOOLKIT_PLUGIN="automattic/a8c-ci-toolkit#5.3.1" # ← infra: confirm current version

# Go toolchain the build scripts require. encoding/json/v2 is standard in Go 1.27.
export GO_VERSION="1.27" # ← infra: match your agent provisioning

# Binary base name + macOS signing identities (team PZYM8XX95Q = Automattic, Inc.).
export BIN_BASE="vip-next"
export MACOS_TEAM_ID="PZYM8XX95Q"
export MACOS_SIGN_IDENTITY="Developer ID Application: Automattic, Inc. (PZYM8XX95Q)"
export MACOS_INSTALLER_IDENTITY="Developer ID Installer: Automattic, Inc. (PZYM8XX95Q)" # ← infra: confirm this cert exists

# NOTE: unlike the reference we set no Xcode IMAGE_ID / .xcode-version — this is a
# plain Go build, no fyne/Xcode. If your mac queue requires a specific VM image,
# add: export IMAGE_ID="<image>" # ← infra
113 changes: 113 additions & 0 deletions .github/workflows/ci-go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: ci-go

on:
push:
branches: [trunk, feature/go-rewrite]
paths:
- '**/*.go'
- 'go.mod'
- 'go.sum'
- 'Makefile'
- '.github/workflows/ci-go.yml'
- 'testdata/parity/**'
- 'testdata/parity-local/**'
- 'internal/gql/schema.gql'
- 'internal/gql/operations/**.graphql'
- 'internal/gql/generated.go'
# The Node CLI is a build input now: this job diffs vip-next against it.
# A change to Node's behaviour, or to how dist/ is produced, can break
# parity, so it has to retrigger this workflow.
- 'src/**'
- 'package.json'
- 'package-lock.json'
- 'babel.config.js'
- '.nvmrc'
pull_request:
paths:
- '**/*.go'
- 'go.mod'
- 'go.sum'
- 'Makefile'
- '.github/workflows/ci-go.yml'
- 'testdata/parity/**'
- 'testdata/parity-local/**'
- 'internal/gql/schema.gql'
- 'internal/gql/operations/**.graphql'
- 'internal/gql/generated.go'
# See above.
- 'src/**'
- 'package.json'
- 'package-lock.json'
- 'babel.config.js'
- '.nvmrc'

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.27'
check-latest: true
cache: true

# The Node CLI is the reference implementation the differential parity
# scenarios diff vip-next against. Without it, every Node-vs-Go scenario
# skips and this job goes green having compared nothing — which is
# exactly how ~90 parity divergences reached a review unnoticed.
#
# Version comes from .nvmrc (lts/*), the same source ci.yml pins by hand.
# It must satisfy package.json#engines (>=22.19.0 on trunk 4.1.0) or the
# `postinstall` guard (helpers/check-version.js) aborts the install with
# exit 1.
- name: Set up Node.js environment
uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: npm
cache-dependency-path: package-lock.json

# `npm ci` runs the `prepare` lifecycle script (clean + babel build),
# which is what produces dist/bin/vip.js. No separate build step needed.
- name: Install Node dependencies and build the Node CLI
run: npm ci

# Fail loudly if the above did not actually yield a runnable Node CLI.
# `make test-parity-unit` only WARNS in that case, on purpose, so that a
# contributor without node_modules is not hard-failed. CI has no such
# excuse: here a skipped differential is a broken build.
- name: Assert the Node-vs-Go differential can actually run
run: make require-node-vip-bin

- name: go mod download
run: go mod download

- name: Verify generated GraphQL code is fresh
run: make verify-gql-stale

# Via make, not bare `go vet ./...` / `go test ./...`: now that `npm ci`
# has run, node_modules is inside the module and a bare `./...` would
# compile and vet an npm dependency's vendored Go package
# (node_modules/flatted/golang/pkg/flatted). The make targets discover
# the package list and drop node_modules from it.
- name: go vet
run: make lint

- name: go test
run: make test

# This is the step the Node CLI was installed for. On a Linux runner the
# credential the Node CLI reads comes from configstore rather than a
# system keyring (see internal/parity/keychain.go); the harness seeds
# through Node's own getKeychain(), so it lands in whichever store Node
# itself would read, and a store that cannot be driven at all produces a
# loud skip rather than a hang or a false pass.
- name: Offline compatibility and parity-harness tests
run: make test-parity-unit

- name: make build
run: make build

- name: smoke
run: ./bin/vip-next --version
Comment on lines +46 to +113
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,19 @@ coverage
*.iml

schema.gql

# Keep the checked-in GraphQL schema despite the blanket schema.gql rule above
!internal/gql/schema.gql

# Go build artifacts
/bin/
*.exe
coverage.out
go.work
go.work.sum

# Vendored go-search-replace binaries: fetched + checksum-verified by
# `make vendor-search-replace` from the pinned release in
# third_party/go-search-replace/MANIFEST (which IS tracked). Binaries stay out
# of git so the repo does not carry ~19 MB of executables per upgrade.
third_party/go-search-replace/*/
Loading
Loading