Build all cryptad packages + draft upstream release (v2) #56
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 all cryptad packages + draft upstream release (v2) | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version number (e.g. 1.2.3). If set, checks out release/<version>" | |
| required: false | |
| default: "" | |
| branch: | |
| description: "Branch or tag to build when version is empty" | |
| required: false | |
| default: "main" | |
| permissions: | |
| contents: read | |
| env: | |
| UPSTREAM_REPO: crypta-network/cryptad | |
| jobs: | |
| snap: | |
| name: Snap (amd64/arm64) | |
| uses: ./.github/workflows/build-cryptad-snap.yml | |
| secrets: inherit | |
| with: | |
| version: ${{ inputs.version }} | |
| branch: ${{ inputs.branch }} | |
| flatpak: | |
| name: Flatpak (x86_64/aarch64) | |
| uses: ./.github/workflows/build-cryptad-flatpak.yml | |
| secrets: inherit | |
| with: | |
| version: ${{ inputs.version }} | |
| branch: ${{ inputs.branch }} | |
| linux: | |
| name: Linux packages (deb/rpm) | |
| uses: ./.github/workflows/build-cryptad-linux-packages.yml | |
| secrets: inherit | |
| with: | |
| version: ${{ inputs.version }} | |
| branch: ${{ inputs.branch }} | |
| macos: | |
| name: macOS DMG (arm64) | |
| uses: ./.github/workflows/build-cryptad-macos.yml | |
| secrets: inherit | |
| with: | |
| version: ${{ inputs.version }} | |
| branch: ${{ inputs.branch }} | |
| windows: | |
| name: Windows installers (amd64/arm64) | |
| uses: ./.github/workflows/build-cryptad-windows.yml | |
| secrets: inherit | |
| with: | |
| version: ${{ inputs.version }} | |
| branch: ${{ inputs.branch }} | |
| jar: | |
| name: JAR | |
| uses: ./.github/workflows/build-cryptad-jar.yml | |
| secrets: inherit | |
| with: | |
| version: ${{ inputs.version }} | |
| branch: ${{ inputs.branch }} | |
| release: | |
| name: Draft upstream release | |
| needs: [snap, flatpak, linux, macos, windows, jar] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout release-ops repo | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Resolve release version | |
| id: resolve | |
| shell: bash | |
| env: | |
| INPUT_VERSION: ${{ inputs.version }} | |
| SNAP_VERSION: ${{ needs.snap.outputs.version }} | |
| FLATPAK_VERSION: ${{ needs.flatpak.outputs.version }} | |
| LINUX_VERSION: ${{ needs.linux.outputs.version }} | |
| MACOS_VERSION: ${{ needs.macos.outputs.version }} | |
| WINDOWS_VERSION: ${{ needs.windows.outputs.version }} | |
| JAR_VERSION: ${{ needs.jar.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| pick_first() { | |
| for v in "$@"; do | |
| if [ -n "$v" ]; then echo "$v"; return 0; fi | |
| done | |
| return 1 | |
| } | |
| VERSION=$(pick_first "$INPUT_VERSION" "$SNAP_VERSION" "$FLATPAK_VERSION" "$LINUX_VERSION" "$MACOS_VERSION" "$WINDOWS_VERSION" "$JAR_VERSION" || true) | |
| if [ -z "$VERSION" ]; then | |
| echo "::error::Could not resolve version from inputs or child workflows" | |
| exit 1 | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Resolved release version: $VERSION (tag v$VERSION)" | |
| - name: Remove existing draft release for this tag (upstream) | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | |
| env: | |
| TAG: v${{ steps.resolve.outputs.version }} | |
| with: | |
| github-token: ${{ secrets.UPSTREAM_REPO_TOKEN }} | |
| script: | | |
| const [owner, repo] = process.env.UPSTREAM_REPO.split('/'); | |
| const tag = process.env.TAG; | |
| try { | |
| const rel = await github.rest.repos.getReleaseByTag({ owner, repo, tag }); | |
| if (rel && rel.data && rel.data.draft) { | |
| core.notice(`Deleting existing draft release id=${rel.data.id} tag=${rel.data.tag_name}`); | |
| await github.rest.repos.deleteRelease({ owner, repo, release_id: rel.data.id }); | |
| } else if (rel && rel.data) { | |
| core.notice(`Release for ${tag} exists and is not draft; leaving intact.`); | |
| } | |
| } catch (e) { | |
| if (e.status === 404) { | |
| core.notice(`No existing release found for tag ${tag}.`); | |
| } else { | |
| throw e; | |
| } | |
| } | |
| - name: Download all artifacts from child builds | |
| uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Filter release files (standardized names only) | |
| id: filter | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p dist-release | |
| echo "::group::All downloaded artifacts" | |
| find dist -type f -print | sed 's/^/::debug::[artifact] /' || true | |
| echo "::endgroup::" | |
| echo "::group::Selecting standardized files" | |
| # Only include files that match our normalized naming scheme | |
| # cryptad-v<version>-<arch>.(snap|flatpak|deb|rpm|dmg) and CryptaInstaller-v<version>-(amd64|arm64).exe | |
| mapfile -t FILES < <(find dist -type f \( \ | |
| -iname 'cryptad-v*-*.snap' -o \ | |
| -iname 'cryptad-v*-*.flatpak' -o \ | |
| -iname 'cryptad-v*-*.deb' -o \ | |
| -iname 'cryptad-v*-*.rpm' -o \ | |
| -iname 'cryptad-v*-*.dmg' -o \ | |
| -iname 'CryptaInstaller-v*-amd64.exe' -o \ | |
| -iname 'CryptaInstaller-v*-arm64.exe' -o \ | |
| -iname 'cryptad.jar' \ | |
| \) -print) | |
| if [ ${#FILES[@]} -eq 0 ]; then | |
| echo "::error::No release files found in dist/" | |
| exit 1 | |
| fi | |
| for f in "${FILES[@]}"; do | |
| cp -f "$f" dist-release/ | |
| echo "::debug::Kept $(basename "$f")" | |
| done | |
| echo "::endgroup::" | |
| echo "count=${#FILES[@]}" >> "$GITHUB_OUTPUT" | |
| # Ensure macOS DMG is included and normalized even if child workflow didn't standardize name | |
| VERSION='${{ steps.resolve.outputs.version }}' | |
| TARGET="dist-release/cryptad-v${VERSION}-arm64.dmg" | |
| if [ ! -f "$TARGET" ]; then | |
| # Try to locate any DMG under the downloaded artifacts | |
| SRC=$(find dist -type f -iname '*.dmg' -print | head -n1 || true) | |
| if [ -n "$SRC" ]; then | |
| cp -f "$SRC" "$TARGET" | |
| echo "::debug::Copied $(basename "$SRC") -> $(basename "$TARGET")" | |
| fi | |
| fi | |
| # Remove any extra DMGs that don't match the normalized name | |
| if ls dist-release/*.dmg >/dev/null 2>&1; then | |
| find dist-release -type f -iname '*.dmg' ! -name "$(basename "$TARGET")" -delete | |
| fi | |
| # Ensure the JAR has the canonical filename | |
| if ls dist-release/cryptad.jar >/dev/null 2>&1; then | |
| echo "::debug::JAR present: cryptad.jar" | |
| else | |
| # If a jar exists with any other name under dist, force-copy it as cryptad.jar | |
| JAR_SRC=$(find dist -type f -iname '*.jar' -print | head -n1 || true) | |
| if [ -n "$JAR_SRC" ]; then | |
| cp -f "$JAR_SRC" dist-release/cryptad.jar | |
| echo "::debug::Copied $(basename "$JAR_SRC") -> cryptad.jar" | |
| fi | |
| fi | |
| - name: Generate SHA256 checksums (and upsert body section) | |
| id: checksums | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.UPSTREAM_REPO_TOKEN }} | |
| UPSTREAM_REPO: ${{ env.UPSTREAM_REPO }} | |
| VERSION: ${{ steps.resolve.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| # Use GitHub-provided workspace path; requires actions/checkout earlier in this job | |
| REPO_ROOT="${GITHUB_WORKSPACE}" | |
| echo "::group::Compute SHA256 over dist-release/**" | |
| cd dist-release | |
| # Create sorted, portable checksums file | |
| find . -type f -print0 | sort -z | xargs -0 shasum -a 256 > SHA256SUMS.txt | |
| echo "::debug::$(wc -l < SHA256SUMS.txt) checksum entries" | |
| echo "::endgroup::" | |
| # Compose the checksums section as a standalone fragment | |
| { | |
| echo "## SHA256 Checksums" | |
| echo | |
| echo '```text' | |
| cat SHA256SUMS.txt | |
| echo '```' | |
| } > RELEASE_BODY_CHECKSUMS.md | |
| # Try to fetch existing release body for tag v$VERSION (if it exists) | |
| # We deliberately do not fail if it can't be fetched. | |
| EXISTING_BODY_FILE=EXISTING_BODY.md | |
| EXISTING_BODY="" | |
| if [ -n "${VERSION:-}" ] && [ -n "${UPSTREAM_REPO:-}" ]; then | |
| echo "::group::Fetch existing release body (if any)" | |
| if gh release view "v${VERSION}" --repo "${UPSTREAM_REPO}" --json body --jq '.body' > "$EXISTING_BODY_FILE" 2>/dev/null; then | |
| echo "::debug::Fetched existing release body for v${VERSION}" | |
| EXISTING_BODY=$(cat "$EXISTING_BODY_FILE") | |
| else | |
| echo "::notice::No existing release body retrieved for v${VERSION}; will merge checksums into fresh body" | |
| : > "$EXISTING_BODY_FILE" | |
| fi | |
| echo "::endgroup::" | |
| else | |
| : > "$EXISTING_BODY_FILE" | |
| fi | |
| # Upsert/replace the "## SHA256 Checksums" section within the existing body | |
| # - If section exists, replace its content with our new fragment | |
| # - If not, append the section to the end (preserving existing body) | |
| NEW_SECTION_FILE=RELEASE_BODY_CHECKSUMS.md | |
| MERGED_FILE=RELEASE_BODY.md | |
| python3 "$REPO_ROOT/.github/scripts/upsert_sha256_section.py" \ | |
| "$EXISTING_BODY_FILE" \ | |
| "$NEW_SECTION_FILE" \ | |
| "$MERGED_FILE" | |
| - name: Create draft release in upstream repo | |
| uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 | |
| if: ${{ steps.resolve.outputs.version != '' }} | |
| with: | |
| token: ${{ secrets.UPSTREAM_REPO_TOKEN }} | |
| repository: ${{ env.UPSTREAM_REPO }} | |
| draft: true | |
| tag_name: v${{ steps.resolve.outputs.version }} | |
| name: Crypta v${{ steps.resolve.outputs.version }} | |
| generate_release_notes: true | |
| body_path: dist-release/RELEASE_BODY.md | |
| files: | | |
| dist-release/*.snap | |
| dist-release/*.flatpak | |
| dist-release/*.deb | |
| dist-release/*.rpm | |
| dist-release/*.dmg | |
| dist-release/CryptaInstaller-*.exe | |
| dist-release/cryptad.jar | |
| dist-release/SHA256SUMS.txt |