name: Release on: push: branches: [ "main" ] jobs: check-release: runs-on: ubuntu-latest outputs: match: ${{ steps.check.outputs.match }} version: ${{ steps.check.outputs.version }} steps: - uses: actions/checkout@v4 - name: Check commit message id: check env: COMMIT_MSG: ${{ github.event.head_commit.message }} run: | if echo "$COMMIT_MSG" | grep -Eq 'Release [vV]?[0-9]+\.[0-9]+\.[0-9]+'; then # Strips "Release " and an optional "v" or "V" to isolate just the numbers VERSION=$(echo "$COMMIT_MSG" | grep -Eo 'Release [vV]?[0-9]+\.[0-9]+\.[0-9]+' | head -n1 | sed -E 's/Release [vV]?//') PACKAGE_VERSION=$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -n1) if [ "$VERSION" != "$PACKAGE_VERSION" ]; then echo "Release version $VERSION does not match Cargo.toml version $PACKAGE_VERSION" >&2 exit 1 fi echo "match=true" >> $GITHUB_OUTPUT echo "version=$VERSION" >> $GITHUB_OUTPUT else echo "match=false" >> $GITHUB_OUTPUT fi build_gnu: needs: check-release if: needs.check-release.outputs.match == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: targets: x86_64-unknown-linux-gnu - name: Build run: cargo build --release --target x86_64-unknown-linux-gnu - name: Rename Raw Executable run: cp target/x86_64-unknown-linux-gnu/release/convertis convertis-x86_64-unknown-linux-gnu - name: Upload Binary for Packaging uses: actions/upload-artifact@v3 with: name: build-gnu path: | target/x86_64-unknown-linux-gnu/release/convertis target/man/convertis.1 - name: Upload Raw Executable Artifact uses: actions/upload-artifact@v3 with: name: raw-gnu path: convertis-x86_64-unknown-linux-gnu build_musl: needs: check-release if: needs.check-release.outputs.match == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: targets: x86_64-unknown-linux-musl - name: Install cross-platform linkers run: | sudo apt-get update sudo apt-get install -y musl-tools - name: Build run: cargo build --release --target x86_64-unknown-linux-musl - name: Rename Raw Executable run: cp target/x86_64-unknown-linux-musl/release/convertis convertis-x86_64-unknown-linux-musl - name: Upload Binary for Packaging uses: actions/upload-artifact@v3 with: name: build-musl path: | target/x86_64-unknown-linux-musl/release/convertis target/man/convertis.1 - name: Upload Raw Executable Artifact uses: actions/upload-artifact@v3 with: name: raw-musl path: convertis-x86_64-unknown-linux-musl build_windows: needs: check-release if: needs.check-release.outputs.match == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: targets: x86_64-pc-windows-gnu - name: Install cross-platform linkers run: | sudo apt-get update sudo apt-get install -y mingw-w64 - name: Build run: cargo build --release --target x86_64-pc-windows-gnu - name: Rename Raw Executable run: cp target/x86_64-pc-windows-gnu/release/convertis.exe convertis-x86_64-pc-windows-gnu.exe - name: Upload Raw Executable Artifact uses: actions/upload-artifact@v3 with: name: raw-windows path: convertis-x86_64-pc-windows-gnu.exe package_gnu: needs: [check-release, build_gnu] if: needs.check-release.outputs.match == 'true' strategy: matrix: type: [deb, rpm] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: targets: x86_64-unknown-linux-gnu - name: Cache cargo uses: Swatinem/rust-cache@v2 with: key: package-${{ matrix.type }}-gnu - name: Install cargo-deb if: matrix.type == 'deb' uses: taiki-e/install-action@v2 with: tool: cargo-deb - name: Install cargo-generate-rpm if: matrix.type == 'rpm' run: cargo install cargo-generate-rpm - name: Download compiled binary uses: actions/download-artifact@v3 with: name: build-gnu path: target/ - name: Build package run: | mkdir -p dist/ sed -i '/\[package\.metadata\.deb\]/a name = "convertis-gnu"' Cargo.toml sed -i '/\[package\.metadata\.generate-rpm\]/a name = "convertis-gnu"' Cargo.toml if [ "${{ matrix.type }}" = "deb" ]; then cargo deb --no-build --target x86_64-unknown-linux-gnu deb_file=$(ls target/x86_64-unknown-linux-gnu/debian/*.deb) cp "$deb_file" "dist/$(basename "$deb_file" .deb)-gnu.deb" elif [ "${{ matrix.type }}" = "rpm" ]; then cargo generate-rpm --target x86_64-unknown-linux-gnu rpm_file=$(ls target/x86_64-unknown-linux-gnu/generate-rpm/*.rpm) cp "$rpm_file" "dist/$(basename "$rpm_file" .rpm)-gnu.rpm" fi - name: Import GPG key if: matrix.type == 'rpm' uses: crazy-max/ghaction-import-gpg@v6 id: import-gpg with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} passphrase: ${{ secrets.GPG_PASSPHRASE }} - name: Sign packages if: matrix.type == 'rpm' env: GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | sudo apt-get update sudo apt-get install -y rpm PASSPHRASE_FILE=$(mktemp) PUBLIC_KEY_FILE=$(mktemp) trap 'rm -f "$PASSPHRASE_FILE" "$PUBLIC_KEY_FILE"' EXIT printf '%s' "$GPG_PASSPHRASE" > "$PASSPHRASE_FILE" chmod 600 "$PASSPHRASE_FILE" echo "%_signature gpg" > ~/.rpmmacros echo "%_gpg_name ${{ steps.import-gpg.outputs.keyid }}" >> ~/.rpmmacros echo "%_gpg_path $HOME/.gnupg" >> ~/.rpmmacros echo "%_gpg_passphrase_file $PASSPHRASE_FILE" >> ~/.rpmmacros echo '%__gpg_sign_cmd /usr/bin/gpg --batch --yes --pinentry-mode loopback --passphrase-file %{_gpg_passphrase_file} --no-verbose --no-armor --no-secmem-warning -u %{_gpg_name} -sbo %{__signature_filename} -- %{__plaintext_filename}' >> ~/.rpmmacros rpm --addsign dist/*.rpm gpg --armor --export "${{ steps.import-gpg.outputs.keyid }}" > "$PUBLIC_KEY_FILE" sudo rpm --import "$PUBLIC_KEY_FILE" SIGNATURE_CHECK=$(rpm --checksig --verbose dist/*.rpm) echo "$SIGNATURE_CHECK" echo "$SIGNATURE_CHECK" | grep -Eq '[Ss]ignature.*: OK' - name: Upload Package Artifact uses: actions/upload-artifact@v3 with: name: pkg-${{ matrix.type }}-gnu path: dist/* package_musl: needs: [check-release, build_musl] if: needs.check-release.outputs.match == 'true' strategy: matrix: type: [deb, rpm] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: targets: x86_64-unknown-linux-musl - name: Cache cargo uses: Swatinem/rust-cache@v2 with: key: package-${{ matrix.type }}-musl - name: Install cargo-deb if: matrix.type == 'deb' uses: taiki-e/install-action@v2 with: tool: cargo-deb - name: Install cargo-generate-rpm if: matrix.type == 'rpm' run: cargo install cargo-generate-rpm - name: Download compiled binary uses: actions/download-artifact@v3 with: name: build-musl path: target/ - name: Build package run: | mkdir -p dist/ sed -i '/\[package\.metadata\.deb\]/a name = "convertis-musl"' Cargo.toml sed -i '/\[package\.metadata\.generate-rpm\]/a name = "convertis-musl"' Cargo.toml if [ "${{ matrix.type }}" = "deb" ]; then cargo deb --no-build --target x86_64-unknown-linux-musl deb_file=$(ls target/x86_64-unknown-linux-musl/debian/*.deb) cp "$deb_file" "dist/$(basename "$deb_file" .deb)-musl.deb" elif [ "${{ matrix.type }}" = "rpm" ]; then cargo generate-rpm --target x86_64-unknown-linux-musl rpm_file=$(ls target/x86_64-unknown-linux-musl/generate-rpm/*.rpm) cp "$rpm_file" "dist/$(basename "$rpm_file" .rpm)-musl.rpm" fi - name: Import GPG key if: matrix.type == 'rpm' uses: crazy-max/ghaction-import-gpg@v6 id: import-gpg with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} passphrase: ${{ secrets.GPG_PASSPHRASE }} - name: Sign packages if: matrix.type == 'rpm' env: GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | sudo apt-get update sudo apt-get install -y rpm PASSPHRASE_FILE=$(mktemp) PUBLIC_KEY_FILE=$(mktemp) trap 'rm -f "$PASSPHRASE_FILE" "$PUBLIC_KEY_FILE"' EXIT printf '%s' "$GPG_PASSPHRASE" > "$PASSPHRASE_FILE" chmod 600 "$PASSPHRASE_FILE" echo "%_signature gpg" > ~/.rpmmacros echo "%_gpg_name ${{ steps.import-gpg.outputs.keyid }}" >> ~/.rpmmacros echo "%_gpg_path $HOME/.gnupg" >> ~/.rpmmacros echo "%_gpg_passphrase_file $PASSPHRASE_FILE" >> ~/.rpmmacros echo '%__gpg_sign_cmd /usr/bin/gpg --batch --yes --pinentry-mode loopback --passphrase-file %{_gpg_passphrase_file} --no-verbose --no-armor --no-secmem-warning -u %{_gpg_name} -sbo %{__signature_filename} -- %{__plaintext_filename}' >> ~/.rpmmacros rpm --addsign dist/*.rpm gpg --armor --export "${{ steps.import-gpg.outputs.keyid }}" > "$PUBLIC_KEY_FILE" sudo rpm --import "$PUBLIC_KEY_FILE" SIGNATURE_CHECK=$(rpm --checksig --verbose dist/*.rpm) echo "$SIGNATURE_CHECK" echo "$SIGNATURE_CHECK" | grep -Eq '[Ss]ignature.*: OK' - name: Upload Package Artifact uses: actions/upload-artifact@v3 with: name: pkg-${{ matrix.type }}-musl path: dist/* publish-release: needs: [check-release, build_windows, package_gnu, package_musl] runs-on: ubuntu-latest permissions: contents: write packages: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Download All Artifacts uses: actions/download-artifact@v3 with: path: all-packages/ merge-multiple: false - name: Cleanup internal build artifacts run: | rm -rf all-packages/build-gnu rm -rf all-packages/build-musl - name: Generate Changelog run: | LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD) git log ${LAST_TAG}..HEAD --pretty=format:"- %s (%an)" > commits.txt echo "## Changelog" > changelog.md echo "### Features" >> changelog.md grep -i "^- feat:" commits.txt >> changelog.md || echo "No new features" >> changelog.md echo "### Fixes" >> changelog.md grep -i "^- fix:" commits.txt >> changelog.md || echo "No fixes" >> changelog.md echo "### Refactoring & Chores" >> changelog.md grep -i "^- refactor:\|^- chore:\|^- style:" commits.txt >> changelog.md || echo "No refactoring or chores" >> changelog.md echo "### Others" >> changelog.md grep -vi "^- feat:\|^- fix:\|^- refactor:\|^- chore:\|^- style:" commits.txt >> changelog.md || echo "No other changes" >> changelog.md - name: Create Gitea Release uses: softprops/action-gh-release@v2 with: tag_name: v${{ needs.check-release.outputs.version }} name: Release v${{ needs.check-release.outputs.version }} body_path: changelog.md files: all-packages/**/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Publish Packages to Gitea Registry run: | GITEA_URL="${{ github.server_url }}" GITEA_OWNER="${{ github.repository_owner }}" TOKEN="${{ secrets.PACKAGE_PAT }}" echo "Publishing RPM packages..." find all-packages/ -type f -name "*.rpm" | while read -r rpm_file; do echo "Uploading $rpm_file..." HTTP_CODE=$(curl -sS -w "%{http_code}" -o /dev/null -u "${{ github.actor }}:$TOKEN" \ --upload-file "$rpm_file" \ "$GITEA_URL/api/packages/$GITEA_OWNER/rpm/upload") if [ "$HTTP_CODE" -ne 201 ] && [ "$HTTP_CODE" -ne 409 ]; then echo "Upload failed with HTTP $HTTP_CODE" exit 1 fi done echo "Publishing DEB packages..." find all-packages/ -type f -name "*.deb" | while read -r deb_file; do echo "Uploading $deb_file..." HTTP_CODE=$(curl -sS -w "%{http_code}" -o /dev/null -u "${{ github.actor }}:$TOKEN" \ --upload-file "$deb_file" \ "$GITEA_URL/api/packages/$GITEA_OWNER/debian/pool/debian/main/upload") if [ "$HTTP_CODE" -ne 201 ] && [ "$HTTP_CODE" -ne 409 ]; then echo "Upload failed with HTTP $HTTP_CODE" exit 1 fi done