diff --git a/.github/workflows/prep-self-release.yml b/.github/workflows/prep-self-release.yml
index a3654290..b59e97a3 100644
--- a/.github/workflows/prep-self-release.yml
+++ b/.github/workflows/prep-self-release.yml
@@ -26,6 +26,8 @@ on:
jobs:
prep_release:
runs-on: ubuntu-latest
+ permissions:
+ contents: write
steps:
- uses: actions/checkout@v4
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
@@ -37,7 +39,7 @@ jobs:
id: prep-release
uses: jupyter-server/jupyter_releaser/.github/actions/prep-release@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ secrets.GITHUB_TOKEN }}
version_spec: ${{ github.event.inputs.version_spec }}
post_version_spec: ${{ github.event.inputs.post_version_spec }}
target: jupyter-server/jupyter_releaser
diff --git a/.github/workflows/publish-changelog.yml b/.github/workflows/publish-changelog.yml
index dc31f350..8ec872ad 100644
--- a/.github/workflows/publish-changelog.yml
+++ b/.github/workflows/publish-changelog.yml
@@ -12,18 +12,27 @@ on:
jobs:
publish_changelog:
runs-on: ubuntu-latest
+ environment: release
steps:
- uses: actions/checkout@v4
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
+
- name: Install Dependencies
shell: bash
run: |
pip install -e .
+
+ - uses: actions/create-github-app-token@v1
+ id: app-token
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+
- name: Publish changelog
id: publish-changelog
uses: jupyter-server/jupyter_releaser/.github/actions/publish-changelog@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ steps.app-token.outputs.token }}
target: ${{ github.event.inputs.target }}
branch: ${{ github.event.inputs.branch }}
diff --git a/.github/workflows/publish-self-release.yml b/.github/workflows/publish-self-release.yml
index d039a844..ad9f9beb 100644
--- a/.github/workflows/publish-self-release.yml
+++ b/.github/workflows/publish-self-release.yml
@@ -17,8 +17,6 @@ jobs:
runs-on: ubuntu-latest
environment: release
permissions:
- # This is useful if you want to use PyPI trusted publisher
- # and NPM provenance
id-token: write
steps:
- uses: actions/checkout@v4
@@ -27,11 +25,16 @@ jobs:
shell: bash
run: |
pip install -e .
+ - uses: actions/create-github-app-token@v1
+ id: app-token
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Populate Release
id: populate-release
uses: jupyter-server/jupyter_releaser/.github/actions/populate-release@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ steps.app-token.outputs.token }}
target: jupyter-server/jupyter_releaser
branch: ${{ github.event.inputs.branch }}
release_url: ${{ github.event.inputs.release_url }}
@@ -43,7 +46,7 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
uses: jupyter-server/jupyter_releaser/.github/actions/finalize-release@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ steps.app-token.outputs.token }}
target: ${{ github.event.inputs.target }}
release_url: ${{ steps.populate-release.outputs.release_url }}
diff --git a/docs/source/background/theory.md b/docs/source/background/theory.md
index c6fe3647..dbe796ff 100644
--- a/docs/source/background/theory.md
+++ b/docs/source/background/theory.md
@@ -13,6 +13,13 @@ This project should help maintainers reduce toil and save time in the release pr
- Dry run publish on CI
- Revert to Dev version after release (optional)
+## Security
+
+We strive to use the most secure release practices possible, reflected in the `Checklist for Adoption`
+and the example workflows.
+This includes using PyPI Trusted Publishing, using GitHub Environments, encouraging the use of Rulesets and GitHub Apps with limited bypass capability, and provenance data for npm.
+In addition, there is an automatic check for whether the user who triggered the action is an admin.
+
## Action Details
Detailed workflows are available to draft a changelog, draft a release, publish a release, and check a release.
diff --git a/docs/source/how_to_guides/convert_repo_from_repo.md b/docs/source/how_to_guides/convert_repo_from_repo.md
index af0fb0e9..462cecb8 100644
--- a/docs/source/how_to_guides/convert_repo_from_repo.md
+++ b/docs/source/how_to_guides/convert_repo_from_repo.md
@@ -16,26 +16,32 @@ See checklist below for details:
## Checklist for Adoption
-- [ ] Add a GitHub [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token), preferably from a "machine user" GitHub
- account that has admin access to the repository. The token itself will
- need "public_repo", and "repo:status" permissions. Save the token as
- `ADMIN_GITHUB_TOKEN`
- in the [repository secrets](https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository). We need this
- access token to allow for branch protection rules, which block the pushing
- of commits when using the `GITHUB_TOKEN`, even when run from an admin user
- account.
+- [ ] Set up a [GitHub App](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps#github-apps-that-act-on-their-own-behalf) on your organization (or personal account for a personal project).
+
+ - Disable the web hook
+ - Enable Repository permissions > Contents > Read and write
+ - Select "Only on this account"
+ - Click "Create GitHub App"
+ - Browse to the App Settings
+ - Select "Install App" and install on all repositories
+ - Under "General" click "Generate a private key"
+ - Store the `APP_ID` and the private key in a secure location (Jupyter Vault if using a Jupyter Org)
+
+- [ ] Create a "release" [environment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment) on your repository and add an `APP_ID` Environment Variable and `APP_PRIVATE_KEY` secret.
+ The environment should be enabled for ["Protected branches only"](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-branches-and-tags).
+
+- [ ] Configure [Rulesets](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets) for the repository
+
+ - Set up branch protection (with default rules) on publication branches
+ - Remove global tag protection.
+ - Add a branch Ruleset for all branches
+ - Allow the GitHub App to bypass protections
+ - Set up Pull Request and Required Checks
+ - Add a tags Ruleset for all tags
+ - Allow the GitHub App to bypass protections
- [ ] Set up PyPI:
-Using PyPI token (legacy way)
-
-- Add access token for the [PyPI registry](https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#saving-credentials-on-github) stored as `PYPI_TOKEN`.
- _Note_ For security reasons, it is recommended that you scope the access
- to a single repository. Additionally, this token should belong to a
- machine account and not a user account.
-
-
-
Using PyPI trusted publisher (modern way)
- Set up your PyPI project by [adding a trusted publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/)
@@ -45,10 +51,18 @@ See checklist below for details:
+Using PyPI token (legacy way)
+
+- Add access token for the [PyPI registry](https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#saving-credentials-on-github) stored as `PYPI_TOKEN`.
+ _Note_ For security reasons, it is recommended that you scope the access
+ to a single repository. Additionally, this token should belong to a
+ machine account and not a user account.
+
+
+
- [ ] If needed, add access token for [npm](https://docs.npmjs.com/creating-and-viewing-access-tokens), saved as `NPM_TOKEN`. Again this should
be created using a machine account that only has publish access.
-- [ ] Ensure that only trusted users with 2FA have admin access to the
- repository, since they will be able to trigger releases.
+- [ ] Ensure that only trusted users with 2FA have admin access to the repository, since they will be able to trigger releases.
- [ ] Switch to Markdown Changelog
- We recommend [MyST](https://myst-parser.readthedocs.io/en/latest/?badge=latest), especially if some of your docs are in reStructuredText.
- Can use `pandoc -s changelog.rst -o changelog.md` and some hand edits as needed.
diff --git a/docs/source/how_to_guides/maintain_fork.md b/docs/source/how_to_guides/maintain_fork.md
index fe079c5d..da624882 100644
--- a/docs/source/how_to_guides/maintain_fork.md
+++ b/docs/source/how_to_guides/maintain_fork.md
@@ -2,7 +2,7 @@
## How to keep fork of Jupyter Releaser up to date
-- The manual workflow files target the `@v1` actions in the source repository, which means that as long as
+- The manual workflow files target the `@v2` actions in the source repository, which means that as long as
the workflow files themselves are up to date, you will always be running the most up to date actions.
- Make sure your workflow is up to date by checking the "Fetch Upstream" dropdown on the main page of your fork.
diff --git a/example-workflows/full-release.yml b/example-workflows/full-release.yml
index 186631b8..b8ee1fcf 100644
--- a/example-workflows/full-release.yml
+++ b/example-workflows/full-release.yml
@@ -27,12 +27,10 @@ on:
description: "Comma separated list of steps to skip during Populate Release"
required: false
jobs:
- full_release:
+ prep_release:
runs-on: ubuntu-latest
permissions:
- # This is useful if you want to use PyPI trusted publisher
- # and NPM provenance
- id-token: write
+ contents: write
steps:
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
@@ -40,34 +38,48 @@ jobs:
id: prep-release
uses: jupyter-server/jupyter_releaser/.github/actions/prep-release@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ secrets.GITHUB_TOKEN }}
version_spec: ${{ github.event.inputs.version_spec }}
+ # silent: ${{ github.event.inputs.silent }}
post_version_spec: ${{ github.event.inputs.post_version_spec }}
+ target: ${{ github.event.inputs.target }}
branch: ${{ github.event.inputs.branch }}
- # silent: ${{ github.event.inputs.silent }}
since: ${{ github.event.inputs.since }}
since_last_stable: ${{ github.event.inputs.since_last_stable }}
+ publish_release:
+ needs: [prep_release]
+ runs-on: ubuntu-latest
+ environment: release
+ permissions:
+ id-token: write
+ steps:
+ - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
+
+ - uses: actions/create-github-app-token@v1
+ id: app-token
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+
- name: Populate Release
id: populate-release
uses: jupyter-server/jupyter_releaser/.github/actions/populate-release@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ steps.app-token.outputs.token }}
+ target: ${{ github.event.inputs.target }}
branch: ${{ github.event.inputs.branch }}
- release_url: ${{ steps.prep-release.outputs.release_url }}
+ release_url: ${{ github.event.inputs.release_url }}
steps_to_skip: ${{ github.event.inputs.steps_to_skip }}
- name: Finalize Release
id: finalize-release
env:
- # The following are needed if you use legacy PyPI set up
- # PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
- # PYPI_TOKEN_MAP: ${{ secrets.PYPI_TOKEN_MAP }}
- # TWINE_USERNAME: __token__
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
uses: jupyter-server/jupyter_releaser/.github/actions/finalize-release@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ steps.app-token.outputs.token }}
+ target: ${{ github.event.inputs.target }}
release_url: ${{ steps.populate-release.outputs.release_url }}
- name: "** Next Step **"
@@ -75,3 +87,9 @@ jobs:
run: |
echo "Verify the final release"
echo ${{ steps.finalize-release.outputs.release_url }}
+
+ - name: "** Failure Message **"
+ if: ${{ failure() }}
+ run: |
+ echo "Failed to Publish the Draft Release Url:"
+ echo ${{ steps.populate-release.outputs.release_url }}
diff --git a/example-workflows/prep-release.yml b/example-workflows/prep-release.yml
index 83f876f8..4f8621f1 100644
--- a/example-workflows/prep-release.yml
+++ b/example-workflows/prep-release.yml
@@ -26,6 +26,8 @@ on:
jobs:
prep_release:
runs-on: ubuntu-latest
+ permissions:
+ contents: write
steps:
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
@@ -33,11 +35,12 @@ jobs:
id: prep-release
uses: jupyter-server/jupyter_releaser/.github/actions/prep-release@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ secrets.GITHUB_TOKEN }}
version_spec: ${{ github.event.inputs.version_spec }}
+ # silent: ${{ github.event.inputs.silent }}
post_version_spec: ${{ github.event.inputs.post_version_spec }}
+ target: ${{ github.event.inputs.target }}
branch: ${{ github.event.inputs.branch }}
- # silent: ${{ github.event.inputs.silent }}
since: ${{ github.event.inputs.since }}
since_last_stable: ${{ github.event.inputs.since_last_stable }}
diff --git a/example-workflows/publish-changelog.yml b/example-workflows/publish-changelog.yml
index ad612f26..60af4c5f 100644
--- a/example-workflows/publish-changelog.yml
+++ b/example-workflows/publish-changelog.yml
@@ -12,13 +12,21 @@ on:
jobs:
publish_changelog:
runs-on: ubuntu-latest
+ environment: release
steps:
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
+
+ - uses: actions/create-github-app-token@v1
+ id: app-token
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+
- name: Publish changelog
id: publish-changelog
uses: jupyter-server/jupyter_releaser/.github/actions/publish-changelog@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ steps.app-token.outputs.token }}
branch: ${{ github.event.inputs.branch }}
- name: "** Next Step **"
diff --git a/example-workflows/publish-release.yml b/example-workflows/publish-release.yml
index cf6d9058..c1881060 100644
--- a/example-workflows/publish-release.yml
+++ b/example-workflows/publish-release.yml
@@ -15,18 +15,23 @@ on:
jobs:
publish_release:
runs-on: ubuntu-latest
+ environment: release
permissions:
- # This is useful if you want to use PyPI trusted publisher
- # and NPM provenance
id-token: write
steps:
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
+ - uses: actions/create-github-app-token@v1
+ id: app-token
+ with:
+ app-id: ${{ vars.APP_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+
- name: Populate Release
id: populate-release
uses: jupyter-server/jupyter_releaser/.github/actions/populate-release@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ steps.app-token.outputs.token }}
branch: ${{ github.event.inputs.branch }}
release_url: ${{ github.event.inputs.release_url }}
steps_to_skip: ${{ github.event.inputs.steps_to_skip }}
@@ -34,14 +39,10 @@ jobs:
- name: Finalize Release
id: finalize-release
env:
- # The following are needed if you use legacy PyPI set up
- # PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
- # PYPI_TOKEN_MAP: ${{ secrets.PYPI_TOKEN_MAP }}
- # TWINE_USERNAME: __token__
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
uses: jupyter-server/jupyter_releaser/.github/actions/finalize-release@v2
with:
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ token: ${{ steps.app-token.outputs.token }}
release_url: ${{ steps.populate-release.outputs.release_url }}
- name: "** Next Step **"