Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ name: Docker
on:
push:
tags: ['v*']
# Dry run. The layer cache and the build itself are only otherwise
# exercised by a real release, which is a bad place to discover that the
# cache backend is misconfigured. A dispatch builds both architectures
# exactly as a release does but PUBLISHES NOTHING: no registry push, no
# digest artifacts, no manifest list, no tag moved. See the `dry_run`
# expression on the build step's outputs.
workflow_dispatch:

concurrency:
# The workflow only accepts tag pushes. Include both the tag and target SHA
Expand Down Expand Up @@ -35,6 +42,14 @@ jobs:
id: ancestry
run: |
set -euo pipefail
# A dispatch publishes nothing, so there is no tag to vouch for and
# nothing for this gate to protect. Allow it through explicitly
# rather than by accident: the gate exists to stop an unreviewed
# commit reaching Docker Hub, and a dry run cannot.
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "allowed=true" >> "$GITHUB_OUTPUT"
exit 0
fi
git fetch --no-tags origin main:refs/remotes/origin/main
if git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
echo "allowed=true" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -71,6 +86,7 @@ jobs:
network=host

- name: Log in to Docker Hub
if: github.event_name != 'workflow_dispatch'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
Expand All @@ -97,7 +113,10 @@ jobs:
build-args: |
VERSION=${{ github.ref_name }}
REVISION=${{ github.sha }}
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
# push=false on a dispatch. Everything above this line is identical
# between a dry run and a release, so the dry run measures the real
# build and populates the real cache.
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'workflow_dispatch' }}
cache-from: type=gha,url=http://127.0.0.1:49160/,scope=oxidex-${{ matrix.arch }},version=1
cache-to: type=gha,url=http://127.0.0.1:49160/,scope=oxidex-${{ matrix.arch }},mode=max,version=1

Expand All @@ -106,6 +125,7 @@ jobs:
# job: that runs on ubuntu-latest and could not execute the arm64 image
# without the emulation this design avoids.
- name: Smoke test
if: github.event_name != 'workflow_dispatch'
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
Expand All @@ -129,6 +149,7 @@ jobs:
echo "Smoke test passed for $ref"

- name: Export digest
if: github.event_name != 'workflow_dispatch'
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
Expand All @@ -137,6 +158,7 @@ jobs:
touch "${RUNNER_TEMP}/digests/${DIGEST#sha256:}"

- name: Upload digest
if: github.event_name != 'workflow_dispatch'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: digest-${{ matrix.arch }}
Expand All @@ -147,7 +169,7 @@ jobs:
merge:
name: Publish manifest list
needs: [verify-tag-on-main, build]
if: needs.verify-tag-on-main.outputs.allowed == 'true' && needs.build.result == 'success'
if: github.event_name != 'workflow_dispatch' && needs.verify-tag-on-main.outputs.allowed == 'true' && needs.build.result == 'success'
# 2x: no image build happens here -- this downloads two digest stubs and
# calls `buildx imagetools create`, which is registry I/O end to end. It
# is on a Warp runner rather than ubuntu-latest to avoid the GitHub-hosted
Expand Down
11 changes: 9 additions & 2 deletions src/core/jpeg_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1716,7 +1716,12 @@ pub fn process_dji_thermal_segments(segments: &[Segment], metadata: &mut Metadat
/// multiplied by positive 100, which is reproduced before calling `snprintf`.
fn perl_sprintf_g(value: f64) -> String {
let value = if value == 0.0 { 0.0 } else { value };
let mut output = [0_i8; 32];
// `libc::c_char`, not a literal `i8`: it is signed on x86_64 and UNSIGNED
// on aarch64, so hardcoding either side compiles on one architecture and
// fails on the other. This was `[0_i8; 32]`, which broke every arm64
// build -- caught by the Docker dry run, since arm64 is only built there
// and in release.yml.
let mut output = [0 as libc::c_char; 32];
// SAFETY: `output` is writable for its full reported size, the format is a
// static NUL-terminated C string with one `%g`, and `value` has the
// required promoted `double` type. A default-precision rendering of any
Expand All @@ -1727,7 +1732,9 @@ fn perl_sprintf_g(value: f64) -> String {
let length = usize::try_from(length).unwrap_or(0).min(output.len() - 1);
let bytes = &output[..length];
// `%g` emits ASCII digits, punctuation, exponent markers, or the
// implementation's ASCII inf/nan spelling.
// implementation's ASCII inf/nan spelling. `as u8` is a no-op where
// c_char is already unsigned and a reinterpret where it is signed; both
// are correct for ASCII.
String::from_utf8(bytes.iter().map(|byte| *byte as u8).collect()).expect("C %g output is ASCII")
}
/// Emits `APP3:ImagingData`, the InfiRay IR + thermal + visible payload.
Expand Down
Loading