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
42 changes: 42 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ on:
description: Mark the VSIX as a pre-release.
type: boolean
default: false
extension-targets:
# Every VSIX bundles a platform-specific JRE, so a release has to build
# one package per platform. That is ~125 MB each, which is why anything
# that is not a release asks for the host alone.
description: 'Platforms to package the extension for: host, all, or a comma-separated list.'
type: string
default: host
test-extension:
description: Run the VS Code extension test suites.
type: boolean
Expand All @@ -42,6 +49,9 @@ on:
maven-cache-key:
description: Cache key holding the Maven dependencies, for pruning.
value: ${{ jobs.build.outputs.maven-cache-key }}
temurin-cache-key:
description: Cache key holding the bundled Java runtimes, for pruning.
value: ${{ jobs.build.outputs.temurin-cache-key }}
cli-version:
description: The osate-cli version from the build provenance.
value: ${{ jobs.build.outputs.cli-version }}
Expand All @@ -65,6 +75,7 @@ jobs:
osate-sha: ${{ steps.pin.outputs.sha }}
osate-cache-key: ${{ steps.keys.outputs.osate }}
maven-cache-key: ${{ steps.keys.outputs.maven }}
temurin-cache-key: ${{ steps.keys.outputs.temurin }}
cli-version: ${{ steps.versions.outputs.cli }}
extension-version: ${{ steps.versions.outputs.extension }}

Expand Down Expand Up @@ -102,9 +113,11 @@ jobs:
env:
OSATE_KEY: osate-${{ runner.os }}-${{ steps.pin.outputs.sha }}-v1
MAVEN_KEY: maven-${{ runner.os }}-${{ steps.pin.outputs.sha }}-${{ hashFiles('**/pom.xml') }}
TEMURIN_KEY: temurin-${{ runner.os }}-${{ hashFiles('scripts/lib/temurin.sh') }}
run: |
echo "osate=$OSATE_KEY" >> "$GITHUB_OUTPUT"
echo "maven=$MAVEN_KEY" >> "$GITHUB_OUTPUT"
echo "temurin=$TEMURIN_KEY" >> "$GITHUB_OUTPUT"

# Third-party downloads, above all Tycho's resolution of the Eclipse target
# platform. Safe to reuse partially, so prefix fallback is enabled. org/osate
Expand Down Expand Up @@ -203,6 +216,21 @@ jobs:
fi
echo "osate2 pin: \`${{ steps.pin.outputs.sha }}\`" >> "$GITHUB_STEP_SUMMARY"

# The bundled JREs the extension packages: ~50 MB per platform, so a release
# that builds all six downloads ~300 MB from Adoptium. Keyed on the helper
# that owns the feature version. Safe to reuse loosely — the helper verifies
# every archive against Adoptium's published checksum and re-downloads one
# that a floating GA release has superseded.
- name: Restore the bundled Java runtimes
id: temurin
if: inputs.build-extension
uses: actions/cache/restore@v5
with:
path: target/temurin-downloads
key: temurin-${{ runner.os }}-${{ hashFiles('scripts/lib/temurin.sh') }}
restore-keys: |
temurin-${{ runner.os }}-

# Build only what the caller asked for. A release of one component has no
# reason to build the other two.
- name: Build and test
Expand All @@ -219,6 +247,9 @@ jobs:
if [ "${{ inputs.extension-pre-release }}" = "true" ]; then
args+=(--extension-pre-release)
fi
if [ "${{ inputs.build-extension }}" = "true" ]; then
args+=(--extension-targets "${{ inputs.extension-targets }}")
fi
printf 'build-test-release %s\n' "${args[*]}"
./scripts/build-test-release "${args[@]}"

Expand Down Expand Up @@ -260,12 +291,16 @@ jobs:
aadl-language-server/releng/org.osate.aadl.ls.repository/target/*.zip
target/build-provenance.properties

# One VSIX per platform, each ~125 MB because of its bundled runtime, so
# these are not kept for the default 90 days. A release publishes from them
# within the same workflow run.
- name: Upload the VS Code extension
if: inputs.upload-artifacts && inputs.build-extension
uses: actions/upload-artifact@v6
with:
name: vscode-extension
if-no-files-found: error
retention-days: 7
path: vscode-extension/aadl2-*.vsix

- name: Upload the CLI distribution
Expand Down Expand Up @@ -298,3 +333,10 @@ jobs:
~/.m2/repository
!~/.m2/repository/org/osate
key: maven-${{ runner.os }}-${{ steps.pin.outputs.sha }}-${{ hashFiles('**/pom.xml') }}

- name: Save the bundled Java runtimes
if: always() && inputs.build-extension && steps.temurin.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: target/temurin-downloads
key: temurin-${{ runner.os }}-${{ hashFiles('scripts/lib/temurin.sh') }}
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ jobs:
GH_TOKEN: ${{ github.token }}
OSATE_KEEP: ${{ needs.build.outputs.osate-cache-key }}
MAVEN_KEEP: ${{ needs.build.outputs.maven-cache-key }}
TEMURIN_KEEP: ${{ needs.build.outputs.temurin-cache-key }}
steps:
- name: Delete superseded cache entries
run: |
Expand Down Expand Up @@ -95,6 +96,7 @@ jobs:

prune "osate-${{ runner.os }}-" "$OSATE_KEEP"
prune "maven-${{ runner.os }}-" "$MAVEN_KEEP"
prune "temurin-${{ runner.os }}-" "$TEMURIN_KEEP"

# Pull-request caches live on their own refs, which the prune above
# deliberately does not touch, and they are the bulk of the usage: one
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/release-osate-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,19 @@ jobs:
-Djavadoc=false -DfailIfNoTests=false \
clean install

# The four bundled JREs, ~190 MB from Adoptium on every release otherwise.
# Keyed on the helper that owns the feature version and shared in spirit with
# build.yml's cache; the paths differ because each packaging path keeps its
# own download directory. Loose reuse is safe: the helper verifies every
# archive against Adoptium's published checksum.
- name: Cache the bundled Java runtimes
uses: actions/cache@v5
with:
path: osate-cli/packaging/target/downloads
key: temurin-cli-${{ runner.os }}-${{ hashFiles('scripts/lib/temurin.sh') }}
restore-keys: |
temurin-cli-${{ runner.os }}-

- name: Build the tooling and CLI
run: ./scripts/build-test-release --skip-osate --skip-extension

Expand Down
131 changes: 98 additions & 33 deletions .github/workflows/release-vscode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,18 @@ jobs:
# Decided at build time: vsce refuses to publish a package as a pre-release
# unless the marker is already in its manifest.
extension-pre-release: ${{ needs.verify-version.outputs.pre-release == 'true' }}
# Every package carries its own platform's Java runtime, so a release has to
# build all of them. Clients are offered the package matching their platform
# and nothing at all on a platform we do not build.
extension-targets: all

publish:
name: Publish
needs: [verify-version, build]
runs-on: ubuntu-latest
timeout-minutes: 20
# Six platform packages of ~125 MB each are uploaded twice over, to the
# Marketplace and to Open VSX.
timeout-minutes: 45
permissions:
contents: write
env:
Expand All @@ -104,38 +110,69 @@ jobs:
name: vscode-extension
path: dist

- name: Check the VSIX
- name: Check the VSIX set
run: |
vsix="dist/aadl2-${VERSION}.vsix"
if [ ! -f "$vsix" ]; then
echo "Expected $vsix; got:" >&2
ls -l dist >&2
exit 1
fi
# The server plug-ins reach the VSIX through a symlink that vsce
# dereferences. An empty server/aadl/lib means the package is useless.
count=$(unzip -l "$vsix" | grep -c 'extension/server/aadl/lib/.*\.jar')
echo "$count server plug-in jars in the VSIX"
if [ "$count" -lt 100 ]; then
echo "Too few server plug-ins; the symlink did not resolve." >&2
exit 1
fi
# Every supported platform must be present before anything is published.
# Marketplace versions are immutable, so publishing some targets and
# failing on the rest would leave platforms permanently on different
# versions of the extension.
targets=$(vscode-extension/packaging/scripts/stage-runtime --all-targets)
paths=()
for target in $targets; do
vsix="dist/aadl2-${target}-${VERSION}.vsix"
if [ ! -f "$vsix" ]; then
echo "Expected $vsix; got:" >&2
ls -l dist >&2
exit 1
fi

# The marker is baked in at package time, so confirm the package matches
# what the tag asked for. vsce would reject a mismatch in one direction
# and silently publish a pre-release as stable in the other.
if unzip -p "$vsix" extension.vsixmanifest | grep -q 'Microsoft.VisualStudio.Code.PreRelease'; then
marked=true
else
marked=false
fi
echo "packaged as pre-release: $marked (tag asked for $PRE_RELEASE)"
if [ "$marked" != "$PRE_RELEASE" ]; then
echo "VSIX pre-release marker does not match the tag." >&2
exit 1
fi
# The server plug-ins and the bundled runtime both reach the VSIX
# through symlinks that vsce dereferences. Either one missing makes the
# package useless, and neither shows up as a packaging failure.
jars=$(unzip -l "$vsix" | grep -c 'extension/server/aadl/lib/.*\.jar' || true)
if [ "$jars" -lt 100 ]; then
echo "$vsix holds only $jars server plug-ins; the symlink did not resolve." >&2
exit 1
fi

case "$target" in
win32-*) java_entry='extension/runtime/bin/java\.exe$' ;;
*) java_entry='extension/runtime/bin/java$' ;;
esac
# grep -c, not -q: -q closes the pipe and the resulting SIGPIPE fails
# unzip, which under pipefail fails the step.
if [ "$(unzip -l "$vsix" | grep -cE "$java_entry" || true)" -eq 0 ]; then
echo "$vsix has no bundled Java runtime." >&2
exit 1
fi

manifest=$(unzip -p "$vsix" extension.vsixmanifest)

# A package built for the wrong platform would install on clients that
# cannot run its runtime.
if ! printf '%s' "$manifest" | grep -q "TargetPlatform=\"$target\""; then
echo "$vsix is not marked for $target." >&2
exit 1
fi

# The marker is baked in at package time, so confirm the package matches
# what the tag asked for. vsce would reject a mismatch in one direction
# and silently publish a pre-release as stable in the other.
if printf '%s' "$manifest" | grep -q 'Microsoft.VisualStudio.Code.PreRelease'; then
marked=true
else
marked=false
fi
if [ "$marked" != "$PRE_RELEASE" ]; then
echo "$vsix pre-release marker is $marked; the tag asked for $PRE_RELEASE." >&2
exit 1
fi

echo "$vsix: $jars server plug-ins, bundled runtime, pre-release=$marked"
paths+=("$vsix")
done

echo "VSIX=$vsix" >> "$GITHUB_ENV"
echo "VSIX_PATHS=${paths[*]}" >> "$GITHUB_ENV"

- name: Create the GitHub release
if: github.ref_type == 'tag'
Expand All @@ -146,12 +183,13 @@ jobs:
if [ "$PRE_RELEASE" = "true" ]; then
args+=(--prerelease)
fi
# shellcheck disable=SC2086 # the paths are a deliberate word list
gh release create "$GITHUB_REF_NAME" \
--repo "$GITHUB_REPOSITORY" \
--title "AADL2 VS Code extension $VERSION" \
--generate-notes \
"${args[@]+"${args[@]}"}" \
"$VSIX"
$VSIX_PATHS

- name: Install publishing tools
if: github.ref_type == 'tag' && (env.VSCE_PAT != '' || env.OVSX_PAT != '')
Expand All @@ -168,7 +206,15 @@ jobs:
if [ "$PRE_RELEASE" = "true" ]; then
args+=(--pre-release)
fi
npx --no-install vsce publish --packagePath "../$VSIX" "${args[@]+"${args[@]}"}"
paths=()
for vsix in $VSIX_PATHS; do
paths+=("../$vsix")
done
# One invocation with every package: --packagePath is variadic, and
# publishing the platforms together is the closest thing to an atomic
# release the Marketplace offers. --pre-release comes first because the
# variadic option would otherwise swallow it.
npx --no-install vsce publish "${args[@]+"${args[@]}"}" --packagePath "${paths[@]}"

# Requires the "osate" namespace claimed at open-vsx.org and its token
# stored as OVSX_PAT.
Expand All @@ -182,7 +228,26 @@ jobs:
if [ "$PRE_RELEASE" = "true" ]; then
args+=(--pre-release)
fi
npx --yes ovsx publish "../$VSIX" --pat "$OVSX_PAT" "${args[@]+"${args[@]}"}"
paths=()
for vsix in $VSIX_PATHS; do
paths+=("../$vsix")
done
# Retried per package: six ~125 MB uploads is the flakiest step in this
# workflow, and a re-run of the whole job cannot republish what already
# landed, because a published version is permanent.
for path in "${paths[@]}"; do
for attempt in 1 2 3; do
if npx --yes ovsx publish "$path" --pat "$OVSX_PAT" "${args[@]+"${args[@]}"}"; then
break
fi
if [ "$attempt" = 3 ]; then
echo "Open VSX publish failed for $path." >&2
exit 1
fi
echo "Retrying $path in 30s"
sleep 30
done
done

- name: Note skipped publications
if: github.ref_type == 'tag'
Expand Down
9 changes: 8 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ the VSIX before those plug-ins exist.
Important outputs:

- `aadl-language-server/releng/org.osate.aadl.ls.repository/target/repository/`
- `vscode-extension/aadl2-*.vsix`
- `vscode-extension/aadl2-<platform>-<version>.vsix` (host platform by default;
`--extension-targets all` builds every published platform)
- `osate-cli/dist/target/dist/`

Run Maven/Tycho builds, CLI integration tests, VS Code integration tests, and
Expand Down Expand Up @@ -126,6 +127,12 @@ owns the OSATE cache and delegates the rest to `scripts/build-test-release`;
build and a VSIX that silently ships no server or a stale one.
- Keep the plug-in exclusion lists in `osate-cli/dist/pom.xml` and
`vscode-extension/.vscodeignore` synchronized.
- Both deliverables bundle an Eclipse Temurin JRE through
`scripts/lib/temurin.sh`. The VS Code extension runs that runtime and nothing
else, so every VSIX is platform-specific: `vsce package --target` produces one
package per platform and no universal fallback is published. A build that does
not stage a runtime cannot produce a working package, which is why the
packaging script, not Maven, drives the per-target loop.
- The CLI workspace server loads language-server plug-ins from sibling JARs
using an isolated `URLClassLoader`. Do not shade it or nest the plug-in JARs.
- Protocol-visible command changes may require coordinated updates to the
Expand Down
36 changes: 29 additions & 7 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,23 @@ so a missing packager cannot silently drop the four Linux packages.

**`vscode-v*`** — [`release-vscode.yml`](.github/workflows/release-vscode.yml)

`aadl2-<version>.vsix` attached to a GitHub Release, then published to the VS
Code Marketplace and Open VSX. The VSIX is checked for a plausible number of
bundled server plug-ins first, because the server reaches it through a symlink
that would otherwise fail silently.
Six platform packages — `aadl2-<platform>-<version>.vsix` for darwin-x64,
darwin-arm64, linux-x64, linux-arm64, win32-x64 and win32-arm64 — attached to a
GitHub Release, then published to the VS Code Marketplace and Open VSX. Each one
embeds an Eclipse Temurin 21 JRE for its platform, which is why there is a package
per platform and no universal one.

Every package is checked before anything is published: a plausible number of
server plug-ins, the bundled runtime, the declared target platform, and the
pre-release marker. The plug-ins and the runtime both reach the VSIX through
symlinks that would otherwise fail silently, and Marketplace versions are
immutable — publishing three targets and then failing would leave platforms
stranded on different versions for good. For the same reason all six go to the
Marketplace in a single `vsce publish` invocation.

Clients on a platform we do not build for — Alpine Linux, 32-bit ARM, and the web
— are offered nothing at all, because no untargeted fallback package is
published.

### Stable or pre-release

Expand Down Expand Up @@ -129,6 +142,10 @@ To produce a pre-release VSIX locally:
./scripts/build-test-release --skip-osate --extension-pre-release
```

That packages the host platform only. Add `--extension-targets all` to reproduce
the full release set; each package downloads its platform's JRE once and is then
cached under `target/temurin-downloads`.

**`ls-v*`** — [`release-server.yml`](.github/workflows/release-server.yml)

The p2 repository archive plus `build-provenance.properties`, attached to a
Expand Down Expand Up @@ -179,8 +196,13 @@ gh workflow run release-osate-cli.yml --repo osate/aadl-tooling --ref main
- The `osate2` submodule pin is part of every release. `build-provenance.properties`
records the tooling commit, the OSATE commit and gitlink, and all three
versions, so a released artifact can always be traced back to its exact inputs.
- macOS tarballs are unsigned and not notarized. Downloads through a browser will
be quarantined by Gatekeeper; the Homebrew path is not affected. The `.deb` and
`.rpm` packages are unsigned too.
- macOS **tarballs** are unsigned and not notarized. Downloads through a browser
will be quarantined by Gatekeeper; the Homebrew path is not affected. The `.deb`
and `.rpm` packages are unsigned too. This does not apply to the VS Code
packages: Adoptium's own per-binary signatures travel inside the JRE, and VS
Code does not quarantine the files it extracts from a VSIX.
- The bundled JREs are verified against Adoptium's published checksums at build
time, and `build-provenance.properties` records the vendor, feature version and
resolved runtime version that shipped.
- Release runs reuse the cached OSATE build when the submodule pin has not moved.
A release right after a submodule bump pays for a full OSATE build.
Loading
Loading