From 5e77de80c3619d378c8a85ab0e2413086a57a8a5 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 15 Apr 2016 21:38:41 +0000 Subject: proposal (15apr16) git-svn-id: svn://tug.org/texlive/trunk@40538 c570f23f-e606-0410-a88d-b1316a301751 --- .../source/latex/proposal/bin/Modparse.pm | 189 +++++++++ Master/texmf-dist/source/latex/proposal/bin/README | 32 ++ .../texmf-dist/source/latex/proposal/bin/checksum | 71 ++++ .../texmf-dist/source/latex/proposal/bin/filedate | 45 ++ .../source/latex/proposal/bin/generate-issues | 470 +++++++++++++++++++++ 5 files changed, 807 insertions(+) create mode 100644 Master/texmf-dist/source/latex/proposal/bin/Modparse.pm create mode 100644 Master/texmf-dist/source/latex/proposal/bin/README create mode 100755 Master/texmf-dist/source/latex/proposal/bin/checksum create mode 100755 Master/texmf-dist/source/latex/proposal/bin/filedate create mode 100755 Master/texmf-dist/source/latex/proposal/bin/generate-issues (limited to 'Master/texmf-dist/source/latex/proposal/bin') diff --git a/Master/texmf-dist/source/latex/proposal/bin/Modparse.pm b/Master/texmf-dist/source/latex/proposal/bin/Modparse.pm new file mode 100644 index 00000000000..2fd732b063d --- /dev/null +++ b/Master/texmf-dist/source/latex/proposal/bin/Modparse.pm @@ -0,0 +1,189 @@ +####################################################################################### +# A module for loading .tex files and feeding the data to the various applications. # +# Copyright (c) 2005, Ioan Sucan, released under the Gnu General Public License (GPL) # +# see http://www.gnu.org/copyleft/gpl.html # +# $URL: svn://kwarc.eecs.iu-bremen.de/repos/kwarc/projects/content/bin/Modparse.pm$ # +# $Date: 2005-09-26 12:44:28 -0500 (Mon, 26 Sep 2005) $ $Rev: 4243 $ # +####################################################################################### + +package Modparse; + +use File::Basename; +use File::Path; + +# if both snippath and snippathList are specified, +# snippathList is ignored ! + +# if a default snippath definition file is found +# (snippath.def) it is used if no snippath is given +# as argument; +sub new { + my ($class, %options) = @_; + + $options{recursion} = 1 unless defined $options{recursion}; + $options{requireStatus} = 'include|exclude' unless defined $options{requireStatus}; + $options{loadInputs} = 1 unless defined $options{loadInputs}; + $options{onlyModuleLines} = 0 unless defined $options{onlyModuleLines}; + + if ( !(%{$options{snippath}}) && (-e "snippath.def") ) { + open(SNIPPATHDEF, 'snippath.def'); + my @snlist = ; + close(SNIPPATHDEF); + for (my $i = 0 ; $i <= $#snlist; $i++) { chomp($snlist[$i]); } + %{$options{snippath}} = @snlist; } + @{$options{snippathList}} = keys(%{$options{snippath}}) + if ($options{snippath} && %{$options{snippath}}); + $options{debug} = 0; + + bless(\%options, $class); } + +sub execute{ + my ($self, $file) = @_; + + local *run_event = sub { + my ($event, $arg) = @_; + if ($self->{$event}) { $self->{$event}($arg); } + }; + + local *go = sub { + my ($location, %param) = @_; + my ($tex, $path, @tex_data) = $self->readLocation($location); + return unless @tex_data; + my $NL = $#tex_data+1; + + $param{filename} = $tex; + $param{depth}++; + $param{filecount}++; + my $i = 0; + + $param{nextline} = sub { $tex_data[++$i]; }; + $param{prevline} = sub { $tex_data[--$i]; }; + $param{rewriteline}= sub { $tex_data[$i] = $_[0]; }; + + print "Processing $tex...\n" if $self->{verbose}; + run_event('onBeginFile', \%param); + + while ($i<$NL){ + $param{original} = $tex_data[$i]; + $_ = $tex_data[$i]; chomp; /([^%]*)((%.*)?)/; + ($param{tex}, $param{comment}) = ($1, $2); $_ = $1; + s/\s+//g; $param{simple_tex} = $_; + + if (/\\begin\{module\}(\[([^\]]*)\])?/) { + if ($param{in_module}) { + push(@{$param{module_stack}}, [$param{module_id}, $param{module_arg}]); } + $param{in_module} = 1; + $param{module_args} = $1 ? $2 : undef; + $param{module_id} = ($param{module_args} && + $param{module_args} =~ /id=([^\],]+)/) ? $1 : undef; + $param{module_nesting}++; + run_event('onBeginModule', \%param); } + + if (/\\end\{module\}/) { + run_event('onEndModule', \%param); + if ($#{$param{module_stack}} >= 0) { + ($param{module_id}, $param{module_args}) = @{pop(@{$param{module_stack}})}; + } else { + $param{in_module} = 0; + ($param{module_id}, $param{module_args}) = (undef, undef); } + $param{module_nesting}--; + } + + foreach my $srch_snippath(@{$self->{snippathList}}){ + $self->{snippath}{$srch_snippath} = $1 + if (/\\def\\$srch_snippath\#1\{([^\#]+)\#1\}$/); } + + if ($self->{follow} && (/\\((requiremodules(\[($self->{requireStatus})\])?)|input|include)\{([^\}]+)\}/)){ + my $req_filename = $5; my $req_spath = ''; my $command = $1; + my $is_require = ($command =~ /requiremodules/) ? 1 : 0; + if ($is_require || $self->{loadInputs}) { + foreach my $srch_snippath(@{$self->{snippathList}}){ + if ($req_filename =~ /\\$srch_snippath\{(.+)/){ + ($req_spath, $req_filename) = ($self->{snippath}{$srch_snippath}, $1); + last; }} + run_event('onRecurse', \%param); + if ($self->{recursion} || !$is_require) { + go($self->makeLocation($req_filename, $path, $req_spath), %param); } + } + } + run_event('onEveryLine', \%param) if ($param{in_module} || !$self->{onlyModuleLines}); + + last if (/\\end\{document\}/ && $self->{stopAtEnd}); + $i++; + } + run_event('onEndFile', \%param); + $param{depth}--; return; + }; + + my %param = (pack=>$self, perm=>$self->{_AUX_DATA}, depth=>0, filecount=>0, + in_module=>0, module_nesting=>0); + run_event('onBegin', \%param); + go($self->makeLocation($file), %param); + run_event('onEnd', \%param); + return; +} + + +# add a [.tex] extension to the filename +sub add_ext{ + my ($self, $fn, $ext) = @_; + $ext = '.tex' unless $ext; + $fn.=$ext if $fn !~ /\Q$ext\E$/; + $fn; } + +# as long as the extension is .tex, drop it +sub drop_ext{ + my ($self, $filename) = @_; + while ($filename =~ /\.tex$/){ + while ($filename !~ /\.$/ ) { chop($filename); } + chop($filename); } + $filename; } + +sub new_ext{ + my ($self, $filename, $ext) = @_; + $self->add_ext($self->drop_ext($filename), $ext); } + +# location = pointer to array of strings +# first element is filename; next are possible paths +sub makeLocation{ my $self = shift; return \@_; } + +# read a location into an array of strings +# aslo return filename and path to it (just in +# case some changes were made to it, like adding +# extension and/or relative paths) +sub readLocation{ + my ($self, $location) = @_; + + my ($filename, @paths) = @{$location}; + # try to add proper extension; + my $filename_ext = $self->new_ext($filename) unless -e $filename; + $filename = $filename_ext if $filename_ext && -e $filename_ext; + + # try search paths + if (! -e $filename){ + my $all_p = ''; + foreach my $p(@paths){ + $all_p .= $p; + $filename = $all_p.$filename and last if -e $all_p.$filename; + $filename = $p.$filename and last if -e $p.$filename; + if ($filename_ext){ + $filename = $all_p.$filename_ext and last if -e $all_p.$filename_ext; + $filename = $p.$filename_ext and last if -e $p.$filename_ext; } + }} + # don't do the same work too many times + foreach my $file(@{$self->{loadedFiles}}) + { return undef if $file eq $filename; } + + push(@{$self->{loadedFiles}}, $filename); + open(FIN, $filename) or die "Cannot open '$filename'\n"; + my @data = ; + close(FIN); + + # find path of current file + my $path = dirname($filename); + $path.='/' if ($path ne ''); + + return ($filename, $path, @data); +} + +1; diff --git a/Master/texmf-dist/source/latex/proposal/bin/README b/Master/texmf-dist/source/latex/proposal/bin/README new file mode 100644 index 00000000000..e33b26bf256 --- /dev/null +++ b/Master/texmf-dist/source/latex/proposal/bin/README @@ -0,0 +1,32 @@ +This directory contains the binaries for the proposal class. + +We have three utilities: +- filedate and checksum are for managing metadata in the + DTX files of the sTeX distribution (written in perl) +- Modparse.pm is a perl library they depend on +- generate-issues is a ruby library that generates issues + on http://github.com for the project (experimental) + +============== Details ================= + +checksum + "enable, update, and disable checksums in DTX files" + +Usage: + checksum [options] + +Options: + -- disable disable the checksum by wrapping in \iffalse ... \fi + -- enable remove those again + -- update update to the correct checksum + + +filedate + +Purpose: + Update the dates for a \ProvidePackage invocation in a given .dtx source + +Example: + filedate omdoc.dtx + + diff --git a/Master/texmf-dist/source/latex/proposal/bin/checksum b/Master/texmf-dist/source/latex/proposal/bin/checksum new file mode 100755 index 00000000000..9f0e533a8d4 --- /dev/null +++ b/Master/texmf-dist/source/latex/proposal/bin/checksum @@ -0,0 +1,71 @@ +#!/usr/bin/perl -w +####################################################################################### +# A tool for updating .dtx files with a correspodning checksum counter # +# Copyright (c) 2010, Deyan Ginev, released under the Gnu General Public License (GPL)# +# see http://www.gnu.org/copyleft/gpl.html # +# $URL: https://svn.kwarc.info/repos/stex/trunk/bin/checksum$ # +####################################################################################### + +use strict; + +use Getopt::Long; +use Modparse; +use Pod::Usage; +use Cwd qw(abs_path); + +####### start of program ####### +my $mode = "update"; #Default is update +my $action = { "update" => \&update, "disable" => \&disable, "enable"=>\&enable }; +GetOptions("disable" => sub { $mode="disable"; }, + "enable" => sub { $mode="enable"; }, + "update" => sub { $mode="update"; }); + + +my ($path) = @ARGV; +$path = abs_path($path); +my ($volume,$dir,$file) = File::Spec->splitpath( $path ); +my @lines = `cd $dir; pdflatex "\\nonstopmode\\input{$file}"` if ($mode eq "update"); +my ($checksum) = map {$_=~/^\* The checksum should be (\d+)!/; $1;} grep ($_ =~ /^\* The checksum should be (\d+)!/,@lines) if @lines; +if (!$checksum) { +#One more possible error message: + ($checksum) = map {$_=~/^! Package doc Error: Checksum not passed \((\d+)<>(\d+)\)\./; $2;} grep ($_ =~ /^! Package doc Error: Checksum not passed \((\d+)<>(\d+)\)\./,@lines) if @lines; +} +open(IN,"<$path") or die "Cannot open DTX source: $path\n"; +@lines = (); +while () { + push @lines, &{$$action{$mode}}($_,$checksum); +} +close(IN); +open(OUT,">$path") or die "Cannot write to DTX target: $path\n"; +print OUT join("",@lines); +close(OUT); +######### Subroutines ############ +sub update { + my $checksum=$_[1]; + $_[0]=~s/\\CheckSum\{\d*\}/\\CheckSum{$checksum}/ if $checksum; + $_[0]; +} + +sub disable { + $_[0]=~s/\\CheckSum\{(\d*)\}/\\iffalse\\CheckSum\{$1}\\fi/ unless $_[0]=~/\\iffalse\\CheckSum\{(\d*)\}\\fi/; + $_[0]; +} + +sub enable { + $_[0]=~s/\\iffalse\\CheckSum\{(\d*)\}\\fi/\\CheckSum{$1}/; + $_[0]; +} + + +__END__ + + +=head1 SYNOPSIS + +checksum --update|enable|disable + +Purpose: + Update, enable or disable the \CheckSum macro for a given .dtx source + +Example: + checksum omd.dtx --update diff --git a/Master/texmf-dist/source/latex/proposal/bin/filedate b/Master/texmf-dist/source/latex/proposal/bin/filedate new file mode 100755 index 00000000000..af49b6550dd --- /dev/null +++ b/Master/texmf-dist/source/latex/proposal/bin/filedate @@ -0,0 +1,45 @@ +#!/usr/bin/perl -w +####################################################################################### +# A tool for updating .dtx files with a given package date. # +# Copyright (c) 2010, Deyan Ginev, released under the Gnu General Public License (GPL)# +# see http://www.gnu.org/copyleft/gpl.html # +# $URL: https://svn.kwarc.info/repos/stex/trunk/bin/filedate$ # +####################################################################################### + +use strict; + +use Getopt::Long; +use Modparse; +use Pod::Usage; +use Cwd qw(abs_path); + +my ($file) = @ARGV; +my $date = `git log --format='%ai' $file`; +$date = substr($date, 0, index($date, ' ')); +$date =~ s/-/\//g; +$file = abs_path($file); +open(IN,"<$file"); +my @lines = (); +while () { + if ($_ =~ /\\Provides(Package|Class)/) { + $_ =~ s/\[(.*?)\s/\[$date /; + } + push @lines, $_; +} +close(IN); +open(OUT,">$file"); +print OUT join("",@lines); +close(OUT); + +__END__ + + +=head1 SYNOPSIS + +filedate + +Purpose: + Update the dates for a \ProvidePackage invocation in a given .dtx source + +Example: + filedate omdoc.dtx diff --git a/Master/texmf-dist/source/latex/proposal/bin/generate-issues b/Master/texmf-dist/source/latex/proposal/bin/generate-issues new file mode 100755 index 00000000000..84ebfd80fd6 --- /dev/null +++ b/Master/texmf-dist/source/latex/proposal/bin/generate-issues @@ -0,0 +1,470 @@ +#!/usr/bin/env ruby +# Generate GitHub Issues for tasks and deliverables +# - Each work package gets a WPX/README.md file +# - Each task gets an Issue +# - Each deliverable gets a Milestone +# - Each deliverable gets an Issue associated with its Milestone +# - Each deliverable Issue gets 'deliverable', 'WPX' tags +# - Each task Issue gets 'task', 'WPX' tags + +# Run me from the proposal dir containing proposal.pdata: +# cd ~/OpenDreamKit/Proposal +# ~/path/to/LaTeX-Proposal/bin/generate-issues + +require 'date' +require 'Octokit' +require 'netrc' + +# constants for running the script are for OpenDreamKit +# update these for your own proposal + +START_DATE = Date::new(2015, 9, 1) # start date of the project +HOMEPAGE = "http://opendreamkit.org" +PROPOSAL_URL = "https://github.com/OpenDreamKit/OpenDreamKit/raw/master/Proposal/proposal-www.pdf" +PROJECT = "OpenDreamKit" +REPO = "OpenDreamKit/OpenDreamKit" +TARGET = "final" # or 'proposal' + +# throttle github creation requests to 5 Hz to avoid getting flagged for abuse +THROTTLE_SECONDS = 5 + +NATURES = { + 'R' => 'Report', + 'DEM' => 'Demonstrator', + 'DEC' => 'Websites, Media, etc.', + 'OTHER' => 'Other', +} + +DISSEMINATIONS = { + 'PU' => 'Public', + 'CO' => 'Confidential', + 'CI' => 'Classified', +} + +SITES = { + 'PS' => 'Université Paris-Sud', + 'LL' => 'Logilab', + 'UV' => 'Université de Versailles Saint-Quentin', + 'UJF' => 'Université Joseph Fourier', + 'UB' => 'CNRS', + 'UO' => 'University of Oxford', + 'USH' => 'University of Sheffield', + 'USO' => 'University of Southampton', + 'SA' => 'University of St Andrews', + 'UW' => 'University of Warwick', + 'JU' => 'Jacobs University Bremen', + 'UK' => 'University of Kaiserslautern', + 'US' => 'University of Silesia', + 'ZH' => 'Universität Zürich', + 'SR' => 'Simula Research Laboratory', +} + +REVERSE_SITES = {} +SITES.each_pair do |key, name| + REVERSE_SITES[name] = key +end + +# Some sites would prefer alternative abbreviations +REVERSE_SITES['University of St Andrews'] = 'USTAN' +REVERSE_SITES['Université de Versailles Saint-Quentin'] = 'UVSQ' + +#------------------- Parsing proposal.pdata --------------------- + +def split_line(line) + # super primitive state-machine line split (not going to regex this) + parts = [] + level = 0 + buffer = [] + line.each_char do |c| + case c + when '{' + if level > 0 + buffer.push c + end + level += 1 + when '}' + level -= 1 + if level > 0 + buffer.push c + end + if level == 0 + parts.push(scrub_tex(buffer.join(''))) + buffer = [] + end + else + if level > 0 + buffer.push c + end + end + end + return parts +end + + +def scrub_tex(text) + # scrub some latex markup from text + text.gsub!(/\\\w+/, '') + text.gsub!(/[{}]/, '') + text.strip.split.join(' ') +end + + +def transform_value(key, value) + case key + when 'lead' + return SITES[value] + when 'partners' + return value.split(',').map { |v| SITES[v] } + when 'dissem' + return DISSEMINATIONS[value] + when 'nature' + return NATURES[value] + when 'month' + return value.to_i + when 'delivs' + return value.split(',') + else + return scrub_tex(value) + end +end + + +def load_pdata(proposal_dir) + pdata = File.join(proposal_dir, "#{TARGET}.pdata") + deliv_data = File.join(proposal_dir, "#{TARGET}.deliverables") + + workpackages = {} # mapping of workpackage id => wp info + deliverables = {} + + File.readlines(pdata).each do |line| + key, *args = split_line line + + case key + when 'wp' + name, key, value = args + value = transform_value(key, value) + if not workpackages.include? name + workpackages[name] = { + "tasks" => {}, + "unknown-task" => nil, + "deliverables" => [], + } + end + wp = workpackages[name] + wp[key] = value + + when 'task' + name, key, value = args + value = transform_value(key, value) + # find my workpackage + if name.index('@') + wpkey, short_name = name.split('@') + if short_name.match(/task\d+/) + workpackages[wpkey]['unknown-task'] = short_name + name = short_name + end + else + wpkey = workpackages.keys.select { |wpkey| name.start_with? wpkey }.first + end + # handle workpackage@taskNN weirdness + wp = workpackages[wpkey] + tasks = wp['tasks'] + if not wp['unknown-task'].nil? and wp['unknown-task'] != name + tasks[name] = tasks.delete(wp['unknown-task']) + wp['unknown-task'] = nil + end + if not tasks.include? name + tasks[name] = {} + end + tasks[name][key] = value + when 'deliv' + name, key, value = args + + value = transform_value(key, value) + if not deliverables.include? name + deliverables[name] = {} + end + deliverables[name][key] = value + + else + # DEBUG: + # puts " Ignored: #{args}" + end + end + + # get deliverable data, workpackage id from proposal.deliverables + File.readlines(deliv_data).each do |line| + args = split_line line + name = args[3] + if deliverables.include? name + deliverable = deliverables[name] + else + deliverable = {} + end + month = args[0].to_i + deliverable = (deliverables[name] or {}).merge({ + "month" => month, + # due date is last day of the given month, so subtract one day + "due_date" => (START_DATE >> month) - 1, + "label" => scrub_tex(args[2]), + "deliv_id" => args[3], + "dissem" => transform_value('dissem', args[4]), + "nature" => transform_value('nature', args[5]), + "title" => scrub_tex(args[6]), + "lead" => SITES[scrub_tex(args[8])], + }) + wpid = scrub_tex(args[7]) + wp = workpackages.values.find {|wp| wp['label'] == wpid} + wp['deliverables'].push(deliverable) + end + + workpackages.values.each do |wp| + wp['deliverables'].sort_by! {|d| d['label']} + end + + return workpackages.values.sort_by {|wp| wp['label']} +end + + +#--------------- GitHub-related --------------------- + +def check_token + # get GitHub auth token, creating one if we don't find it. + rc = Netrc.read Netrc.default_path + if not rc['api.github.com'].nil? + return + end + puts "We need your password to generate an OAuth token. The password will not be stored." + username = ask "Username: " + password = ask("Password: ") { |q| q.echo = '*' } + client = Octokit::Client.new( + :login => username, + :password => password, + ) + reply = client.create_authorization( + :scopes => ["public_repo"], + :note => "Issue Migration", + ) + token = reply.token + rc['api.github.com'] = username, token + rc.save +end + +$cache = { + 'issues' => {}, + 'milestones' => {}, +} + +def get_issues(github, repo) + # get issues for a repo (cached) + cache = $cache['issues'] + if not cache.include? repo + cache[repo] = github.issues(repo) + end + return cache[repo] +end + +def get_milestones(github, repo) + # get issues for a repo (cached) + cache = $cache['milestones'] + if not cache.include? repo + cache[repo] = github.list_milestones(repo) + end + return cache[repo] +end + +# Templates for making readmes, issues + +README_TPL = <<-END +# %{title} + +Lead Institution: %{lead} + +See page %{page} of the [proposal](#{PROPOSAL_URL}) for the full description. +END + +TASK_TPL = <<-END +- **%{wplabel}:** [%{wptitle}](https://github.com/#{REPO}/tree/master/%{wplabel}) +- **Lead Institution:** %{lead} +- **Partners:** %{partners} +- **Work phases:** %{wphases} + +See page %{page} of the [proposal](#{PROPOSAL_URL}) for the full description. +END + +DELIV_TPL = <<-END +- **%{wplabel}:** [%{wptitle}](https://github.com/#{REPO}/tree/master/%{wplabel}) +- **Lead Institution:** %{lead} +- **Due:** %{date} (month %{month}) +- **Nature:** %{nature} + +See page %{page} of the [proposal](#{PROPOSAL_URL}) for the full description. +END + +DELIV_MILESTONE_TPL = "# %{title}\n\n#{DELIV_TPL}" + + +def make_task_issue(github, repo, task, workpackage, options) + title = "#{task['label']}: #{task['title']}" + issues = get_issues(github, repo) + issue = issues.find { |i| i.title.start_with?(task['label'] + ':') } + if issue.nil? + body = TASK_TPL % { + wplabel: workpackage['label'], + wptitle: workpackage['title'], + lead: task['lead'], + page: task['page'], + wphases: task['wphases'], + partners: (task['partners'] or ['None']).join(', ') + } + puts "\n\nMaking Issue on #{repo}: #{title}" + puts body + + github.create_issue(repo, title, body, options) + # throttle creation calls to avoid flags for abuse + sleep THROTTLE_SECONDS + else + puts "Found Issue #{repo}##{issue.number}: #{issue.title}" + existing_labels = issue.labels.map { |label| label['name']} + missing_labels = options[:labels].reject { |label| existing_labels.include? label } + if not missing_labels.empty? + puts "Updating milestone, labels on #{repo}##{issue.number}" + github.update_issue(repo, issue.number, options) + sleep THROTTLE_SECONDS + end + end +end + + +def make_deliverable_issue(github, repo, deliverable, workpackage, options) + title = "#{deliverable['label']}: #{deliverable['title']}" + issues = get_issues(github, repo) + issue = issues.find { |i| i.title.start_with?(deliverable['label'] + ':') } + if issue.nil? + body = DELIV_TPL % { + wplabel: workpackage['label'], + wptitle: workpackage['title'], + lead: deliverable['lead'], + date: deliverable['due_date'], + month: deliverable['month'], + nature: deliverable['nature'], + page: deliverable['page'], + } + puts "\n\nMaking Issue on #{repo}: #{title}" + puts body + github.create_issue(repo, title, body, options) + # throttle creation calls to avoid flags for abuse + sleep THROTTLE_SECONDS + else + puts "Found Issue #{repo}##{issue.number}: #{issue.title}" + existing_labels = issue.labels.map { |label| label['name']} + missing_labels = options[:labels].reject { |label| existing_labels.include? label } + if issue.milestone.nil? or not missing_labels.empty? + puts "Updating milestone, labels on #{repo}##{issue.number}" + github.update_issue(repo, issue.number, options) + sleep THROTTLE_SECONDS + end + end +end + + +def make_deliverable_milestone(github, repo, deliverable, workpackage) + title = deliverable['label'] + + milestone = get_milestones(github, repo).find { |ms| ms.title == title } + if milestone.nil? + puts "Making milestone on #{repo}: #{title}" + body = DELIV_MILESTONE_TPL % { + wplabel: workpackage['label'], + wptitle: workpackage['title'], + title: deliverable['title'], + lead: deliverable['lead'], + date: deliverable['due_date'], + month: deliverable['month'], + nature: deliverable['nature'], + page: deliverable['page'], + } + + milestone = github.create_milestone(repo, title, + :due_on => deliverable['due_date'], + :description => body, + ) + # throttle creation calls to avoid flags for abuse + sleep THROTTLE_SECONDS + else + puts "Milestone #{repo}@#{title} exists" + end + return milestone.number +end + + +def populate_workpackage(github, repo, workpackage) + # populate issues for a given workpackage + label = workpackage['label'] + + readme_path = "#{label}/README.md" + begin + github.contents(repo, :path => readme_path) + rescue Octokit::NotFound + readme = README_TPL % { + title: "#{workpackage['label']}: #{workpackage['title']}", + lead: workpackage['lead'], + page: workpackage['page'], + } + + puts "Creating readme at #{repo}/#{readme_path}" + puts readme + github.create_contents(repo, readme_path, "Creating #{readme_path}", readme) + # throttle creation calls to avoid flags for abuse + sleep THROTTLE_SECONDS + end + + workpackage['tasks'].each_value do |task| + org_labels = [REVERSE_SITES[task['lead']]] + \ + (task['partners'] or []).map {|site| REVERSE_SITES[site]} + + make_task_issue(github, repo, task, workpackage, + :labels => [ + 'task', + workpackage['label'], + ] + org_labels + ) + end + workpackage['deliverables'].each do |deliverable| + milestone = make_deliverable_milestone(github, repo, deliverable, workpackage) + make_deliverable_issue(github, repo, deliverable, workpackage, { + :milestone => milestone, + :labels => [ + 'deliverable', + workpackage['label'], + REVERSE_SITES[deliverable['lead']] + ] + }) + end +end + + +def main(proposal_dir) + # run the whole thing + + # verify that there's a GitHub token in .netrc + check_token + + # create client + github = Octokit::Client.new(:netrc => true) + github.auto_paginate = true + + load_pdata(proposal_dir).each do |wp| + populate_workpackage(github, REPO, wp) + end +end + +if __FILE__ == $0 + puts ARGV + if ARGV.length > 1 + proposal_dir = ARGV[1] + else + proposal_dir = '.' + end + main(proposal_dir) +end \ No newline at end of file -- cgit v1.2.3