From 83b7573bd12db5468c729cf90215a7026263cd35 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 23 Sep 2026 11:14:36 +0800 Subject: [PATCH 1/4] fix(install): honor --non-interactive, create api/.env, resolve --directory - --non-interactive no longer runs the four core enquirer prompts (host, environment, directory, app name); without a TTY they blocked or threw. Values come from flags or their defaults, and --app-name is a new flag. - --environment is validated (development | production) on the flag path. - --directory is path.resolve()d so relative and Git Bash style paths work for the file writes and the cwd handed to docker compose. - api/.env is created (empty, comment header) before docker compose up. docker-compose.yml bind-mounts it; when missing, Docker created a directory at that path and the API could not boot. If a directory is already there the installer explains and exits 1. - README documents --app-name, --non-interactive and the api/.env behaviour. --- README.md | 9 ++++++++ index.js | 67 +++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 405c534..d52a3df 100644 --- a/README.md +++ b/README.md @@ -159,12 +159,21 @@ flb install-fleetbase - `--host `: Host or IP address to bind to (default: `localhost`) - `--environment `: Environment: `development` or `production` (default: `development`) - `--directory `: Installation directory (default: current directory) +- `--app-name `: Application name (default: `Fleetbase`) +- `--non-interactive`: Skip every prompt and use the flags above plus safe defaults (CI/CD, scripted installs) + +The installer creates an empty `api/.env` (bind-mounted by `docker-compose.yml`) when one does not exist. **Example:** ```bash flb install-fleetbase --host 0.0.0.0 --environment production --directory /opt/fleetbase ``` +**Non-interactive example:** +```bash +flb install-fleetbase --non-interactive --directory /opt/fleetbase +``` + ### Publishing a Extension To publish a extension, navigate to the extension directory and run: diff --git a/index.js b/index.js index 8692f5c..4776753 100755 --- a/index.js +++ b/index.js @@ -1112,6 +1112,36 @@ function buildEnvBlock(vars, indent = 6) { .join('\n'); } +/** + * Make sure api/.env exists before `docker compose up`. + * docker-compose.yml bind-mounts ./api/.env into the application container; when the + * file is missing Docker creates a *directory* at that path and Laravel cannot boot. + * Values set in docker-compose.override.yml take precedence over this file, so an + * empty file with a comment header is the correct default. + * @param {string} directory resolved installation directory + */ +async function ensureApiEnvFile(directory) { + const envPath = path.join(directory, 'api', '.env'); + if (await fs.pathExists(envPath)) { + const stat = await fs.stat(envPath); + if (stat.isDirectory()) { + console.error(`\n✖ ${envPath} is a directory (left behind by an earlier "docker compose up" before the file existed).`); + console.error(' Remove it and re-run the installer.'); + process.exit(1); + } + console.log('✔ api/.env already present'); + return; + } + await fs.ensureDir(path.dirname(envPath)); + await fs.writeFile(envPath, [ + '# Fleetbase API environment overrides.', + '# Runtime configuration comes from docker-compose.override.yml and takes precedence', + '# over this file; add per-host secrets or extra overrides here.', + '', + ].join('\n')); + console.log('✔ api/.env created'); +} + // Command to install Fleetbase via Docker async function installFleetbaseCommand(options) { const crypto = require('crypto'); @@ -1156,8 +1186,15 @@ async function installFleetbaseCommand(options) { console.log('✔ Pre-flight checks complete\n'); try { + const nonInteractive = !!options.nonInteractive; + if (nonInteractive) { + console.log(' ℹ Non-interactive mode: no prompts; flags and safe defaults are used.'); + } + // ── Step 1: Core installation parameters ──────────────────────────── - const coreAnswers = await prompt([ + // Without a TTY (CI, piped stdin) enquirer would block or throw, so in + // non-interactive mode every core value comes from a flag or its default. + const coreAnswers = nonInteractive ? {} : await prompt([ { type: 'input', name: 'host', @@ -1186,14 +1223,21 @@ async function installFleetbaseCommand(options) { type: 'input', name: 'appName', message: 'Application name:', - initial: 'Fleetbase', + initial: options.appName || 'Fleetbase', }, ]); - const host = options.host || coreAnswers.host; - const environment = options.environment || coreAnswers.environment; - const directory = options.directory || coreAnswers.directory; - const appName = coreAnswers.appName || 'Fleetbase'; + const host = options.host || coreAnswers.host || 'localhost'; + const environment = options.environment || coreAnswers.environment || 'development'; + const appName = options.appName || coreAnswers.appName || 'Fleetbase'; + // Resolve so relative paths (and Git Bash style paths on Windows) work for both + // the file writes below and the `cwd` handed to docker compose. + const directory = path.resolve(options.directory || coreAnswers.directory || process.cwd()); + + if (!['development', 'production'].includes(environment)) { + console.error(`\n✖ Invalid environment "${environment}". Use "development" or "production".`); + process.exit(1); + } const useHttps = environment === 'production'; const appDebug = environment !== 'production'; @@ -1201,11 +1245,6 @@ async function installFleetbaseCommand(options) { const schemeApi = useHttps ? 'https' : 'http'; const schemeConsole = useHttps ? 'https' : 'http'; const isLocalhost = host === 'localhost' || host === '0.0.0.0' || host === '127.0.0.1'; - const nonInteractive = !!options.nonInteractive; - - if (nonInteractive) { - console.log(' ℹ Non-interactive mode: all optional steps will use safe defaults.'); - } // ── Step 2: Clone repo if needed ───────────────────────────────────── const dockerComposePath = path.join(directory, 'docker-compose.yml'); @@ -1557,6 +1596,9 @@ ${buildEnvBlock(dbEnvVars)} console.log('✔ Console configuration files updated'); + // ── Step 10b: Ensure api/.env exists (bind-mounted by docker-compose.yml) ── + await ensureApiEnvFile(directory); + // ── Step 11: Start containers ───────────────────────────────────────── console.log('\n⏳ Starting Fleetbase containers...'); console.log(' This may take a few minutes on first run...\n'); @@ -1998,7 +2040,8 @@ program .option('--host ', 'Host or IP address to bind to (default: localhost)') .option('--environment ', 'Environment: development or production (default: development)') .option('--directory ', 'Installation directory (default: current directory)') - .option('--non-interactive', 'Skip all optional prompts and use safe defaults (useful for CI/CD)') + .option('--app-name ', 'Application name (default: Fleetbase)') + .option('--non-interactive', 'Skip every prompt; use flags and safe defaults (useful for CI/CD)') .action(installFleetbaseCommand); program From 6cb6a7a60327ecdca551b4c9542fad5bc3976b95 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 23 Sep 2026 11:14:37 +0800 Subject: [PATCH 2/4] bump version to v0.0.7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ae12546..9f629a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@fleetbase/cli", - "version": "0.0.4", + "version": "0.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@fleetbase/cli", - "version": "0.0.4", + "version": "0.0.7", "license": "AGPL-3.0-or-later", "dependencies": { "axios": "^1.7.3", diff --git a/package.json b/package.json index ab1483a..540b6fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/cli", - "version": "0.0.6", + "version": "0.0.7", "description": "CLI tool for managing Fleetbase Extensions", "repository": "https://github.com/fleetbase/fleetbase", "license": "AGPL-3.0-or-later", From 4d718b10c3c0c118e39c4b848bc24ec1c1795e15 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 30 Sep 2026 15:05:23 +0800 Subject: [PATCH 3/4] release: v0.0.7 Adopt the release/v* branch convention: CI runs on PRs into release/v*, release.yml tags the version when a release branch merges to main, and create-release.yml publishes the GitHub Release from RELEASE.md. --- .github/workflows/ci.yml | 2 +- .github/workflows/create-release.yml | 22 +++++++++++++ .github/workflows/release.yml | 47 ++++++++++++++++++++++++++++ RELEASE.md | 16 ++++++++++ package-lock.json | 4 +-- package.json | 2 +- 6 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/create-release.yml create mode 100644 .github/workflows/release.yml create mode 100644 RELEASE.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70714d7..6011208 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: tags: - 'v*' pull_request: - branches: [ main ] + branches: [ main, 'release/v*', 'dev-v*' ] jobs: build: diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 0000000..40e80db --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,22 @@ +name: Create Release +on: + push: + tags: + - 'v*' + +jobs: + create: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Publish GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + name: ${{ github.ref_name }} + body_path: RELEASE.md + generate_release_notes: true + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b3fc94a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,47 @@ +name: Release Tag + +# Tags a release when a release branch is merged to main, and pushes the tag. Delegates +# to the reusable workflow in fleetbase/fleetbase. Requires org secret _GITHUB_AUTH_TOKEN +# (inherited); no-ops with a warning until it is set. +# +# It pushes the tag and nothing else — create-release.yml and the npm_publish job in +# ci.yml already chain off `push: tags: v*`. +# +# The version is never retyped: it comes from the branch name, and the tag is refused +# unless the files below and RELEASE.md already agree with it. This repository is a +# node CLI, so it carries package.json and package-lock.json. +# +# RELEASE.md is required, and its first line must name the version being released. That +# check is the only thing that can tell notes written for this release from the previous +# release's notes nobody replaced. +# +# Deliberately unpinned — it tracks the reusable workflow on main, so there is no ref +# here to remember to bump. +# +# workflow_dispatch is the recovery path. Use it when a release failed validation and the +# fix landed on main after the merge: re-running the merge event would check out the +# merge commit and never see that fix. + +on: + pull_request: + types: [closed] + branches: [main] + workflow_dispatch: + inputs: + version: + description: "Version to tag, e.g. 1.2.3. Recovery only — a normal release reads it from the branch." + required: true +permissions: + contents: read + +jobs: + tag: + if: github.event_name == 'workflow_dispatch' || + (github.event.pull_request.merged == true && + (startsWith(github.event.pull_request.head.ref, 'release/v') || + startsWith(github.event.pull_request.head.ref, 'dev-v'))) + uses: fleetbase/fleetbase/.github/workflows/release-tag.yml@main + with: + version-files: package.json,package-lock.json + version: ${{ github.event.inputs.version || '' }} + secrets: inherit diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..da7f6da --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,16 @@ +> v0.0.7 ~ "Reliable install-fleetbase on macOS, Windows and CI" + +--- +## Fixes +- `flb install-fleetbase --non-interactive` no longer prompts for host, environment, directory or app name. Values come from flags or defaults, so the command runs without a TTY (CI, piped stdin). +- The installer now creates `api/.env` before starting containers. Previously Docker created a directory at that bind-mount path on a fresh clone and the API container could not boot. +- `--directory` is resolved to an absolute path, so relative paths and Git Bash style paths on Windows work. +- `--environment` is validated (`development` or `production`) and exits with a clear error otherwise. + +--- +## What's New +- `--app-name ` flag for `flb install-fleetbase`. + +--- +## Release process +- Release branches are now named `release/v`. CI runs on pull requests into `release/v*`, and merging a release branch into `main` tags the release, publishes it to npm and creates the GitHub Release from this file. diff --git a/package-lock.json b/package-lock.json index ae12546..9f629a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@fleetbase/cli", - "version": "0.0.4", + "version": "0.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@fleetbase/cli", - "version": "0.0.4", + "version": "0.0.7", "license": "AGPL-3.0-or-later", "dependencies": { "axios": "^1.7.3", diff --git a/package.json b/package.json index ab1483a..540b6fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/cli", - "version": "0.0.6", + "version": "0.0.7", "description": "CLI tool for managing Fleetbase Extensions", "repository": "https://github.com/fleetbase/fleetbase", "license": "AGPL-3.0-or-later", From 46fc3dcded5cee69112e3d68b1d74a329089481e Mon Sep 17 00:00:00 2001 From: Ron Date: Wed, 30 Sep 2026 15:06:51 +0800 Subject: [PATCH 4/4] Potential fix for pull request finding 'CodeQL / Workflow does not contain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/create-release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 40e80db..579b36e 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -4,6 +4,9 @@ on: tags: - 'v*' +permissions: + contents: write + jobs: create: runs-on: ubuntu-latest