@harperfast/template-vue-ts-studio 1.13.0 → 1.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/harper-best-practices/AGENTS.md +553 -141
- package/.agents/skills/harper-best-practices/SKILL.md +2 -1
- package/.agents/skills/harper-best-practices/rules/creating-a-fabric-account-and-cluster.md +7 -4
- package/.agents/skills/harper-best-practices/rules/creating-harper-apps.md +71 -4
- package/.agents/skills/harper-best-practices/rules/deploying-from-ci.md +358 -0
- package/.agents/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +135 -91
- package/.agents/skills/harper-best-practices/rules/load-env.md +37 -49
- package/.agents/skills/harper-best-practices/rules.manifest.yaml +184 -8
- package/agent/skills/harper-best-practices/AGENTS.md +553 -141
- package/agent/skills/harper-best-practices/SKILL.md +2 -1
- package/agent/skills/harper-best-practices/rules/creating-a-fabric-account-and-cluster.md +7 -4
- package/agent/skills/harper-best-practices/rules/creating-harper-apps.md +71 -4
- package/agent/skills/harper-best-practices/rules/deploying-from-ci.md +358 -0
- package/agent/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +135 -91
- package/agent/skills/harper-best-practices/rules/load-env.md +37 -49
- package/agent/skills/harper-best-practices/rules.manifest.yaml +184 -8
- package/package.json +1 -1
- package/skills-lock.json +1 -1
|
@@ -77,7 +77,8 @@ See the concrete examples embedded in each rule subsection below (GraphQL schema
|
|
|
77
77
|
|
|
78
78
|
### 4. Infrastructure & Ops (MEDIUM)
|
|
79
79
|
|
|
80
|
-
- `deploying-to-harper-fabric` — How to deploy a Harper application to
|
|
80
|
+
- `deploying-to-harper-fabric` — How to deploy a Harper application to Harper Fabric with the CLI, check that the release was certified, and roll back by activating a previous release's `deployment_id`.
|
|
81
|
+
- `deploying-from-ci` — How to deploy a Harper application from GitHub Actions with OIDC trusted publishing, so CI stores no Harper credential, including the one-command setup with `harper deploy setup=true provider=github-actions`.
|
|
81
82
|
- `creating-a-fabric-account-and-cluster` — How to create a Harper Fabric account, organization, and cluster.
|
|
82
83
|
- `creating-harper-apps` — How to initialize a new Harper application using the CLI.
|
|
83
84
|
- `serving-web-content` — How to serve static files and integrated Vite/React applications in Harper.
|
|
@@ -16,7 +16,7 @@ Follow these steps to set up your Harper Fabric environment for deployment.
|
|
|
16
16
|
3. **Create a Cluster**: Create a new cluster. This can be on the free tier, no credit card required.
|
|
17
17
|
4. **Set Credentials**: During setup, set the cluster username and password to finish configuring it.
|
|
18
18
|
5. **Get Application URL**: Navigate to the **Config** tab and copy the **Application URL**.
|
|
19
|
-
6. **
|
|
19
|
+
6. **Connect the CLI**: Run `harper login` with the Application URL to store a token for the cluster, and set the URL as `HARPER_CLI_TARGET` in your `.env` file. Prefer this to putting the cluster username and password in `.env`, and don't store them in GitHub Actions secrets: to deploy from GitHub Actions, set up OIDC trusted publishing instead (see [deploying-from-ci](deploying-from-ci.md)).
|
|
20
20
|
7. **Next Steps**: See the [deploying-to-harper-fabric](deploying-to-harper-fabric.md) rule for detailed instructions on deploying your application successfully.
|
|
21
21
|
|
|
22
22
|
## Examples
|
|
@@ -24,7 +24,10 @@ Follow these steps to set up your Harper Fabric environment for deployment.
|
|
|
24
24
|
### Environment Configuration
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
harper login YOUR_CLUSTER_URL
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# .env
|
|
32
|
+
HARPER_CLI_TARGET='YOUR_CLUSTER_URL'
|
|
30
33
|
```
|
|
@@ -48,7 +48,74 @@ npm create harper@latest my-app --template default
|
|
|
48
48
|
|
|
49
49
|
## Next Steps
|
|
50
50
|
|
|
51
|
-
1. **
|
|
52
|
-
2. **
|
|
53
|
-
3. **
|
|
54
|
-
|
|
51
|
+
1. **Define Schema**: Modify `schema.graphql` to fit your application's data model.
|
|
52
|
+
2. **Start Development**: Run `npm run dev` to start the local Harper instance.
|
|
53
|
+
3. **Connect the CLI to your cluster**: Create a cluster (see
|
|
54
|
+
[creating-a-fabric-account-and-cluster](creating-a-fabric-account-and-cluster.md)), run `harper login` with its
|
|
55
|
+
Application URL as a super user, and set that URL as `HARPER_CLI_TARGET` in the project's `.env` file. Don't put a
|
|
56
|
+
cluster username or password in `.env` or in GitHub secrets.
|
|
57
|
+
4. **Deploy**: See [Deploying](#deploying).
|
|
58
|
+
|
|
59
|
+
## Deploying
|
|
60
|
+
|
|
61
|
+
Every template except the legacy `early-hints` example ships a GitHub Actions workflow,
|
|
62
|
+
`.github/workflows/deploy.yaml`. It tests every pull request and deploys to the cluster when a change merges to `main`.
|
|
63
|
+
The deploy job holds no Harper credential: its `harper` commands authenticate with GitHub's OIDC identity token under a
|
|
64
|
+
trust policy on the cluster. The Next.js templates' workflow also runs `next build` before it uploads.
|
|
65
|
+
|
|
66
|
+
1. **Set up CI deploys once**: Push the project to a GitHub repository, then run `npm run deploy:setup-ci` from the
|
|
67
|
+
project, logged in to the cluster as a super user. It runs `harper deploy setup=true provider=github-actions` for the
|
|
68
|
+
project, which creates a deploy-only role and user and a trust policy matched to the workflow, and sets the
|
|
69
|
+
repository's `HARPER_CLI_TARGET` variable when the `gh` CLI is signed in (otherwise it prints the command to run).
|
|
70
|
+
Running it again changes nothing. See [deploying-from-ci](deploying-from-ci.md) for what it checks and how to
|
|
71
|
+
customize it.
|
|
72
|
+
|
|
73
|
+
The command needs the Harper 5.4.0 CLI or later (`harper version`); an earlier CLI can't run it. Without 5.4.0, set
|
|
74
|
+
up the same role, user, policy and variable by hand, as a super user. The policy must match the scaffolded workflow:
|
|
75
|
+
its `deploy.yaml` file on `main`, in the `production` environment. The `audience` is the cluster URL with its port
|
|
76
|
+
and a trailing slash, and `HARPER_CLI_TARGET` must be that same string:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
gh api repos/<owner>/<repo> --jq .id # the repository_id
|
|
80
|
+
harper add_role role=<name>-ci-deploy permission='{"operations":["deploy_component","get_job","registration_info"]}'
|
|
81
|
+
harper add_user username=<name>-ci-deploy role=<name>-ci-deploy active=true password="$(openssl rand -base64 32)"
|
|
82
|
+
harper add_oidc_trust \
|
|
83
|
+
id=github-actions-<name> \
|
|
84
|
+
issuer=https://token.actions.githubusercontent.com \
|
|
85
|
+
audience=https://my-cluster.example.com:9925/ \
|
|
86
|
+
user=<name>-ci-deploy \
|
|
87
|
+
claims='{"repository_id":"<id>","workflow_ref":"<owner>/<repo>/.github/workflows/deploy.yaml@refs/heads/main","environment":"production"}'
|
|
88
|
+
gh variable set HARPER_CLI_TARGET --repo <owner>/<repo> --body https://my-cluster.example.com:9925/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
If you restrict the `production` environment's deployment branches, allow `main`.
|
|
92
|
+
|
|
93
|
+
2. **Deploy by merging to `main`**, or by running the workflow from the **Actions** tab. The workflow deploys with
|
|
94
|
+
`restart=rolling` and waits for every node to take the release. The run's summary names the `deployment_id` and its
|
|
95
|
+
`certification`.
|
|
96
|
+
3. **Deploy by hand with `npm run deploy`**: It runs `harper deploy project=<name> restart=rolling` from your machine, as
|
|
97
|
+
the user you logged in with.
|
|
98
|
+
4. **Go back to an earlier release** by activating its `deployment_id`, with nothing reinstalled:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
harper list_deployments project=<name>
|
|
102
|
+
harper deploy project=<name> deployment_id=<id> restart=rolling
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
See [deploying-to-harper-fabric](deploying-to-harper-fabric.md).
|
|
106
|
+
|
|
107
|
+
Use your package manager's equivalent of `npm run` (`pnpm run`, `bun run`, `yarn`) if the project uses another one.
|
|
108
|
+
|
|
109
|
+
Projects created before create-harper 1.13.0 deploy on version tags with a `HARPER_CLI_REFRESH_TOKEN` secret, and
|
|
110
|
+
don't have `deploy:setup-ci`. To move one over:
|
|
111
|
+
|
|
112
|
+
1. Scaffold a new project with the same name and template, and copy its `.github/workflows/deploy.yaml` over the old
|
|
113
|
+
workflow.
|
|
114
|
+
2. Set up the cluster for it, with the `project=` value that workflow passes to `harper deploy` as `<name>`: with the
|
|
115
|
+
5.4.0 CLI, `harper deploy setup=true provider=github-actions project=<name>`; with an earlier one, by hand as in
|
|
116
|
+
step 1.
|
|
117
|
+
3. Push the new workflow to `main`.
|
|
118
|
+
4. Once the workflow's run on `main` has succeeded, wait step included, delete the `HARPER_CLI_REFRESH_TOKEN` secret.
|
|
119
|
+
Deleting it doesn't revoke the token, which stays valid until it expires. To cut it off now, mint a new one for that
|
|
120
|
+
user with `harper login --for-ci`, which invalidates the old one, or deactivate the user with `alter_user` if
|
|
121
|
+
nothing else uses it.
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deploying-from-ci
|
|
3
|
+
description: >-
|
|
4
|
+
How to deploy a Harper application from GitHub Actions with OIDC trusted
|
|
5
|
+
publishing, so CI stores no Harper credential, including the one-command setup
|
|
6
|
+
with `harper deploy setup=true provider=github-actions`.
|
|
7
|
+
metadata:
|
|
8
|
+
mode: generate
|
|
9
|
+
sources:
|
|
10
|
+
- reference/v5/cli/commands.md#GitHub Actions deploys (OIDC)
|
|
11
|
+
- >-
|
|
12
|
+
learn/developers/deploying-from-ci.md#Let the workflow authenticate with
|
|
13
|
+
OIDC
|
|
14
|
+
- >-
|
|
15
|
+
learn/developers/deploying-from-ci.md#Path A: deploy a tagged release from
|
|
16
|
+
your repository
|
|
17
|
+
- >-
|
|
18
|
+
learn/developers/deploying-from-ci.md#Give Harper a durable credential for
|
|
19
|
+
a private source
|
|
20
|
+
- >-
|
|
21
|
+
learn/developers/deploying-from-ci.md#Path B: build in CI, publish to
|
|
22
|
+
GitHub Packages, deploy the artifact
|
|
23
|
+
- learn/developers/deploying-from-ci.md#Check the deployment record
|
|
24
|
+
- learn/developers/deploying-from-ci.md#Operational notes
|
|
25
|
+
- reference/v5/cli/authentication.md#Workload identity (OIDC)
|
|
26
|
+
- >-
|
|
27
|
+
reference/v5/operations-api/operations.md#Policy specificity for GitHub
|
|
28
|
+
Actions
|
|
29
|
+
- reference/v5/operations-api/operations.md#`list_oidc_trust`
|
|
30
|
+
- reference/v5/operations-api/operations.md#`drop_oidc_trust`
|
|
31
|
+
sourceCommit: 2dde0597443a13af0c753f2adbe528e8442d8ee4
|
|
32
|
+
inputHash: b235eb6a7fb4d9a1
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# Deploying a Harper Application from GitHub Actions with OIDC
|
|
36
|
+
|
|
37
|
+
Instructions for the agent to configure and execute credential-free Harper deployments from GitHub Actions using OIDC trusted publishing, including one-command setup and complete workflow examples.
|
|
38
|
+
|
|
39
|
+
## When to Use
|
|
40
|
+
|
|
41
|
+
Apply this rule when asked to deploy from CI, to set up or fix a deploy workflow (including the `.github/workflows/deploy.yaml` that create-harper projects ship), or to move a pipeline off a stored Harper credential such as `HARPER_CLI_REFRESH_TOKEN`. Use it when setting up OIDC trust for the first time, writing or debugging a deploy workflow, or revoking/rotating pipeline access.
|
|
42
|
+
|
|
43
|
+
## How It Works
|
|
44
|
+
|
|
45
|
+
### One-Command Setup (Branch-Based Deploys)
|
|
46
|
+
|
|
47
|
+
1. **Run setup once** from the project checkout, signed in as a `super_user`:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
harper login https://my-cluster.example.com:9925
|
|
51
|
+
harper deploy setup=true provider=github-actions project=my-app
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If your workflow file is not `.github/workflows/deploy.yaml`, pass `workflow=` explicitly — otherwise the policy pins `deploy.yaml` and runs of your workflow cannot match it:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
harper deploy setup=true provider=github-actions project=my-app workflow=.github/workflows/deploy.yml
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
2. **Understand what setup does** — in order:
|
|
61
|
+
- Resolves the GitHub repository's numeric id and canonical name from the `origin` remote (or `repo=<owner>/<name>`). Pass `repository_id=<id>` when the API is unavailable (e.g., private repo without `gh`).
|
|
62
|
+
- **The workflow is checked before anything is written.** It must be able to run on `main` (`branch=`) and run a job in the `production` environment (`environment=`) with `permissions: id-token: write`; one that never could stops setup. What it can't decide is reported as not verified.
|
|
63
|
+
- Creates a role and a user named `<project>-ci-deploy` with permissions limited to `deploy_component`, `get_job`, and `registration_info`, and a trust policy named `github-actions-<project>`.
|
|
64
|
+
- Reads the policy back and compares it claim by claim with what it intended to write.
|
|
65
|
+
- Sets the repository's `HARPER_CLI_TARGET` Actions variable via `gh` if one does not already exist.
|
|
66
|
+
|
|
67
|
+
3. **Handle idempotency**: setup only creates — it never updates. A role, user, or policy that already exists but differs stops setup and names what differs. To replace a policy:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
harper drop_oidc_trust id=github-actions-<project>
|
|
71
|
+
harper deploy setup=true provider=github-actions project=my-app
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Manual Setup (Tag-Based or Custom Deploys)
|
|
75
|
+
|
|
76
|
+
Use this path when deploying on version tags, because a branch-pinned policy cannot match a tag ref.
|
|
77
|
+
|
|
78
|
+
1. **Create a deploy-only role and user**:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
harper add_role role=ci_deploy permission='{"operations":["deploy_component","get_job","get_deployment","registration_info"]}'
|
|
82
|
+
harper add_user username=ci-deploy role=ci_deploy active=true password="$(openssl rand -base64 32)"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- `deploy_component` — the deploy itself.
|
|
86
|
+
- `get_job` — required for the wait step; an `operations` allowlist refuses anything not listed, so without it the wait step fails with `403`.
|
|
87
|
+
- `get_deployment` — reads the deployment record.
|
|
88
|
+
- `registration_info` — version check `harper deploy` makes before uploading.
|
|
89
|
+
- `restart_service` is not needed: Harper starts the rolling restart's job itself. The password is required to create the user but a GitHub Actions pipeline never uses it, so this command discards it.
|
|
90
|
+
|
|
91
|
+
2. **Add a trust policy** pinned to `repository_id`, `workflow_path`, and `environment` (use this shape when the tag is unknown at policy-write time):
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
harper add_oidc_trust \
|
|
95
|
+
id=my-app-release \
|
|
96
|
+
issuer=https://token.actions.githubusercontent.com \
|
|
97
|
+
audience=https://my-cluster.example.com:9925/ \
|
|
98
|
+
user=ci-deploy \
|
|
99
|
+
claims='{"repository_id":"67890","workflow_path":"my-org/my-app/.github/workflows/deploy.yml","environment":"production"}'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Key claim rules:
|
|
103
|
+
- `repository_id` is immutable — survives renames. Look it up with `gh api repos/my-org/my-app --jq .id`.
|
|
104
|
+
- `workflow_path` pins the workflow file. Harper derives it from the token's `workflow_ref` by removing the `@<ref>` suffix, so it is `<owner>/<repo>/<path>`.
|
|
105
|
+
- `environment` is the ref gate. Without it, anyone who can push a branch can mint a token.
|
|
106
|
+
- `audience` must be the exact target URL with its port and a **trailing slash**.
|
|
107
|
+
- A workflow that deploys on a push to `main` can pin `workflow_ref` (`my-org/my-app/.github/workflows/deploy.yml@refs/heads/main`) instead, which gates the ref by itself.
|
|
108
|
+
|
|
109
|
+
3. **Configure the GitHub environment**: in **Settings → Environments → production**, set **Deployment branches and tags** to the `v*` tag pattern. Add required reviewers if a release needs approval.
|
|
110
|
+
|
|
111
|
+
4. **Store the target as a repository variable** (not a secret — it is not sensitive):
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
gh variable set HARPER_CLI_TARGET --repo my-org/my-app --body https://my-cluster.example.com:9925/
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### How OIDC Authentication Works in the Workflow
|
|
118
|
+
|
|
119
|
+
Once a trust policy exists, every `harper` command in the deploy job authenticates through it automatically when all four conditions hold:
|
|
120
|
+
|
|
121
|
+
1. **The job may request an identity token.** On GitHub Actions that is `permissions: id-token: write` on the job.
|
|
122
|
+
2. **`HARPER_CLI_TARGET` is the policy's `audience`.** The CLI requests its token for the target it connects to, so the target must be the URL the policy names, with its port and trailing slash. Use `vars.HARPER_CLI_TARGET` — it is not sensitive, so a repository variable rather than a secret.
|
|
123
|
+
3. **The run matches the policy's claims**: its repository, its workflow file and branch, and its environment.
|
|
124
|
+
4. **Nothing else is configured to authenticate.** `auth_username=`/`auth_password=`, credentials in the target URL, `HARPER_CLI_USERNAME`/`HARPER_CLI_PASSWORD`, `HARPER_CLI_REFRESH_TOKEN` or `HARPER_CLI_OPERATION_TOKEN`, and a saved `harper login` all outrank the exchange.
|
|
125
|
+
|
|
126
|
+
What each command may then do is up to the role of the user the policy names: a role that lists only `deploy_component`, `get_job` and `registration_info`, like the one setup creates, refuses anything else with a `403`.
|
|
127
|
+
|
|
128
|
+
The exchange runs only when GitHub sets `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN`, which it does for a job that declares `permissions: id-token: write`. Without them, the CLI falls through to its other credential sources rather than reporting a failure.
|
|
129
|
+
|
|
130
|
+
Nothing in the workflow names the policy. The CLI prints to stderr which policy authenticated it and as whom:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
Requesting a GitHub Actions identity token for https://my-instance.harperdb.io:9925/...
|
|
134
|
+
Authenticated as 'ci-deploy' via OIDC trust policy 'my-app-prod'.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Diagnose a rejected exchange with `list_oidc_trust` (check for `invalid_reason` on each entry) and the instance's `oidc-trust` log. The server deliberately does not report which check failed.
|
|
138
|
+
|
|
139
|
+
### Policy Specificity for GitHub Actions
|
|
140
|
+
|
|
141
|
+
For `https://token.actions.githubusercontent.com`, a policy must satisfy all three of these:
|
|
142
|
+
|
|
143
|
+
| Requirement | Satisfied by one of | Left open otherwise |
|
|
144
|
+
| ---------------------- | ------------------------------------------------------------------------ | ----------------------------------------------- |
|
|
145
|
+
| **Pin the repository** | `repository_id`, `repository` | Any repository |
|
|
146
|
+
| **Pin the workflow** | `workflow_ref`, `workflow_path`, `job_workflow_ref`, `job_workflow_path` | Any workflow in that repository |
|
|
147
|
+
| **Gate the ref** | `workflow_ref`, `ref`, `environment` | Any branch that can be pushed to the repository |
|
|
148
|
+
|
|
149
|
+
Additional constraints:
|
|
150
|
+
|
|
151
|
+
- `repository_id` is preferred over `repository` — it is immutable and survives renames.
|
|
152
|
+
- A tag-triggered release cannot pin `workflow_ref` since the tag is unknown when the policy is written. Pin `workflow_path` and gate on `environment`.
|
|
153
|
+
- `ref_type: tag` is not accepted as a ref gate, `sub` is not accepted as a pin, and `job_workflow_ref` pins the workflow without gating the ref. `pull_request_target` runs are denied unless the policy constrains `event_name`.
|
|
154
|
+
|
|
155
|
+
### Revoking Access
|
|
156
|
+
|
|
157
|
+
- **Stop future runs**: `harper drop_oidc_trust id=github-actions-<project>` (does not revoke already-issued tokens, which are valid up to one hour).
|
|
158
|
+
- **Stop an already-issued token**: `harper alter_user username=<project>-ci-deploy active=false`.
|
|
159
|
+
- **No CI secret to rotate** — there is none.
|
|
160
|
+
|
|
161
|
+
## Examples
|
|
162
|
+
|
|
163
|
+
### Path A: Deploy a Tagged Release (`v1.2.3`) from the Repository
|
|
164
|
+
|
|
165
|
+
Push a semver tag (`v1.2.3`), and every node clones and installs that commit. `by_ref=true` deploys `git+https://github.com/<owner>/<repo>.git#<sha>`, where the SHA is the commit the tag points to (`GITHUB_SHA`), and every node clones that exact commit even if the tag moves later. For a private repository, add `credential=true` to attach the stored credential (`deploy.my-app.git.github.com`). If the repository needs a build step to be runnable, don't reach for `install_allow_scripts`: use Path B.
|
|
166
|
+
|
|
167
|
+
```yaml
|
|
168
|
+
# .github/workflows/deploy.yml
|
|
169
|
+
name: Deploy to Harper
|
|
170
|
+
on:
|
|
171
|
+
push:
|
|
172
|
+
tags: ['v*']
|
|
173
|
+
|
|
174
|
+
jobs:
|
|
175
|
+
deploy:
|
|
176
|
+
runs-on: ubuntu-latest
|
|
177
|
+
environment: production
|
|
178
|
+
permissions:
|
|
179
|
+
contents: read
|
|
180
|
+
id-token: write
|
|
181
|
+
env:
|
|
182
|
+
HARPER_CLI_TARGET: ${{ vars.HARPER_CLI_TARGET }}
|
|
183
|
+
steps:
|
|
184
|
+
- uses: actions/checkout@v4
|
|
185
|
+
with:
|
|
186
|
+
persist-credentials: false
|
|
187
|
+
- uses: actions/setup-node@v4
|
|
188
|
+
with:
|
|
189
|
+
node-version: 22
|
|
190
|
+
- run: npm install --global harper@^5.3
|
|
191
|
+
- name: Deploy the tagged commit
|
|
192
|
+
run: harper deploy project=my-app by_ref=true restart=rolling json=true > deploy.json
|
|
193
|
+
- name: Wait for every node to take the release
|
|
194
|
+
run: |
|
|
195
|
+
JOB_ID=$(jq -r '.restartJobId // empty' deploy.json)
|
|
196
|
+
[ -n "$JOB_ID" ] || { echo "The rolling deploy returned no restartJobId"; exit 1; }
|
|
197
|
+
LAST="could not be read"
|
|
198
|
+
for attempt in $(seq 60); do
|
|
199
|
+
sleep 10
|
|
200
|
+
harper get_job id="$JOB_ID" json=true > job.json || continue
|
|
201
|
+
case $(jq -r '.[0].status // "MISSING"' job.json) in
|
|
202
|
+
COMPLETE) exit 0 ;;
|
|
203
|
+
ERROR) jq -r '.[0].message' job.json; exit 1 ;;
|
|
204
|
+
MISSING) LAST="was not on the node that answered; its outcome is unknown" ;;
|
|
205
|
+
*) LAST="was still running" ;;
|
|
206
|
+
esac
|
|
207
|
+
done
|
|
208
|
+
echo "After 60 polls, job $JOB_ID $LAST"
|
|
209
|
+
exit 1
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Key details:
|
|
213
|
+
|
|
214
|
+
- **No secrets.** `HARPER_CLI_TARGET` is the only thing the workflow is given. `id-token: write` lets the CLI request an identity token, and `environment: production` puts the `environment` claim the trust policy requires into it.
|
|
215
|
+
- **`persist-credentials: false`** keeps the job's `GITHUB_TOKEN` out of `.git/config`.
|
|
216
|
+
- **`harper@^5.3`** pins the CLI's major version. 5.3 is the first CLI that authenticates with the identity token.
|
|
217
|
+
- **`replicated` isn't needed.** On Harper Fabric and Harper Pro, a deploy goes to every node unless you pass `replicated=false`. Each node fetches and installs the package itself.
|
|
218
|
+
- **`restart=rolling`** restarts nodes one at a time. Use `restart=true` for a single-node or dev instance.
|
|
219
|
+
- **`json=true`** prints the deploy's result as JSON on stdout; progress and authentication messages go to stderr.
|
|
220
|
+
- **The wait step** polls `restartJobId` via `get_job` until `COMPLETE` or `ERROR`, checking `.[0].status`. It retries a poll that fails (such as one that reaches a node while it restarts) and gives up after 60 polls (~10 minutes).
|
|
221
|
+
- **`certification`**: on v5.4.0 and later, `restart=rolling` loads the release in a canary worker on each node before that node serves it, so a release that fails to load is rejected. The `certification` field in the deploy result reflects this. With `restart=rolling` on v5.4.0 and later, the deploy can report `success` before the rolling job has visited all nodes — wait on the job, not the record.
|
|
222
|
+
- **The job lives on one node.** Through a **load-balanced** cluster URL, polls must eventually reach that node. Routing that always sends polls to a different node than the deploy causes the wait step to fail saying the job was not on the node that answered.
|
|
223
|
+
|
|
224
|
+
### Path B: Build in CI, Publish to GitHub Packages, Deploy the Artifact
|
|
225
|
+
|
|
226
|
+
Give `package.json` a scoped name and `"publishConfig": { "registry": "https://npm.pkg.github.com" }`:
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"name": "@my-org/my-app",
|
|
231
|
+
"version": "1.2.3",
|
|
232
|
+
"publishConfig": { "registry": "https://npm.pkg.github.com" },
|
|
233
|
+
"files": ["config.yaml", "schema.graphql", "dist/"]
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The deploy installs the version the tag names, and `npm publish` publishes the version in `package.json`, so they must agree: `npm version 1.2.3` sets one and creates the other as `v1.2.3`.
|
|
238
|
+
|
|
239
|
+
```yaml
|
|
240
|
+
# .github/workflows/deploy.yml
|
|
241
|
+
name: Build, publish, and deploy to Harper
|
|
242
|
+
on:
|
|
243
|
+
push:
|
|
244
|
+
tags: ['v*']
|
|
245
|
+
|
|
246
|
+
jobs:
|
|
247
|
+
publish:
|
|
248
|
+
runs-on: ubuntu-latest
|
|
249
|
+
permissions:
|
|
250
|
+
contents: read
|
|
251
|
+
packages: write
|
|
252
|
+
steps:
|
|
253
|
+
- uses: actions/checkout@v4
|
|
254
|
+
with:
|
|
255
|
+
persist-credentials: false
|
|
256
|
+
- uses: actions/setup-node@v4
|
|
257
|
+
with:
|
|
258
|
+
node-version: 22
|
|
259
|
+
registry-url: https://npm.pkg.github.com
|
|
260
|
+
scope: '@my-org'
|
|
261
|
+
- run: npm ci
|
|
262
|
+
env:
|
|
263
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
264
|
+
- run: npm run build
|
|
265
|
+
- run: npm test
|
|
266
|
+
- name: Publish to GitHub Packages
|
|
267
|
+
run: npm publish
|
|
268
|
+
env:
|
|
269
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
270
|
+
|
|
271
|
+
deploy:
|
|
272
|
+
needs: publish
|
|
273
|
+
runs-on: ubuntu-latest
|
|
274
|
+
environment: production
|
|
275
|
+
permissions:
|
|
276
|
+
id-token: write
|
|
277
|
+
env:
|
|
278
|
+
HARPER_CLI_TARGET: ${{ vars.HARPER_CLI_TARGET }}
|
|
279
|
+
steps:
|
|
280
|
+
- uses: actions/setup-node@v4
|
|
281
|
+
with:
|
|
282
|
+
node-version: 22
|
|
283
|
+
- run: npm install --global harper@^5.3
|
|
284
|
+
- name: Deploy the published version
|
|
285
|
+
run: |
|
|
286
|
+
harper deploy project=my-app package="@my-org/my-app@${GITHUB_REF_NAME#v}" \
|
|
287
|
+
credentials='[{"registry":"https://npm.pkg.github.com","scope":"@my-org","secret":"deploy.my-app.npm.pkg.github.com"}]' \
|
|
288
|
+
restart=rolling json=true > deploy.json
|
|
289
|
+
- name: Wait for every node to take the release
|
|
290
|
+
run: |
|
|
291
|
+
JOB_ID=$(jq -r '.restartJobId // empty' deploy.json)
|
|
292
|
+
[ -n "$JOB_ID" ] || { echo "The rolling deploy returned no restartJobId"; exit 1; }
|
|
293
|
+
LAST="could not be read"
|
|
294
|
+
for attempt in $(seq 60); do
|
|
295
|
+
sleep 10
|
|
296
|
+
harper get_job id="$JOB_ID" json=true > job.json || continue
|
|
297
|
+
case $(jq -r '.[0].status // "MISSING"' job.json) in
|
|
298
|
+
COMPLETE) exit 0 ;;
|
|
299
|
+
ERROR) jq -r '.[0].message' job.json; exit 1 ;;
|
|
300
|
+
MISSING) LAST="was not on the node that answered; its outcome is unknown" ;;
|
|
301
|
+
*) LAST="was still running" ;;
|
|
302
|
+
esac
|
|
303
|
+
done
|
|
304
|
+
echo "After 60 polls, job $JOB_ID $LAST"
|
|
305
|
+
exit 1
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Two jobs keep the identity token isolated: `publish` installs, builds, tests and publishes with `contents: read` and `packages: write` only — it cannot request an identity token. `deploy` runs once `publish` finishes (`needs: publish`), holds `environment: production` and `id-token: write`, and installs only the Harper CLI before deploying.
|
|
309
|
+
|
|
310
|
+
Credential roles:
|
|
311
|
+
|
|
312
|
+
- **Publishing** and installing private `@my-org` dependencies use the workflow's ephemeral `GITHUB_TOKEN` (`packages: write` includes read). Both happen entirely inside `publish`.
|
|
313
|
+
- **Installing on Harper nodes** uses the durable `read:packages` token stored as `deploy.my-app.npm.pkg.github.com` — because installs can happen long after this workflow ends.
|
|
314
|
+
|
|
315
|
+
For `my-app`, the credential names are `deploy.my-app.git.github.com` for the repository and `deploy.my-app.npm.pkg.github.com` for GitHub Packages. Store a durable credential once from your machine:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
harper deploy setup=true provider=github project=my-app
|
|
319
|
+
# or for GitHub Packages:
|
|
320
|
+
harper deploy setup=true provider=npm project=my-app registry=https://npm.pkg.github.com scope=@my-org
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
For a private repository, use a GitHub fine-grained personal access token with **Contents: read-only** scoped to the application repository. For GitHub Packages, use a token with the `read:packages` scope. Do not use the workflow's ephemeral `GITHUB_TOKEN` for the durable credential — it expires when the job ends.
|
|
324
|
+
|
|
325
|
+
### Check the Deployment Record
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
harper get_deployment deployment_id="$(jq -r .deployment_id deploy.json)" json=true
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Only the node you called writes that record, so with `restart=rolling` on v5.4.0 and later it can report `success` before the job has visited the other nodes, and keeps reporting it if one of them rejects the release. Wait on the job, not the record, to confirm all nodes took the release. `list_deployments` gives the history of what was deployed and when.
|
|
332
|
+
|
|
333
|
+
### List and Inspect Trust Policies
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
harper list_oidc_trust
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Returns `{ "policies": [ ... ] }`. Each entry includes `id`, `issuer`, `audience`, `claims`, `user`, `enabled`, and optionally `invalid_reason`. Check `invalid_reason` first when diagnosing a rejected exchange — it is set when the policy is malformed or its user was deleted or deactivated.
|
|
340
|
+
|
|
341
|
+
### Drop a Trust Policy
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
harper drop_oidc_trust id=github-actions-<project>
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Notes
|
|
348
|
+
|
|
349
|
+
- `HARPER_CLI_REFRESH_TOKEN` and any other stored credential outrank the OIDC exchange. Remove them from the workflow if switching to OIDC.
|
|
350
|
+
- **Go back to a previous release** by activating its `deployment_id`, with no rebuild or reinstall: `harper deploy project=my-app deployment_id=<id> restart=rolling`. To deploy a release that is no longer kept, re-run the deploy with its tag or version.
|
|
351
|
+
- **Rotating the deploy credential**: run `harper deploy setup=true` again with the same name and a new value. The pipeline doesn't change.
|
|
352
|
+
- **Revoking the pipeline's access**: `harper drop_oidc_trust` stops new runs; `harper alter_user` also stops a token already issued (valid up to one hour).
|
|
353
|
+
- The `<project>-ci-deploy` user created by setup can deploy any component on the cluster, not only the named project. Access control comes down to who can merge to the branch.
|
|
354
|
+
- The `audience` in `add_oidc_trust` must be the exact target URL with its port and a trailing slash.
|
|
355
|
+
- For tag-triggered workflows, `provider=github-actions` setup is not sufficient — it pins a branch. Write the policy manually using `add_oidc_trust` with `workflow_path` and `environment` claims.
|
|
356
|
+
- `token.actions.githubusercontent.com` is the required `issuer` value for GitHub Actions OIDC policies.
|
|
357
|
+
- Git-host credentials require git ≥ 2.31 on the Harper nodes and are not supported on Windows nodes. Registry credentials have no such constraints.
|
|
358
|
+
- See [deploying-to-harper-fabric.md](deploying-to-harper-fabric.md) for cluster-level deployment context.
|