content: reformat with prettier

This commit is contained in:
Harsh Shandilya 2022-01-07 20:16:34 +05:30
parent 7bef839204
commit 8d1734559a
29 changed files with 221 additions and 199 deletions

View File

@ -30,14 +30,35 @@ After coming across [this list](https://github.com/budparr/awesome-hugo#theme-co
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@msfjarvis" />
<meta name="description" content="Optimize social media exposure with the right metadata for your site" />
<meta
name="description"
content="Optimize social media exposure with the right metadata for your site"
/>
<meta name="keywords" content="hugo,webdev,static sites," />
<meta property="og:url" content="https://msfjarvis.dev/posts/adding-social-metadata-to-your-hugo-sites/" />
<meta property="og:title" content="Adding social metadata to your Hugo sites &middot; Harsh Shandilya" />
<meta name="twitter:title" content="Adding social metadata to your Hugo sites &middot; Harsh Shandilya" />
<meta name="og:description" content="Optimize social media exposure with the right metadata for your site" />
<meta name="twitter:description" content="Optimize social media exposure with the right metadata for your site" />
<meta name="twitter:url" content="https://msfjarvis.dev/posts/adding-social-metadata-to-your-hugo-sites/" />
<meta
property="og:url"
content="https://msfjarvis.dev/posts/adding-social-metadata-to-your-hugo-sites/"
/>
<meta
property="og:title"
content="Adding social metadata to your Hugo sites &middot; Harsh Shandilya"
/>
<meta
name="twitter:title"
content="Adding social metadata to your Hugo sites &middot; Harsh Shandilya"
/>
<meta
name="og:description"
content="Optimize social media exposure with the right metadata for your site"
/>
<meta
name="twitter:description"
content="Optimize social media exposure with the right metadata for your site"
/>
<meta
name="twitter:url"
content="https://msfjarvis.dev/posts/adding-social-metadata-to-your-hugo-sites/"
/>
<meta name="twitter:image:src" content="android-chrome-512x512.webp" />
```

View File

@ -8,13 +8,13 @@ tags = ["relnotes", "oss", "android-password-store"]
title = "Android Password Store August release"
+++
Continuing this new tradition, here are the detailed release notes for the [v1.11.0](https://github.com/android-password-store/Android-Password-Store/releases/tag/v1.11.0) build of of Android Password Store that is going out right now on the Play Store and to F-Droid in the coming days. The overall focus of this release has been to improve UX and resolve bugs. Regular feature development has already resumed for next month's release where we'll be bringing [Android Keystore](https://source.android.com/security/keystore) backed SSH key generation as well as a rewritten OpenKeychain integration for SSH connections.
Continuing this new tradition, here are the detailed release notes for the [v1.11.0](https://github.com/android-password-store/Android-Password-Store/releases/tag/v1.11.0) build of of Android Password Store that is going out right now on the Play Store and to F-Droid in the coming days. The overall focus of this release has been to improve UX and resolve bugs. Regular feature development has already resumed for next month's release where we'll be bringing [Android Keystore](https://source.android.com/security/keystore) backed SSH key generation as well as a rewritten OpenKeychain integration for SSH connections.
# New features
## One URL field to rule them all
Previously you'd have to set the URL to your repository across multiple fields like username, server, repository name and what not. Annoying! These things make sense to us as developers, but users should not have to be dealing with all that complexity when all they want to do is enter a single URL. We've received numerous bug reports over time as a result of people misunderstanding and ultimately misconfiguring things when exposed to this hellscape. Thanks to some *amazing* work from Fabian, we now have a single URL field for users to fill into.
Previously you'd have to set the URL to your repository across multiple fields like username, server, repository name and what not. Annoying! These things make sense to us as developers, but users should not have to be dealing with all that complexity when all they want to do is enter a single URL. We've received numerous bug reports over time as a result of people misunderstanding and ultimately misconfiguring things when exposed to this hellscape. Thanks to some _amazing_ work from Fabian, we now have a single URL field for users to fill into.
![Single URL field in repository information](/uploads/aps-august-release-single-url-field.webp)

View File

@ -57,13 +57,13 @@ While still potentially finicky, we're now confident that this is ready to be sh
As always, there are a handful of Quality of Life changes to make the app more enjoyable to use:
- When retrying password authentication, the option to see what you're typing would be obscured by the error icon for wrong password. This has been remedied, and the error state will now be cleared as soon as you enter anything into the password field.
- Authentication modes will now be dynamically hidden and shown based on the URL's schema so you're aware of what methods you have for authentication for any given remote repository.
- Since decryption can sometimes take a couple seconds due to how OpenKeychain works, we now hide the action buttons at the top of the screen until the decrypt operation has completed since using the buttons before that can leave the app in an odd state.
- Users will be prompted if they need to provide a username in their URLs. For example, if your repository is at `https://github.com/john.doe/passwords`, you will have to change the URL to `https://john.doe@github.com/john.doe/passwords` for HTTPS authentication to work.
- If it appears that an SSH URL contains a custom port but does not specify the `ssh://` schema, the user will be prompted to accept a quickfix that does it for them.
- Pressing the save button is no longer necessary to save changes to authentication mode.
- TOTP values might sometimes be outdated because we always wait 30 seconds to generate a new one. Now the app will calculate the time left before the first generated value goes stale, generate a new one once it does, and then resume the 30 second cycle.
- When retrying password authentication, the option to see what you're typing would be obscured by the error icon for wrong password. This has been remedied, and the error state will now be cleared as soon as you enter anything into the password field.
- Authentication modes will now be dynamically hidden and shown based on the URL's schema so you're aware of what methods you have for authentication for any given remote repository.
- Since decryption can sometimes take a couple seconds due to how OpenKeychain works, we now hide the action buttons at the top of the screen until the decrypt operation has completed since using the buttons before that can leave the app in an odd state.
- Users will be prompted if they need to provide a username in their URLs. For example, if your repository is at `https://github.com/john.doe/passwords`, you will have to change the URL to `https://john.doe@github.com/john.doe/passwords` for HTTPS authentication to work.
- If it appears that an SSH URL contains a custom port but does not specify the `ssh://` schema, the user will be prompted to accept a quickfix that does it for them.
- Pressing the save button is no longer necessary to save changes to authentication mode.
- TOTP values might sometimes be outdated because we always wait 30 seconds to generate a new one. Now the app will calculate the time left before the first generated value goes stale, generate a new one once it does, and then resume the 30 second cycle.
There's definitely more fixes here, but we ended up rewriting, breaking and fixing so many things for this release that it's hard to tell what was actually broken in the previous release and what is just us fixing regressions during refactoring. We've been busy :)
@ -78,4 +78,3 @@ Our excellent Autofill capabilities are now bundled as a separate Android librar
Based on the issues raised in the repository and the support emails I've received, the maintainers have come to the conclusion that nearly all users who choose to store their pass repositories in their device storage or external SD card as opposed to the app's private, hidden directory are not users of Git and rely on solutions like Syncthing and Nextcloud to keep the repository in sync with their other devices.
As such, we are now in the process of removing Git support from these repositories. We've carefully evaluated how we want to do this, and have started with removing the ability to clone repositories to public storage in this release. If this doesn't blow up in our faces, we will be completing the transition in v1.13.0. If you believe the change adversely affects your usage of the app, we wanna know! Drop a comment on [GitHub](https://msfjarvis.dev/aps/issue/1118) and we will do our best to either propose an alternative for your use case or entirely scrap our plans if we discover that our initial inferences were misguided.

View File

@ -59,12 +59,12 @@ Cheeky.
This post is just a brief overview of how I went about setting things up for learning Zig. I intend to post more detailed blogs as I progress :)
[Zig]: https://ziglang.org
[Rust]: https://rust-lang.org
[documented their experience with Zig]: https://kevinlynagh.com/rust-zig/
[installing Zig]: https://ziglang.org/learn/getting-started/#installing-zig
[zig]: https://ziglang.org
[rust]: https://rust-lang.org
[documented their experience with zig]: https://kevinlynagh.com/rust-zig/
[installing zig]: https://ziglang.org/learn/getting-started/#installing-zig
[stable release cadence]: https://ziglang.org/learn/getting-started/#tagged-release-or-nightly-build
[Nix]: https://nixos.org/
[nix]: https://nixos.org/
[zls]: https://github.com/zigtools/zls
[ziglearn.org]: https://ziglearn.org/
[rustlings]: https://github.com/rust-lang/rustlings

View File

@ -96,7 +96,7 @@ Before GitHub had the [GitHub CLI][4], it had [hub][5]. `hub` wraps `git` and ad
## git-quickfix
[`git-quickfix`][9] is another Git extension, which allows moving commits to a new branch quickly. The most common use case for it is this: Imagine you're working on a feature branch, and notice a small problem that is unrelated to your current branch. `git-quickfix` would allow you to make a commit on your current branch, then *move it to a new branch* in just one command.
[`git-quickfix`][9] is another Git extension, which allows moving commits to a new branch quickly. The most common use case for it is this: Imagine you're working on a feature branch, and notice a small problem that is unrelated to your current branch. `git-quickfix` would allow you to make a commit on your current branch, then _move it to a new branch_ in just one command.
// Add asciinema recording
@ -104,7 +104,6 @@ Before GitHub had the [GitHub CLI][4], it had [hub][5]. `hub` wraps `git` and ad
[`gh`][4] is GitHub's very own CLI for interacting with their platform. Since my day-to-day work revolves around GitHub, `gh` is extremely helpful is being able to triage issues, raise PRs, view the status of CI jobs and much more. Definitely a must-have if you're a terminal fiend and use GitHub!
[1]: https://msfjarvis.dev/g/dotfiles
[2]: https://github.com/so-fancy/diff-so-fancy
[3]: ./uploads/diff-so-fancy-screenshot.webp

View File

@ -10,13 +10,13 @@ slug = "how-to-get-involved-in-open-source"
The most common question I get when I recommend open source as a launching pad for budding developers is "Where do I start?".
The answer: *anywhere!*
The answer: _anywhere!_
There's a plethora of open source software out there, and not everybody needs to have an encyclopaedic knowledge of the codebase to contribute. You can contribute small things like [fixing dead links in the README](https://github.com/portainer/portainer/commit/173c673d37ea2e4bb82d159b601e60109a435601) to [resolving trivial compilation warnings](https://github.com/mozilla-mobile/fenix/commits/master?author=msfjarvis) to simply [tweaking an issue template](https://github.com/opengapps/opengapps/commits/master/.github/ISSUE_TEMPLATE.md).
The reason I'm linking my own commits is because I want to let people know that the guy helming a [theme engine](https://github.com/substratum) is also out of his element at times and there's no shame in admitting it :)
Thanks to the adoption of specs like [all-contributors](https://allcontributors.org), OSS is more friendly and welcoming than ever. Contribute literally __anything__ to a project you use and scale up from there.
Thanks to the adoption of specs like [all-contributors](https://allcontributors.org), OSS is more friendly and welcoming than ever. Contribute literally **anything** to a project you use and scale up from there.
Remember: You _will_ make mistakes in the process. Don't give up! There's always a project looking for any kind of help it can get. Start your search at home -- See what apps and desktop software you use that's open source, and if that's something you'd like to give back to or even fix something in, even if it's driven by the need to enhance your experience than your goodwill. Linus Torvalds, the creator of Linux [famously said](https://www.bbc.com/news/technology-18419231) this:
@ -24,6 +24,6 @@ Remember: You _will_ make mistakes in the process. Don't give up! There's always
And it's true! Most apps I contribute to right now, like [AdAway](https://github.com/AdAway/AdAway) and [Android Password Store](https://github.com/zeapo/Android-Password-Store), began as a manifestation of personal annoyance. I found things to be lacking, and decided to address it. In the end that benefitted both me and the project.
In conclusion, I'd like to reiterate this -- Contributing __anything__ is contributing!
In conclusion, I'd like to reiterate this -- Contributing **anything** is contributing!
P.S. It's okay to be nervous about it. I spent two weeks researching SSL before submitting a simple null check to Google's [conscrypt](https://github.com/google/conscrypt/pull/471) library :P

View File

@ -22,7 +22,6 @@ class EmailAddress(val value: String)
This is _fine_, but there are hidden performance costs. Classes are initialized on the heap, and fields are much cheaper, fields of primitive types even more so. We only really need to be able to call some `String`s an `EmailAddress`, so we can make this class `inline`.
```kotlin
inline class EmailAddress(val value: String)
```
@ -59,7 +58,7 @@ inline class EmailAddress @PublishedApi
internal constructor(private val value: String) {
override fun toString(): String = value
companion object {
fun parse(string: String): EmailAddress {
check(string.contains('@')) { "invalid email address." }
return EmailAddress(string)
@ -74,7 +73,6 @@ Yeah. What's happening here is that we've made the constructor of `EmailAddress`
Doing this allows us to have an `EmailAddress` type that can be used to verify that a `String` is a valid email and then have that information be carried downstream as the value's type. Why is having the type important? Allow me to refer you to "[Aiming for correctness with types]", provided you have a free hour or so. It's worth it.
[inline classes]: https://kotlinlang.org/docs/reference/inline-classes.html#inline-classes
[representation of inline classes]: https://kotlinlang.org/docs/reference/inline-classes.html#representation
[`@publishedapi`]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-published-api/

View File

@ -21,20 +21,18 @@ The interesting part! Hugo offers a Disqus template internally, but any other co
```html
<div id="commento"></div>
<script defer src="https://commento.example.com/js/commento.js">
</script>
<script defer src="https://commento.example.com/js/commento.js"></script>
```
Hugo offers a powerful tool called [partials](https://gohugo.io/templates/partials/#use-partials-in-your-templates) that allows injecting code into pages from another HTML file. I quickly created a partial with the integration code, scoped out the domain with a variable, and ended up with this.
```html
<div id="commento"></div>
<script defer src="{{ .Site.Params.CommentoURL }}/js/commento.js">
</script>
<script defer src="{{ .Site.Params.CommentoURL }}/js/commento.js"></script>
<noscript>Please enable JavaScript to load the comments.</noscript>
```
With this saved as `layouts/partials/commento.html` and `CommentoURL` set in my `config.toml`, I set out to wire this into the posts. Because of a [pre-existing hack](https://github.com/msfjarvis/msfjarvis.dev/commit/5447bb36258934d6a5bc86be99ef91a9eeb9eb17) that I use for linkifying headings, I already had the `single.html` file from my theme copied into `layouts/_default/single.html`. If you don't, copy it over and open it. Add the following lines, removing any mention of Disqus if you find it.
With this saved as `layouts/partials/commento.html` and `CommentoURL` set in my `config.toml`, I set out to wire this into the posts. Because of a [pre-existing hack](https://github.com/msfjarvis/msfjarvis.dev/commit/5447bb36258934d6a5bc86be99ef91a9eeb9eb17) that I use for linkifying headings, I already had the `single.html` file from my theme copied into `layouts/_default/single.html`. If you don't, copy it over and open it. Add the following lines, removing any mention of Disqus if you find it.
```go
{{ if and .Site.Params.CommentoURL (and (not .Site.BuildDrafts) (not .Site.IsServer)) -}}

View File

@ -11,12 +11,10 @@ In the [previous post] I documented how I went about setting up my Zig environme
My preferred method of learning new languages is rebuilding an existing project in them, like I did when going from [Python] to [Kotlin] to [Rust]. For Zig, I've elected to rebuild my [healthchecks-rs] library. It's something I use on a day-to-day basis for keeping an eye on my backup jobs, and it would be a great addition to the [healthchecks.io ecosystem].
# Getting the basics down
Among the resources enlisted on the Zig [getting started] page, I opted to go with [ziglearn.org] for learning the ropes of the language. It is concise yet detailed, and the chapter-wise breakdown makes for great mental "checkpoints", much like the [Rust book].
For this post I'm going through [chapter 1].
# Thoughts™
@ -25,7 +23,7 @@ I'm going to use this section to jot down my thoughts about Zig, broken down by
## Assignment
The presence of `undefined` is *very* interesting to me. It appears to be functionally identical to Rust's [Default trait], as shown in this snippet (had to skip to structs for this since I was so curious about it).
The presence of `undefined` is _very_ interesting to me. It appears to be functionally identical to Rust's [Default trait], as shown in this snippet (had to skip to structs for this since I was so curious about it).
```zig
const std = @import("std");
@ -67,7 +65,6 @@ const implicitly_sized_array[_]u8 = {};
Rust also [disallows this](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=f27a1a0b20feebe3e6d0a3417f25ce45), but the error is surprisingly worse than with Zig. Rust's resident diagnostics magician Esteban [assures me](https://twitter.com/ekuber/status/1393566561005314048) this is a regression and is being tracked.
The only problem I encountered here was that I can't figure out how to print an array!
```zig
@ -116,11 +113,11 @@ I like how easy it is to define errors, but the syntax feels kinda icky. Having
const NumericError = error{};
fn mayError(shouldError: bool) anyerror!u32 {
return if (shouldError)
// This is different from what I'm accustomed to as a user of
return if (shouldError)
// This is different from what I'm accustomed to as a user of
// Either/Result type monads.
error.NumericError
else
else
10;
}
```
@ -129,24 +126,22 @@ fn mayError(shouldError: bool) anyerror!u32 {
Being able to turn off runtime safety features (like bounds checking) in specific blocks is pretty interesting! Not sure if I'll ever have a valid use for it though...
# Conclusion
I like most of what I've seen so far in Zig. Aside from the issues I mentioned above, the lack of string type is a very confusing thing for me. I've kinda come to expect it everywhere based on my previous experiences with Python, Java, Kotlin, and Rust; but maybe I'll now learn to appreciate how every character on my screen is just numbers :D.
I was very easily distracted today, so I only made it a third of the way in 3 hours for a chapter that is supposed to take 1 hour for the whole thing. Hoping to finish it tomorrow!
[previous post]: /posts/first-steps-with-zig
[Python]: https://msfjarvis.dev/g/walls-manager
[Kotlin]: https://msfjarvis.dev/g/walls-bot
[Rust]: https://msfjarvis.dev/g/walls-bot-rs
[python]: https://msfjarvis.dev/g/walls-manager
[kotlin]: https://msfjarvis.dev/g/walls-bot
[rust]: https://msfjarvis.dev/g/walls-bot-rs
[healthchecks-rs]: https://msfjarvis.dev/g/healthchecks-rs
[healthchecks.io ecosystem]: https://healthchecks.io/docs/resources/
[getting started]: https://ziglang.org/learn/getting-started/
[ziglearn.org]: https://ziglearn.org/
[Rust book]: https://doc.rust-lang.org/book/
[rust book]: https://doc.rust-lang.org/book/
[chapter 1]: https://ziglearn.org/chapter-1/
[Default trait]: https://doc.rust-lang.org/std/default/trait.Default.html
[PR that overhauls formatting]: https://github.com/ziglang/zig/pull/6870
[default trait]: https://doc.rust-lang.org/std/default/trait.Default.html
[pr that overhauls formatting]: https://github.com/ziglang/zig/pull/6870
[`unreachable`]: https://ziglearn.org/chapter-1/#unreachable

View File

@ -53,12 +53,11 @@ I have never worked with `union`s, but [tagged unions] gave me awful ideas about
Zig's type coercion syntax is nicer than Rust's, though the lack of a runtime error concerned me initially. Rust's [TryInto] trait is explicit about the fact that the conversion is fallible, and thus returns a [Result]. Zig on the other hand attempts to validate these conversions at compile-time. Given this code:
```zig
fn returnsNum() u32 {
return 1_00_000;
}
test "typecast" {
testing.expect(@TypeOf(@as(u8, returnsNum())) == u8);
}
@ -105,14 +104,14 @@ Everything following optionals was either uncontroversial or too powerful/low le
[Chapter 2] introduces JSON, which we'll need for our eventual healthchecks.io library, so I'm looking forward to it! I do have work tomorrow, so we'll have to see if I can keep up the daily streak :)
[Yesterday's post]: /posts/learning-zig--day-2
[yesterday's post]: /posts/learning-zig--day-2
[chapter 1]: https://ziglearn.org/chapter-1/
[many-item pointers]: https://ziglearn.org/chapter-1/#many-item-pointers
[pointer-sized integers]: https://ziglearn.org/chapter-1/#pointer-sized-integers
[sealed classes]: https://kotlinlang.org/docs/sealed-classes.html
[tagged unions]: https://ziglang.org/documentation/master/#Tagged-union
[TryInto]: https://doc.rust-lang.org/std/convert/trait.TryInto.html
[Result]: https://doc.rust-lang.org/std/result/enum.Result.html
[Optionals]: https://ziglang.org/documentation/master/#Optionals
[Option]: https://doc.rust-lang.org/std/option/enum.Option.html
[tryinto]: https://doc.rust-lang.org/std/convert/trait.TryInto.html
[result]: https://doc.rust-lang.org/std/result/enum.Result.html
[optionals]: https://ziglang.org/documentation/master/#Optionals
[option]: https://doc.rust-lang.org/std/option/enum.Option.html
[chapter 2]: https://ziglearn.org/chapter-2/

View File

@ -10,9 +10,9 @@ title = "Making a Bluetooth adapter work on Linux"
I made a couple of purchases yesterday, including a Bluetooth speaker and a USB Bluetooth dongle to pair it to my computer. Now here's a couple things that you need to know about said computer:
- It runs Linux
- It runs a customized build of the Zen kernel with a very slimmed down config
- It has never had Bluetooth connectivity before
- It runs Linux
- It runs a customized build of the Zen kernel with a very slimmed down config
- It has never had Bluetooth connectivity before
Thanks to this combination of factors, things got weird. I tried a bunch of things before getting it working, so it is entirely possible that I miss some steps that were important but I didn't think so while writing this. Please let me know on [Twitter](https://twitter.com/msfjarvis) if these steps didn't work for you and I'll try to fix this post.

View File

@ -11,7 +11,7 @@ toc = true
### What is Moshi?
[Moshi] is a fast and powerful JSON parsing library for the JVM and Android, built by the former creators of Google's Gson to address some of its shortcomings and to have an alternative that was actively maintained.
[Moshi] is a fast and powerful JSON parsing library for the JVM and Android, built by the former creators of Google's Gson to address some of its shortcomings and to have an alternative that was actively maintained.
Unlike Gson, Moshi has excellent Kotlin support and supports both reflection based parsing and a kapt-backed codegen backend that eliminates the runtime performance cost in favor of generating adapters during build time. The `kotlin-reflect` dependency required for doing reflection-based parsing can add up to 1.8 mB to the final binary, so it's recommended to use the codegen method if possible.
@ -43,7 +43,7 @@ println(TextPartsJsonAdapter(moshi).toJson(text))
What this means is, given a JSON object that looks like this
```json
{"heading":"This is the heading","body":"And this is the body"}
{ "heading": "This is the heading", "body": "And this is the body" }
```
We can get an instance of `TextParts` that looks like this
@ -55,7 +55,10 @@ val text = TextParts("This is the heading", "And this is the body")
Cool! Now, let's make things unfortunate. Imagine your backend team is stretched thin, and due to a limitation with how they initially built their database schema, you can only get the above JSON in this form
```json
{"heading":"This is the heading","extras":{"body":"And this is the body"}}
{
"heading": "This is the heading",
"extras": { "body": "And this is the body" }
}
```
If you try to parse this with the old `TextPartsJsonAdapter`, your app is going to crash, because the JSON and its Kotlin representation have diverged. The equivalent Kotlin for this new JSON is going to be something like this:
@ -79,7 +82,7 @@ class TextPartsJsonAdapter {
// Moshi is flexible about the parameters of these two methods, and for simpler types
// you will find it easier to follow the example from the Moshi README which does not
// use JsonReader/JsonWriter and instead directly converts items to and from their String
// representations. The method names are also not enforced, as Moshi only uses the
// representations. The method names are also not enforced, as Moshi only uses the
// annotations to find relevant methods. The internal implementation of how they do it
// can be found here: https://git.io/JLwnb
@ -143,7 +146,7 @@ fun toJson(writer: JsonWriter: value: TextParts?) {
}
```
Parsing JSON manually is relatively easy to screw up and Moshi will let you know if you get nesting wrong (missed a closing `endObject()` or `endArray()`) and other easily detectable problems, but you should definitely have tests for all possible cases. I'll let the readers do that on their own, but if you *really* need to see an example then scream at me on [Twitter] and I'll do something about it.
Parsing JSON manually is relatively easy to screw up and Moshi will let you know if you get nesting wrong (missed a closing `endObject()` or `endArray()`) and other easily detectable problems, but you should definitely have tests for all possible cases. I'll let the readers do that on their own, but if you _really_ need to see an example then scream at me on [Twitter] and I'll do something about it.
Anyways, that's the object -> JSON part sorted. Now let's try to do the reverse. Here's where we are as of now.
@ -155,7 +158,6 @@ fun fromJson(reader: JsonReader): TextParts? {
Same as writing JSON, we need to start by making an object.
```diff
fun fromJson(reader: JsonReader): TextParts? {
+ // We'll be constructing the object at the end so these
@ -373,7 +375,6 @@ class TextPartsJsonAdapter {
This is certainly a lengthy job to do, and this blog post is a result of nearly 8 hours I spent writing JSON adapters by hand. Certainly not recommended if avoidable, but sometimes you just need to. When it comes to it, now you hopefully know how :)
[gson]: https://github.com/google/gson
[json spec]: https://TODO
[moshi]: https://github.com/square/moshi

View File

@ -34,11 +34,11 @@ Earlier this year we had migrated a selection UI in one of our screens to use [C
There were a lot more smaller changes that were made to address the remaining visual bugs
- Our onboarding flow was using an incorrect interpretation of `?attr/colorPrimary` for theming, and was migrated to use `?android:attr/colorBackground`
- A lot of screens were using hard-coded colors, which were migrated to theme attributes
- Many screens used hard-coded styles for buttons and text fields, and were also migrated to theme attributes
- Multiple layouts also referenced typography styles directly and were migrated to the corresponding M3 attributes, based on the mapping table in the "[Migration to Material 3]" article.
- System bars and Toolbar had to be given explicit styles and colors to match the "flat" aesthetic from our M2 designs.
- Our onboarding flow was using an incorrect interpretation of `?attr/colorPrimary` for theming, and was migrated to use `?android:attr/colorBackground`
- A lot of screens were using hard-coded colors, which were migrated to theme attributes
- Many screens used hard-coded styles for buttons and text fields, and were also migrated to theme attributes
- Multiple layouts also referenced typography styles directly and were migrated to the corresponding M3 attributes, based on the mapping table in the "[Migration to Material 3]" article.
- System bars and Toolbar had to be given explicit styles and colors to match the "flat" aesthetic from our M2 designs.
## The final stretch

View File

@ -8,6 +8,7 @@ socialImage = "uploads/dagger_story_social.webp"
tags = ["dagger"]
title = "My Dagger Story"
+++
[Dagger](https://dagger.dev) is infamous for very good reasons. It's complicated to use, the documentation is an absolute shitshow, and simpler 'alternatives' exist. While [Koin](http://insert-koin.io/) and to a lesser extent [Kodein](https://kodein.org/di/) do the job, they're still service locators at their core and don't automatically inject dependencies like Dagger does.
## Background
@ -32,27 +33,27 @@ Around mid-December 2019, [Arun](https://twitter.com/arunkumar_9t2) released the
Together, we dug up my previous efforts and I started [a PR](https://github.com/msfjarvis/viscerion/pull/214) so he could review it and help me past the point I dropped out last time. He helped [me on GitHub](https://github.com/msfjarvis/viscerion/pull/214#pullrequestreview-336919368), privately on Telegram and together in about 2 days Viscerion was completely Koin-free and ready to kill. I put down my thoughts about the migration briefly [on the PR](https://github.com/msfjarvis/viscerion/pull/214#issuecomment-569541678), which I'll reproduce and expand on below.
> * Dagger is ridiculously complex without a human to guide you around.
> - Dagger is ridiculously complex without a human to guide you around.
I will die on this hill. Without Sasikanth's help I would have never gotten around to even _trying_ Dagger again.
> * Koin's service locator pattern makes it far too easy to write bad code because you can inject anything anywhere.
> - Koin's service locator pattern makes it far too easy to write bad code because you can inject anything anywhere.
Again, very strong opinion that I will continue to have. I overlooked a clean way of implementing a feature and went for a quick and dirty version because Koin allowed me the freedom to do it. Dagger forced me to re-evaluate my code and I ended up being able to extract all Android dependencies from that package and move it into a separate module.
> * Dagger can feel like a lot of boilerplate but some clever techniques can mitigate that.
> - Dagger can feel like a lot of boilerplate but some clever techniques can mitigate that.
Because the Dagger documentation wasn't helpful, I didn't realise that a `Provides` annotated method and an `@Inject`ed constructor was an either-or situation and I didn't need to write both for a class to be injectable. Sasikanth [with the rescue again](https://github.com/msfjarvis/viscerion/pull/214#discussion_r361800427).
> * Writing `inject` methods for every single class can feel like a drag because it is.
> - Writing `inject` methods for every single class can feel like a drag because it is.
[I mean\...](https://github.com/msfjarvis/viscerion/blob/4a40f3692e62939d3b4c3693efe41ad03fb5f330/app/src/main/java/com/wireguard/android/di/AppComponent.kt#L69-L101)
> * Injecting into Kotlin `object`s appears to be a no-go. I opted to [refactor out the staticity where possible](https://github.com/msfjarvis/viscerion/pull/214/commits/9eb532521f51d0f7bb66a2a78aa1fc5688128a22), [pass injected dependencies to the function](https://github.com/msfjarvis/viscerion/commit/e23f878140d4bda9e2c54d6c2684e07994066fd6#diff-28007a5799b03e7b556f5bb942754031) or [fall back to \'dirty\' patterns](https://github.com/msfjarvis/viscerion/pull/214/commits/fc54ec6bb8e99ec639c6617765e814e12d91ea1a#diff-74f75ab44e1cd2909c4ec4d704bbbab7R65) as needed. Do what you feel like.
> - Injecting into Kotlin `object`s appears to be a no-go. I opted to [refactor out the staticity where possible](https://github.com/msfjarvis/viscerion/pull/214/commits/9eb532521f51d0f7bb66a2a78aa1fc5688128a22), [pass injected dependencies to the function](https://github.com/msfjarvis/viscerion/commit/e23f878140d4bda9e2c54d6c2684e07994066fd6#diff-28007a5799b03e7b556f5bb942754031) or [fall back to \'dirty\' patterns](https://github.com/msfjarvis/viscerion/pull/214/commits/fc54ec6bb8e99ec639c6617765e814e12d91ea1a#diff-74f75ab44e1cd2909c4ec4d704bbbab7R65) as needed. Do what you feel like.
I have no idea if that's even a good ability to begin with, so I chose to change myself rather than fight the system.
> * I still do not _love_ Dagger. Fuck you Google.
> - I still do not _love_ Dagger. Fuck you Google.
This, I probably don't subscribe to anymore. Dagger was horrible to get started with, but I can now claim passing knowledge and familiarity with it, enough to be able to use it for simple projects and be comfortable while doing so.

View File

@ -9,7 +9,7 @@ tags = ["gradle", "github", "packaging"]
title = "Publishing an Android library to GitHub Packages"
+++
>UPDATE(06/06/2020): The Android Gradle Plugin supports Gradle's inbuilt `maven-publish` plugin since version 4.0.0, so I've added the updated process for utilising it at the beginning of this guide. The previous post follows that section.
> UPDATE(06/06/2020): The Android Gradle Plugin supports Gradle's inbuilt `maven-publish` plugin since version 4.0.0, so I've added the updated process for utilising it at the beginning of this guide. The previous post follows that section.
GitHub released the Package Registry beta in May of this year, and graduated it to public availability in Universe 2019, rebranded as [GitHub Packages](https://github.com/features/packages "GitHub Packages"). It supports NodeJS, Docker, Maven, Gradle, NuGet, and RubyGems. That's a LOT of ground covered for a service that's about one year old.
@ -55,6 +55,7 @@ afterEvaluate {
```
For Kotlin:
```kotlin
plugins {
id("maven-publish")
@ -112,13 +113,13 @@ Copy the official integration step from GitHub's [guide](https://help.github.com
apply plugin: "com.android.library"
apply plugin: "kotlin-android"
+apply plugin: "maven-publish"
apply from: "../dependencies.gradle"
// apply from: "../bintrayconfig.gradle"
@@ -28,6 +29,24 @@ android {
}
}
+publishing {
+ repositories {
+ maven {
@ -156,7 +157,7 @@ Switch out the `maven-publish` plugin with [this](https://github.com/wupdigital/
+ classpath deps.gradle_plugins.android_maven_publish
}
}
--- dependencies.gradle
+++ dependencies.gradle
@@ -12,7 +12,8 @@ ext.deps = [
@ -167,7 +168,7 @@ Switch out the `maven-publish` plugin with [this](https://github.com/wupdigital/
+ kotlin: "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.60",
+ android_maven_publish: "digital.wup:android-maven-publish:3.6.2"
],
kotlin: [
--- library/build.gradle
@ -177,7 +178,7 @@ Switch out the `maven-publish` plugin with [this](https://github.com/wupdigital/
apply plugin: "kotlin-android"
-apply plugin: "maven-publish"
+apply plugin: "digital.wup.android-maven-publish"
apply from: "../dependencies.gradle"
// apply from: "../bintrayconfig.gradle"
```
@ -210,9 +211,9 @@ implementation 'com.example:my-fancy-library:1.0.0'
Here:
* Group ID: `com.example`
* Artifact ID: `my-fancy-library`
* Version: `1.0.0`
- Group ID: `com.example`
- Artifact ID: `my-fancy-library`
- Version: `1.0.0`
We'll need to configure these too. I prefer using the `gradle.properties` file for this purpose since it's very easy to access variables from it, but if you have a favorite way of configuring build properties, use that instead! \[[Commit link](https://github.com/msfjarvis/github-packages-deployment-sample/commit/cee74a5e0b3b76d1d7a2d4eb9636d80fb1db49d6)\]

View File

@ -8,6 +8,7 @@ socialImage = "uploads/goaccess_social.webp"
tags = ["caddyserver", "goaccess", "analytics"]
title = "Server-side analytics with Goaccess"
+++
Analytics are a very helpful aspect of any development. They allow developers to know what parts of their apps are visited the most often and can use more attention, and for bloggers to know what content does or does not resonate with their readers.
There are many, many analytics providers and software stacks each with their specific pros and cons, but nearly all managed analytics come with the overarching concern of privacy of user data. [Google Analytics](https://analytics.google.com/) is a _huge_ analytics vendor, with the capabilities to almost accurately extrapolate even the **age** of your visitors. That's nuts, and honestly scary.
@ -38,13 +39,13 @@ goaccess --log-format=VCOMMON \
--real-time-html
```
* `--ws-url`: This option allows us to specify the path for our WebSocket server that's responsible for dispatching updates.
* `--output`: File to dump HTML reports into.
* `--log-file`: The source file to read logs from.
* `--no-query-string`: Does not parse the query string from URLs (`example.org/contact?utm_source=twitter` => `example.org/contact`). This can greatly decrease memory consumption and is often not helpful.
* `--double-decode`: Attempts to decode values like user-agent, request and referrer that are often encoded twice.
* `--real-os`: Displays the real OS names behind the browsers.
* `--real-time-html`: The hero of the show -- the option that makes our analytics real-time and self-updating in the browser.
- `--ws-url`: This option allows us to specify the path for our WebSocket server that's responsible for dispatching updates.
- `--output`: File to dump HTML reports into.
- `--log-file`: The source file to read logs from.
- `--no-query-string`: Does not parse the query string from URLs (`example.org/contact?utm_source=twitter` => `example.org/contact`). This can greatly decrease memory consumption and is often not helpful.
- `--double-decode`: Attempts to decode values like user-agent, request and referrer that are often encoded twice.
- `--real-os`: Displays the real OS names behind the browsers.
- `--real-time-html`: The hero of the show -- the option that makes our analytics real-time and self-updating in the browser.
The final step in this process is to expose the local WebSocket server to the `/ws` endpoint of your domain to allow real-time updates to work. Here's how I do it in Caddy.

View File

@ -24,8 +24,8 @@ lto = "fat"
This makes the following changes to the `release` profile:
- Forces `rustc` to build the entire crate as a single unit, which lets LLVM make smarter decisions about optimization thanks to all the code being together.
- Switches LTO to the `fat` variant. In `fat` mode, LTO will perform [optimization across the entire dependency graph](https://doc.rust-lang.org/rustc/codegen-options/index.html#lto) as opposed to the default option of doing it just to the local crate.
- Forces `rustc` to build the entire crate as a single unit, which lets LLVM make smarter decisions about optimization thanks to all the code being together.
- Switches LTO to the `fat` variant. In `fat` mode, LTO will perform [optimization across the entire dependency graph](https://doc.rust-lang.org/rustc/codegen-options/index.html#lto) as opposed to the default option of doing it just to the local crate.
## Use a different memory allocator
@ -69,6 +69,6 @@ abs_all(&mut input);
# References
- Pascal Hertleif's [blog](https://deterministic.space/) - He's a very popular and active Rust developer and writes amazing, insightful articles.
- Amos Wenger's [blog](https://fasterthanli.me) - Amos' articles often go over important topics like API design through a comparison angle between Rust and another language to highlight differences and benefits to each approach.
- Stjepan Glavina's [blog](https://stjepang.github.io/) - He's done a lot of interesting perf-related work including optimising sorting in the stdlib and building async libraries. His writeups for the library work are very intriguing and go into great detail about the process.
- Pascal Hertleif's [blog](https://deterministic.space/) - He's a very popular and active Rust developer and writes amazing, insightful articles.
- Amos Wenger's [blog](https://fasterthanli.me) - Amos' articles often go over important topics like API design through a comparison angle between Rust and another language to highlight differences and benefits to each approach.
- Stjepan Glavina's [blog](https://stjepang.github.io/) - He's done a lot of interesting perf-related work including optimising sorting in the stdlib and building async libraries. His writeups for the library work are very intriguing and go into great detail about the process.

View File

@ -78,7 +78,7 @@ Because PurelyMail has no bells and whistles, you won't be penalized on the feat
#### You pay for it
I am in a fortunate position where I can pay for things solely based on principle, without having to worry *too* much. Not everybody is similarly blessed, or you may simply have technical issues with being able to pay online for internet things. Stripe and PayPal are not available globally, and fees are often insane. I completely understand.
I am in a fortunate position where I can pay for things solely based on principle, without having to worry _too_ much. Not everybody is similarly blessed, or you may simply have technical issues with being able to pay online for internet things. Stripe and PayPal are not available globally, and fees are often insane. I completely understand.
#### Roundcube is great, but it ain't no GMail

View File

@ -8,6 +8,7 @@ socialImage = "uploads/teachingkotlin_social.webp"
tags = []
title = "#TeachingKotlin - Kotlin for Android Java developers"
+++
Anybody familiar with my work knows that I am a fan of the [Kotlin](https://kotlinlang.org/ "Kotlin") programming language, especially it's interoperability with Java with respect to Android. I'll admit, I've not been a fan since day one. The abundant lambdas worried me and everything being that much shorter to implement was confusing to a person whose first real programming task was in the Java programming language.
As I leaped over the initial hurdle of hesitation and really got into Kotlin, I was mindblown. Everything is so much better! Being able to break away from Java's explicit verbosity into letting the language do things for you is a bit daunting at first but over time you'll come to appreciate the time you save and in turn how many potential problems you can avoid by simply not having to do everything yourself. [Can't have bugs if you don't write code](https://github.com/kelseyhightower/nocode) :p

View File

@ -8,6 +8,7 @@ title = "#TeachingKotlin Part 1 - Classes and Objects and everything in between"
devLink = "https://dev.to/msfjarvis/teachingkotlin-part-1-classes-and-objects-and-everything-in-between-5bn0"
socialImage = "uploads/teachingkotlin_social.webp"
+++
Classes in Kotlin closely mimic their Java counterparts in implementation, with some crucial changes that I will attempt to outline here.
Let's declare two identical classes in Kotlin and Java as a starting point. We'll be making changes to them alongside to show how different patterns are implemented in the two languages.
@ -16,11 +17,11 @@ Java:
{{< highlight java >}}
class Person {
private final String name;
private final String name;
public Person(String name) {
this.name = name;
}
public Person(String name) {
this.name = name;
}
}
{{< /highlight >}}
@ -42,12 +43,12 @@ The primary constructor cannot have any code so Kotlin provides something called
{{< highlight kotlin >}}
class Person(val name: String) {
init {
println("Invoking constructor!")
}
init {
println("Invoking constructor!")
}
}
val _ = Person("Matt")
val \_ = Person("Matt")
{{< /highlight >}}
Moving on, let's add an optional age parameter to our classes, with a default value of 18. To make it convenient to see how different constructors affect values, we're also including an implementation of the `toString` method for some classing print debugging.
@ -57,22 +58,22 @@ Java:
{{< highlight java >}}
class Person {
private final String name;
private int age = 18;
private final String name;
private int age = 18;
public Person(String name) {
this.name = name;
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this(name);
this.age = age;
}
public Person(String name, int age) {
this(name);
this.age = age;
}
@Override
public String toString() {
return "Name=" + name + ",age=" + Integer.toString(age);
}
@Override
public String toString() {
return "Name=" + name + ",age=" + Integer.toString(age);
}
}
{{< /highlight >}}
@ -80,10 +81,10 @@ Kotlin:
{{< highlight kotlin >}}
class Person(val name: String, val age: Int = 18) {
override fun toString() : String {
// I'll go over string templates in a future post, hold me to it :)
return "Name=$name,age=$age"
}
override fun toString() : String {
// I'll go over string templates in a future post, hold me to it :)
return "Name=$name,age=$age"
}
}
{{< /highlight >}}
@ -103,9 +104,9 @@ An important note here is that constructors with default values don't directly w
{{< highlight kotlin >}}
class Person @JvmOverloads constructor(val name: String, val age: Int = 18) {
override fun toString() : String {
return "Name=$name,age=$age"
}
override fun toString() : String {
return "Name=$name,age=$age"
}
}
{{< /highlight >}}
@ -118,7 +119,7 @@ In Kotlin, all classes are final by default, and cannot be inherited while Java
Java:
{{< highlight java >}}
public class Man extends Person { /* Class body */ } // Valid in Java
public class Man extends Person { /_ Class body _/ } // Valid in Java
{{< /highlight >}}
Kotlin:
@ -130,29 +131,33 @@ class Man(val firstName: String) : Person(firstName) // Errors!
Trying it out in the Kotlin REPL
{{< highlight kotlin >}}
>>> class Person @JvmOverloads constructor(val name: String, val age: Int = 18) {
... override fun toString() : String {
... return "Name=$name,age=$age"
... }
... }
>>> class Man(val firstName: String) : Person(firstName)
error: this type is final, so it cannot be inherited from
class Man(val firstName: String) : Person(firstName)
> > > class Person @JvmOverloads constructor(val name: String, val age: Int = 18) {
> > > ... override fun toString() : String {
> > > ... return "Name=$name,age=$age"
> > > ... }
> > > ... }
> > > class Man(val firstName: String) : Person(firstName)
> > > error: this type is final, so it cannot be inherited from
> > > class Man(val firstName: String) : Person(firstName)
^
{{< /highlight >}}
Makes sense, since that's default for Kotlin. Let's add the `open` keyword to our definition of `Person` and try again.
{{< highlight kotlin >}}
>>> open class Person @JvmOverloads constructor(val name: String, val age: Int = 18) {
... override fun toString() : String {
... return "Name=$name,age=$age"
... }
... }
>>> class Man(val firstName: String) : Person(firstName)
>>> println(Man("Henry"))
Name=Henry,age=18
{{< /highlight >}}
> > > open class Person @JvmOverloads constructor(val name: String, val age: Int = 18) {
> > > ... override fun toString() : String {
> > > ... return "Name=$name,age=$age"
> > > ... }
> > > ... }
> > > class Man(val firstName: String) : Person(firstName)
> > > println(Man("Henry"))
> > > Name=Henry,age=18
> > > {{< /highlight >}}
And everything works as we'd expect it to. This is a behavior change that is confusing and undesirable to a lot of people, so Kotlin provides a compiler plugin to mark all classes as `open` by default. Check out the [`kotlin-allopen`](https://kotlinlang.org/docs/reference/compiler-plugins.html#all-open-compiler-plugin) page for more information about how to configure the plugin for your needs.
@ -166,9 +171,9 @@ Java:
{{< highlight java >}}
public static final class StringUtils {
public static String normalizePath(final String str) {
return str.replace("/document/primary:", "/sdcard/");
}
public static String normalizePath(final String str) {
return str.replace("/document/primary:", "/sdcard/");
}
}
{{< /highlight >}}
@ -176,8 +181,8 @@ Kotlin:
{{< highlight kotlin >}}
object StringUtils {
// I'll cover this declaration style too. It's just the first post!
fun normalizePath(str: String): String = str.replace("/document/primary:", "/sdcard/")
// I'll cover this declaration style too. It's just the first post!
fun normalizePath(str: String): String = str.replace("/document/primary:", "/sdcard/")
}
{{< /highlight >}}

View File

@ -13,27 +13,27 @@ Even the variables in Kotlin are supercharged!
Let's start with a simple [data class](https://kotlinlang.org/docs/reference/data-classes.html#data-classes) and see how the variables in there behave.
``` kotlin
```kotlin
data class Student(val name: String, val age: Int, val subjects: ArrayList<String>)
```
To use the variables in this class, Kotlin let's you directly use the dot notation for accessing.
``` kotlin
```kotlin
>>> val s1 = Student("Keith Hernandez", 21, arrayListOf("Mathematics", "Social Studies"))
>>> println(s1.name)
Keith Hernandez
>>> println(s1) // data classes automatically generate `toString` and `hashCode`
>>> println(s1) // data classes automatically generate `toString` and `hashCode`
Student(name=Keith Hernandez, age=21, subjects=[Mathematics, Social Studies])
```
For Java callers, Kotlin also generates getters and setter methods.
``` java
```java
final Student s1 = new Student("Keith Hernandez", 21, arrayListOf("Mathematics", "Social Studies"));
System.out.println(s1.getName());
System.out.println(s1);
@ -41,7 +41,7 @@ System.out.println(s1);
The same properties apply to variables in non-data classes as well.
``` kotlin
```kotlin
>>> class Item(id: Int, name: String) {
@ -68,7 +68,7 @@ As you can notice, the `toString` implementation is not identical to our data cl
While Kotlin creates getters and setters automatically, we can customize their behavior.
``` kotlin
```kotlin
class Item(id: Int, name: String) {
var itemId = id
var itemName = name

View File

@ -8,6 +8,7 @@ socialImage = "uploads/teachingkotlin_social.webp"
tags = []
title = "#TeachingKotlin Part 3 - Caveats coming from Java"
+++
When you start migrating your Java code to Kotlin, you will encounter multiple subtle changes that might catch you off guard. I'll document some of these gotchas that I and other people I follow have found and written about.
## Splitting strings
@ -18,31 +19,31 @@ Java's `java.lang.String#split` [method](https://docs.oracle.com/javase/8/docs/a
## Runtime asserts
Square's [Jesse Wilson](https://twitter.com/jessewilson) found through an [OkHttp bug](https://github.com/square/okhttp/issues/5586) that Kotlin's `assert` function differs from Java's in a very critical way - the asserted expression is *always* executed. He's written about it on his blog which you can check out for a proper write up: [Kotlins Assert Is Not Like Javas Assert](https://publicobject.com/2019/11/18/kotlins-assert-is-not-like-javas-assert/).
Square's [Jesse Wilson](https://twitter.com/jessewilson) found through an [OkHttp bug](https://github.com/square/okhttp/issues/5586) that Kotlin's `assert` function differs from Java's in a very critical way - the asserted expression is _always_ executed. He's written about it on his blog which you can check out for a proper write up: [Kotlins Assert Is Not Like Javas Assert](https://publicobject.com/2019/11/18/kotlins-assert-is-not-like-javas-assert/).
TL; DR Java's `assert` checks the `java.lang.Class#desiredAssertionStatus` method **before** executing the expression, but Kotlin does it **after** which results in unnecessary, potentially significant overhead.
{{< highlight java >}}
// Good :)
@Override void flush() {
if (Http2Stream.class.desiredAssertionStatus()) {
if (!Thread.holdsLock(Http2Stream.this) == false) {
throw new AssertionError();
}
}
...
if (Http2Stream.class.desiredAssertionStatus()) {
if (!Thread.holdsLock(Http2Stream.this) == false) {
throw new AssertionError();
}
}
...
}
{{< / highlight >}}
{{< highlight kotlin >}}
// Bad :(
override fun flush() {
if (!Thread.holdsLock(this@Http2Stream) == false) {
if (Http2Stream::class.java.desiredAssertionStatus()) {
throw AssertionError()
}
}
...
if (!Thread.holdsLock(this@Http2Stream) == false) {
if (Http2Stream::class.java.desiredAssertionStatus()) {
throw AssertionError()
}
}
...
}
{{< / highlight >}}

View File

@ -24,7 +24,7 @@ For [Android Password Store](https://msfjarvis.dev/aps), we maintain a list of k
name: Update Publix Suffix List data
on:
schedule:
- cron: '0 0 * * 6'
- cron: "0 0 * * 6"
jobs:
update-publicsuffix-data:
@ -65,8 +65,8 @@ The core logic of this operation is composed of three parts. The [github context
Jobs in a workflow run in parallel by default, and GitHub comes with an amazing matrix functionality that can automatically generate multiple jobs for you from a single definition. Take this specific example:
| | Windows | MacOS | Ubuntu |
|---------|-------------------|-----------------|------------------|
| | Windows | MacOS | Ubuntu |
| ------- | ----------------- | --------------- | ---------------- |
| Stable | Windows + Stable | MacOS + Stable | Ubuntu + Stable |
| Beta | Windows + Beta | MacOS + Beta | Ubuntu + Beta |
| Nightly | Windows + Nightly | MacOS + Nightly | Ubuntu + Nightly |
@ -89,15 +89,15 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
components: rustfmt, clippy
# Installs the Rust toolchain for the channel picked by the matrix
toolchain: ${{ matrix.rust }}
- uses: actions-rs/toolchain@v1
with:
profile: minimal
components: rustfmt, clippy
# Installs the Rust toolchain for the channel picked by the matrix
toolchain: ${{ matrix.rust }}
```
This will automatically generate 9 (3 platforms * 3 Rust channels) parallel jobs to test this entire configuration, without requiring us to manually define each of them. [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) at its finest :)
This will automatically generate 9 (3 platforms \* 3 Rust channels) parallel jobs to test this entire configuration, without requiring us to manually define each of them. [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) at its finest :)
## Make a job run after another
@ -125,7 +125,7 @@ jobs:
# Mitigating security concerns with Actions
GitHub Actions benefits from a vibrant ecosystem of user-authored actions, which opens it up to equal opportunities for abuse. It is relatively easy to work around the common ones, and I'm going to outline them here. I'm no authority on security, and these recommendations are based on a combination of my reading and understanding. These *should* be helpful, but this list is not exhaustive, and you should exercise all the caution you can.
GitHub Actions benefits from a vibrant ecosystem of user-authored actions, which opens it up to equal opportunities for abuse. It is relatively easy to work around the common ones, and I'm going to outline them here. I'm no authority on security, and these recommendations are based on a combination of my reading and understanding. These _should_ be helpful, but this list is not exhaustive, and you should exercise all the caution you can.
## Use exact commit hashes rather than tags

View File

@ -9,6 +9,7 @@ tags = ["libraries"]
title = "Tips and tricks for building libraries in Kotlin"
+++
Building a library is arguably a far more involved task than building an application. You need to be _extra_ mindful of your dependencies, and ensure that you are not breaking source and/or binary compatibility unintentionally. When doing so in Kotlin, you may also need to also provide an idiomatic API surface for Java callers if you're offering JVM support.
I have _some_ experience building libraries, and have had the fortune of seeing a **lot** of other, much smarter people do it. This post aims to serve as a collection of what I've learned by doing things myself and observing others, that will hopefully be helpful to people trying their hand at library development.

View File

@ -41,7 +41,7 @@ Being a beginner, the ability for code to be checked within the editor and not r
So this is my list of must-have tooling that has helped me continuously improve as a Rustacean. I'm VERY curious to hear what others are using! I opted to stick with official tools where possible since they've proven very reliable and I seem to find considerably more help online with them, but I'd love to try out non-official alternatives that offer significant benefits :)
[Rust]: https://rust-lang.org/
[rust]: https://rust-lang.org/
[cargo-edit]: https://github.com/killercup/cargo-edit
[cargo-clippy]: https://github.com/rust-lang/rust-clippy
[rustfmt]: https://github.com/rust-lang/rustfmt

View File

@ -7,6 +7,7 @@ slug = "why-i-went-back-to-the-gradle-groovy-dsl"
tags = ["groovy", "gradle"]
title = "Why I went back to the Gradle Groovy DSL"
+++
About an year ago when I first discovered the [Gradle Kotlin DSL](https://docs.gradle.org/current/userguide/kotlin_dsl.html), I was very quick to [jump](https://github.com/msfjarvis/viscerion/commit/c16d11a816c3c7e3f7bab51ef2f32569b6b657bf) [on](https://github.com/android-password-store/Android-Password-Store/commit/3c06063153d0b7f71998128dc6fb4e5967e33624) [that](https://github.com/substratum/substratum/commit/ebff9a3a88781d093565526b171d9d5b8e9c1bed) [train](https://github.com/substratum/substratum/commit/5065e082055cde19e41ee02920ca07d0e33c89f5). Now it feels like a mistake.
The initial premise of the Gradle Kotlin DSL was very cool. You get first class code completion in the IDE, and you get to write Kotlin rather than the arguably weird Groovy. People were excited to finally be able to write complex build logic using the `buildSrc` functionality that this change introduced.

View File

@ -100,23 +100,23 @@ Apps no longer have [silent access to screen contents](https://developer.android
### Storage changes
- Apps targeting Android 11 are [no longer allowed to opt out of scoped storage](https://developer.android.com/about/versions/11/privacy/storage#scoped-storage).
- Apps targeting Android 11 are [no longer allowed to opt out of scoped storage](https://developer.android.com/about/versions/11/privacy/storage#scoped-storage).
- All encompassing access to a large set of directories and files is [completely disabled](https://developer.android.com/about/versions/11/privacy/storage#file-directory-restrictions), including the root of the internal storage, the `Download` folder, and the data and obb subdirectories of the `Android` folder.
- All encompassing access to a large set of directories and files is [completely disabled](https://developer.android.com/about/versions/11/privacy/storage#file-directory-restrictions), including the root of the internal storage, the `Download` folder, and the data and obb subdirectories of the `Android` folder.
### Permission changes
- Location, microphone and camera related permissions can now [be granted on a one-off basis](https://developer.android.com/about/versions/11/privacy/permissions#one-time), meaning they'll automatically get revoked when the app process exits.
- Location, microphone and camera related permissions can now [be granted on a one-off basis](https://developer.android.com/about/versions/11/privacy/permissions#one-time), meaning they'll automatically get revoked when the app process exits.
- Apps that are not used for a few months will [have their permissions automatically revoked](https://developer.android.com/about/versions/11/privacy/permissions#auto-reset).
- Apps that are not used for a few months will [have their permissions automatically revoked](https://developer.android.com/about/versions/11/privacy/permissions#auto-reset).
- A new `READ_PHONE_NUMBERS` permission [has been added](https://developer.android.com/about/versions/11/privacy/permissions#phone-numbers) to call certain APIs that expose phone numbers.
- A new `READ_PHONE_NUMBERS` permission [has been added](https://developer.android.com/about/versions/11/privacy/permissions#phone-numbers) to call certain APIs that expose phone numbers.
### Location changes
- [One time access](https://developer.android.com/about/versions/11/privacy/location#one-time-access) is now an option for location, allowing users to not grant persistent access when they don't wish to.
- [One time access](https://developer.android.com/about/versions/11/privacy/location#one-time-access) is now an option for location, allowing users to not grant persistent access when they don't wish to.
- Background location needs to [be requested separately now](https://developer.android.com/about/versions/11/privacy/location#background-location) and asking for it together with foreground location will throw an exception.
- Background location needs to [be requested separately now](https://developer.android.com/about/versions/11/privacy/location#background-location) and asking for it together with foreground location will throw an exception.
### Data access auditing

View File

@ -8,10 +8,10 @@ type = "page"
I use the `gnome-terminal` that ships with Linux Mint's Cinnamon Edition with `bash` and a custom prompt from [starship](https://starship.rs). The editor I use depends on what code I am working with:
- Web, Python: VS Code
- Rust: VS Code with rust-analyzer
- Android: Android Studio
- Kotlin: IntelliJ IDEA
- Web, Python: VS Code
- Rust: VS Code with rust-analyzer
- Android: Android Studio
- Kotlin: IntelliJ IDEA
My terminal-based text editor of choice is currently [micro](https://micro-editor.com/).
@ -23,10 +23,10 @@ I use [Nix](https://nixos.org/nix/) with [home-manager](https://github.com/nix-c
### PC
- CPU: Ryzen 5 1600 (6c/12t) @ 3.2 GHz
- GPU: Nvidia GeForce GTX 1650 Super
- RAM: 8 + 8 (Kingston Value DDR4) + 16 (Kingston HyperX)
- Motherboard: ASRock A320M Pro4
- CPU: Ryzen 5 1600 (6c/12t) @ 3.2 GHz
- GPU: Nvidia GeForce GTX 1650 Super
- RAM: 8 + 8 (Kingston Value DDR4) + 16 (Kingston HyperX)
- Motherboard: ASRock A320M Pro4
### Laptop
@ -34,4 +34,4 @@ I use [Nix](https://nixos.org/nix/) with [home-manager](https://github.com/nix-c
### Phone
- 128GB Google Pixel 4a running Android 12.
- 128GB Google Pixel 4a running Android 12.

View File

@ -44,4 +44,4 @@ I may update our Privacy Policy from time to time. Thus, you are advised to revi
If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me.
This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.firebaseapp.com/)
This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.firebaseapp.com/)