mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 10:18:42 +00:00
feat(ci): automate dev beta releases
This commit is contained in:
1 parent
d57d3810e4
commit
9a6fd2dec9
4 files changed
+515
-2
No files matched your search
@@ -0,0 +1,320 @@
|
||||
name: Dev CI and Beta Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: dev-beta-release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
GHCR_IMAGE: ghcr.io/${{ github.repository }}
|
||||
DOCKERHUB_IMAGE: digitop/goclaw
|
||||
INITIAL_VERSION: 3.11.3
|
||||
PRERELEASE_ID: beta
|
||||
|
||||
jobs:
|
||||
go:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
pg:
|
||||
image: pgvector/pgvector:pg18
|
||||
env:
|
||||
POSTGRES_PASSWORD: test
|
||||
POSTGRES_DB: goclaw_test
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd "pg_isready -U postgres"
|
||||
--health-interval 5s
|
||||
--health-timeout 3s
|
||||
--health-retries 10
|
||||
env:
|
||||
TEST_DATABASE_URL: postgres://postgres:test@localhost:5432/goclaw_test?sslmode=disable
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache-dependency-path: go.sum
|
||||
- run: go build ./...
|
||||
- run: go build -tags sqliteonly ./...
|
||||
- run: go vet ./...
|
||||
- name: Unit tests
|
||||
run: go test -race -timeout=5m -coverpkg=./... -coverprofile=coverage.out ./...
|
||||
- name: Invariant tests (P0)
|
||||
run: go test -race -timeout=90s -tags integration ./tests/invariants/...
|
||||
- name: Contract tests (P1)
|
||||
run: go test -race -timeout=90s -tags integration ./tests/contracts/... || echo "::warning::Contract tests skipped (no server configured)"
|
||||
continue-on-error: true
|
||||
- name: Integration tests
|
||||
run: go test -race -timeout=180s -tags integration ./tests/integration/
|
||||
- name: Coverage summary
|
||||
run: go tool cover -func=coverage.out | tail -1
|
||||
|
||||
web:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ui/web
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
cache-dependency-path: ui/web/pnpm-lock.yaml
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm lint
|
||||
- run: pnpm build
|
||||
|
||||
release_preflight:
|
||||
needs: [go, web]
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Validate release secrets
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
run: |
|
||||
test -n "$DOCKERHUB_USERNAME" || { echo "::error::Missing DOCKERHUB_USERNAME secret"; exit 1; }
|
||||
test -n "$DOCKERHUB_TOKEN" || { echo "::error::Missing DOCKERHUB_TOKEN secret"; exit 1; }
|
||||
|
||||
beta_version:
|
||||
needs: [go, web, release_preflight]
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
released: ${{ steps.version.outputs.released }}
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
tag: ${{ steps.version.outputs.tag }}
|
||||
notes_path: ${{ steps.version.outputs.notes_path }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Fetch upstream release tags
|
||||
run: git fetch --force --tags https://github.com/nextlevelbuilder/goclaw.git "refs/tags/v*:refs/tags/v*"
|
||||
- name: Compute semantic beta version
|
||||
id: version
|
||||
run: node scripts/ci/semantic-beta-version.mjs
|
||||
- name: Create or verify beta tag
|
||||
if: steps.version.outputs.released == 'true'
|
||||
env:
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
if ! git rev-parse "$TAG" >/dev/null 2>&1; then
|
||||
git tag -a "$TAG" -m "Release $TAG"
|
||||
git push origin "$TAG"
|
||||
fi
|
||||
- name: Upload release notes
|
||||
if: steps.version.outputs.released == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-notes
|
||||
path: ${{ steps.version.outputs.notes_path }}
|
||||
|
||||
build_binaries:
|
||||
needs: beta_version
|
||||
if: needs.beta_version.outputs.released == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.beta_version.outputs.tag }}
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache-dependency-path: go.sum
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Build web UI
|
||||
run: |
|
||||
corepack enable && corepack prepare pnpm@10.28.2 --activate
|
||||
cd ui/web && pnpm install --frozen-lockfile && pnpm build && cd ../..
|
||||
mkdir -p internal/webui/dist
|
||||
cp -r ui/web/dist/* internal/webui/dist/
|
||||
|
||||
- name: Build binary
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
VERSION: ${{ needs.beta_version.outputs.tag }}
|
||||
run: |
|
||||
CGO_ENABLED=0 go build -tags embedui \
|
||||
-ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION}" \
|
||||
-o goclaw .
|
||||
tar -czf "goclaw-${VERSION}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" goclaw migrations/
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: goclaw-*.tar.gz
|
||||
|
||||
publish_release:
|
||||
needs: [beta_version, build_binaries, promote_beta_aliases]
|
||||
if: needs.beta_version.outputs.released == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: binary-*
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
|
||||
- name: Download release notes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: release-notes
|
||||
path: release-notes
|
||||
|
||||
- name: Publish prerelease
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ needs.beta_version.outputs.tag }}
|
||||
run: |
|
||||
if gh release view "$TAG" >/dev/null 2>&1; then
|
||||
gh release edit "$TAG" \
|
||||
--title "GoClaw $TAG" \
|
||||
--notes-file release-notes/release-notes.md \
|
||||
--prerelease
|
||||
else
|
||||
gh release create "$TAG" \
|
||||
--title "GoClaw $TAG" \
|
||||
--notes-file release-notes/release-notes.md \
|
||||
--prerelease
|
||||
fi
|
||||
gh release upload "$TAG" artifacts/* --clobber
|
||||
|
||||
docker_images:
|
||||
needs: beta_version
|
||||
if: needs.beta_version.outputs.released == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- variant: latest
|
||||
suffix: ""
|
||||
enable_otel: "false"
|
||||
enable_embedui: "true"
|
||||
enable_python: "true"
|
||||
enable_full_skills: "false"
|
||||
- variant: full
|
||||
suffix: "-full"
|
||||
enable_otel: "false"
|
||||
enable_embedui: "true"
|
||||
enable_python: "true"
|
||||
enable_full_skills: "true"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.beta_version.outputs.tag }}
|
||||
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.GHCR_IMAGE }}
|
||||
${{ env.DOCKERHUB_IMAGE }}
|
||||
tags: |
|
||||
type=raw,value=${{ needs.beta_version.outputs.tag }},suffix=${{ matrix.suffix }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
ENABLE_OTEL=${{ matrix.enable_otel }}
|
||||
ENABLE_EMBEDUI=${{ matrix.enable_embedui }}
|
||||
ENABLE_PYTHON=${{ matrix.enable_python }}
|
||||
ENABLE_FULL_SKILLS=${{ matrix.enable_full_skills }}
|
||||
VERSION=${{ needs.beta_version.outputs.tag }}
|
||||
cache-from: type=gha,scope=dev-beta-${{ matrix.variant }}
|
||||
cache-to: type=gha,mode=max,scope=dev-beta-${{ matrix.variant }}
|
||||
|
||||
promote_beta_aliases:
|
||||
needs: [beta_version, docker_images]
|
||||
if: needs.beta_version.outputs.released == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Promote beta aliases
|
||||
env:
|
||||
TAG: ${{ needs.beta_version.outputs.tag }}
|
||||
run: |
|
||||
docker buildx imagetools create -t "${GHCR_IMAGE}:beta" "${GHCR_IMAGE}:${TAG}"
|
||||
docker buildx imagetools create -t "${GHCR_IMAGE}:beta-full" "${GHCR_IMAGE}:${TAG}-full"
|
||||
docker buildx imagetools create -t "${DOCKERHUB_IMAGE}:beta" "${DOCKERHUB_IMAGE}:${TAG}"
|
||||
docker buildx imagetools create -t "${DOCKERHUB_IMAGE}:beta-full" "${DOCKERHUB_IMAGE}:${TAG}-full"
|
||||
@@ -123,6 +123,7 @@ make desktop-dmg VERSION=0.1.0 # Create .dmg installer (macOS only
|
||||
| Workflow | Trigger | Purpose |
|
||||
|----------|---------|---------|
|
||||
| `ci.yaml` | push main, PR→main/dev | Go build+test+vet, Web build |
|
||||
| `dev-beta-release.yaml` | push dev | Go build+test+vet, Web build, semantic beta prerelease, beta Docker |
|
||||
| `release.yaml` | tag `v[0-9]+.[0-9]+.[0-9]+` | Binaries + Docker (4 variants + web) + Discord |
|
||||
| `release-beta.yaml` | tag `v*-beta*` / `v*-rc*` | Beta binaries + Docker + GitHub prerelease |
|
||||
| `release-desktop.yaml` | tag `lite-v*` | Desktop app (macOS+Windows), auto prerelease for `-beta`/`-rc` tags |
|
||||
@@ -136,7 +137,8 @@ git tag v3.0.0 && git push origin v3.0.0
|
||||
|
||||
**Beta release** (from dev):
|
||||
```bash
|
||||
git tag v2.67.0-beta.1 && git push origin v2.67.0-beta.1 # standard beta
|
||||
git push origin dev # auto beta via CI
|
||||
git tag v2.67.0-beta.1 && git push origin v2.67.0-beta.1 # manual/backfill beta
|
||||
git tag lite-v1.2.0-beta.1 && git push origin lite-v1.2.0-beta.1 # lite beta
|
||||
```
|
||||
|
||||
@@ -163,9 +165,10 @@ OTel and Tailscale variants are not pre-built — build from source with the app
|
||||
### Tag Pattern Safety
|
||||
|
||||
- `release.yaml`: tag-triggered (`v[0-9]+.[0-9]+.[0-9]+`) — clean semver only, no beta/rc
|
||||
- `dev-beta-release.yaml`: branch-triggered on `dev`; creates `vX.Y.Z-beta.N` tags after CI passes
|
||||
- `release-beta.yaml`: tag-triggered (`v*-beta*`, `v*-rc*`) — never matches clean semver
|
||||
- `release-desktop.yaml`: tag-triggered (`lite-v*`) — `lite-` prefix prevents overlap
|
||||
- No workflow triggers overlap — each tag pattern is distinct. Merging to `main` only triggers CI, not release
|
||||
- Stable and desktop tag patterns remain distinct. `dev` branch pushes create beta releases only after CI passes
|
||||
|
||||
## Desktop Edition (Lite)
|
||||
|
||||
|
||||
@@ -6,6 +6,16 @@ Significant changes, features, and fixes in reverse chronological order.
|
||||
|
||||
## 2026-05-17
|
||||
|
||||
### CI/CD: dev branch beta automation
|
||||
|
||||
**Features**
|
||||
|
||||
- Added a `Dev CI and Beta Release` GitHub Actions workflow for `dev` pushes that runs Go and Web UI checks before publishing a beta prerelease.
|
||||
- Added semantic-release-style beta version calculation from Conventional Commits, creating `vX.Y.Z-beta.N` tags and prereleases automatically after tests pass.
|
||||
- Beta automation uploads Linux binaries and publishes `beta` Docker image tags for the same release version.
|
||||
|
||||
---
|
||||
|
||||
### Agent Permissions: channel and workspace matrix
|
||||
|
||||
**Features**
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env node
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { writeFileSync, appendFileSync } from "node:fs";
|
||||
|
||||
const prerelease = process.env.PRERELEASE_ID || "beta";
|
||||
const initialVersion = process.env.INITIAL_VERSION || "0.1.0";
|
||||
|
||||
function git(args) {
|
||||
return execFileSync("git", args, { encoding: "utf8" }).trim();
|
||||
}
|
||||
|
||||
function setOutput(name, value) {
|
||||
const output = process.env.GITHUB_OUTPUT;
|
||||
if (output) {
|
||||
appendFileSync(output, `${name}=${value}\n`);
|
||||
return;
|
||||
}
|
||||
console.log(`${name}=${value}`);
|
||||
}
|
||||
|
||||
function parseVersion(value) {
|
||||
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+)\.(\d+))?$/.exec(value);
|
||||
if (!match) return null;
|
||||
return {
|
||||
raw: value,
|
||||
major: Number(match[1]),
|
||||
minor: Number(match[2]),
|
||||
patch: Number(match[3]),
|
||||
preid: match[4] || "",
|
||||
prenumber: match[5] ? Number(match[5]) : 0,
|
||||
};
|
||||
}
|
||||
|
||||
function tagCommit(tag) {
|
||||
return git(["rev-list", "-n", "1", tag]);
|
||||
}
|
||||
|
||||
function compareBase(a, b) {
|
||||
return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
|
||||
}
|
||||
|
||||
function compareVersion(a, b) {
|
||||
const base = compareBase(a, b);
|
||||
if (base !== 0) return base;
|
||||
if (!a.preid && b.preid) return 1;
|
||||
if (a.preid && !b.preid) return -1;
|
||||
if (a.preid !== b.preid) return a.preid.localeCompare(b.preid);
|
||||
return a.prenumber - b.prenumber;
|
||||
}
|
||||
|
||||
function bump(base, level) {
|
||||
if (level === "major") return { major: base.major + 1, minor: 0, patch: 0 };
|
||||
if (level === "minor") return { major: base.major, minor: base.minor + 1, patch: 0 };
|
||||
return { major: base.major, minor: base.minor, patch: base.patch + 1 };
|
||||
}
|
||||
|
||||
function versionText(version) {
|
||||
return `${version.major}.${version.minor}.${version.patch}`;
|
||||
}
|
||||
|
||||
function commitLevel(message) {
|
||||
const header = message.split(/\r?\n/, 1)[0] || "";
|
||||
if (/^[a-zA-Z]+(?:\([^)]+\))?!:/.test(header) || /\nBREAKING[ -]CHANGE:/.test(message)) {
|
||||
return "major";
|
||||
}
|
||||
if (/^feat(?:\([^)]+\))?:/.test(header)) return "minor";
|
||||
if (/^(fix|perf)(?:\([^)]+\))?:/.test(header) || /^revert:/.test(header)) return "patch";
|
||||
return "";
|
||||
}
|
||||
|
||||
function maxBase(a, b) {
|
||||
return compareBase(a, b) >= 0 ? a : b;
|
||||
}
|
||||
|
||||
function writeNoRelease(reason) {
|
||||
setOutput("released", "false");
|
||||
setOutput("version", "");
|
||||
setOutput("tag", "");
|
||||
console.log(reason);
|
||||
}
|
||||
|
||||
const tags = git(["tag", "--merged", "HEAD", "--list", "v[0-9]*"])
|
||||
.split(/\r?\n/)
|
||||
.filter(Boolean)
|
||||
.map(parseVersion)
|
||||
.filter(Boolean)
|
||||
.map((tag) => ({ ...tag, commit: tagCommit(tag.raw) }))
|
||||
.sort(compareVersion)
|
||||
.reverse();
|
||||
|
||||
const latest = tags[0];
|
||||
const latestStable = tags.find((tag) => !tag.preid);
|
||||
const latestPrerelease = tags.find((tag) => tag.preid === prerelease);
|
||||
const head = git(["rev-parse", "HEAD"]);
|
||||
const previousTag = tags.find((tag) => tag.raw !== latest?.raw);
|
||||
const repairTag = latest?.preid === prerelease && latest.commit === head ? latest : null;
|
||||
const range = latest ? [`${latest.raw}..HEAD`] : [];
|
||||
const logRange = repairTag && previousTag ? [`${previousTag.raw}..HEAD`] : range;
|
||||
const log = git(["log", "--format=%B%x1e", ...logRange]);
|
||||
const messages = log.split("\x1e").map((message) => message.trim()).filter(Boolean);
|
||||
|
||||
if (latest?.preid && latest.preid !== prerelease) {
|
||||
writeNoRelease(`Latest prerelease tag ${latest.raw} is not a ${prerelease} release; skipping ${prerelease} automation.`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (repairTag) {
|
||||
const version = repairTag.raw.replace(/^v/, "");
|
||||
const releaseNotes = [
|
||||
`## ${repairTag.raw}`,
|
||||
"",
|
||||
`Automated ${prerelease} release from ${head}.`,
|
||||
"",
|
||||
"### Changes",
|
||||
"",
|
||||
...(messages.length ? messages.map((message) => `- ${message.split(/\r?\n/, 1)[0]}`) : ["- Repair release assets for this tag."]),
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
writeFileSync("release-notes.md", releaseNotes);
|
||||
setOutput("released", "true");
|
||||
setOutput("version", version);
|
||||
setOutput("tag", repairTag.raw);
|
||||
setOutput("notes_path", "release-notes.md");
|
||||
console.log(`Repairing ${prerelease} release: ${repairTag.raw}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
let level = "";
|
||||
for (const message of messages) {
|
||||
const next = commitLevel(message);
|
||||
if (next === "major") {
|
||||
level = "major";
|
||||
break;
|
||||
}
|
||||
if (next === "minor" && level !== "major") level = "minor";
|
||||
if (next === "patch" && !level) level = "patch";
|
||||
}
|
||||
|
||||
if (!level) {
|
||||
writeNoRelease("No release-worthy conventional commits found since the last release tag.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const zero = { major: 0, minor: 0, patch: 0 };
|
||||
const initial = parseVersion(initialVersion) || { major: 0, minor: 1, patch: 0 };
|
||||
const stableBase = latestStable || initial || zero;
|
||||
const bumpedBase = bump(stableBase, level);
|
||||
const prereleaseBase = latestPrerelease || zero;
|
||||
let targetBase = maxBase(bumpedBase, prereleaseBase);
|
||||
if (!latest && compareBase(targetBase, initial) < 0) targetBase = initial;
|
||||
|
||||
const nextNumber = compareBase(targetBase, prereleaseBase) === 0
|
||||
? latestPrerelease.prenumber + 1
|
||||
: 1;
|
||||
const version = `${versionText(targetBase)}-${prerelease}.${nextNumber}`;
|
||||
const tag = `v${version}`;
|
||||
|
||||
if (tags.some((existing) => existing.raw === tag)) {
|
||||
writeNoRelease(`Computed tag ${tag} already exists.`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const releaseNotes = [
|
||||
`## ${tag}`,
|
||||
"",
|
||||
`Automated ${prerelease} release from ${process.env.GITHUB_SHA || git(["rev-parse", "HEAD"])}.`,
|
||||
"",
|
||||
"### Changes",
|
||||
"",
|
||||
...messages.map((message) => `- ${message.split(/\r?\n/, 1)[0]}`),
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
writeFileSync("release-notes.md", releaseNotes);
|
||||
setOutput("released", "true");
|
||||
setOutput("version", version);
|
||||
setOutput("tag", tag);
|
||||
setOutput("notes_path", "release-notes.md");
|
||||
console.log(`Next ${prerelease} release: ${tag}`);
|
||||
Reference in new issue
Block a user