Refactor source of Git commit build info (closes #661)
This commit is contained in:
parent
b624405b0c
commit
8b94c62697
|
|
@ -0,0 +1,22 @@
|
|||
use std::process::Command;
|
||||
|
||||
const GRAPHITE_RELEASE_SERIES: &str = "Alpha Milestone 1";
|
||||
|
||||
fn main() {
|
||||
// Execute a Git command for its stdout. Early exit if it fails for any of the possible reasons.
|
||||
let try_git_command = |args: &[&str]| -> Option<String> {
|
||||
let git_output = Command::new("git").args(args).output().ok()?;
|
||||
let maybe_empty = String::from_utf8(git_output.stdout).ok()?;
|
||||
let command_result = (!maybe_empty.is_empty()).then(|| maybe_empty)?;
|
||||
Some(command_result)
|
||||
};
|
||||
// Execute a Git command for its output. Return "unknown" if it fails for any of the possible reasons.
|
||||
let git_command = |args| -> String { try_git_command(args).unwrap_or_else(|| String::from("unknown")) };
|
||||
|
||||
// Rather than printing to any terminal, these commands set environment variables in the Cargo toolchain.
|
||||
// They are accessed with the `env!("...")` macro in the codebase.
|
||||
println!("cargo:rustc-env=GRAPHITE_GIT_COMMIT_DATE={}", git_command(&["log", "-1", "--format=%cd"]));
|
||||
println!("cargo:rustc-env=GRAPHITE_GIT_COMMIT_HASH={}", git_command(&["rev-parse", "HEAD"]));
|
||||
println!("cargo:rustc-env=GRAPHITE_GIT_COMMIT_BRANCH={}", git_command(&["rev-parse", "--abbrev-ref", "HEAD"]));
|
||||
println!("cargo:rustc-env=GRAPHITE_RELEASE_SERIES={}", GRAPHITE_RELEASE_SERIES);
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Provides metadata about the build environment.
|
||||
///
|
||||
/// This data is viewable in the editor via the [`crate::dialog::AboutGraphite`] dialog.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct BuildMetadata {
|
||||
pub release: String,
|
||||
pub timestamp: String,
|
||||
pub hash: String,
|
||||
pub branch: String,
|
||||
}
|
||||
|
||||
impl Default for BuildMetadata {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
release: "unknown".to_string(),
|
||||
timestamp: "unknown".to_string(),
|
||||
hash: "unknown".to_string(),
|
||||
branch: "unknown".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildMetadata {
|
||||
pub fn release_series(&self) -> String {
|
||||
format!("Release Series: {}", self.release)
|
||||
}
|
||||
|
||||
pub fn commit_info(&self) -> String {
|
||||
format!("{}\n{}\n{}", self.commit_timestamp(), self.commit_hash(), self.commit_branch())
|
||||
}
|
||||
|
||||
pub fn commit_timestamp(&self) -> String {
|
||||
format!("Date: {}", self.timestamp)
|
||||
}
|
||||
|
||||
pub fn commit_hash(&self) -> String {
|
||||
format!("Hash: {}", self.hash)
|
||||
}
|
||||
|
||||
pub fn commit_branch(&self) -> String {
|
||||
format!("Branch: {}", self.branch)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,14 +8,11 @@ use crate::workspace::WorkspaceMessageHandler;
|
|||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use super::BuildMetadata;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Dispatcher {
|
||||
message_queue: VecDeque<Message>,
|
||||
pub responses: Vec<FrontendMessage>,
|
||||
message_handlers: DispatcherMessageHandlers,
|
||||
build_metadata: BuildMetadata,
|
||||
}
|
||||
|
||||
#[remain::sorted]
|
||||
|
|
@ -75,7 +72,7 @@ impl Dispatcher {
|
|||
Dialog(message) => {
|
||||
self.message_handlers
|
||||
.dialog_message_handler
|
||||
.process_action(message, (&self.build_metadata, &self.message_handlers.portfolio_message_handler), &mut self.message_queue);
|
||||
.process_action(message, &self.message_handlers.portfolio_message_handler, &mut self.message_queue);
|
||||
}
|
||||
Frontend(message) => {
|
||||
// Image and font loading should be immediately handled
|
||||
|
|
@ -120,9 +117,6 @@ impl Dispatcher {
|
|||
.workspace_message_handler
|
||||
.process_action(message, &self.message_handlers.input_preprocessor_message_handler, &mut self.message_queue);
|
||||
}
|
||||
|
||||
#[remain::unsorted]
|
||||
PopulateBuildMetadata { new } => self.build_metadata = new,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use super::BuildMetadata;
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use graphite_proc_macros::*;
|
||||
|
|
@ -41,9 +40,6 @@ pub enum Message {
|
|||
Tool(ToolMessage),
|
||||
#[child]
|
||||
Workspace(WorkspaceMessage),
|
||||
|
||||
#[remain::unsorted]
|
||||
PopulateBuildMetadata { new: BuildMetadata },
|
||||
}
|
||||
|
||||
impl Message {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
mod build_metadata;
|
||||
pub mod dispatcher;
|
||||
pub mod message;
|
||||
pub mod message_handler;
|
||||
|
||||
pub use crate::communication::dispatcher::*;
|
||||
pub use crate::input::InputPreprocessorMessageHandler;
|
||||
pub use build_metadata::BuildMetadata;
|
||||
|
||||
use rand_chacha::rand_core::{RngCore, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
|
|
|
|||
|
|
@ -7,14 +7,15 @@ use super::{ExportDialogUpdate, NewDocumentDialogUpdate};
|
|||
#[impl_message(Message, Dialog)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum DialogMessage {
|
||||
// Sub-messages
|
||||
#[remain::unsorted]
|
||||
#[child]
|
||||
ExportDialog(ExportDialogUpdate),
|
||||
|
||||
#[remain::unsorted]
|
||||
#[child]
|
||||
NewDocumentDialog(NewDocumentDialogUpdate),
|
||||
|
||||
// Messages
|
||||
CloseAllDocumentsWithConfirmation,
|
||||
CloseDialogAndThen {
|
||||
followup: Box<Message>,
|
||||
|
|
@ -24,6 +25,9 @@ pub enum DialogMessage {
|
|||
description: String,
|
||||
},
|
||||
RequestAboutGraphiteDialog,
|
||||
RequestAboutGraphiteDialogWithLocalizedCommitDate {
|
||||
localized_commit_date: String,
|
||||
},
|
||||
RequestComingSoonDialog {
|
||||
issue: Option<i32>,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use crate::communication::BuildMetadata;
|
||||
use crate::document::PortfolioMessageHandler;
|
||||
use crate::layout::{layout_message::LayoutTarget, widgets::PropertyHolder};
|
||||
use crate::message_prelude::*;
|
||||
|
|
@ -11,9 +10,9 @@ pub struct DialogMessageHandler {
|
|||
new_document_dialog: NewDocument,
|
||||
}
|
||||
|
||||
impl MessageHandler<DialogMessage, (&BuildMetadata, &PortfolioMessageHandler)> for DialogMessageHandler {
|
||||
impl MessageHandler<DialogMessage, &PortfolioMessageHandler> for DialogMessageHandler {
|
||||
#[remain::check]
|
||||
fn process_action(&mut self, message: DialogMessage, (build_metadata, portfolio): (&BuildMetadata, &PortfolioMessageHandler), responses: &mut VecDeque<Message>) {
|
||||
fn process_action(&mut self, message: DialogMessage, portfolio: &PortfolioMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
#[remain::sorted]
|
||||
match message {
|
||||
#[remain::unsorted]
|
||||
|
|
@ -36,9 +35,16 @@ impl MessageHandler<DialogMessage, (&BuildMetadata, &PortfolioMessageHandler)> f
|
|||
responses.push_back(FrontendMessage::DisplayDialog { icon: "Warning".to_string() }.into());
|
||||
}
|
||||
DialogMessage::RequestAboutGraphiteDialog => {
|
||||
let about_graphite = AboutGraphite {
|
||||
build_metadata: build_metadata.clone(),
|
||||
};
|
||||
responses.push_back(
|
||||
FrontendMessage::TriggerAboutGraphiteLocalizedCommitDate {
|
||||
commit_date: env!("GRAPHITE_GIT_COMMIT_DATE").into(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
DialogMessage::RequestAboutGraphiteDialogWithLocalizedCommitDate { localized_commit_date } => {
|
||||
let about_graphite = AboutGraphite { localized_commit_date };
|
||||
|
||||
about_graphite.register_properties(responses, LayoutTarget::DialogDetails);
|
||||
responses.push_back(FrontendMessage::DisplayDialog { icon: "GraphiteLogo".to_string() }.into());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use crate::{communication::BuildMetadata, layout::widgets::*, message_prelude::FrontendMessage};
|
||||
use crate::layout::widgets::*;
|
||||
use crate::message_prelude::FrontendMessage;
|
||||
use crate::misc::build_metadata::{commit_info_localized, release_series};
|
||||
|
||||
/// A dialog for displaying information on [`BuildMetadata`] viewable via `help -> about graphite` in the menu bar.
|
||||
/// A dialog for displaying information on [`BuildMetadata`] viewable via *Help* > *About Graphite* in the menu bar.
|
||||
pub struct AboutGraphite {
|
||||
pub build_metadata: BuildMetadata,
|
||||
pub localized_commit_date: String,
|
||||
}
|
||||
|
||||
impl PropertyHolder for AboutGraphite {
|
||||
|
|
@ -33,13 +35,13 @@ impl PropertyHolder for AboutGraphite {
|
|||
},
|
||||
LayoutRow::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: self.build_metadata.release_series(),
|
||||
value: release_series(),
|
||||
..Default::default()
|
||||
}))],
|
||||
},
|
||||
LayoutRow::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: self.build_metadata.commit_info(),
|
||||
value: commit_info_localized(self.localized_commit_date.as_str()),
|
||||
multiline: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ pub enum FrontendMessage {
|
|||
DisplayRemoveEditableTextbox,
|
||||
|
||||
// Trigger prefix: cause a browser API to do something
|
||||
TriggerAboutGraphiteLocalizedCommitDate { commit_date: String },
|
||||
TriggerFileDownload { document: String, name: String },
|
||||
TriggerFileUpload,
|
||||
TriggerFontLoad { font_file_url: String },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
/// Provides metadata about the build environment.
|
||||
///
|
||||
/// This data is viewable in the editor via the [`crate::dialog::AboutGraphite`] dialog.
|
||||
|
||||
pub fn release_series() -> String {
|
||||
format!("Release Series: {}", env!("GRAPHITE_RELEASE_SERIES"))
|
||||
}
|
||||
|
||||
pub fn commit_info() -> String {
|
||||
format!("{}\n{}\n{}", commit_timestamp(), commit_hash(), commit_branch())
|
||||
}
|
||||
|
||||
pub fn commit_info_localized(localized_commit_date: &str) -> String {
|
||||
format!("{}\n{}\n{}", commit_timestamp_localized(localized_commit_date), commit_hash(), commit_branch())
|
||||
}
|
||||
|
||||
pub fn commit_timestamp() -> String {
|
||||
format!("Date: {}", env!("GRAPHITE_GIT_COMMIT_DATE"))
|
||||
}
|
||||
|
||||
pub fn commit_timestamp_localized(localized_commit_date: &str) -> String {
|
||||
format!("Date: {}", localized_commit_date)
|
||||
}
|
||||
|
||||
pub fn commit_hash() -> String {
|
||||
format!("Hash: {}", &env!("GRAPHITE_GIT_COMMIT_HASH")[..8])
|
||||
}
|
||||
|
||||
pub fn commit_branch() -> String {
|
||||
format!("Branch: {}", env!("GRAPHITE_GIT_COMMIT_BRANCH"))
|
||||
}
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
pub mod build_metadata;
|
||||
pub mod derivable_custom_traits;
|
||||
pub mod hints;
|
||||
pub mod test_utils;
|
||||
|
||||
mod error;
|
||||
|
||||
pub use error::EditorError;
|
||||
pub use hints::*;
|
||||
pub use macros::*;
|
||||
|
||||
mod error;
|
||||
|
|
|
|||
|
|
@ -258,10 +258,10 @@ img {
|
|||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { createBuildMetadataManager } from "@/io-managers/build-metadata";
|
||||
import { createClipboardManager } from "@/io-managers/clipboard";
|
||||
import { createHyperlinkManager } from "@/io-managers/hyperlinks";
|
||||
import { createInputManager } from "@/io-managers/input";
|
||||
import { createLocalizationManager } from "@/io-managers/localization";
|
||||
import { createPanicManager } from "@/io-managers/panic";
|
||||
import { createPersistenceManager } from "@/io-managers/persistence";
|
||||
import { createDialogState, DialogState } from "@/state-providers/dialog";
|
||||
|
|
@ -276,10 +276,10 @@ import LayoutRow from "@/components/layout/LayoutRow.vue";
|
|||
import MainWindow from "@/components/window/MainWindow.vue";
|
||||
|
||||
const managerDestructors: {
|
||||
createBuildMetadataManager?: () => void;
|
||||
createClipboardManager?: () => void;
|
||||
createHyperlinkManager?: () => void;
|
||||
createInputManager?: () => void;
|
||||
createLocalizationManager?: () => void;
|
||||
createPanicManager?: () => void;
|
||||
createPersistenceManager?: () => void;
|
||||
} = {};
|
||||
|
|
@ -332,10 +332,10 @@ export default defineComponent({
|
|||
async mounted() {
|
||||
// Initialize managers, which are isolated systems that subscribe to backend messages to link them to browser API functionality (like JS events, IndexedDB, etc.)
|
||||
Object.assign(managerDestructors, {
|
||||
createBuildMetadataManager: createBuildMetadataManager(this.editor),
|
||||
createClipboardManager: createClipboardManager(this.editor),
|
||||
createHyperlinkManager: createHyperlinkManager(this.editor),
|
||||
createInputManager: createInputManager(this.editor, this.$el.parentElement, this.dialog, this.portfolio, this.fullscreen),
|
||||
createLocalizationManager: createLocalizationManager(this.editor),
|
||||
createPanicManager: createPanicManager(this.editor, this.dialog),
|
||||
createPersistenceManager: await createPersistenceManager(this.editor, this.portfolio),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import { Editor } from "@/wasm-communication/editor";
|
||||
|
||||
// Gets metadata populated in the `process.env` namespace by code in `frontend/vue.config.js`.
|
||||
// TODO: Move that functionality to a build.rs file so our web build system is more lightweight.
|
||||
export function createBuildMetadataManager(editor: Editor): void {
|
||||
// Release
|
||||
const release = process.env.VUE_APP_RELEASE_SERIES;
|
||||
|
||||
// Timestamp
|
||||
const date = new Date(process.env.VUE_APP_COMMIT_DATE || "");
|
||||
const timezoneName = Intl.DateTimeFormat(undefined, { timeZoneName: "long" })
|
||||
.formatToParts(new Date())
|
||||
.find((part) => part.type === "timeZoneName");
|
||||
const dateString = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
||||
const timeString = `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
||||
const timezoneNameString = timezoneName?.value;
|
||||
const timestamp = `${dateString} ${timeString} ${timezoneNameString}`;
|
||||
|
||||
// Hash
|
||||
const hash = (process.env.VUE_APP_COMMIT_HASH || "").substring(0, 8);
|
||||
|
||||
// Branch
|
||||
const branch = process.env.VUE_APP_COMMIT_BRANCH;
|
||||
|
||||
editor.instance.populate_build_metadata(release || "", timestamp, hash, branch || "");
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { Editor } from "@/wasm-communication/editor";
|
||||
import { TriggerAboutGraphiteLocalizedCommitDate } from "@/wasm-communication/messages";
|
||||
|
||||
export function createLocalizationManager(editor: Editor): void {
|
||||
function localizeTimestamp(utc: string): string {
|
||||
// Timestamp
|
||||
const date = new Date(utc);
|
||||
if (Number.isNaN(date.getTime())) return utc;
|
||||
|
||||
const timezoneName = Intl.DateTimeFormat(undefined, { timeZoneName: "long" })
|
||||
.formatToParts(new Date())
|
||||
.find((part) => part.type === "timeZoneName");
|
||||
|
||||
const dateString = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
||||
const timeString = `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
||||
const timezoneNameString = timezoneName?.value;
|
||||
return `${dateString} ${timeString} ${timezoneNameString}`;
|
||||
}
|
||||
|
||||
// Subscribe to process backend event
|
||||
editor.subscriptions.subscribeJsMessage(TriggerAboutGraphiteLocalizedCommitDate, (triggerAboutGraphiteLocalizedCommitDate) => {
|
||||
const localized = localizeTimestamp(triggerAboutGraphiteLocalizedCommitDate.commit_date);
|
||||
editor.instance.request_about_graphite_dialog_with_localized_commit_date(localized);
|
||||
});
|
||||
}
|
||||
|
|
@ -522,6 +522,10 @@ export class TriggerTextCopy extends JsMessage {
|
|||
readonly copy_text!: string;
|
||||
}
|
||||
|
||||
export class TriggerAboutGraphiteLocalizedCommitDate extends JsMessage {
|
||||
readonly commit_date!: string;
|
||||
}
|
||||
|
||||
export class TriggerViewportResize extends JsMessage {}
|
||||
|
||||
// `any` is used since the type of the object should be known from the Rust side
|
||||
|
|
@ -546,6 +550,7 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
TriggerRasterDownload,
|
||||
TriggerTextCommit,
|
||||
TriggerTextCopy,
|
||||
TriggerAboutGraphiteLocalizedCommitDate,
|
||||
TriggerViewportResize,
|
||||
TriggerVisitLink,
|
||||
UpdateActiveDocument,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires, no-console */
|
||||
const { execSync, spawnSync } = require("child_process");
|
||||
const { spawnSync } = require("child_process");
|
||||
const path = require("path");
|
||||
|
||||
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
||||
const LicenseCheckerWebpackPlugin = require("license-checker-webpack-plugin");
|
||||
|
||||
process.env.VUE_APP_COMMIT_DATE = execSync("git log -1 --format=%cd", { encoding: "utf-8" }).trim();
|
||||
process.env.VUE_APP_COMMIT_HASH = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
|
||||
process.env.VUE_APP_COMMIT_BRANCH = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8" }).trim();
|
||||
process.env.VUE_APP_RELEASE_SERIES = "Alpha Milestone 1";
|
||||
|
||||
module.exports = {
|
||||
lintOnSave: "warning",
|
||||
// https://cli.vuejs.org/guide/webpack.html
|
||||
|
|
@ -91,7 +86,7 @@ function formatThirdPartyLicenses(jsLicenses) {
|
|||
if (rustLicenses === null) {
|
||||
// This is probably caused by cargo about not being installed
|
||||
console.error(`
|
||||
Could not run 'cargo about', which is required to generate license information.
|
||||
Could not run \`cargo about\`, which is required to generate license information.
|
||||
To install cargo-about on your system, you can run:
|
||||
cargo install cargo-about
|
||||
License information is required on production builds. Aborting.`);
|
||||
|
|
@ -178,18 +173,18 @@ ${license.licenseText}
|
|||
}
|
||||
|
||||
function generateRustLicenses() {
|
||||
console.info("Generating license information for rust code");
|
||||
console.info("Generating license information for Rust code");
|
||||
const { stdout, stderr, status } = spawnSync("cargo", ["about", "generate", "about.hbs"], {
|
||||
cwd: path.join(__dirname, ".."),
|
||||
encoding: "utf8",
|
||||
timeout: 60000, // one minute
|
||||
timeout: 60000, // One minute
|
||||
shell: true,
|
||||
windowsHide: true, // hide the DOS window on windows
|
||||
windowsHide: true, // Hide the terminal on Windows
|
||||
});
|
||||
|
||||
if (status !== 0) {
|
||||
if (status !== 101) {
|
||||
// cargo returns 101 when the subcommand wasn't found
|
||||
// Cargo returns 101 when the subcommand wasn't found
|
||||
console.error("cargo-about failed", status, stderr);
|
||||
}
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -239,14 +239,13 @@ impl JsEditorHandle {
|
|||
self.dispatch(message);
|
||||
}
|
||||
|
||||
pub fn populate_build_metadata(&self, release: String, timestamp: String, hash: String, branch: String) {
|
||||
let new = editor::communication::BuildMetadata { release, timestamp, hash, branch };
|
||||
let message = Message::PopulateBuildMetadata { new };
|
||||
pub fn request_about_graphite_dialog(&self) {
|
||||
let message = DialogMessage::RequestAboutGraphiteDialog;
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
||||
pub fn request_about_graphite_dialog(&self) {
|
||||
let message = DialogMessage::RequestAboutGraphiteDialog;
|
||||
pub fn request_about_graphite_dialog_with_localized_commit_date(&self, localized_commit_date: String) {
|
||||
let message = DialogMessage::RequestAboutGraphiteDialogWithLocalizedCommitDate { localized_commit_date };
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue