Install the binary on a macOS agent, bind a read-scoped service credential from the Jenkins credentials store, and run hexsign certificates download and hexsign profiles download before xcodebuild. Signing material never lives on the controller or in the job config.
What you need
- A macOS agent (labelled, for example,
macos) with Xcode installed. - A service credential with
hexsign-api/readscope, created under Settings → CLI Tokens in the HexSign dashboard. The secret is shown exactly once. - Two Secret text credentials in Jenkins, for example
hexsign-client-idandhexsign-client-secret, holding that credential. - The certificate and profile IDs you sign with (or a team id and bundle id if you fetch by filter).
A declarative Jenkinsfile
// Jenkinsfile
pipeline {
agent { label 'macos' }
environment {
HEXSIGN_CLIENT_ID = credentials('hexsign-client-id')
HEXSIGN_CLIENT_SECRET = credentials('hexsign-client-secret')
HEXSIGN_CERT_ID = 'a1b2c3d4-0000-0000-0000-000000000000'
HEXSIGN_PROFILE_ID = 'e5f6a7b8-0000-0000-0000-000000000000'
CLI_VERSION = 'v0.4.2'
}
stages {
stage('Install HexSign CLI') {
steps {
sh '''
base="https://github.com/hexsign/hexsign-cli/releases/download/$CLI_VERSION"
curl -fsSL -o hexsign "$base/hexsign-darwin-arm64"
curl -fsSL -o checksums.txt "$base/checksums.txt"
shasum -a 256 -c checksums.txt --ignore-missing
chmod +x hexsign
'''
}
}
stage('Fetch signing material') {
steps {
sh '''
./hexsign certificates download "$HEXSIGN_CERT_ID" \
--output-dir build/sign --keychain "$WORKSPACE/signing.keychain-db"
./hexsign profiles download "$HEXSIGN_PROFILE_ID" \
--output-dir build/sign --install
'''
}
}
stage('Archive') {
steps {
sh '''
xcrun xcodebuild archive \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-archivePath build/MyApp.xcarchive
'''
}
}
}
post {
always {
sh 'security delete-keychain "$WORKSPACE/signing.keychain-db" || true'
}
}
}Binding the credential through credentials('hexsign-client-id') puts the value in the environment for the duration of the build and masks it in the console log. The install stage verifies the binary's SHA-256 against the release's signed checksums.txt before it runs, so a tampered or partial download fails the build. --keychain creates a dedicated keychain, imports the .p12, and runs the set-key-partition-list authorization codesign needs; --install drops the profile where Xcode looks for it.
Rotation-proof: fetch by type and bundle id
If you regenerate signing material regularly, swap the UUIDs for filters so the pipeline never changes when a certificate or profile rotates. Set HEXSIGN_TEAM_ID and HEXSIGN_BUNDLE_ID as environment values and fetch every match:
./hexsign certificates download \ --type DISTRIBUTION --team-id "$HEXSIGN_TEAM_ID" \ --output-dir build/sign --keychain "$WORKSPACE/signing.keychain-db" ./hexsign profiles download \ --bundle-id "$HEXSIGN_BUNDLE_ID" --team-id "$HEXSIGN_TEAM_ID" \ --output-dir build/sign --install
Freestyle jobs
On a freestyle job the same pattern applies without a Jenkinsfile. Add both credentials with the Use secret text(s) or file(s) build environment option so they land in the environment as HEXSIGN_CLIENT_ID and HEXSIGN_CLIENT_SECRET, then put the install and download commands in an Execute shell build step ahead of your xcodebuild step.
How auth works
With HEXSIGN_CLIENT_ID and HEXSIGN_CLIENT_SECRET in the environment, the CLI runs in machine mode: it exchanges them for an access token against identity.hexsign.net/oauth2/token and uses it for the download requests, with no interactive login.