How do I use `srb rbi suggest-typed` without downgrading sigils? - sorbet

The Sorbet typechecker for Ruby has a utility, srb rbi suggest-typed, which will automatically update the # typed: sigils on each file. However, as the Sorbet docs explain, “currently, the suggestion process is fallible, and may suggest downgrading when it's not necessary.” I have a Sorbet-typed codebase that I’d like to try upgrading the types on (to “ratchet” any files that have been fully typed without having their sigil changed), but because it’s a very large codebase I don’t want to have to manually audit every file that’s changed. Is there a way to get Sorbet to only apply a change to the sigil if it’s an upgrade?

There doesn’t seem to be any way to get Sorbet to do this itself. Instead, as a workaround, here are some instructions for using Git to only commit the changes that upgrade the sigil, and discard any changes that would be a downgrade.
Start with a clean working directory (make sure git status doesn’t show any changed files).
Run srb rbi suggest-typed. This will change a lot of sigils, both upgrading and downgrading them.
Produce a summary of the different kinds of changes that have been made:
git diff --word-diff -U0 | grep '# typed: ' | sort | uniq -c
36 # typed: [-false-]{+ignore+}
8 # typed: [-false-]{+strict+}
27 # typed: [-false-]{+true+}
36 # typed: [-strict-]{+false+}
150 # typed: [-true-]{+strict+}
80 {+# typed: false+}
Use grepdiff to find each of the changes you don’t want, and pipe the result to git checkout to drop those changes (modify the below commands as needed for the changes you have gotten):
git diff | grepdiff --output-matching=hunk --only-match=rem 'typed: false' | grepdiff --only-match=add 'typed: ignore' --strip=1 | xargs git checkout --
git diff | grepdiff --output-matching=hunk --only-match=rem 'typed: strict' | grepdiff --only-match=add 'typed: false' --strip=1 | xargs git checkout --
git diff | grepdiff --only-match=add 'typed: false' --strip=1 | xargs git checkout --
Rerun the summary command from step 3 to make sure the results include only what you want.
Commit.

I think you can make creative use of rubocop-sorbet for this:
Add rubocop-sorbet if it isn't already in the Gemfile
Change rubocop.yml to something like:
---
inherit_from: .rubocop_todo.yml
inherit_mode:
merge:
- Include
require:
- rubocop-sorbet
AllCops:
DisabledByDefault: true
Include:
- '**/*/*.rbi'
Sorbet/FalseSigil:
Enabled: true
Sorbet/IgnoreSigil:
Enabled: true
Sorbet/StrictSigil:
Enabled: true
Sorbet/StrongSigil:
Enabled: true
Sorbet/TrueSigil:
Enabled: true
Invoke rubocop --auto-gen-config --exclude-limit 10000 (or some other sufficiently large number that prevents rubocop from disabling rules entirely)
Invoke srb rbi suggest-typed
Any downgraded files should now trigger rubocop violations. You can undo changes to these files with rubocop -f fi | xargs git checkout --
Also remember to revert any unwanted changes to Gemfile, .rubocop.yml, .rubocop_todo.yml, etc. before committing the result.

Related

KDE SVN2GIT "WARN: Branch ... in repository ... doesn't exist at revision ... -- did you resume from the wrong revision?" Can't continue

I'm trying to migrate an 11GB SVN repo with over than 24k revisions inside to a single GIT repository.
I did a single file dump of the SVN using svnrdump command and load it into my local SVN server, placed on my MacBook machine.
I downloaded the svn2git from the https://github.com/svn-all-fast-export/svn2git repository.
Due to differences in the way how SVN and GIT handle tags, I used a merged-branches-tags.rules from the svn2git sample directory, which look like this (I've removed comments):
create repository myproject
end repository
match /trunk/
repository myproject
branch master
end match
match /(branches|tags)/([^/]+)/
repository myproject
branch \2
end match
Then I used a docker image solution as described in the documentation (in my console it was a single line. I've did split it to clarify what I was doing):
docker run --rm -it \
-v /Users/me/work/SVN/dest:/workdir \
-v /Users/me/work/svnServer/repositories/my_svn_repo:/tmp/svn \
-v /Users/me/work/SVN/svn2git/samples:/tmp/conf \
svn2git /usr/local/svn2git/svn-all-fast-export \
--rules /tmp/conf/merged-branches-tags.rules \
--add-metadata --svn-branches --debug-rules --svn-ignore --empty-dirs \
/tmp/svn/
During the first try I got an error between revisions 12600 and 126001:
Exporting revision 12601 /tags/7.0M0p0000 was copied from /tags rev 12600
rev 12601 /tags/7.0M0p0000/ matched rule: "/tmp/conf/merged-branches-tags.rules:28 /(branches|tags)/([^/]+++++
)/" exporting.
.WARN: SVN reports a "copy from" # 12601 from /tags # 12600 but no matching rules found! Ignoring copy, treating as a modification
WARN: Transaction: "7.0M0p0000" is not a known branch in repository "myproject"
Going to create it automatically
add/change dir ( /tags/7.0M0p0000 -> "7.0M0p0000" "" )
+++++
WARN: Branch "7.0M0p0000" in repository "myproject" doesn't exist at revision 12601 -- did you resume from the wrong revision?
Failed to write to process: Process crashed for repository myproject
6223345 modifications from SVN /tags/7.0M0p0000/ to myproject/7.0M0p0000%
I've check it and in the rev 12601 there there is a new tag named as "7.0M0p0000", which I'm going to import as a branch and which wasn't in the repo in rev 12600.
Do you have any ideas what can I do to fix that and continue my migration?
Any help will be really appreciated.
After a further investigation, it turns out that the mentioned "7.0M0p0000" tag was created in the rev 12601 as a copy of all the tags from rev 12600.
I've found it in the dump file, created using this command:
svnrdump dump -r 12600:12601 --incremental http://xxx.xxx.xxx.xxx/svn/my_repo > my_repo.dump
There was an entry:
Revision-number: 12601
...
Node-path: tags/7.0M0p0000
Node-kind: dir
Node-action: add
Node-copyfrom-rev: 12600
Node-copyfrom-path: tags
It seems that KDE's svn2git is unable to deal with such cases (which was probably done by mistake).
The only solution I found was completely skip this tag by adding a match to my merged-branches-tags.rules file (order of match expressions is important):
match /tags/7.0M0p0000/
min revision 12600
max revision 12606
end match
...
match /(branches|tags)/([^/]+)/
repository myproject
branch \2
end match

Remotely show the most recent tag reachable from a git commit

I can use git describe to locally show the most recent tag on a local git repository that is reachable from a git commit. Using procps as an instance, git describe returns this v3.3.11-4-g99fa7f9 value (at the time of this writing). However, if I don't already have a local git repository, I can use
git ls-remote --tags https://gitlab.com/procps-ng/procps.git | grep -v "\^" | cut -d/ -f3 | sort -Vu | tail -1
to remotely do the same as git describe --abbrev=0 that returns this v3.3.11 value and not this v3.3.11-4-g99fa7f9 value. So, is it possible to remotely show the most recent tag that is reachable from a git commit to return this v3.3.11-4-g99fa7f9 value instead of this v3.3.11 value (preferably with a single instruction sans external scripts)?

Custom error handling in makefile

I need to build a C program which requires a particular Linux package. I set a variable, PACKAGENOTIFICATION, to a shell command which is supposed to check if the package is installed for Ubuntu and print a notification if not:
PACKAGENOTIFICATION := if cat /etc/issue | grep Ubuntu -c >>/dev/null; then if ! dpkg -l | grep libx11-dev -c >>/dev/null; then echo "<insert notification here>"; fi; fi
[...]
maintarget: dependencies
$(PACKAGENOTIFICATION)
other_commands
Unfortunately, while making the dependencies, it runs into the files which need the package, and errors out before executing my PACKAGENOTIFICATION. An alternative formulation is to make a separate target whose only purpose is to run the notification:
maintarget: notify other_dependencies
commands
notify:
$(PACKAGENOTIFICATION)
However, since this phantom dependency always needs to be executed, make never reports that the program is up to date.
What's the best way to have make always report as up to date, but also execute my notification before it dies?
Thanks!
If your version of Make supports "order-only" prerequisites, this will do it:
# Note the pipe
maintarget: other_dependencies | notify
commands
# This should be an order-only preq of any target that needs the package
notify:
$(PACKAGENOTIFICATION)
If not, there are other approaches.

How to undo bzr add

Sometimes I type bzr add and don't notice that I am not in the root of the branch but an ignored sub-folder. This then adds all files in that folders - often it is a build folder, with lots of files. Hence the question: how to undo a bzr add.
There is built-in way without need of xargs: bzr remove --new --keep
This answer is shamelessly stolen from here to make it more accessible (to me as well).
This will undo an erroneous bzr add:
bzr added -0 | xargs -0 bzr rm --keep

How to detect code change frequency?

I am working on a program written by several folks with largely varying skill level. There are files in there that have never changed (and probably never will, as we're afraid to touch them) and others that are changing constantly.
I wonder, are there any tools out there that would look at the entire repo history (git) and produce analysis on how frequently a given file changes? Or package? Or project?
It would be of value to recognize that (for example) we spent 25% of our time working on a set of packages, which would be indicative or code's fragility, as compared with code that "just works".
If you're looking for an OS solution, I'd probably consider starting with gitstats and look at extending it by grabbing file logs and aggregating that data.
I'd have a look at NChurn:
NChurn is a utility that helps asses the churn level of your files in
your repository. Churn can help you detect which files are changed the
most in their life time. This helps identify potential bug hives, and
improper design.The best thing to do is to plug NChurn into your build
process and store history of each run. Then, you can plot the
evolution of your repository's churn.
I wrote something that we use to visualize this information successfully.
https://github.com/bcarlso/defect-density-heatmap
Take a look at the project and you can see what the output looks like in the readme.
You can do what you need by first getting a list of files that have changed in each commit from Git.
~ $ git log --pretty="format:" --name-only | grep -v ^$ > file-changes.txt
~ $ for i in `cat file-changes.txt | cut -d"." -f1,2 | uniq`; do num=`cat file-changes.txt | grep $i | wc -l`; if (( $num > 1 )); then echo $num,0,$i; fi; done | heatmap > results.html
This will give you a tag cloud with files that churn more will show up larger.
I suggest using a command like
git log --follow -p file
That will give you all the changes that happened to the file in the history (including renames). If you want to get the number of commits that changed the file then you can do on a UNIX-based OS :
git log --follow --format=oneline Gemfile | wc -l
You can then create a bash script to apply this to multiple files with the name aside.
Hope it helped !
Building on a previous answer I suggest the following script to parse all project files
#!/bin/sh
cd $1
find . -path ./.git -prune -o -name "*" -exec sh -c 'git log --follow --format=oneline $1 | wc -l | awk "{ print \$1,\"\\t\",\"$1\" }" ' {} {} \; | sort -nr
cd ..
If you call the script as file_churn.sh you can parse your git project directory calling
> ./file_churn.sh project_dir
Hope it helps.