From b479874fc5b74dedfce0c958b51d2307e7849660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Porras=20Campo?= Date: Thu, 21 May 2026 12:26:52 +0200 Subject: [PATCH] feat: publish p2 update site to GitHub Pages via gh-pages branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create a snapshot workflow that build and deploys on every push to master or v[0-9]* branches into p2/snapshots//; keep last 20, clean up older ones - Create a release workflow to promote existing snapshots to releases; The workflow creates a tag and copy the corresponding snapshot for that commit into p2/releases// on gh-pages - Add composite p2 repositories (compositeContent.xml, compositeArtifacts.xml, p2.index) for p2/releases/latest/ and p2/snapshots/latest/ so Eclipse can resolve "latest" without a fixed URL - Add generated index.html listing all snapshot and release links - Document p2 update site URLs in README.md Co-authored-by: João Dinis Ferreira --- .github/scripts/cleanup-p2-snapshots.sh | 43 +++++++ .github/scripts/publish-p2-ghpages.sh | 127 +++++++++++++++++++ .github/workflows/release.yml | 156 ++++++++++++++++++++++++ .github/workflows/snapshot.yml | 96 +++++++++++++++ README.md | 47 +++++++ ddk-parent/pom.xml | 36 ++++++ 6 files changed, 505 insertions(+) create mode 100644 .github/scripts/cleanup-p2-snapshots.sh create mode 100644 .github/scripts/publish-p2-ghpages.sh create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/snapshot.yml diff --git a/.github/scripts/cleanup-p2-snapshots.sh b/.github/scripts/cleanup-p2-snapshots.sh new file mode 100644 index 0000000000..043663a3b5 --- /dev/null +++ b/.github/scripts/cleanup-p2-snapshots.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Removes old p2 snapshots from gh-pages, keeping the last 20. +# Ordering uses the .created timestamp written by publish-p2-ghpages.sh; +# dirs without that file (pre-dating this change) are treated as oldest. +# Required env vars: GITHUB_TOKEN + +set -euo pipefail + +KEEP=20 + +cd gh-pages +git config user.email "github-actions[bot]@users.noreply.github.com" +git config user.name "github-actions[bot]" + +if [ -d p2/snapshots ]; then + to_delete=$( + for d in $(ls -1 p2/snapshots | grep -v latest); do + ts=0 + [ -f "p2/snapshots/$d/.created" ] && ts=$(cat "p2/snapshots/$d/.created") + printf '%s %s\n' "$ts" "$d" + done | sort -n | head -n -"$KEEP" | awk '{print $2}' + ) + if [ -n "$to_delete" ]; then + echo "$to_delete" | xargs -I{} rm -rf "p2/snapshots/{}" + fi +fi + +git add -A +if git diff --cached --quiet; then + echo "No snapshot cleanup needed" + exit 0 +fi +git commit -m "Clean up old p2 snapshots" +for attempt in 1 2 3; do + if git push origin HEAD:gh-pages; then + exit 0 + fi + echo "Push attempt $attempt failed; fetching and rebasing" + git fetch origin gh-pages + git rebase origin/gh-pages +done +echo "Failed to push gh-pages after 3 attempts" +exit 1 diff --git a/.github/scripts/publish-p2-ghpages.sh b/.github/scripts/publish-p2-ghpages.sh new file mode 100644 index 0000000000..ad3239c8ba --- /dev/null +++ b/.github/scripts/publish-p2-ghpages.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# Publishes the p2 update site to the gh-pages branch. +# +# Snapshot mode (RELEASE_VERSION unset): +# Copies ddk-repository/target/repository/ → p2/snapshots// +# Updates p2/snapshots/latest/ composite repository +# +# Release mode (RELEASE_VERSION set): +# Copies p2/snapshots// → p2/releases// +# Updates p2/releases/latest/ composite repository +# +# Required env vars: GITHUB_TOKEN, GITHUB_SHA, GITHUB_REPOSITORY +# Release-only vars: RELEASE_VERSION, SNAPSHOT_SHA + +set -euo pipefail + +write_composite() { + local dir="$1" + local name="$2" + local child="$3" + rm -rf "$dir" + mkdir -p "$dir" + cat > "$dir/p2.index" << 'EOF' +version=1 +metadata.repository.factory.order=compositeContent.xml,\! +artifact.repository.factory.order=compositeArtifacts.xml,\! +EOF + cat > "$dir/compositeContent.xml" << EOF + + + + + + +EOF + cat > "$dir/compositeArtifacts.xml" << EOF + + + + + + +EOF +} + +cd gh-pages +git config user.email "github-actions[bot]@users.noreply.github.com" +git config user.name "github-actions[bot]" + +touch .nojekyll + +if [ -n "${RELEASE_VERSION:-}" ]; then + TARGET="p2/releases/$RELEASE_VERSION" + REF_DESC="release $RELEASE_VERSION" + rm -rf "$TARGET" + mkdir -p "$TARGET" + cp -r "p2/snapshots/$SNAPSHOT_SHA/." "$TARGET/" +else + TARGET="p2/snapshots/${GITHUB_SHA::8}" + REF_DESC="snapshot ${GITHUB_SHA::8}" + rm -rf "$TARGET" + mkdir -p "$TARGET" + cp -r ../ddk-repository/target/repository/. "$TARGET/" + date +%s > "$TARGET/.created" +fi + +TIMESTAMP="$(date +%s)000" +if [ -n "${RELEASE_VERSION:-}" ]; then + HIGHEST=$(ls -1 p2/releases | grep -v latest | sort -Vr | head -1) + write_composite p2/releases/latest "DDK p2 Latest Release" "../$HIGHEST/" +else + SHORT_SHA="${GITHUB_SHA::8}" + write_composite p2/snapshots/latest "DDK p2 Latest Snapshot" "../$SHORT_SHA/" +fi + +# Generate index.html +{ + echo '' + echo '' + echo '

DSL DevKit p2 Repository

' + echo '

Snapshots

' + echo '
    ' + if [ -d p2/snapshots/latest ]; then + echo '
  • Latest Snapshot
  • ' + fi + if [ -d p2/snapshots ]; then + while IFS= read -r s; do + echo "
  • $s
  • " + done < <( + for d in $(ls -1 p2/snapshots | grep -v latest); do + ts=0 + [ -f "p2/snapshots/$d/.created" ] && ts=$(cat "p2/snapshots/$d/.created") + printf '%s %s\n' "$ts" "$d" + done | sort -rn | head -20 | awk '{print $2}' + ) + fi + echo '
' + if [ -d p2/releases ] && [ -n "$(ls -A p2/releases 2>/dev/null)" ]; then + echo '

Releases

' + echo '
    ' + if [ -d p2/releases/latest ]; then + echo '
  • Latest Release
  • ' + fi + for v in $(ls -1 p2/releases | grep -v latest | sort -Vr); do + echo "
  • $v
  • " + done + echo '
' + fi + echo '' +} > index.html + +git add -A +if git diff --cached --quiet; then + echo "No changes to publish" + exit 0 +fi +git commit -m "Publish $REF_DESC" +for attempt in 1 2 3; do + if git push origin HEAD:gh-pages; then + exit 0 + fi + echo "Push attempt $attempt failed; fetching and rebasing" + git fetch origin gh-pages + git rebase origin/gh-pages +done +echo "Failed to push gh-pages after 3 attempts" +exit 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..ba9b1502b6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,156 @@ +name: release + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to release from' + required: false + default: master + type: string + version_bump: + description: 'Version component to bump' + required: false + default: patch + type: choice + options: + - patch + - minor + - major + +concurrency: + group: publish-ghpages-${{ github.repository }} + cancel-in-progress: false + +permissions: + contents: write + +jobs: + create_tag: + runs-on: ubuntu-24.04 + environment: release + outputs: + tag: ${{ steps.version.outputs.tag }} + snapshot_sha: ${{ steps.snapshot.outputs.sha }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: ${{ inputs.branch }} + fetch-depth: 0 + + - name: Compute next version + id: version + run: | + LATEST=$(git tag --list 'v*' --sort=-version:refname | head -1) + if [ -z "$LATEST" ]; then + LATEST="v0.0.0" + fi + VERSION="${LATEST#v}" + MAJOR=$(echo "$VERSION" | cut -d. -f1) + MINOR=$(echo "$VERSION" | cut -d. -f2) + PATCH=$(echo "$VERSION" | cut -d. -f3) + case "${{ inputs.version_bump }}" in + major) MAJOR=$((MAJOR+1)); MINOR=0; PATCH=0 ;; + minor) MINOR=$((MINOR+1)); PATCH=0 ;; + patch) PATCH=$((PATCH+1)) ;; + esac + NEW_TAG="v${MAJOR}.${MINOR}.${PATCH}" + echo "tag=$NEW_TAG" >> "$GITHUB_OUTPUT" + echo "Previous version $LATEST → next version $NEW_TAG" + + - name: Set snapshot SHA + id: snapshot + run: echo "sha=$(git rev-parse --short=8 HEAD)" >> "$GITHUB_OUTPUT" + + - name: Check out gh-pages branch + id: checkout-gh-pages + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: gh-pages + path: gh-pages + fetch-depth: 1 + continue-on-error: true + + - name: Verify snapshot exists for release + run: | + SHA="${{ steps.snapshot.outputs.sha }}" + if [ "${{ steps.checkout-gh-pages.outcome }}" != "success" ]; then + echo "::error::gh-pages branch does not exist; no snapshot available for commit $SHA" + exit 1 + fi + if [ ! -d "gh-pages/p2/snapshots/$SHA" ]; then + echo "::error::No snapshot found for commit $SHA in p2/snapshots/; the commit must be built and deployed as a snapshot before releasing" + exit 1 + fi + echo "Found snapshot for commit $SHA" + + - name: Create and push tag + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + echo "Bumping to ${{ steps.version.outputs.tag }}" + git tag "${{ steps.version.outputs.tag }}" + git push origin "${{ steps.version.outputs.tag }}" + + publish: + needs: create_tag + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: ${{ needs.create_tag.outputs.tag }} + fetch-depth: 0 + + - name: Check out gh-pages branch + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: gh-pages + path: gh-pages + fetch-depth: 1 + + - name: Publish p2 release to gh-pages + run: bash .github/scripts/publish-p2-ghpages.sh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_VERSION: ${{ needs.create_tag.outputs.tag }} + SNAPSHOT_SHA: ${{ needs.create_tag.outputs.snapshot_sha }} + + - name: Create release with p2 update site + run: | + RELEASE_VERSION="${{ needs.create_tag.outputs.tag }}" + SNAPSHOT_SHA="${{ needs.create_tag.outputs.snapshot_sha }}" + mkdir -p release-staging/repository + cp -r "gh-pages/p2/snapshots/$SNAPSHOT_SHA/." release-staging/repository/ + cd release-staging + zip -r p2-update-site.zip repository/ + gh release create "$RELEASE_VERSION" \ + p2-update-site.zip \ + --generate-notes \ + --title "Release ${RELEASE_VERSION#v}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Summary + run: | + TAG="${{ needs.create_tag.outputs.tag }}" + REPO="${{ github.server_url }}/${{ github.repository }}" + echo "::notice::Created release $TAG from branch ${{ inputs.branch }}" + { + echo "## Release created" + echo "" + echo "| | |" + echo "|---|---|" + echo "| **Tag** | [$TAG]($REPO/releases/tag/$TAG) |" + echo "| **Branch** | \`${{ inputs.branch }}\` |" + echo "| **Bump** | \`${{ inputs.version_bump }}\` |" + } >> "$GITHUB_STEP_SUMMARY" + +# Release workflow: +# Snapshots: every push to master or v[0-9]* branches → see snapshot.yml +# Release: workflow_dispatch → create tag, promote snapshot to p2/releases// +# +# p2 repository URLs (GitHub Pages): +# Latest snapshot: https://dsldevkit.github.io/dsl-devkit/p2/snapshots/latest/ +# Pinned snapshot: https://dsldevkit.github.io/dsl-devkit/p2/snapshots// +# Latest release: https://dsldevkit.github.io/dsl-devkit/p2/releases/latest/ +# Pinned release: https://dsldevkit.github.io/dsl-devkit/p2/releases/{version}/ \ No newline at end of file diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml new file mode 100644 index 0000000000..c2b29fb76d --- /dev/null +++ b/.github/workflows/snapshot.yml @@ -0,0 +1,96 @@ +name: snapshot + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to build snapshot from' + required: true + default: master + push: + branches: [master, 'v[0-9]*'] + +concurrency: + group: publish-ghpages-${{ github.repository }} + cancel-in-progress: false + +permissions: + contents: write + +jobs: + publish: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: ${{ github.event.inputs.branch || github.ref }} + fetch-depth: 0 + + - name: Set up JDK 21 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + distribution: 'temurin' + java-version: '21' + + - name: Log tool versions + run: | + java --version + mvn --version + + - name: Cache Maven dependencies + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-publish-${{ hashFiles('**/pom.xml', '**/*.target') }} + restore-keys: | + ${{ runner.os }}-maven-publish- + ${{ runner.os }}-maven-0- + + - name: Build p2 update site + run: | + xvfb-run mvn clean verify \ + -f ./ddk-parent/pom.xml \ + --batch-mode + + - name: Upload p2 update site as artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: p2-update-site-${{ github.sha }} + path: ddk-repository/target/repository/ + retention-days: 30 + + - name: Check out gh-pages branch + id: checkout-gh-pages + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: gh-pages + path: gh-pages + fetch-depth: 1 + continue-on-error: true + + - name: Bootstrap gh-pages branch if missing + if: steps.checkout-gh-pages.outcome != 'success' + run: | + rm -rf gh-pages + mkdir gh-pages + cd gh-pages + git init -b gh-pages + touch .nojekyll + git remote add origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish p2 snapshot to gh-pages + run: bash .github/scripts/publish-p2-ghpages.sh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Clean up old p2 snapshots + run: bash .github/scripts/cleanup-p2-snapshots.sh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +# Snapshot workflow: +# Triggered on every push to master or v[0-9]* branches +# Builds, deploys to p2/snapshots//, keeps last 20 +# See release.yml for promoting a snapshot to a release diff --git a/README.md b/README.md index 2a24e3ec3a..b6524656c5 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,53 @@ Copyright 2016 Avaloq Group AG +# p2 Update Site + +The DDK p2 update site is published to GitHub Pages and can be used directly in Eclipse target platform definitions or as a p2 repository URL. + +> **Browse available releases and snapshots:** [dsldevkit.github.io/dsl-devkit/](https://dsldevkit.github.io/dsl-devkit/) +> Note: subdirectory URLs (e.g. `/p2/releases/latest/`) are for Eclipse only and return 404 in a browser. + +## Releases + +Use a pinned release URL for reproducible builds: + +``` +jar:https://github.com/dsldevkit/dsl-devkit/releases/download/v/p2-update-site.zip!/repository +``` + +For example, for version `v17.3.2`: + +``` +jar:https://github.com/dsldevkit/dsl-devkit/releases/download/v17.3.2/p2-update-site.zip!/repository +``` + +Alternatively, use the GitHub Pages p2 repository directly (no zip unwrapping needed): + +``` +https://dsldevkit.github.io/dsl-devkit/p2/releases/v17.3.2/ +``` + +Or always point to the latest release: + +``` +https://dsldevkit.github.io/dsl-devkit/p2/releases/latest/ +``` + +## Snapshots + +To use the latest snapshot from the `master` branch: + +``` +https://dsldevkit.github.io/dsl-devkit/p2/snapshots/latest/ +``` + +To pin a specific snapshot by commit SHA (first 8 characters): + +``` +https://dsldevkit.github.io/dsl-devkit/p2/snapshots// +``` + # Documentation You can find more detailed documentation and examples at [ddk.tools.avaloq.com](https://ddk.tools.avaloq.com/). diff --git a/ddk-parent/pom.xml b/ddk-parent/pom.xml index ab6dd15d6c..60c4f83e3a 100644 --- a/ddk-parent/pom.xml +++ b/ddk-parent/pom.xml @@ -56,6 +56,13 @@ 7.24.0 5.0.2 2.42.0 + + + error + + + https://dsldevkit.github.io/dsl-devkit/p2/releases/latest/ + true @@ -215,7 +222,16 @@ org.eclipse.tycho tycho-packaging-plugin ${tycho.version} + + + org.eclipse.tycho + tycho-buildtimestamp-jgit + ${tycho.version} + + + jgit + ${jgit.dirtyWorkingTree} 'v'yyyyMMdd-HHmm @@ -242,6 +258,26 @@ + + org.eclipse.tycho.extras + tycho-p2-extras-plugin + ${tycho.version} + + + compare-version-with-baselines + verify + + compare-version-with-baselines + + + + ${baseline.repo.url} + + ${baseline.skip} + + + + org.eclipse.tycho tycho-surefire-plugin