🚀 AssetIT Asset Importer — Setup & Development Guide
Forge app (Jira admin page) that imports devices from Microsoft Intune and Jamf Pro into AssetIT.
🧬 Clone repository
git clone <https://<your-repo-url>>.git
cd <your-project-folder>
🔑 Bitbucket app password
Create an app password: 👉 https://bitbucket.org/account/settings/app-passwords/new
⚙️ Node version
CI builds on Node 22 (node:22.22.0). Use a 22.x release locally:
node -v # v22.x
📦 Install
npm i -g pnpm
pnpm install
pnpm generate # prisma generate
🧪 Development
Do you need the backend running locally? No (working on the Forge UI / resolver / mapping logic) — skip steps 1–2 and step 5: the committed manifest.yml points at the deployed staging backend, which is sufficient for everyday work. Yes (changing anything under src/backend) — complete all steps.
▶️ 1. Start the local databases — only for a local backend
docker compose up -d
|
Service |
Details |
|---|---|
|
Postgres 17 |
on 5432 ( |
|
Redis |
on 6380 (container port 6379) |
pnpm studio opens Prisma Studio against DATABASE_URL.
🔧 2. Configure src/backend/.env — only for a local backend
Gitignored; local only:
DATABASE_URL=postgres://<user>:<pass>@localhost:<port>/<db>
JWT_SECRET=<any local value>
BE_SECRET_SECURED=<any local value>
REDIS_HOST=localhost
REDIS_PORT=6380
NODE_ENV=development
DATABASE_URL must point at an AssetIT database, not an empty one. The Prisma schema maps onto AssetIT's tables (assets, api_keys, AddonSettings, models, …); the importer does not own its schema.
🚀 3. Run everything
pnpm dev
Turbo starts four persistent tasks: frontend (vite, 5173), resolver (nodemon → tsup), backend (nodemon → tsup → node, 3001), and open-api.
To restart a single task: pnpm dev -- --filter=forge-resolvers.
Stop with Ctrl+C in the terminal. Using an IDE stop button sends the signal only to child processes; turbo then reports a task failure that is not an actual error.
🛠 4. Forge CLI
npm i -g @forge/cli
forge --version
|
Action |
Notes |
|---|---|
|
Generate an API token |
|
|
|
|
|
Ask an app admin to add you as a Forge App Developer |
|
|
Install into your site |
Use |
forge install # select Jira, then enter your instance domain
🔗 5. Which backend does the resolver talk to?
🌐 Option A — staging backend (default)
manifest.yml as committed points both remotes at the deployed staging backend:
baseUrl: https://assetit-assets-importer-dev-dot-devsamurai-dev.ue.r.appspot.com
Leave it as is to develop the Forge UI and resolver without Docker, without src/backend/.env, and without running the backend. AssetIT data comes from staging.
🏠 Option B — local backend
Use this only when changing src/backend. The resolver needs a public HTTPS URL — localhost will not work. Expose :3001 (e.g., ngrok) and place that URL in two locations in manifest.yml:
remotes:
connect:
key: connect
baseUrl: https://<your-tunnel>.ngrok-free.app # + connect-eu
permissions:
external:
fetch:
backend:
- address: https://<your-tunnel>.ngrok-free.app
Do not commit a tunnel URL. Restore the staging URL before pushing — check git diff manifest.yml each time.
📦 6. Tunnel
# Terminal 1
pnpm dev
# Terminal 2
forge tunnel
forge tunnel runs the resolver locally against the development environment. Resolver logs appear in this terminal.
🔐 7. Microsoft client secret (once per environment)
Ask the Azure app admin for the client secret of the Azure app registration for the environment you are setting up — it is not in this repo and is unrecoverable from the Azure portal after creation. Then:
forge providers configure microsoft \
-s "<client secret value from the Azure admin>" \
-e development --verbose
manifest.yml (dev/staging, clientId 3d7901f7-…) and manifest.prod.yml (clientId 393307ca-…) use different Azure app registrations; each has its own secret. Set the secret separately for development, staging, and production.
Jamf requires no app-level secret: each user enters their Jamf Pro Client ID and Secret in the app; the app stores them with storage.setSecret.
🏗 Manual deploy (development environment)
pnpm dev-deploy # turbo run build && forge deploy -> development
Or in two steps: pnpm build then forge deploy. pnpm start runs the built backend (src/backend/dist/index.js) without watch mode.
Useful build filters:
|
Command |
Builds |
|---|---|
|
|
everything |
|
|
frontend + resolver + contracts ( |
|
|
backend only |
🚢 Staging & Production (CI)
Deploys use bitbucket-pipelines.yml — do not deploy staging or production manually.
⬆️ develop → Staging
⚙️ envsubst fills the $VARS in src/backend/src/loadEnv.ts and src/resolvers/src/loadEnv.ts from pipeline variables.
📁 app.stg.yaml is rendered as app.yaml and deployed to App Engine: project devsamurai-dev, service assetit-assets-importer-dev.
pnpm build-forge
forge lint -e staging
forge deploy -e staging
forge install --upgrade --site $FORGE_STG_SITE --product jira -e staging
📂 If packages/contract/** or src/open-api/** changed, the generated OpenAPI documentation is pushed to the ds-dev-internal-shared GCS bucket.
🚀 main → Production
🌐 Backend deploys to two regions in parallel, both as service assetit-assets-importer-prod:
-
US —
app.prod.us.yaml→ projectatlas-app-03 -
EU —
app.prod.eu.yaml→ projectatlas-app-eu
🗂 Forge renders the production manifest and deploys it:
cat manifest.prod.yml | envsubst > manifest.yml
forge lint -e production && forge deploy -e production
manifest.prod.yml keeps $US_SERVER_BASE_URL / $EU_SERVER_BASE_URL as placeholders — the committed prod manifest contains no real URLs.
⛔ There is no forge install --upgrade in production: existing installs pick up the new version automatically; a major-scope change requires the customer to re-consent.
📋 Required pipeline variables
|
Variable |
Used for |
|---|---|
|
|
GCP service account — staging + docs bucket |
|
|
GCP service accounts — prod US / EU |
|
|
|
|
|
Staging Jira site for |
|
|
Substituted into |
|
|
Substituted into the resolver's |
|
|
Substituted into |
Redis hosts are not pipeline variables — they are hardcoded per environment in the app.*.yaml files and reached over a VPC connector.
🩺 Debugging
|
Item |
Details |
|---|---|
|
Resolver logs while tunneling |
the |
|
Deployed logs |
https://developer.atlassian.com/console/myapps/8004a698-ad73-4cba-8f5b-a079761ece2e/monitoring/logs?environment=production&interval=P1D (switch the |
|
Diagnostic |
Several diagnostic |
🧹 Code quality
pnpm lint # biome lint
pnpm lint-fix # biome check --write