mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 10:43:13 +00:00
129 lines
4.6 KiB
Bash
Executable file
129 lines
4.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# package-elastic-beanstalk.sh — build a deterministic Elastic Beanstalk source
|
|
# bundle for the shoc-backend .NET 8 application.
|
|
#
|
|
# Layout of the resulting ZIP (the Beanstalk application root):
|
|
# ./ published Api.SeaHavenIndustries (self-contained, linux-x64)
|
|
# ./efbundle self-contained EF Core 8.0.8 migrations bundle (linux-x64, +x)
|
|
# ./.ebextensions/* leader-only migration container command
|
|
#
|
|
# The bundle reads ConnectionStrings__DefaultConnection from the runtime
|
|
# environment (Elastic Beanstalk property). No connection string or secret is
|
|
# written into the package.
|
|
#
|
|
# The script only ever removes its own generated directory (.artifacts/elastic-beanstalk)
|
|
# and validates that path before doing so.
|
|
#
|
|
# Usage:
|
|
# bash scripts/package-elastic-beanstalk.sh [output.zip]
|
|
#
|
|
# Environment:
|
|
# DOTNET_BIN optional path to the dotnet executable
|
|
# RUNTIME optional target RID for local validation (default: linux-x64)
|
|
set -euo pipefail
|
|
|
|
OUTPUT_ZIP="${1:-.artifacts/elastic-beanstalk/site.zip}"
|
|
STAGING_DIR=".artifacts/elastic-beanstalk/staging"
|
|
TOOLS_DIR=".artifacts/dotnet-tools"
|
|
|
|
log() { printf '\n\033[1m== %s ==\033[0m\n' "$1"; }
|
|
die() { printf '\033[31mERR\033[0m %s\n' "$1" >&2; exit 1; }
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
case "$OUTPUT_ZIP" in
|
|
.artifacts/elastic-beanstalk/*.zip) : ;;
|
|
*) die "output must be a .zip beneath .artifacts/elastic-beanstalk" ;;
|
|
esac
|
|
|
|
if [[ -n "${DOTNET_BIN:-}" ]]; then
|
|
DOTNET="$DOTNET_BIN"
|
|
elif command -v dotnet >/dev/null 2>&1; then
|
|
DOTNET="$(command -v dotnet)"
|
|
elif [[ -x "$HOME/.dotnet/dotnet" ]]; then
|
|
DOTNET="$HOME/.dotnet/dotnet"
|
|
else
|
|
die "dotnet is unavailable; set DOTNET_BIN or install the .NET 8 SDK."
|
|
fi
|
|
|
|
DOTNET_DIR="$(cd "$(dirname "$DOTNET")" && pwd)"
|
|
export PATH="$DOTNET_DIR:$PATH"
|
|
if [[ -d "$DOTNET_DIR/host/fxr" ]]; then
|
|
export DOTNET_ROOT="$DOTNET_DIR"
|
|
fi
|
|
|
|
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|
export DOTNET_NOLOGO=1
|
|
export TZ=UTC
|
|
export SOURCE_DATE_EPOCH="315532800"
|
|
|
|
API_PROJECT="Api.SeaHavenIndustries/Api.SeaHavenIndustries.csproj"
|
|
MIGRATIONS_PROJECT="Data.SeaHavenIndustries/Data.SeaHavenIndustries.csproj"
|
|
EF_VERSION="8.0.8"
|
|
RUNTIME="${RUNTIME:-linux-x64}"
|
|
|
|
[[ -f "$API_PROJECT" ]] || die "missing API project: $API_PROJECT"
|
|
[[ -f "$MIGRATIONS_PROJECT" ]] || die "missing migrations project: $MIGRATIONS_PROJECT"
|
|
command -v zip >/dev/null 2>&1 || die "zip is required to build the source bundle."
|
|
|
|
GENERATED_ROOT="$(dirname "$STAGING_DIR")"
|
|
case "$GENERATED_ROOT" in
|
|
.artifacts/elastic-beanstalk) : ;;
|
|
*) die "refusing to remove unexpected generated dir: $GENERATED_ROOT" ;;
|
|
esac
|
|
|
|
log "clean generated artifacts"
|
|
rm -rf "$GENERATED_ROOT" "$TOOLS_DIR"
|
|
mkdir -p "$STAGING_DIR" "$TOOLS_DIR"
|
|
|
|
log "publish $API_PROJECT (Release, self-contained, $RUNTIME)"
|
|
"$DOTNET" publish "$API_PROJECT" \
|
|
-c Release \
|
|
--self-contained \
|
|
--runtime "$RUNTIME" \
|
|
-o "$STAGING_DIR" \
|
|
/p:ContinuousIntegrationBuild=true \
|
|
/p:UseAppHost=true
|
|
|
|
log "install dotnet-ef $EF_VERSION (local tool path)"
|
|
if ! "$DOTNET" tool install dotnet-ef --version "$EF_VERSION" --tool-path "$TOOLS_DIR" 2>/dev/null; then
|
|
"$DOTNET" tool update dotnet-ef --version "$EF_VERSION" --tool-path "$TOOLS_DIR"
|
|
fi
|
|
EF="$TOOLS_DIR/dotnet-ef"
|
|
|
|
log "build EF migrations bundle (self-contained, $RUNTIME)"
|
|
"$EF" migrations bundle \
|
|
--project "$MIGRATIONS_PROJECT" \
|
|
--startup-project "$API_PROJECT" \
|
|
--configuration Release \
|
|
--self-contained \
|
|
--runtime "$RUNTIME" \
|
|
--output "$STAGING_DIR/efbundle"
|
|
|
|
chmod 0755 "$STAGING_DIR/efbundle"
|
|
|
|
log "copy .ebextensions into bundle root"
|
|
mkdir -p "$STAGING_DIR/.ebextensions"
|
|
cp -R .ebextensions/. "$STAGING_DIR/.ebextensions/"
|
|
|
|
log "verify no committed secret placeholders survived publish"
|
|
if grep -rIEl -- 'Server=.*;.*Password=|AccountKey=|aws_secret|AKIA[0-9A-Z]{16}' "$STAGING_DIR" 2>/dev/null; then
|
|
die "potential secret detected in publish output; refusing to package."
|
|
fi
|
|
|
|
log "assemble source bundle (contents, not the containing directory)"
|
|
(
|
|
cd "$STAGING_DIR"
|
|
# ZIP stores file mtimes. Normalize them so identical source/build inputs
|
|
# produce byte-identical source bundles.
|
|
find . -type f -exec touch -t 198001010000 {} +
|
|
find . -type f -print | LC_ALL=C sort \
|
|
| zip -q -X -@ "$REPO_ROOT/$OUTPUT_ZIP"
|
|
)
|
|
|
|
log "package written: $OUTPUT_ZIP"
|
|
printf ' contents: %d files\n' "$(find "$STAGING_DIR" -type f | wc -l | tr -d ' ')"
|
|
printf ' size: %s bytes\n' "$(wc -c < "$REPO_ROOT/$OUTPUT_ZIP" | tr -d ' ')"
|
|
printf ' efbundle: %s\n' "$(file -b "$STAGING_DIR/efbundle" 2>/dev/null || echo present)"
|