-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add docker build with all formatting tools #82
Changes from 7 commits
108c343
c3327d2
20234f7
392bc7d
f668c8f
f4e22c9
72479b3
f3243cb
66b773d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
name: Build and push docker image | ||
|
||
on: | ||
workflow_dispatch: | ||
push: | ||
branches: [main] | ||
|
||
env: | ||
CARGO_TERM_COLOR: always | ||
REGISTRY_IMAGE: ghcr.io/svix/openapi-codegen | ||
|
||
jobs: | ||
build: | ||
strategy: | ||
matrix: | ||
platform: | ||
- runner: ubuntu-24.04 | ||
name: amd64 | ||
- runner: ubuntu-24.04-arm | ||
name: arm64 | ||
name: Build and publish ${{ matrix.platform.name }} docker image | ||
if: github.ref == 'refs/heads/main' | ||
runs-on: "${{ matrix.platform.runner }}" | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Login to ghcr | ||
uses: docker/login-action@v3 | ||
with: | ||
registry: ghcr.io | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
|
||
- name: Build and push by digest | ||
id: build | ||
uses: docker/build-push-action@v6 | ||
with: | ||
tags: ${{ env.REGISTRY_IMAGE }} | ||
file: Dockerfile.${{ matrix.platform.name }} | ||
cache-from: type=gha | ||
cache-to: type=gha | ||
platforms: linux/${{ matrix.platform.name }} | ||
outputs: type=image,push-by-digest=true,name-canonical=true,push=true | ||
|
||
- name: Export digest | ||
# we create empty files with the sha256 digest of the docker image as the filename | ||
# since we did not push with a tag, the only way to identify the image is with the digest | ||
run: | | ||
mkdir -p ${{ runner.temp }}/digests | ||
digest="${{ steps.build.outputs.digest }}" | ||
touch "${{ runner.temp }}/digests/${digest#sha256:}" | ||
|
||
- name: Upload digest | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: digests-${{ matrix.platform.name }} | ||
path: ${{ runner.temp }}/digests/* | ||
if-no-files-found: error | ||
retention-days: 1 | ||
|
||
publish-merged-manifest: | ||
if: github.ref == 'refs/heads/main' | ||
runs-on: ubuntu-24.04 | ||
needs: | ||
- build | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Download digests | ||
uses: actions/download-artifact@v4 | ||
with: | ||
path: ${{ runner.temp }}/digests | ||
pattern: digests-* | ||
merge-multiple: true | ||
|
||
- name: Login to ghcr | ||
uses: docker/login-action@v3 | ||
with: | ||
registry: ghcr.io | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
|
||
- run: echo "IMAGE_TAG=$(date +%Y%m%d)-$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" | ||
|
||
- name: Create manifest list and push | ||
# inside the ${{ runner.temp }}/digests we downloaded empty files with the sha256 digest of the image as the filename | ||
# using printf we get the digest from the filename and we add the digest to the manifest | ||
# this is the recommend way of doing things :( | ||
# https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners | ||
working-directory: ${{ runner.temp }}/digests | ||
run: | | ||
docker buildx imagetools create \ | ||
-t ${{ env.REGISTRY_IMAGE }}:latest \ | ||
-t ${{ env.REGISTRY_IMAGE }}:${{ env.IMAGE_TAG }} \ | ||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) | ||
|
||
- name: Inspect image | ||
run: | | ||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:latest | ||
svix-mman marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# build openapi-codegen | ||
FROM docker.io/lukemathwalker/cargo-chef:latest-rust-1.85 AS chef | ||
WORKDIR /app | ||
|
||
FROM chef AS planner | ||
|
||
COPY Cargo.toml . | ||
COPY Cargo.lock . | ||
COPY build.rs . | ||
COPY src /app/src | ||
|
||
RUN cargo chef prepare --recipe-path recipe.json | ||
|
||
FROM chef AS openapi-codegen-builder | ||
|
||
COPY --from=planner /app/recipe.json recipe.json | ||
|
||
RUN cargo chef cook --release --recipe-path recipe.json | ||
|
||
COPY Cargo.toml . | ||
COPY Cargo.lock . | ||
COPY build.rs . | ||
COPY src /app/src | ||
|
||
RUN cargo build --release --bin openapi-codegen | ||
|
||
# build goimports | ||
FROM docker.io/golang:1.24-bookworm AS goimports-builder | ||
RUN go install golang.org/x/tools/cmd/goimports@latest | ||
|
||
# build rubyfmt | ||
FROM docker.io/rust:1.85 AS rubyfmt-builder | ||
WORKDIR /app | ||
|
||
RUN apt-get update && \ | ||
apt-get install -y --no-install-recommends ruby bison && \ | ||
apt-get clean | ||
|
||
RUN git clone https://github.com/fables-tales/rubyfmt.git \ | ||
--recurse-submodules --shallow-submodules /app && \ | ||
git checkout 71cbb4adc53d3d8b36a6f1b3dcff87865d0204b8 | ||
|
||
RUN cargo build --release | ||
|
||
# main container | ||
FROM docker.io/ubuntu:noble | ||
|
||
ENV DEBIAN_FRONTEND=noninteractive | ||
ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.cargo/bin/:/root/.dotnet/tools" | ||
|
||
RUN apt-get update && \ | ||
apt-get install -y --no-install-recommends curl default-jre-headless dotnet8 && \ | ||
apt-get clean | ||
|
||
|
||
# C# | ||
RUN dotnet tool install csharpier --version 0.30.6 -g | ||
|
||
# # Rust | ||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \ | ||
-y \ | ||
--profile minimal \ | ||
--no-modify-path \ | ||
--no-update-default-toolchain \ | ||
--default-toolchain nightly-2025-02-27 \ | ||
--component rustfmt | ||
|
||
|
||
# Javascript | ||
COPY --from=ghcr.io/biomejs/biome:1.9.4 /usr/local/bin/biome /usr/bin/biome | ||
|
||
# Python | ||
COPY --from=ghcr.io/astral-sh/ruff:0.9.8 /ruff /usr/bin/ruff | ||
|
||
# Java | ||
RUN echo "25157797a0a972c2290b5bc71530c4f7ad646458025e3484412a6e5a9b8c9aa6 google-java-format-1.25.2-all-deps.jar" > google-java-format-1.25.2-all-deps.jar.sha256 && \ | ||
curl -fsSL --output google-java-format-1.25.2-all-deps.jar "https://github.com/google/google-java-format/releases/download/v1.25.2/google-java-format-1.25.2-all-deps.jar" && \ | ||
sha256sum google-java-format-1.25.2-all-deps.jar.sha256 -c && \ | ||
rm google-java-format-1.25.2-all-deps.jar.sha256 && \ | ||
mv google-java-format-1.25.2-all-deps.jar /usr/bin/ && \ | ||
echo '#!/usr/bin/bash\njava -jar /usr/bin/google-java-format-1.25.2-all-deps.jar $@' > /usr/bin/google-java-format && \ | ||
chmod +x /usr/bin/google-java-format | ||
|
||
# Kotlin | ||
RUN echo "5e7eb28a0b2006d1cefbc9213bfc73a8191ec2f85d639ec4fc4ec0cd04212e82 ktfmt-0.54-jar-with-dependencies.jar" > ktfmt-0.54-jar-with-dependencies.jar.sha256 && \ | ||
curl -fsSL --output ktfmt-0.54-jar-with-dependencies.jar "https://github.com/facebook/ktfmt/releases/download/v0.54/ktfmt-0.54-jar-with-dependencies.jar" && \ | ||
sha256sum ktfmt-0.54-jar-with-dependencies.jar.sha256 -c && \ | ||
rm ktfmt-0.54-jar-with-dependencies.jar.sha256 && \ | ||
mv ktfmt-0.54-jar-with-dependencies.jar /usr/bin/ && \ | ||
echo '#!/usr/bin/bash\njava -jar /usr/bin/ktfmt-0.54-jar-with-dependencies.jar $@' > /usr/bin/ktfmt && \ | ||
chmod +x /usr/bin/ktfmt | ||
|
||
# Go | ||
COPY --from=goimports-builder /go/bin/goimports /usr/bin | ||
COPY --from=goimports-builder /usr/local/go/bin/gofmt /usr/bin | ||
|
||
# openapi-codegen | ||
COPY --from=openapi-codegen-builder /app/target/release/openapi-codegen /usr/bin/ | ||
|
||
# Ruby | ||
COPY --from=rubyfmt-builder /app/target/release/rubyfmt-main /usr/bin/rubyfmt | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ use crate::{ | |
postprocessing::Postprocessor, | ||
template, | ||
types::Types, | ||
GenerateFlags, | ||
}; | ||
|
||
#[derive(Default, Deserialize)] | ||
|
@@ -29,7 +30,7 @@ pub(crate) fn generate( | |
types: Types, | ||
tpl_name: String, | ||
output_dir: &Utf8Path, | ||
no_postprocess: bool, | ||
flags: GenerateFlags, | ||
) -> anyhow::Result<()> { | ||
let (name_without_jinja_suffix, tpl_path) = match tpl_name.strip_suffix(".jinja") { | ||
Some(basename) => (basename, &tpl_name), | ||
|
@@ -70,7 +71,7 @@ pub(crate) fn generate( | |
tpl_file_ext, | ||
output_dir, | ||
postprocessor: &postprocessor, | ||
no_postprocess, | ||
flags, | ||
}; | ||
|
||
match tpl_kind { | ||
|
@@ -80,7 +81,7 @@ pub(crate) fn generate( | |
TemplateKind::Summary => generator.generate_summary(types, api)?, | ||
} | ||
|
||
if !no_postprocess { | ||
if !flags.no_postprocess { | ||
postprocessor.run_postprocessor(); | ||
} | ||
|
||
|
@@ -92,7 +93,7 @@ struct Generator<'a> { | |
tpl_file_ext: &'a str, | ||
output_dir: &'a Utf8Path, | ||
postprocessor: &'a Postprocessor, | ||
no_postprocess: bool, | ||
flags: GenerateFlags, | ||
} | ||
|
||
impl Generator<'_> { | ||
|
@@ -172,11 +173,15 @@ impl Generator<'_> { | |
}; | ||
|
||
let file_path = self.output_dir.join(format!("{basename}.{tpl_file_ext}")); | ||
|
||
if !self.output_dir.exists() { | ||
fs::create_dir_all(self.output_dir)?; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we actually have to check whether the dir exists first? I would expect that calling Also can't this happen further up the call tree? So we run it once per invocation, rather than once per template invocation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Just tested it and you are right, not sure why the docs mention this
Good call, let me move it up the call tree |
||
let out_file = BufWriter::new(File::create(&file_path)?); | ||
|
||
self.tpl.render_to_write(ctx, out_file)?; | ||
|
||
if !self.no_postprocess { | ||
if !self.flags.no_postprocess { | ||
self.postprocessor.add_path(&file_path); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Down the line we should probably do releases for
openapi-codegen
and its Docker image, but for now this seems like a good start 👍