feat: complete full-test diagnostics and component controls #148
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Release | |
| on: | |
| push: | |
| tags: | |
| - "v*.*.*" | |
| workflow_dispatch: | |
| inputs: | |
| release_tag: | |
| description: "Existing git tag to release, for example v0.1.143" | |
| required: true | |
| type: string | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| goreleaser: | |
| runs-on: ubuntu-latest | |
| container: | |
| # 1.20 是 Windows 7/8 Server 2008/2012 最后一个支持版本 | |
| image: goreleaser/goreleaser-cross:v1.20 | |
| env: | |
| TMPDIR: /__w/_temp/goecs/tmp | |
| GOTMPDIR: /__w/_temp/goecs/go-tmp | |
| GOCACHE: /__w/_temp/goecs/go-build | |
| GOMODCACHE: /__w/_temp/goecs/go-mod | |
| steps: | |
| - run: | | |
| git config --global --add safe.directory /__w/ecs/ecs | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref }} | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Verify release ref | |
| run: | | |
| if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then | |
| RELEASE_REF="${{ inputs.release_tag }}" | |
| else | |
| RELEASE_REF="${GITHUB_REF#refs/tags/}" | |
| fi | |
| git fetch --force --tags origin | |
| git describe --exact-match --tags HEAD | |
| TAG_COMMIT=$(git rev-list -n 1 "$RELEASE_REF") | |
| HEAD_COMMIT=$(git rev-parse HEAD) | |
| if [[ "$TAG_COMMIT" != "$HEAD_COMMIT" ]]; then | |
| echo "Tag $RELEASE_REF points to $TAG_COMMIT but HEAD is $HEAD_COMMIT" | |
| exit 1 | |
| fi | |
| - name: Prepare Go build directories | |
| run: | | |
| rm -rf .cache .tmp | |
| mkdir -p "$TMPDIR" "$GOTMPDIR" "$GOCACHE" "$GOMODCACHE" | |
| df -h | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| check-latest: true | |
| cache: false | |
| - name: Resolve component refs from go.mod | |
| id: component_refs | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| python3 - <<'PY' | |
| import json | |
| import os | |
| import subprocess | |
| modules = { | |
| "unlocktests": "github.com/oneclickvirt/UnlockTests", | |
| "backtrace": "github.com/oneclickvirt/backtrace", | |
| "basics": "github.com/oneclickvirt/basics", | |
| "cputest": "github.com/oneclickvirt/cputest", | |
| "defaultset": "github.com/oneclickvirt/defaultset", | |
| "disktest": "github.com/oneclickvirt/disktest", | |
| "gostun": "github.com/oneclickvirt/gostun", | |
| "memorytest": "github.com/oneclickvirt/memorytest", | |
| "nt3": "github.com/oneclickvirt/nt3", | |
| "pingtest": "github.com/oneclickvirt/pingtest", | |
| "portchecker": "github.com/oneclickvirt/portchecker", | |
| "privatespeedtest": "github.com/oneclickvirt/privatespeedtest", | |
| "security": "github.com/oneclickvirt/security", | |
| "speedtest": "github.com/oneclickvirt/speedtest", | |
| } | |
| go_mod = json.loads(subprocess.check_output( | |
| ["go", "mod", "edit", "-json"], text=True | |
| )) | |
| required = { | |
| item["Path"]: item["Version"] | |
| for item in go_mod.get("Require", []) | |
| } | |
| with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output: | |
| for key, path in modules.items(): | |
| version = required.get(path, "") | |
| if not version: | |
| raise SystemExit(f"{path} is not pinned in go.mod") | |
| print(f"{key}={version}", file=output) | |
| print(f"{path} -> {version}") | |
| PY | |
| - name: Checkout structured component sources | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/UnlockTests | |
| ref: ${{ steps.component_refs.outputs.unlocktests }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/UnlockTests | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/backtrace | |
| ref: ${{ steps.component_refs.outputs.backtrace }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/backtrace | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/basics | |
| ref: ${{ steps.component_refs.outputs.basics }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/basics | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/cputest | |
| ref: ${{ steps.component_refs.outputs.cputest }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/cputest | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/defaultset | |
| ref: ${{ steps.component_refs.outputs.defaultset }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/defaultset | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/disktest | |
| ref: ${{ steps.component_refs.outputs.disktest }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/disktest | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/gostun | |
| ref: ${{ steps.component_refs.outputs.gostun }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/gostun | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/memorytest | |
| ref: ${{ steps.component_refs.outputs.memorytest }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/memorytest | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/nt3 | |
| ref: ${{ steps.component_refs.outputs.nt3 }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/nt3 | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/pingtest | |
| ref: ${{ steps.component_refs.outputs.pingtest }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/pingtest | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/portchecker | |
| ref: ${{ steps.component_refs.outputs.portchecker }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/portchecker | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/privatespeedtest | |
| ref: ${{ steps.component_refs.outputs.privatespeedtest }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/privatespeedtest | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/security | |
| ref: ${{ steps.component_refs.outputs.security }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/security | |
| - uses: actions/checkout@v4 | |
| with: | |
| repository: oneclickvirt/speedtest | |
| ref: ${{ steps.component_refs.outputs.speedtest }} | |
| token: ${{ secrets.GHT }} | |
| path: deps/speedtest | |
| - name: Configure Git for Private Modules | |
| run: | | |
| git config --global --unset-all url."git@github.com:".insteadOf || true | |
| git config --global --replace-all url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/" | |
| - name: Configure structured component workspace | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| printf '/deps/\n/go.work\n/go.work.sum\n' >> .git/info/exclude | |
| go work init . | |
| for module in UnlockTests backtrace basics cputest defaultset disktest gostun memorytest nt3 pingtest portchecker privatespeedtest security speedtest; do | |
| expected="github.com/oneclickvirt/${module}" | |
| actual="$(awk '$1 == "module" { print $2; exit }' "deps/${module}/go.mod")" | |
| if [[ "$actual" != "$expected" ]]; then | |
| echo "Module path mismatch for ${module}: expected ${expected}, got ${actual:-<empty>}" >&2 | |
| exit 1 | |
| fi | |
| go work use "deps/${module}" | |
| done | |
| export GOWORK="$PWD/go.work" | |
| for module in UnlockTests backtrace basics cputest defaultset disktest gostun memorytest nt3 pingtest portchecker privatespeedtest security speedtest; do | |
| resolved="$(go list -m -f '{{.Dir}}' "github.com/oneclickvirt/${module}")" | |
| expected="$PWD/deps/${module}" | |
| if [[ "$resolved" != "$expected" ]]; then | |
| echo "Workspace did not select ${module}: expected ${expected}, got ${resolved:-<empty>}" >&2 | |
| exit 1 | |
| fi | |
| done | |
| go test -race ./... | |
| go vet ./... | |
| git diff --exit-code -- go.mod go.sum | |
| - name: Run embedded-registry canary | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| export GOWORK="$PWD/go.work" | |
| output="$TMPDIR/offline-canary.json" | |
| go run ./cmd/ecs-canary -offline -canary-deadline 30s > "$output" | |
| python3 - "$output" <<'PY' | |
| import json | |
| import sys | |
| with open(sys.argv[1], "r", encoding="utf-8") as handle: | |
| report = json.load(handle) | |
| if report.get("schema_version") != "goecs.report/v1": | |
| raise SystemExit("offline canary returned an unexpected schema") | |
| files = report.get("data_files") or [] | |
| if len(files) < 8: | |
| raise SystemExit(f"offline canary returned only {len(files)} component registries") | |
| failed = [item for item in files if item.get("status") != "ok" or item.get("source") != "embedded"] | |
| if failed: | |
| raise SystemExit(f"offline component registry validation failed: {failed}") | |
| PY | |
| # - name: Install UPX | |
| # run: | | |
| # apk add --no-cache upx | |
| - name: Clean Go build cache before build | |
| run: | | |
| rm -rf "$TMPDIR" "$GOTMPDIR" | |
| mkdir -p "$TMPDIR" "$GOTMPDIR" | |
| go clean -cache -testcache | |
| available_kb="$(df -Pk / | awk 'NR == 2 { print $4 }')" | |
| if [[ -z "$available_kb" || "$available_kb" -lt 5242880 ]]; then | |
| echo "Insufficient release disk space: ${available_kb:-unknown} KiB available" >&2 | |
| exit 1 | |
| fi | |
| df -h | |
| - name: Download Go modules | |
| run: go mod download | |
| env: | |
| GOPRIVATE: github.com/oneclickvirt/security,github.com/oneclickvirt/privatespeedtest | |
| - name: Run GoReleaser | |
| uses: goreleaser/goreleaser-action@v6 | |
| with: | |
| distribution: goreleaser | |
| # version: latest | |
| version: '~> v2' | |
| # --parallelism 1: build targets sequentially so the per-build | |
| # embed_geekbench.sh pre-hook can safely modify the shared | |
| # module-cache geekbench_bins directory without race conditions. | |
| args: release --clean --parallelism 1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GHT }} | |
| GOPRIVATE: github.com/oneclickvirt/security,github.com/oneclickvirt/privatespeedtest | |
| - name: Update goecs.sh with new version | |
| run: | | |
| if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then | |
| VERSION="${{ inputs.release_tag }}" | |
| VERSION="${VERSION#v}" | |
| elif [[ "$GITHUB_REF" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| else | |
| VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.1.37") | |
| fi | |
| echo "Using version: $VERSION" | |
| FILE="goecs.sh" | |
| BRANCH="master" | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| git config --global --unset url."git@github.com:".insteadOf || true | |
| git fetch origin $BRANCH | |
| git checkout $BRANCH | |
| if [ ! -f "$FILE" ]; then | |
| echo "Error: $FILE not found" | |
| exit 1 | |
| fi | |
| sed -i "s/\(_yellow \"Unable to get version info, using default version \).*\(\".*\)/\1$VERSION\2/" "$FILE" | |
| sed -i "s/\(ECS_VERSION=\"\).*\(\"\)/\1$VERSION\2/" "$FILE" | |
| if git diff --quiet "$FILE"; then | |
| echo "No changes detected in $FILE" | |
| exit 0 | |
| fi | |
| git add "$FILE" | |
| git commit -m "chore: update ECS_VERSION to $VERSION in goecs.sh" | |
| git push origin $BRANCH | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GHT }} |