diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f76ee6..6ecc75c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,19 @@ on: tags: v*.* pull_request: branches: [ localstack ] + # Callable by weekly-release.yml; publishes a release when `version` is set. + workflow_call: + inputs: + version: + description: "Release version to tag and publish (e.g. v0.2.1). When set, a release is published." + type: string + required: false + default: "" + prerelease: + description: "Publish as a pre-release. Promoted to a full release once localstack-pro validates it." + type: boolean + required: false + default: false jobs: @@ -25,6 +38,8 @@ jobs: build: runs-on: ubuntu-latest needs: test + permissions: + contents: write steps: - uses: actions/checkout@v7 @@ -42,10 +57,17 @@ jobs: with: name: aws-lambda-rie path: bin/* + - name: Push tag + if: inputs.version != '' + run: | + git tag "${{ inputs.version }}" "$GITHUB_SHA" + git push origin "${{ inputs.version }}" + - name: Release binaries uses: softprops/action-gh-release@v3 - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') || inputs.version != '' with: + tag_name: ${{ inputs.version || github.ref_name }} files: bin/* generate_release_notes: true - prerelease: ${{ endsWith(github.ref, '-pre') }} + prerelease: ${{ inputs.prerelease || endsWith(github.ref, '-pre') || endsWith(inputs.version, '-pre') }} diff --git a/.github/workflows/weekly-release.yml b/.github/workflows/weekly-release.yml new file mode 100644 index 0000000..6102e49 --- /dev/null +++ b/.github/workflows/weekly-release.yml @@ -0,0 +1,97 @@ +# Weekly auto-release: patch-bumps and publishes if there are new commits on localstack. +name: Weekly Release + +on: + schedule: + # Fridays 06:00 UTC. The release is a pre-release until localstack-pro validates and promotes it, + # which is what gates the downstream lambda-images bump. + - cron: '0 6 * * 5' + workflow_dispatch: + inputs: + dryRun: + description: "Compute the next version but do not release." + type: boolean + default: false + +permissions: + contents: read + +jobs: + version: + runs-on: ubuntu-latest + outputs: + should_release: ${{ steps.ver.outputs.should_release }} + next: ${{ steps.ver.outputs.next }} + steps: + - uses: actions/checkout@v7 + with: + ref: localstack + fetch-depth: 0 + + - name: Determine next version + id: ver + env: + GH_TOKEN: ${{ github.token }} + run: | + git fetch --tags --force + # Highest released version, including pre-releases still awaiting promotion, so a pending + # promotion cannot make us recompute a version whose tag already exists. The grep keeps + # RC tags (v0.0.0-rc.*) out; sort -V picks the highest version, not the newest. + latest=$(gh release list --exclude-drafts \ + --json tagName -q '.[].tagName' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n1) + if [ -z "$latest" ]; then + echo "::error::No published vX.Y.Z release found to bump from." && exit 1 + fi + count=$(git rev-list "${latest}..HEAD" --count) + ver=${latest#v} + IFS=. read -r major minor patch <<< "$ver" + next="v${major}.${minor}.$((patch + 1))" + should_release=true + if [ "$count" = "0" ]; then + should_release=false + echo "No new commits since $latest; nothing to release." + fi + if [ "${{ inputs.dryRun }}" = "true" ]; then + should_release=false + echo "dryRun requested; not releasing." + fi + { + echo "next=$next" + echo "should_release=$should_release" + } >> "$GITHUB_OUTPUT" + echo "Latest release: $latest | new commits since: $count | next: $next | release: $should_release" + + release: + needs: version + if: needs.version.outputs.should_release == 'true' + permissions: + contents: write + # Reuse build.yml's test -> build -> release path with the computed version. Published as a + # pre-release; localstack-pro promotes it to a full release once its CI validates the version. + uses: ./.github/workflows/build.yml + with: + version: ${{ needs.version.outputs.next }} + prerelease: true + + notify: + name: Report a broken weekly release + if: always() && (needs.version.result == 'failure' || needs.release.result == 'failure') + runs-on: ubuntu-latest + needs: + - version + - release + steps: + - name: "Send Message" + uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 + env: + MESSAGE: "_*Weekly RIE release failed*_ :turtle-headache::broken_heart:\n\nNo new pre-release was published, so CVE remediation is stalled until this is fixed. Investigate the failed workflow run <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|here> :mag_right:" + with: + webhook: ${{ secrets.COSY_WEBHOOK_URL }} + webhook-type: incoming-webhook + payload: | + blocks: + - type: "section" + text: + type: "mrkdwn" + text: "${{ env.MESSAGE }}" diff --git a/README-LOCALSTACK.md b/README-LOCALSTACK.md index 03e893d..6efa417 100644 --- a/README-LOCALSTACK.md +++ b/README-LOCALSTACK.md @@ -69,6 +69,25 @@ previous release (e.g. [`v0.1.47`](https://github.com/localstack/lambda-runtime- > LocalStack releases — it is a manual `workflow_dispatch` that checks out `main` (the upstream > mirror) rather than `localstack`, so it would not include the LocalStack customizations. +### Weekly auto-release + +[`weekly-release.yml`](./.github/workflows/weekly-release.yml) runs every Friday at 06:00 UTC. If +`localstack` has new commits since the highest existing release, it patch-bumps the version and calls +`build.yml` to run the tests, build the binaries, push the tag, and publish the release. Together with +Renovate automerge, this is what carries dependency and CVE fixes downstream without manual work. + +The release is published as a **pre-release**, and only reaches consumers once it has been validated: + +1. `weekly-release.yml` publishes `vX.Y.Z`, marked as a pre-release. +2. localstack-pro opens a PR bumping `LAMBDA_RUNTIME_DEFAULT_VERSION` to that version; its CI is the + quality gate. +3. On merge, localstack-pro flips the same release to a full release through the GitHub API — no new + tag and no rebuild, so the binaries that were validated are the binaries that ship. +4. lambda-images ignores pre-releases, so Renovate only opens a bump PR there after the promotion. + +Run it manually via the **Weekly Release** workflow (`workflow_dispatch`); `dryRun` reports the next +version without releasing. A failed run posts to Slack. + ### RC (release candidate) pre-release RC pre-releases let an **unmerged** PR be tested against localstack-pro CI without cutting a real