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
66 changes: 61 additions & 5 deletions .github/workflows/release-vscode-extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
contents: read
outputs:
version: ${{ steps.v.outputs.version }}
publisher: ${{ steps.v.outputs.publisher }}
name: ${{ steps.v.outputs.name }}
extension-id: ${{ steps.v.outputs.extension-id }}
publish: ${{ steps.v.outputs.publish }}
release: ${{ steps.v.outputs.release }}
steps:
Expand All @@ -49,6 +52,18 @@ jobs:
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

# The manifest is the only source for the Marketplace identity: `vsce`
# and `ovsx` derive what they publish from `publisher` and `name`, so
# anything this workflow states about the extension has to be read from
# there too. A second copy here would decide which listing gets queried
# while the manifest decided which listing gets written.
PUBLISHER=$(node -p "require('./vscode-extension/package.json').publisher")
NAME=$(node -p "require('./vscode-extension/package.json').name")
EXT_ID="${PUBLISHER}.${NAME}"
echo "publisher=$PUBLISHER" >> "$GITHUB_OUTPUT"
echo "name=$NAME" >> "$GITHUB_OUTPUT"
echo "extension-id=$EXT_ID" >> "$GITHUB_OUTPUT"

# A release is a version change, and nothing else. This workflow wakes
# on any edit to the manifest, which also carries the icon, the
# commands and the settings schema — so an unrelated edit would
Expand All @@ -68,14 +83,14 @@ jobs:

MKT=$(curl -s -X POST https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery \
-H "Content-Type: application/json" -H "Accept: application/json;api-version=7.2-preview.1" \
-d '{"filters":[{"criteria":[{"filterType":7,"value":"tumaet.apollon-vscode"}]}],"flags":914}' \
-d "$(jq -nc --arg id "$EXT_ID" '{filters:[{criteria:[{filterType:7,value:$id}]}],flags:914}')" \
| jq -r '.results[0].extensions[0].versions[]?.version' | grep -Fx "$VERSION" || true)
if [ -n "$MKT" ]; then
echo "publish=false" >> "$GITHUB_OUTPUT"
else
echo "publish=true" >> "$GITHUB_OUTPUT"
fi
TAG="apollon-vscode@${VERSION}"
TAG="${NAME}@${VERSION}"
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
echo "release=false" >> "$GITHUB_OUTPUT"
else
Expand Down Expand Up @@ -156,6 +171,8 @@ jobs:
environment: vscode-marketplace
permissions:
contents: read
outputs:
ovsx: ${{ steps.ovsx.outputs.configured }}
steps:
# `.nvmrc` is the one source for the Node version, and this job has no
# working tree to read it from. `checkout` cleans the directory, so it has
Expand All @@ -181,13 +198,39 @@ jobs:
npm install --prefix "$RUNNER_TEMP/cli" --ignore-scripts @vscode/vsce@3.9.1 ovsx@0.10.12
echo "$RUNNER_TEMP/cli/node_modules/.bin" >> "$GITHUB_PATH"

- name: Verify the Marketplace PAT can write this publisher
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
PUBLISHER: ${{ needs.check.outputs.publisher }}
# `vsce publish` surfaces a missing publisher grant only after uploading,
# so ask first. The manifest names the publisher; the PAT must be a member
# of it with Marketplace *Manage* scope, or nothing else in this job works.
run: vsce verify-pat "$PUBLISHER"

- name: Publish to VS Marketplace
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
VSIX_FILE: ${{ needs.build.outputs.vsix-name }}
run: vsce publish --skip-duplicate --no-dependencies --packagePath "$VSIX_FILE"

# Open VSX is optional: the Marketplace is the release channel of record.
# Without a token, say so and move on rather than failing a release that
# already reached the Marketplace.
- name: Check for an Open VSX token
id: ovsx
env:
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: |
set -euo pipefail
if [ -n "${OVSX_PAT:-}" ]; then
echo "configured=true" >> "$GITHUB_OUTPUT"
else
echo "configured=false" >> "$GITHUB_OUTPUT"
echo "::notice::OVSX_PAT is not set — skipping Open VSX. The VS Marketplace release is unaffected."
fi

- name: Publish to Open VSX
if: steps.ovsx.outputs.configured == 'true'
env:
OVSX_PAT: ${{ secrets.OVSX_PAT }}
VSIX_FILE: ${{ needs.build.outputs.vsix-name }}
Expand Down Expand Up @@ -229,9 +272,13 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ needs.check.outputs.version }}
PUBLISHER: ${{ needs.check.outputs.publisher }}
NAME: ${{ needs.check.outputs.name }}
EXT_ID: ${{ needs.check.outputs.extension-id }}
OVSX: ${{ needs.publish.outputs.ovsx }}
run: |
set -euo pipefail
TAG="apollon-vscode@${VERSION}"
TAG="${NAME}@${VERSION}"
if ! git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
Expand All @@ -252,16 +299,25 @@ jobs:
# --generate-notes only when there is no changelog section for this
# version (e.g. a release that carried no changeset).
CHANGELOG=$(node scripts/extract-changelog.mjs "$VERSION" vscode-extension)

# Name the registries this release actually reached. Open VSX is
# skipped when no token is configured, and a release body that claims
# a publish that never happened is worse than one that omits it.
REGISTRIES="the [VS Marketplace](https://marketplace.visualstudio.com/items?itemName=${EXT_ID})"
if [ "${OVSX:-}" = "true" ]; then
REGISTRIES="${REGISTRIES} and [Open VSX](https://open-vsx.org/extension/${PUBLISHER}/${NAME})"
fi

{
if [ -n "$CHANGELOG" ]; then printf '%s\n\n' "$CHANGELOG"; fi
cat <<EOF
Install:

\`\`\`sh
code --install-extension tumaet.apollon-vscode
code --install-extension ${EXT_ID}
\`\`\`

Published to [VS Marketplace](https://marketplace.visualstudio.com/items?itemName=tumaet.apollon-vscode) and [Open VSX](https://open-vsx.org/extension/tumaet/apollon-vscode). VSIX attached for sideloading.
Published to ${REGISTRIES}. VSIX attached for sideloading.
EOF
} > release-notes.md

Expand Down
10 changes: 5 additions & 5 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ We aim to acknowledge reports within 5 working days and to coordinate a fix and

Only the latest minor of each major is supported with security fixes.

| Package | Supported |
| --------------------------------------------------- | ---------------- |
| `@tumaet/apollon` (npm) | `4.x` |
| Standalone Docker images (server + webapp) | latest `vX.Y.Z` |
| `tumaet.apollon-vscode` (VS Marketplace / Open VSX) | latest published |
| Package | Supported |
| ---------------------------------------------------- | ---------------- |
| `@tumaet/apollon` (npm) | `4.x` |
| Standalone Docker images (server + webapp) | latest `vX.Y.Z` |
| `aet-tum.apollon-vscode` (VS Marketplace / Open VSX) | latest published |

Older majors are end-of-life and will not receive backports.

Expand Down
20 changes: 11 additions & 9 deletions docs/contributor/deployment/npm-publishing.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ description: How the three independently versioned Apollon artifacts ship — np

Three independently versioned artifacts, each with its own release workflow:

| Artifact | Version source | Tag | Workflow |
| --------------------------------------------------- | ----------------------------------------- | ----------------------- | ------------------------------ |
| `@tumaet/apollon` (npm) | `library/package.json` | `@tumaet/apollon@X.Y.Z` | `release-library.yml` |
| Standalone Docker images | `standalone/{webapp,server}/package.json` | `vX.Y.Z` | `release-standalone.yml` |
| `tumaet.apollon-vscode` (VS Marketplace + Open VSX) | `vscode-extension/package.json` | `apollon-vscode@X.Y.Z` | `release-vscode-extension.yml` |
| Artifact | Version source | Tag | Workflow |
| ---------------------------------------------------- | ----------------------------------------- | ----------------------- | ------------------------------ |
| `@tumaet/apollon` (npm) | `library/package.json` | `@tumaet/apollon@X.Y.Z` | `release-library.yml` |
| Standalone Docker images | `standalone/{webapp,server}/package.json` | `vX.Y.Z` | `release-standalone.yml` |
| `aet-tum.apollon-vscode` (VS Marketplace + Open VSX) | `vscode-extension/package.json` | `apollon-vscode@X.Y.Z` | `release-vscode-extension.yml` |

Standalone starts at `4.2.18` (the library version at the time of the release-pipeline switchover). Future `vX.Y.Z` tags advance from there and do not collide with legacy tags.

Expand All @@ -36,7 +36,7 @@ The GitHub Release body for each track is built from that `CHANGELOG.md` section
2. On merge:
- `release-library.yml` fires when `library/package.json` changes: builds with pnpm, packs the tarball with `pnpm pack`, publishes with `npm publish` for OIDC trusted publishing + provenance (pnpm does not yet support OIDC trusted publishing natively — tracked in [pnpm#9812](https://github.com/pnpm/pnpm/issues/9812)). Tags `@tumaet/apollon@X.Y.Z` → GitHub Release. Skipped if the version is already on npm.
- `release-standalone.yml` fires after the push-to-main Docker build succeeds: retag `sha-<commit>` → `X.Y.Z` → cosign-sign → tag `vX.Y.Z` → GitHub Release. Staging is already running the same digest under the `sha-<commit>` tag from the push-to-main deploy, so no second deploy is needed. Skipped if a release for that version already exists.
- `release-vscode-extension.yml` fires when `vscode-extension/package.json` changes: builds the library + extension, packages the VSIX, attests it via sigstore (`actions/attest-build-provenance`), then publishes to both VS Marketplace (`vsce`) and Open VSX (`ovsx`) gated on the `vscode-marketplace` environment. Tags `apollon-vscode@X.Y.Z` → GitHub Release with the VSIX attached.
- `release-vscode-extension.yml` fires when the `version` in `vscode-extension/package.json` changes: builds the library + extension, packages the VSIX, attests it via sigstore (`actions/attest-build-provenance`), then publishes to the VS Marketplace (`vsce`) and, when `OVSX_PAT` is set, to Open VSX (`ovsx`) — both gated on the `vscode-marketplace` environment. Tags `apollon-vscode@X.Y.Z` → GitHub Release with the VSIX attached. Any other edit to the manifest, including the `publisher` and `name` that decide the Marketplace identity, is picked up by the next release rather than triggering one; publish it with a `workflow_dispatch`.
3. Promote standalone to production: Actions → **Deploy to Production** → `image-tag: X.Y.Z`.

## Verify a Docker image signature
Expand All @@ -57,13 +57,15 @@ cosign verify \

### VS Marketplace + Open VSX (vscode-extension)

- **Azure DevOps PAT**: create at `https://dev.azure.com/<your-org>/_usersSettings/tokens` with scope `Marketplace → Manage`, organization "All accessible organizations". Max lifetime is 1 year — calendar a rotation reminder.
- **Open VSX PAT**: create at `https://open-vsx.org/user-settings/tokens`. The namespace `tumaet` must exist on Open VSX first — if it doesn't, run `ovsx create-namespace tumaet -p <PAT>` once locally (or have any namespace member do it). Verify at `https://open-vsx.org/namespace/tumaet`.
The extension publishes as `aet-tum.apollon-vscode`, from the `publisher` and `name` fields of `vscode-extension/package.json`. `aet-tum` is the organization's publisher — the one that also owns `aet-tum.iris-thaumantias`.

- **Azure DevOps PAT** (required): create at `https://dev.azure.com/<your-org>/_usersSettings/tokens` with scope `Marketplace → Manage`, organization "All accessible organizations". Max lifetime is 1 year — calendar a rotation reminder. The PAT's account must be a member of the `aet-tum` publisher: a token that is valid but not a member fails with `Access Denied … needs the following permission(s) on the resource /aet-tum/apollon-vscode`. Check with `vsce verify-pat aet-tum`; the release workflow runs the same command before it uploads anything.
- **Open VSX PAT** (optional): create at `https://open-vsx.org/user-settings/tokens`. When `OVSX_PAT` is unset the release skips Open VSX and publishes to the Marketplace alone; the workflow logs a notice and stays green. The namespace `aet-tum` must exist on Open VSX — verify at `https://open-vsx.org/namespace/aet-tum`, and if it is missing run `ovsx create-namespace aet-tum -p <PAT>` once locally.
- **GitHub Environment `vscode-marketplace`**:
- Settings → Environments → New environment → name `vscode-marketplace`.
- Deployment branches and tags → "Selected branches and tags" → add `main`.
- Required reviewers → add the release maintainer; turn on "Prevent self-review" if a second maintainer is available.
- Environment secrets:
- `VSCE_PAT` = Azure DevOps PAT (above).
- `OVSX_PAT` = Open VSX PAT (above).
- `OVSX_PAT` = Open VSX PAT (above), if Open VSX is wanted.
- Delete any pre-existing repo-level `VSCE_PAT` / `OVSX_PAT` after the environment-scoped ones are in place — repo secrets bypass the environment's deployment-branch and reviewer gates.
22 changes: 12 additions & 10 deletions docs/contributor/development/project-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@ Apollon/

## Workspaces

| Workspace | Name | Published as |
| --------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------ |
| `library/` | `@tumaet/apollon` | [npm](https://www.npmjs.com/package/@tumaet/apollon) |
| `packages/ui/` | `@tumaet/ui` | internal design system (consumed by the webapp; not published) |
| `standalone/webapp/` | `@tumaet/webapp` | `ghcr.io/ls1intum/apollon/webapp` |
| `standalone/server/` | `@tumaet/server` | `ghcr.io/ls1intum/apollon/server` |
| `vscode-extension/` | `apollon-vscode` | [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=tumaet.apollon-vscode) |
| `vscode-extension/webview/` | `@tumaet/vscode-webview` | bundled into the extension VSIX |
| `docs/` | `@tumaet/docs` | published as the Docusaurus site at <https://ls1intum.github.io/Apollon/> |
| Workspace | Name | Published as |
| --------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------- |
| `library/` | `@tumaet/apollon` | [npm](https://www.npmjs.com/package/@tumaet/apollon) |
| `packages/ui/` | `@tumaet/ui` | internal design system (consumed by the webapp; not published) |
| `standalone/webapp/` | `@tumaet/webapp` | `ghcr.io/ls1intum/apollon/webapp` |
| `standalone/server/` | `@tumaet/server` | `ghcr.io/ls1intum/apollon/server` |
| `vscode-extension/` | `apollon-vscode` | [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=aet-tum.apollon-vscode) |
| `vscode-extension/webview/` | `@tumaet/vscode-webview` | bundled into the extension VSIX |
| `docs/` | `@tumaet/docs` | published as the Docusaurus site at <https://ls1intum.github.io/Apollon/> |

The scope carries the organization and the name carries the role, so `@tumaet/apollon` is the product and every other workspace is named for the job it does. `apollon-vscode` is the sole exception: the VS Code Marketplace requires an extension name to match `[a-z0-9][a-z0-9-]*`, so `vsce` rejects a scope outright. Its Marketplace identity, `tumaet.apollon-vscode`, is the same name spelled the way the Marketplace spells scopes — and it is fixed for good, because the identity is what installed clients follow for updates.
The scope carries the organization and the name carries the role, so `@tumaet/apollon` is the product and every other workspace is named for the job it does. `apollon-vscode` is the sole exception: the VS Code Marketplace requires an extension name to match `[a-z0-9][a-z0-9-]*`, so `vsce` rejects a scope outright.

Its Marketplace identity is `publisher.name` — `aet-tum.apollon-vscode`, where `aet-tum` is the organization's publisher, alongside `aet-tum.iris-thaumantias`. `publisher` and `name` in `vscode-extension/package.json` are the only source for that identity: the release workflow reads both to decide which listing to query, which tag to cut, and what to print in the release notes. Changing either points the release at a different listing, and installed clients follow the identity, not the repository — a new identity reaches nobody who already has the extension.
2 changes: 1 addition & 1 deletion docs/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ const config: Config = {
},
{
label: "VS Code Extension",
href: "https://marketplace.visualstudio.com/items?itemName=tumaet.apollon-vscode",
href: "https://marketplace.visualstudio.com/items?itemName=aet-tum.apollon-vscode",
},
{
label: "Release Notes",
Expand Down
2 changes: 1 addition & 1 deletion docs/user/getting-started/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Open <https://apollon.aet.cit.tum.de> in a browser. No install, no account. Diag

## VS Code extension

Install **Apollon** from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=tumaet.apollon-vscode) (or [Open VSX](https://open-vsx.org/extension/tumaet/apollon-vscode)). Diagrams live next to your code as `.apollon` files.
Install **Apollon** from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=aet-tum.apollon-vscode) (or [Open VSX](https://open-vsx.org/extension/aet-tum/apollon-vscode)). Diagrams live next to your code as `.apollon` files.

## Self-host

Expand Down
2 changes: 1 addition & 1 deletion library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ The server-side wire protocol is exposed through the `@tumaet/apollon/internals`

- Source and issues: <https://github.com/ls1intum/Apollon>
- Live editor: <https://apollon.aet.cit.tum.de>
- The standalone web editor, collaboration server, and [VS Code extension](https://marketplace.visualstudio.com/items?itemName=tumaet.apollon-vscode) live in the same monorepo.
- The standalone web editor, collaboration server, and [VS Code extension](https://marketplace.visualstudio.com/items?itemName=aet-tum.apollon-vscode) live in the same monorepo.

## License

Expand Down
Loading
Loading