feat: Display full recipe with syntax highlighting (#166)
As I was re-arranging my recipe files, I needed a way to ensure that the
order of my recipe is correct without having to read through the
generated `Containerfile`. So I added a `-d`/`--display-full-recipe` arg
to `template` that will print out all of your modules in the order
defined by following the `from-file` property.
```
$> bluebuild template --help
Generate a Containerfile from a recipe
Usage: bluebuild template [OPTIONS] [RECIPE]
Arguments:
[RECIPE]
The recipe file to create a template from
Options:
-o, --output <OUTPUT>
File to output to instead of STDOUT
--registry <REGISTRY>
The registry domain the image will be published to.
This is used for modules that need to know where the image is being published (i.e. the signing module).
--registry-namespace <REGISTRY_NAMESPACE>
The registry namespace the image will be published to.
This is used for modules that need to know where the image is being published (i.e. the signing module).
-d, --display-full-recipe
Instead of creating a Containerfile, display the full recipe after traversing all `from-file` properties.
This can be used to help debug the order you defined your recipe.
-t, --syntax-theme <SYNTAX_THEME>
Choose a theme for the syntax highlighting for the Containerfile or Yaml.
The default is `mocha-dark`.
[possible values: mocha-dark, ocean-dark, ocean-light, eighties-dark, inspired-github, solarized-dark, solarized-light]
-s, --squash
Puts the build in a `squash-stage` and COPY's the results to the final stage as one layer.
WARN: This doesn't work with the docker driver as it has been deprecated.
NOTE: Squash has a performance benefit for the newer versions of podman and buildah.
-B, --build-driver <BUILD_DRIVER>
Select which driver to use to build your image
[possible values: buildah, podman, docker]
-v, --verbose...
Increase logging verbosity
-I, --inspect-driver <INSPECT_DRIVER>
Select which driver to use to inspect images
[possible values: skopeo, podman, docker]
-q, --quiet...
Decrease logging verbosity
-h, --help
Print help (see a summary with '-h')
```
Preview of Containerfile/Dockerfile syntax highlighting:

Preview of Yaml highlighting:

This commit is contained in:
parent
a7503d561e
commit
92150693d4
9 changed files with 477 additions and 9 deletions
|
|
@ -9,8 +9,10 @@ license.workspace = true
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2.14"
|
||||
directories = "5"
|
||||
process_control = { version = "4.0.3", features = ["crossbeam-channel"] }
|
||||
syntect = "5.2.0"
|
||||
which = "6"
|
||||
|
||||
anyhow.workspace = true
|
||||
|
|
@ -23,6 +25,13 @@ serde.workspace = true
|
|||
serde_yaml.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
[dependencies.clap]
|
||||
workspace = true
|
||||
features = ["derive"]
|
||||
|
||||
[build-dependencies]
|
||||
syntect = "5.2.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
|
|
|
|||
24
utils/build.rs
Normal file
24
utils/build.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use syntect::dumps;
|
||||
use syntect::parsing::syntax_definition::SyntaxDefinition;
|
||||
use syntect::parsing::SyntaxSetBuilder;
|
||||
|
||||
fn main() {
|
||||
let mut ssb = SyntaxSetBuilder::new();
|
||||
ssb.add(
|
||||
SyntaxDefinition::load_from_str(
|
||||
include_str!("highlights/Dockerfile.sublime-syntax"),
|
||||
true,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let ss = ssb.build();
|
||||
|
||||
dumps::dump_to_uncompressed_file(
|
||||
&ss,
|
||||
PathBuf::from(env::var("OUT_DIR").unwrap()).join("docker_syntax.bin"),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
141
utils/highlights/Dockerfile.sublime-syntax
Normal file
141
utils/highlights/Dockerfile.sublime-syntax
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
%YAML 1.2
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright 2014 Asbjorn Enge
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
# https://github.com/asbjornenge/Docker.tmbundle
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: Dockerfile
|
||||
scope: source.dockerfile
|
||||
|
||||
file_extensions:
|
||||
- Dockerfile
|
||||
- dockerfile
|
||||
|
||||
hidden_file_extensions:
|
||||
- .Dockerfile
|
||||
|
||||
first_line_match: ^\s*(?i:(from(?!\s+\S+\s+import)|arg))\s+
|
||||
|
||||
variables:
|
||||
onbuild_directive: (?i:(onbuild)\s+)?
|
||||
onbuild_commands_directive:
|
||||
"{{onbuild_directive}}(?i:add|arg|env|expose|healthcheck|label|run|shell|stopsignal|user|volume|workdir)"
|
||||
nononbuild_commands_directive: (?i:maintainer)
|
||||
runtime_directive: "{{onbuild_directive}}(?i:cmd|entrypoint)"
|
||||
from_directive: (?i:(from))\s+[^\s:@]+(?:[:@](\S+))?(?:\s+(?i:(as))\s+(\S+))?
|
||||
copy_directive: ({{onbuild_directive}}(?i:copy))(?:\s+--from=(\S+))?
|
||||
|
||||
contexts:
|
||||
main:
|
||||
- include: comments
|
||||
- match: ^(?i:arg)\s
|
||||
scope: keyword.control.dockerfile
|
||||
- include: from
|
||||
|
||||
from:
|
||||
- match: ^{{from_directive}}
|
||||
captures:
|
||||
1: keyword.control.dockerfile
|
||||
2: entity.name.enum.tag-digest
|
||||
3: keyword.control.dockerfile
|
||||
4: variable.stage-name
|
||||
push: body
|
||||
|
||||
body:
|
||||
- include: comments
|
||||
- include: directives
|
||||
- include: invalid
|
||||
- include: from
|
||||
|
||||
directives:
|
||||
- match: ^\s*{{onbuild_commands_directive}}\s
|
||||
captures:
|
||||
0: keyword.control.dockerfile
|
||||
1: keyword.other.special-method.dockerfile
|
||||
push: args
|
||||
- match: ^\s*{{nononbuild_commands_directive}}\s
|
||||
scope: keyword.control.dockerfile
|
||||
push: args
|
||||
- match: ^\s*{{copy_directive}}\s
|
||||
captures:
|
||||
1: keyword.control.dockerfile
|
||||
2: keyword.other.special-method.dockerfile
|
||||
3: variable.stage-name
|
||||
push: args
|
||||
- match: ^\s*{{runtime_directive}}\s
|
||||
captures:
|
||||
0: keyword.operator.dockerfile
|
||||
1: keyword.other.special-method.dockerfile
|
||||
push: args
|
||||
|
||||
escaped-char:
|
||||
- match: \\.
|
||||
scope: constant.character.escaped.dockerfile
|
||||
|
||||
args:
|
||||
- include: comments
|
||||
- include: escaped-char
|
||||
- match: ^\s*$
|
||||
- match: \\\s+$
|
||||
- match: \n
|
||||
pop: true
|
||||
- match: '"'
|
||||
scope: punctuation.definition.string.begin.dockerfile
|
||||
push: double_quote_string
|
||||
- match: "'"
|
||||
scope: punctuation.definition.string.begin.dockerfile
|
||||
push: single_quote_string
|
||||
|
||||
double_quote_string:
|
||||
- meta_scope: string.quoted.double.dockerfile
|
||||
- include: escaped-char
|
||||
- match: ^\s*$
|
||||
- match: \\\s+$
|
||||
- match: \n
|
||||
set: invalid
|
||||
- match: '"'
|
||||
scope: punctuation.definition.string.end.dockerfile
|
||||
pop: true
|
||||
|
||||
single_quote_string:
|
||||
- meta_scope: string.quoted.single.dockerfile
|
||||
- include: escaped-char
|
||||
- match: ^\s*$
|
||||
- match: \\\s+$
|
||||
- match: \n
|
||||
set: invalid
|
||||
- match: "'"
|
||||
scope: punctuation.definition.string.end.dockerfile
|
||||
pop: true
|
||||
|
||||
comments:
|
||||
- match: ^(\s*)((#).*$\n?)
|
||||
comment: comment.line
|
||||
captures:
|
||||
1: punctuation.whitespace.comment.leading.dockerfile
|
||||
2: comment.dockerfile
|
||||
3: punctuation.definition.comment.dockerfile
|
||||
|
||||
invalid:
|
||||
- match: ^[^A-Z\n](.*)$
|
||||
scope: invalid
|
||||
set: body
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod command_output;
|
||||
pub mod constants;
|
||||
pub mod logging;
|
||||
pub mod syntax_highlighting;
|
||||
|
||||
use std::{ffi::OsStr, io::Write, path::PathBuf, process::Command, thread, time::Duration};
|
||||
|
||||
|
|
|
|||
85
utils/src/syntax_highlighting.rs
Normal file
85
utils/src/syntax_highlighting.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use clap::ValueEnum;
|
||||
use log::trace;
|
||||
use serde::ser::Serialize;
|
||||
use syntect::{dumps, easy::HighlightLines, highlighting::ThemeSet, parsing::SyntaxSet};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, ValueEnum)]
|
||||
pub enum DefaultThemes {
|
||||
#[default]
|
||||
MochaDark,
|
||||
OceanDark,
|
||||
OceanLight,
|
||||
EightiesDark,
|
||||
InspiredGithub,
|
||||
SolarizedDark,
|
||||
SolarizedLight,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DefaultThemes {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match *self {
|
||||
Self::MochaDark => "base16-mocha.dark",
|
||||
Self::OceanDark => "base16-ocean.dark",
|
||||
Self::OceanLight => "base16-ocean.light",
|
||||
Self::EightiesDark => "base16-eighties.dark",
|
||||
Self::InspiredGithub => "InspiredGithub",
|
||||
Self::SolarizedDark => "Solarized (dark)",
|
||||
Self::SolarizedLight => "Solarized (light)",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints the file with syntax highlighting.
|
||||
///
|
||||
/// # Errors
|
||||
/// Will error if the theme doesn't exist, the syntax doesn't exist, or the file
|
||||
/// failed to serialize.
|
||||
pub fn print(file: &str, file_type: &str, theme: Option<DefaultThemes>) -> Result<()> {
|
||||
trace!("syntax_highlighting::print({file}, {file_type}, {theme:?})");
|
||||
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
let ss: SyntaxSet = if file_type == "dockerfile" || file_type == "Dockerfile" {
|
||||
dumps::from_uncompressed_data(include_bytes!(concat!(
|
||||
env!("OUT_DIR"),
|
||||
"/docker_syntax.bin"
|
||||
)))?
|
||||
} else {
|
||||
SyntaxSet::load_defaults_newlines()
|
||||
};
|
||||
let ts = ThemeSet::load_defaults();
|
||||
|
||||
let syntax = ss
|
||||
.find_syntax_by_extension(file_type)
|
||||
.ok_or_else(|| anyhow!("Failed to get syntax"))?;
|
||||
let mut h = HighlightLines::new(
|
||||
syntax,
|
||||
ts.themes
|
||||
.get(theme.unwrap_or_default().to_string().as_str())
|
||||
.ok_or_else(|| anyhow!("Failed to get highlight theme"))?,
|
||||
);
|
||||
for line in file.lines() {
|
||||
let ranges = h.highlight_line(line, &ss)?;
|
||||
let escaped = syntect::util::as_24_bit_terminal_escaped(&ranges, false);
|
||||
println!("{escaped}");
|
||||
}
|
||||
println!("\x1b[0m");
|
||||
} else {
|
||||
println!("{file}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Takes a serializable struct and prints it out with syntax highlighting.
|
||||
///
|
||||
/// # Errors
|
||||
/// Will error if the theme doesn't exist, the syntax doesn't exist, or the file
|
||||
/// failed to serialize.
|
||||
pub fn print_ser<T: Serialize>(
|
||||
file: &T,
|
||||
file_type: &str,
|
||||
theme: Option<DefaultThemes>,
|
||||
) -> Result<()> {
|
||||
print(serde_yaml::to_string(file)?.as_str(), file_type, theme)?;
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue