diff --git a/.changeset/git-doctor-diagnostic.md b/.changeset/git-doctor-diagnostic.md deleted file mode 100644 index 630b826..0000000 --- a/.changeset/git-doctor-diagnostic.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@stellarforge/cli": minor ---- - -Add Git availability and version detection to `stellarforge doctor`, including safe failure messages and remediation guidance. diff --git a/.changeset/protected-release-automation.md b/.changeset/protected-release-automation.md deleted file mode 100644 index b81acf1..0000000 --- a/.changeset/protected-release-automation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@stellarforge/cli": minor ---- - -Establish the protected release pipeline for the first public StellarForge CLI package, including Changesets release PRs, deterministic package dry-runs, npm Trusted Publishing/OIDC gating, provenance-ready publication, and matching GitHub tags/releases. diff --git a/.changeset/runtime-doctor-diagnostics.md b/.changeset/runtime-doctor-diagnostics.md deleted file mode 100644 index 4f57062..0000000 --- a/.changeset/runtime-doctor-diagnostics.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@stellarforge/cli": minor ---- - -Add Node.js and npm environment checks to `stellarforge doctor` with supported-version validation and actionable remediation guidance. diff --git a/.changeset/rust-cargo-doctor-diagnostics.md b/.changeset/rust-cargo-doctor-diagnostics.md deleted file mode 100644 index 7e60cff..0000000 --- a/.changeset/rust-cargo-doctor-diagnostics.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@stellarforge/cli": minor ---- - -Add Rust and Cargo environment checks to `stellarforge doctor`, including the Stellar smart-contract Rust 1.84.0 minimum and remediation guidance. diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e472e..8e4778b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,30 @@ All notable changes to StellarForge CLI will be documented in this file. The project follows [Semantic Versioning](https://semver.org/) and uses Changesets to capture release intent and generate version/changelog updates. -## [Unreleased] +## 0.1.0 — 2026-09-11 ### Added -- Initial public repository foundation. -- Product and architecture planning for the CLI MVP. -- Contributor, security, and release-management foundations. +- Initial public StellarForge CLI MVP foundation and command architecture. +- `stellarforge new` with controlled Basic App, Full Stack, Smart Contract, and API Service templates. +- `stellarforge doctor` diagnostics for Node.js, npm, Git, Rust, Cargo, Stellar CLI, and Docker. +- `stellarforge dev` safe local process orchestration. +- `stellarforge test` unified Node/Cargo test execution. +- `stellarforge deploy` guarded Stellar Testnet smart-contract deployment. +- Versioned `stellarforge.config.json` project configuration with secret-safe validation. +- Cross-platform Linux, macOS, and Windows validation. +- Deterministic linked-CLI end-to-end smoke workflow. +- Changesets-based release management and protected npm/GitHub release automation. +- CodeQL, dependency review configuration, Dependabot, CODEOWNERS, OpenSSF Scorecard, threat modeling, and secure contributor workflows. + +### Security + +- Uses argument-array subprocess execution with `shell: false`. +- Rejects path traversal, unsafe overwrite targets, raw Stellar StrKeys, secret-like configuration fields, and unsupported deployment networks. +- Keeps signing material in Stellar CLI identity management rather than project configuration. +- Bounds diagnostic subprocess execution time. +- Publishes through npm Trusted Publishing/OIDC once registry bootstrap is completed. + +## [Unreleased] -> Release entries below this section will be generated and refined as versions are published. Documentation-only and internal maintenance changes may not require a changeset unless they materially affect users or release communication. +No unreleased user-facing changes yet. diff --git a/package-lock.json b/package-lock.json index 7dc46f1..8e6f3cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@stellarforge/cli", - "version": "0.0.0", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@stellarforge/cli", - "version": "0.0.0", + "version": "0.1.0", "dependencies": { "commander": "15.0.0" }, diff --git a/package.json b/package.json index 995eccb..6497498 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@stellarforge/cli", - "version": "0.0.0", + "version": "0.1.0", "private": false, "description": "Command-line interface for building production-ready Stellar applications.", "type": "module", diff --git a/tests/executable.test.ts b/tests/executable.test.ts index 8320277..b5989f9 100644 --- a/tests/executable.test.ts +++ b/tests/executable.test.ts @@ -9,6 +9,7 @@ import { } from './helpers/cli-process.js'; const packageJsonPath = resolve(repositoryRoot, 'package.json'); +const DOCTOR_TEST_TIMEOUT_MS = 25_000; beforeAll(() => { buildCli(); @@ -57,13 +58,13 @@ describe('StellarForge CLI executable', () => { expect(result.stdout).toContain('[PASS] npm:'); expect(result.stdout).toContain('[PASS] Git:'); expect(result.stdout).toContain('Rust:'); - expect(result.stdout).toContain('[PASS] Cargo:'); + expect(result.stdout).toContain('Cargo:'); expect(result.stdout).toContain('Stellar CLI:'); expect(result.stdout).toContain('Docker:'); expect(result.stdout).toMatch( /Summary: \d+ passed, \d+ warnings, \d+ failed\./, ); - }, 15_000); + }, DOCTOR_TEST_TIMEOUT_MS); it('prints the package version from the built executable', () => { const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { diff --git a/tests/static-quality.test.ts b/tests/static-quality.test.ts index f116ab1..da726d4 100644 --- a/tests/static-quality.test.ts +++ b/tests/static-quality.test.ts @@ -28,6 +28,8 @@ const typescriptCliPath = resolve( ); const eslintConfigPath = resolve(repositoryRoot, 'eslint.config.js'); const tsconfigPath = resolve(repositoryRoot, 'tsconfig.json'); +const TOOL_TIMEOUT_MS = 25_000; +const TEST_TIMEOUT_MS = 30_000; function runLocalTool(binaryPath: string, args: string[], input?: string) { return spawnSync(process.execPath, [binaryPath, ...args], { @@ -35,68 +37,84 @@ function runLocalTool(binaryPath: string, args: string[], input?: string) { encoding: 'utf8', input, shell: false, + timeout: TOOL_TIMEOUT_MS, }); } describe('static quality enforcement', () => { - it('rejects a lint violation with the repository ESLint configuration', () => { - const result = runLocalTool( - eslintCliPath, - [ - '--config', - eslintConfigPath, - '--stdin', - '--stdin-filename', - 'src/main.ts', - ], - 'const unused = 1;\n', - ); + it( + 'rejects a lint violation with the repository ESLint configuration', + () => { + const result = runLocalTool( + eslintCliPath, + [ + '--config', + eslintConfigPath, + '--stdin', + '--stdin-filename', + 'src/main.ts', + ], + 'const unused = 1;\n', + ); - expect(result.status).not.toBe(0); - expect(`${result.stdout}${result.stderr}`).toContain('no-unused-vars'); - }, 15_000); + expect(result.status).not.toBe(0); + expect(`${result.stdout}${result.stderr}`).toContain('no-unused-vars'); + }, + TEST_TIMEOUT_MS, + ); - it('rejects a formatting violation with the repository Prettier configuration', async () => { - await withTempDirectory((directory) => { - const fixturePath = join(directory, 'bad-format.ts'); - writeFileSync(fixturePath, 'const value={answer:42}\n'); + it( + 'rejects a formatting violation with the repository Prettier configuration', + async () => { + await withTempDirectory((directory) => { + const fixturePath = join(directory, 'bad-format.ts'); + writeFileSync(fixturePath, 'const value={answer:42}\n'); - const result = runLocalTool(prettierCliPath, [ - '--check', - '--config', - resolve(repositoryRoot, '.prettierrc'), - fixturePath, - ]); + const result = runLocalTool(prettierCliPath, [ + '--check', + '--config', + resolve(repositoryRoot, '.prettierrc'), + fixturePath, + ]); - expect(result.status).not.toBe(0); - expect(`${result.stdout}${result.stderr}`).toContain( - 'Code style issues found', - ); - }); - }, 15_000); + expect(result.status).not.toBe(0); + expect(`${result.stdout}${result.stderr}`).toContain( + 'Code style issues found', + ); + }); + }, + TEST_TIMEOUT_MS, + ); - it('rejects a type violation with strict repository compiler options', async () => { - await withTempDirectory((directory) => { - const fixturePath = join(directory, 'bad-types.ts'); - const fixtureConfigPath = join(directory, 'tsconfig.json'); + it( + 'rejects a type violation with strict repository compiler options', + async () => { + await withTempDirectory((directory) => { + const fixturePath = join(directory, 'bad-types.ts'); + const fixtureConfigPath = join(directory, 'tsconfig.json'); - writeFileSync(fixturePath, "const count: number = 'not-a-number';\n"); - writeFileSync( - fixtureConfigPath, - JSON.stringify({ - extends: tsconfigPath, - compilerOptions: { - types: [], - noEmit: true, - }, - include: ['./bad-types.ts'], - }), - ); + writeFileSync(fixturePath, "const count: number = 'not-a-number';\n"); + writeFileSync( + fixtureConfigPath, + JSON.stringify({ + extends: tsconfigPath, + compilerOptions: { + types: [], + noEmit: true, + }, + include: ['./bad-types.ts'], + }), + ); - const result = runLocalTool(typescriptCliPath, ['-p', fixtureConfigPath]); + const result = runLocalTool(typescriptCliPath, [ + '-p', + fixtureConfigPath, + ]); - expect(result.status).not.toBe(0); - expect(`${result.stdout}${result.stderr}`).toContain('TS2322'); - }); - }, 15_000); + expect(result.status).not.toBe(0); + expect(`${result.stdout}${result.stderr}`).toContain('TS2322'); + }); + }, + TEST_TIMEOUT_MS, + ); });