# Releases and versioning

Every site deploy and every installer release carries a **version** and a **build number**. Agents manage both
automatically on every deploy, so the user never has to think about them.

## Policy

- **Build number**: goes up on every deploy. heremyapp assigns site builds itself (1, 2, 3, ...). For installers the
  build is the app's own build number (for example `CFBundleVersion`), which you increase before building.
- **Version**: [Semantic Versioning](https://semver.org) `MAJOR.MINOR.PATCH`, bumped by what changed since the previous
  release:

| Bump | Apps and installers | Landing pages | Example |
|---|---|---|---|
| major | Incompatible or large changes: removed features, a new minimum OS, data or file formats older versions cannot read, a paid upgrade | Redesign or rebrand | 1.4.2 → 2.0.0 |
| minor | New features or improvements: dark mode, a new setting, a new integration, noticeably faster | New sections, pages or languages | 1.4.2 → 1.5.0 |
| patch | Fixes: bug and crash fixes, hotfixes, translation fixes | Updating release info (version, size, notes), typos, broken links, small style fixes | 1.4.2 → 1.4.3 |

When a release mixes changes, use the largest bump that applies: bug fixes plus dark mode is **minor**.

Decide the bump yourself from the changes you made or can see (your edits, `git diff`, `git log`). Do not ask the user
to pick a version; tell them the result in your report. Use a pre-release such as `2.0.0-beta.1` only when the user asks
for a beta.

## Before every deploy: check the current versions

```bash
curl -sS --fail-with-body "$API/spaces/my-app/versions" -H "$AUTH"
```

```json
{"policy":"Every deploy gets the next build number automatically. ...",
 "site":{"live":{"deployment_id":"dep_5adj...","version":"1.4.2","build":17,"notes":"Fix typo","created_at":"..."},
         "latest":{"deployment_id":"dep_5adj...","version":"1.4.2","build":17,"notes":"Fix typo","created_at":"..."},
         "next":{"build":18,"patch":"1.4.3","minor":"1.5.0","major":"2.0.0"}},
 "artifacts":[{"filename":"MyApp.pkg","latest":{"id":"art_3kq9...","version":"1.2.0","build":"45","status":"active"},
               "live":{"id":"art_3kq9...","version":"1.2.0","build":"45"},
               "next":{"patch":"1.2.1","minor":"1.3.0","major":"2.0.0","build":"46"}}],
 "contact_status":"provided"}
```

- `site.live` is what visitors see now; `site.latest` is the newest deploy (they differ after a rollback).
  Next numbers always continue from `latest`.
- For each installer file, `latest` is the highest version ever published (including deleted ones, whose numbers cannot
  be reused) and `live` is what the `latest` download link serves now.
- `contact_status` tells you whether to ask the user for contact details (see
  [Landing page checklist](https://heremyapp.com/docs/landing.md)).

## Deploy a site with its version

Add `bump` (and a one-line `notes` summary) to the deploy URL. curl's `--url-query` encodes the values for you:

```bash
tar -czf - -C dist . | curl -sS --fail-with-body -X POST "$API/spaces/my-app/deployments" \
  --url-query "bump=minor" --url-query "notes=Add Japanese page" \
  -H "$AUTH" -H "content-type: application/gzip" --data-binary @-
```

- curl older than 7.87 has no `--url-query`: put the values in the URL instead and percent-encode the notes, for
  example `"$API/spaces/my-app/deployments?bump=minor&notes=Add%20Japanese%20page"`.
- The response includes `version` and `build`. Without `bump`, heremyapp bumps the patch version.
- Send `version=X.Y.Z` instead of `bump` to set an exact version; it must be higher than the current one
  (`409 version_not_increasing` lists suggestions).
- The first deploy of a space is `1.0.0`, build 1.
- Every page of the live site carries `X-Heremyapp-Version` and `X-Heremyapp-Build` headers, so you can confirm the new
  version is live:

```bash
curl -sIL https://heremyapp.com/ACCOUNT/my-app | grep -iE "^x-heremyapp-(version|build)"
```

## Release an installer with its version

1. Read the app's current version and build from the project:
   - Xcode: `MARKETING_VERSION` and `CURRENT_PROJECT_VERSION` build settings (`CFBundleShortVersionString` and
     `CFBundleVersion`). With Apple Generic Versioning: `agvtool what-marketing-version`, `agvtool what-version`.
   - Electron or Node: `version` in `package.json`; a build number if the project keeps one.
2. Work out the new numbers from `artifacts[].latest` (the published release):
   - Version: bump the published version by the policy above. If the project's version is already higher than that
     (the user or an earlier step set it and it was never published), keep the project's version.
   - Build: one more than the published build, or the project's build if that is already higher.
   - Build numbers compare part by part as numbers (`46 < 47`, `1.2.9 < 1.2.10`). Keep one format per app.
3. Write them into the project (for example `agvtool new-marketing-version 1.3.0` and `agvtool new-version -all 46`,
   or edit the build settings), then build, sign and notarize.
4. Register the installer with `version`, `build` and `notes` ([Installers](https://heremyapp.com/docs/installers.md)). heremyapp rejects a
   version or build that does not increase (`version_not_increasing`, `build_not_increasing`, with suggestions).
5. Update the version, size and release notes shown on the landing page, and deploy the site (usually `bump=patch`,
   or `minor` if you added content).

The landing page should show the **app's** version (from the installer). The site's own version tracks changes to the
page itself.
