Skip to content

Commit 0fdc489

Browse files
authored
feat(repo): add github-action to publish new versions
feat(repo): add github-action to publish new versions
2 parents 4f2ab97 + cf34729 commit 0fdc489

File tree

3 files changed

+508
-0
lines changed

3 files changed

+508
-0
lines changed

.github/workflows/release.yml

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
name: Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
release_date:
7+
description: "Override date (UTC YYYY.MM.DD). Leave empty for today."
8+
required: false
9+
type: string
10+
dry_run:
11+
description: "Do not push/tag/publish (build only)"
12+
required: false
13+
type: boolean
14+
default: false
15+
16+
permissions:
17+
contents: write
18+
packages: write
19+
20+
concurrency:
21+
group: release-${{ github.ref }}
22+
cancel-in-progress: false
23+
24+
env:
25+
CARGO_TERM_COLOR: always
26+
27+
jobs:
28+
validate:
29+
name: Validate (fmt, clippy, tests)
30+
runs-on: ubuntu-latest
31+
steps:
32+
- name: Checkout
33+
uses: actions/checkout@v4
34+
with:
35+
fetch-depth: 0
36+
lfs: true
37+
38+
- name: Rust toolchain
39+
uses: dtolnay/rust-toolchain@stable
40+
41+
- name: Rust cache
42+
uses: Swatinem/rust-cache@v2
43+
44+
- name: Format check
45+
run: cargo fmt --all -- --check
46+
47+
- name: Clippy
48+
run: cargo clippy --workspace --all-targets -- -D warnings
49+
50+
- name: Tests
51+
run: cargo test --workspace -- --nocapture
52+
53+
prepare_version:
54+
name: Prepare version, tag, push
55+
needs: validate
56+
runs-on: ubuntu-latest
57+
outputs:
58+
version: ${{ steps.setver.outputs.version }}
59+
tag: ${{ steps.setver.outputs.tag }}
60+
commit_sha: ${{ steps.commit.outputs.sha }}
61+
steps:
62+
- name: Checkout
63+
uses: actions/checkout@v4
64+
with:
65+
fetch-depth: 0
66+
lfs: true
67+
68+
- name: Configure git
69+
run: |
70+
git config user.name "github-actions[bot]"
71+
git config user.email "github-actions[bot]@users.noreply.github.com"
72+
73+
- name: Rust toolchain
74+
uses: dtolnay/rust-toolchain@stable
75+
76+
- name: Install cargo-workspaces
77+
run: cargo install cargo-workspaces --locked
78+
79+
- name: Compute date-based version
80+
id: setver
81+
shell: bash
82+
run: |
83+
set -euo pipefail
84+
BASE="${{ inputs.release_date }}"
85+
if [[ -z "${BASE}" ]]; then
86+
BASE="$(date -u +%Y.%m.%d)"
87+
fi
88+
git fetch --tags --quiet
89+
LAST=$(git tag -l "v${BASE}*" | sed 's/^v//' | sort -V | tail -n1 || true)
90+
if [[ -z "${LAST}" ]]; then
91+
VERSION="${BASE}"
92+
else
93+
if [[ "${LAST}" == "${BASE}" ]]; then
94+
N=1
95+
else
96+
SUF="${LAST#${BASE}-}"
97+
N=$((10#${SUF} + 1))
98+
fi
99+
VERSION=$(printf "%s-%02d" "${BASE}" "${N}")
100+
fi
101+
echo "version=${VERSION}" | tee -a "$GITHUB_OUTPUT"
102+
echo "tag=v${VERSION}" | tee -a "$GITHUB_OUTPUT"
103+
echo "${VERSION}" > VERSION
104+
105+
- name: Bump workspace versions
106+
if: ${{ !inputs.dry_run }}
107+
shell: bash
108+
run: |
109+
set -euo pipefail
110+
# Update all crate versions and internal dependency requirements
111+
cargo workspaces version ${{ steps.setver.outputs.version }} \
112+
--exact --force --yes --no-git-tag --no-git-commit
113+
# Align lockfile
114+
cargo update -w
115+
116+
- name: Commit and tag
117+
id: commit
118+
if: ${{ !inputs.dry_run }}
119+
shell: bash
120+
run: |
121+
set -euo pipefail
122+
git add -A
123+
git commit -m "[release] v${{ steps.setver.outputs.version }}"
124+
git tag "v${{ steps.setver.outputs.version }}"
125+
git push origin HEAD:${{ github.ref_name }}
126+
git push origin "v${{ steps.setver.outputs.version }}"
127+
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
128+
129+
publish_crates:
130+
name: Publish to crates.io
131+
needs: prepare_version
132+
if: ${{ !inputs.dry_run }}
133+
runs-on: ubuntu-latest
134+
env:
135+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
136+
steps:
137+
- name: Checkout release tag
138+
uses: actions/checkout@v4
139+
with:
140+
fetch-depth: 0
141+
lfs: true
142+
ref: ${{ needs.prepare_version.outputs.tag }}
143+
144+
- name: Rust toolchain
145+
uses: dtolnay/rust-toolchain@stable
146+
147+
- name: Install cargo-workspaces
148+
run: cargo install cargo-workspaces --locked
149+
150+
- name: Publish workspace
151+
run: |
152+
set -euo pipefail
153+
# Publish in dependency order, skipping those already on the registry
154+
cargo workspaces publish --from-git --yes --skip-published
155+
156+
build:
157+
name: Build artifacts (${{ matrix.os_slug }})
158+
needs: prepare_version
159+
runs-on: ${{ matrix.os }}
160+
strategy:
161+
fail-fast: false
162+
matrix:
163+
include:
164+
- os: ubuntu-latest
165+
os_slug: linux
166+
archive: tar.gz
167+
- os: macos-latest
168+
os_slug: macos
169+
archive: tar.gz
170+
- os: windows-latest
171+
os_slug: windows
172+
archive: zip
173+
steps:
174+
- name: Checkout release tag
175+
uses: actions/checkout@v4
176+
with:
177+
fetch-depth: 0
178+
lfs: true
179+
ref: ${{ needs.prepare_version.outputs.tag }}
180+
181+
- name: Rust toolchain
182+
uses: dtolnay/rust-toolchain@stable
183+
with:
184+
profile: minimal
185+
components: clippy,rustfmt
186+
187+
- name: Rust cache
188+
uses: Swatinem/rust-cache@v2
189+
190+
- name: Install jq
191+
if: ${{ runner.os != 'Windows' }}
192+
shell: bash
193+
run: |
194+
if [ "${{ runner.os }}" = "Linux" ]; then
195+
sudo apt-get update && sudo apt-get install -y jq
196+
else
197+
brew update && brew install jq
198+
fi
199+
200+
- name: Install jq (Windows)
201+
if: ${{ runner.os == 'Windows' }}
202+
shell: pwsh
203+
run: choco install jq -y
204+
205+
- name: Build binaries
206+
shell: bash
207+
run: |
208+
set -euo pipefail
209+
cargo build --workspace --release --bins
210+
211+
- name: Stage files
212+
id: stage
213+
shell: bash
214+
run: |
215+
set -euo pipefail
216+
VERSION='${{ needs.prepare_version.outputs.version }}'
217+
OUTDIR="stage/lambda-${VERSION}-${{ matrix.os_slug }}"
218+
mkdir -p "${OUTDIR}/bin"
219+
# List workspace binary targets
220+
bins=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[].targets[] | select(.kind[]=="bin") | .name' | sort -u)
221+
for b in $bins; do
222+
if [[ "${{ runner.os }}" == "Windows" ]]; then
223+
src="target/release/${b}.exe"
224+
else
225+
src="target/release/${b}"
226+
fi
227+
if [[ -f "$src" ]]; then
228+
cp "$src" "${OUTDIR}/bin/"
229+
fi
230+
done
231+
# Include example 'minimal' if present
232+
if [[ -f target/release/examples/minimal ]]; then
233+
mkdir -p "${OUTDIR}/examples"
234+
cp target/release/examples/minimal "${OUTDIR}/examples/"
235+
elif [[ -f target/release/examples/minimal.exe ]]; then
236+
mkdir -p "${OUTDIR}/examples"
237+
cp target/release/examples/minimal.exe "${OUTDIR}/examples/"
238+
fi
239+
# Include assets if present
240+
if [[ -d crates/lambda-rs/assets ]]; then
241+
mkdir -p "${OUTDIR}/assets"
242+
cp -R crates/lambda-rs/assets/* "${OUTDIR}/assets/" || true
243+
fi
244+
# Top-level docs
245+
for f in LICENSE LICENSE.md README.md README; do
246+
[[ -f "$f" ]] && cp "$f" "${OUTDIR}/" || true
247+
done
248+
echo "${VERSION}" > "${OUTDIR}/VERSION"
249+
250+
- name: Package (tar.gz)
251+
if: ${{ matrix.archive == 'tar.gz' }}
252+
shell: bash
253+
run: |
254+
set -euo pipefail
255+
cd stage
256+
tar -czf "lambda-${{ needs.prepare_version.outputs.version }}-${{ matrix.os_slug }}.tar.gz" "lambda-${{ needs.prepare_version.outputs.version }}-${{ matrix.os_slug }}"
257+
if command -v shasum >/dev/null 2>&1; then
258+
shasum -a 256 "lambda-${{ needs.prepare_version.outputs.version }}-${{ matrix.os_slug }}.tar.gz" > "lambda-${{ needs.prepare_version.outputs.version }}-${{ matrix.os_slug }}.tar.gz.sha256"
259+
elif command -v sha256sum >/dev/null 2>&1; then
260+
sha256sum "lambda-${{ needs.prepare_version.outputs.version }}-${{ matrix.os_slug }}.tar.gz" > "lambda-${{ needs.prepare_version.outputs.version }}-${{ matrix.os_slug }}.tar.gz.sha256"
261+
fi
262+
263+
- name: Package (zip)
264+
if: ${{ matrix.archive == 'zip' }}
265+
shell: pwsh
266+
run: |
267+
$v = "${{ needs.prepare_version.outputs.version }}"
268+
$slug = "${{ matrix.os_slug }}"
269+
$src = "stage/lambda-$v-$slug"
270+
$zip = "stage/lambda-$v-$slug.zip"
271+
Compress-Archive -Path "$src\*" -DestinationPath $zip -CompressionLevel Optimal
272+
(Get-FileHash $zip -Algorithm SHA256).Hash + " *$(Split-Path -Leaf $zip)" | Out-File "$zip.sha256" -Encoding ascii
273+
274+
- name: Upload artifacts
275+
uses: actions/upload-artifact@v4
276+
with:
277+
name: release-${{ matrix.os_slug }}
278+
path: stage/*
279+
if-no-files-found: error
280+
281+
release:
282+
name: Create GitHub release
283+
needs: [prepare_version, build, publish_crates]
284+
if: ${{ !inputs.dry_run }}
285+
runs-on: ubuntu-latest
286+
steps:
287+
- name: Checkout release tag
288+
uses: actions/checkout@v4
289+
with:
290+
fetch-depth: 0
291+
lfs: false
292+
ref: ${{ needs.prepare_version.outputs.tag }}
293+
294+
- name: Generate changelog
295+
id: changelog
296+
shell: bash
297+
run: |
298+
set -euo pipefail
299+
TAG='${{ needs.prepare_version.outputs.tag }}'
300+
VERSION='${{ needs.prepare_version.outputs.version }}'
301+
REPO_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}"
302+
git fetch --tags --quiet
303+
PREV=$(git tag -l 'v*' | sort -V | awk -v t="$TAG" '$0==t{print last; exit} {last=$0}')
304+
if [[ -n "${PREV}" ]]; then
305+
RANGE="${PREV}..${TAG}"
306+
COMPARE_URL="${REPO_URL}/compare/${PREV}...${TAG}"
307+
else
308+
RANGE="${TAG}"
309+
COMPARE_URL="${REPO_URL}/commits/${TAG}"
310+
fi
311+
FILE="CHANGELOG-v${VERSION}.md"
312+
{
313+
echo "# Changelog";
314+
echo;
315+
echo "Version ${TAG}";
316+
echo;
317+
if [[ -n "${PREV}" ]]; then
318+
echo "Changes since ${PREV}";
319+
else
320+
echo "Initial release";
321+
fi
322+
echo;
323+
echo "Compare: ${COMPARE_URL}";
324+
echo;
325+
echo "Commits:";
326+
echo;
327+
} > "${FILE}"
328+
git log --no-merges --pretty=format:"- [%h](${REPO_URL}/commit/%H) %s" ${RANGE} >> "${FILE}"
329+
echo "path=${FILE}" >> "$GITHUB_OUTPUT"
330+
331+
- name: Download artifacts
332+
uses: actions/download-artifact@v4
333+
with:
334+
path: dl
335+
merge-multiple: true
336+
337+
- name: Create release and upload assets
338+
uses: softprops/action-gh-release@v2
339+
with:
340+
tag_name: ${{ needs.prepare_version.outputs.tag }}
341+
name: "lambda v${{ needs.prepare_version.outputs.version }}"
342+
draft: false
343+
prerelease: false
344+
body_path: ${{ steps.changelog.outputs.path }}
345+
files: |
346+
dl/*.tar.gz
347+
dl/*.tar.gz.sha256
348+
dl/*.zip
349+
dl/*.zip.sha256
350+
${{ steps.changelog.outputs.path }}

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
</p>
44

55
[![Cross Platform builds & tests](https://github.com/lambda-sh/lambda/actions/workflows/compile_lambda_rs.yml/badge.svg)](https://github.com/lambda-sh/lambda/actions/workflows/compile_lambda_rs.yml)
6+
[![Release](https://github.com/lambda-sh/lambda/actions/workflows/release.yml/badge.svg)](https://github.com/lambda-sh/lambda/actions/workflows/release.yml)
67
![lambda-rs](https://img.shields.io/crates/d/lambda-rs)
78
![lambda-rs](https://img.shields.io/crates/v/lambda-rs)
89

@@ -18,6 +19,7 @@
1819
1. [Getting started](#get_started)
1920
1. [Examples](#examples)
2021
1. [Planned additions](#plans)
22+
1. [Releases & Publishing](#publishing)
2123
1. [How to contribute](#contribute)
2224
1. [Resources](#resources)
2325
## Description <a name="description"></a>
@@ -177,6 +179,13 @@ cargo run --example triangles
177179
- [ ] Unit tests.
178180
- [ ] Nightly builds.
179181

182+
## Releases & Publishing <a name="publishing"></a>
183+
For cutting releases, publishing crates to crates.io, and attaching
184+
multi-platform artifacts to GitHub Releases, see:
185+
186+
- docs/publishing.md
187+
188+
180189
## How to contribute <a name="contribute"></a>
181190
Fork the current repository and then make the changes that you'd like to
182191
said fork. Stable releases will happen within the main branch requiring that

0 commit comments

Comments
 (0)