diff --git a/.github/workflows/build-libraries.yaml b/.github/workflows/build-libraries.yaml index bb8894f83b..3b4802751b 100644 --- a/.github/workflows/build-libraries.yaml +++ b/.github/workflows/build-libraries.yaml @@ -126,8 +126,10 @@ jobs: uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: - draft: true + draft: ${{ !endsWith(github.ref_name, '-latest') }} + prerelease: ${{ endsWith(github.ref_name, '-latest') }} fail_on_unmatched_files: true + overwrite_files: ${{ endsWith(github.ref_name, '-latest') }} files: | build/libs/atomvmlib-${{ github.ref_name }}.avm build/libs/atomvmlib-${{ github.ref_name }}.avm.sha256 diff --git a/.github/workflows/build-linux-artifacts.yaml b/.github/workflows/build-linux-artifacts.yaml index 55a8c76cb8..6ff6667d56 100644 --- a/.github/workflows/build-linux-artifacts.yaml +++ b/.github/workflows/build-linux-artifacts.yaml @@ -253,7 +253,9 @@ jobs: uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: - draft: true + draft: ${{ !endsWith(github.ref_name, '-latest') }} + prerelease: ${{ endsWith(github.ref_name, '-latest') }} fail_on_unmatched_files: true + overwrite_files: ${{ endsWith(github.ref_name, '-latest') }} files: | build/AtomVM* diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 5d5bffdc76..c41321bb2c 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -8,6 +8,8 @@ name: "CodeQL" on: push: + tags-ignore: + - '*-latest' paths-ignore: - 'src/platforms/emscripten/**' - 'src/platforms/esp32/**' diff --git a/.github/workflows/esp32-build.yaml b/.github/workflows/esp32-build.yaml index 253d0e5e99..80e31e2b26 100644 --- a/.github/workflows/esp32-build.yaml +++ b/.github/workflows/esp32-build.yaml @@ -8,6 +8,8 @@ name: ESP32 Builds on: push: + tags-ignore: + - '*-latest' paths: - '.github/workflows/esp32-build.yaml' - 'CMakeLists.txt' diff --git a/.github/workflows/esp32-mkimage.yaml b/.github/workflows/esp32-mkimage.yaml index 6e6c00832a..f855460e08 100644 --- a/.github/workflows/esp32-mkimage.yaml +++ b/.github/workflows/esp32-mkimage.yaml @@ -188,8 +188,10 @@ jobs: uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: - draft: true + draft: ${{ !endsWith(github.ref_name, '-latest') }} + prerelease: ${{ endsWith(github.ref_name, '-latest') }} fail_on_unmatched_files: true + overwrite_files: ${{ endsWith(github.ref_name, '-latest') }} files: | src/platforms/esp32/build/AtomVM-${{ matrix.soc }}${{ matrix.flavor }}-${{ github.ref_name }}.img src/platforms/esp32/build/AtomVM-${{ matrix.soc }}${{ matrix.flavor }}-${{ github.ref_name }}.img.sha256 diff --git a/.github/workflows/esp32-simtest.yaml b/.github/workflows/esp32-simtest.yaml index 6fab76fe75..fc9276dbc7 100644 --- a/.github/workflows/esp32-simtest.yaml +++ b/.github/workflows/esp32-simtest.yaml @@ -9,6 +9,8 @@ name: ESP32 Sim test on: push: + tags-ignore: + - '*-latest' paths: - ".github/workflows/esp32-simtest.yaml" - "CMakeLists.txt" diff --git a/.github/workflows/nightly-release-tags.yaml b/.github/workflows/nightly-release-tags.yaml new file mode 100644 index 0000000000..18e420ad5e --- /dev/null +++ b/.github/workflows/nightly-release-tags.yaml @@ -0,0 +1,116 @@ +# +# Copyright 2026 AtomVM Contributors +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +# +# Maintainer notes: +# - Requires RELEASE_TAG_PUSH_TOKEN (a fine-grained PAT). +# - GitHub: Settings -> Developer settings -> Personal access tokens -> Fine-grained tokens. +# - Grant it Contents: Read and write on atomvm/AtomVM. +# - Add it with: gh secret set RELEASE_TAG_PUSH_TOKEN --repo atomvm/AtomVM +# - Manual run: gh workflow run nightly-release-tags.yaml --repo atomvm/AtomVM --ref main +# - Only if cleanup is needed, delete the stale release/tag and rerun: +# gh release delete main-latest --repo atomvm/AtomVM --yes +# Only for a complete cleanup, also delete the tag: +# gh api -X DELETE repos/atomvm/AtomVM/git/refs/tags/main-latest + +name: Nightly Release Tags + +on: + schedule: + # Avoid the top of the hour when GitHub Actions is most congested. + - cron: "17 2 * * *" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +jobs: + update-nightly-tags: + runs-on: ubuntu-24.04 + + strategy: + fail-fast: false + matrix: + include: + # Add one entry per branch that should publish a moving nightly tag. + - branch: main + tag: main-latest + - branch: release-0.7 + tag: release-0.7-latest + + env: + # Use a PAT or GitHub App token that can push tags. + # We intentionally do not use GITHUB_TOKEN here because downstream + # release workflows should run when the nightly tag moves. + TAG_PUSH_TOKEN: ${{ secrets.RELEASE_TAG_PUSH_TOKEN }} + + steps: + # In the official repository we fail fast if the secret is missing, so + # nightly releases do not silently stop updating. + - name: Require nightly tag token in official repository + if: github.repository == 'atomvm/AtomVM' && env.TAG_PUSH_TOKEN == '' + run: | + echo "::error::Missing RELEASE_TAG_PUSH_TOKEN secret. Configure a token that can push tags and trigger downstream workflows." + exit 1 + + # Forks can run this workflow manually without configuring the secret. + # That makes it easier to verify the workflow wiring before enabling + # real nightly tag updates in a test repository. + - name: Skip nightly tag updates without token in forks + if: github.repository != 'atomvm/AtomVM' && env.TAG_PUSH_TOKEN == '' + run: echo "::warning::Skipping nightly tag update because RELEASE_TAG_PUSH_TOKEN is not configured in this fork." + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Check remote branch exists + id: branch + shell: bash + run: | + if git ls-remote --exit-code origin "refs/heads/${{ matrix.branch }}" > /dev/null 2>&1; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "::warning::Skipping missing branch ${{ matrix.branch }}" + fi + + - name: Update nightly tag + if: env.TAG_PUSH_TOKEN != '' && steps.branch.outputs.exists == 'true' + shell: bash + run: | + set -euo pipefail + git fetch origin "${{ matrix.branch }}" --force + git checkout --detach FETCH_HEAD + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + BRANCH_HEAD="$(git rev-parse HEAD)" + EXISTING_TAG="$(git ls-remote --tags origin "refs/tags/${{ matrix.tag }}^{}" | awk '{print $1}')" + # Fall back to lightweight tag if the dereferenced form is missing. + if [ -z "$EXISTING_TAG" ]; then + EXISTING_TAG="$(git ls-remote --tags origin "refs/tags/${{ matrix.tag }}" | awk '{print $1}')" + fi + + if [ "$BRANCH_HEAD" = "$EXISTING_TAG" ]; then + echo "Tag ${{ matrix.tag }} already points at ${BRANCH_HEAD:0:12} – nothing to do." + exit 0 + fi + + git tag -fa "${{ matrix.tag }}" -F - <> $GITHUB_PATH - name: "Git config safe.directory for codeql" + if: ${{ !(startsWith(github.ref, 'refs/tags/') && endsWith(github.ref_name, '-latest')) }} run: git config --global --add safe.directory /__w/AtomVM/AtomVM - name: "Initialize CodeQL" + if: ${{ !(startsWith(github.ref, 'refs/tags/') && endsWith(github.ref_name, '-latest')) }} uses: github/codeql-action/init@v4 with: languages: "cpp" @@ -176,6 +178,7 @@ jobs: cmake --build . --target=AtomVM - name: "Perform CodeQL Analysis" + if: ${{ !(startsWith(github.ref, 'refs/tags/') && endsWith(github.ref_name, '-latest')) }} uses: github/codeql-action/analyze@v4 - name: Install nvm and nodejs 20 @@ -256,8 +259,10 @@ jobs: uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') && matrix.board != 'pico_w' && matrix.board != 'pico2_w' && matrix.platform == '' && matrix.jit == '' with: - draft: true + draft: ${{ !endsWith(github.ref_name, '-latest') }} + prerelease: ${{ endsWith(github.ref_name, '-latest') }} fail_on_unmatched_files: true + overwrite_files: ${{ endsWith(github.ref_name, '-latest') }} files: | src/platforms/rp2/build/src/AtomVM-${{ matrix.board }}-${{ github.ref_name }}.uf2 src/platforms/rp2/build/src/AtomVM-${{ matrix.board }}-${{ github.ref_name }}.uf2.sha256 @@ -270,8 +275,10 @@ jobs: uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') && (matrix.board == 'pico_w' || matrix.board == 'pico2_w') && matrix.platform == '' && matrix.jit == '' with: - draft: true + draft: ${{ !endsWith(github.ref_name, '-latest') }} + prerelease: ${{ endsWith(github.ref_name, '-latest') }} fail_on_unmatched_files: true + overwrite_files: ${{ endsWith(github.ref_name, '-latest') }} files: | src/platforms/rp2/build/src/AtomVM-${{ matrix.board }}-${{ github.ref_name }}.uf2 src/platforms/rp2/build/src/AtomVM-${{ matrix.board }}-${{ github.ref_name }}.uf2.sha256 diff --git a/.github/workflows/publish-docs.yaml b/.github/workflows/publish-docs.yaml index 6438ec9afd..e37803186e 100644 --- a/.github/workflows/publish-docs.yaml +++ b/.github/workflows/publish-docs.yaml @@ -13,7 +13,7 @@ on: # Triggers the workflow on pull request, tag events and pushes on main push: tags: - - '**' + - 'v*' branches: - 'main' - 'release-**' diff --git a/.github/workflows/stm32-build.yaml b/.github/workflows/stm32-build.yaml index 894acd7715..76614366a4 100644 --- a/.github/workflows/stm32-build.yaml +++ b/.github/workflows/stm32-build.yaml @@ -8,6 +8,8 @@ name: STM32 Build on: push: + tags-ignore: + - '*-latest' paths: - '.github/workflows/stm32-build.yaml' - 'CMakeLists.txt' diff --git a/.github/workflows/wasm-build.yaml b/.github/workflows/wasm-build.yaml index a3be9405cb..2b97cf4d81 100644 --- a/.github/workflows/wasm-build.yaml +++ b/.github/workflows/wasm-build.yaml @@ -55,9 +55,11 @@ jobs: run: apt update && apt install -y gperf zlib1g-dev cmake ninja-build - name: "Git config safe.directory for codeql" + if: ${{ !(startsWith(github.ref, 'refs/tags/') && endsWith(github.ref_name, '-latest')) }} run: git config --global --add safe.directory /__w/AtomVM/AtomVM - name: "Initialize CodeQL" + if: ${{ !(startsWith(github.ref, 'refs/tags/') && endsWith(github.ref_name, '-latest')) }} uses: github/codeql-action/init@v4 with: languages: ${{matrix.language}} @@ -74,6 +76,7 @@ jobs: ninja AtomVM atomvmlib atomvmlib-emscripten erlang_test_modules test_etest test_alisp test_estdlib hello_world run_script call_cast html5_events wasm_webserver - name: "Perform CodeQL Analysis" + if: ${{ !(startsWith(github.ref, 'refs/tags/') && endsWith(github.ref_name, '-latest')) }} uses: github/codeql-action/analyze@v4 - name: Upload AtomVM and test modules @@ -162,8 +165,10 @@ jobs: uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: - draft: true + draft: ${{ !endsWith(github.ref_name, '-latest') }} + prerelease: ${{ endsWith(github.ref_name, '-latest') }} fail_on_unmatched_files: true + overwrite_files: ${{ endsWith(github.ref_name, '-latest') }} files: | src/platforms/emscripten/build/src/AtomVM-node-${{ github.ref_name }}.js src/platforms/emscripten/build/src/AtomVM-node-${{ github.ref_name }}.js.sha256 @@ -191,6 +196,7 @@ jobs: run: sudo apt update -y && sudo apt install -y cmake gperf - name: "Initialize CodeQL" + if: ${{ !(startsWith(github.ref, 'refs/tags/') && endsWith(github.ref_name, '-latest')) }} uses: github/codeql-action/init@v3 with: languages: ${{matrix.language}} @@ -208,6 +214,7 @@ jobs: emmake make -j - name: "Perform CodeQL Analysis" + if: ${{ !(startsWith(github.ref, 'refs/tags/') && endsWith(github.ref_name, '-latest')) }} uses: github/codeql-action/analyze@v3 - name: Upload wasm build for web @@ -285,8 +292,10 @@ jobs: uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: - draft: true + draft: ${{ !endsWith(github.ref_name, '-latest') }} + prerelease: ${{ endsWith(github.ref_name, '-latest') }} fail_on_unmatched_files: true + overwrite_files: ${{ endsWith(github.ref_name, '-latest') }} files: | src/platforms/emscripten/build/src/AtomVM-web-${{ github.ref_name }}.js src/platforms/emscripten/build/src/AtomVM-web-${{ github.ref_name }}.js.sha256