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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/github_release_changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: minor
---

# The changelog page now shows each GitHub release's notes
35 changes: 35 additions & 0 deletions .github/workflows/changelog_publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Publish Changelog

on:
release:
types: [published, edited]
workflow_dispatch:

concurrency:
group: changelog-publish
cancel-in-progress: true

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
steps:
# The launcher reads releases/latest/download/changelog.json, so the
# snapshot of the last 50 release bodies always lives on the latest release.
- name: Build changelog.json from release bodies
run: |
gh api "repos/$GH_REPO/releases?per_page=50" --jq '[.[]
| select(.draft | not)
| select(.tag_name | startswith("oneclient-"))
| {
version: (.tag_name | ltrimstr("oneclient-")),
body: ((.body // "") | sub("^Automated release for OneClient [^\n]*\\s*"; ""))
}]' > changelog.json
jq -r '.[].version' changelog.json

- name: Upload to latest release
run: gh release upload "$(gh release view --json tagName -q .tagName)" changelog.json --clobber
103 changes: 36 additions & 67 deletions .github/workflows/oneclient_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,6 @@ name: OneClient Release Build

on:
workflow_dispatch:
inputs:
bump:
description: 'Bump the workspace version in Cargo.toml before releasing'
type: choice
required: false
default: none
options:
- none
- patch
- minor
- major

# NOTE: CARGO_PACKAGER_SIGN_PRIVATE_KEY* are deliberately NOT declared at
# workflow level. Swatinem/rust-cache folds every `CARGO*`-prefixed environment
Expand All @@ -37,18 +26,25 @@ jobs:
sha: ${{ steps.version.outputs.sha }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
# Knope finds the commit (and so the PR author) that added each change file
fetch-depth: 0

- name: Setup Rust
if: inputs.bump != 'none'
uses: ./.github/actions/setup-rust
with:
restore-cache: false

- name: Resolve version, bump if requested, refuse to clobber a release
- name: Install Knope
uses: ./.github/actions/install-cargo-tool
with:
tool: knope
version: 0.23.0

- name: Consume changesets
id: version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUMP: ${{ inputs.bump }}
shell: bash
run: |
set -euo pipefail
Expand All @@ -57,63 +53,29 @@ jobs:
sed -n -E '/^\[workspace\.package\]/,/^\[/ s/^version[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/p' Cargo.toml | head -n1
}

# No change files means a re-run of a release whose version commit
# already landed, so carry on with the current version.
RELEASED=0
if compgen -G ".changeset/*.md" > /dev/null; then
knope prepare-release
cargo update --workspace --offline || cargo update --workspace
RELEASED=1
fi

VERSION="$(read_version)"
if [ -z "$VERSION" ]; then
echo "::error::Could not read version from [workspace.package] in Cargo.toml"
exit 1
fi
echo "Current version: $VERSION"

if [ "$BUMP" != "none" ]; then
if ! [[ "$VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
echo "::error::Version '$VERSION' is not plain semver major.minor.patch; cannot bump automatically."
exit 1
fi
MAJOR="${BASH_REMATCH[1]}"; MINOR="${BASH_REMATCH[2]}"; PATCH="${BASH_REMATCH[3]}"
case "$BUMP" in
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
patch) PATCH=$((PATCH + 1)) ;;
esac
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
echo "Bumping ($BUMP): $VERSION -> $NEW_VERSION"

# Only rewrite the `version` key inside [workspace.package]; the
# dependency pins further down the file must not be touched.
NEW_VERSION="$NEW_VERSION" python3 - <<'PY'
import os, re
new = os.environ["NEW_VERSION"]
src = open("Cargo.toml").read()
start = src.index("[workspace.package]")
end = src.find("\n[", start + 1)
end = len(src) if end == -1 else end
section, count = re.subn(
r'(?m)^version\s*=\s*"[^"]+"', f'version = "{new}"', src[start:end], count=1
)
assert count == 1, "no version key found in [workspace.package]"
open("Cargo.toml", "w").write(src[:start] + section + src[end:])
PY

# Workspace members inherit `version.workspace = true`, so only the
# lockfile's own entries for those members need refreshing.
cargo update --workspace --offline || cargo update --workspace

ACTUAL="$(read_version)"
if [ "$ACTUAL" != "$NEW_VERSION" ]; then
echo "::error::Version rewrite failed (Cargo.toml still reports '$ACTUAL')"
exit 1
fi
VERSION="$NEW_VERSION"
fi

TAG="oneclient-$VERSION"
echo "Releasing OneClient $VERSION"

# Abort rather than clobber: a published release (or an existing tag,
# which only appears once a release is published) means this version
# already shipped. A *draft* is a previous failed/partial run of this
# same workflow, so reuse it.
if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
echo "::error::Tag $TAG already exists on origin - OneClient $VERSION has already been released. Re-run with a bump, or bump the version in Cargo.toml."
echo "::error::Tag $TAG already exists on origin - OneClient $VERSION has already been released. Add a change file with \`knope document-change\` first."
exit 1
fi

Expand All @@ -123,17 +85,17 @@ jobs:
RELEASE_EXISTS=1
fi
if [ "$RELEASE_EXISTS" = "1" ] && [ "$IS_DRAFT" != "true" ]; then
echo "::error::Release $TAG already exists and is published - refusing to overwrite OneClient $VERSION. Re-run with a bump, or bump the version in Cargo.toml."
echo "::error::Release $TAG already exists and is published - refusing to overwrite OneClient $VERSION. Add a change file with \`knope document-change\` first."
exit 1
fi

# Commit the bump only after the guards pass, so a rejected release
# does not leave a stray version bump on the branch.
if [ "$BUMP" != "none" ]; then
# Commit only after the guards pass, so a rejected release does not
# leave a stray version bump on the branch.
if [ "$RELEASED" = "1" ]; then
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Cargo.toml Cargo.lock
git commit -m "chore: bump to $VERSION"
git add -A Cargo.toml Cargo.lock .changeset CHANGELOG.md
git commit -m "chore: release $VERSION"
git push origin "HEAD:${GITHUB_REF_NAME}"
fi

Expand All @@ -143,11 +105,18 @@ jobs:
echo "Reusing existing draft release $TAG"
gh release edit "$TAG" --target "$SHA"
else
# The body is what the launcher's changelog page shows
awk -v heading="## $VERSION " '/^## /{p = index($0, heading) == 1; next} p' \
CHANGELOG.md > notes.md 2>/dev/null || true
if ! grep -q '[^[:space:]]' notes.md; then
echo "::error::No changelog entry for $VERSION. Add a change file with \`knope document-change\` first."
exit 1
fi
gh release create "$TAG" \
--draft \
--target "$SHA" \
--title "OneClient $VERSION" \
--notes "Automated release for OneClient $VERSION."
--notes-file notes.md
fi

echo "version=$VERSION" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -189,7 +158,7 @@ jobs:

runs-on: ${{ matrix.platform }}
steps:
# Pin to the commit `prepare` resolved - with `bump` that is the version
# Pin to the commit `prepare` resolved - after a release that is the version
# commit it just pushed, which github.sha would not point at.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
Expand Down
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ and per-OS prerequisites.
Once you have finished making your changes, create a pull request (PR) to submit them.

- Fill out the template to help reviewers understand your changes and the purpose of your PR.
- If your change is user-facing, add a change file with `knope document-change`, or create `.changeset/<anything>.md` by hand:
```md
---
default: patch
---

# Fixed the thing players noticed
```
Use `patch` for fixes, `minor` for features and `major` for breaking changes. The title becomes your line in the release notes.
- If you are addressing an existing issue, don't forget to [link your PR to the issue].
- Enable the checkbox to [allow maintainer edits] so that the branch can be updated for merging.
- Once you submit your PR, a team member will review your proposal. They may ask questions or request additional information.
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ cargo packager --release -p oneclient_app --formats <targets>

The workspace shares a single version, defined in the root [`Cargo.toml`](./Cargo.toml) under `[workspace.package]`.

Versions and release notes come from [Knope](https://knope.tech) change files in [`.changeset/`](./.changeset).
Add one per user-facing change with `knope document-change` (installed via `cargo install knope`). The
`OneClient Release Build` workflow consumes them, bumps the version, writes [`CHANGELOG.md`](./CHANGELOG.md),
and uses the new entry as the GitHub release body, which is what the launcher's changelog page shows.


## Code signing

Expand Down
28 changes: 28 additions & 0 deletions knope.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
versioned_files = ["Cargo.toml"]
changelog = "CHANGELOG.md"

[changes]
ignore_conventional_commits = true

[release_notes]
change_templates = [
"- $summary by [$pr_author_login](https://github.com/$pr_author_login)",
"- $summary",
]

[github]
owner = "Polyfrost"
repo = "OneLauncher"

[[workflows]]
name = "prepare-release"

[[workflows.steps]]
type = "PrepareRelease"

[[workflows]]
name = "document-change"

[[workflows.steps]]
type = "CreateChangeFile"
2 changes: 1 addition & 1 deletion packages/oneclient_app/src/hooks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub use queries::{
UseDiscardLeftovers, UseLogAction, UseRefreshAccount, UseRemoveAccount, UseScreenshotAction,
UseSetDefaultAccount, UseStorageAction, UseUploadLog, VERSIONS_PAGE_SIZE,
accounts_have_microsoft, bundle_overrides_map, bundles_with_status_items, category_list,
changelog_error, changelog_groups, changelog_is_loading, cluster_content_items,
changelog_entries, changelog_error, changelog_is_loading, cluster_content_items,
content_type_for_slug, has_migration_data, invalidate_cluster_content_queries,
invalidate_cluster_queries, invalidate_java_queries, invalidate_leftovers_queries,
invalidate_logs_queries, invalidate_profile_queries, invalidate_screenshots_queries,
Expand Down
22 changes: 7 additions & 15 deletions packages/oneclient_app/src/hooks/queries/changelog.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,30 @@
use freya::query::{Query, QueryCapability, UseQuery, use_query};
use oneclient_core::{ChangelogGroup, LauncherError, fetch_changelog, parse_changelog};

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ChangelogKeys {
pub meta_url_base: String,
}
use oneclient_core::{ChangelogEntry, LauncherError, fetch_changelog};

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ChangelogQuery;

impl QueryCapability for ChangelogQuery {
type Ok = Vec<ChangelogGroup>;
type Ok = Vec<ChangelogEntry>;
type Err = LauncherError;
type Keys = ChangelogKeys;
type Keys = ();

async fn run(&self, _keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
let state = crate::launcher::state()?;
let markdown = fetch_changelog(&state.services.requester).await?;
Ok(parse_changelog(&markdown))
fetch_changelog(&state.services.requester).await
}
}

pub fn use_changelog() -> UseQuery<ChangelogQuery> {
let meta_url_base = super::use_meta_url_key();

use_query(Query::new(ChangelogKeys { meta_url_base }, ChangelogQuery))
use_query(Query::new((), ChangelogQuery))
}

pub fn changelog_groups(query: &UseQuery<ChangelogQuery>) -> Option<Vec<ChangelogGroup>> {
pub fn changelog_entries(query: &UseQuery<ChangelogQuery>) -> Option<Vec<ChangelogEntry>> {
super::state::settled_or_loading(query)
}

pub fn latest_changelog_version(query: &UseQuery<ChangelogQuery>) -> Option<String> {
changelog_groups(query).and_then(|groups| groups.first().map(|group| group.version.clone()))
changelog_entries(query).and_then(|entries| entries.first().map(|entry| entry.version.clone()))
}

pub fn changelog_error(query: &UseQuery<ChangelogQuery>) -> Option<String> {
Expand Down
2 changes: 1 addition & 1 deletion packages/oneclient_app/src/hooks/queries/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub use bundles::{
use_onboarding_bundles,
};
pub use changelog::{
changelog_error, changelog_groups, changelog_is_loading, latest_changelog_version,
changelog_entries, changelog_error, changelog_is_loading, latest_changelog_version,
use_changelog,
};
pub use cluster_content::{cluster_content_items, use_cluster_content};
Expand Down
Loading