diff --git a/.github/actions/draft-changelog/action.yml b/.github/actions/draft-changelog/action.yml
index 31caf692..0e2443fe 100644
--- a/.github/actions/draft-changelog/action.yml
+++ b/.github/actions/draft-changelog/action.yml
@@ -44,6 +44,8 @@ runs:
         export RH_DRY_RUN=${{ inputs.dry_run }}
         export RH_REF=${GITHUB_REF}
 
+        # Install Jupyter Releaser from git
+        pip install -q git+https://github.com/jupyter-server/jupyter_releaser.git@v1
+
         # Draft Changelog
-        pip install -q jupyter-releaser
         python -m jupyter_releaser.actions.draft_changelog
diff --git a/.github/actions/draft-release/action.yml b/.github/actions/draft-release/action.yml
index 02790443..429ee6e3 100644
--- a/.github/actions/draft-release/action.yml
+++ b/.github/actions/draft-release/action.yml
@@ -48,5 +48,8 @@ runs:
         export RH_POST_VERSION_SPEC=${{ inputs.post_version_spec }}
         export RH_DRY_RUN=${{ inputs.dry_run }}
 
+        # Install Jupyter Releaser from git
+        pip install -q git+https://github.com/jupyter-server/jupyter_releaser.git@v1
+
         # Draft Release
         python -m jupyter_releaser.actions.draft_release
diff --git a/.github/actions/publish-release/action.yml b/.github/actions/publish-release/action.yml
index 3ea6d484..d88e1a7f 100644
--- a/.github/actions/publish-release/action.yml
+++ b/.github/actions/publish-release/action.yml
@@ -31,6 +31,8 @@ runs:
         export RH_DRY_RUN=${{ inputs.dry_run }}
         export release_url=${{ inputs.release_url }}
 
+        # Install Jupyter Releaser from git
+        pip install -q git+https://github.com/jupyter-server/jupyter_releaser.git@v1
+
         # Publish release
-        pip install -q jupyter-releaser
         python -m jupyter_releaser.actions.publish_release
diff --git a/.github/workflows/draft-changelog.yml b/.github/workflows/draft-changelog.yml
index 94cbe2c9..44917cfe 100644
--- a/.github/workflows/draft-changelog.yml
+++ b/.github/workflows/draft-changelog.yml
@@ -29,12 +29,9 @@ jobs:
       - name: Upgrade packaging dependencies
         run: |
           pip install --upgrade pip setuptools wheel --user
-      - name: Install Dependencies
-        run: |
-          pip install -e .
       - name: Draft Changelog
         id: draft-changelog
-        uses: ./.github/actions/draft-changelog
+        uses: jupyter-server/jupyter_server/.github/actions/draft-changelog@v1
         with:
           token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
           version_spec: ${{ github.event.inputs.version_spec }}
diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml
index 4a585c32..a962df60 100644
--- a/.github/workflows/draft-release.yml
+++ b/.github/workflows/draft-release.yml
@@ -35,12 +35,9 @@ jobs:
       - name: Upgrade packaging dependencies
         run: |
           pip install --upgrade pip setuptools wheel --user
-      - name: Install Dependencies
-        run: |
-          pip install -e .
       - name: Create Draft GitHub Release
         id: draft-release
-        uses: ./.github/actions/draft-release
+        uses: jupyter-server/release_helper/.github/actions/draft-release@v1
         with:
           token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
           target: ${{ github.event.inputs.target }}
diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
index 3fd45496..6c5ab690 100644
--- a/.github/workflows/publish-release.yml
+++ b/.github/workflows/publish-release.yml
@@ -23,15 +23,12 @@ jobs:
       - name: Upgrade packaging dependencies
         run: |
           pip install --upgrade pip setuptools wheel --user
-      - name: Install Dependencies
-        run: |
-          pip install -e .
       - name: Publish Release
         id: publish-release
         env:
           TWINE_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }} # use final when ready to publish
           TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/
-        uses: ./.github/actions/publish-release
+        uses: jupyter-server/jupyter_releaser/.github/actions/publish-release@v1
         with:
           token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
           release_url: ${{ github.event.inputs.release_url }}
diff --git a/.gitignore b/.gitignore
index 4482bcc5..53054c74 100644
--- a/.gitignore
+++ b/.gitignore
@@ -130,3 +130,6 @@ dmypy.json
 
 # Local git checkout
 .jupyter_releaser_checkout
+
+# macOS
+.DS_Store
diff --git a/jupyter_releaser/changelog.py b/jupyter_releaser/changelog.py
index 152252fa..cb338719 100644
--- a/jupyter_releaser/changelog.py
+++ b/jupyter_releaser/changelog.py
@@ -75,7 +75,6 @@ def get_version_entry(branch, repo, version, *, auth=None, resolve_backports=Fal
     md = generate_activity_md(
         repo,
         since=since,
-        until=until,
         kind="pr",
         heading_level=2,
         auth=auth,
@@ -87,6 +86,7 @@ def get_version_entry(branch, repo, version, *, auth=None, resolve_backports=Fal
         return f"## {version}\n\nNo merged PRs"
 
     entry = md.replace("[full changelog]", "[Full Changelog]")
+    entry = entry.replace("...None", f"...{until}")
 
     entry = entry.splitlines()[2:]
 
diff --git a/jupyter_releaser/lib.py b/jupyter_releaser/lib.py
index 0753e70e..f6c3ceab 100644
--- a/jupyter_releaser/lib.py
+++ b/jupyter_releaser/lib.py
@@ -128,11 +128,13 @@ def make_changelog_pr(auth, branch, repo, title, commit_message, body, dry_run=F
     pr_branch = f"changelog-{uuid.uuid1().hex}"
 
     if not dry_run:
-        util.run("git --no-pager diff")
-        util.run("git stash")
+        dirty = util.run("git --no-pager diff --stat") != ""
+        if dirty:
+            util.run("git stash")
         util.run(f"git fetch origin {branch}")
         util.run(f"git checkout -b {pr_branch} origin/{branch}")
-        util.run("git stash apply")
+        if dirty:
+            util.run("git stash apply")
 
     # Add a commit with the message
     util.run(commit_message)
@@ -363,6 +365,8 @@ def publish_assets(dist_dir, npm_token, npm_cmd, twine_cmd, dry_run, use_checkou
         os.environ["TWINE_USERNAME"] = "foo"
         os.environ["TWINE_PASSWORD"] = "bar"
         npm_cmd = "npm publish --dry-run"
+    else:
+        os.environ.setdefault("TWINE_USERNAME", "__token__")
 
     if npm_token:
         npm.handle_auth_token(npm_token)
diff --git a/jupyter_releaser/tests/test_functions.py b/jupyter_releaser/tests/test_functions.py
index 5358fd78..aa7bcce0 100644
--- a/jupyter_releaser/tests/test_functions.py
+++ b/jupyter_releaser/tests/test_functions.py
@@ -53,16 +53,8 @@ def test_get_changelog_version_entry(py_package, mocker):
     mocked_gen.return_value = testutil.CHANGELOG_ENTRY
     branch = "foo"
     resp = changelog.get_version_entry(branch, "bar/baz", version)
-    until = util.run(f'git --no-pager log -n 1 origin/{branch} --pretty=format:"%H"')
-    until = until.replace("%", "")
     mocked_gen.assert_called_with(
-        "bar/baz",
-        since="v0.0.1",
-        kind="pr",
-        branch=branch,
-        heading_level=2,
-        auth=None,
-        until=until,
+        "bar/baz", since="v0.0.1", kind="pr", branch=branch, heading_level=2, auth=None
     )
 
     assert f"## {version}" in resp
@@ -75,7 +67,6 @@ def test_get_changelog_version_entry(py_package, mocker):
     mocked_gen.assert_called_with(
         "bar/baz",
         since="v0.0.1",
-        until=until,
         kind="pr",
         branch=branch,
         heading_level=2,