From 91da07a5999302e4561d7f8268d0738cdca1ed9e Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Mon, 13 Jul 2026 16:38:11 -0400 Subject: [PATCH 01/11] feat: scaffold deploy-by-reference using the native harper CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Templates deploy by *reference* (the Harper cluster clones a pinned commit over SSH) with no per-project scripts: deploy -> `harper deploy by_ref=true restart=true replicated=true`, deploy:setup -> `harper deploy setup=true`. Centralized GitHub Actions workflow (.github/workflows/deploy.yaml — also fixes the singular workflow/ -> workflows/ path bug so GitHub runs it); tag+dispatch triggered. .env carries only CLI_TARGET (creds via harper login / CI secrets). README deployment sections rewritten around the reference model. End-state assuming native CLI support has landed: harper#1777 (by_ref), harper#1778 (deploy setup), harper-pro#570 (add_ssh_key generate). Tracks create-harper#117. DRAFT until those release. Co-Authored-By: Claude Opus 4.8 --- .../_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-react-ssr/README.md | 39 ++++++++++++-- .../_github/workflow/deploy.yaml | 33 ------------ .../_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-react-ssr/package.json | 3 +- template-react-ts-ssr/README.md | 39 ++++++++++++-- .../_github/workflow/deploy.yaml | 33 ------------ .../_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-react-ts-ssr/package.json | 3 +- template-react-ts/README.md | 39 ++++++++++++-- .../_github/workflow/deploy.yaml | 33 ------------ .../_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-react-ts/package.json | 3 +- template-react/README.md | 39 ++++++++++++-- template-react/_github/workflow/deploy.yaml | 33 ------------ template-react/_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-react/package.json | 3 +- template-vanilla-ts/README.md | 39 ++++++++++++-- .../_github/workflow/deploy.yaml | 33 ------------ .../_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-vanilla-ts/package.json | 3 +- template-vanilla/README.md | 39 ++++++++++++-- template-vanilla/_github/workflow/deploy.yaml | 33 ------------ .../_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-vanilla/package.json | 3 +- template-vue-ssr/README.md | 39 ++++++++++++-- template-vue-ssr/_github/workflow/deploy.yaml | 33 ------------ .../_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-vue-ssr/package.json | 3 +- template-vue-ts-ssr/README.md | 39 ++++++++++++-- .../_github/workflow/deploy.yaml | 33 ------------ .../_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-vue-ts-ssr/package.json | 3 +- template-vue-ts/README.md | 39 ++++++++++++-- template-vue-ts/_github/workflow/deploy.yaml | 33 ------------ template-vue-ts/_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-vue-ts/package.json | 3 +- template-vue/README.md | 39 ++++++++++++-- template-vue/_github/workflow/deploy.yaml | 33 ------------ template-vue/_github/workflows/deploy.yaml | 53 +++++++++++++++++++ template-vue/package.json | 3 +- template.tests/template.test.js | 14 ++++- .../all/_github/workflows/deploy.yaml | 53 +++++++++++++++++++ 43 files changed, 1028 insertions(+), 372 deletions(-) create mode 100644 template-early-hints/_github/workflows/deploy.yaml delete mode 100644 template-react-ssr/_github/workflow/deploy.yaml create mode 100644 template-react-ssr/_github/workflows/deploy.yaml delete mode 100644 template-react-ts-ssr/_github/workflow/deploy.yaml create mode 100644 template-react-ts-ssr/_github/workflows/deploy.yaml delete mode 100644 template-react-ts/_github/workflow/deploy.yaml create mode 100644 template-react-ts/_github/workflows/deploy.yaml delete mode 100644 template-react/_github/workflow/deploy.yaml create mode 100644 template-react/_github/workflows/deploy.yaml delete mode 100644 template-vanilla-ts/_github/workflow/deploy.yaml create mode 100644 template-vanilla-ts/_github/workflows/deploy.yaml delete mode 100644 template-vanilla/_github/workflow/deploy.yaml create mode 100644 template-vanilla/_github/workflows/deploy.yaml delete mode 100644 template-vue-ssr/_github/workflow/deploy.yaml create mode 100644 template-vue-ssr/_github/workflows/deploy.yaml delete mode 100644 template-vue-ts-ssr/_github/workflow/deploy.yaml create mode 100644 template-vue-ts-ssr/_github/workflows/deploy.yaml delete mode 100644 template-vue-ts/_github/workflow/deploy.yaml create mode 100644 template-vue-ts/_github/workflows/deploy.yaml delete mode 100644 template-vue/_github/workflow/deploy.yaml create mode 100644 template-vue/_github/workflows/deploy.yaml create mode 100644 templates-shared/all/_github/workflows/deploy.yaml diff --git a/template-early-hints/_github/workflows/deploy.yaml b/template-early-hints/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-early-hints/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-react-ssr/README.md b/template-react-ssr/README.md index f31ffcd..893ffe2 100644 --- a/template-react-ssr/README.md +++ b/template-react-ssr/README.md @@ -103,20 +103,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-react-ssr/_github/workflow/deploy.yaml b/template-react-ssr/_github/workflow/deploy.yaml deleted file mode 100644 index cc5039e..0000000 --- a/template-react-ssr/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-react-ssr/_github/workflows/deploy.yaml b/template-react-ssr/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-react-ssr/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-react-ssr/package.json b/template-react-ssr/package.json index 86066fd..7946679 100644 --- a/template-react-ssr/package.json +++ b/template-react-ssr/package.json @@ -13,7 +13,8 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "dependencies": { "@harperfast/schema-codegen": "^2.0.0", diff --git a/template-react-ts-ssr/README.md b/template-react-ts-ssr/README.md index 90bc495..9af4e5f 100644 --- a/template-react-ts-ssr/README.md +++ b/template-react-ts-ssr/README.md @@ -111,20 +111,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-react-ts-ssr/_github/workflow/deploy.yaml b/template-react-ts-ssr/_github/workflow/deploy.yaml deleted file mode 100644 index cc5039e..0000000 --- a/template-react-ts-ssr/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-react-ts-ssr/_github/workflows/deploy.yaml b/template-react-ts-ssr/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-react-ts-ssr/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-react-ts-ssr/package.json b/template-react-ts-ssr/package.json index a6d1bfd..665fa14 100644 --- a/template-react-ts-ssr/package.json +++ b/template-react-ts-ssr/package.json @@ -13,7 +13,8 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "dependencies": { "@harperfast/schema-codegen": "^2.0.0", diff --git a/template-react-ts/README.md b/template-react-ts/README.md index 90bc495..9af4e5f 100644 --- a/template-react-ts/README.md +++ b/template-react-ts/README.md @@ -111,20 +111,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-react-ts/_github/workflow/deploy.yaml b/template-react-ts/_github/workflow/deploy.yaml deleted file mode 100644 index cc5039e..0000000 --- a/template-react-ts/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-react-ts/_github/workflows/deploy.yaml b/template-react-ts/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-react-ts/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-react-ts/package.json b/template-react-ts/package.json index a6d1bfd..665fa14 100644 --- a/template-react-ts/package.json +++ b/template-react-ts/package.json @@ -13,7 +13,8 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "dependencies": { "@harperfast/schema-codegen": "^2.0.0", diff --git a/template-react/README.md b/template-react/README.md index f31ffcd..893ffe2 100644 --- a/template-react/README.md +++ b/template-react/README.md @@ -103,20 +103,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-react/_github/workflow/deploy.yaml b/template-react/_github/workflow/deploy.yaml deleted file mode 100644 index cc5039e..0000000 --- a/template-react/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-react/_github/workflows/deploy.yaml b/template-react/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-react/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-react/package.json b/template-react/package.json index 86066fd..7946679 100644 --- a/template-react/package.json +++ b/template-react/package.json @@ -13,7 +13,8 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "dependencies": { "@harperfast/schema-codegen": "^2.0.0", diff --git a/template-vanilla-ts/README.md b/template-vanilla-ts/README.md index 7ff7891..6556313 100644 --- a/template-vanilla-ts/README.md +++ b/template-vanilla-ts/README.md @@ -111,20 +111,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-vanilla-ts/_github/workflow/deploy.yaml b/template-vanilla-ts/_github/workflow/deploy.yaml deleted file mode 100644 index e3ee65c..0000000 --- a/template-vanilla-ts/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Deploy - run: npm run deploy diff --git a/template-vanilla-ts/_github/workflows/deploy.yaml b/template-vanilla-ts/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-vanilla-ts/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-vanilla-ts/package.json b/template-vanilla-ts/package.json index 463b97b..a193ab2 100644 --- a/template-vanilla-ts/package.json +++ b/template-vanilla-ts/package.json @@ -12,7 +12,8 @@ "format": "prettier --write .", "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/template-vanilla/README.md b/template-vanilla/README.md index 3e6ec4f..d8a32ee 100644 --- a/template-vanilla/README.md +++ b/template-vanilla/README.md @@ -103,20 +103,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-vanilla/_github/workflow/deploy.yaml b/template-vanilla/_github/workflow/deploy.yaml deleted file mode 100644 index e3ee65c..0000000 --- a/template-vanilla/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Deploy - run: npm run deploy diff --git a/template-vanilla/_github/workflows/deploy.yaml b/template-vanilla/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-vanilla/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-vanilla/package.json b/template-vanilla/package.json index 463b97b..a193ab2 100644 --- a/template-vanilla/package.json +++ b/template-vanilla/package.json @@ -12,7 +12,8 @@ "format": "prettier --write .", "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/template-vue-ssr/README.md b/template-vue-ssr/README.md index f31ffcd..893ffe2 100644 --- a/template-vue-ssr/README.md +++ b/template-vue-ssr/README.md @@ -103,20 +103,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-vue-ssr/_github/workflow/deploy.yaml b/template-vue-ssr/_github/workflow/deploy.yaml deleted file mode 100644 index cc5039e..0000000 --- a/template-vue-ssr/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-vue-ssr/_github/workflows/deploy.yaml b/template-vue-ssr/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-vue-ssr/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-vue-ssr/package.json b/template-vue-ssr/package.json index 092be7f..9881a5b 100644 --- a/template-vue-ssr/package.json +++ b/template-vue-ssr/package.json @@ -13,7 +13,8 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "dependencies": { "@harperfast/schema-codegen": "^2.0.0", diff --git a/template-vue-ts-ssr/README.md b/template-vue-ts-ssr/README.md index 90bc495..9af4e5f 100644 --- a/template-vue-ts-ssr/README.md +++ b/template-vue-ts-ssr/README.md @@ -111,20 +111,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-vue-ts-ssr/_github/workflow/deploy.yaml b/template-vue-ts-ssr/_github/workflow/deploy.yaml deleted file mode 100644 index cc5039e..0000000 --- a/template-vue-ts-ssr/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-vue-ts-ssr/_github/workflows/deploy.yaml b/template-vue-ts-ssr/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-vue-ts-ssr/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-vue-ts-ssr/package.json b/template-vue-ts-ssr/package.json index ba5246f..55efd27 100644 --- a/template-vue-ts-ssr/package.json +++ b/template-vue-ts-ssr/package.json @@ -13,7 +13,8 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "dependencies": { "@harperfast/schema-codegen": "^2.0.0", diff --git a/template-vue-ts/README.md b/template-vue-ts/README.md index 90bc495..9af4e5f 100644 --- a/template-vue-ts/README.md +++ b/template-vue-ts/README.md @@ -111,20 +111,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-vue-ts/_github/workflow/deploy.yaml b/template-vue-ts/_github/workflow/deploy.yaml deleted file mode 100644 index cc5039e..0000000 --- a/template-vue-ts/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-vue-ts/_github/workflows/deploy.yaml b/template-vue-ts/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-vue-ts/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-vue-ts/package.json b/template-vue-ts/package.json index ba5246f..55efd27 100644 --- a/template-vue-ts/package.json +++ b/template-vue-ts/package.json @@ -13,7 +13,8 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "dependencies": { "@harperfast/schema-codegen": "^2.0.0", diff --git a/template-vue/README.md b/template-vue/README.md index f31ffcd..893ffe2 100644 --- a/template-vue/README.md +++ b/template-vue/README.md @@ -103,20 +103,53 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -When you are ready, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in to your account, and create a cluster. +Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. -Come back and log in your local CLI to your cluster: +First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: ```sh harper login ``` -Then you can deploy your app to your cluster: +### One-time setup + +So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: + +```sh +npm run deploy:setup +``` + +This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) + +### Deploy ```sh npm run deploy ``` +This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. + +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy + +### Other options + +- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. +- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. + +See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. + ## Keep Going! For more information about getting started with Harper and building applications, see our [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-vue/_github/workflow/deploy.yaml b/template-vue/_github/workflow/deploy.yaml deleted file mode 100644 index cc5039e..0000000 --- a/template-vue/_github/workflow/deploy.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-vue/_github/workflows/deploy.yaml b/template-vue/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/template-vue/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-vue/package.json b/template-vue/package.json index 092be7f..9881a5b 100644 --- a/template-vue/package.json +++ b/template-vue/package.json @@ -13,7 +13,8 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy_component . restart=true replicated=true" + "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy:setup": "harper deploy setup=true" }, "dependencies": { "@harperfast/schema-codegen": "^2.0.0", diff --git a/template.tests/template.test.js b/template.tests/template.test.js index 0bc2504..b6582d9 100644 --- a/template.tests/template.test.js +++ b/template.tests/template.test.js @@ -69,15 +69,25 @@ describe('Integration tests', () => { const templateDir = path.resolve(root, `template-${template}`); if (fs.existsSync(path.join(templateDir, '_env'))) { expect(fs.existsSync(path.join(targetDir, '.env'))).toBe(true); + // Credentials come from `harper login` (local) or GitHub Actions secrets (CI); the + // scaffolded .env only selects the target cluster. const envContent = fs.readFileSync(path.join(targetDir, '.env'), 'utf-8'); - expect(envContent).toContain('CLI_TARGET_USERNAME'); - expect(envContent).toContain('CLI_TARGET_PASSWORD'); expect(envContent).toContain('CLI_TARGET'); + expect(envContent).not.toContain('CLI_TARGET_USERNAME'); + expect(envContent).not.toContain('CLI_TARGET_PASSWORD'); } if (fs.existsSync(path.join(templateDir, '_env.example'))) { expect(fs.existsSync(path.join(targetDir, '.env.example'))).toBe(true); } + + // Deploy-by-reference scaffolding: the workflow must live under `.github/workflows/` + // (plural — GitHub only runs workflows there), and deploy is driven by the native harper CLI + // (no per-project scripts). + expect(fs.existsSync(path.join(targetDir, '.github', 'workflows', 'deploy.yaml'))).toBe(true); + expect(fs.existsSync(path.join(targetDir, 'scripts', 'deploy.mjs'))).toBe(false); + expect(pkgJson.scripts.deploy).toBe('harper deploy by_ref=true restart=true replicated=true'); + expect(pkgJson.scripts['deploy:setup']).toBe('harper deploy setup=true'); }); } }); diff --git a/templates-shared/all/_github/workflows/deploy.yaml b/templates-shared/all/_github/workflows/deploy.yaml new file mode 100644 index 0000000..5b7ef1a --- /dev/null +++ b/templates-shared/all/_github/workflows/deploy.yaml @@ -0,0 +1,53 @@ +# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` +# (`npm run deploy`). +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster +# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) +# using the read-only deploy key you registered once with `npm run deploy:setup`. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_USERNAME a cluster user allowed to deploy +# HARPER_CLI_PASSWORD that user's password +# +# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? +# See the "Deployment" section of the README for those alternatives. +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm test + - name: Run lint + run: npm run lint + - name: Install Harper CLI + run: npm install -g harper + - name: Deploy + run: npm run deploy + env: + CLI_TARGET: ${{ secrets.CLI_TARGET }} + HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} + HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} From 1f54169afeeff52644c9b44751d9a0b07ebb2eb0 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Fri, 17 Jul 2026 14:24:19 -0400 Subject: [PATCH 02/11] =?UTF-8?q?fix(templates):=20address=20PR=20review?= =?UTF-8?q?=20=E2=80=94=20pin=20harper=20CLI,=20exclude=20legacy=20early-h?= =?UTF-8?q?ints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pin the workflow's global install to `harper@^5.2.0` (the 5.2 line where deploy-by-reference lands; blocks a breaking 6.x bump) instead of unpinned latest. Exclude template-early-hints (a legacy harperdb EdgeWorker example, not published via npm create harper) from the deploy migration: remove the stray _github workflow it got via fan-out, and filter _github out of applySharedTemplates for it so it isn't re-added (it keeps every other shared file). The per-template deploy.yaml duplication is intentional (templates-shared/README.md: shared files are committed into each template for discoverability + wholesale-copy scaffolding), so it's left as-is. Co-Authored-By: Claude Opus 4.8 --- .../_github/workflows/deploy.yaml | 53 ------------------- .../_github/workflows/deploy.yaml | 3 +- .../_github/workflows/deploy.yaml | 3 +- .../_github/workflows/deploy.yaml | 3 +- template-react/_github/workflows/deploy.yaml | 3 +- .../_github/workflows/deploy.yaml | 3 +- .../_github/workflows/deploy.yaml | 3 +- .../_github/workflows/deploy.yaml | 3 +- .../_github/workflows/deploy.yaml | 3 +- template-vue-ts/_github/workflows/deploy.yaml | 3 +- template-vue/_github/workflows/deploy.yaml | 3 +- .../all/_github/workflows/deploy.yaml | 3 +- templates-shared/applySharedTemplates.js | 9 +++- 13 files changed, 30 insertions(+), 65 deletions(-) delete mode 100644 template-early-hints/_github/workflows/deploy.yaml diff --git a/template-early-hints/_github/workflows/deploy.yaml b/template-early-hints/_github/workflows/deploy.yaml deleted file mode 100644 index 5b7ef1a..0000000 --- a/template-early-hints/_github/workflows/deploy.yaml +++ /dev/null @@ -1,53 +0,0 @@ -# Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). -# -# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. -# -# Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password -# -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. -name: Deploy to Harper Fabric -on: - workflow_dispatch: - push: - tags: - - 'v*' - -concurrency: - group: deploy - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run unit tests - run: npm test - - name: Run lint - run: npm run lint - - name: Install Harper CLI - run: npm install -g harper - - name: Deploy - run: npm run deploy - env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} diff --git a/template-react-ssr/_github/workflows/deploy.yaml b/template-react-ssr/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-react-ssr/_github/workflows/deploy.yaml +++ b/template-react-ssr/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/template-react-ts-ssr/_github/workflows/deploy.yaml b/template-react-ts-ssr/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-react-ts-ssr/_github/workflows/deploy.yaml +++ b/template-react-ts-ssr/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/template-react-ts/_github/workflows/deploy.yaml b/template-react-ts/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-react-ts/_github/workflows/deploy.yaml +++ b/template-react-ts/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/template-react/_github/workflows/deploy.yaml b/template-react/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-react/_github/workflows/deploy.yaml +++ b/template-react/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/template-vanilla-ts/_github/workflows/deploy.yaml b/template-vanilla-ts/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-vanilla-ts/_github/workflows/deploy.yaml +++ b/template-vanilla-ts/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/template-vanilla/_github/workflows/deploy.yaml b/template-vanilla/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-vanilla/_github/workflows/deploy.yaml +++ b/template-vanilla/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/template-vue-ssr/_github/workflows/deploy.yaml b/template-vue-ssr/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-vue-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ssr/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/template-vue-ts-ssr/_github/workflows/deploy.yaml b/template-vue-ts-ssr/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-vue-ts-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ts-ssr/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/template-vue-ts/_github/workflows/deploy.yaml b/template-vue-ts/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-vue-ts/_github/workflows/deploy.yaml +++ b/template-vue-ts/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/template-vue/_github/workflows/deploy.yaml b/template-vue/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/template-vue/_github/workflows/deploy.yaml +++ b/template-vue/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/templates-shared/all/_github/workflows/deploy.yaml b/templates-shared/all/_github/workflows/deploy.yaml index 5b7ef1a..c73ffd9 100644 --- a/templates-shared/all/_github/workflows/deploy.yaml +++ b/templates-shared/all/_github/workflows/deploy.yaml @@ -44,7 +44,8 @@ jobs: - name: Run lint run: npm run lint - name: Install Harper CLI - run: npm install -g harper + # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 - name: Deploy run: npm run deploy env: diff --git a/templates-shared/applySharedTemplates.js b/templates-shared/applySharedTemplates.js index 1e35766..7efb867 100644 --- a/templates-shared/applySharedTemplates.js +++ b/templates-shared/applySharedTemplates.js @@ -19,11 +19,18 @@ import { copyDir } from '../lib/fs/copyDir.js'; 'template-vue-ts-ssr', ], }; + // template-early-hints is a legacy `harperdb` EdgeWorker example (not published via + // `npm create harper`), so it is intentionally excluded from the deploy-by-reference + // migration — it must not receive the `_github` deploy workflow that targets the modern + // `harper` CLI. It still gets every other shared file. + const excludeGithubForEarlyHints = (src) => !src.split(path.sep).includes('_github'); + for (const key in copiesToMake) { const fromShared = path.resolve(import.meta.dirname, key); for (const targetTemplate of copiesToMake[key]) { const toTemplate = path.resolve(import.meta.dirname, '..', targetTemplate); - copyDir(fromShared, toTemplate); + const filter = targetTemplate === 'template-early-hints' ? excludeGithubForEarlyHints : undefined; + copyDir(fromShared, toTemplate, filter); } } })(); From 8d78b9ef1555f0ce3011ab366940c120c4834924 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Fri, 17 Jul 2026 14:50:09 -0400 Subject: [PATCH 03/11] refactor(deploy): reconcile to sealed-token + git+https credential model Aligns the scaffold with where the deploy design landed (harper#1799 in-memory git-host credential + client-side-sealed durable tokens) instead of SSH deploy keys: deploy -> `harper deploy by_ref=true credential=github.com ...` (git+https ref + a sealed-token credential reference the cluster resolves at clone time; public repos drop credential=github.com). deploy:setup -> `harper deploy setup=true` seals a durable GitHub/npm token locally with the cluster's public key and stores only ciphertext. README rewritten around the sealed-token flow; the workflow no longer handles any token itself. Depends on harper#1777 (by_ref git+https + credential), harper#1778 (deploy setup), harper#1799. Co-Authored-By: Claude Opus 4.8 --- template-react-ssr/README.md | 19 ++++++++++--------- .../_github/workflows/deploy.yaml | 9 +++++---- template-react-ssr/package.json | 2 +- template-react-ts-ssr/README.md | 19 ++++++++++--------- .../_github/workflows/deploy.yaml | 9 +++++---- template-react-ts-ssr/package.json | 2 +- template-react-ts/README.md | 19 ++++++++++--------- .../_github/workflows/deploy.yaml | 9 +++++---- template-react-ts/package.json | 2 +- template-react/README.md | 19 ++++++++++--------- template-react/_github/workflows/deploy.yaml | 9 +++++---- template-react/package.json | 2 +- template-vanilla-ts/README.md | 19 ++++++++++--------- .../_github/workflows/deploy.yaml | 9 +++++---- template-vanilla-ts/package.json | 2 +- template-vanilla/README.md | 19 ++++++++++--------- .../_github/workflows/deploy.yaml | 9 +++++---- template-vanilla/package.json | 2 +- template-vue-ssr/README.md | 19 ++++++++++--------- .../_github/workflows/deploy.yaml | 9 +++++---- template-vue-ssr/package.json | 2 +- template-vue-ts-ssr/README.md | 19 ++++++++++--------- .../_github/workflows/deploy.yaml | 9 +++++---- template-vue-ts-ssr/package.json | 2 +- template-vue-ts/README.md | 19 ++++++++++--------- template-vue-ts/_github/workflows/deploy.yaml | 9 +++++---- template-vue-ts/package.json | 2 +- template-vue/README.md | 19 ++++++++++--------- template-vue/_github/workflows/deploy.yaml | 9 +++++---- template-vue/package.json | 2 +- template.tests/template.test.js | 4 +++- .../all/_github/workflows/deploy.yaml | 9 +++++---- 32 files changed, 168 insertions(+), 145 deletions(-) diff --git a/template-react-ssr/README.md b/template-react-ssr/README.md index 893ffe2..1da0137 100644 --- a/template-react-ssr/README.md +++ b/template-react-ssr/README.md @@ -103,7 +103,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -111,15 +111,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -127,7 +129,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -143,12 +145,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-react-ssr/_github/workflows/deploy.yaml b/template-react-ssr/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-react-ssr/_github/workflows/deploy.yaml +++ b/template-react-ssr/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-react-ssr/package.json b/template-react-ssr/package.json index 7946679..a80c3e1 100644 --- a/template-react-ssr/package.json +++ b/template-react-ssr/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-react-ts-ssr/README.md b/template-react-ts-ssr/README.md index 9af4e5f..eaeff6f 100644 --- a/template-react-ts-ssr/README.md +++ b/template-react-ts-ssr/README.md @@ -111,7 +111,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -119,15 +119,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -135,7 +137,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -151,12 +153,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-react-ts-ssr/_github/workflows/deploy.yaml b/template-react-ts-ssr/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-react-ts-ssr/_github/workflows/deploy.yaml +++ b/template-react-ts-ssr/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-react-ts-ssr/package.json b/template-react-ts-ssr/package.json index 665fa14..c16402f 100644 --- a/template-react-ts-ssr/package.json +++ b/template-react-ts-ssr/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-react-ts/README.md b/template-react-ts/README.md index 9af4e5f..eaeff6f 100644 --- a/template-react-ts/README.md +++ b/template-react-ts/README.md @@ -111,7 +111,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -119,15 +119,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -135,7 +137,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -151,12 +153,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-react-ts/_github/workflows/deploy.yaml b/template-react-ts/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-react-ts/_github/workflows/deploy.yaml +++ b/template-react-ts/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-react-ts/package.json b/template-react-ts/package.json index 665fa14..c16402f 100644 --- a/template-react-ts/package.json +++ b/template-react-ts/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-react/README.md b/template-react/README.md index 893ffe2..1da0137 100644 --- a/template-react/README.md +++ b/template-react/README.md @@ -103,7 +103,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -111,15 +111,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -127,7 +129,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -143,12 +145,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-react/_github/workflows/deploy.yaml b/template-react/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-react/_github/workflows/deploy.yaml +++ b/template-react/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-react/package.json b/template-react/package.json index 7946679..a80c3e1 100644 --- a/template-react/package.json +++ b/template-react/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-vanilla-ts/README.md b/template-vanilla-ts/README.md index 6556313..e899205 100644 --- a/template-vanilla-ts/README.md +++ b/template-vanilla-ts/README.md @@ -111,7 +111,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -119,15 +119,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -135,7 +137,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -151,12 +153,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vanilla-ts/_github/workflows/deploy.yaml b/template-vanilla-ts/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-vanilla-ts/_github/workflows/deploy.yaml +++ b/template-vanilla-ts/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-vanilla-ts/package.json b/template-vanilla-ts/package.json index a193ab2..852381f 100644 --- a/template-vanilla-ts/package.json +++ b/template-vanilla-ts/package.json @@ -12,7 +12,7 @@ "format": "prettier --write .", "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "devDependencies": { diff --git a/template-vanilla/README.md b/template-vanilla/README.md index d8a32ee..51bd5a9 100644 --- a/template-vanilla/README.md +++ b/template-vanilla/README.md @@ -103,7 +103,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -111,15 +111,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -127,7 +129,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -143,12 +145,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vanilla/_github/workflows/deploy.yaml b/template-vanilla/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-vanilla/_github/workflows/deploy.yaml +++ b/template-vanilla/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-vanilla/package.json b/template-vanilla/package.json index a193ab2..852381f 100644 --- a/template-vanilla/package.json +++ b/template-vanilla/package.json @@ -12,7 +12,7 @@ "format": "prettier --write .", "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "devDependencies": { diff --git a/template-vue-ssr/README.md b/template-vue-ssr/README.md index 893ffe2..1da0137 100644 --- a/template-vue-ssr/README.md +++ b/template-vue-ssr/README.md @@ -103,7 +103,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -111,15 +111,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -127,7 +129,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -143,12 +145,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vue-ssr/_github/workflows/deploy.yaml b/template-vue-ssr/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-vue-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ssr/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-vue-ssr/package.json b/template-vue-ssr/package.json index 9881a5b..3b6bdb5 100644 --- a/template-vue-ssr/package.json +++ b/template-vue-ssr/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-vue-ts-ssr/README.md b/template-vue-ts-ssr/README.md index 9af4e5f..eaeff6f 100644 --- a/template-vue-ts-ssr/README.md +++ b/template-vue-ts-ssr/README.md @@ -111,7 +111,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -119,15 +119,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -135,7 +137,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -151,12 +153,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vue-ts-ssr/_github/workflows/deploy.yaml b/template-vue-ts-ssr/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-vue-ts-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ts-ssr/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-vue-ts-ssr/package.json b/template-vue-ts-ssr/package.json index 55efd27..2ba8301 100644 --- a/template-vue-ts-ssr/package.json +++ b/template-vue-ts-ssr/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-vue-ts/README.md b/template-vue-ts/README.md index 9af4e5f..eaeff6f 100644 --- a/template-vue-ts/README.md +++ b/template-vue-ts/README.md @@ -111,7 +111,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -119,15 +119,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -135,7 +137,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -151,12 +153,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vue-ts/_github/workflows/deploy.yaml b/template-vue-ts/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-vue-ts/_github/workflows/deploy.yaml +++ b/template-vue-ts/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-vue-ts/package.json b/template-vue-ts/package.json index 55efd27..2ba8301 100644 --- a/template-vue-ts/package.json +++ b/template-vue-ts/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-vue/README.md b/template-vue/README.md index 893ffe2..1da0137 100644 --- a/template-vue/README.md +++ b/template-vue/README.md @@ -103,7 +103,7 @@ Take a look at the [default configuration](./config.yaml), which specifies how f ## Deployment -Deploy your app to a Harper cluster **by reference**: rather than uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. +Deploy your app to a Harper cluster **by reference**: instead of uploading a snapshot of your files, you tell Harper which commit of your GitHub repository to run, pinned by its exact commit SHA. Re-deploying the same commit is repeatable, and rolling back is just deploying an older commit. First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log in, and create a cluster. Then log your local CLI in to it: @@ -111,15 +111,17 @@ First, head to [https://fabric.harper.fast/](https://fabric.harper.fast/), log i harper login ``` -### One-time setup +### One-time setup (private repos) -So the cluster can clone your (private) repository, register a read-only SSH **deploy key**: +So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh npm run deploy:setup ``` -This generates the key, registers it with your cluster, and prints a public key to add to your repository under **Settings → Deploy keys**. A deploy key is scoped to this one repository and keeps working even if the person who created it leaves. (It uses Harper's `add_ssh_key`, available on Harper Pro / Fabric.) +This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. + +> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. ### Deploy @@ -127,7 +129,7 @@ This generates the key, registers it with your cluster, and prints a public key npm run deploy ``` -This deploys the current commit — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. +This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. ### Deploy automatically from CI @@ -143,12 +145,11 @@ Add these repository secrets first, under **Settings → Secrets and variables - `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) - `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy -### Other options +The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. -- **Public repository?** Skip the deploy key and let Harper clone over HTTPS (`git+https://…#`) with no cluster-side credentials. -- **Prefer to build in CI** instead of on your Harper nodes? Publish your app to a registry such as GitHub Packages and deploy it by version using `registryAuth`. +### Private npm dependencies -See the [Harper deployment docs](https://docs.harperdb.io/reference/v5/components/applications#deploy_component) for both. +If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vue/_github/workflows/deploy.yaml b/template-vue/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/template-vue/_github/workflows/deploy.yaml +++ b/template-vue/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: diff --git a/template-vue/package.json b/template-vue/package.json index 9881a5b..3b6bdb5 100644 --- a/template-vue/package.json +++ b/template-vue/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy by_ref=true restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template.tests/template.test.js b/template.tests/template.test.js index b6582d9..0079fa6 100644 --- a/template.tests/template.test.js +++ b/template.tests/template.test.js @@ -86,7 +86,9 @@ describe('Integration tests', () => { // (no per-project scripts). expect(fs.existsSync(path.join(targetDir, '.github', 'workflows', 'deploy.yaml'))).toBe(true); expect(fs.existsSync(path.join(targetDir, 'scripts', 'deploy.mjs'))).toBe(false); - expect(pkgJson.scripts.deploy).toBe('harper deploy by_ref=true restart=true replicated=true'); + expect(pkgJson.scripts.deploy).toBe( + 'harper deploy by_ref=true credential=github.com restart=true replicated=true', + ); expect(pkgJson.scripts['deploy:setup']).toBe('harper deploy setup=true'); }); } diff --git a/templates-shared/all/_github/workflows/deploy.yaml b/templates-shared/all/_github/workflows/deploy.yaml index c73ffd9..16e83aa 100644 --- a/templates-shared/all/_github/workflows/deploy.yaml +++ b/templates-shared/all/_github/workflows/deploy.yaml @@ -2,16 +2,17 @@ # (`npm run deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster -# fetches the tagged commit over SSH (pinned by its exact SHA, so peers can't diverge on a moved tag) -# using the read-only deploy key you registered once with `npm run deploy:setup`. +# fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a +# moved tag), authenticating a private clone with the encrypted token you sealed once via +# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 # HARPER_CLI_USERNAME a cluster user allowed to deploy # HARPER_CLI_PASSWORD that user's password # -# Public repo and want zero cluster-side auth, or prefer building in CI and publishing to a registry? -# See the "Deployment" section of the README for those alternatives. +# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: workflow_dispatch: From 47aacea9506bbec163a604a2e6b6b1e2472f1636 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Wed, 29 Jul 2026 14:31:49 -0400 Subject: [PATCH 04/11] refactor(deploy): align scaffolding with token-based CI auth and Next.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows main's Next.js templates and harper#1876's CI credential flow. Next.js stays on deploy-by-payload, deliberately. By-reference means the cluster clones the repo and builds on the node, which Next.js can't do: `.next` is gitignored, so a git reference carries no build output, and an on-cluster build currently fails outright (Turbopack crashes in a Harper worker thread, HarperFast/nextjs#57; a webpack build overruns the component-load timeout, HarperFast/nextjs#58). They keep `next build && harper deploy_component .` until those land. `applySharedTemplates.js` records why they're absent from the fan-out, so their omission reads as a decision rather than an oversight. They do get the `_github/workflow/` → `_github/workflows/` fix this PR makes everywhere else — GitHub only runs workflows from the plural directory, so the one they shipped never triggered — plus their own payload-deploy workflow with the same auth wiring. CI now authenticates with a token instead of a password. The workflows consume HARPER_CLI_TARGET + HARPER_CLI_REFRESH_TOKEN (harper#1876) rather than HARPER_CLI_USERNAME/HARPER_CLI_PASSWORD, and the docs point at the pipe that sets both without the token ever reaching the screen or shell history: harper login --for-ci | gh secret set --env-file - template.test.js asserts the deploy model per template, with the payload-based ones as an explicit allow-list — a newly added template fails the by-reference assertions until it's either wired up or added there with a reason, so it can't silently opt out. Verified by flipping the list: exactly the two Next.js templates fail, confirming the assertions actually run. Co-Authored-By: Claude Opus 4.8 --- template-nextjs-ts/README.md | 24 ++++++++ .../_github/workflow/deploy.yaml | 31 ---------- .../_github/workflows/deploy.yaml | 58 +++++++++++++++++++ template-nextjs/README.md | 24 ++++++++ template-nextjs/_github/workflow/deploy.yaml | 31 ---------- template-nextjs/_github/workflows/deploy.yaml | 58 +++++++++++++++++++ template-react-ssr/README.md | 12 +++- .../_github/workflows/deploy.yaml | 16 +++-- template-react-ts-ssr/README.md | 12 +++- .../_github/workflows/deploy.yaml | 16 +++-- template-react-ts/README.md | 12 +++- .../_github/workflows/deploy.yaml | 16 +++-- template-react/README.md | 12 +++- template-react/_github/workflows/deploy.yaml | 16 +++-- template-vanilla-ts/README.md | 12 +++- .../_github/workflows/deploy.yaml | 16 +++-- template-vanilla/README.md | 12 +++- .../_github/workflows/deploy.yaml | 16 +++-- template-vue-ssr/README.md | 12 +++- .../_github/workflows/deploy.yaml | 16 +++-- template-vue-ts-ssr/README.md | 12 +++- .../_github/workflows/deploy.yaml | 16 +++-- template-vue-ts/README.md | 12 +++- template-vue-ts/_github/workflows/deploy.yaml | 16 +++-- template-vue/README.md | 12 +++- template-vue/_github/workflows/deploy.yaml | 16 +++-- template.tests/template.test.js | 27 ++++++--- .../all/_github/workflows/deploy.yaml | 16 +++-- templates-shared/applySharedTemplates.js | 6 ++ 29 files changed, 400 insertions(+), 155 deletions(-) delete mode 100644 template-nextjs-ts/_github/workflow/deploy.yaml create mode 100644 template-nextjs-ts/_github/workflows/deploy.yaml delete mode 100644 template-nextjs/_github/workflow/deploy.yaml create mode 100644 template-nextjs/_github/workflows/deploy.yaml diff --git a/template-nextjs-ts/README.md b/template-nextjs-ts/README.md index 6f96d52..645dab1 100644 --- a/template-nextjs-ts/README.md +++ b/template-nextjs-ts/README.md @@ -72,6 +72,30 @@ npm run deploy `npm run deploy` runs `next build` locally and ships the prebuilt `.next` output, then Harper serves it — no build runs on the cluster. (Building on the cluster currently fails; see the note in [`config.yaml`](./config.yaml).) +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) builds and deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) + +> **Why this template deploys differently.** The other create-harper templates deploy _by reference_: the cluster clones your repo at a pinned commit and builds there. Next.js can't do that yet — `.next` is gitignored, so a git reference carries no build output, and an on-cluster build currently fails ([nextjs#57](https://github.com/HarperFast/nextjs/issues/57), [nextjs#58](https://github.com/HarperFast/nextjs/issues/58)). Until those land, this template uploads the build itself. + ## Keep Going! For more on building Harper applications, see the [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-nextjs-ts/_github/workflow/deploy.yaml b/template-nextjs-ts/_github/workflow/deploy.yaml deleted file mode 100644 index 2dc4dc5..0000000 --- a/template-nextjs-ts/_github/workflow/deploy.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-nextjs-ts/_github/workflows/deploy.yaml b/template-nextjs-ts/_github/workflows/deploy.yaml new file mode 100644 index 0000000..3a6e5c1 --- /dev/null +++ b/template-nextjs-ts/_github/workflows/deploy.yaml @@ -0,0 +1,58 @@ +# Deploys this app to your Harper cluster by *payload* via `harper deploy_component .` +# (`npm run deploy`), which runs `next build` first and uploads the prebuilt `.next` output. +# +# The other create-harper templates deploy by *reference* (`harper deploy by_ref=true`), where the +# cluster clones your repo and builds on the node. Next.js can't use that yet: `.next` is gitignored +# (so a git reference carries no build output) and building on the cluster currently fails — +# Turbopack crashes inside a Harper worker thread (HarperFast/nextjs#57) and a webpack build +# overruns the component-load timeout (HarperFast/nextjs#58). Once those land, this template can +# switch to the by-reference workflow like the rest. +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run lint + run: npm run lint + - name: Install Harper CLI + # Pinned to the 5.2 line; ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 + - name: Build & deploy + run: npm run deploy + env: + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-nextjs/README.md b/template-nextjs/README.md index f274945..a1a7ca1 100644 --- a/template-nextjs/README.md +++ b/template-nextjs/README.md @@ -72,6 +72,30 @@ npm run deploy `npm run deploy` runs `next build` locally and ships the prebuilt `.next` output, then Harper serves it — no build runs on the cluster. (Building on the cluster currently fails; see the note in [`config.yaml`](./config.yaml).) +### Deploy automatically from CI + +The included [GitHub Actions workflow](./.github/workflows/deploy.yaml) builds and deploys whenever you push a version tag: + +```sh +git tag v1.0.0 +git push --tags +``` + +Add these repository secrets first, under **Settings → Secrets and variables → Actions**: + +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) + +> **Why this template deploys differently.** The other create-harper templates deploy _by reference_: the cluster clones your repo at a pinned commit and builds there. Next.js can't do that yet — `.next` is gitignored, so a git reference carries no build output, and an on-cluster build currently fails ([nextjs#57](https://github.com/HarperFast/nextjs/issues/57), [nextjs#58](https://github.com/HarperFast/nextjs/issues/58)). Until those land, this template uploads the build itself. + ## Keep Going! For more on building Harper applications, see the [getting started guide](https://docs.harperdb.io/docs). diff --git a/template-nextjs/_github/workflow/deploy.yaml b/template-nextjs/_github/workflow/deploy.yaml deleted file mode 100644 index 2dc4dc5..0000000 --- a/template-nextjs/_github/workflow/deploy.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: Deploy to Harper Fabric -on: - workflow_dispatch: -# push: -# branches: -# - main - -concurrency: - group: main - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 0 - fetch-tags: true - - name: Set up Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 - with: - cache: 'npm' - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Run lint - run: npm run lint - - name: Build & deploy - run: npm run deploy diff --git a/template-nextjs/_github/workflows/deploy.yaml b/template-nextjs/_github/workflows/deploy.yaml new file mode 100644 index 0000000..3a6e5c1 --- /dev/null +++ b/template-nextjs/_github/workflows/deploy.yaml @@ -0,0 +1,58 @@ +# Deploys this app to your Harper cluster by *payload* via `harper deploy_component .` +# (`npm run deploy`), which runs `next build` first and uploads the prebuilt `.next` output. +# +# The other create-harper templates deploy by *reference* (`harper deploy by_ref=true`), where the +# cluster clones your repo and builds on the node. Next.js can't use that yet: `.next` is gitignored +# (so a git reference carries no build output) and building on the cluster currently fails — +# Turbopack crashes inside a Harper worker thread (HarperFast/nextjs#57) and a webpack build +# overruns the component-load timeout (HarperFast/nextjs#58). Once those land, this template can +# switch to the by-reference workflow like the rest. +# +# Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - +name: Deploy to Harper Fabric +on: + workflow_dispatch: + push: + tags: + - 'v*' + +concurrency: + group: deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + cache: 'npm' + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci + - name: Run lint + run: npm run lint + - name: Install Harper CLI + # Pinned to the 5.2 line; ^5.2.0 blocks a breaking 6.x bump. + run: npm install -g harper@^5.2.0 + - name: Build & deploy + run: npm run deploy + env: + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-react-ssr/README.md b/template-react-ssr/README.md index 1da0137..85ba10d 100644 --- a/template-react-ssr/README.md +++ b/template-react-ssr/README.md @@ -142,8 +142,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-react-ssr/_github/workflows/deploy.yaml b/template-react-ssr/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-react-ssr/_github/workflows/deploy.yaml +++ b/template-react-ssr/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-react-ts-ssr/README.md b/template-react-ts-ssr/README.md index eaeff6f..421c54c 100644 --- a/template-react-ts-ssr/README.md +++ b/template-react-ts-ssr/README.md @@ -150,8 +150,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-react-ts-ssr/_github/workflows/deploy.yaml b/template-react-ts-ssr/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-react-ts-ssr/_github/workflows/deploy.yaml +++ b/template-react-ts-ssr/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-react-ts/README.md b/template-react-ts/README.md index eaeff6f..421c54c 100644 --- a/template-react-ts/README.md +++ b/template-react-ts/README.md @@ -150,8 +150,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-react-ts/_github/workflows/deploy.yaml b/template-react-ts/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-react-ts/_github/workflows/deploy.yaml +++ b/template-react-ts/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-react/README.md b/template-react/README.md index 1da0137..85ba10d 100644 --- a/template-react/README.md +++ b/template-react/README.md @@ -142,8 +142,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-react/_github/workflows/deploy.yaml b/template-react/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-react/_github/workflows/deploy.yaml +++ b/template-react/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vanilla-ts/README.md b/template-vanilla-ts/README.md index e899205..6d7b7d0 100644 --- a/template-vanilla-ts/README.md +++ b/template-vanilla-ts/README.md @@ -150,8 +150,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-vanilla-ts/_github/workflows/deploy.yaml b/template-vanilla-ts/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-vanilla-ts/_github/workflows/deploy.yaml +++ b/template-vanilla-ts/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vanilla/README.md b/template-vanilla/README.md index 51bd5a9..6161abc 100644 --- a/template-vanilla/README.md +++ b/template-vanilla/README.md @@ -142,8 +142,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-vanilla/_github/workflows/deploy.yaml b/template-vanilla/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-vanilla/_github/workflows/deploy.yaml +++ b/template-vanilla/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vue-ssr/README.md b/template-vue-ssr/README.md index 1da0137..85ba10d 100644 --- a/template-vue-ssr/README.md +++ b/template-vue-ssr/README.md @@ -142,8 +142,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-vue-ssr/_github/workflows/deploy.yaml b/template-vue-ssr/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-vue-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ssr/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vue-ts-ssr/README.md b/template-vue-ts-ssr/README.md index eaeff6f..421c54c 100644 --- a/template-vue-ts-ssr/README.md +++ b/template-vue-ts-ssr/README.md @@ -150,8 +150,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-vue-ts-ssr/_github/workflows/deploy.yaml b/template-vue-ts-ssr/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-vue-ts-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ts-ssr/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vue-ts/README.md b/template-vue-ts/README.md index eaeff6f..421c54c 100644 --- a/template-vue-ts/README.md +++ b/template-vue-ts/README.md @@ -150,8 +150,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-vue-ts/_github/workflows/deploy.yaml b/template-vue-ts/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-vue-ts/_github/workflows/deploy.yaml +++ b/template-vue-ts/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vue/README.md b/template-vue/README.md index 1da0137..85ba10d 100644 --- a/template-vue/README.md +++ b/template-vue/README.md @@ -142,8 +142,16 @@ git push --tags Add these repository secrets first, under **Settings → Secrets and variables → Actions**: -- `CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) -- `HARPER_CLI_USERNAME` and `HARPER_CLI_PASSWORD` — a cluster user allowed to deploy +- `HARPER_CLI_TARGET` — your cluster's operations URL (e.g. `https://your-cluster.harperdb.io:9925`) +- `HARPER_CLI_REFRESH_TOKEN` — a long-lived token CI authenticates with, so no password is stored + +Set both in one command — this pipes the credentials straight from your cluster into GitHub, so the token never appears on screen or in your shell history: + +```sh +harper login --for-ci | gh secret set --env-file - +``` + +(No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. diff --git a/template-vue/_github/workflows/deploy.yaml b/template-vue/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/template-vue/_github/workflows/deploy.yaml +++ b/template-vue/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template.tests/template.test.js b/template.tests/template.test.js index 0079fa6..663fd53 100644 --- a/template.tests/template.test.js +++ b/template.tests/template.test.js @@ -8,6 +8,11 @@ const root = path.resolve(import.meta.dirname, '..'); const cliPath = path.resolve(root, 'index.js'); const tempDir = path.resolve(root, '.temp-integration-tests'); +// Templates that still deploy by payload rather than by reference. Kept as an explicit list so +// adding a template can't silently opt out of deploy-by-reference — a new one fails the by-ref +// assertions below until it's either wired up or added here with a reason. +const PAYLOAD_DEPLOY_TEMPLATES = new Set(['nextjs', 'nextjs-ts']); + describe('Integration tests', () => { beforeAll(() => { if (fs.existsSync(tempDir)) { @@ -81,15 +86,23 @@ describe('Integration tests', () => { expect(fs.existsSync(path.join(targetDir, '.env.example'))).toBe(true); } - // Deploy-by-reference scaffolding: the workflow must live under `.github/workflows/` - // (plural — GitHub only runs workflows there), and deploy is driven by the native harper CLI - // (no per-project scripts). + // The deploy workflow must live under `.github/workflows/` (plural — GitHub only runs + // workflows there; the singular `workflow/` these templates used to ship never triggered). expect(fs.existsSync(path.join(targetDir, '.github', 'workflows', 'deploy.yaml'))).toBe(true); + // Deploy is driven by the native harper CLI, never a per-project script. expect(fs.existsSync(path.join(targetDir, 'scripts', 'deploy.mjs'))).toBe(false); - expect(pkgJson.scripts.deploy).toBe( - 'harper deploy by_ref=true credential=github.com restart=true replicated=true', - ); - expect(pkgJson.scripts['deploy:setup']).toBe('harper deploy setup=true'); + + if (PAYLOAD_DEPLOY_TEMPLATES.has(template)) { + // Next.js deploys by payload: `.next` is gitignored, so a git reference carries no + // build output, and building on the cluster fails (HarperFast/nextjs#57, #58). + expect(pkgJson.scripts.deploy).toBe('next build && harper deploy_component . restart=true replicated=true'); + expect(pkgJson.scripts['deploy:setup']).toBeUndefined(); + } else { + expect(pkgJson.scripts.deploy).toBe( + 'harper deploy by_ref=true credential=github.com restart=true replicated=true', + ); + expect(pkgJson.scripts['deploy:setup']).toBe('harper deploy setup=true'); + } }); } }); diff --git a/templates-shared/all/_github/workflows/deploy.yaml b/templates-shared/all/_github/workflows/deploy.yaml index 16e83aa..ecf1647 100644 --- a/templates-shared/all/_github/workflows/deploy.yaml +++ b/templates-shared/all/_github/workflows/deploy.yaml @@ -7,9 +7,14 @@ # `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): -# CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 -# HARPER_CLI_USERNAME a cluster user allowed to deploy -# HARPER_CLI_PASSWORD that user's password +# HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 +# HARPER_CLI_REFRESH_TOKEN a long-lived refresh token; the CLI mints a short-lived operation +# token from it on each run, so no password is stored in CI +# +# Set both in one command — this pipes them straight from your cluster into GitHub, so the token +# never appears on screen or in your shell history: +# +# harper login --for-ci | gh secret set --env-file - # # Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. @@ -50,6 +55,5 @@ jobs: - name: Deploy run: npm run deploy env: - CLI_TARGET: ${{ secrets.CLI_TARGET }} - HARPER_CLI_USERNAME: ${{ secrets.HARPER_CLI_USERNAME }} - HARPER_CLI_PASSWORD: ${{ secrets.HARPER_CLI_PASSWORD }} + HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} + HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/templates-shared/applySharedTemplates.js b/templates-shared/applySharedTemplates.js index 7efb867..af588ca 100644 --- a/templates-shared/applySharedTemplates.js +++ b/templates-shared/applySharedTemplates.js @@ -25,6 +25,12 @@ import { copyDir } from '../lib/fs/copyDir.js'; // `harper` CLI. It still gets every other shared file. const excludeGithubForEarlyHints = (src) => !src.split(path.sep).includes('_github'); + // The Next.js templates are absent from the list above on purpose. They deploy by *payload* + // (`next build && harper deploy_component .`) because `.next` is gitignored — so a git + // reference carries no build output — and building on the cluster currently fails + // (HarperFast/nextjs#57, #58). They keep their own payload deploy workflow; once those issues + // land they can join this fan-out and switch to deploy-by-reference like the rest. + for (const key in copiesToMake) { const fromShared = path.resolve(import.meta.dirname, key); for (const targetTemplate of copiesToMake[key]) { From f1cc94938f25ed37a7c7830ad3f312ff76a05a57 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Wed, 12 Aug 2026 18:18:43 -0400 Subject: [PATCH 05/11] chore(templates): scaffold credential=true instead of naming the host harper#1850 now derives the credential host from the package being deployed, so naming it is redundant: `credential=true` is the documented form, and an explicit host is accepted only when it matches the package's. Replaces every occurrence across the 10 by-reference templates (deploy script, README note, workflow comment), the shared workflow source, and the template test that pins the deploy script string. The Next.js templates are unaffected - they deploy by payload and carry no credential. Co-Authored-By: Claude Opus 5 --- template-react-ssr/README.md | 2 +- template-react-ssr/_github/workflows/deploy.yaml | 2 +- template-react-ssr/package.json | 2 +- template-react-ts-ssr/README.md | 2 +- template-react-ts-ssr/_github/workflows/deploy.yaml | 2 +- template-react-ts-ssr/package.json | 2 +- template-react-ts/README.md | 2 +- template-react-ts/_github/workflows/deploy.yaml | 2 +- template-react-ts/package.json | 2 +- template-react/README.md | 2 +- template-react/_github/workflows/deploy.yaml | 2 +- template-react/package.json | 2 +- template-vanilla-ts/README.md | 2 +- template-vanilla-ts/_github/workflows/deploy.yaml | 2 +- template-vanilla-ts/package.json | 2 +- template-vanilla/README.md | 2 +- template-vanilla/_github/workflows/deploy.yaml | 2 +- template-vanilla/package.json | 2 +- template-vue-ssr/README.md | 2 +- template-vue-ssr/_github/workflows/deploy.yaml | 2 +- template-vue-ssr/package.json | 2 +- template-vue-ts-ssr/README.md | 2 +- template-vue-ts-ssr/_github/workflows/deploy.yaml | 2 +- template-vue-ts-ssr/package.json | 2 +- template-vue-ts/README.md | 2 +- template-vue-ts/_github/workflows/deploy.yaml | 2 +- template-vue-ts/package.json | 2 +- template-vue/README.md | 2 +- template-vue/_github/workflows/deploy.yaml | 2 +- template-vue/package.json | 2 +- template.tests/template.test.js | 2 +- templates-shared/all/_github/workflows/deploy.yaml | 2 +- 32 files changed, 32 insertions(+), 32 deletions(-) diff --git a/template-react-ssr/README.md b/template-react-ssr/README.md index 85ba10d..c0c66e4 100644 --- a/template-react-ssr/README.md +++ b/template-react-ssr/README.md @@ -121,7 +121,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-react-ssr/_github/workflows/deploy.yaml b/template-react-ssr/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-react-ssr/_github/workflows/deploy.yaml +++ b/template-react-ssr/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-react-ssr/package.json b/template-react-ssr/package.json index a80c3e1..d705813 100644 --- a/template-react-ssr/package.json +++ b/template-react-ssr/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-react-ts-ssr/README.md b/template-react-ts-ssr/README.md index 421c54c..53cf140 100644 --- a/template-react-ts-ssr/README.md +++ b/template-react-ts-ssr/README.md @@ -129,7 +129,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-react-ts-ssr/_github/workflows/deploy.yaml b/template-react-ts-ssr/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-react-ts-ssr/_github/workflows/deploy.yaml +++ b/template-react-ts-ssr/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-react-ts-ssr/package.json b/template-react-ts-ssr/package.json index c16402f..463708e 100644 --- a/template-react-ts-ssr/package.json +++ b/template-react-ts-ssr/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-react-ts/README.md b/template-react-ts/README.md index 421c54c..53cf140 100644 --- a/template-react-ts/README.md +++ b/template-react-ts/README.md @@ -129,7 +129,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-react-ts/_github/workflows/deploy.yaml b/template-react-ts/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-react-ts/_github/workflows/deploy.yaml +++ b/template-react-ts/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-react-ts/package.json b/template-react-ts/package.json index c16402f..463708e 100644 --- a/template-react-ts/package.json +++ b/template-react-ts/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-react/README.md b/template-react/README.md index 85ba10d..c0c66e4 100644 --- a/template-react/README.md +++ b/template-react/README.md @@ -121,7 +121,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-react/_github/workflows/deploy.yaml b/template-react/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-react/_github/workflows/deploy.yaml +++ b/template-react/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-react/package.json b/template-react/package.json index a80c3e1..d705813 100644 --- a/template-react/package.json +++ b/template-react/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-vanilla-ts/README.md b/template-vanilla-ts/README.md index 6d7b7d0..9769232 100644 --- a/template-vanilla-ts/README.md +++ b/template-vanilla-ts/README.md @@ -129,7 +129,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-vanilla-ts/_github/workflows/deploy.yaml b/template-vanilla-ts/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-vanilla-ts/_github/workflows/deploy.yaml +++ b/template-vanilla-ts/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-vanilla-ts/package.json b/template-vanilla-ts/package.json index 852381f..769c149 100644 --- a/template-vanilla-ts/package.json +++ b/template-vanilla-ts/package.json @@ -12,7 +12,7 @@ "format": "prettier --write .", "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "devDependencies": { diff --git a/template-vanilla/README.md b/template-vanilla/README.md index 6161abc..8cd6be0 100644 --- a/template-vanilla/README.md +++ b/template-vanilla/README.md @@ -121,7 +121,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-vanilla/_github/workflows/deploy.yaml b/template-vanilla/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-vanilla/_github/workflows/deploy.yaml +++ b/template-vanilla/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-vanilla/package.json b/template-vanilla/package.json index 852381f..769c149 100644 --- a/template-vanilla/package.json +++ b/template-vanilla/package.json @@ -12,7 +12,7 @@ "format": "prettier --write .", "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "devDependencies": { diff --git a/template-vue-ssr/README.md b/template-vue-ssr/README.md index 85ba10d..c0c66e4 100644 --- a/template-vue-ssr/README.md +++ b/template-vue-ssr/README.md @@ -121,7 +121,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-vue-ssr/_github/workflows/deploy.yaml b/template-vue-ssr/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-vue-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ssr/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-vue-ssr/package.json b/template-vue-ssr/package.json index 3b6bdb5..791dfca 100644 --- a/template-vue-ssr/package.json +++ b/template-vue-ssr/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-vue-ts-ssr/README.md b/template-vue-ts-ssr/README.md index 421c54c..53cf140 100644 --- a/template-vue-ts-ssr/README.md +++ b/template-vue-ts-ssr/README.md @@ -129,7 +129,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-vue-ts-ssr/_github/workflows/deploy.yaml b/template-vue-ts-ssr/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-vue-ts-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ts-ssr/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-vue-ts-ssr/package.json b/template-vue-ts-ssr/package.json index 2ba8301..c9b95f8 100644 --- a/template-vue-ts-ssr/package.json +++ b/template-vue-ts-ssr/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-vue-ts/README.md b/template-vue-ts/README.md index 421c54c..53cf140 100644 --- a/template-vue-ts/README.md +++ b/template-vue-ts/README.md @@ -129,7 +129,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-vue-ts/_github/workflows/deploy.yaml b/template-vue-ts/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-vue-ts/_github/workflows/deploy.yaml +++ b/template-vue-ts/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-vue-ts/package.json b/template-vue-ts/package.json index 2ba8301..c9b95f8 100644 --- a/template-vue-ts/package.json +++ b/template-vue-ts/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.ts", "test:watch": "node --watch --test test/*.test.ts", "build": "vite build", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template-vue/README.md b/template-vue/README.md index 85ba10d..c0c66e4 100644 --- a/template-vue/README.md +++ b/template-vue/README.md @@ -121,7 +121,7 @@ npm run deploy:setup This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. -> Public repo? Skip this step and drop `credential=github.com` from the `deploy` script — no credential is needed. +> Public repo? Skip this step and drop `credential=true` from the `deploy` script — no credential is needed. ### Deploy diff --git a/template-vue/_github/workflows/deploy.yaml b/template-vue/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/template-vue/_github/workflows/deploy.yaml +++ b/template-vue/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: diff --git a/template-vue/package.json b/template-vue/package.json index 3b6bdb5..791dfca 100644 --- a/template-vue/package.json +++ b/template-vue/package.json @@ -13,7 +13,7 @@ "test": "node --test test/*.test.js", "test:watch": "node --watch --test test/*.test.js", "build": "vite build", - "deploy": "harper deploy by_ref=true credential=github.com restart=true replicated=true", + "deploy": "harper deploy by_ref=true credential=true restart=true replicated=true", "deploy:setup": "harper deploy setup=true" }, "dependencies": { diff --git a/template.tests/template.test.js b/template.tests/template.test.js index 663fd53..40deb9e 100644 --- a/template.tests/template.test.js +++ b/template.tests/template.test.js @@ -99,7 +99,7 @@ describe('Integration tests', () => { expect(pkgJson.scripts['deploy:setup']).toBeUndefined(); } else { expect(pkgJson.scripts.deploy).toBe( - 'harper deploy by_ref=true credential=github.com restart=true replicated=true', + 'harper deploy by_ref=true credential=true restart=true replicated=true', ); expect(pkgJson.scripts['deploy:setup']).toBe('harper deploy setup=true'); } diff --git a/templates-shared/all/_github/workflows/deploy.yaml b/templates-shared/all/_github/workflows/deploy.yaml index ecf1647..96a8d56 100644 --- a/templates-shared/all/_github/workflows/deploy.yaml +++ b/templates-shared/all/_github/workflows/deploy.yaml @@ -16,7 +16,7 @@ # # harper login --for-ci | gh secret set --env-file - # -# Public repo? Drop `credential=github.com` from the `deploy` script — no cluster-side auth needed. +# Public repo? Drop `credential=true` from the `deploy` script — no cluster-side auth needed. # See the "Deployment" section of the README for details. name: Deploy to Harper Fabric on: From 542c344b4c81eafb0df2ef866d9abba664c0f185 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Fri, 14 Aug 2026 10:21:49 -0400 Subject: [PATCH 06/11] fix(templates): generate deploy workflows for the detected package manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scaffolded deploy workflows hard-coded npm, but create-harper installs with whichever package manager invoked it. A project created by `pnpm create harper` commits pnpm-lock.yaml, against which setup-node's npm cache cannot resolve a package lock and `npm ci` then fails — so the tag workflow died before it ever reached tests or deploy. Same for Yarn, Bun and Deno. The workflows now carry placeholders that scaffolding resolves from the detected package manager: its setup step (pinned to the version that wrote the project's lockfile), setup-node's `cache:` input, the lockfile-respecting install command, and the run-script prefix. Both standalone Next.js payload workflows get the same treatment, since they sit outside the shared fan-out. Template READMEs follow suit — they already disagreed with the CLI's own closing 'now run' hint. Placeholders that stand in for a whole line are written as YAML comments, so the committed workflows stay valid, formattable YAML. Notes: - The Harper CLI install stays npm: it is a global tool and npm always ships with Node.js. - Yarn picks `--immutable` or `--frozen-lockfile` by major, since 2.0 dropped the latter. - Bun and Deno get no setup-node cache; setup-node supports npm, Yarn and pnpm only, and their own setup actions cache for them. - The Studio build resolves the placeholders to npm and no longer ships `.github` at all: Studio deploys from Studio, and it empties `scripts`, so the workflow had nothing to run. template.tests/deployWorkflow.test.js gates this in CI — template.tests/template.test.js is still dormant, so the new file is wired into vitest.config.js directly. It asserts no template workflow hard-codes a package manager, and scaffolds under pnpm and Bun user agents to check the generated workflows. Verified both halves fail when the logic or a template regresses. Co-Authored-By: Claude Opus 5 --- lib/init.js | 2 +- lib/init.test.js | 2 + lib/pkg/getWorkflowSubstitutions.js | 178 ++++++++++++++++++ lib/pkg/getWorkflowSubstitutions.test.js | 120 ++++++++++++ lib/steps/scaffoldProject.js | 11 +- lib/steps/scaffoldProject.test.js | 7 +- template-nextjs-ts/README.md | 6 +- .../_github/workflows/deploy.yaml | 16 +- template-nextjs/README.md | 6 +- template-nextjs/_github/workflows/deploy.yaml | 16 +- template-react-ssr/README.md | 10 +- .../_github/workflows/deploy.yaml | 20 +- template-react-ts-ssr/README.md | 10 +- .../_github/workflows/deploy.yaml | 20 +- template-react-ts/README.md | 10 +- .../_github/workflows/deploy.yaml | 20 +- template-react/README.md | 10 +- template-react/_github/workflows/deploy.yaml | 20 +- template-vanilla-ts/README.md | 10 +- .../_github/workflows/deploy.yaml | 20 +- template-vanilla/README.md | 10 +- .../_github/workflows/deploy.yaml | 20 +- template-vue-ssr/README.md | 10 +- .../_github/workflows/deploy.yaml | 20 +- template-vue-ts-ssr/README.md | 10 +- .../_github/workflows/deploy.yaml | 20 +- template-vue-ts/README.md | 10 +- template-vue-ts/_github/workflows/deploy.yaml | 20 +- template-vue/README.md | 10 +- template-vue/_github/workflows/deploy.yaml | 20 +- template.tests/deployWorkflow.test.js | 145 ++++++++++++++ .../all/_github/workflows/deploy.yaml | 20 +- templates-studio/buildStudioTemplates.js | 21 ++- vitest.config.js | 1 + 34 files changed, 684 insertions(+), 167 deletions(-) create mode 100644 lib/pkg/getWorkflowSubstitutions.js create mode 100644 lib/pkg/getWorkflowSubstitutions.test.js create mode 100644 template.tests/deployWorkflow.test.js diff --git a/lib/init.js b/lib/init.js index 549a271..0918d78 100644 --- a/lib/init.js +++ b/lib/init.js @@ -76,7 +76,7 @@ export async function init() { // Write out the contents based on all prior steps. const cwd = process.cwd(); const root = path.join(cwd, targetDir); - scaffoldProject(root, projectName, packageName, template, envVars, pkgManager); + scaffoldProject(root, projectName, packageName, template, envVars, pkgManager, pkgInfo?.version); // Log out the next steps. installAndOptionallyStart(root, pkgManager, immediate, args.skipInstall, selectedSkills, selectedAgents); diff --git a/lib/init.test.js b/lib/init.test.js index cb228d1..03e7cb8 100644 --- a/lib/init.test.js +++ b/lib/init.test.js @@ -124,6 +124,8 @@ describe('init.js', () => { 'vanilla', { target: 't' }, expect.any(String), + // The package manager's version, which pins it in the scaffolded CI workflows. + expect.any(String), ); expect(installAndOptionallyStart).toHaveBeenCalledWith( expect.stringContaining('my-dir'), diff --git a/lib/pkg/getWorkflowSubstitutions.js b/lib/pkg/getWorkflowSubstitutions.js new file mode 100644 index 0000000..7b3b999 --- /dev/null +++ b/lib/pkg/getWorkflowSubstitutions.js @@ -0,0 +1,178 @@ +// Indentation of the scaffolded GitHub Actions workflows (2-space YAML, so a `steps:` entry's +// `-` sits six columns in). Each multi-line value below replaces a placeholder comment that +// already sits at that indent, so a value's *first* line carries no indentation and every +// continuation line carries it explicitly. +const STEP_INDENT = ' '.repeat(6); +const KEY_INDENT = ' '.repeat(2); +const INPUT_INDENT = ' '.repeat(4); + +/** + * Builds one `steps:` entry, or a bare comment when a package manager needs no setup step. + * + * @param {{name?: string, comment?: string[], uses?: string, inputs?: Record}} step + * @returns {string} - The step's YAML, indented for substitution into the workflow. + */ +function buildStep({ name, comment, uses, inputs }) { + const lines = []; + if (name) { lines.push(`- name: ${name}`); } + for (const line of comment ?? []) { + // A comment-only value stands in for a step, so it starts at the step indent; a comment + // documenting a step is nested with that step's other keys. + lines.push(`${name ? KEY_INDENT : ''}# ${line}`); + } + if (uses) { lines.push(`${KEY_INDENT}uses: ${uses}`); } + if (inputs) { + lines.push(`${KEY_INDENT}with:`); + for (const [input, value] of Object.entries(inputs)) { + lines.push(`${INPUT_INDENT}${input}: ${value}`); + } + } + return lines.join(`\n${STEP_INDENT}`); +} + +/** + * Extracts the major version from a package manager version string. + * + * @param {string | undefined} version - A version such as '4.9.1'. + * @returns {number | undefined} - The major version, or undefined if it can't be determined. + */ +function majorVersion(version) { + const major = Number.parseInt(version ?? '', 10); + return Number.isNaN(major) ? undefined : major; +} + +/** + * Builds the step that puts the project's package manager on PATH, pinned to the version that + * generated its lockfile. npm and Yarn need none — npm ships with Node.js, and Yarn is + * preinstalled on GitHub's Ubuntu runners — so they get a comment saying so instead. + * + * @param {string} agent - The package manager agent ('npm', 'pnpm', 'yarn', 'bun' or 'deno'). + * @param {string} [version] - The agent's version, as reported by the user agent that invoked us. + * @returns {string} - The step's YAML. + */ +function getSetupStep(agent, version) { + switch (agent) { + case 'pnpm': + return buildStep({ + name: 'Set up pnpm', + comment: [ + "Pinned to the pnpm that wrote this project's lockfile. The action is SHA-pinned, but a", + 'floating `version:` would still let it self-install an unvetted pnpm at run time.', + ], + uses: 'pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9', + inputs: { version: version ?? 'latest' }, + }); + case 'bun': + return buildStep({ + name: 'Set up Bun', + comment: ["Pinned to the Bun that wrote this project's lockfile."], + uses: 'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0', + inputs: { 'bun-version': version ?? 'latest' }, + }); + case 'deno': + return buildStep({ + name: 'Set up Deno', + comment: ["Pinned to the Deno that wrote this project's lockfile."], + uses: 'denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5', + inputs: { 'deno-version': version ?? 'vx.x.x' }, + }); + case 'yarn': + return buildStep({ + comment: [ + "Yarn is preinstalled on GitHub's Ubuntu runners, so it needs no setup step. On Yarn 2+,", + 'commit `.yarnrc.yml` with a `yarnPath` (`yarn set version`) so CI runs the same Yarn that', + "wrote yarn.lock rather than the runner's Yarn 1.x.", + ], + }); + default: + return buildStep({ comment: ['npm ships with Node.js, so it needs no setup step.'] }); + } +} + +/** + * Builds `actions/setup-node`'s `cache:` input. Only npm, Yarn and pnpm are supported there; + * Bun and Deno cache through their own setup actions, so they get a comment explaining the gap + * rather than an input setup-node would reject. + * + * @param {string} agent - The package manager agent ('npm', 'pnpm', 'yarn', 'bun' or 'deno'). + * @returns {string} - The `cache:` input, or a comment. + */ +function getNodeCacheInput(agent) { + switch (agent) { + case 'bun': + return "# setup-node caches npm, Yarn and pnpm only; oven-sh/setup-bun caches Bun's store itself."; + case 'deno': + return '# setup-node caches npm, Yarn and pnpm only; denoland/setup-deno caches DENO_DIR itself.'; + case 'pnpm': + case 'yarn': + return `cache: '${agent}'`; + default: + return "cache: 'npm'"; + } +} + +/** + * Gets the lockfile-respecting install command for CI, which must fail rather than update the + * lockfile when it has drifted from package.json. + * + * @param {string} agent - The package manager agent ('npm', 'pnpm', 'yarn', 'bun' or 'deno'). + * @param {string} [version] - The agent's version, as reported by the user agent that invoked us. + * @returns {string} - The install command. + */ +function getCiInstallCommand(agent, version) { + switch (agent) { + case 'pnpm': + case 'bun': + return `${agent} install --frozen-lockfile`; + case 'yarn': + // Yarn renamed the flag in 2.0; Yarn 1 rejects `--immutable` and Yarn 2+ rejects + // `--frozen-lockfile`, so pick by the version that scaffolded the project. + return (majorVersion(version) ?? 1) >= 2 ? 'yarn install --immutable' : 'yarn install --frozen-lockfile'; + case 'deno': + return 'deno install --frozen'; + default: + return 'npm ci'; + } +} + +/** + * Gets the command prefix that runs a package.json script, e.g. `npm run` in `npm run deploy`. + * + * @param {string} agent - The package manager agent ('npm', 'pnpm', 'yarn', 'bun' or 'deno'). + * @returns {string} - The prefix, without a trailing space. + */ +function getRunScriptPrefix(agent) { + switch (agent) { + case 'deno': + return 'deno task'; + case 'pnpm': + case 'yarn': + case 'bun': + return `${agent} run`; + default: + return 'npm run'; + } +} + +/** + * Builds the substitutions that adapt a scaffolded project's GitHub Actions workflows to the + * package manager that invoked us. Without them the workflows would hard-code npm and fail for + * everyone else: setup-node can't resolve a package lock for a project whose lockfile is + * `pnpm-lock.yaml`, and `npm ci` errors out before the job ever reaches tests or deploy. + * + * Placeholders that stand in for a whole line are written as YAML comments in the templates, so + * the committed workflows stay valid, formattable YAML; the indentation contract for their + * multi-line replacements lives in this module. + * + * @param {string} agent - The package manager agent ('npm', 'pnpm', 'yarn', 'bun' or 'deno'). + * @param {string} [version] - The agent's version, as reported by the user agent that invoked us. + * @returns {Record} - A mapping of placeholder to replacement. + */ +export function getWorkflowSubstitutions(agent, version) { + return { + '# your-package-manager-setup-step-here': getSetupStep(agent, version), + '# your-package-manager-node-cache-here': getNodeCacheInput(agent), + 'your-package-manager-install-here': getCiInstallCommand(agent, version), + 'your-package-manager-run-here': getRunScriptPrefix(agent), + }; +} diff --git a/lib/pkg/getWorkflowSubstitutions.test.js b/lib/pkg/getWorkflowSubstitutions.test.js new file mode 100644 index 0000000..4eb1a55 --- /dev/null +++ b/lib/pkg/getWorkflowSubstitutions.test.js @@ -0,0 +1,120 @@ +import { describe, expect, test } from 'vitest'; +import { getWorkflowSubstitutions } from './getWorkflowSubstitutions.js'; + +const SETUP_STEP = '# your-package-manager-setup-step-here'; +const NODE_CACHE = '# your-package-manager-node-cache-here'; +const INSTALL = 'your-package-manager-install-here'; +const RUN = 'your-package-manager-run-here'; + +describe(getWorkflowSubstitutions, () => { + test('substitutes every placeholder the scaffolded workflows carry', () => { + expect(Object.keys(getWorkflowSubstitutions('npm', '10.9.0'))).toEqual([ + SETUP_STEP, + NODE_CACHE, + INSTALL, + RUN, + ]); + }); + + describe('npm', () => { + test('needs no setup step, and caches and installs through npm', () => { + const substitutions = getWorkflowSubstitutions('npm', '10.9.0'); + + expect(substitutions[SETUP_STEP]).toBe('# npm ships with Node.js, so it needs no setup step.'); + expect(substitutions[NODE_CACHE]).toBe("cache: 'npm'"); + expect(substitutions[INSTALL]).toBe('npm ci'); + expect(substitutions[RUN]).toBe('npm run'); + }); + }); + + describe('pnpm', () => { + test('sets pnpm up at the detected version, SHA-pinning the action', () => { + const substitutions = getWorkflowSubstitutions('pnpm', '11.17.0'); + + // The step is substituted into a comment that already sits at the workflow's step + // indent, so the first line is bare and the rest carry their indentation. + expect(substitutions[SETUP_STEP]).toBe( + `- name: Set up pnpm + # Pinned to the pnpm that wrote this project's lockfile. The action is SHA-pinned, but a + # floating \`version:\` would still let it self-install an unvetted pnpm at run time. + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + with: + version: 11.17.0`, + ); + expect(substitutions[NODE_CACHE]).toBe("cache: 'pnpm'"); + expect(substitutions[INSTALL]).toBe('pnpm install --frozen-lockfile'); + expect(substitutions[RUN]).toBe('pnpm run'); + }); + + test('falls back to the latest pnpm when the user agent carried no version', () => { + expect(getWorkflowSubstitutions('pnpm')[SETUP_STEP]).toContain('version: latest'); + }); + }); + + describe('yarn', () => { + test("needs no setup step, and uses Yarn 1's lockfile flag", () => { + const substitutions = getWorkflowSubstitutions('yarn', '1.22.22'); + + expect(substitutions[SETUP_STEP]).toContain("Yarn is preinstalled on GitHub's Ubuntu runners"); + expect(substitutions[NODE_CACHE]).toBe("cache: 'yarn'"); + expect(substitutions[INSTALL]).toBe('yarn install --frozen-lockfile'); + expect(substitutions[RUN]).toBe('yarn run'); + }); + + test('uses --immutable on Yarn 2+, which dropped --frozen-lockfile', () => { + expect(getWorkflowSubstitutions('yarn', '2.4.3')[INSTALL]).toBe('yarn install --immutable'); + expect(getWorkflowSubstitutions('yarn', '4.9.1')[INSTALL]).toBe('yarn install --immutable'); + }); + + test('assumes Yarn 1 when the user agent carried no usable version', () => { + expect(getWorkflowSubstitutions('yarn')[INSTALL]).toBe('yarn install --frozen-lockfile'); + expect(getWorkflowSubstitutions('yarn', 'stable')[INSTALL]).toBe('yarn install --frozen-lockfile'); + }); + }); + + describe('bun', () => { + test('sets Bun up at the detected version and leaves caching to setup-bun', () => { + const substitutions = getWorkflowSubstitutions('bun', '1.2.19'); + + expect(substitutions[SETUP_STEP]).toBe( + `- name: Set up Bun + # Pinned to the Bun that wrote this project's lockfile. + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: 1.2.19`, + ); + // setup-node rejects any cache value other than npm, Yarn or pnpm, so this has to be a + // comment rather than an input. + expect(substitutions[NODE_CACHE]).toMatch(/^#/); + expect(substitutions[NODE_CACHE]).toContain('oven-sh/setup-bun'); + expect(substitutions[INSTALL]).toBe('bun install --frozen-lockfile'); + expect(substitutions[RUN]).toBe('bun run'); + }); + }); + + describe('deno', () => { + test('sets Deno up at the detected version and runs scripts as tasks', () => { + const substitutions = getWorkflowSubstitutions('deno', '2.5.0'); + + expect(substitutions[SETUP_STEP]).toBe( + `- name: Set up Deno + # Pinned to the Deno that wrote this project's lockfile. + uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5 + with: + deno-version: 2.5.0`, + ); + expect(substitutions[NODE_CACHE]).toMatch(/^#/); + expect(substitutions[NODE_CACHE]).toContain('denoland/setup-deno'); + expect(substitutions[INSTALL]).toBe('deno install --frozen'); + expect(substitutions[RUN]).toBe('deno task'); + }); + }); + + test('falls back to npm for an agent with no CI story, so the job fails loudly', () => { + const substitutions = getWorkflowSubstitutions('unknown', '1.0.0'); + + expect(substitutions[NODE_CACHE]).toBe("cache: 'npm'"); + expect(substitutions[INSTALL]).toBe('npm ci'); + expect(substitutions[RUN]).toBe('npm run'); + }); +}); diff --git a/lib/steps/scaffoldProject.js b/lib/steps/scaffoldProject.js index 88b1663..6ddd26a 100644 --- a/lib/steps/scaffoldProject.js +++ b/lib/steps/scaffoldProject.js @@ -3,6 +3,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { crawlTemplateDir } from '../fs/crawlTemplateDir.js'; +import { getWorkflowSubstitutions } from '../pkg/getWorkflowSubstitutions.js'; /** * Step 6: Create the project structure and files based on the collected information. @@ -13,16 +14,22 @@ import { crawlTemplateDir } from '../fs/crawlTemplateDir.js'; * @param {string} template - The template name to use. * @param {import('./getEnvVars.js').EnvVars} [envVars] - Environment variables to substitute. * @param {string} [pkgManager] - The package manager that invoked us (e.g. npm, pnpm, yarn, bun). Defaults to npm. + * @param {string} [pkgManagerVersion] - That package manager's version, used to pin it in CI. */ -export function scaffoldProject(root, projectName, packageName, template, envVars, pkgManager) { +export function scaffoldProject(root, projectName, packageName, template, envVars, pkgManager, pkgManagerVersion) { fs.mkdirSync(root, { recursive: true }); prompts.log.step(`Scaffolding project in ${root}...`); + const agent = pkgManager || 'npm'; const substitutions = { 'your-project-name-here': projectName || 'your-project-name-here', 'your-package-name-here': packageName || 'your-package-name-here', 'your-fabric.harper.fast-cluster-url-here': envVars?.target || 'your-fabric.harper.fast-cluster-url-here', - 'your-package-manager-here': pkgManager || 'npm', + // Substitution runs these keys in order as plain string replaces, so the longer + // `your-package-manager-*-here` placeholders resolve before the bare one below and can + // never have their prefix eaten by it. + ...getWorkflowSubstitutions(agent, pkgManagerVersion), + 'your-package-manager-here': agent, '\n\t"repository": "github:HarperFast/create-harper",': '', }; diff --git a/lib/steps/scaffoldProject.test.js b/lib/steps/scaffoldProject.test.js index c986f48..d550ee6 100644 --- a/lib/steps/scaffoldProject.test.js +++ b/lib/steps/scaffoldProject.test.js @@ -22,7 +22,7 @@ describe('scaffoldProject', () => { target: 'testtarget', }; - scaffoldProject(root, projectName, packageName, template, envVars, 'pnpm'); + scaffoldProject(root, projectName, packageName, template, envVars, 'pnpm', '11.17.0'); expect(fs.mkdirSync).toHaveBeenCalledWith(expect.any(String), { recursive: true }); expect(prompts.log.step).toHaveBeenCalledWith(expect.stringContaining('Scaffolding project')); @@ -34,6 +34,9 @@ describe('scaffoldProject', () => { 'your-package-name-here': packageName, 'your-fabric.harper.fast-cluster-url-here': envVars.target, 'your-package-manager-here': 'pnpm', + // The CI workflows follow the detected package manager rather than hard-coding npm. + 'your-package-manager-install-here': 'pnpm install --frozen-lockfile', + 'your-package-manager-run-here': 'pnpm run', }), ); }); @@ -51,6 +54,8 @@ describe('scaffoldProject', () => { 'your-package-name-here': 'your-package-name-here', 'your-fabric.harper.fast-cluster-url-here': 'your-fabric.harper.fast-cluster-url-here', 'your-package-manager-here': 'npm', + 'your-package-manager-install-here': 'npm ci', + 'your-package-manager-run-here': 'npm run', }), ); }); diff --git a/template-nextjs-ts/README.md b/template-nextjs-ts/README.md index 645dab1..a0c6d14 100644 --- a/template-nextjs-ts/README.md +++ b/template-nextjs-ts/README.md @@ -19,7 +19,7 @@ npm install -g harper Start the app: ```sh -npm run dev +your-package-manager-run-here dev ``` Then open [http://localhost:9926](http://localhost:9926) 🎉 @@ -67,10 +67,10 @@ harper login Then deploy your app: ```sh -npm run deploy +your-package-manager-run-here deploy ``` -`npm run deploy` runs `next build` locally and ships the prebuilt `.next` output, then Harper serves it — no build runs on the cluster. (Building on the cluster currently fails; see the note in [`config.yaml`](./config.yaml).) +`your-package-manager-run-here deploy` runs `next build` locally and ships the prebuilt `.next` output, then Harper serves it — no build runs on the cluster. (Building on the cluster currently fails; see the note in [`config.yaml`](./config.yaml).) ### Deploy automatically from CI diff --git a/template-nextjs-ts/_github/workflows/deploy.yaml b/template-nextjs-ts/_github/workflows/deploy.yaml index 3a6e5c1..b4e352a 100644 --- a/template-nextjs-ts/_github/workflows/deploy.yaml +++ b/template-nextjs-ts/_github/workflows/deploy.yaml @@ -1,5 +1,6 @@ # Deploys this app to your Harper cluster by *payload* via `harper deploy_component .` -# (`npm run deploy`), which runs `next build` first and uploads the prebuilt `.next` output. +# (`your-package-manager-run-here deploy`), which runs `next build` first and uploads the prebuilt +# `.next` output. # # The other create-harper templates deploy by *reference* (`harper deploy by_ref=true`), where the # cluster clones your repo and builds on the node. Next.js can't use that yet: `.next` is gitignored @@ -39,20 +40,23 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line; ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line; ^5.2.0 blocks a + # breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Build & deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-nextjs/README.md b/template-nextjs/README.md index a1a7ca1..873ab97 100644 --- a/template-nextjs/README.md +++ b/template-nextjs/README.md @@ -19,7 +19,7 @@ npm install -g harper Start the app: ```sh -npm run dev +your-package-manager-run-here dev ``` Then open [http://localhost:9926](http://localhost:9926) 🎉 @@ -67,10 +67,10 @@ harper login Then deploy your app: ```sh -npm run deploy +your-package-manager-run-here deploy ``` -`npm run deploy` runs `next build` locally and ships the prebuilt `.next` output, then Harper serves it — no build runs on the cluster. (Building on the cluster currently fails; see the note in [`config.yaml`](./config.yaml).) +`your-package-manager-run-here deploy` runs `next build` locally and ships the prebuilt `.next` output, then Harper serves it — no build runs on the cluster. (Building on the cluster currently fails; see the note in [`config.yaml`](./config.yaml).) ### Deploy automatically from CI diff --git a/template-nextjs/_github/workflows/deploy.yaml b/template-nextjs/_github/workflows/deploy.yaml index 3a6e5c1..b4e352a 100644 --- a/template-nextjs/_github/workflows/deploy.yaml +++ b/template-nextjs/_github/workflows/deploy.yaml @@ -1,5 +1,6 @@ # Deploys this app to your Harper cluster by *payload* via `harper deploy_component .` -# (`npm run deploy`), which runs `next build` first and uploads the prebuilt `.next` output. +# (`your-package-manager-run-here deploy`), which runs `next build` first and uploads the prebuilt +# `.next` output. # # The other create-harper templates deploy by *reference* (`harper deploy by_ref=true`), where the # cluster clones your repo and builds on the node. Next.js can't use that yet: `.next` is gitignored @@ -39,20 +40,23 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line; ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line; ^5.2.0 blocks a + # breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Build & deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-react-ssr/README.md b/template-react-ssr/README.md index c0c66e4..6d8029d 100644 --- a/template-react-ssr/README.md +++ b/template-react-ssr/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` ### Define Your Schema @@ -116,7 +116,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -126,7 +126,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -153,11 +153,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-react-ssr/_github/workflows/deploy.yaml b/template-react-ssr/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-react-ssr/_github/workflows/deploy.yaml +++ b/template-react-ssr/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-react-ts-ssr/README.md b/template-react-ts-ssr/README.md index 53cf140..ba52829 100644 --- a/template-react-ts-ssr/README.md +++ b/template-react-ts-ssr/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` TypeScript is supported at runtime in Node.js through [type stripping](https://nodejs.org/api/typescript.html#type-stripping). Full TypeScript language support can be enabled through integrating third party build steps to transpile your TypeScript into JavaScript. @@ -124,7 +124,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -134,7 +134,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -161,11 +161,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-react-ts-ssr/_github/workflows/deploy.yaml b/template-react-ts-ssr/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-react-ts-ssr/_github/workflows/deploy.yaml +++ b/template-react-ts-ssr/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-react-ts/README.md b/template-react-ts/README.md index 53cf140..ba52829 100644 --- a/template-react-ts/README.md +++ b/template-react-ts/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` TypeScript is supported at runtime in Node.js through [type stripping](https://nodejs.org/api/typescript.html#type-stripping). Full TypeScript language support can be enabled through integrating third party build steps to transpile your TypeScript into JavaScript. @@ -124,7 +124,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -134,7 +134,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -161,11 +161,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-react-ts/_github/workflows/deploy.yaml b/template-react-ts/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-react-ts/_github/workflows/deploy.yaml +++ b/template-react-ts/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-react/README.md b/template-react/README.md index c0c66e4..6d8029d 100644 --- a/template-react/README.md +++ b/template-react/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` ### Define Your Schema @@ -116,7 +116,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -126,7 +126,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -153,11 +153,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-react/_github/workflows/deploy.yaml b/template-react/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-react/_github/workflows/deploy.yaml +++ b/template-react/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vanilla-ts/README.md b/template-vanilla-ts/README.md index 9769232..68b396b 100644 --- a/template-vanilla-ts/README.md +++ b/template-vanilla-ts/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` TypeScript is supported at runtime in Node.js through [type stripping](https://nodejs.org/api/typescript.html#type-stripping). Full TypeScript language support can be enabled through integrating third party build steps to transpile your TypeScript into JavaScript. @@ -124,7 +124,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -134,7 +134,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -161,11 +161,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vanilla-ts/_github/workflows/deploy.yaml b/template-vanilla-ts/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-vanilla-ts/_github/workflows/deploy.yaml +++ b/template-vanilla-ts/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vanilla/README.md b/template-vanilla/README.md index 8cd6be0..71fd0df 100644 --- a/template-vanilla/README.md +++ b/template-vanilla/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` ### Define Your Schema @@ -116,7 +116,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -126,7 +126,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -153,11 +153,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vanilla/_github/workflows/deploy.yaml b/template-vanilla/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-vanilla/_github/workflows/deploy.yaml +++ b/template-vanilla/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vue-ssr/README.md b/template-vue-ssr/README.md index c0c66e4..6d8029d 100644 --- a/template-vue-ssr/README.md +++ b/template-vue-ssr/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` ### Define Your Schema @@ -116,7 +116,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -126,7 +126,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -153,11 +153,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vue-ssr/_github/workflows/deploy.yaml b/template-vue-ssr/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-vue-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ssr/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vue-ts-ssr/README.md b/template-vue-ts-ssr/README.md index 53cf140..ba52829 100644 --- a/template-vue-ts-ssr/README.md +++ b/template-vue-ts-ssr/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` TypeScript is supported at runtime in Node.js through [type stripping](https://nodejs.org/api/typescript.html#type-stripping). Full TypeScript language support can be enabled through integrating third party build steps to transpile your TypeScript into JavaScript. @@ -124,7 +124,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -134,7 +134,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -161,11 +161,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vue-ts-ssr/_github/workflows/deploy.yaml b/template-vue-ts-ssr/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-vue-ts-ssr/_github/workflows/deploy.yaml +++ b/template-vue-ts-ssr/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vue-ts/README.md b/template-vue-ts/README.md index 53cf140..ba52829 100644 --- a/template-vue-ts/README.md +++ b/template-vue-ts/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` TypeScript is supported at runtime in Node.js through [type stripping](https://nodejs.org/api/typescript.html#type-stripping). Full TypeScript language support can be enabled through integrating third party build steps to transpile your TypeScript into JavaScript. @@ -124,7 +124,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -134,7 +134,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -161,11 +161,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vue-ts/_github/workflows/deploy.yaml b/template-vue-ts/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-vue-ts/_github/workflows/deploy.yaml +++ b/template-vue-ts/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template-vue/README.md b/template-vue/README.md index c0c66e4..6d8029d 100644 --- a/template-vue/README.md +++ b/template-vue/README.md @@ -17,7 +17,7 @@ npm install -g harper Then you can start your app: ```sh -npm run dev +your-package-manager-run-here dev ``` ### Define Your Schema @@ -116,7 +116,7 @@ harper login So the cluster can clone your private repository, give it a read-only token — sealed on your machine, stored encrypted: ```sh -npm run deploy:setup +your-package-manager-run-here deploy:setup ``` This fetches your cluster's public key, has you provide a GitHub token (a fine-grained PAT with **Contents: Read-only**, or your `gh` CLI session), **encrypts it locally**, and stores only the ciphertext in the cluster's secret store. The plaintext never leaves your machine; the cluster decrypts it in memory only while cloning. Because the token is durable, rollbacks keep working for as long as it's valid. @@ -126,7 +126,7 @@ This fetches your cluster's public key, has you provide a GitHub token (a fine-g ### Deploy ```sh -npm run deploy +your-package-manager-run-here deploy ``` This deploys the current commit over `git+https` — commit and push first, since the cluster clones from GitHub and only sees pushed commits. To roll back, check out an older commit and run it again. @@ -153,11 +153,11 @@ harper login --for-ci | gh secret set --env-file - (No [`gh` CLI](https://cli.github.com)? `harper login --for-ci | pbcopy` copies the two lines for you to paste in by hand.) -The clone credential already lives in the cluster from `npm run deploy:setup`, so CI never handles a token itself. +The clone credential already lives in the cluster from `your-package-manager-run-here deploy:setup`, so CI never handles a token itself. ### Private npm dependencies -If your app depends on private npm packages, run `npm run deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. +If your app depends on private npm packages, run `your-package-manager-run-here deploy:setup` again and choose the npm registry — the same sealed-token flow, stored as a separate credential. ## Keep Going! diff --git a/template-vue/_github/workflows/deploy.yaml b/template-vue/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/template-vue/_github/workflows/deploy.yaml +++ b/template-vue/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/template.tests/deployWorkflow.test.js b/template.tests/deployWorkflow.test.js new file mode 100644 index 0000000..d3125c8 --- /dev/null +++ b/template.tests/deployWorkflow.test.js @@ -0,0 +1,145 @@ +import { spawnSync } from 'node:child_process'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterAll, beforeAll, describe, expect, test } from 'vitest'; + +const root = path.resolve(import.meta.dirname, '..'); +const cliPath = path.resolve(root, 'index.js'); +const workflowPath = path.join('_github', 'workflows', 'deploy.yaml'); + +// Every template that ships a deploy workflow. template-early-hints deliberately ships none — it +// is a legacy `harperdb` EdgeWorker example, excluded from the deploy migration. +const templateDirs = fs.readdirSync(root) + .filter((name) => name.startsWith('template-') && fs.existsSync(path.join(root, name, workflowPath))); + +/** + * The deploy workflows must follow the package manager that scaffolded the project. create-harper + * installs with whichever one invoked it, so a project created by `pnpm create harper` commits + * `pnpm-lock.yaml` — against which setup-node's npm cache cannot resolve a package lock and + * `npm ci` fails, killing the job before it ever reaches tests or deploy. + */ +describe('deploy workflows are package-manager agnostic', () => { + test('finds template deploy workflows to check', () => { + expect(templateDirs.length).toBeGreaterThan(0); + }); + + for (const dir of templateDirs) { + test(`${dir} hard-codes no package manager`, () => { + const workflow = fs.readFileSync(path.join(root, dir, workflowPath), 'utf-8'); + + expect(workflow).toContain('# your-package-manager-setup-step-here'); + expect(workflow).toContain('# your-package-manager-node-cache-here'); + expect(workflow).toContain('run: your-package-manager-install-here'); + + expect(workflow).not.toContain('npm ci'); + expect(workflow).not.toMatch(/cache: '/); + // `npm install -g harper` stays npm on purpose — it is a global tool, and npm always + // comes with the Node.js the workflow sets up. Running a package.json script must not. + expect(workflow).not.toContain('npm run'); + expect(workflow).not.toContain('npm test'); + }); + } +}); + +describe('scaffolding with a non-npm package manager', () => { + /** @type {string} */ + let tempDir; + + /** + * Scaffolds a template as the given package manager would, and reads back the deploy workflow + * it generated. + * + * @param {string} template - The template name to scaffold. + * @param {string} userAgent - The `npm_config_user_agent` to scaffold under. + * @returns {string} - The generated workflow's contents. + */ + function scaffold(template, userAgent) { + const projectName = `test-${template}-${userAgent.replace(/\W/g, '-')}`; + + const result = spawnSync('node', [ + cliPath, + projectName, + '--template', + template, + '--no-interactive', + '--overwrite', + ], { + cwd: tempDir, + env: { + ...process.env, + _HARPER_TEST_CLI: '1', + CREATE_HARPER_SKIP_UPDATE: '1', + npm_config_user_agent: userAgent, + }, + encoding: 'utf-8', + }); + + if (result.status !== 0) { + console.error(result.stderr); + console.log(result.stdout); + } + expect(result.status).toBe(0); + + return fs.readFileSync( + path.join(tempDir, projectName, '.github', 'workflows', 'deploy.yaml'), + 'utf-8', + ); + } + + beforeAll(() => { + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'create-harper-deploy-workflow-')); + }); + + afterAll(() => { + fs.rmSync(tempDir, { recursive: true, force: true }); + }); + + test('pnpm gets a pinned pnpm setup, a pnpm cache, and pnpm commands', () => { + const workflow = scaffold('vanilla', 'pnpm/11.17.0 node/v22.0.0 darwin arm64'); + + expect(workflow).toContain('- name: Set up pnpm'); + expect(workflow).toContain('uses: pnpm/action-setup@'); + expect(workflow).toContain('version: 11.17.0'); + expect(workflow).toContain("cache: 'pnpm'"); + expect(workflow).toContain('run: pnpm install --frozen-lockfile'); + expect(workflow).toContain('run: pnpm run test'); + expect(workflow).toContain('run: pnpm run deploy'); + + expect(workflow).not.toContain('npm ci'); + expect(workflow).not.toContain("cache: 'npm'"); + // The Harper CLI is the one deliberate npm holdout. + expect(workflow).toContain('run: npm install -g harper@'); + + // No placeholder may survive into a scaffolded project. + expect(workflow).not.toContain('your-package-manager'); + }); + + test('bun gets a Bun setup and no setup-node cache, which supports npm/Yarn/pnpm only', () => { + const workflow = scaffold('vanilla', 'bun/1.2.19 node/v22.0.0 darwin arm64'); + + expect(workflow).toContain('- name: Set up Bun'); + expect(workflow).toContain('uses: oven-sh/setup-bun@'); + expect(workflow).toContain('bun-version: 1.2.19'); + expect(workflow).toContain('run: bun install --frozen-lockfile'); + expect(workflow).toContain('run: bun run deploy'); + + expect(workflow).not.toMatch(/cache: '/); + expect(workflow).not.toContain('your-package-manager'); + }); + + // The Next.js templates keep their own payload-deploy workflow instead of the shared + // by-reference one, so it needs the same treatment rather than inheriting it from the fan-out. + test('the standalone Next.js payload workflow follows the package manager too', () => { + const workflow = scaffold('nextjs', 'pnpm/11.17.0 node/v22.0.0 darwin arm64'); + + expect(workflow).toContain('harper deploy_component'); + expect(workflow).toContain('- name: Set up pnpm'); + expect(workflow).toContain("cache: 'pnpm'"); + expect(workflow).toContain('run: pnpm install --frozen-lockfile'); + expect(workflow).toContain('run: pnpm run deploy'); + + expect(workflow).not.toContain('npm ci'); + expect(workflow).not.toContain('your-package-manager'); + }); +}); diff --git a/templates-shared/all/_github/workflows/deploy.yaml b/templates-shared/all/_github/workflows/deploy.yaml index 96a8d56..bf76af9 100644 --- a/templates-shared/all/_github/workflows/deploy.yaml +++ b/templates-shared/all/_github/workflows/deploy.yaml @@ -1,10 +1,11 @@ # Deploys this app to your Harper cluster by *reference* via `harper deploy by_ref=true` -# (`npm run deploy`). +# (`your-package-manager-run-here deploy`). # # Runs on a version tag (e.g. `git tag v1.2.3 && git push --tags`) or manually. The Harper cluster # fetches the tagged commit over git+https (pinned by its exact SHA, so peers can't diverge on a # moved tag), authenticating a private clone with the encrypted token you sealed once via -# `npm run deploy:setup`. The credential lives on the cluster, so this workflow handles no token. +# `your-package-manager-run-here deploy:setup`. The credential lives on the cluster, so this +# workflow handles no token. # # Required repository secrets (Settings → Secrets and variables → Actions): # HARPER_CLI_TARGET your cluster's operations URL, e.g. https://my-cluster.harperdb.io:9925 @@ -38,22 +39,25 @@ jobs: with: fetch-depth: 0 fetch-tags: true + # your-package-manager-setup-step-here - name: Set up Node.js uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: - cache: 'npm' + # your-package-manager-node-cache-here node-version-file: '.nvmrc' - name: Install dependencies - run: npm ci + run: your-package-manager-install-here - name: Run unit tests - run: npm test + run: your-package-manager-run-here test - name: Run lint - run: npm run lint + run: your-package-manager-run-here lint - name: Install Harper CLI - # Pinned to the 5.2 line (where deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. + # Installed with npm regardless of this project's package manager: it is a global tool, and + # npm always comes with the Node.js set up above. Pinned to the 5.2 line (where + # deploy-by-reference lands); ^5.2.0 blocks a breaking 6.x bump. run: npm install -g harper@^5.2.0 - name: Deploy - run: npm run deploy + run: your-package-manager-run-here deploy env: HARPER_CLI_TARGET: ${{ secrets.HARPER_CLI_TARGET }} HARPER_CLI_REFRESH_TOKEN: ${{ secrets.HARPER_CLI_REFRESH_TOKEN }} diff --git a/templates-studio/buildStudioTemplates.js b/templates-studio/buildStudioTemplates.js index dc0467f..dbc8c21 100644 --- a/templates-studio/buildStudioTemplates.js +++ b/templates-studio/buildStudioTemplates.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -import fs from 'node:fs'; import path from 'node:path'; import { studioTemplateNames } from '../lib/constants/templates.js'; import { copyDir } from '../lib/fs/copyDir.js'; @@ -20,13 +19,25 @@ import { run } from '../lib/run.js'; fromTemplate, toTemplate, // Studio apps run deployed on Harper Fabric (no local `npm run dev`), so skip the - // `.claude/launch.json` preview config that only applies to local development. + // `.claude/launch.json` preview config that only applies to local development, and the + // `_github` deploy workflow: Studio deploys from Studio, and `scripts` is emptied below, + // so the workflow's install/test/deploy steps would have nothing to run. // Match on the basename so a clone path containing `_env`/`_claude` doesn't skip everything. (srcFile) => { const filename = path.basename(srcFile); - return !filename.startsWith('_env') && filename !== '_claude'; + return !filename.startsWith('_env') && filename !== '_claude' && filename !== '_github'; }, (sourceContent, targetPath) => { + // Only `npm create harper` knows which package manager invoked it, so it alone can + // substitute the `your-package-manager-*` placeholders. Studio templates are cloned + // rather than scaffolded, so resolve them to npm here instead of shipping the raw + // tokens. Reassigning the parameter, rather than branching on a local, keeps this + // applied on every path out of this function — including the fallback return. + // Longest token first, so a shorter one can never eat a longer one's prefix. + sourceContent = sourceContent + .replaceAll('your-package-manager-run-here', 'npm run') + .replaceAll('your-package-manager-here', 'npm'); + if (targetPath.endsWith('/package.json')) { return sourceContent .replace(/your-package-name-here/g, `@harperfast/${targetTemplate}-studio`) @@ -75,10 +86,6 @@ see what different users will be able to access through your API.`, }, ); - if (fs.existsSync(path.resolve(fromTemplate, '_github'))) { - emptyDir(path.resolve(toTemplate, '.github')); - renameFile(path.resolve(toTemplate, '_github'), path.resolve(toTemplate, '.github')); - } renameFile(path.resolve(toTemplate, '_nvmrc'), path.resolve(toTemplate, '.nvmrc')); renameFile(path.resolve(toTemplate, '_gitignore'), path.resolve(toTemplate, '.gitignore')); renameFile(path.resolve(toTemplate, '_aiignore'), path.resolve(toTemplate, '.aiignore')); diff --git a/vitest.config.js b/vitest.config.js index c6ea4ef..53d2dbc 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -11,6 +11,7 @@ export default defineConfig({ include: [ 'lib/**/*.test.js', 'template.tests/staticConfig.test.js', + 'template.tests/deployWorkflow.test.js', ], }, }); From e248479442e9509f0a81712ac0a4dd0fab9eacd4 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Fri, 14 Aug 2026 10:23:33 -0400 Subject: [PATCH 07/11] chore: ignore a node_modules symlink, not just a real directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vendored Node.gitignore entry is `node_modules/`. The trailing slash makes it match a directory only, so a `node_modules` symlink — as git worktrees commonly use to share the main checkout's install — is not ignored and can be committed by accident. Such a symlink points at an absolute path in one developer's home directory, so it dangles on every other checkout and can make package managers fail when they try to create or populate node_modules. Co-Authored-By: Claude Opus 5 --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index bb9bc11..3e6609e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ .DS_Store .temp-integration-tests +# Matches a `node_modules` *symlink* too — git worktrees commonly link to the main checkout's +# install, and the `node_modules/` entry in the vendored Node.gitignore below has a trailing +# slash, so it only ever matches a real directory. +node_modules + # Playwright e2e artifacts /test-results/ /playwright-report/ From 6f5cb934e0be322a0fe88a7341ec324d063ad9d9 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Fri, 14 Aug 2026 10:32:38 -0400 Subject: [PATCH 08/11] test: assert generated workflows in process, not through a subprocess env var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Windows runners failed all three scaffold assertions: `_HARPER_TEST_CLI` and `CREATE_HARPER_SKIP_UPDATE` reached the child, but `npm_config_user_agent` — the one key npm had already set in our own environment — did not, so the CLI detected npm and generated the npm workflow. Windows matches environment variable names case-insensitively, so an inherited casing can survive alongside the one we set and win. The workflow-content assertions now call `scaffoldProject` directly, which still exercises the real template files, substitution and file writing with no subprocess env semantics in the way, and adds Deno and npm coverage while it's cheap. One end-to-end test still goes through the CLI to cover the `npm_config_user_agent` plumbing itself; it strips every casing of the variable before setting its own, and asserts the CLI's own account of the package manager it detected first, so a failure says whether the user agent arrived or the workflow came out wrong. Co-Authored-By: Claude Opus 5 --- template.tests/deployWorkflow.test.js | 144 +++++++++++++++++++------- 1 file changed, 105 insertions(+), 39 deletions(-) diff --git a/template.tests/deployWorkflow.test.js b/template.tests/deployWorkflow.test.js index d3125c8..9cd367f 100644 --- a/template.tests/deployWorkflow.test.js +++ b/template.tests/deployWorkflow.test.js @@ -2,7 +2,12 @@ import { spawnSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { afterAll, beforeAll, describe, expect, test } from 'vitest'; +import { afterAll, afterEach, beforeAll, describe, expect, test, vi } from 'vitest'; +import { scaffoldProject } from '../lib/steps/scaffoldProject.js'; + +// Scaffolding narrates every file it writes; the assertions below care about the files, not the +// narration. The CLI runs out of process, so this only quiets the in-process calls. +vi.mock('@clack/prompts'); const root = path.resolve(import.meta.dirname, '..'); const cliPath = path.resolve(root, 'index.js'); @@ -42,61 +47,36 @@ describe('deploy workflows are package-manager agnostic', () => { } }); -describe('scaffolding with a non-npm package manager', () => { +describe('generated deploy workflows', () => { /** @type {string} */ let tempDir; /** - * Scaffolds a template as the given package manager would, and reads back the deploy workflow - * it generated. + * Scaffolds a template for the given package manager and reads back the deploy workflow. * * @param {string} template - The template name to scaffold. - * @param {string} userAgent - The `npm_config_user_agent` to scaffold under. + * @param {string} agent - The package manager to scaffold for. + * @param {string} [version] - That package manager's version. * @returns {string} - The generated workflow's contents. */ - function scaffold(template, userAgent) { - const projectName = `test-${template}-${userAgent.replace(/\W/g, '-')}`; - - const result = spawnSync('node', [ - cliPath, - projectName, - '--template', - template, - '--no-interactive', - '--overwrite', - ], { - cwd: tempDir, - env: { - ...process.env, - _HARPER_TEST_CLI: '1', - CREATE_HARPER_SKIP_UPDATE: '1', - npm_config_user_agent: userAgent, - }, - encoding: 'utf-8', - }); - - if (result.status !== 0) { - console.error(result.stderr); - console.log(result.stdout); - } - expect(result.status).toBe(0); - - return fs.readFileSync( - path.join(tempDir, projectName, '.github', 'workflows', 'deploy.yaml'), - 'utf-8', - ); + function scaffoldFor(template, agent, version) { + const target = path.join(tempDir, `${template}-${agent}`); + scaffoldProject(target, 'test-project', 'test-project', template, undefined, agent, version); + return fs.readFileSync(path.join(target, '.github', 'workflows', 'deploy.yaml'), 'utf-8'); } beforeAll(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'create-harper-deploy-workflow-')); + vi.spyOn(console, 'log').mockImplementation(() => {}); }); afterAll(() => { + vi.restoreAllMocks(); fs.rmSync(tempDir, { recursive: true, force: true }); }); test('pnpm gets a pinned pnpm setup, a pnpm cache, and pnpm commands', () => { - const workflow = scaffold('vanilla', 'pnpm/11.17.0 node/v22.0.0 darwin arm64'); + const workflow = scaffoldFor('vanilla', 'pnpm', '11.17.0'); expect(workflow).toContain('- name: Set up pnpm'); expect(workflow).toContain('uses: pnpm/action-setup@'); @@ -116,7 +96,7 @@ describe('scaffolding with a non-npm package manager', () => { }); test('bun gets a Bun setup and no setup-node cache, which supports npm/Yarn/pnpm only', () => { - const workflow = scaffold('vanilla', 'bun/1.2.19 node/v22.0.0 darwin arm64'); + const workflow = scaffoldFor('vanilla', 'bun', '1.2.19'); expect(workflow).toContain('- name: Set up Bun'); expect(workflow).toContain('uses: oven-sh/setup-bun@'); @@ -128,10 +108,28 @@ describe('scaffolding with a non-npm package manager', () => { expect(workflow).not.toContain('your-package-manager'); }); + test('deno runs package.json scripts as tasks', () => { + const workflow = scaffoldFor('vanilla', 'deno', '2.5.0'); + + expect(workflow).toContain('- name: Set up Deno'); + expect(workflow).toContain('run: deno install --frozen'); + expect(workflow).toContain('run: deno task deploy'); + expect(workflow).not.toContain('your-package-manager'); + }); + + test('npm still gets the npm workflow', () => { + const workflow = scaffoldFor('vanilla', 'npm', '10.9.0'); + + expect(workflow).toContain("cache: 'npm'"); + expect(workflow).toContain('run: npm ci'); + expect(workflow).toContain('run: npm run deploy'); + expect(workflow).not.toContain('your-package-manager'); + }); + // The Next.js templates keep their own payload-deploy workflow instead of the shared // by-reference one, so it needs the same treatment rather than inheriting it from the fan-out. test('the standalone Next.js payload workflow follows the package manager too', () => { - const workflow = scaffold('nextjs', 'pnpm/11.17.0 node/v22.0.0 darwin arm64'); + const workflow = scaffoldFor('nextjs', 'pnpm', '11.17.0'); expect(workflow).toContain('harper deploy_component'); expect(workflow).toContain('- name: Set up pnpm'); @@ -143,3 +141,71 @@ describe('scaffolding with a non-npm package manager', () => { expect(workflow).not.toContain('your-package-manager'); }); }); + +describe('the invoking package manager reaches the generated workflow', () => { + /** @type {string} */ + let tempDir; + + beforeAll(() => { + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'create-harper-user-agent-')); + }); + + afterEach(() => { + fs.rmSync(tempDir, { recursive: true, force: true }); + fs.mkdirSync(tempDir, { recursive: true }); + }); + + afterAll(() => { + fs.rmSync(tempDir, { recursive: true, force: true }); + }); + + // End to end through the CLI, so the `npm_config_user_agent` plumbing is covered too and not + // just the substitution it feeds. + test('`pnpm create harper` produces a pnpm workflow', () => { + const projectName = 'test-user-agent'; + + // Windows matches environment variable names case-insensitively, so the + // `npm_config_user_agent` npm set when it ran this test suite can survive alongside the one + // set here and win. Drop every casing of it before setting ours. + const env = Object.fromEntries( + Object.entries(process.env).filter(([key]) => key.toLowerCase() !== 'npm_config_user_agent'), + ); + + const result = spawnSync('node', [ + cliPath, + projectName, + '--template', + 'vanilla', + '--no-interactive', + '--overwrite', + ], { + cwd: tempDir, + env: { + ...env, + _HARPER_TEST_CLI: '1', + CREATE_HARPER_SKIP_UPDATE: '1', + npm_config_user_agent: 'pnpm/11.17.0 npm/? node/v22.0.0 linux x64', + }, + encoding: 'utf-8', + }); + + if (result.status !== 0) { + console.error(result.stderr); + console.log(result.stdout); + } + expect(result.status).toBe(0); + + // Checked first, and against the CLI's own account of what it detected, so a failure here + // separates "the user agent never reached the child" from "the workflow came out wrong". + expect(result.stdout, `CLI output was:\n${result.stdout}`).toContain('dependencies with pnpm'); + + const workflow = fs.readFileSync( + path.join(tempDir, projectName, '.github', 'workflows', 'deploy.yaml'), + 'utf-8', + ); + + expect(workflow).toContain('run: pnpm install --frozen-lockfile'); + expect(workflow).toContain('version: 11.17.0'); + expect(workflow).not.toContain('npm ci'); + }); +}); From 77f1143b45d1ab03ef60a08a4069b54e2ab5bf14 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Mon, 17 Aug 2026 10:10:31 -0400 Subject: [PATCH 09/11] fix(templates): provision Yarn Berry via Corepack in generated deploy workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated workflow selected `yarn install --immutable` for Yarn 2+ but never installed or activated that Yarn. GitHub's runners ship Yarn 1.x (Classic), and a scaffolded project pins no `packageManager`/`.yarnrc.yml`, so the preinstalled Classic ran the install and rejected `--immutable` (its flag is `--frozen-lockfile`), stopping the deploy before tests or the deploy step. Yarn 2+ now gets a setup step that activates the exact detected version with Corepack — `corepack enable && corepack prepare yarn@ --activate` — which makes a bare `yarn` resolve to it with no packageManager field required. It sits before setup-node, matching the other managers' setup slot. Yarn 1.x is unchanged: preinstalled, no setup step, `--frozen-lockfile`. When the user agent carries no usable version we assume Classic rather than emit a version-less `corepack prepare`. Coverage: - getWorkflowSubstitutions.test.js asserts the Berry setup step (pinned Corepack activation) and the Classic no-op, across the 2.x/3.x/4.x lines and the no-version fallback. - deployWorkflow.test.js asserts the generated workflow carries the Corepack step + immutable install for Berry and neither for Classic. - A new integration.yaml job runs the generated setup + install path for real, in the workflow's own ordering (Corepack activation under the runner's default Node, before setup-node swaps it), and runs `yarn install --immutable` against a scaffolded Berry project — the exact command Classic rejects — proving activation survives the Node swap. Verified end to end locally too (Berry 4.9.1, PnP install, immutable re-install clean). Co-Authored-By: Claude Opus 5 --- .github/workflows/integration.yaml | 53 ++++++++++++++++++++++++ lib/pkg/getWorkflowSubstitutions.js | 28 +++++++++---- lib/pkg/getWorkflowSubstitutions.test.js | 40 ++++++++++++++---- template.tests/deployWorkflow.test.js | 20 +++++++++ 4 files changed, 124 insertions(+), 17 deletions(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 71146b4..302a237 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -180,3 +180,56 @@ jobs: fi echo "Integration test passed for ${{ matrix.pkg-manager }} with ${{ matrix.template }}!" + + # The matrix above exercises Yarn 1.x (Classic). Yarn 2+ (Berry) is a different animal: it isn't + # on the runner, and a scaffolded project pins no packageManager/.yarnrc.yml, so the generated + # deploy workflow provisions it with Corepack (lib/pkg/getWorkflowSubstitutions.js). This job runs + # that generated setup + install path for real, in the workflow's own ordering — Corepack + # activation with the runner's default Node *before* setup-node swaps it — to prove the activated + # Yarn survives the Node swap and that `yarn install --immutable` (which Classic would reject) then + # succeeds. deployWorkflow.test.js asserts the generator still emits exactly these commands. + yarn-berry-install: + name: Yarn Berry deploy-install path + runs-on: ubuntu-latest + env: + YARN_VERSION: 4.9.1 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + # Runs against the runner's default Node, before the pinned Node below — exactly as the + # generated workflow's "Set up Yarn" step precedes its "Set up Node.js" step. + - name: Activate Yarn Berry with Corepack + run: corepack enable && corepack prepare "yarn@$YARN_VERSION" --activate + + - name: Setup Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version-file: '.nvmrc' + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Scaffold a project as `yarn create harper` would + env: + CREATE_HARPER_SKIP_UPDATE: 'true' + run: | + npm_config_user_agent="yarn/$YARN_VERSION npm/? node/$(node -v) linux x64" \ + node index.js berry-app --template vanilla --no-interactive --overwrite + + - name: The generated workflow must drive Yarn through Corepack, not the runner's Yarn 1 + run: | + workflow="berry-app/.github/workflows/deploy.yaml" + grep -q "corepack prepare yarn@$YARN_VERSION --activate" "$workflow" + grep -q 'yarn install --immutable' "$workflow" + + # The reviewer's failure mode: `yarn install --immutable` under the runner's preinstalled + # Yarn 1 errors on the unknown flag. If Corepack activation survived the Node swap, the bare + # `yarn` here is Berry and both installs succeed. + - name: Run the generated install path + working-directory: berry-app + run: | + echo "yarn is $(yarn --version) at $(which yarn)" + yarn install + yarn install --immutable diff --git a/lib/pkg/getWorkflowSubstitutions.js b/lib/pkg/getWorkflowSubstitutions.js index 7b3b999..8e760f4 100644 --- a/lib/pkg/getWorkflowSubstitutions.js +++ b/lib/pkg/getWorkflowSubstitutions.js @@ -9,10 +9,10 @@ const INPUT_INDENT = ' '.repeat(4); /** * Builds one `steps:` entry, or a bare comment when a package manager needs no setup step. * - * @param {{name?: string, comment?: string[], uses?: string, inputs?: Record}} step + * @param {{name?: string, comment?: string[], uses?: string, inputs?: Record, run?: string}} step * @returns {string} - The step's YAML, indented for substitution into the workflow. */ -function buildStep({ name, comment, uses, inputs }) { +function buildStep({ name, comment, uses, inputs, run }) { const lines = []; if (name) { lines.push(`- name: ${name}`); } for (const line of comment ?? []) { @@ -27,6 +27,7 @@ function buildStep({ name, comment, uses, inputs }) { lines.push(`${INPUT_INDENT}${input}: ${value}`); } } + if (run) { lines.push(`${KEY_INDENT}run: ${run}`); } return lines.join(`\n${STEP_INDENT}`); } @@ -43,8 +44,8 @@ function majorVersion(version) { /** * Builds the step that puts the project's package manager on PATH, pinned to the version that - * generated its lockfile. npm and Yarn need none — npm ships with Node.js, and Yarn is - * preinstalled on GitHub's Ubuntu runners — so they get a comment saying so instead. + * generated its lockfile. npm and Yarn 1.x (Classic) need none — npm ships with Node.js, and + * Classic is preinstalled on GitHub's Ubuntu runners — so they get a comment saying so instead. * * @param {string} agent - The package manager agent ('npm', 'pnpm', 'yarn', 'bun' or 'deno'). * @param {string} [version] - The agent's version, as reported by the user agent that invoked us. @@ -77,12 +78,21 @@ function getSetupStep(agent, version) { inputs: { 'deno-version': version ?? 'vx.x.x' }, }); case 'yarn': + // Only Yarn 2+ (Berry) needs provisioning. The runner ships Yarn 1.x (Classic), and a + // scaffolded project pins no `packageManager`/`.yarnrc.yml`, so without this Berry's + // `--immutable` (below) would run under Classic, which rejects the flag and stops the + // deploy at install. Corepack activates the exact Yarn that wrote yarn.lock: `enable` + // installs the shim, `prepare … --activate` sets that version as the default a bare + // `yarn` resolves to (no `packageManager` field required). + if ((majorVersion(version) ?? 1) < 2) { + return buildStep({ + comment: ["Yarn 1.x (Classic) is preinstalled on GitHub's Ubuntu runners, so it needs no setup step."], + }); + } return buildStep({ - comment: [ - "Yarn is preinstalled on GitHub's Ubuntu runners, so it needs no setup step. On Yarn 2+,", - 'commit `.yarnrc.yml` with a `yarnPath` (`yarn set version`) so CI runs the same Yarn that', - "wrote yarn.lock rather than the runner's Yarn 1.x.", - ], + name: `Set up Yarn ${version}`, + comment: ["Pinned to the Yarn that wrote this project's lockfile."], + run: `corepack enable && corepack prepare yarn@${version} --activate`, }); default: return buildStep({ comment: ['npm ships with Node.js, so it needs no setup step.'] }); diff --git a/lib/pkg/getWorkflowSubstitutions.test.js b/lib/pkg/getWorkflowSubstitutions.test.js index 4eb1a55..29b7dab 100644 --- a/lib/pkg/getWorkflowSubstitutions.test.js +++ b/lib/pkg/getWorkflowSubstitutions.test.js @@ -52,23 +52,47 @@ describe(getWorkflowSubstitutions, () => { }); describe('yarn', () => { - test("needs no setup step, and uses Yarn 1's lockfile flag", () => { + test('1.x (Classic) is preinstalled, so no setup step, and uses its lockfile flag', () => { const substitutions = getWorkflowSubstitutions('yarn', '1.22.22'); - expect(substitutions[SETUP_STEP]).toContain("Yarn is preinstalled on GitHub's Ubuntu runners"); + expect(substitutions[SETUP_STEP]).toBe( + "# Yarn 1.x (Classic) is preinstalled on GitHub's Ubuntu runners, so it needs no setup step.", + ); + // No provisioning, so nothing to reject `--frozen-lockfile`. + expect(substitutions[SETUP_STEP]).not.toContain('corepack'); expect(substitutions[NODE_CACHE]).toBe("cache: 'yarn'"); expect(substitutions[INSTALL]).toBe('yarn install --frozen-lockfile'); expect(substitutions[RUN]).toBe('yarn run'); }); - test('uses --immutable on Yarn 2+, which dropped --frozen-lockfile', () => { - expect(getWorkflowSubstitutions('yarn', '2.4.3')[INSTALL]).toBe('yarn install --immutable'); - expect(getWorkflowSubstitutions('yarn', '4.9.1')[INSTALL]).toBe('yarn install --immutable'); + test('2+ (Berry) is provisioned via Corepack, pinned to the detected version', () => { + const substitutions = getWorkflowSubstitutions('yarn', '4.9.1'); + + // Berry isn't on the runner; without this step the preinstalled Yarn 1 would run the + // `--immutable` install below and reject the flag. `--activate` makes a bare `yarn` + // resolve to this version with no `packageManager`/`.yarnrc.yml` in the project. + expect(substitutions[SETUP_STEP]).toBe( + `- name: Set up Yarn 4.9.1 + # Pinned to the Yarn that wrote this project's lockfile. + run: corepack enable && corepack prepare yarn@4.9.1 --activate`, + ); + expect(substitutions[INSTALL]).toBe('yarn install --immutable'); + expect(substitutions[RUN]).toBe('yarn run'); + }); + + test('every 2+ line gets the Corepack step, since 2.0 dropped --frozen-lockfile', () => { + for (const version of ['2.4.3', '3.8.7', '4.9.1']) { + const substitutions = getWorkflowSubstitutions('yarn', version); + expect(substitutions[SETUP_STEP]).toContain(`corepack prepare yarn@${version} --activate`); + expect(substitutions[INSTALL]).toBe('yarn install --immutable'); + } }); - test('assumes Yarn 1 when the user agent carried no usable version', () => { - expect(getWorkflowSubstitutions('yarn')[INSTALL]).toBe('yarn install --frozen-lockfile'); - expect(getWorkflowSubstitutions('yarn', 'stable')[INSTALL]).toBe('yarn install --frozen-lockfile'); + test('assumes Classic when the user agent carried no usable version, so it never emits a bare `corepack prepare yarn@`', () => { + for (const substitutions of [getWorkflowSubstitutions('yarn'), getWorkflowSubstitutions('yarn', 'stable')]) { + expect(substitutions[SETUP_STEP]).not.toContain('corepack'); + expect(substitutions[INSTALL]).toBe('yarn install --frozen-lockfile'); + } }); }); diff --git a/template.tests/deployWorkflow.test.js b/template.tests/deployWorkflow.test.js index 9cd367f..7ebe737 100644 --- a/template.tests/deployWorkflow.test.js +++ b/template.tests/deployWorkflow.test.js @@ -117,6 +117,26 @@ describe('generated deploy workflows', () => { expect(workflow).not.toContain('your-package-manager'); }); + test('Yarn 2+ (Berry) provisions the detected Yarn via Corepack before installing immutably', () => { + const workflow = scaffoldFor('vanilla', 'yarn', '4.9.1'); + + // Without the Corepack step, `yarn install --immutable` runs under the runner's preinstalled + // Yarn 1, which rejects `--immutable`. The verify-yarn-berry-install job in integration.yaml + // runs this exact pair of commands on a real runner to prove they work together. + expect(workflow).toContain('run: corepack enable && corepack prepare yarn@4.9.1 --activate'); + expect(workflow).toContain('run: yarn install --immutable'); + expect(workflow).not.toContain('--frozen-lockfile'); + expect(workflow).not.toContain('your-package-manager'); + }); + + test('Yarn 1 (Classic) is preinstalled, so no Corepack step and the classic lockfile flag', () => { + const workflow = scaffoldFor('vanilla', 'yarn', '1.22.22'); + + expect(workflow).not.toContain('corepack'); + expect(workflow).toContain('run: yarn install --frozen-lockfile'); + expect(workflow).not.toContain('your-package-manager'); + }); + test('npm still gets the npm workflow', () => { const workflow = scaffoldFor('vanilla', 'npm', '10.9.0'); From 6f33546baa30a9f63f795707d40d2d3bcc9379e5 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Mon, 17 Aug 2026 12:29:59 -0400 Subject: [PATCH 10/11] fix(ci): scaffold the Yarn Berry check outside the repo, skip the CLI's own install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The job failed at the scaffold step: it created the project inside the checkout and let the CLI run its own install, so the Corepack-activated Berry walked up to create-harper's package.json and refused the nested project ('doesn't seem to be part of the project declared in …'). Scaffold into a mktemp dir outside the repo (Berry's upward project-root search then stops at the new project) and pass --skip-install so the two explicit installs below are the only ones — the second, `yarn install --immutable`, being the command the runner's Yarn 1 would reject. Co-Authored-By: Claude Opus 5 --- .github/workflows/integration.yaml | 33 +++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 302a237..7563675 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -211,25 +211,30 @@ jobs: - name: Install dependencies run: npm ci - - name: Scaffold a project as `yarn create harper` would + - name: Run the generated Yarn Berry setup + install path env: CREATE_HARPER_SKIP_UPDATE: 'true' run: | - npm_config_user_agent="yarn/$YARN_VERSION npm/? node/$(node -v) linux x64" \ - node index.js berry-app --template vanilla --no-interactive --overwrite - - - name: The generated workflow must drive Yarn through Corepack, not the runner's Yarn 1 - run: | - workflow="berry-app/.github/workflows/deploy.yaml" + set -euo pipefail + REPO="$(pwd)" + # Scaffold outside the repo: Berry looks upward for its project root, and create-harper's + # own package.json would otherwise capture the new project as a stray workspace. + PROJECT_DIR="$(mktemp -d)/berry-app" + mkdir -p "$(dirname "$PROJECT_DIR")" + + ( cd "$(dirname "$PROJECT_DIR")" \ + && npm_config_user_agent="yarn/$YARN_VERSION npm/? node/$(node -v) linux x64" \ + node "$REPO/index.js" berry-app --template vanilla --no-interactive --overwrite --skip-install ) + + workflow="$PROJECT_DIR/.github/workflows/deploy.yaml" + echo "Asserting the generated workflow drives Yarn through Corepack, not the runner's Yarn 1..." grep -q "corepack prepare yarn@$YARN_VERSION --activate" "$workflow" grep -q 'yarn install --immutable' "$workflow" - # The reviewer's failure mode: `yarn install --immutable` under the runner's preinstalled - # Yarn 1 errors on the unknown flag. If Corepack activation survived the Node swap, the bare - # `yarn` here is Berry and both installs succeed. - - name: Run the generated install path - working-directory: berry-app - run: | - echo "yarn is $(yarn --version) at $(which yarn)" + cd "$PROJECT_DIR" + echo "yarn is $(yarn --version) at $(command -v yarn)" + # The reviewer's failure mode: `yarn install --immutable` under the runner's preinstalled + # Yarn 1 errors on the unknown flag. If the Corepack activation above survived setup-node's + # Node swap, this bare `yarn` is Berry and both installs succeed. yarn install yarn install --immutable From a9f5c8db47453617e93a60a883959c4268493a5b Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Mon, 17 Aug 2026 12:33:36 -0400 Subject: [PATCH 11/11] fix(ci): seed the Yarn Berry lockfile before the immutable install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Berry auto-enables immutable installs on a public PR runner, so the first `yarn install` (there to create the lockfile a real project would have committed) failed with YN0028 — 'the lockfile would have been created by this install, which is explicitly forbidden'. Disable immutable installs for that one seeding install; the following `yarn install --immutable` — the command Yarn 1 rejects and the point of the job — then runs against the created lockfile. The prior run already confirmed the part that couldn't be checked locally: it logged `yarn is 4.9.1 at /usr/local/bin/yarn`, so the Corepack activation survives setup-node's Node swap. Co-Authored-By: Claude Opus 5 --- .github/workflows/integration.yaml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 7563675..57ca498 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -233,8 +233,15 @@ jobs: cd "$PROJECT_DIR" echo "yarn is $(yarn --version) at $(command -v yarn)" + + # Stand in for the yarn.lock the user commits: Berry auto-enables immutable installs on a + # public PR runner, which forbids creating a lockfile, so disable it for this one seeding + # install. (A fresh scaffold ships no lockfile; the deploy workflow runs against a repo + # that already has one.) + YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install + # The reviewer's failure mode: `yarn install --immutable` under the runner's preinstalled - # Yarn 1 errors on the unknown flag. If the Corepack activation above survived setup-node's - # Node swap, this bare `yarn` is Berry and both installs succeed. - yarn install + # Yarn 1 errors on the unknown flag. That `yarn is 4.9.1` line above already shows the + # Corepack activation survived setup-node's Node swap; this proves the generated command + # then succeeds under Berry. yarn install --immutable