Skip to content

GH-49686: [C++][FlightRPC][ODBC][Release] Create signing script for Windows FlightSQL ODBC build#49788

Open
amoeba wants to merge 15 commits intoapache:mainfrom
amoeba:GH-49560--flightsql-release-signing
Open

GH-49686: [C++][FlightRPC][ODBC][Release] Create signing script for Windows FlightSQL ODBC build#49788
amoeba wants to merge 15 commits intoapache:mainfrom
amoeba:GH-49560--flightsql-release-signing

Conversation

@amoeba
Copy link
Copy Markdown
Member

@amoeba amoeba commented Apr 18, 2026

Rationale for this change

We need a script for the release manager to run during the release to locally sign the Windows artifacts for the FlightSQL ODBC driver.

Ref: #49404

What changes are included in this PR?

  • New script, 07-flightsql-odbc-upload.sh
  • Update to release guide

Are these changes tested?

Not 100% but I've tested each step separately. I tested on my fork using fake tags and releases.

Are there any user-facing changes?

No.

@amoeba amoeba requested a review from alinaliBQ April 18, 2026 01:20
@amoeba
Copy link
Copy Markdown
Member Author

amoeba commented Apr 18, 2026

The new script depends on the unmerged changes in #49603 so this PR will need a rebase after that's merged. Reviewers will note that the script in this PR refers to a workflow_dispatch trigger in #49603 that's not on main yet.

Copy link
Copy Markdown
Member

@kou kou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add new scripts to lint targets?

- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.10.0
hooks:
- id: shellcheck
alias: shell
# TODO: Remove this when we fix all lint failures
files: >-
(
?^c_glib/test/run-test\.sh$|
?^ci/scripts/c_glib_build\.sh$|
?^ci/scripts/c_glib_test\.sh$|
?^ci/scripts/ccache_setup\.sh$|
?^ci/scripts/cpp_test\.sh$|
?^ci/scripts/download_tz_database\.sh$|
?^ci/scripts/install_azurite\.sh$|
?^ci/scripts/install_ccache\.sh$|
?^ci/scripts/install_ceph\.sh$|
?^ci/scripts/install_chromedriver\.sh$|
?^ci/scripts/install_cmake\.sh$|
?^ci/scripts/install_conda\.sh$|
?^ci/scripts/install_dask\.sh$|
?^ci/scripts/install_emscripten\.sh$|
?^ci/scripts/install_gcs_testbench\.sh$|
?^ci/scripts/install_iwyu\.sh$|
?^ci/scripts/install_minio\.sh$|
?^ci/scripts/install_ninja\.sh$|
?^ci/scripts/install_numba\.sh$|
?^ci/scripts/install_numpy\.sh$|
?^ci/scripts/install_pandas\.sh$|
?^ci/scripts/install_python\.sh$|
?^ci/scripts/install_sccache\.sh$|
?^ci/scripts/install_spark\.sh$|
?^ci/scripts/install_vcpkg\.sh$|
?^ci/scripts/integration_arrow_build\.sh$|
?^ci/scripts/integration_arrow\.sh$|
?^ci/scripts/integration_dask\.sh$|
?^ci/scripts/integration_hdfs\.sh$|
?^ci/scripts/integration_spark\.sh$|
?^ci/scripts/matlab_build\.sh$|
?^ci/scripts/msys2_setup\.sh$|
?^ci/scripts/msys2_system_clean\.sh$|
?^ci/scripts/msys2_system_upgrade\.sh$|
?^ci/scripts/nanoarrow_build\.sh$|
?^ci/scripts/python_build_emscripten\.sh$|
?^ci/scripts/python_build\.sh$|
?^ci/scripts/python_sdist_build\.sh$|
?^ci/scripts/python_sdist_test\.sh$|
?^ci/scripts/python_wheel_unix_test\.sh$|
?^ci/scripts/python_test_type_annotations\.sh$|
?^ci/scripts/r_build\.sh$|
?^ci/scripts/r_revdepcheck\.sh$|
?^ci/scripts/release_test\.sh$|
?^ci/scripts/ruby_test\.sh$|
?^ci/scripts/rust_build\.sh$|
?^ci/scripts/util_download_apache\.sh$|
?^ci/scripts/util_enable_core_dumps\.sh$|
?^ci/scripts/util_free_space\.sh$|
?^ci/scripts/util_log\.sh$|
?^cpp/build-support/build-lz4-lib\.sh$|
?^cpp/build-support/build-zstd-lib\.sh$|
?^cpp/build-support/get-upstream-commit\.sh$|
?^cpp/build-support/update-thrift\.sh$|
?^cpp/examples/minimal_build/run\.sh$|
?^cpp/examples/tutorial_examples/run\.sh$|
?^cpp/src/arrow/flight/sql/odbc/install/unix/install_odbc\.sh$|
?^dev/release/05-binary-upload\.sh$|
?^dev/release/08-binary-verify\.sh$|
?^dev/release/binary-recover\.sh$|
?^dev/release/post-03-binary\.sh$|
?^dev/release/post-08-docs\.sh$|
?^dev/release/post-09-python\.sh$|
?^dev/release/setup-rhel-rebuilds\.sh$|
?^dev/release/utils-generate-checksum\.sh$|
?^swift/gen-protobuffers\.sh$|
)

files: >-
(
?^ci/scripts/python_test_type_annotations\.sh$|
?^cpp/src/arrow/flight/sql/odbc/install/mac/install_odbc\.sh$|
?^dev/release/05-binary-upload\.sh$|
?^dev/release/binary-recover\.sh$|
?^dev/release/post-03-binary\.sh$|
?^dev/release/post-08-docs\.sh$|
?^dev/release/post-09-python\.sh$|
)

Comment on lines +52 to +59
if [ -z "${ESIGNER_STOREPASS:-}" ]; then
echo "ERROR: ESIGNER_STOREPASS is not set" >&2
exit 1
fi
if [ -z "${ESIGNER_KEYPASS:-}" ]; then
echo "ERROR: ESIGNER_KEYPASS is not set" >&2
exit 1
fi
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about setting them in dev/release/.env?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Done in 62265bd.

Comment thread dev/release/07-flightsqlodbc-upload.sh Outdated
}

# All work with release artifacts happens in a temp dir
tmp_dir="$(mktemp -d)"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using constant temporary directory (such as dev/release/tmp/) instead of mktemp -d?
If we use mktemp -d, we need to remove the temporary directory manually.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Changed in 92a8ae8.

Comment on lines +133 to +137
echo "[4/9] Removing unsigned DLL from GitHub Release..."
gh release delete-asset "${tag}" \
--repo "${GITHUB_REPOSITORY}" \
--yes \
"${dll_unsigned}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to remove unsigned DLL?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to reduce confusion and artifact usage, we don't need to. I don't know if I have a principled reason either way. If we did jsign signing entirely in CI, we'd probably build the unsigned DLL, sign it, and only upload the signed version. Happy to change this though.

Comment on lines +142 to +146
run_url=$(gh workflow run cpp_extra.yml \
--repo "${GITHUB_REPOSITORY}" \
--ref "${tag}" \
--field odbc_release_step=true 2>&1 | grep -oE 'https://[^ ]+')
run_id=${run_url##*/} # Extract the run ID from the URL (the part after the last slash)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using --json and --jq like we did in

RUN_ID=$(gh run list \
--branch "${TAG}" \
--jq '.[].databaseId' \
--json databaseId \
--limit 1 \
--repo "${REPOSITORY}" \
--workflow "${WORKFLOW}")
?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workflow run doesn't have a --json output arg and the command just prints,

[4/9] Triggering odbc_release_step in cpp_extra.yml workflow...
✓ Created workflow_dispatch event for cpp_extra.yml at AMOEBA-arrow-99.9.9-rc0
https://github.com/amoeba/arrow/actions/runs/24583481107

To see the created workflow run, try: gh run view 24583481107
To see runs for this workflow, try: gh run list --workflow="cpp_extra.yml"

Doing it this way isn't ideal but it seems slightly better than the alternative which would be to use gh run list with the right filters and assume the first result is the correct run. I guess that's a pretty safe assumption.

Do you have a preference?

Comment thread dev/release/07-flightsqlodbc-upload.sh Outdated
echo "Triggered run: ${run_url}"

echo "[6/9] Waiting for workflow to complete..."
gh run watch "${run_id}" --repo "${GITHUB_REPOSITORY}" --exit-status
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this may take long time, we may want to add --interval XXX.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in 5b01a0f.

Comment thread dev/release/07-flightsqlodbc-upload.sh Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove 07-publish-gh-release.sh?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, my mistake there. I still had the final commit in my git stash.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f120e3c.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove 08-binary-verify.sh?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same mistake. Will fix.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f120e3c.

@github-actions github-actions bot removed the awaiting review Awaiting review label Apr 18, 2026
@kou kou requested a review from Copilot April 18, 2026 11:35
@github-actions github-actions bot added the awaiting changes Awaiting changes label Apr 18, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds release automation scripts under dev/release/ to support Windows FlightSQL ODBC artifact signing and GitHub Release/RC verification steps as part of the Arrow release process.

Changes:

  • Add a new 07-flightsqlodbc-upload.sh script intended to download, sign, and re-upload the FlightSQL ODBC DLL/MSI to the RC GitHub Release.
  • Add 08-publish-gh-release.sh to publish (undraft) an RC GitHub Release.
  • Add 09-binary-verify.sh to rerun the verify_rc.yml workflow for an RC tag.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.

File Description
dev/release/07-flightsqlodbc-upload.sh New local signing + upload workflow for FlightSQL ODBC Windows DLL/MSI artifacts.
dev/release/08-publish-gh-release.sh New script to mark the RC GitHub Release as non-draft.
dev/release/09-binary-verify.sh New script to rerun the GitHub Actions RC verification workflow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +84 to +96
# Utility function to use jsign to check if a file is signed or not
is_signed() {
local file="$1"
local output
local exit_code
output=$(jsign extract --format PEM "${file}" 2>&1)
exit_code=$?
# jsign writes a PEM file even though it also prints to stdout. Clean up after
# it. Use -f since so it still runs on unsigned files without error.
rm -f "${file}.sig.pem"

return ${exit_code}
}
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_signed() uses output=$(jsign extract ...) while set -e is enabled. If the file is unsigned, jsign extract will return non-zero and the whole script will exit before exit_code=$? / cleanup runs, so the “unsigned vs signed” checks can’t work. Consider temporarily disabling -e inside the function (or using an if jsign extract ...; then ... pattern) and always running the cleanup.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good suggestion. I changed the call to use || to avoid this issue: 33866c7.

Comment thread dev/release/07-flightsqlodbc-upload.sh Outdated
Comment on lines +112 to +132
echo "[2/9] Signing ${dll_signed}..."
echo "NOTE: Running jsign. You may be prompted for your OTP PIN..."
jsign --storetype ESIGNER \
--alias d97c5110-c66a-4c0c-ac0c-1cd6af812ee6 \
--storepass "${ESIGNER_STOREPASS}" \
--keypass "${ESIGNER_KEYPASS}" \
--tsaurl="http://ts.ssl.com" \
--tsmode RFC3161 \
--alg SHA256 \
"${tmp_dir}/${dll_unsigned}"
if ! is_signed "${tmp_dir}/${dll_signed}"; then
echo "ERROR: ${dll_signed} is not signed" >&2
exit 1
fi

echo "[3/9] Uploading signed DLL to GitHub Release..."
gh release upload "${tag}" \
--repo "${GITHUB_REPOSITORY}" \
--clobber \
"${tmp_dir}/${dll_signed}"

Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DLL signing phase signs ${dll_unsigned} in place, but then verifies/uploads ${dll_signed} (arrow_flight_sql_odbc.dll) without ever creating/renaming that file. As written, the verification will fail and the upload will reference a non-existent path. After signing, rename/copy the signed ${dll_unsigned} to ${dll_signed} (or update the subsequent checks/uploads to point at the actual signed file).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a correct call-out. Fixed in 47ec038.

Comment on lines +142 to +151
run_url=$(gh workflow run cpp_extra.yml \
--repo "${GITHUB_REPOSITORY}" \
--ref "${tag}" \
--field odbc_release_step=true 2>&1 | grep -oE 'https://[^ ]+')
run_id=${run_url##*/} # Extract the run ID from the URL (the part after the last slash)
if [ -z "${run_id}" ]; then
echo "ERROR: failed to get run ID from workflow trigger" >&2
exit 1
fi
echo "Triggered run: ${run_url}"
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_url=$(gh workflow run ... | grep ...) runs under set -e -o pipefail; if gh output format changes or grep finds no match, the assignment will cause an immediate exit and the later run_id validation won’t run. Prefer using gh workflow run ... --json (if available) or add a non-fatal fallback (|| true) and/or follow up by querying gh run list for the latest run on the tag.

Suggested change
run_url=$(gh workflow run cpp_extra.yml \
--repo "${GITHUB_REPOSITORY}" \
--ref "${tag}" \
--field odbc_release_step=true 2>&1 | grep -oE 'https://[^ ]+')
run_id=${run_url##*/} # Extract the run ID from the URL (the part after the last slash)
if [ -z "${run_id}" ]; then
echo "ERROR: failed to get run ID from workflow trigger" >&2
exit 1
fi
echo "Triggered run: ${run_url}"
trigger_output=$(gh workflow run cpp_extra.yml \
--repo "${GITHUB_REPOSITORY}" \
--ref "${tag}" \
--field odbc_release_step=true 2>&1)
run_url=$(printf '%s\n' "${trigger_output}" | grep -oE 'https://[^ ]+' || true)
run_id=""
if [ -n "${run_url}" ]; then
run_id=${run_url##*/} # Extract the run ID from the URL (the part after the last slash)
fi
if [ -z "${run_id}" ]; then
run_id=$(gh run list \
--repo "${GITHUB_REPOSITORY}" \
--workflow cpp_extra.yml \
--branch "${tag}" \
--limit 1 \
--json databaseId \
--jq '.[0].databaseId' || true)
fi
if [ -z "${run_id}" ] || [ "${run_id}" = "null" ]; then
echo "ERROR: failed to get run ID from workflow trigger" >&2
exit 1
fi
if [ -n "${run_url}" ]; then
echo "Triggered run: ${run_url}"
else
echo "Triggered run id: ${run_id}"
fi

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems overly cautious. The call-out about the grep erroring and failing the script is fair though so I added a || true so it continues and empty run_id gets caught below. 7f0e6e0

Comment thread dev/release/07-flightsqlodbc-upload.sh Outdated
Comment thread dev/release/07-flightsqlodbc-upload.sh
Comment thread dev/release/08-publish-gh-release.sh
Comment thread dev/release/09-binary-verify.sh
Comment thread dev/release/09-binary-verify.sh
Co-authored-by: Sutou Kouhei <kou@cozmixng.org>
@github-actions github-actions bot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Apr 18, 2026
@amoeba
Copy link
Copy Markdown
Member Author

amoeba commented Apr 18, 2026

The pre-commit failure in https://github.com/apache/arrow/actions/runs/24610839359/job/71964834631?pr=49788#step:5:192 looks like it's caused by an unrelated issue. The fs package recently made a breaking change and it now requires libuv-dev.

I'm going to test a fix in ff033b6 but will revert it and put it into another PR. See #49594 for more context.

@amoeba
Copy link
Copy Markdown
Member Author

amoeba commented Apr 18, 2026

That seems to fix it and now pre-commit is green in CI. I filed #49791. Reverted the test change in 7b99cd6.

@amoeba amoeba requested a review from kou April 18, 2026 18:32
@amoeba
Copy link
Copy Markdown
Member Author

amoeba commented Apr 18, 2026

Thanks for the review @kou. I think I've addressed everything so this is ready for another review.

amoeba added a commit that referenced this pull request Apr 18, 2026
…kage (#49791)

### Rationale for this change

Pre-commit in CI just started to fail trying to install the R `fs` package. See https://github.com/apache/arrow/actions/runs/24610839359. 

I think we didn't see this until now because we were using cached pre-commit and a recent PR of mine just invalidated the cache incidentally. See also #49594. The `fs` package recently made a change that requires we install libuv development headers to install it.

### What changes are included in this PR?

- Updates dev.yml to Install libuv1-dev

### Are these changes tested?

Yes. See my other PR: #49788 (comment).

### Are there any user-facing changes?

No.

Authored-by: Bryce Mecum <petridish@gmail.com>
Signed-off-by: Bryce Mecum <petridish@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants