From a29ab53ff2ad2b8777ac79ca37b99410a4993470 Mon Sep 17 00:00:00 2001 From: Zoltan Csizmadia Date: Fri, 11 Sep 2026 08:54:09 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20publish=20on=20a=20tag=20=E2=80=94=20ic?= =?UTF-8?q?on,=20gallery=20banner,=20release=20workflow=20(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Everything in #4 that is not a token. The publisher `hawserhq` is registered and already in the manifest; what was missing was an icon and a way to actually ship. The icon is generated upstream (hawserhq/hawser's tools/genicons) at the 128px the Marketplace wants, and copied here rather than hand-cut, so it cannot drift from the brand source. release.yml runs on a `v*` tag, and on workflow_dispatch so a failed publish can be retried without cutting a new tag. Three things in it are deliberate: **The tag and package.json must agree.** A Marketplace listing whose version names no tag in the repository cannot be traced back to source, so a mismatch fails the release instead of publishing. **The vsix is attached to the GitHub release before either publish.** A failed Marketplace call then still leaves an installable artifact rather than nothing. **Each publish is skipped when its secret is absent**, so a fork -- and this repository until the tokens are added -- gets a packaged, attached vsix instead of a red run. The secrets are job-scoped rather than step-scoped: a step's `if` cannot see env defined in that same step, so step-scoped secrets would have made both conditions evaluate wrongly. Also: gallery banner in the brand slate, a Keep-a-Changelog CHANGELOG, and CI and licence badges. The Marketplace badges are staged in an HTML comment with a note, because they render "not found" until the first publish. Verified by packaging: 8 files, 17.78 KB, no vsce warnings, and the icon inside the vsix is really 128x128 with the manifest reading publisher hawserhq, id hawser, version 0.1.0. --- .github/workflows/release.yml | 79 ++++++++++++++++++++++++++++++++++ CHANGELOG.md | 25 +++++++++-- README.md | 8 ++++ icon.png | Bin 0 -> 6336 bytes package.json | 2 + 5 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 icon.png diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..41aad51 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,79 @@ +# Publish the extension on a v* tag (#4). +# +# Deliberately manual to trigger: a tag is the decision to release, and +# nothing here runs on a push to main. The Marketplace and Open VSX are +# permanent once published, so there is no undo to lean on. +name: release + +on: + push: + tags: ['v*'] + # So a failed publish can be retried without cutting a new tag. + workflow_dispatch: + inputs: + tag: + description: 'Existing tag to publish (e.g. v0.1.0)' + required: true + +permissions: + contents: write + +jobs: + publish: + name: package and publish + runs-on: ubuntu-latest + # Job-level, not step-level: a step's `if` cannot see env defined in that + # same step, so the skip conditions below would never evaluate correctly. + env: + VSCE_PAT: ${{ secrets.VSCE_PAT }} + OVSX_PAT: ${{ secrets.OVSX_PAT }} + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ inputs.tag || github.ref }} + + - uses: actions/setup-node@v7 + with: + node-version: 20 + cache: npm + + - run: npm ci + + # The same checks CI runs. A release is not the place to find out the + # typecheck was broken. + - run: npm run check + - run: npm run build -- --production + + # The tag and the manifest must agree, or the Marketplace shows a + # version nobody can find in the repository. + - name: tag matches package.json version + run: | + set -euo pipefail + tag="${{ inputs.tag || github.ref_name }}" + manifest="v$(node -p "require('./package.json').version")" + if [ "$tag" != "$manifest" ]; then + echo "::error::tag $tag does not match package.json $manifest" + exit 1 + fi + echo "$tag matches the manifest" + + - name: package + run: npx @vscode/vsce package --no-dependencies -o hawser.vsix + + # Attached first, so a failed Marketplace publish still leaves an + # installable artifact on the release rather than nothing. + - name: attach the vsix to the release + env: + GH_TOKEN: ${{ github.token }} + run: gh release upload "${{ inputs.tag || github.ref_name }}" hawser.vsix --clobber + + # Both publishes are skipped when their secret is absent, so a fork — or + # this repository before the tokens are added — gets a packaged, attached + # vsix instead of a red run. + - name: publish to the VS Code Marketplace + if: env.VSCE_PAT != '' + run: npx @vscode/vsce publish --no-dependencies --packagePath hawser.vsix + + - name: publish to Open VSX + if: env.OVSX_PAT != '' + run: npx ovsx publish hawser.vsix --pat "$OVSX_PAT" diff --git a/CHANGELOG.md b/CHANGELOG.md index 9894fce..1c4ee5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,28 @@ # Changelog -## 0.1.0 — unreleased +All notable changes to this extension are recorded here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the extension +follows [semantic versioning](https://semver.org/spec/v2.0.0.html). + +The version here and the `v*` tag that publishes it must match — the release +workflow refuses to publish when they do not, because a Marketplace listing +whose version names no tag in the repository cannot be traced back to source. + +## [Unreleased] + +## [0.1.0] — unreleased Initial extension. -- Engine status in the status bar: running / idle / stopped, with a click menu +### Added + +- Engine state in the status bar: running / idle / stopped, with a click menu for start, stop, restart, doctor, and refresh. -- Notifications when the engine idle-stops to free RAM or wakes back up. +- Notifications when the engine idle-stops to free RAM or wakes back up — the + state an always-on Docker Desktop has no way to show. - `Hawser: Run Doctor` opens `hawser doctor` in a terminal. -- Talks to `hawser.exe` only through its `--json` CLI contract (Hawser ≥ 0.3.0). +- Talks to `hawser.exe` only through its `--json` CLI contract (Hawser ≥ 0.3.0), + so the extension holds no engine logic of its own. + +[Unreleased]: https://github.com/hawserhq/hawser-vscode/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/hawserhq/hawser-vscode/releases/tag/v0.1.0 diff --git a/README.md b/README.md index 736aeaf..fe4544b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ # Hawser for VS Code +[![CI](https://github.com/hawserhq/hawser-vscode/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/hawserhq/hawser-vscode/actions/workflows/ci.yml) +[![License: Apache-2.0](https://img.shields.io/github/license/hawserhq/hawser-vscode?color=2F3B45)](LICENSE) + + + See and control the [Hawser](https://github.com/hawserhq/hawser) engine — the upstream open source Docker Engine on Windows via WSL2 — from inside VS Code. diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0ff1be77f5c314556571032cf21fad15d3c8c9ea GIT binary patch literal 6336 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