name: custom release permissions: contents: write on: push: branches: - main paths: - ".github/workflows/custom-release.yml" - "patches/**" - "scripts/update-custom-release.sh" - "scripts/sync-upstream.sh" workflow_dispatch: inputs: upstream_tag: description: "Optional upstream Syncthing tag, for example v2.1.3" required: false suffix: description: "Optional custom release suffix, for example stignore.7" required: false schedule: - cron: "17 04 * * *" jobs: build-custom-release: runs-on: macos-14 steps: - name: Check out repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v5 with: go-version-file: go.mod cache: false - name: Configure Git author run: | git config user.name "GitHub Actions" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Mirror upstream and rebuild patched main run: ./scripts/sync-upstream.sh env: SYNC_REMOTE: origin - name: Import Developer ID certificate run: | set -euo pipefail keychain_path="$RUNNER_TEMP/syncthing-release-signing.keychain-db" keychain_password="$(openssl rand -hex 24)" certificate_path="$RUNNER_TEMP/developer-id-application.p12" previous_default_keychain="$(security default-keychain -d user 2>/dev/null | sed 's/[ "]//g' || true)" echo "::add-mask::$keychain_password" echo "CUSTOM_RELEASE_KEYCHAIN_PATH=$keychain_path" >> "$GITHUB_ENV" echo "CUSTOM_RELEASE_KEYCHAIN_PASSWORD=$keychain_password" >> "$GITHUB_ENV" echo "CUSTOM_RELEASE_CERTIFICATE_PATH=$certificate_path" >> "$GITHUB_ENV" echo "CUSTOM_RELEASE_PREVIOUS_DEFAULT_KEYCHAIN=$previous_default_keychain" >> "$GITHUB_ENV" if [ -z "$DEVELOPER_ID_APPLICATION_P12_BASE64" ]; then echo "DEVELOPER_ID_APPLICATION_P12_BASE64 secret is required" >&2 exit 1 fi if [ -z "$DEVELOPER_ID_APPLICATION_P12_PASSWORD" ]; then echo "DEVELOPER_ID_APPLICATION_P12_PASSWORD secret is required" >&2 exit 1 fi printf '%s' "$DEVELOPER_ID_APPLICATION_P12_BASE64" | base64 -D > "$certificate_path" security create-keychain -p "$keychain_password" "$keychain_path" security set-keychain-settings -lut 21600 "$keychain_path" security unlock-keychain -p "$keychain_password" "$keychain_path" security import "$certificate_path" -k "$keychain_path" -P "$DEVELOPER_ID_APPLICATION_P12_PASSWORD" -A -T /usr/bin/codesign -T /usr/bin/security security list-keychains -d user -s "$keychain_path" security default-keychain -d user -s "$keychain_path" security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$keychain_password" "$keychain_path" identity_output="$(security find-identity -v -p codesigning "$keychain_path")" printf '%s\n' "$identity_output" codesign_identity="$(printf '%s\n' "$identity_output" | sed -n 's/.*"\(Developer ID Application:[^"]*\)".*/\1/p' | head -n 1)" if [ -z "$codesign_identity" ]; then echo "Developer ID Application signing identity is required in DEVELOPER_ID_APPLICATION_P12_BASE64" >&2 exit 1 fi probe_binary="$RUNNER_TEMP/codesign-probe" cp /usr/bin/true "$probe_binary" codesign --force --dryrun --sign "$codesign_identity" --keychain "$keychain_path" --options runtime --timestamp "$probe_binary" echo "CUSTOM_RELEASE_CODESIGN_IDENTITY=$codesign_identity" >> "$GITHUB_ENV" env: DEVELOPER_ID_APPLICATION_P12_BASE64: ${{ secrets.DEVELOPER_ID_APPLICATION_P12_BASE64 }} DEVELOPER_ID_APPLICATION_P12_PASSWORD: ${{ secrets.DEVELOPER_ID_APPLICATION_P12_PASSWORD }} - name: Build patched Syncthing release run: | case "$(uname -m)" in arm64) darwin_arch=arm64 ;; x86_64) darwin_arch=amd64 ;; *) echo "Unsupported macOS runner architecture: $(uname -m)" >&2; exit 1 ;; esac export CUSTOM_RELEASE_BUILDS="darwin/$darwin_arch/zip/1 linux/amd64/tar/0 linux/arm64/tar/0" ./scripts/update-custom-release.sh env: CUSTOM_RELEASE_UPSTREAM_TAG: ${{ github.event.inputs.upstream_tag }} CUSTOM_RELEASE_SUFFIX: ${{ github.event.inputs.suffix }} CUSTOM_RELEASE_PUSH: "1" CUSTOM_RELEASE_PUSH_BRANCH: "0" CUSTOM_RELEASE_REMOTE: origin CUSTOM_RELEASE_CODESIGN_TEAM_ID: "NG5W75WE8U" CUSTOM_RELEASE_SIGN_DARWIN: "1" CUSTOM_RELEASE_REQUIRE_GATEKEEPER_ASSESSMENT: "0" CUSTOM_RELEASE_CREATE_GITEA_RELEASE: "0" GH_TOKEN: ${{ github.token }} - name: Publish GitHub release run: | tag="${CUSTOM_RELEASE_UPSTREAM_TAG:-$(git ls-remote --refs --tags --sort='version:refname' https://github.com/syncthing/syncthing.git 'v[0-9]*' | awk '{ tag = $2; sub("refs/tags/", "", tag); if (tag ~ /^v[0-9]+\.[0-9]+\.[0-9]+$/) latest = tag } END { print latest }')}-$CUSTOM_RELEASE_SUFFIX" if gh release view "$tag" >/dev/null 2>&1; then echo "GitHub release $tag already exists; nothing to do." exit 0 fi assets=() for asset in dist/*; do [ -f "$asset" ] || continue [ "$(basename "$asset")" = release-notes.md ] && continue assets+=("$asset") done gh release create "$tag" "${assets[@]}" --title "$tag" --notes-file dist/release-notes.md --verify-tag env: CUSTOM_RELEASE_UPSTREAM_TAG: ${{ github.event.inputs.upstream_tag }} CUSTOM_RELEASE_SUFFIX: ${{ github.event.inputs.suffix || 'stignore.7' }} GH_TOKEN: ${{ github.token }} - name: Delete temporary keychain if: always() run: | if [ -n "${CUSTOM_RELEASE_PREVIOUS_DEFAULT_KEYCHAIN:-}" ] && [ -e "$CUSTOM_RELEASE_PREVIOUS_DEFAULT_KEYCHAIN" ]; then security default-keychain -d user -s "$CUSTOM_RELEASE_PREVIOUS_DEFAULT_KEYCHAIN" || true fi if [ -n "${CUSTOM_RELEASE_KEYCHAIN_PATH:-}" ]; then security delete-keychain "$CUSTOM_RELEASE_KEYCHAIN_PATH" || true fi if [ -n "${CUSTOM_RELEASE_CERTIFICATE_PATH:-}" ]; then rm -f "$CUSTOM_RELEASE_CERTIFICATE_PATH" fi