Skip to content
Merged
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
28 changes: 27 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@ on:
tags: ['v*']
pull_request:
workflow_dispatch:
inputs:
verify_handoff:
description: Verify the plugin handoff credentials without releasing
required: false
default: false
type: boolean

permissions:
contents: read

jobs:
test:
if: github.event_name != 'workflow_dispatch' || !inputs.verify_handoff
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
Expand Down Expand Up @@ -54,8 +61,26 @@ jobs:
name: browser-failures
path: test-results/

handoff-credentials:
if: startsWith(github.ref, 'refs/tags/v') || inputs.verify_handoff
runs-on: ubuntu-latest
steps:
- name: Create plugin installation token
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349
id: app
with:
app-id: ${{ vars.SYNCSHELL_APP_ID }}
private-key: ${{ secrets.SYNCSHELL_APP_PRIVATE_KEY }}
owner: omarchy-QOL
repositories: syncshell
permission-contents: write
- name: Verify plugin repository access
env:
GH_TOKEN: ${{ steps.app.outputs.token }}
run: test "$(gh api repos/omarchy-QOL/syncshell --jq .full_name)" = "omarchy-QOL/syncshell"

publish:
needs: test
needs: [test, handoff-credentials]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
Expand Down Expand Up @@ -87,6 +112,7 @@ jobs:
private-key: ${{ secrets.SYNCSHELL_APP_PRIVATE_KEY }}
owner: omarchy-QOL
repositories: syncshell
permission-contents: write
- name: Notify pinned plugin update
env:
GH_TOKEN: ${{ steps.app.outputs.token }}
Expand Down
64 changes: 52 additions & 12 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ clean checkout:
```sh
npm ci
npm test
npm run release -- 0.1.2
bash scripts/test-release.sh "$PWD/release/syncshell-webui-v0.1.2.tar.gz"
npm run release -- 0.1.3
bash scripts/test-release.sh "$PWD/release/syncshell-webui-v0.1.3.tar.gz"
```

The release command rebuilds from committed source. The archive records its
Expand All @@ -28,18 +28,45 @@ files. Only add a Syncthing version to the manifest after testing it.
Expected Web repository: `syncshell/syncshell-webui`.
Current plugin repository: `omarchy-QOL/syncshell`.

Create a GitHub App with repository Contents and Pull requests write
permissions. Install it only on the plugin repository. Store its private key
as `SYNCSHELL_APP_PRIVATE_KEY` and its ID as repository variable
`SYNCSHELL_APP_ID` in both repositories. The Web workflow requests a plugin
installation token solely to send the release notification. The plugin uses
its token to push the update branch and open the pull request.
Create a private GitHub App owned by `omarchy-QOL` with repository Contents
and Pull requests write permissions. Disable webhooks and install it only on
`omarchy-QOL/syncshell`. Store its private key as
`SYNCSHELL_APP_PRIVATE_KEY` and its ID as repository variable
`SYNCSHELL_APP_ID` in both repositories. The private key does not expire; only
replace it after a compromise or deliberate rotation.

Configure both repositories from the machine holding the downloaded PEM:

```sh
app_id=REPLACE_WITH_APP_ID
app_key_file=/absolute/path/to/syncshell-release.pem
for repo in syncshell/syncshell-webui omarchy-QOL/syncshell; do
gh variable set SYNCSHELL_APP_ID --repo "$repo" --body "$app_id"
gh secret set SYNCSHELL_APP_PRIVATE_KEY --repo "$repo" \
<"$app_key_file"
done
```

Keep the PEM secure until both credential checks pass. GitHub stores only the
public half of an App key, so a lost private key must be replaced rather than
downloaded again.

The Web workflow requests a one-hour plugin installation token solely to send
the release notification. The plugin requests a separate one-hour token to
push the update branch and open the pull request. Each workflow narrows its
token to the permissions needed by that job.

The plugin's `update-webui.yml` must exist on its default branch for
`repository_dispatch` delivery, even though updates target `dev`. Landing
that workflow is an activation prerequisite. Repository Actions policies must
allow the pinned actions and the GitHub App's operations.

Before the first release, manually run the Web repository's `webui` workflow
with `verify_handoff` enabled. Then run the plugin repository's `update webui`
workflow with `verify_only` enabled. These checks validate both copies of the
App ID and private key without publishing a release or changing either
repository.

On Web pull requests and branch pushes, CI builds, packages and tests. A
`vX.Y.Z` tag matching `package.json` publishes the tested archive through
GitHub Releases. Then it sends the exact version and checksum to the plugin.
Expand All @@ -51,10 +78,23 @@ The plugin importer is `go -C core run ./cmd/import-webui`, using its existing
Go module. It validates the archive and atomically exchanges the bundle
directory on Linux. No second importer implementation is supported.

No workflow merges to `main` or publishes a plugin release. A failed handoff
leaves the Web release available. Retry the notification job or run the
plugin update workflow manually with the published version and checksum.
Published tags and archives must remain unchanged; fixes use a new version.
No workflow merges the update pull request or publishes a plugin release. The
pull request remains open against `dev` until a maintainer merges or closes
it. A failed handoff leaves the Web release available. Retry the notification
job or run the plugin update workflow manually with the published version and
checksum. Published tags and archives must remain unchanged; fixes use a new
version.

## Release procedure

1. Merge the plugin receiver workflow into its default branch.
2. Configure and verify the GitHub App in both repositories.
3. Merge the tested Web changes into `main` and set the stable package version.
4. Run the local packaging and archive test from a clean checkout.
5. Push the matching annotated tag, for example `v0.1.3`.
6. Confirm the Web release succeeds and the plugin update pull request opens
against `dev`.
7. Review the plugin pull request checks and leave it open until approved.

## Theme contract 1

Expand Down