From 535028640e640ba8b98258c1da8d07fd94b7d67e Mon Sep 17 00:00:00 2001 From: Lutz Reinhardt Date: Tue, 14 Apr 2026 11:00:37 +0000 Subject: [PATCH 1/8] slopped release code --- .github/workflows/weekly-release.yaml | 97 ++++++++++++++++++++++++ docs/README.md | 11 +++ scripts/release_metadata.sh | 104 ++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 .github/workflows/weekly-release.yaml create mode 100755 scripts/release_metadata.sh diff --git a/.github/workflows/weekly-release.yaml b/.github/workflows/weekly-release.yaml new file mode 100644 index 0000000..d9ae739 --- /dev/null +++ b/.github/workflows/weekly-release.yaml @@ -0,0 +1,97 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +# ***************************************************************************** +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ***************************************************************************** +name: Weekly DevContainer Release +description: This workflow creates a weekly semantic version release when main changed since the last release tag. +on: + schedule: + - cron: '0 8 * * 1' + workflow_dispatch: +permissions: + contents: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false +jobs: + prepare-release: + name: Prepare Weekly Release + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-24.04 + outputs: + has_changes: ${{ steps.metadata.outputs.has_changes }} + previous_tag: ${{ steps.metadata.outputs.previous_tag }} + next_tag: ${{ steps.metadata.outputs.next_tag }} + release_notes: ${{ steps.metadata.outputs.release_notes }} + steps: + - name: Checkout (GitHub) + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Fetch tags + run: git fetch --force --tags origin + - name: Determine release metadata + id: metadata + run: ./scripts/release_metadata.sh >> "${GITHUB_OUTPUT}" + - name: Skip release + if: steps.metadata.outputs.has_changes != 'true' + run: | + if [ -n "${{ steps.metadata.outputs.previous_tag }}" ]; then + echo "No commits since ${{ steps.metadata.outputs.previous_tag }}; skipping release." + else + echo "Repository has no releasable commits yet; skipping release." + fi + create-release: + name: Create Git Tag And Release + needs: [prepare-release, publish-release] + if: needs.prepare-release.outputs.has_changes == 'true' + runs-on: ubuntu-24.04 + permissions: + contents: write + steps: + - name: Checkout (GitHub) + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Fetch tags + run: git fetch --force --tags origin + - name: Configure git author + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + - name: Create and push tag + run: | + git tag -a "${{ needs.prepare-release.outputs.next_tag }}" -m "chore(release): ${{ needs.prepare-release.outputs.next_tag }}" + git push origin "${{ needs.prepare-release.outputs.next_tag }}" + - name: Create GitHub release + env: + GH_TOKEN: ${{ github.token }} + RELEASE_NOTES: ${{ needs.prepare-release.outputs.release_notes }} + run: | + printf '%s\n' "${RELEASE_NOTES}" > release_notes.md + gh release create "${{ needs.prepare-release.outputs.next_tag }}" \ + --title "${{ needs.prepare-release.outputs.next_tag }}" \ + --notes-file release_notes.md \ + # remove this and next line when the code works + --draft \ + --latest diff --git a/docs/README.md b/docs/README.md index 20f8a09..bb91971 100644 --- a/docs/README.md +++ b/docs/README.md @@ -98,3 +98,14 @@ It strengthens the supply chain by pinning versions and hashes, sourcing feature Pre-built images have a higer availability than the set of all tools which are installed (one download from a location controlled by S-CORE vs. many downloads from "everywhere"). Pre-built images can be easily archived anywhere, e.g. for reproducibility of builds in real production use-cases. It also enforces a clear separation of concerns: general tooling is delivered through reusable features, S-CORE–specific logic lives in a dedicated feature, and image composition plus publishing are centralized. + +## Release Automation + +Releases are cut automatically once per week from `main`, but only if commits were added since the latest `v..` tag. +The scheduled workflow creates the git tag and GitHub release, and the existing tag-triggered release workflow then builds, tests and publishes the matching container image. + +The next semantic version is derived from commit messages since the previous release: + +* breaking changes (`!` in the conventional commit header or `BREAKING CHANGE:` in the body) increment the major version +* `feat` commits increment the minor version +* every other commit increments the patch version so maintenance-only weeks still publish a new immutable image diff --git a/scripts/release_metadata.sh b/scripts/release_metadata.sh new file mode 100755 index 0000000..c1e6127 --- /dev/null +++ b/scripts/release_metadata.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +set -euo pipefail + +LATEST_TAG="$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | head -n 1 || true)" +BASE_VERSION="0.0.0" +REVISION_RANGE="HEAD" + +if [[ -n "${LATEST_TAG}" ]]; then + BASE_VERSION="${LATEST_TAG#v}" + REVISION_RANGE="${LATEST_TAG}..HEAD" +fi + +COMMIT_COUNT="$(git rev-list --count "${REVISION_RANGE}")" + +if [[ "${COMMIT_COUNT}" == "0" ]]; then + echo "has_changes=false" + echo "previous_tag=${LATEST_TAG}" + echo "next_version=" + echo "next_tag=" + echo "release_type=" + echo "commit_count=0" + echo "release_notes< Date: Tue, 14 Apr 2026 12:00:40 +0000 Subject: [PATCH 2/8] cleanup + move as much logic as possible into shell script --- .github/workflows/publish.yaml | 108 ++++++++++++++++++++ .github/workflows/release.yaml | 109 ++++---------------- .github/workflows/weekly-release.yaml | 97 ------------------ scripts/release.sh | 137 ++++++++++++++++++++++++++ 4 files changed, 265 insertions(+), 186 deletions(-) create mode 100644 .github/workflows/publish.yaml delete mode 100644 .github/workflows/weekly-release.yaml create mode 100755 scripts/release.sh diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..bdce8a8 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,108 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +name: Validate & Publish DevContainer +description: This workflow is checking that for releases, updates do not break stuff and publishes the released container. +on: + push: + tags: + - v[0-9]+.[0-9]+.[0-9]+ +jobs: + build: + strategy: + matrix: + os: [arm64, amd64] + include: + - os: amd64 + name: DevContainer (amd64) + runner: ubuntu-24.04 + - os: arm64 + name: DevContainer (arm64) + runner: ubuntu-24.04-arm + name: ${{ matrix.name }} + runs-on: ${{ matrix.runner }} + permissions: + contents: read + packages: write + id-token: write + steps: + - uses: eclipse-score/more-disk-space@v1 + - name: Checkout (GitHub) + uses: actions/checkout@v6 + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # Use .devcontainer from THIS repo for building and testing + - name: Check, Build, Test, Publish + uses: devcontainers/ci@v0.3 + with: + # The .devcontainer is never published as pre-built container. + # We want to only use it for building and testing the actual container, which resides in src/s-core-devcontainer. + push: never + runCmd: | + set -eux pipefail + + # Check + pre-commit run --show-diff-on-failure --color=always --all-files || exit 1 + + # Create builder for multi-arch builds + ./scripts/create_builder.sh + + # Build + ./scripts/build.sh --${{ matrix.os }} "${{ github.ref_name }}" "latest" + + # Test + ./scripts/test.sh + + # Publish + # We do not use the push feature of devcontainers/ci here, since that would push the wrong container. + # Instead, we use the publish script which pushes the correct container (residing in src/s-core-devcontainer). + # manually login to ghcr.io for publishing + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + ./scripts/publish.sh --${{ matrix.os }} "${{ github.ref_name }}" "latest" + merge: + name: Merge Labels + needs: [build] + runs-on: ubuntu-24.04 + permissions: + contents: read + packages: write + id-token: write + steps: + - uses: eclipse-score/more-disk-space@v1 + - name: Checkout (GitHub) + uses: actions/checkout@v6 + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # Use .devcontainer from THIS repo for building and testing + - name: Merge + uses: devcontainers/ci@v0.3 + with: + # The .devcontainer is never published as pre-built container. + # We want to only use it for building and testing the actual container, which resides in src/s-core-devcontainer. + push: never + runCmd: | + set -eux pipefail + + # Merge + # We do not use the push feature of devcontainers/ci here, since that would push the wrong container. + # Instead, we use the publish script which pushes the correct container (residing in src/s-core-devcontainer). + # manually login to ghcr.io for publishing + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + ./scripts/merge.sh "${{ github.ref_name }}" "latest" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index bdce8a8..96d77b0 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,99 +10,30 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -name: Validate & Publish DevContainer -description: This workflow is checking that for releases, updates do not break stuff and publishes the released container. +name: DevContainer Release +description: This workflow creates a semantic version release when main changed since the last release tag. on: - push: - tags: - - v[0-9]+.[0-9]+.[0-9]+ + schedule: + - cron: '0 8 * * 1' + workflow_dispatch: +permissions: + contents: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false jobs: - build: - strategy: - matrix: - os: [arm64, amd64] - include: - - os: amd64 - name: DevContainer (amd64) - runner: ubuntu-24.04 - - os: arm64 - name: DevContainer (arm64) - runner: ubuntu-24.04-arm - name: ${{ matrix.name }} - runs-on: ${{ matrix.runner }} - permissions: - contents: read - packages: write - id-token: write - steps: - - uses: eclipse-score/more-disk-space@v1 - - name: Checkout (GitHub) - uses: actions/checkout@v6 - - name: Login to GitHub Container Registry - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # Use .devcontainer from THIS repo for building and testing - - name: Check, Build, Test, Publish - uses: devcontainers/ci@v0.3 - with: - # The .devcontainer is never published as pre-built container. - # We want to only use it for building and testing the actual container, which resides in src/s-core-devcontainer. - push: never - runCmd: | - set -eux pipefail - - # Check - pre-commit run --show-diff-on-failure --color=always --all-files || exit 1 - - # Create builder for multi-arch builds - ./scripts/create_builder.sh - - # Build - ./scripts/build.sh --${{ matrix.os }} "${{ github.ref_name }}" "latest" - - # Test - ./scripts/test.sh - - # Publish - # We do not use the push feature of devcontainers/ci here, since that would push the wrong container. - # Instead, we use the publish script which pushes the correct container (residing in src/s-core-devcontainer). - # manually login to ghcr.io for publishing - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - ./scripts/publish.sh --${{ matrix.os }} "${{ github.ref_name }}" "latest" - merge: - name: Merge Labels - needs: [build] + release: + name: Release + if: github.ref == 'refs/heads/main' runs-on: ubuntu-24.04 - permissions: - contents: read - packages: write - id-token: write steps: - - uses: eclipse-score/more-disk-space@v1 - name: Checkout (GitHub) uses: actions/checkout@v6 - - name: Login to GitHub Container Registry - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # Use .devcontainer from THIS repo for building and testing - - name: Merge - uses: devcontainers/ci@v0.3 with: - # The .devcontainer is never published as pre-built container. - # We want to only use it for building and testing the actual container, which resides in src/s-core-devcontainer. - push: never - runCmd: | - set -eux pipefail - - # Merge - # We do not use the push feature of devcontainers/ci here, since that would push the wrong container. - # Instead, we use the publish script which pushes the correct container (residing in src/s-core-devcontainer). - # manually login to ghcr.io for publishing - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - ./scripts/merge.sh "${{ github.ref_name }}" "latest" + fetch-depth: 0 + - name: Run release + env: + GH_TOKEN: ${{ github.token }} + RELEASE_DRAFT: 'true' + RELEASE_LATEST: 'true' + run: ./scripts/release.sh run diff --git a/.github/workflows/weekly-release.yaml b/.github/workflows/weekly-release.yaml deleted file mode 100644 index d9ae739..0000000 --- a/.github/workflows/weekly-release.yaml +++ /dev/null @@ -1,97 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* -# ***************************************************************************** -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ***************************************************************************** -name: Weekly DevContainer Release -description: This workflow creates a weekly semantic version release when main changed since the last release tag. -on: - schedule: - - cron: '0 8 * * 1' - workflow_dispatch: -permissions: - contents: write -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false -jobs: - prepare-release: - name: Prepare Weekly Release - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-24.04 - outputs: - has_changes: ${{ steps.metadata.outputs.has_changes }} - previous_tag: ${{ steps.metadata.outputs.previous_tag }} - next_tag: ${{ steps.metadata.outputs.next_tag }} - release_notes: ${{ steps.metadata.outputs.release_notes }} - steps: - - name: Checkout (GitHub) - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - name: Fetch tags - run: git fetch --force --tags origin - - name: Determine release metadata - id: metadata - run: ./scripts/release_metadata.sh >> "${GITHUB_OUTPUT}" - - name: Skip release - if: steps.metadata.outputs.has_changes != 'true' - run: | - if [ -n "${{ steps.metadata.outputs.previous_tag }}" ]; then - echo "No commits since ${{ steps.metadata.outputs.previous_tag }}; skipping release." - else - echo "Repository has no releasable commits yet; skipping release." - fi - create-release: - name: Create Git Tag And Release - needs: [prepare-release, publish-release] - if: needs.prepare-release.outputs.has_changes == 'true' - runs-on: ubuntu-24.04 - permissions: - contents: write - steps: - - name: Checkout (GitHub) - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - name: Fetch tags - run: git fetch --force --tags origin - - name: Configure git author - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - name: Create and push tag - run: | - git tag -a "${{ needs.prepare-release.outputs.next_tag }}" -m "chore(release): ${{ needs.prepare-release.outputs.next_tag }}" - git push origin "${{ needs.prepare-release.outputs.next_tag }}" - - name: Create GitHub release - env: - GH_TOKEN: ${{ github.token }} - RELEASE_NOTES: ${{ needs.prepare-release.outputs.release_notes }} - run: | - printf '%s\n' "${RELEASE_NOTES}" > release_notes.md - gh release create "${{ needs.prepare-release.outputs.next_tag }}" \ - --title "${{ needs.prepare-release.outputs.next_tag }}" \ - --notes-file release_notes.md \ - # remove this and next line when the code works - --draft \ - --latest diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..6fa8d29 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash + +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +usage() { + echo "Usage:" + echo " $0 run" + echo " $0 prepare-metadata" + echo " $0 print-skip-message [previous_tag]" + echo " $0 create-and-publish " +} + +run_release() { + local metadata + metadata="$(prepare_metadata)" + + local has_changes previous_tag next_tag release_notes + has_changes="$(printf '%s\n' "${metadata}" | grep '^has_changes=' | cut -d= -f2-)" + previous_tag="$(printf '%s\n' "${metadata}" | grep '^previous_tag=' | cut -d= -f2-)" + next_tag="$(printf '%s\n' "${metadata}" | grep '^next_tag=' | cut -d= -f2-)" + release_notes="$(printf '%s\n' "${metadata}" | awk '/^release_notes</dev/null; then + echo "Error: tag ${next_tag} already exists." + exit 1 + fi + + git tag -a "${next_tag}" -m "chore(release): ${next_tag}" + git push origin "${next_tag}" + + local notes_file + notes_file="$(mktemp)" + trap 'rm -f "${notes_file}"' EXIT + + printf '%s\n' "${RELEASE_NOTES}" > "${notes_file}" + + local -a gh_args + gh_args=(release create "${next_tag}" --title "${next_tag}" --notes-file "${notes_file}") + + if [[ "${RELEASE_DRAFT:-false}" == "true" ]]; then + gh_args+=(--draft) + fi + + if [[ "${RELEASE_LATEST:-true}" == "true" ]]; then + gh_args+=(--latest) + fi + + gh "${gh_args[@]}" +} + +COMMAND="${1:-}" +case "${COMMAND}" in + run) + run_release + ;; + prepare-metadata) + prepare_metadata + ;; + print-skip-message) + shift + print_skip_message "${1:-}" + ;; + create-and-publish) + shift + create_and_publish "${1:-}" + ;; + *) + usage + exit 1 + ;; +esac From af331cea993b2a518cc5feb527ff9157c1a8646c Mon Sep 17 00:00:00 2001 From: Lutz Reinhardt Date: Tue, 14 Apr 2026 12:39:01 +0000 Subject: [PATCH 3/8] simplify release.sh --- .github/workflows/release.yaml | 6 +- scripts/release.sh | 131 +++++++++++++++++++++++++-------- scripts/release_metadata.sh | 104 -------------------------- 3 files changed, 101 insertions(+), 140 deletions(-) delete mode 100755 scripts/release_metadata.sh diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 96d77b0..8034c3e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -32,8 +32,4 @@ jobs: with: fetch-depth: 0 - name: Run release - env: - GH_TOKEN: ${{ github.token }} - RELEASE_DRAFT: 'true' - RELEASE_LATEST: 'true' - run: ./scripts/release.sh run + run: ./scripts/release.sh ${{ github.token }} diff --git a/scripts/release.sh b/scripts/release.sh index 6fa8d29..90ebf5a 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -15,14 +15,9 @@ set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - usage() { echo "Usage:" - echo " $0 run" - echo " $0 prepare-metadata" - echo " $0 print-skip-message [previous_tag]" - echo " $0 create-and-publish " + echo " $0 [GH_TOKEN]" } run_release() { @@ -49,7 +44,94 @@ fetch_tags() { prepare_metadata() { fetch_tags - "${SCRIPT_DIR}/release_metadata.sh" + + LATEST_TAG="$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | head -n 1 || true)" + BASE_VERSION="0.0.0" + REVISION_RANGE="HEAD" + + if [[ -n "${LATEST_TAG}" ]]; then + BASE_VERSION="${LATEST_TAG#v}" + REVISION_RANGE="${LATEST_TAG}..HEAD" + fi + + COMMIT_COUNT="$(git rev-list --count "${REVISION_RANGE}")" + + if [[ "${COMMIT_COUNT}" == "0" ]]; then + echo "has_changes=false" + echo "previous_tag=${LATEST_TAG}" + echo "next_version=" + echo "next_tag=" + echo "release_type=" + echo "commit_count=0" + echo "release_notes< Date: Tue, 14 Apr 2026 13:02:53 +0000 Subject: [PATCH 4/8] more simplification --- scripts/release.sh | 64 ++++++++++------------------------------------ 1 file changed, 14 insertions(+), 50 deletions(-) diff --git a/scripts/release.sh b/scripts/release.sh index 90ebf5a..0d566ad 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -21,21 +21,14 @@ usage() { } run_release() { - local metadata - metadata="$(prepare_metadata)" - - local has_changes previous_tag next_tag release_notes - has_changes="$(printf '%s\n' "${metadata}" | grep '^has_changes=' | cut -d= -f2-)" - previous_tag="$(printf '%s\n' "${metadata}" | grep '^previous_tag=' | cut -d= -f2-)" - next_tag="$(printf '%s\n' "${metadata}" | grep '^next_tag=' | cut -d= -f2-)" - release_notes="$(printf '%s\n' "${metadata}" | awk '/^release_notes< Date: Tue, 14 Apr 2026 13:46:07 +0000 Subject: [PATCH 5/8] switch to semantic-release --- .github/workflows/release.yaml | 32 ++++++++++++++++++++++---------- .releaserc | 3 +++ REUSE.toml | 5 +++++ 3 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 .releaserc diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 8034c3e..d88574e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,19 +17,31 @@ on: - cron: '0 8 * * 1' workflow_dispatch: permissions: - contents: write -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false + contents: read # for checkout jobs: release: - name: Release if: github.ref == 'refs/heads/main' - runs-on: ubuntu-24.04 + permissions: + contents: write # to be able to publish a GitHub release + issues: write # to be able to comment on released issues + pull-requests: write # to be able to comment on released pull requests + id-token: write # to enable use of OIDC for npm provenance + name: release + runs-on: ubuntu-latest steps: - - name: Checkout (GitHub) - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - name: Run release - run: ./scripts/release.sh ${{ github.token }} + fetch-tags: true + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + cache: npm + node-version: lts/* + - run: npm clean-install + - run: npm install --global corepack@latest + - run: corepack npm audit signatures + # pinned version updated automatically by Renovate. + # details at https://semantic-release.gitbook.io/semantic-release/usage/installation#global-installation + - run: npx semantic-release@25.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000..c9a35d7 --- /dev/null +++ b/.releaserc @@ -0,0 +1,3 @@ +{ + "branches": ["main"] +} diff --git a/REUSE.toml b/REUSE.toml index fb1753d..d6231a3 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -29,3 +29,8 @@ path = ["resources/reopen_in_container.png", ] SPDX-FileCopyrightText = "Copyright (c) 2026 Contributors to the Eclipse Foundation" SPDX-License-Identifier = "Apache-2.0" + +[[annotations]] +path = [".releaserc"] +SPDX-FileCopyrightText = "Copyright (c) 2026 Contributors to the Eclipse Foundation" +SPDX-License-Identifier = "Apache-2.0" From 97d442333dc05376d7a62fa4e1c56b70cd7ecc83 Mon Sep 17 00:00:00 2001 From: Lutz Reinhardt Date: Tue, 14 Apr 2026 21:33:42 +0000 Subject: [PATCH 6/8] working release workflow --- .github/workflows/release.yaml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d88574e..312f26a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -14,7 +14,7 @@ name: DevContainer Release description: This workflow creates a semantic version release when main changed since the last release tag. on: schedule: - - cron: '0 8 * * 1' + - cron: '0 0 * * 1' workflow_dispatch: permissions: contents: read # for checkout @@ -25,22 +25,18 @@ jobs: contents: write # to be able to publish a GitHub release issues: write # to be able to comment on released issues pull-requests: write # to be able to comment on released pull requests - id-token: write # to enable use of OIDC for npm provenance name: release runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/devcontainers/javascript-node:4-24 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 fetch-tags: true - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - with: - cache: npm - node-version: lts/* - - run: npm clean-install - - run: npm install --global corepack@latest - - run: corepack npm audit signatures - # pinned version updated automatically by Renovate. + # this should have been done by the checkout action, but it doesn't work in a container, see https://github.com/actions/checkout/issues/766 + - run: git config --global --add safe.directory $PWD + # pinned version updated automatically by Dependabot. # details at https://semantic-release.gitbook.io/semantic-release/usage/installation#global-installation - run: npx semantic-release@25.0.1 env: From 8c443be95723b500cf58fcad27a0ce98325c786b Mon Sep 17 00:00:00 2001 From: Lutz Reinhardt Date: Tue, 14 Apr 2026 21:36:51 +0000 Subject: [PATCH 7/8] add .releaserc --- .releaserc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.releaserc b/.releaserc index c9a35d7..0cab322 100644 --- a/.releaserc +++ b/.releaserc @@ -1,3 +1,10 @@ { - "branches": ["main"] + "branches": [ + "main" + ], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/github" + ] } From 708ad513186ffa573a509c3d77da7a89d04bde4f Mon Sep 17 00:00:00 2001 From: Lutz Reinhardt Date: Tue, 14 Apr 2026 21:37:18 +0000 Subject: [PATCH 8/8] remove script --- scripts/release.sh | 170 --------------------------------------------- 1 file changed, 170 deletions(-) delete mode 100755 scripts/release.sh diff --git a/scripts/release.sh b/scripts/release.sh deleted file mode 100755 index 0d566ad..0000000 --- a/scripts/release.sh +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env bash - -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* - -set -euo pipefail - -usage() { - echo "Usage:" - echo " $0 [GH_TOKEN]" -} - -run_release() { - prepare_metadata - - if [[ "${has_changes}" != "true" ]]; then - echo "No commits since ${LATEST_TAG}; skipping release." - return 0 - fi - - create_and_publish "${NEXT_TAG}" -} - -fetch_tags() { - git fetch --force --tags origin -} - -prepare_metadata() { - fetch_tags - - LATEST_TAG="$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | head -n 1 || true)" - BASE_VERSION="0.0.0" - REVISION_RANGE="HEAD" - - if [[ -n "${LATEST_TAG}" ]]; then - BASE_VERSION="${LATEST_TAG#v}" - REVISION_RANGE="${LATEST_TAG}..HEAD" - fi - - COMMIT_COUNT="$(git rev-list --count "${REVISION_RANGE}")" - - if [[ "${COMMIT_COUNT}" == "0" ]]; then - has_changes=false - exit 0 - fi - - # Default to a patch bump for any change. Conventional commits can then - # raise the bump to minor or major. - BUMP_LEVEL=1 - RELEASE_TYPE="patch" - - while IFS= read -r COMMIT_HASH; do - SUBJECT="$(git show -s --format=%s "${COMMIT_HASH}")" - BODY="$(git show -s --format=%b "${COMMIT_HASH}")" - - if printf '%s\n%s' "${SUBJECT}" "${BODY}" | grep -qiE '(^[a-z]+(\([^)]+\))?!:)|(^|[[:space:]])BREAKING CHANGE:'; then - BUMP_LEVEL=3 - RELEASE_TYPE="major" - break - fi - - if [[ "${BUMP_LEVEL}" -lt 2 ]] && printf '%s\n' "${SUBJECT}" | grep -qE '^feat(\([^)]+\))?:'; then - BUMP_LEVEL=2 - RELEASE_TYPE="minor" - fi - done < <(git rev-list --reverse "${REVISION_RANGE}") - - IFS='.' read -r MAJOR MINOR PATCH <<< "${BASE_VERSION}" - - case "${BUMP_LEVEL}" in - 3) - MAJOR=$((MAJOR + 1)) - MINOR=0 - PATCH=0 - ;; - 2) - MINOR=$((MINOR + 1)) - PATCH=0 - ;; - *) - PATCH=$((PATCH + 1)) - ;; - esac - - NEXT_VERSION="${MAJOR}.${MINOR}.${PATCH}" - NEXT_TAG="v${NEXT_VERSION}" - CHANGELOG="$(git log --reverse --format='* %s (%h)' "${REVISION_RANGE}")" - - has_changes=true - RELEASE_NOTES="## Release summary\n" - RELEASE_NOTES+=$'\n' - RELEASE_NOTES+="- Previous release: ${LATEST_TAG}\n" - RELEASE_NOTES+="- Version bump: ${RELEASE_TYPE}\n" - RELEASE_NOTES+="- Included commits: ${COMMIT_COUNT}\n" - RELEASE_NOTES+=$'\n' - RELEASE_NOTES+="## Changes\n" - RELEASE_NOTES+=$'\n' - RELEASE_NOTES+="${CHANGELOG}\n" -} - -create_and_publish() { - local next_tag="${1:-}" - - if [[ -z "${next_tag}" ]]; then - echo "Error: next_tag is required." - usage - exit 1 - fi - - if [[ -z "${GH_TOKEN:-}" ]]; then - echo "Error: GH_TOKEN is required to create a GitHub release." - exit 1 - fi - - if [[ -z "${RELEASE_NOTES:-}" ]]; then - echo "Error: RELEASE_NOTES is required." - exit 1 - fi - - fetch_tags - - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - if git rev-parse -q --verify "refs/tags/${next_tag}" >/dev/null; then - echo "Error: tag ${next_tag} already exists." - exit 1 - fi - - git tag -a "${next_tag}" -m "chore(release): ${next_tag}" - git push origin "${next_tag}" - - local notes_file - notes_file="$(mktemp)" - trap 'rm -f "${notes_file}"' EXIT - - printf '%s\n' "${RELEASE_NOTES}" > "${notes_file}" - - local -a gh_args - gh_args=(release create "${next_tag}" --title "${next_tag}" --notes-file "${notes_file}") - - # remove when everything works as expected - gh_args+=(--draft) - - if [[ "${RELEASE_LATEST:-true}" == "true" ]]; then - gh_args+=(--latest) - fi - - gh "${gh_args[@]}" -} - -GH_TOKEN="${1:-}" -if [[ -z "${GH_TOKEN}" ]]; then - echo "Error: GH_TOKEN is required to run the release process." - usage - exit 1 -fi - -export GH_TOKEN -run_release