# Release Pipeline using Dockerfile.cross # Uses existing Dockerfile.cross for consistent multi-platform builds # Triggers on tags: v* when: event: tag tag: v* steps: # Create GitHub-style release via Gitea API create-release: image: alpine:latest secrets: [gitea_token] commands: - apk add --no-cache curl jq - | RELEASE_DATA=$(jq -n \ --arg tag "${CI_COMMIT_TAG}" \ --arg name "Release ${CI_COMMIT_TAG}" \ --arg body "Multi-platform build using Dockerfile.cross" \ '{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}') RELEASE_ID=$(curl -X POST "${GITEA_URL}/api/v1/repos/${CI_REPO}/releases" \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/json" \ -d "${RELEASE_DATA}" | jq -r '.id') echo "RELEASE_ID=${RELEASE_ID}" > /tmp/release.env echo "✓ Created release ${CI_COMMIT_TAG} (ID: ${RELEASE_ID})" # Build binaries using Dockerfile.cross (matrix) build-binaries: image: docker:27-cli volumes: - /var/run/docker.sock:/var/run/docker.sock matrix: TARGET: - x86_64-unknown-linux-gnu - aarch64-unknown-linux-gnu - x86_64-apple-darwin - aarch64-apple-darwin - x86_64-pc-windows-msvc commands: - echo "Building for target ${TARGET}..." # Build using Dockerfile.cross - | docker build -t typedialog-build:${TARGET} \ --build-arg TARGET=${TARGET} \ -f .woodpecker/Dockerfile.cross . # Create container and extract binaries - docker create --name extract-${TARGET} typedialog-build:${TARGET} - mkdir -p dist-${TARGET} - docker cp extract-${TARGET}:/output/bin/. dist-${TARGET}/ - docker cp extract-${TARGET}:/output/BUILD_INFO.json dist-${TARGET}/ || true - docker rm extract-${TARGET} # Package binaries - tar czf typedialog-${CI_COMMIT_TAG}-${TARGET}.tar.gz -C dist-${TARGET} . - sha256sum typedialog-${CI_COMMIT_TAG}-${TARGET}.tar.gz > typedialog-${CI_COMMIT_TAG}-${TARGET}.tar.gz.sha256 - echo "✓ Built and packaged ${TARGET}" depends_on: - create-release # Generate SBOMs (SPDX + CycloneDX) generate-sbom: image: typedialog-ci:latest # Uses custom image with cargo-sbom pre-installed commands: - cargo sbom --output-format spdx_json_2_3 > sbom-spdx.json - cargo sbom --output-format cyclone_dx_json_1_4 > sbom-cyclonedx.json - echo "✓ Generated SBOMs" depends_on: - create-release # Upload all artifacts to Gitea release upload-artifacts: image: alpine:latest secrets: [gitea_token] commands: - apk add --no-cache curl jq - source /tmp/release.env - echo "Uploading to release ID ${RELEASE_ID}..." # Upload binary archives and checksums - | for file in typedialog-${CI_COMMIT_TAG}-*.tar.gz*; do if [ -f "${file}" ]; then echo "Uploading ${file}..." curl -X POST "${GITEA_URL}/api/v1/repos/${CI_REPO}/releases/${RELEASE_ID}/assets?name=${file}" \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/octet-stream" \ --data-binary "@${file}" fi done # Upload SBOMs - | for file in sbom-*.json; do if [ -f "${file}" ]; then echo "Uploading ${file}..." curl -X POST "${GITEA_URL}/api/v1/repos/${CI_REPO}/releases/${RELEASE_ID}/assets?name=${file}" \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/json" \ --data-binary "@${file}" fi done - echo "✓ All artifacts uploaded successfully" depends_on: - build-binaries - generate-sbom # Optional: Publish to crates.io publish-crates: image: rust:latest secrets: [cargo_token] when: # Only if CARGO_TOKEN secret is set evaluate: 'CI_PIPELINE_EVENT == "tag" && CI_COMMIT_TAG =~ "^v[0-9]"' commands: - | for crate in typedialog-core typedialog-ag-core typedialog-ai typedialog-prov-gen typedialog typedialog-tui typedialog-web typedialog-ag typedialog-ag-server; do echo "Publishing ${crate}..." cargo publish -p ${crate} --token ${CARGO_TOKEN} || true sleep 30 done - echo "✓ Published to crates.io" depends_on: - upload-artifacts