Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
tags:
- 'v*'
pull_request:
branches: [ main ]
branches: [ main, 'release/v*', 'dev-v*' ]

jobs:
build:
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Create Release
on:
push:
tags:
- 'v*'

permissions:
contents: write

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 }}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
47 changes: 47 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,21 @@ flb install-fleetbase
- `--host <host>`: Host or IP address to bind to (default: `localhost`)
- `--environment <environment>`: Environment: `development` or `production` (default: `development`)
- `--directory <directory>`: Installation directory (default: current directory)
- `--app-name <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:
Expand Down
16 changes: 16 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -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 <name>` flag for `flb install-fleetbase`.

---
## Release process
- Release branches are now named `release/v<semver>`. 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.
67 changes: 55 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -1186,26 +1223,28 @@ 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';
const scSecure = useHttps;
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');
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -1998,7 +2040,8 @@ program
.option('--host <host>', 'Host or IP address to bind to (default: localhost)')
.option('--environment <environment>', 'Environment: development or production (default: development)')
.option('--directory <directory>', 'Installation directory (default: current directory)')
.option('--non-interactive', 'Skip all optional prompts and use safe defaults (useful for CI/CD)')
.option('--app-name <name>', 'Application name (default: Fleetbase)')
.option('--non-interactive', 'Skip every prompt; use flags and safe defaults (useful for CI/CD)')
.action(installFleetbaseCommand);

program
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading