Import upstream v0.16.22, stripped

Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f
Enterprise-only files removed or emptied: 63
Enterprise-only snippets removed: 117 in 50 files
Dangling module declarations removed: 5
Cargo edits turning enterprise off: 14
Verification: clean
Enterprise feature gates left for rebuilt features: 19 in 18 files

Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
2026-09-18 10:21:56 -07:00
commit 7dae9b29fd
1650 changed files with 485521 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
name: Auto-close untriaged issues
on:
issues:
types: [opened, reopened]
permissions:
issues: write
jobs:
auto-close:
runs-on: ubuntu-latest
steps:
- name: Close issues from non-allowed authors
uses: actions/github-script@v7
with:
script: |
// Users allowed to open issues directly. All other authors will have
// their issues auto-closed. Add GitHub usernames (lowercase) here to
// grant additional contributors permission to open issues.
const allowedAuthors = [
'mdecimus',
];
const issue = context.payload.issue;
const author = (issue.user && issue.user.login) || '';
if (allowedAuthors.includes(author.toLowerCase())) {
core.info(`Issue #${issue.number} opened by allowed author '${author}'. Skipping.`);
return;
}
const comment = [
`Hi @${author}, thanks for taking the time to file this report.`,
``,
`This issue is being **automatically closed** because all bug reports must first be triaged at our support portal: **[support.stalw.art](https://support.stalw.art)**. Please re-post this report there so that a maintainer can review it; once confirmed as a bug, an Issue will be created on your behalf.`,
``,
`You can sign in to support.stalw.art with your existing GitHub account, so no separate registration is required.`,
``,
`Thank you for understanding.`,
].join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: comment,
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed',
state_reason: 'not_planned',
});
try {
await github.rest.issues.lock({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
lock_reason: 'off-topic',
});
} catch (err) {
core.warning(`Could not lock issue #${issue.number}: ${err.message}`);
}
+131
View File
@@ -0,0 +1,131 @@
name: Auto-close PRs from non-allowed authors
on:
pull_request_target:
types: [opened, reopened]
permissions:
pull-requests: write
issues: write
jobs:
auto-close:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
sparse-checkout: .github/allowed-pr-authors.txt
sparse-checkout-cone-mode: false
- name: Close PRs from non-allowed authors
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
let allowedAuthors = [];
try {
allowedAuthors = fs.readFileSync('.github/allowed-pr-authors.txt', 'utf8')
.split('\n')
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'))
.map(line => line.toLowerCase());
} catch (err) {
core.warning(`Could not read allowed-pr-authors.txt: ${err.message}`);
}
const pr = context.payload.pull_request;
const author = (pr.user && pr.user.login) || '';
const login = author.toLowerCase();
if (author.endsWith('[bot]')) {
core.info(`PR #${pr.number} opened by bot '${author}'. Skipping.`);
return;
}
if (allowedAuthors.includes(login)) {
core.info(`PR #${pr.number} opened by allowed author '${author}'. Skipping.`);
return;
}
const actor = (context.payload.sender && context.payload.sender.login) || '';
const isCollaborator = async (username) => {
if (!username) {
return false;
}
try {
const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username,
});
return perm.permission === 'admin' || perm.permission === 'write';
} catch (err) {
core.info(`Could not resolve collaborator permission for '${username}': ${err.message}`);
return false;
}
};
if (await isCollaborator(author)) {
core.info(`PR #${pr.number} author '${author}' is a collaborator. Skipping.`);
return;
}
if (actor.toLowerCase() !== login && await isCollaborator(actor)) {
core.info(`PR #${pr.number} action triggered by collaborator '${actor}'. Skipping.`);
return;
}
const contributingUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/HEAD/CONTRIBUTING.md`;
const haystack = `${pr.title || ''}\n${pr.body || ''}`;
const aiPatterns = [
/[—―]/,
];
const looksAiGenerated = aiPatterns.some(re => re.test(haystack));
const aiMessage = [
`Hi @${author}, thanks for your interest in contributing.`,
``,
`This pull request is being **automatically closed and locked**. The description contains strong indicators of AI-generated content, and this project does not accept AI-generated code or unsolicited machine-authored contributions.`,
``,
`Please read [CONTRIBUTING.md](${contributingUrl}) to learn what kinds of contributions are currently accepted. If this is a genuine hand-written change that fits those guidelines, please open a discussion at **[support.stalw.art](https://support.stalw.art)** before submitting.`,
].join('\n');
const standardMessage = [
`Hi @${author}, thanks for taking the time to open this pull request.`,
``,
`This PR is being **automatically closed** because it was submitted by an author who is not on the list of approved contributors. This policy helps us keep review capacity focused and filter out unsolicited or low-quality contributions.`,
``,
`Please read [CONTRIBUTING.md](${contributingUrl}) to learn what kinds of contributions are currently accepted. If your change fits those guidelines, please first discuss it at our support portal: **[support.stalw.art](https://support.stalw.art)**. You can sign in with your existing GitHub account.`,
``,
`Thank you for understanding.`,
].join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: looksAiGenerated ? aiMessage : standardMessage,
});
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
state: 'closed',
});
if (looksAiGenerated) {
try {
await github.rest.issues.lock({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
lock_reason: 'spam',
});
} catch (err) {
core.warning(`Could not lock PR #${pr.number}: ${err.message}`);
}
}
@@ -0,0 +1,47 @@
name: Redirect new discussions to the support portal
on:
discussion:
types: [created]
permissions:
discussions: write
jobs:
redirect:
runs-on: ubuntu-latest
steps:
- name: Post support portal redirect
uses: actions/github-script@v7
with:
script: |
const discussion = context.payload.discussion;
const author = (discussion.user && discussion.user.login) || '';
const body = [
`Hi @${author}, thanks for posting!`,
``,
`Stalwart support has moved to **[support.stalw.art](https://support.stalw.art)**. The support portal is now the canonical place to ask questions, request help, and report issues for triage. Other community members may still reply here, but the maintainers no longer answer support questions through GitHub Discussions, so your question may go unanswered unless you also post it on the portal.`,
``,
`You can sign in to support.stalw.art with your existing GitHub account, so no separate registration is required. Google, Discord, LinkedIn, and email/password sign-in are also available.`,
``,
`**Why we are unifying our support channels**`,
``,
`Until now, Stalwart support has been spread across GitHub Discussions, Discord, Matrix, and Reddit. As the project has grown, tracking parallel inboxes and deduplicating threads has become unsustainable; the result has been slower answers, repeated work for the people helping out, and good information buried in chat scrollback where the next person with the same question would never find it.`,
``,
`[support.stalw.art](https://support.stalw.art) is a Discourse instance that we operate ourselves, hosted at Hetzner in Germany and GDPR-compliant.`,
``,
`Thank you for helping us keep the conversation in one place.`,
].join('\n');
await github.graphql(
`mutation($discussionId: ID!, $body: String!) {
addDiscussionComment(input: { discussionId: $discussionId, body: $body }) {
comment { id }
}
}`,
{
discussionId: discussion.node_id,
body,
}
);
+29
View File
@@ -0,0 +1,29 @@
name: "CI retry"
on:
workflow_run:
workflows: ["CI"]
types: [completed]
permissions:
actions: write
jobs:
rerun:
name: Re-run failed jobs
if: >
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.run_attempt < 3
runs-on: ubuntu-latest
steps:
- name: Re-run failed jobs
env:
GH_TOKEN: ${{ secrets.CI_RETRY_TOKEN || github.token }}
GH_REPO: ${{ github.repository }}
RUN_ID: ${{ github.event.workflow_run.id }}
RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
run: |
set -eu
echo "Run $RUN_ID failed on attempt $RUN_ATTEMPT, re-running failed jobs"
sleep 60
gh run rerun "$RUN_ID" --failed
+567
View File
@@ -0,0 +1,567 @@
name: "CI"
on:
workflow_dispatch:
inputs:
Docker:
required: false
default: false
type: boolean
Release:
required: false
default: false
type: boolean
push:
tags: ["v*.*.*"]
env:
SCCACHE_GHA_ENABLED: true
RUSTC_WRAPPER: sccache
CARGO_TERM_COLOR: always
CARGO_NET_RETRY: 10
CARGO_NET_GIT_FETCH_WITH_CLI: true
AWS_LC_SYS_PREBUILT_NASM: 1
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
multiarch:
strategy:
fail-fast: false
matrix:
include:
- variant: gnu
- variant: musl
name: Merge image / ${{matrix.variant}}
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
attestations: write
packages: write
needs: [linux]
if: github.event_name == 'push' || inputs.Docker
steps:
- name: Install Cosign
uses: sigstore/[email protected]
- name: Log In to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{github.repository_owner}}
password: ${{github.token}}
- name: Log In to DockerHub
uses: docker/login-action@v4
with:
username: ${{secrets.DOCKERHUB_USERNAME}}
password: ${{secrets.DOCKERHUB_TOKEN}}
- name: Download ${{matrix.variant}} meta bake definition
uses: actions/download-artifact@v8
with:
name: bake-meta-${{matrix.variant}}
path: ${{ runner.temp }}/${{matrix.variant}}
- name: Download ${{matrix.variant}} digests
uses: actions/download-artifact@v8
with:
path: ${{ runner.temp }}/${{matrix.variant}}/digests
pattern: digests-${{matrix.variant}}-*
merge-multiple: true
- name: Create ${{matrix.variant}} manifest list and push
working-directory: ${{ runner.temp }}/${{matrix.variant}}/digests
run: |
docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("ghcr.io/${{github.repository}}")) | "-t " + .) | join(" ")' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json) \
$(printf 'ghcr.io/${{github.repository}}@sha256:%s ' *)
docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("index.docker.io/${{github.repository}}")) | "-t " + .) | join(" ")' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json) \
$(printf 'index.docker.io/${{github.repository}}@sha256:%s ' *)
- name: Inspect ${{matrix.variant}} image
id: manifest-digest
run: |
docker buildx imagetools inspect --format '{{json .Manifest}}' ghcr.io/${{github.repository}}:$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json) | jq -r '.digest' > GHCR_DIGEST_SHA
echo "GHCR_DIGEST_SHA=$(cat GHCR_DIGEST_SHA)" | tee -a "${GITHUB_ENV}"
docker buildx imagetools inspect --format '{{json .Manifest}}' index.docker.io/${{github.repository}}:$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json) | jq -r '.digest' > DOCKERHUB_DIGEST_SHA
echo "DOCKERHUB_DIGEST_SHA=$(cat DOCKERHUB_DIGEST_SHA)" | tee -a "${GITHUB_ENV}"
cosign sign --yes $(jq --arg GHCR_DIGEST_SHA "$(cat GHCR_DIGEST_SHA)" -cr '.target."docker-metadata-action".tags | map(select(startswith("ghcr.io/${{github.repository}}")) | . + "@" + $GHCR_DIGEST_SHA) | join(" ")' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json)
cosign sign --yes $(jq --arg DOCKERHUB_DIGEST_SHA "$(cat DOCKERHUB_DIGEST_SHA)" -cr '.target."docker-metadata-action".tags | map(select(startswith("index.docker.io/${{github.repository}}")) | . + "@" + $DOCKERHUB_DIGEST_SHA) | join(" ")' ${{ runner.temp }}/${{matrix.variant}}/bake-meta.json)
- name: Attest GHCR
uses: actions/attest-build-provenance@v4
with:
subject-name: ghcr.io/${{github.repository}}
subject-digest: ${{ env.GHCR_DIGEST_SHA }}
push-to-registry: true
- name: Attest Dockerhub
uses: actions/attest-build-provenance@v4
with:
subject-name: index.docker.io/${{github.repository}}
subject-digest: ${{ env.DOCKERHUB_DIGEST_SHA }}
push-to-registry: true
linux:
permissions:
id-token: write
contents: write
attestations: write
packages: write
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
platform: linux/amd64
suffix: ""
build_env: ""
- target: x86_64-unknown-linux-musl
platform: linux/amd64
suffix: "-alpine"
build_env: ""
- target: aarch64-unknown-linux-gnu
platform: linux/arm64
suffix: ""
build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16 "
- target: aarch64-unknown-linux-musl
platform: linux/arm64
suffix: "-alpine"
build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16 "
- target: armv7-unknown-linux-gnueabihf
platform: linux/arm/v7
suffix: ""
build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16 "
- target: armv7-unknown-linux-musleabihf
platform: linux/arm/v7
suffix: "-alpine"
build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16 "
- target: arm-unknown-linux-gnueabihf
platform: linux/arm/v6
suffix: ""
build_env: ""
- target: arm-unknown-linux-musleabihf
platform: linux/arm/v6
suffix: "-alpine"
build_env: ""
name: Build / ${{matrix.target}}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Free disk space (heavy ARM targets)
if: contains(matrix.target, 'arm') || contains(matrix.target, 'aarch64')
run: |
df -h /mnt /
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/.ghcup /usr/local/share/powershell /usr/share/swift /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force || true
df -h /mnt /
- name: Add swap (heavy ARM targets)
if: contains(matrix.target, 'arm') || contains(matrix.target, 'aarch64')
run: |
mnt_avail=$(df --output=avail -k /mnt | tail -1)
if [ "$mnt_avail" -lt 18874368 ]; then
echo "Insufficient space on /mnt (${mnt_avail}K available), aborting swap setup"
exit 1
fi
sudo fallocate -l 16G /mnt/swapfile
sudo chmod 600 /mnt/swapfile
sudo mkswap /mnt/swapfile
sudo swapon /mnt/swapfile
sudo sysctl vm.swappiness=80
free -h
swapon --show
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
with:
platforms: "arm64,arm"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
buildkitd-config-inline: |
[registry."docker.io"]
mirrors = ["https://mirror.gcr.io"]
driver-opts: |
network=host
- name: Log In to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{github.repository_owner}}
password: ${{github.token}}
- name: Log In to DockerHub
uses: docker/login-action@v4
with:
username: ${{secrets.DOCKERHUB_USERNAME}}
password: ${{secrets.DOCKERHUB_TOKEN}}
- name: Calculate shasum of external deps
id: cal-dep-shasum
run: |
echo "checksum=$(yq -p toml -oy '.package[] | select((.source | contains("")) or (.checksum | contains("")))' Cargo.lock | sha256sum | awk '{print $1}')" >> "$GITHUB_OUTPUT"
- name: Cache apt
uses: actions/[email protected]
id: apt-cache
with:
path: |
var-cache-apt
var-lib-apt
key: apt-cache-${{ hashFiles('Dockerfile.build') }}
- name: Cache Cargo
uses: actions/[email protected]
id: cargo-cache
with:
path: |
usr-local-cargo-registry
usr-local-cargo-git
key: cargo-cache-${{ steps.cal-dep-shasum.outputs.checksum }}
- name: Inject cache into docker
uses: reproducible-containers/[email protected]
with:
cache-map: |
{
"var-cache-apt": "/var/cache/apt",
"var-lib-apt": "/var/lib/apt",
"usr-local-cargo-registry": "/usr/local/cargo/registry",
"usr-local-cargo-git": "/usr/local/cargo/git"
}
skip-extraction: ${{ steps.cargo-cache.outputs.cache-hit }} && ${{ steps.apt-cache.outputs.cache-hit }}
- name: Extract Metadata for Docker
uses: docker/metadata-action@v6
id: meta
with:
images: |
index.docker.io/${{github.repository}}
ghcr.io/${{github.repository}}
flavor: |
suffix=${{matrix.suffix}},onlatest=true
tags: |
type=ref,event=tag
type=ref,event=branch,prefix=branch-
type=edge,branch=main
type=semver,pattern=v{{major}}.{{minor}}
- name: Build Artifact
id: bake
uses: docker/bake-action@v7
env:
DOCKER_BUILD_RECORD_UPLOAD: false
TARGET: ${{matrix.target}}
GHCR_REPO: ghcr.io/${{github.repository}}
BUILD_ENV: ${{matrix.build_env}}
DOCKER_PLATFORM: ${{matrix.platform}}
SUFFIX: ${{matrix.suffix}}
with:
source: .
set: |
*.tags=
image.output=type=image,"name=ghcr.io/${{github.repository}},index.docker.io/${{github.repository}}",push-by-digest=true,name-canonical=true,push=true,compression=zstd,compression-level=9,force-compression=true,oci-mediatypes=true
files: |
docker-bake.hcl
${{ steps.meta.outputs.bake-file }}
targets: ${{(github.event_name == 'push' || inputs.Docker) && 'build,image' || 'build'}}
- name: Upload Artifacts
uses: actions/[email protected]
with:
name: artifact-${{matrix.target}}
path: |
artifact
!artifact/*.json
- name: Export digest & Rename meta bake definition file
if: github.event_name == 'push' || inputs.Docker
run: |
mv "${{ steps.meta.outputs.bake-file }}" "${{ runner.temp }}/bake-meta.json"
mkdir -p ${{ runner.temp }}/digests
digest="${{ fromJSON(steps.bake.outputs.metadata).image['containerimage.digest'] }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
if: github.event_name == 'push' || inputs.Docker
uses: actions/[email protected]
with:
name: digests-${{matrix.suffix == '' && 'gnu' || 'musl'}}-${{ matrix.target }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
- name: Upload GNU meta bake definition
uses: actions/[email protected]
if: (github.event_name == 'push' || inputs.Docker) && endsWith(matrix.target,'gnu') && startsWith(matrix.target,'x86')
with:
name: bake-meta-gnu
path: ${{ runner.temp }}/bake-meta.json
if-no-files-found: error
retention-days: 1
- name: Upload musl meta bake definition
uses: actions/[email protected]
if: (github.event_name == 'push' || inputs.Docker) && endsWith(matrix.target,'musl') && startsWith(matrix.target,'x86')
with:
name: bake-meta-musl
path: ${{ runner.temp }}/bake-meta.json
if-no-files-found: error
retention-days: 1
windows:
name: Build / ${{matrix.target}}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
# - target: aarch64-pc-windows-msvc
- target: x86_64-pc-windows-msvc
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Run sccache-cache
uses: mozilla-actions/[email protected]
with:
disable_annotations: true
- name: Build
run: |
rustup target add ${{matrix.target}}
cargo build --release --target ${{matrix.target}} -p stalwart --no-default-features --features "sqlite postgres mysql rocks s3 redis azure nats enterprise"
mkdir -p artifacts
mv ./target/${{matrix.target}}/release/stalwart.exe ./artifacts/stalwart.exe
- name: Upload Artifacts
uses: actions/[email protected]
with:
name: artifact-${{matrix.target}}
path: artifacts
macos:
name: Build / ${{matrix.target}}
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-apple-darwin
- target: x86_64-apple-darwin
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Run sccache-cache
uses: mozilla-actions/[email protected]
with:
disable_annotations: true
#- name: Build FoundationDB Edition
# env:
# GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# run: |
# rustup target add ${{matrix.target}}
# # Pin FoundationDB 7.4.x (Apple publishes these as prereleases)
# curl --retry 5 -Lso foundationdb.pkg "$(gh api -X GET /repos/apple/foundationdb/releases --jq '[.[] | select(.tag_name | startswith("7.4."))] | sort_by(.tag_name | split(".") | map(tonumber)) | reverse | .[0].assets[] | select(.name | test("${{startsWith(matrix.target, 'x86') && 'x86_64' || 'arm64'}}" + ".pkg$")) | .browser_download_url')"
# echo "=== Package contents ==="
# pkgutil --payload-files foundationdb.pkg || true
# sudo installer -allowUntrusted -verbose -dumplog -pkg foundationdb.pkg -target /
# cargo build --release --target ${{matrix.target}} -p stalwart --no-default-features --features "foundationdb s3 redis nats enterprise"
# mkdir -p artifacts
# mv ./target/${{matrix.target}}/release/stalwart ./artifacts/stalwart-foundationdb
- name: Build
run: |
rustup target add ${{matrix.target}}
cargo build --release --target ${{matrix.target}} -p stalwart --no-default-features --features "sqlite postgres mysql rocks s3 redis azure nats enterprise"
mkdir -p artifacts
mv ./target/${{matrix.target}}/release/stalwart ./artifacts/stalwart
- name: Upload Artifacts
uses: actions/[email protected]
with:
name: artifact-${{matrix.target}}
path: artifacts
freebsd:
name: Build / ${{matrix.target}}
runs-on: ubuntu-latest
timeout-minutes: 360
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-freebsd
arch: x86_64
# - target: aarch64-unknown-freebsd
# arch: aarch64
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Build in FreeBSD VM
uses: vmactions/freebsd-vm@v1
with:
release: "15.1"
arch: ${{matrix.arch}}
usesh: true
mem: 14336
cpu: 4
sync: rsync
copyback: true
# gmake: required by jemalloc-sys on BSD hosts
# llvm: provides libclang for bindgen (librocksdb-sys)
# rust: libsqlite3-sys 0.38 uses cfg_select!, stabilized in Rust
# 1.95. The default 'quarterly' pkg repo still ships rust 1.94, so
# switch to the 'latest' repo (currently 1.96.1). rustup is not an
# option here: aarch64-unknown-freebsd has no rustup toolchains yet.
prepare: |
set -e
mkdir -p /usr/local/etc/pkg/repos
echo 'FreeBSD: { url: "pkg+https://pkg.freebsd.org/${ABI}/latest", mirror_type: "srv" }' > /usr/local/etc/pkg/repos/FreeBSD.conf
pkg update -f
env ASSUME_ALWAYS_YES=yes pkg bootstrap -f
pkg update -f
pkg install -y rust gmake llvm rocksdb
rustc --version
run: |
set -e
export CARGO_TARGET_DIR=/tmp/target
export CARGO_TERM_COLOR=always
export CARGO_NET_RETRY=10
cargo build --release -p stalwart --no-default-features --features "sqlite postgres mysql rocks s3 redis azure nats enterprise"
mkdir -p artifacts
cp /tmp/target/release/stalwart artifacts/stalwart
- name: Upload Artifacts
uses: actions/[email protected]
with:
name: artifact-${{matrix.target}}
path: artifacts
release:
name: Release
permissions:
id-token: write
contents: write
attestations: write
if: github.event_name == 'push' || inputs.Release
needs: [linux, windows, macos, freebsd]
runs-on: ubuntu-latest
steps:
# Must run before artifacts are downloaded — checkout cleans the workspace.
- name: Checkout (for CHANGELOG)
if: startsWith(github.ref, 'refs/tags/')
uses: actions/checkout@v7
- name: Download Artifacts
uses: actions/download-artifact@v8
with:
path: archive
pattern: artifact-*
- name: Compress
run: |
set -eux
BASE_DIR="$(pwd)/archive"
compress_files() {
local dir="$1"
local archive_dir_name="${dir#artifact-}"
cd "$dir"
# Process each file in the directory
for file in `ls`; do
filename="${file%.*}"
extension="${file##*.}"
if [ "$extension" = "exe" ]; then
7z a -tzip "${filename}-${archive_dir_name}.zip" "$file" > /dev/null
else
tar -czf "${filename}-${archive_dir_name}.tar.gz" "$file"
fi
done
cd $BASE_DIR
}
cd $BASE_DIR
for arch_dir in `ls`; do
dir_name=$(basename "$arch_dir")
compress_files "$dir_name"
done
- name: Attest binary
id: attest
uses: actions/attest-build-provenance@v4
with:
subject-path: |
archive/**/*.tar.gz
archive/**/*.zip
- name: Use cosign to sign existing artifacts
uses: sigstore/[email protected]
with:
inputs: |
archive/**/*.tar.gz
archive/**/*.zip
- name: Build release body
run: |
if [ "${{ startsWith(github.ref, 'refs/tags/') }}" = "true" ]; then
awk '/^## \[/{c++} c==1' CHANGELOG.md > release_body.md
echo "" >> release_body.md
else
: > release_body.md
fi
cat >> release_body.md <<EOF
<hr />
### Check binary attestation [here](${{ steps.attest.outputs.attestation-url }})
EOF
- name: Release
uses: softprops/action-gh-release@v3
with:
files: |
archive/**/*.tar.gz
archive/**/*.zip
archive/**/*.sigstore.json
prerelease: ${{!startsWith(github.ref, 'refs/tags/') || null}}
tag_name: ${{!startsWith(github.ref, 'refs/tags/') && 'nightly' || null}}
# Tag-push releases are created as drafts; the `publish` job un-drafts
# them only after all build jobs succeed, so watcher notifications
# don't fire on broken builds.
draft: ${{ startsWith(github.ref, 'refs/tags/') || null }}
body_path: release_body.md
publish:
name: Publish release
needs: [linux, windows, macos, freebsd, multiarch, release]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Un-draft release
env:
GH_TOKEN: ${{ github.token }}
run: gh release edit "${{ github.ref_name }}" --draft=false --latest --repo "${{ github.repository }}"
cleanup:
name: Cleanup failed release
needs: [linux, windows, macos, freebsd, multiarch, release]
if: failure() && startsWith(github.ref, 'refs/tags/') && github.run_attempt >= 3
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Delete draft release and tag
env:
GH_TOKEN: ${{ github.token }}
run: gh release delete "${{ github.ref_name }}" --yes --cleanup-tag --repo "${{ github.repository }}" || true
+78
View File
@@ -0,0 +1,78 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '31 6 * * 0'
push:
branches: [ "main" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
# `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4.2.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
# file_mode: git
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/[email protected]
with:
sarif_file: results.sarif
+57
View File
@@ -0,0 +1,57 @@
name: Test
on:
workflow_dispatch:
jobs:
style:
name: Check Style
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Check Style
run: cargo fmt --all --check
test:
name: Test
needs: style
runs-on: ubuntu-latest
env:
STORE: RocksDb
RUST_MIN_STACK: "16777216"
steps:
- name: Checkout
uses: actions/checkout@v7
# External services (OpenLDAP, Keycloak, PostgreSQL, MySQL, Redis, NATS,
# MinIO, OpenSearch, Meilisearch) are provisioned on demand by the test
# suite via testcontainers using the Docker daemon available on the
# runner; see tests/src/utils/containers.rs.
- name: Rust Cache
uses: Swatinem/rust-cache@v2
- name: JMAP Protocol Tests
run: cargo test -p jmap_proto -- --nocapture
- name: IMAP Protocol Tests
run: cargo test -p imap_proto -- --nocapture
- name: Full-text search Tests
run: cargo test -p store -- --nocapture
- name: Directory Tests
run: cargo test -p tests directory -- --nocapture
- name: SMTP Tests
run: cargo test -p tests smtp -- --nocapture
- name: IMAP Tests
run: cargo test -p tests imap -- --nocapture
- name: JMAP Tests
run: cargo test -p tests jmap -- --nocapture
+41
View File
@@ -0,0 +1,41 @@
# trivy ci workflow
name: trivy
on:
workflow_dispatch:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '00 12 * * *'
permissions:
contents: read
jobs:
build:
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
ignore-unfixed: true
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/[email protected]
with:
sarif_file: 'trivy-results.sarif'